Loading...

Arduino

Open-source electronics platform for building interactive projects with microcontrollers

Experimental Control & Hardware Integration Beginner Recommended Tool
Quick Info
  • Category: Experimental Control & Hardware Integration
  • Level: Beginner
  • Type: Recommended Tool

Why We Recommend Arduino

Arduino makes it easy to create interactive hardware for experiments. Its simple programming interface and vast ecosystem make it ideal for building custom experimental equipment, triggering systems, and data acquisition devices.

Common Use Cases

  • Experimental trigger generation
  • Behavioral task controllers
  • Sensor data acquisition
  • Custom lab equipment

Getting Started

Arduino is an open-source electronics platform based on easy-to-use hardware and software. It’s designed for anyone making interactive projects, from artists to engineers to researchers.

Why Arduino?

  • Easy to Learn: Simple programming language (C/C++ based)
  • Low Cost: Boards start at $20-30
  • Versatile: Control LEDs, motors, sensors, displays
  • Large Community: Extensive documentation and examples
  • Research-Ready: Precise timing for experiments
  • Expandable: Shields and modules for any application

Arduino Boards

  • Microcontroller: ATmega328P
  • Digital I/O: 14 pins (6 PWM)
  • Analog Input: 6 pins
  • Memory: 32 KB Flash, 2 KB RAM
  • Price: ~$25
  • Best For: Learning, general projects

Arduino Mega

  • More I/O: 54 digital, 16 analog
  • More Memory: 256 KB Flash, 8 KB RAM
  • Best For: Complex projects, many sensors

Arduino Nano

  • Compact: Breadboard-friendly
  • Similar to Uno: Same chip, smaller size
  • Best For: Permanent installations

Programming Arduino

The Arduino IDE

  • Free software from arduino.cc
  • Write code, upload to board
  • Serial monitor for debugging
  • Libraries for sensors and modules

Basic Program Structure

void setup() {
  // Runs once at startup
  pinMode(13, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  // Runs repeatedly forever
  digitalWrite(13, HIGH);
  delay(1000);
  digitalWrite(13, LOW);
  delay(1000);
}

Digital I/O

Digital Output

const int ledPin = 13;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  digitalWrite(ledPin, HIGH);  // 5V
  delay(1000);
  digitalWrite(ledPin, LOW);   // 0V
  delay(1000);
}

Digital Input

const int buttonPin = 2;
const int ledPin = 13;

void setup() {
  pinMode(buttonPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);
}

void loop() {
  int buttonState = digitalRead(buttonPin);

  if (buttonState == LOW) {  // Button pressed (pullup inverts)
    digitalWrite(ledPin, HIGH);
  } else {
    digitalWrite(ledPin, LOW);
  }
}

Analog I/O

Analog Input (0-5V → 0-1023)

const int sensorPin = A0;

void setup() {
  Serial.begin(9600);
}

void loop() {
  int sensorValue = analogRead(sensorPin);

  // Convert to voltage
  float voltage = sensorValue * (5.0 / 1023.0);

  Serial.print("Value: ");
  Serial.print(sensorValue);
  Serial.print(", Voltage: ");
  Serial.println(voltage);

  delay(100);
}

PWM Output (Simulated Analog)

const int ledPin = 9;  // Must be PWM-capable pin

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  analogWrite(ledPin, 64);   // 25% brightness
  delay(1000);
  analogWrite(ledPin, 128);  // 50% brightness
  delay(1000);
  analogWrite(ledPin, 255);  // 100% brightness
  delay(1000);
}

Serial Communication

Sending Data

void setup() {
  Serial.begin(9600);  // Baud rate
}

void loop() {
  int value = analogRead(A0);
  Serial.println(value);  // Send to computer
  delay(100);
}

Receiving Commands

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}

void loop() {
  if (Serial.available()) {
    char command = Serial.read();

    if (command == '1') {
      digitalWrite(13, HIGH);
      Serial.println("LED ON");
    } else if (command == '0') {
      digitalWrite(13, LOW);
      Serial.println("LED OFF");
    }
  }
}

Research Applications

TTL Trigger Generation

// Generate precise TTL pulses for experiments
const int triggerPin = 8;

void setup() {
  pinMode(triggerPin, OUTPUT);
  digitalWrite(triggerPin, LOW);
  Serial.begin(115200);
}

void sendTrigger(int duration_us) {
  digitalWrite(triggerPin, HIGH);
  delayMicroseconds(duration_us);
  digitalWrite(triggerPin, LOW);
}

void loop() {
  if (Serial.available()) {
    char command = Serial.read();
    if (command == 'T') {
      sendTrigger(100);  // 100µs pulse
      Serial.println("OK");
    }
  }
}

Behavioral Task Controller

// Simple two-alternative forced choice task
const int leftLED = 12;
const int rightLED = 11;
const int leftButton = 2;
const int rightButton = 3;
const int rewardPin = 8;

void setup() {
  pinMode(leftLED, OUTPUT);
  pinMode(rightLED, OUTPUT);
  pinMode(rewardPin, OUTPUT);
  pinMode(leftButton, INPUT_PULLUP);
  pinMode(rightButton, INPUT_PULLUP);

  Serial.begin(115200);
  randomSeed(analogRead(0));
}

void loop() {
  // Wait for trial start command
  if (Serial.available() && Serial.read() == 'S') {

    // Random stimulus side
    bool correctSide = random(2);  // 0=left, 1=right

    // Show stimulus
    if (correctSide == 0) {
      digitalWrite(leftLED, HIGH);
    } else {
      digitalWrite(rightLED, HIGH);
    }

    // Wait for response
    unsigned long startTime = millis();
    bool responded = false;

    while (!responded) {
      if (digitalRead(leftButton) == LOW) {
        responded = true;
        bool correct = (correctSide == 0);
        unsigned long rt = millis() - startTime;

        // Reward if correct
        if (correct) {
          digitalWrite(rewardPin, HIGH);
          delay(50);
          digitalWrite(rewardPin, LOW);
        }

        // Send result
        Serial.print(correctSide);
        Serial.print(",");
        Serial.print(correct ? 1 : 0);
        Serial.print(",");
        Serial.println(rt);
      }

      if (digitalRead(rightButton) == LOW) {
        responded = true;
        bool correct = (correctSide == 1);
        unsigned long rt = millis() - startTime;

        if (correct) {
          digitalWrite(rewardPin, HIGH);
          delay(50);
          digitalWrite(rewardPin, LOW);
        }

        Serial.print(correctSide);
        Serial.print(",");
        Serial.print(correct ? 1 : 0);
        Serial.print(",");
        Serial.println(rt);
      }
    }

    // Clear LEDs
    digitalWrite(leftLED, LOW);
    digitalWrite(rightLED, LOW);

    delay(1000);  // ITI
  }
}

Sensor Data Logger

// Log multiple sensors to SD card or serial
void setup() {
  Serial.begin(115200);

  // Print header
  Serial.println("time_ms,sensor1,sensor2,sensor3");
}

void loop() {
  unsigned long timestamp = millis();
  int sensor1 = analogRead(A0);
  int sensor2 = analogRead(A1);
  int sensor3 = analogRead(A2);

  Serial.print(timestamp);
  Serial.print(",");
  Serial.print(sensor1);
  Serial.print(",");
  Serial.print(sensor2);
  Serial.print(",");
  Serial.println(sensor3);

  delay(10);  // 100 Hz sampling
}

Python Integration

Control Arduino from Python

import serial
import time

# Connect to Arduino
arduino = serial.Serial('COM3', 115200, timeout=1)  # Adjust port
time.sleep(2)  # Wait for connection

# Send command
arduino.write(b'T')

# Read response
response = arduino.readline().decode().strip()
print(f"Arduino says: {response}")

# Close
arduino.close()

PySerial for Data Acquisition

import serial
import numpy as np
import matplotlib.pyplot as plt

# Connect
ser = serial.Serial('COM3', 115200)

# Collect data
data = []
for i in range(1000):
    line = ser.readline().decode().strip()
    value = int(line)
    data.append(value)

# Plot
plt.plot(data)
plt.xlabel('Sample')
plt.ylabel('Value')
plt.show()

ser.close()

Timing Precision

Microsecond Resolution

void setup() {
  pinMode(13, OUTPUT);
}

void loop() {
  digitalWrite(13, HIGH);
  delayMicroseconds(100);  // 100µs HIGH
  digitalWrite(13, LOW);
  delayMicroseconds(900);  // 900µs LOW
  // Total: 1000µs = 1ms = 1kHz
}

millis() for Non-Blocking Delays

unsigned long previousMillis = 0;
const long interval = 1000;

void loop() {
  unsigned long currentMillis = millis();

  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    // Do something every second
    digitalWrite(13, !digitalRead(13));
  }

  // Other code runs without blocking
}

Common Modules & Shields

  • LCD Display: Show text and values
  • SD Card: Data logging
  • RTC (Real-Time Clock): Accurate timekeeping
  • WiFi/Ethernet: Network connectivity
  • Motor Driver: Control motors
  • Relay Module: Switch high-power devices

Best Practices for Research

  1. Use HIGH baud rate: 115200 for serial communication
  2. Buffer serial data: Don’t miss incoming bytes
  3. Non-blocking code: Avoid delay() in time-critical sections
  4. Timestamp everything: Use millis() or micros()
  5. Validate timing: Measure with oscilloscope
  6. Document pin usage: Comment your code
  7. Use interrupts: For critical timing events

Limitations

  • Limited memory: 2KB RAM on Uno
  • No real-time OS: Can miss events
  • 5V logic: May need level shifters for 3.3V devices
  • Single threaded: No parallel processing
  • Limited floating point: Slow math operations

When to Use Arduino

Good For:

  • Trigger generation
  • Simple sensors
  • Button/response collection
  • LED/display control
  • Prototyping

Consider Alternatives For:

  • Complex timing requirements (→ Teensy, ESP32)
  • High-speed data acquisition (→ DAQ boards)
  • Heavy computation (→ Raspberry Pi)
  • Many I/O pins (→ Arduino Mega, Teensy)

Learning Resources

Summary

Arduino is ideal for:

  • Lab automation: Custom experimental equipment
  • Precise timing: Trigger generation
  • Sensor integration: Data acquisition
  • Behavioral control: Task presentation

Its simplicity and reliability make it a staple in neuroscience and psychology labs worldwide.

Top