Building a reliable line follower robot Arduino project comes down to three things: sensor height calibration, motor driver voltage management, and a steering algorithm that doesn't overcorrect. While you can buy pre-packaged kits, sourcing the components individually and understanding the electrical realities of the drivetrain will save you hours of debugging on the track.
This guide targets the Arduino Uno R3 (ATmega328P) and walks you through a proportional steering build using a 3-channel IR array and an L298N dual H-bridge. We will cover the exact hardware decisions, pin mappings, compilable code with serial error handling, and the specific failure modes that cause robots to spin off the track.
The Verdict: Motor Driver Decision Tree
The most common mistake in embedded robotics is picking a motor driver based solely on price, only to discover it cannot handle the voltage drop or PWM frequency of your specific battery and motor combo. Use this decision path to select your driver. For this guide, we terminate on the L298N due to its 5V logic tolerance and beginner-friendly screw terminals, but review the table to see if your specific power supply demands a different pick.
| If your power/budget constraint is... | Then choose this driver... | Why? |
|---|---|---|
| Budget < $5, using 4x AA (6V) or 2S LiPo (7.4V), learning basics | L298N (Pick for this build) | Robust physical terminals, built-in 5V regulator for the Arduino, handles up to 2A per channel. Suffers a ~2V voltage drop. |
| Battery is 1S LiPo (3.7V) or 3x AA (4.5V), need high efficiency | DRV8833 | MOSFET-based H-bridge means almost zero voltage drop. Essential for low-voltage setups where the L298N's 2V drop would stall the motors. |
| Running high-speed PID control on 2S/3S LiPo, need fast PWM decay | TB6612FNG | High efficiency, supports up to 1.2A continuous, and handles high-frequency PWM without the thermal throttling of the L298N. |
Hardware Spec Sheet & Pin Mapping
Before cutting wires, verify you have the exact variants listed below. Substituting a 5-channel sensor for a 3-channel without adjusting the code will cause immediate compilation or logic failures.
Bill of Materials
- Microcontroller: Arduino Uno R3 (ATmega328P) - Official Specs
- Motor Driver: L298N Dual H-Bridge Module (Red PCB variant)
- Sensors: TCRT5000 3-Channel IR Sensor Array (Digital Output version with onboard comparators)
- Motors: 2x TT Gearmotors (3V-6V DC, 200 RPM no-load)
- Chassis: 2WD Acrylic or ABS baseplate with rear caster wheel
- Power: 2x 18650 Li-ion cells in series (7.4V nominal) OR 4x AA NiMH (4.8V-6V)
Pin Mapping Table
| Component | Module Pin | Arduino Uno R3 Pin | Notes |
|---|---|---|---|
| Left Motor PWM | ENA | D5 (PWM) | Remove the physical jumper cap on the L298N |
| Left Motor Logic | IN1, IN2 | D4, D7 | Digital OUT |
| Right Motor PWM | ENB | D6 (PWM) | Remove the physical jumper cap on the L298N |
| Right Motor Logic | IN3, IN4 | D8, D9 | Digital OUT |
| Left Sensor | OUT1 (or L) | A0 | Read as Digital or Analog |
| Center Sensor | OUT2 (or C) | A1 | Read as Digital or Analog |
| Right Sensor | OUT3 (or R) | A2 | Read as Digital or Analog |
| Power (Driver) | 12V / VCC | Battery (+) | Connect to main battery pack positive |
| Ground | GND | GND | Must share common ground with Arduino and Battery |
Assembly & Wiring Steps
- Mount the Sensors First: Bolt the 3-channel TCRT5000 array to the front of the chassis. Critical dimension: The IR LEDs must sit exactly 10mm to 15mm above the floor. Any higher, and the reflectance cone misses the tape; any lower, and physical track bumps will snap the sensor board.
- Prepare the L298N: Locate the two jumper caps labeled ENA and ENB on the L298N. Pull them off. If you leave them on, the PWM signals from pins D5 and D6 will be ignored, and your motors will only run at 100% speed or not at all.
- Wire the Power Loop: Connect your battery pack positive to the L298N "12V" terminal (this terminal accepts 7V-12V, despite the label). Connect the battery negative to the L298N "GND" terminal.
- Bridge the Logic Ground: Run a wire from the L298N "GND" terminal to the Arduino Uno R3 "GND" pin. Failure to establish this equipotential bond will result in erratic sensor readings and serial communication garbage.
- Power the Arduino: Leave the 5V enable jumper ON the L298N (the one near the 5V output terminal). Run a wire from the L298N "5V" output terminal to the Arduino "Vin" or "5V" pin. This powers the Uno directly from the driver's onboard regulator.
- Connect Motors and Sensors: Follow the pin mapping table above. For the sensors, connect their VCC to the Arduino 5V pin and their GND to the Arduino GND.
The Control Code: Proportional Steering
This code targets the Arduino Uno R3. Instead of basic "bang-bang" control (hard left/hard right), we use a proportional steering algorithm. The robot calculates an "error" value based on which sensor sees the line, and adjusts the speed difference between the left and right motors proportionally. This yields smooth, high-speed cornering.
Assumption: The TCRT5000 digital outputs are configured to pull LOW when over black tape (absorbs IR) and HIGH when over white floor (reflects IR).
// Line Follower Robot Arduino - Proportional Control
// Target Board: Arduino Uno R3 (ATmega328P)
// --- PIN DEFINITIONS ---
#define ENA 5 // Left Motor PWM
#define IN1 4 // Left Motor Direction
#define IN2 7 // Left Motor Direction
#define ENB 6 // Right Motor PWM
#define IN3 8 // Right Motor Direction
#define IN4 9 // Right Motor Direction
#define SENSOR_L A0
#define SENSOR_C A1
#define SENSOR_R A2
// --- TUNING CONSTANTS ---
const int BASE_SPEED = 140; // 0-255 PWM value
const int MAX_SPEED = 220; // Cap to prevent saturation
const int MIN_SPEED = 60; // Below this, TT motors stall
const float KP = 35.0; // Proportional constant (tune this)
void setup() {
Serial.begin(115200);
pinMode(ENA, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
pinMode(SENSOR_L, INPUT);
pinMode(SENSOR_C, INPUT);
pinMode(SENSOR_R, INPUT);
// Initialize motors off
analogWrite(ENA, 0);
analogWrite(ENB, 0);
}
void loop() {
// Read sensors (LOW = Black Line, HIGH = White Floor)
int left = digitalRead(SENSOR_L);
int center = digitalRead(SENSOR_C);
int right = digitalRead(SENSOR_R);
// ERROR HANDLING: Off-track detection
if (left == HIGH && center == HIGH && right == HIGH) {
Serial.println("ERR: Sensor array returned all HIGH");
stopMotors();
delay(500); // Halt to prevent runaway
return;
}
// Calculate Error: -1 (left), 0 (center), +1 (right)
// We assign weights to the outer sensors for sharper turns
int error = 0;
if (left == LOW && center == HIGH && right == HIGH) error = -2;
else if (left == LOW && center == LOW && right == HIGH) error = -1;
else if (left == HIGH && center == LOW && right == HIGH) error = 0;
else if (left == HIGH && center == LOW && right == LOW) error = 1;
else if (left == HIGH && center == HIGH && right == LOW) error = 2;
// Proportional Control Math
int turnEffort = error * KP;
int leftSpeed = BASE_SPEED + turnEffort;
int rightSpeed = BASE_SPEED - turnEffort;
// Constrain speeds to valid PWM and stall thresholds
leftSpeed = constrain(leftSpeed, MIN_SPEED, MAX_SPEED);
rightSpeed = constrain(rightSpeed, MIN_SPEED, MAX_SPEED);
// Apply to motors
driveMotors(leftSpeed, rightSpeed);
}
void driveMotors(int lSpeed, int rSpeed) {
// Left Motor Forward
digitalWrite(IN1, HIGH);
digitalWrite(IN2, LOW);
analogWrite(ENA, lSpeed);
// Right Motor Forward
digitalWrite(IN3, HIGH);
digitalWrite(IN4, LOW);
analogWrite(ENB, rSpeed);
}
void stopMotors() {
analogWrite(ENA, 0);
analogWrite(ENB, 0);
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW);
digitalWrite(IN4, LOW);
}
Debugging: First 3 Things to Check When It Fails
When your robot ignores the line, spins in circles, or shuts down mid-turn, do not rewrite the code. 90% of line follower failures are electrical or mechanical. Check these three items first:
- Sensor Calibration Potentiometers: Every TCRT5000 module has a small blue trimmer potentiometer. Place the robot over the white floor and slowly turn the pot with a precision screwdriver until the onboard LED just turns off. Then, move it over the black tape; the LED should turn on. If the ambient light in your room changes, you must recalibrate.
- L298N ENA/ENB Jumpers: If the robot only moves at maximum speed and ignores the `BASE_SPEED` variable, you forgot to remove the physical plastic jumper caps on the ENA and ENB pins. The driver is hardwired to 5V logic HIGH, bypassing your PWM signals.
- Motor Polarity: If the robot drives backward, or turns left when it should turn right, swap the two wires for the offending motor on the L298N output terminals. Do not change the code to fix a wiring reversal.
Decoding Serial Error Strings
If you have the Serial Monitor open at 115200 baud, you may encounter this specific error string generated by the code above:
ERR: Sensor array returned all HIGH
Ranked Causes for this Error:
- Sensor Height (Most Likely): The chassis is sitting too high. The IR reflectance cone is missing the black tape entirely. Lower the sensor array to 10mm from the ground.
- 5V Rail Brownout: The TT motors are pulling >1A during a stall or start-up, causing the L298N's 5V regulator to drop to 3.2V. The Arduino browns out, and the sensor comparators fail to trigger. Fix: Add a 470µF electrolytic capacitor across the L298N 12V and GND terminals to smooth current spikes.
- IR LED Failure: The IR emitter LEDs are dead. Test: Look at the sensor board through a modern smartphone camera (which can see near-IR light). If the LEDs are not glowing purple/white on the screen, they are burned out or unpowered.
Scaling the Build: Simplify or Extend
Once the proportional steering is dialed in, you have a clear path to either strip the build down for a younger student or scale it up for competition.
How to Simplify (Bang-Bang Control)
If the math in the proportional loop is too abstract for your current classroom or skill level, strip the build down to a 2-sensor "bang-bang" controller. Remove the center sensor. Wire only Left (A0) and Right (A2). If Left sees black, stop the left motor and drive the right. If Right sees black, stop the right motor and drive the left. It will wobble aggressively, but the code drops to roughly 15 lines and requires no tuning constants.
How to Extend (PID and OLED Telemetry)
To make this competitive for speed runs, proportional control isn't enough; you need to account for momentum and past error.
- Add PID: Integrate the Arduino PID Library. You will need to track the derivative (D) to prevent overshooting sharp 90-degree corners, and the integral (I) to correct long, sweeping curves where steady-state error builds up.
- Add Telemetry: Wire a 0.96" I2C OLED (SSD1306) to A4 (SDA) and A5 (SCL). Print the live `error` and `turnEffort` variables. This allows you to watch the robot's "brain" react to track anomalies in real-time without needing a laptop tethered to the Serial port.
- Upgrade the Driver: If you switch to a 3S LiPo (11.1V) for speed, the L298N will overheat and trigger its internal thermal shutdown. Swap to the TB6612FNG, which runs cool at high voltages and supports the 20kHz+ PWM frequencies required for ultra-smooth motor commutation.






