Home › Projects › ESP32 Line Follower

ESP32 Line Follower: a car that follows a black line on its own

Beginner~60 minutesUses the obstacle-car chassisESP32 core 3.x

Line following is the classic first robotics challenge. Two IR sensors and a roll of black tape are enough to make a car track a course — and to learn the core idea of correcting direction from sensor feedback.

✓ Compile-checked on 2026-09-29 with the Arduino ESP32 board package 3.3.12 (all warnings enabled, 0 warnings), board: ESP32 Dev Module.

1. Parts

PartQtyNotes
Obstacle-car chassis (ESP32, L298N, motors, battery)1See the obstacle car
TCRT5000 IR line sensor module2The kind with a blue trimmer and a DO pin
Black electrical tape1 roll1.5–2 cm wide
White floor, poster board or foam boardThe track

2. How it works

Each TCRT5000 shines infrared at the floor. White reflects strongly, black tape barely at all, and the module's comparator turns that into a digital signal — most output HIGH over black.

Mount the two sensors at the front, straddling the line:

LeftRightMeaningAction
whitewhiteLine is between themGo straight
blackwhiteCar drifted rightSteer left
whiteblackCar drifted leftSteer right
blackblackCrossing or overshootKeep turning the last way; stop if lost

3. Wiring

Motors and L298N are wired exactly as in the obstacle car. Add the sensors:

FromToNotes
Left TCRT5000 DOESP32 GPIO 34GPIO 34/35 are input-only — ideal here
Right TCRT5000 DOESP32 GPIO 35
Both VCCESP32 3V3Powered at 3.3 V, the output needs no divider
Both GNDGND

Mount the sensors about 1 cm above the floor, spaced slightly wider than the tape (about 2.5–3 cm).

4. Calibrate (the step people skip)

  1. Upload the code and lift the car.
  2. With a sensor over white, turn its blue trimmer until the module's LED just turns off.
  3. Over the black tape, the LED should turn on. Do both sensors.
  4. If it's the other way round, set LINE_IS_HIGH to false.

5. Code

/*
 * TinyBot Forge — ESP32 Line Follower (two IR sensors)
 * https://tinybotforge.com/en/
 *
 * Hardware: ESP32 DevKit V1, 2x TCRT5000 IR line sensor modules, L298N, two TT motors
 * Track: 1.5-2 cm black electrical tape on a white floor
 * Toolchain: Arduino IDE 2.x + ESP32 board package 3.x
 *
 * Most TCRT5000 modules output HIGH on DO over the black line and LOW over white.
 * If yours is the other way round, set LINE_IS_HIGH to false.
 */

const int PIN_SENSOR_L = 34;   // GPIO34/35 are input-only: perfect for sensors
const int PIN_SENSOR_R = 35;
const bool LINE_IS_HIGH = true;

const int PIN_ENA = 25, PIN_IN1 = 26, PIN_IN2 = 27;
const int PIN_ENB = 33, PIN_IN3 = 32, PIN_IN4 = 13;
const int PWM_FREQ = 1000, PWM_BITS = 8;

const int SPEED_STRAIGHT = 150;
const int SPEED_FAST = 170;      // outer wheel while correcting
const int SPEED_SLOW = -60;      // inner wheel while correcting (slightly reversed for a sharper turn)
const unsigned long LOST_MS = 1500;  // stop if the line is lost for this long

int lastTurn = 0;  // -1 = last corrected left, 1 = right
unsigned long lostSince = 0;

void setMotor(int pinEn, int pinA, int pinB, int speed) {
  speed = constrain(speed, -255, 255);
  digitalWrite(pinA, speed > 0 ? HIGH : LOW);
  digitalWrite(pinB, speed < 0 ? HIGH : LOW);
  ledcWrite(pinEn, abs(speed));
}

void drive(int left, int right) {
  setMotor(PIN_ENA, PIN_IN1, PIN_IN2, left);
  setMotor(PIN_ENB, PIN_IN3, PIN_IN4, right);
}

bool onLine(int pin) {
  return digitalRead(pin) == (LINE_IS_HIGH ? HIGH : LOW);
}

void setup() {
  Serial.begin(115200);
  pinMode(PIN_SENSOR_L, INPUT);
  pinMode(PIN_SENSOR_R, INPUT);
  pinMode(PIN_IN1, OUTPUT); pinMode(PIN_IN2, OUTPUT);
  pinMode(PIN_IN3, OUTPUT); pinMode(PIN_IN4, OUTPUT);
  ledcAttach(PIN_ENA, PWM_FREQ, PWM_BITS);
  ledcAttach(PIN_ENB, PWM_FREQ, PWM_BITS);
  drive(0, 0);
  delay(2000);
}

void loop() {
  bool l = onLine(PIN_SENSOR_L);
  bool r = onLine(PIN_SENSOR_R);

  if (!l && !r) {
    // line is between the sensors: go straight
    drive(SPEED_STRAIGHT, SPEED_STRAIGHT);
    lostSince = 0;
  } else if (l && !r) {
    // left sensor on the line: car drifted right, steer left
    drive(SPEED_SLOW, SPEED_FAST);
    lastTurn = -1;
    lostSince = 0;
  } else if (!l && r) {
    // right sensor on the line: steer right
    drive(SPEED_FAST, SPEED_SLOW);
    lastTurn = 1;
    lostSince = 0;
  } else {
    // both on black: a crossing or overshoot; search in the last direction
    if (lostSince == 0) lostSince = millis();
    if (millis() - lostSince > LOST_MS) {
      drive(0, 0);
    } else if (lastTurn < 0) {
      drive(SPEED_SLOW, SPEED_FAST);
    } else {
      drive(SPEED_FAST, SPEED_SLOW);
    }
  }
  delay(5);
}
Download the .ino file

6. Track tips

7. Troubleshooting

It flies off in corners

Too fast to correct in time. Lower SPEED_STRAIGHT to 120–130, or make SPEED_SLOW more negative for sharper turns.

It wiggles on straights

The sensors are too close together. Space them out a little.

Once completely off the line it keeps driving straight

That's the limit of two sensors: "white, white" looks the same whether the line is centered or gone. Three or five sensors plus PID fix this — that's in the book.

Free: ESP32 Robot Wiring Cheat Sheet

One printable page: safe ESP32 pins, L298N, HC-SR04, servo wiring and the most common fixes.

Download the PDF