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 basicsL298N (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 efficiencyDRV8833MOSFET-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 decayTB6612FNGHigh efficiency, supports up to 1.2A continuous, and handles high-frequency PWM without the thermal throttling of the L298N.
Concrete Pick: For this tutorial, we are using the L298N. It is the standard for 2WD educational chassis because the onboard 7805 voltage regulator can power the Arduino Uno R3 directly, eliminating the need for a separate battery pack for the logic board.

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

ComponentModule PinArduino Uno R3 PinNotes
Left Motor PWMENAD5 (PWM)Remove the physical jumper cap on the L298N
Left Motor LogicIN1, IN2D4, D7Digital OUT
Right Motor PWMENBD6 (PWM)Remove the physical jumper cap on the L298N
Right Motor LogicIN3, IN4D8, D9Digital OUT
Left SensorOUT1 (or L)A0Read as Digital or Analog
Center SensorOUT2 (or C)A1Read as Digital or Analog
Right SensorOUT3 (or R)A2Read as Digital or Analog
Power (Driver)12V / VCCBattery (+)Connect to main battery pack positive
GroundGNDGNDMust share common ground with Arduino and Battery

Assembly & Wiring Steps

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. 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.
  6. 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:

  1. 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.
  2. 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.
  3. 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:

  1. 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.
  2. 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.
  3. 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.