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 Driver | Choose this Sensor Array | Battery System |
|---|---|---|---|
| Ultra-low budget (<$15), simple classroom demo | L298N (BJT, high voltage drop) | 3-Channel TCRT5000 (Digital out) | 4x AA NiMH (6V) |
| High speed, sharp turns, efficient power delivery | TB6612FNG (MOSFET, 0.5V drop) | 5-Channel TCRT5000 (Analog out) | 2S LiPo (7.4V) |
| Complex intersections, PID tuning, >$50 budget | DRV8833 or Dual VNH5019 | QRE1113 Reflectance (8-channel) or OpenMV Cam | 2S or 3S LiPo |
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.
| Component | Exact Variant / Model | Key Spec | Est. Price (2026) |
|---|---|---|---|
| Microcontroller | Arduino Nano v3 (ATmega328P) | 16MHz, 5V logic, CH340 or FTDI USB | $6 (Clone) / $24 (Official) |
| Motor Driver | TB6612FNG Dual Carrier (Pololu or generic) | 1.2A continuous per channel, 100kHz PWM | $5.50 |
| Sensor Array | 5-Channel TCRT5000 IR Tracker | Analog & Digital outs, 10mm spacing | $4.00 |
| Motors | N20 Metal Gearmotors (6V) | 300 RPM no-load, 15:1 gear ratio | $9.00 / pair |
| Power | 2S LiPo Battery (7.4V) + XT60 pigtail | 800mAh - 1300mAh, 25C discharge | $14.00 |
| Wheels | 42mm diameter N20 silicone tires | High 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 Pin | TB6612FNG / Sensor Pin | Function |
|---|---|---|
| D2 | STBY | Standby (HIGH = Active, LOW = Sleep) |
| D4 | AIN1 | Motor A Direction Logic 1 |
| D5 (PWM) | PWMA | Motor A Speed Control |
| D7 | AIN2 | Motor A Direction Logic 2 |
| D8 | BIN1 | Motor B Direction Logic 1 |
| D6 (PWM) | PWMB | Motor B Speed Control |
| D9 | BIN2 | Motor B Direction Logic 2 |
| A0 | Sensor S1 (Far Left) | Analog IR Reflectance |
| A1 | Sensor S2 (Mid Left) | Analog IR Reflectance |
| A2 | Sensor S3 (Center) | Analog IR Reflectance |
| A3 | Sensor S4 (Mid Right) | Analog IR Reflectance |
| A4 | Sensor S5 (Far Right) | Analog IR Reflectance |
| 5V | VCC (Logic) | 5V Logic Supply |
| GND | GND | Common Ground (Critical!) |
Wiring Procedure
- Power the Logic First: Connect the Nano 5V to the TB6612FNG VCC. Do not power the VM (Motor Voltage) pin yet.
- 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.
- 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.
- 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
AIN1andAIN2pin logic in thesetup()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
sensorWeightsarray 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()todigitalRead()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-Libraryby Brett Beauregard, feeding it theerrorvariable 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 theKPvalue, start low (around 10.0). If the robot wobbles on straightaways, yourKPis too high. If it runs off the track on 90-degree turns, yourBASE_SPEEDis too high for the physical grip of your tires. Always tune speed before tuning the PID constants.






