Laboratory DAQ & Interfacing

Arduino in the Physics Laboratory

Bridge physical experimentation with computational data acquisition. Complete firmware sketches, wiring schematics, real-time Python logging scripts, and an interactive virtual oscilloscope.

Virtual Hardware Simulator

Interactive Digital Oscilloscope

Simulate real-time voltage acquisition from Arduino ADC pin A0.

V_max: 5.0V | Sample Rate: 1.0 kS/s | 10-bit ADC

Standard Physics Lab Interfacing Modules

Experiment 01

Determination of 'g' via Optical Photogate Timing

Measure the time period $T$ of a simple pendulum with microsecond precision using digital hardware interrupts.

Principle & Theory

For small amplitude oscillations ($\theta < 10^\circ$), the time period $T$ of a simple pendulum of length $L$ is:

$$ T = 2\pi \sqrt{\frac{L}{g}} \implies g = 4\pi^2 \frac{L}{T^2} $$

The photogate detector is connected to Arduino Pin 2 (Hardware Interrupt INT0). A full period corresponds to 2 successive interruptions of the optical beam.

photogate_pendulum.ino
// ============================================================
// Photogate Simple Pendulum Period Timer (Microsecond Accuracy)
// Connect IR Photogate sensor to Digital Pin 2 (INT0)
// ============================================================

const byte photogatePin = 2;
volatile unsigned long t1 = 0;
volatile unsigned long t2 = 0;
volatile byte count = 0;
volatile bool newPeriodReady = false;

void IRAM_ATTR onBeamBreak() {
  unsigned long now = micros();
  count++;
  if (count == 1) {
    t1 = now;
  } else if (count == 3) {
    t2 = now;
    newPeriodReady = true;
    count = 1;
    t1 = now;
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(photogatePin, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(photogatePin), onBeamBreak, FALLING);
  Serial.println("P4P_PENDULUM_READY");
}

void loop() {
  if (newPeriodReady) {
    unsigned long durationMicros = t2 - t1;
    float periodSec = durationMicros / 1000000.0;
    
    Serial.print("PERIOD_S:");
    Serial.println(periodSec, 5);
    newPeriodReady = false;
  }
}
serial_pendulum_logger.py
import serial
import numpy as np

PORT = 'COM3' # Adjust to your Arduino serial port (e.g., /dev/ttyUSB0 on Linux)
BAUD = 115200
L = 0.85 # Measured pendulum length in meters

ser = serial.Serial(PORT, BAUD, timeout=2)
periods = []

print(f"Logging 20 periods for pendulum L = {L:.3f} m...")

while len(periods) < 20:
    line = ser.readline().decode('utf-8', errors='ignore').strip()
    if line.startswith("PERIOD_S:"):
        T = float(line.split(":")[1])
        periods.append(T)
        g_inst = 4 * (np.pi**2) * L / (T**2)
        print(f"Sample {len(periods):02d}: T = {T:.4f} s  -->  g = {g_inst:.3f} m/s^2")

ser.close()

T_mean = np.mean(periods)
T_std = np.std(periods)
g_final = 4 * (np.pi**2) * L / (T_mean**2)

print("\n--- RESULTS ---")
print(f"Mean Time Period: {T_mean:.4f} +/- {T_std:.4f} s")
print(f"Experimental 'g': {g_final:.3f} m/s^2")
Experiment 02

RC Circuit Charging, Discharging & Time Constant $\tau$

Automated step-voltage response measurement of a capacitor and non-linear regression fit of $\tau = RC$.

Transient Equations

During capacitor charging from $V_0 = 5\text{V}$ through resistor $R$:

$$ V_C(t) = V_0 \left( 1 - e^{-t/\tau} \right), \quad \text{where } \tau = RC $$

During discharging to ground:

$$ V_C(t) = V_0 e^{-t/\tau} $$
rc_transient.ino
// ============================================================
// RC Circuit Step Response Logger
// Pin 8: Charge Pin (Digital Out)
// Pin A0: Voltage Sensing across Capacitor (Analog In)
// ============================================================

const byte chargePin = 8;
const byte sensorPin = A0;

void setup() {
  Serial.begin(115200);
  pinMode(chargePin, OUTPUT);
}

void loop() {
  // Start Charging Phase
  digitalWrite(chargePin, HIGH);
  unsigned long startT = micros();
  
  for (int i = 0; i < 400; i++) {
    int raw = analogRead(sensorPin);
    float voltage = (raw * 5.0) / 1023.0;
    float t_ms = (micros() - startT) / 1000.0;
    
    Serial.print(t_ms, 2);
    Serial.print(",");
    Serial.println(voltage, 3);
    delay(5);
  }

  // Discharge phase
  digitalWrite(chargePin, LOW);
  delay(2000); // Fully discharge
  delay(1000);
}
plot_rc_curve.py
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# Simulated/Acquired experimental data
def rc_model(t, V0, tau):
    return V0 * (1 - np.exp(-t / tau))

t_data = np.linspace(0, 50, 100) # milliseconds
# Sample synthetic noisy acquisition for R=10k, C=1uF (tau = 10ms)
V_data = 5.0 * (1 - np.exp(-t_data / 10.0)) + np.random.normal(0, 0.04, len(t_data))

popt, pcov = curve_fit(rc_model, t_data, V_data, p0=[5.0, 10.0])
v_fit, tau_fit = popt
tau_err = np.sqrt(np.diag(pcov))[1]

print(f"Fitted Voltage V0: {v_fit:.3f} V")
print(f"Fitted Time Constant tau: {tau_fit:.3f} +/- {tau_err:.3f} ms")

plt.figure(figsize=(8, 4.5))
plt.scatter(t_data, V_data, color='#3b82f6', s=15, alpha=0.7, label='ADC Data Points')
plt.plot(t_data, rc_model(t_data, *popt), color='#06b6d4', lw=2, label=f'Fit: $\\tau = {tau_fit:.2f}$ ms')
plt.axvline(tau_fit, color='#ef4444', linestyle='--', label=f'63.2% V_0 at t={tau_fit:.2f}ms')
plt.title('RC Circuit Charging Curve & Exponential Fit')
plt.xlabel('Time (ms)')
plt.ylabel('Capacitor Voltage $V_C$ (V)')
plt.legend()
plt.grid(True, alpha=0.3)
plt.show()