If you are building a line following robot with Arduino in 2026, the legacy combination of an Uno R3, L298N motor driver, and digital bang-bang logic will not yield competitive or smooth results. The optimal modern setup uses the Arduino Uno R4 Minima for its 14-bit ADC resolution, a TB6612FNG MOSFET motor driver to eliminate the 2V voltage drop of older H-bridges, and a 5-channel analog IR sensor array fed into a PID control loop.
This guide provides the exact parts, pin mappings, and compilable PID code to get your robot tracking smoothly on a standard 3/4-inch black electrical tape course.
Decision Tree: Choosing Your Drivetrain and Sensor Array
Before buying parts, match your chassis and electronics to your actual goal. Use this decision matrix to select your hardware.
| Scenario | Motors | Driver | Sensors | Verdict |
|---|---|---|---|---|
| Budget / Classroom | TT Gear (1:48) | L298N | 3-Ch Digital | Skip unless budget is strictly under $20. L298N drops 2V, starving TT motors. |
| Competitive / Smooth | N20 300RPM 6V | TB6612FNG | 5-Ch Analog QTR | DEFAULT PICK: Build this. Best balance of speed, torque, and tracking precision. |
| Heavy Payload / Outdoor | Coreless 12V | VNH5019 | 8-Ch Analog | Overkill for standard tape tracks. Requires 3S LiPo and heavy chassis. |
The Default Pick: We are proceeding with the N20 300RPM motors, TB6612FNG driver, and 5-channel analog sensors. This combination handles sharp 90-degree turns without overshooting and maintains straight-line speed.
Spec Sheet and Exact Parts List
Here is the exact bill of materials. Prices reflect typical 2026 hobbyist supplier rates.
| Component | Exact Model / Variant | Est. Price | Bench Notes |
|---|---|---|---|
| Microcontroller | Arduino Uno R4 Minima | $20.00 | 48MHz Cortex-M4. 14-bit ADC gives 16,383 steps vs R3's 1,023. |
| Motor Driver | TB6612FNG Dual Carrier (Pololu #713) | $6.50 | MOSFET-based. 0.5V drop at 1A. Do not buy the unregulated bare chip. |
| Sensors | Pololu QTR-5A Reflectance Array | $12.00 | Analog output. 5 sensors spaced at 16mm pitch. |
| Motors | N20 Metal Gear 300RPM (6V) | $8.00 (pair) | 100RPM is too slow for PID tuning; 600RPM is too twitchy. 300RPM is the sweet spot. |
| Chassis | 2WD Acrylic Base (120x100mm) | $5.00 | Ensure front caster is a metal ball, not a plastic wheel (less friction). |
| Power | 2S 7.4V 1000mAh LiPo (20C) | $14.00 | Provides 8.4V fully charged. 20C rating prevents voltage sag during stalls. |
| Wheels | 42mm N20 Rubber Wheels | $4.00 | Press-fit onto N20 D-shafts. |
Pin Mapping and Wiring Procedure
The Arduino Uno R4 Minima pinout differs slightly from the R3 in its PWM capabilities. All motor control pins below are mapped to hardware-timer PWM pins to prevent servo-motor jitter.
| Component | Module Pin | Uno R4 Minima Pin | Wire Color / Note |
|---|---|---|---|
| TB6612FNG | PWMA / PWMB | D5 / D6 | Orange (Hardware PWM) |
| AIN1, AIN2 | D4, D7 | Yellow (Digital Out) | |
| BIN1, BIN2 | D8, D9 | Yellow (Digital Out) | |
| STBY | 3.3V | Red (Tie high to enable) | |
| QTR-5A Sensor | VCC / GND | 5V / GND | Red / Black |
| OUT1 to OUT5 | A0 to A4 | White (Analog In) | |
| Power | LiPo Balance Lead | VIN / GND | Use XT60 pigtail. Ensure polarity before plugging in. |
Wiring Steps
- Mount the sensors: Bolt the QTR-5A array to the front of the chassis. The IR LEDs must sit exactly 3mm to 5mm above the floor. Use M3 nylon standoffs. If they are higher than 8mm, the reflectance reading will wash out.
- Wire the TB6612FNG: Solder the motor leads to the A and B output pads. Connect the VM pin to the LiPo positive lead, and GND to the LiPo negative lead. Do not power the logic side (VCC) from the motor supply; tie VCC to the Arduino 5V.
- Route the analog wires: Keep the analog sensor wires away from the motor power lines to prevent EMI noise from corrupting your ADC readings. If you must cross them, do so at 90-degree angles.
- Verify before power-up: Use a multimeter in continuity mode to check for shorts between VIN and GND, and 5V and GND. A reversed LiPo lead will instantly destroy the Uno R4's USB-C power management IC.
Complete PID Control Code for Arduino Uno R4
This sketch targets the Uno R4 Minima. It uses a proportional-integral-derivative (PID) algorithm to calculate the steering error. Unlike basic bang-bang code that just turns left or right, PID applies a scaled correction based on how far off-center the line is, resulting in smooth, high-speed tracking.
// Target Board: Arduino Uno R4 Minima
// Driver: TB6612FNG
// Sensors: 5-Channel Analog QTR
#include <Arduino.h>
// --- PIN DEFINITIONS ---
const int SENSOR_COUNT = 5;
const int sensorPins[SENSOR_COUNT] = {A0, A1, A2, A3, A4};
const int PWMA = 5; // Hardware PWM
const int AIN1 = 4;
const int AIN2 = 7;
const int PWMB = 6; // Hardware PWM
const int BIN1 = 8;
const int BIN2 = 9;
// --- PID TUNING PARAMETERS ---
// Adjust these based on your specific chassis weight and wheel grip
float Kp = 25.0;
float Ki = 0.0; // Keep at 0 for line following to prevent integral windup
float Kd = 15.0;
int baseSpeed = 180; // 0-255 PWM scale
int maxSpeed = 255;
int minSpeed = 0;
// --- SENSOR CALIBRATION VARIABLES ---
int sensorMax[SENSOR_COUNT] = {0, 0, 0, 0, 0};
int sensorMin[SENSOR_COUNT] = {16383, 16383, 16383, 16383, 16383};
int lastError = 0;
int integral = 0;
void setup() {
Serial.begin(115200);
pinMode(PWMA, OUTPUT);
pinMode(PWMB, OUTPUT);
pinMode(AIN1, OUTPUT);
pinMode(AIN2, OUTPUT);
pinMode(BIN1, OUTPUT);
pinMode(BIN2, OUTPUT);
// Set initial motor direction forward
digitalWrite(AIN1, HIGH);
digitalWrite(AIN2, LOW);
digitalWrite(BIN1, HIGH);
digitalWrite(BIN2, LOW);
// Calibrate sensors over the line
calibrateSensors();
}
void calibrateSensors() {
Serial.println("Starting Calibration. Move robot over line edges for 3 seconds...");
unsigned long startTime = millis();
while (millis() - startTime < 3000) {
for (int i = 0; i < SENSOR_COUNT; i++) {
int val = analogRead(sensorPins[i]); // 14-bit on Uno R4 (0-16383)
if (val > sensorMax[i]) sensorMax[i] = val;
if (val < sensorMin[i]) sensorMin[i] = val;
}
delay(10);
}
// Error handling: Check if sensors are disconnected or lifted off table
for (int i = 0; i < SENSOR_COUNT; i++) {
if (sensorMax[i] < 200) {
Serial.print("CALIBRATION_ERROR: Sensor max < 200 on pin A");
Serial.println(i);
Serial.println("Fix: Check wiring, ensure IR LEDs are emitting, lower sensor height to 3mm.");
while(1); // Halt execution
}
}
Serial.println("Calibration Complete.");
}
int readLinePosition() {
long weightedSum = 0;
long totalSum = 0;
for (int i = 0; i < SENSOR_COUNT; i++) {
int raw = analogRead(sensorPins[i]);
// Normalize 14-bit reading to 0-1000 scale
int val = map(raw, sensorMin[i], sensorMax[i], 0, 1000);
val = constrain(val, 0, 1000);
// White surface = low reflectance (low val), Black line = high reflectance (high val)
// Invert if your tape is white on black
weightedSum += (long)val * (i * 1000);
totalSum += val;
}
if (totalSum == 0) {
// Robot is lifted off the track or completely lost the line
return -1;
}
int position = weightedSum / totalSum;
return position; // Ranges from 0 (far left) to 4000 (far right)
}
void setMotors(int leftSpeed, int rightSpeed) {
leftSpeed = constrain(leftSpeed, minSpeed, maxSpeed);
rightSpeed = constrain(rightSpeed, minSpeed, maxSpeed);
analogWrite(PWMA, leftSpeed);
analogWrite(PWMB, rightSpeed);
}
void loop() {
int position = readLinePosition();
// Safety stop if robot is picked up
if (position == -1) {
setMotors(0, 0);
Serial.println("Safety Stop: Line lost or robot lifted.");
delay(50);
return;
}
// Target is the center sensor (index 2), which maps to 2000
int error = 2000 - position;
integral += error;
// Prevent integral windup
integral = constrain(integral, -1000, 1000);
int derivative = error - lastError;
float turnRate = (Kp * error) + (Ki * integral) + (Kd * derivative);
int leftMotorSpeed = baseSpeed + turnRate;
int rightMotorSpeed = baseSpeed - turnRate;
setMotors(leftMotorSpeed, rightMotorSpeed);
lastError = error;
}
Debugging: First Three Things to Check When It Fails
When your line following robot with Arduino fails to track, do not immediately start changing the PID variables. Hardware and calibration issues cause 90% of failures. Check these three items first.
1. The Robot Spins in Circles on Power-Up
Cause: Motor polarity is reversed, or the error calculation sign is inverted. If the robot sees the line on the left, but thinks it is on the right, it will steer away from the line, creating a violent spin.
Fix: Open the Serial Monitor. Place the robot so the line is under the far-left sensor. The position value printed should be near 0. If it reads near 4000, swap the left and right motor wires on the TB6612FNG output terminals, or change the int error = 2000 - position; line to int error = position - 2000;.
2. Serial Monitor Outputs: CALIBRATION_ERROR: Sensor max < 200
Cause: The analog pins are not seeing the voltage swing from the IR phototransistors. This happens if the sensors are mounted too high (>8mm), the IR LEDs are not receiving 5V, or you accidentally wired the digital output (DO) pins instead of the analog output (AO) pins.
Fix: Measure the voltage across the VCC and GND pads on the sensor array with your multimeter; it must read 4.8V to 5.2V. Verify your physical mounting height is exactly 3mm using a feeler gauge or stacked washers.
3. Robot Tracks Well, Then Suddenly Resets or Jitters
Cause: BROWNOUT_RESET: VCC dropped below 4.5V. When both N20 motors stall or accelerate hard simultaneously, they can pull 1.5A+ from the 2S LiPo. If your LiPo has a low C-rating, or your wiring is too thin (e.g., 26 AWG jumper wires instead of 20 AWG silicone), the voltage at the Arduino VIN pin sags, triggering the Uno R4's brownout detection.
Fix: Upgrade the main power leads from the battery to the motor driver to 18 AWG silicone wire. Ensure your LiPo is rated for at least 20C continuous discharge. Add a 470µF electrolytic capacitor across the VM and GND terminals on the TB6612FNG to absorb transient current spikes.
Extending and Simplifying the Build
Once the baseline PID tracking is reliable, you can modify the architecture based on your competition rules or learning goals.
loop() with basic IF/ELSE bang-bang logic. The robot will wobble more, but the code becomes accessible to beginners.
How to Extend for Competition
- Add an OLED Display: Wire an SSD1306 128x64 I2C OLED to A4/A5 (SDA/SCL). Use it to display real-time PID tuning values and battery voltage without needing a laptop tether.
- Implement Coast-Braking: The TB6612FNG allows for "coast" (both IN pins LOW) and "brake" (both IN pins HIGH). For sharp 90-degree intersections, momentarily applying brake to the outer wheel reduces overshoot significantly compared to just dropping PWM to zero.
- Upgrade to Pololu QTRX Sensors: If your track uses reflective tape instead of black electrical tape, standard QTR sensors will fail. QTRX sensors feature adjustable LED current and independent sensor outputs, allowing you to tune the hardware gain for high-ambient-light environments.
Building a high-performance line following robot with Arduino requires moving past legacy components. By pairing the 14-bit ADC of the Uno R4 Minima with the low-resistance TB6612FNG driver and analog reflectance sensors, you eliminate the hardware bottlenecks that cause jitter and overshoot. Stick to the 3mm sensor height, use thick silicone power wires, and tune your Kp and Kd values incrementally on the track.






