The Verdict: Choosing Your Line Following Robot Arduino Hardware

If you have ever built a line following robot Arduino project using the ubiquitous L298N motor driver, you likely ran into the same bench frustration I did: sluggish acceleration and stalling on sharp turns. The L298N uses Darlington BJT transistors, which drop 1.5V to 2.0V across the H-bridge. On a 7.4V LiPo powering 6V motors, you are starving your motors of torque. The decision-forward fix is to switch to a MOSFET-based driver and a higher-resolution sensor array.

If your goal is...Choose this Motor DriverChoose this Sensor ArrayBattery System
Ultra-low budget (<$15), simple classroom demoL298N (BJT, high voltage drop)3-Channel TCRT5000 (Digital out)4x AA NiMH (6V)
High speed, sharp turns, efficient power deliveryTB6612FNG (MOSFET, 0.5V drop)5-Channel TCRT5000 (Analog out)2S LiPo (7.4V)
Complex intersections, PID tuning, >$50 budgetDRV8833 or Dual VNH5019QRE1113 Reflectance (8-channel) or OpenMV Cam2S or 3S LiPo
Default Recommendation: For 90% of hobbyist and competitive builds, terminate your search here: Use the TB6612FNG motor driver paired with a 5-channel analog TCRT5000 array. This combination provides the best balance of PWM efficiency, cornering speed, and code simplicity without requiring a custom PCB.

Exact Parts List & Spec Sheet

To replicate the exact performance benchmarks in this guide, source these specific variants. Substituting the microcontroller or motor driver will require altering the PWM frequencies and voltage scaling in the code.

ComponentExact Variant / ModelKey SpecEst. Price (2026)
MicrocontrollerArduino Nano v3 (ATmega328P)16MHz, 5V logic, CH340 or FTDI USB$6 (Clone) / $24 (Official)
Motor DriverTB6612FNG Dual Carrier (Pololu or generic)1.2A continuous per channel, 100kHz PWM$5.50
Sensor Array5-Channel TCRT5000 IR TrackerAnalog & Digital outs, 10mm spacing$4.00
MotorsN20 Metal Gearmotors (6V)300 RPM no-load, 15:1 gear ratio$9.00 / pair
Power2S LiPo Battery (7.4V) + XT60 pigtail800mAh - 1300mAh, 25C discharge$14.00
Wheels42mm diameter N20 silicone tiresHigh friction, 3mm D-shaft bore$5.00 / pair

For detailed electrical characteristics of the motor driver, refer to the Pololu TB6612FNG Motor Driver Carrier documentation, which outlines the strict 15V absolute maximum on the VM (motor voltage) pin.

Pin Mapping & Wiring Steps

The TB6612FNG requires more logic pins than the L298N because it separates PWM speed control from directional logic. We will use the Arduino Nano's hardware PWM pins (D5, D6) for speed, and standard digital pins for direction.

Pin Mapping Table

Arduino Nano PinTB6612FNG / Sensor PinFunction
D2STBYStandby (HIGH = Active, LOW = Sleep)
D4AIN1Motor A Direction Logic 1
D5 (PWM)PWMAMotor A Speed Control
D7AIN2Motor A Direction Logic 2
D8BIN1Motor B Direction Logic 1
D6 (PWM)PWMBMotor B Speed Control
D9BIN2Motor B Direction Logic 2
A0Sensor S1 (Far Left)Analog IR Reflectance
A1Sensor S2 (Mid Left)Analog IR Reflectance
A2Sensor S3 (Center)Analog IR Reflectance
A3Sensor S4 (Mid Right)Analog IR Reflectance
A4Sensor S5 (Far Right)Analog IR Reflectance
5VVCC (Logic)5V Logic Supply
GNDGNDCommon Ground (Critical!)

Wiring Procedure

  1. Power the Logic First: Connect the Nano 5V to the TB6612FNG VCC. Do not power the VM (Motor Voltage) pin yet.
  2. Establish Common Ground: Tie the Nano GND, TB6612FNG GND, Sensor Array GND, and LiPo negative terminal together. Missing this common ground is the #1 cause of erratic motor spinning.
  3. Wire the Sensors: Connect the 5 analog out pins on the TCRT5000 to A0-A4. Ensure the sensor array is mounted exactly 5mm to 8mm above the track surface. Above 12mm, the IR beam scatters and analog resolution collapses.
  4. Connect Motors and VM: Solder motor leads to AO1/AO2 and BO1/BO2. Connect the LiPo positive lead to the VM pin on the TB6612FNG. Add a 100µF electrolytic capacitor across VM and GND to suppress voltage spikes from the N20 motors.

Complete Compilable Code (Target: Arduino Nano v3)

This code targets the Arduino Nano v3 (ATmega328P, 16MHz). It implements a Proportional (P) control loop. Instead of simple bang-bang steering (which causes oscillation), it calculates the weighted centroid of the line position and scales the motor differential proportionally.

Ensure your IDE is set to Board: "Arduino Nano", Processor: "ATmega328P", and Port: your specific COM port. If using a clone with an older bootloader, select "ATmega328P (Old Bootloader)".


// Line Following Robot Arduino - P-Control with TB6612FNG
// Target: Arduino Nano v3 (ATmega328P 16MHz)

// --- Pin Definitions ---
#define STBY_PIN 2
#define PWMA_PIN 5
#define AIN1_PIN 4
#define AIN2_PIN 7
#define PWMB_PIN 6
#define BIN1_PIN 8
#define BIN2_PIN 9

// Sensor Pins (A0 to A4)
const int sensorPins[5] = {A0, A1, A2, A3, A4};
const float sensorWeights[5] = {-2.0, -1.0, 0.0, 1.0, 2.0};

// --- Tuning Parameters ---
const int BASE_SPEED = 180;      // 0-255 PWM
const int MAX_SPEED = 240;       // Cap to prevent battery brownout
const float KP = 25.0;           // Proportional constant (tune this on track)
const int SENSOR_THRESHOLD = 600; // Analog threshold (0=Black, 1023=White)

void setup() {
  Serial.begin(115200);
  
  pinMode(STBY_PIN, OUTPUT);
  pinMode(PWMA_PIN, OUTPUT);
  pinMode(AIN1_PIN, OUTPUT);
  pinMode(AIN2_PIN, OUTPUT);
  pinMode(PWMB_PIN, OUTPUT);
  pinMode(BIN1_PIN, OUTPUT);
  pinMode(BIN2_PIN, OUTPUT);
  
  // Enable TB6612FNG
  digitalWrite(STBY_PIN, HIGH);
  
  // Set initial direction (Forward)
  digitalWrite(AIN1_PIN, HIGH);
  digitalWrite(AIN2_PIN, LOW);
  digitalWrite(BIN1_PIN, HIGH);
  digitalWrite(BIN2_PIN, LOW);
}

void loop() {
  float weightedSum = 0;
  int totalSum = 0;
  int activeSensors = 0;
  
  // Read sensors and calculate centroid
  for (int i = 0; i < 5; i++) {
    int val = analogRead(sensorPins[i]);
    // Invert logic: TCRT5000 analog reads HIGH on white, LOW on black.
    // We want black line to be the 'active' signal.
    int lineVal = 1023 - val; 
    
    if (lineVal > SENSOR_THRESHOLD) {
      weightedSum += lineVal * sensorWeights[i];
      totalSum += lineVal;
      activeSensors++;
    }
  }
  
  // Error Handling & State Management
  if (activeSensors == 0) {
    // All sensors see white (lost the line) or saturated by sunlight
    Serial.println("ERR: SENSOR_SATURATION");
    stopMotors();
    return; 
  }
  
  if (activeSensors == 5 && totalSum > 4500) {
    // All sensors see deep black (intersection or end of track)
    Serial.println("ERR: INTERSECTION_DETECTED");
    stopMotors();
    return;
  }
  
  // Calculate Error (-2.0 to +2.0)
  float error = weightedSum / totalSum;
  
  // Calculate Motor Speeds
  int leftSpeed = BASE_SPEED + (error * KP);
  int rightSpeed = BASE_SPEED - (error * KP);
  
  // Constrain speeds
  leftSpeed = constrain(leftSpeed, 0, MAX_SPEED);
  rightSpeed = constrain(rightSpeed, 0, MAX_SPEED);
  
  // Apply PWM
  analogWrite(PWMA_PIN, leftSpeed);
  analogWrite(PWMB_PIN, rightSpeed);
  
  // Debug output (throttled to avoid serial bottleneck)
  static unsigned long lastPrint = 0;
  if (millis() - lastPrint > 100) {
    Serial.print("Err: "); Serial.print(error, 2);
    Serial.print(" | L: "); Serial.print(leftSpeed);
    Serial.print(" | R: "); Serial.println(rightSpeed);
    lastPrint = millis();
  }
}

void stopMotors() {
  analogWrite(PWMA_PIN, 0);
  analogWrite(PWMB_PIN, 0);
}

Debugging: First Three Things to Check When It Fails

When a line following robot Arduino build fails to track, the issue is almost always physical wiring or ambient light interference, not the math. Here are the first three things to check, ranked by probability.

1. Upload Fails with Sync Error

Exact Error String: avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x00

  • Cause A (Most Likely): You are using a clone Nano with the CH340 USB-to-Serial chip, and the IDE is trying to use the default bootloader timing.
  • Fix: Go to Tools > Processor and select "ATmega328P (Old Bootloader)". If that fails, install the official CH340 drivers from the WCH website.
  • Cause B: The D0 (RX) and D1 (TX) pins are wired to a Bluetooth module (like an HC-05) during upload.
  • Fix: Disconnect any peripherals from D0 and D1 before uploading. The USB serial bus shares these pins.

2. Robot Spins in Tight Circles on Startup

Symptom: The code uploads fine, but the moment you place it on the track, it violently spins left or right, ignoring the line.

  • Cause A: Motor phase wires are swapped, meaning your "left" motor is actually driving the right wheel in reverse.
  • Fix: Swap the physical wires on the AO1/AO2 terminals of the TB6612FNG, or swap the AIN1 and AIN2 pin logic in the setup() function.
  • Cause B: The sensor array is mounted backwards (cable pointing toward the rear of the robot).
  • Fix: Rotate the sensor array 180 degrees. The P-control math assumes S1 is far-left and S5 is far-right relative to forward motion.

3. Serial Monitor Outputs "ERR: SENSOR_SATURATION"

Exact Error String: ERR: SENSOR_SATURATION (Printed continuously, robot refuses to move).

  • Cause A: Ambient infrared light (direct sunlight or halogen bulbs) is flooding the TCRT5000 phototransistors, maxing out the analog read to 1023 (which the code interprets as 0 line reflectance).
  • Fix: Build a shroud. 3D print or tape a piece of black cardstock over the sensor array to block overhead light. Alternatively, move the robot to a room with LED lighting (which emits minimal IR).
  • Cause B: Sensors are mounted too high (>12mm). The IR LED beam disperses before hitting the track.
  • Fix: Lower the chassis. The optimal focal distance for standard TCRT5000 modules is exactly 5mm to 8mm. Use M3 standoffs to dial in the height.

How to Extend or Simplify the Build

Depending on your competition rules or learning objectives, you may need to scale this architecture up or down. For more foundational theory on IR reflectance sensors, consult the Arduino Nano Hardware Documentation regarding ADC sampling rates.

How to Simplify (For Beginners / Low Budget)

  • Drop to 3 Sensors: Remove S1 and S5. Wire only S2, S3, S4 to A1, A2, A3. Update the sensorWeights array to {-1.0, 0.0, 1.0}. This reduces cornering speed but makes the math easier to visualize.
  • Use Digital Outputs: Instead of reading analog values, use the digital out pins on the sensor board. Turn the tiny blue potentiometer on each sensor module with a jeweler's screwdriver until the LED toggles exactly over the black/white boundary. Change analogRead() to digitalRead() and implement basic IF/ELSE bang-bang steering.

How to Extend (For Advanced / Competitive)

  • Add Full PID Control: The P-controller above will oscillate on long straights. Add an Integral (I) term to correct steady-state error and a Derivative (D) term to dampen oscillation on sharp curves. Use the Arduino-PID-Library by Brett Beauregard, feeding it the error variable calculated in the main loop.
  • Implement Odometry: Solder quadrature encoders to the rear of the N20 motors. By counting ticks, you can maintain a perfectly straight line even when the sensors temporarily lose the track at intersections.
  • Upgrade Sensors: Swap the TCRT5000 array for an 8-channel QRE1113 Reflective Sensor Array (like the Pololu #961). The QRE1113 offers faster response times (microseconds vs milliseconds) and tighter 9mm spacing, allowing for much higher top speeds without overshooting the line.
Bench Note: When tuning the KP value, start low (around 10.0). If the robot wobbles on straightaways, your KP is too high. If it runs off the track on 90-degree turns, your BASE_SPEED is too high for the physical grip of your tires. Always tune speed before tuning the PID constants.