The robot car is the project that has converted more people to embedded systems than any other. The hardware is cheap, the failure modes are immediate and visible, and the moment you tune the PID controller correctly and watch the bot follow the line at full speed is genuinely satisfying. We have built variations of this with several teams; this article is the version we would recommend today.
What we are building
A two-motor robot car that follows a black line on a white floor, slows down for sharp turns, and stops when it detects an obstacle ahead. Total cost around $35; build time one weekend.
Bill of materials
- 2WD or 4WD acrylic chassis kit (motors, wheels, base) — $10
- ESP32 DevKit — $8
- L298N or TB6612FNG motor driver — $4
- 5-channel IR line tracking sensor (TCRT5000 array) — $4
- HC-SR04 ultrasonic distance sensor — $2
- 18650 battery + holder OR 6×AA pack — $5
- Jumper wires, screws, mounts — $3
About $35 total. The 4WD chassis is more torque but the 2WD is simpler to control. The TB6612FNG motor driver is a strict upgrade over the L298N: half the voltage drop, smaller, and more efficient. Use it if you can.
System block diagram
2x18650] -->|VM motor power| Driver[Motor driver
TB6612FNG] Battery -->|via 5V regulator| MCU[[ESP32]] Driver --> ML[Left motor] Driver --> MR[Right motor] MCU -->|PWM AIN BIN| Driver Line[5-ch IR line array] -->|5 digital lines| MCU Ultra[HC-SR04 ultrasonic] -->|Trig Echo| MCU
The system: battery powers the motors directly through the driver; ESP32 reads sensors, decides motor speeds, and outputs PWM. The motor driver isolates the motor's noisy power from the logic.
How a line sensor works
An IR line sensor has an IR LED and an IR phototransistor side by side, both pointing at the floor. White surface reflects most IR back; black surface absorbs it. The phototransistor's output is a digital high (or analog high) over white, low over black. With five sensors in a row across the front of the bot, the position of the line under the bot can be inferred.
For a line directly under the centre sensor, only sensor 3 (of 1-5) reads black. For a line drifting left, sensor 1 or 2 reads black. The control loop adjusts motor speeds to keep the line under sensor 3.
State machine
for 500 ms Avoiding --> Following: obstacle clear
or after avoidance manoeuvre Lost --> Searching: spin in place
looking for line Searching --> Following: line found Searching --> Idle: timeout 5 s Following --> Idle: button pressed
The bot has five clear states. Each transition is an explicit event; no buried if statements.
The PID controller
Proportional-integral-derivative control is the most useful control algorithm in embedded systems and the line follower is the canonical place to learn it. The simplest version (proportional-only) computes:
error = target_position - measured_position
output = Kp * errorFor a line follower, "target_position" is 0 (line directly under centre). "measured_position" is computed from which sensors are reading black. output is the differential between left and right motor speeds.
Adding integral and derivative terms (full PID) damps oscillations and corrects steady-state errors. For most hobby line followers, proportional plus a small derivative term is enough; the integral term is rarely worth the tuning hassle.
Firmware
Tuning the PID
Three rules of thumb that work for line followers:
- Start with Kp small (10), Kd zero, Ki zero. Increase Kp until the bot oscillates around the line.
- Add Kd until the oscillation damps out. Typical ratio: Kd is 5–10× Kp.
- If the bot consistently drifts to one side, adjust the base speeds for left and right motors (small mechanical mismatches mean equal PWM does not give equal speed).
Tune on the actual track at actual speed. PID values that work at 50% speed often fail at 80%; the loop reacts more violently when the bot moves faster.
Common problems
- Bot drives in a circle. Line sensor wired backwards, or weights array reversed. Print sensor readings to serial and verify by hand.
- Bot oscillates wildly. Kp too high. Halve it.
- Bot misses sharp turns. Base speed too high or sensor sample rate too slow. Slow down or read more often.
- Motors do not run on battery but work on USB. Battery cannot supply the inrush current. Use 18650 cells, not AA alkaline.
- Ultrasonic gives random readings. Vibration loosens the sensor mount. Check wiring; mount more rigidly.
Going further
- Add encoders. Wheel encoders give you actual speed feedback, enabling closed-loop velocity control instead of open-loop PWM.
- Add an IMU. The MPU6050 gives gyroscope readings to detect when the bot has tipped or hit a wall.
- Bluetooth control. Pair with a phone for manual override.
- Mapping. Combined with encoders, the bot can map its environment as it moves.
- Camera-based following. Replace IR line sensor with an ESP32-CAM and run a small line-detection algorithm. Much harder; much more capable.
Frequently Asked Questions
Why use a TB6612FNG instead of the more common L298N?
The L298N drops about 1.8V across its bridges, meaning a 7.4V battery only delivers 5.6V to the motors. The TB6612FNG drops 0.5V. Same battery, much more torque. The TB6612FNG also handles 1.2 A continuous (3 A peak) in a smaller form factor.
How fast can this thing actually go?
With a 7.4V supply and the TB6612FNG, the motors typically run 100–200 RPM. With 65 mm wheels, that is roughly 0.3–0.7 m/s. Well-tuned line followers can complete a 5 m track in 8–15 seconds.Why does the bot stop responding when I add Bluetooth?
BLE on ESP32 shares the radio with WiFi. With both running, the radio is busy enough that fast control loops can stutter. Drop the loop frequency to 50 Hz, or pin the BLE handler to a different core, or move the control loop into a FreeRTOS task with high priority.Can the same code work on a 4WD chassis?
Yes. Wire both left motors in parallel (same PWM input) and both right motors in parallel. The motor driver sees one pair as one motor. PID values may need slight adjustment because the chassis is heavier and turns differently.How do I prevent the bot from running off the table?
Add cliff sensors: extra IR sensors angled at the floor at the front edge. If they read no reflection (no floor), stop. Two extra channels and a small code change.Get the complete project package
The article above shows the core firmware and the principles behind it. The complete project package — assembled, tested, and ready to flash — is available by email request. We send it manually, and we read every request.
- Complete Arduino sketch (.ino) with full error handling
- List of required libraries with version numbers
- Printable wiring diagram (PDF)
- Bill of materials with current part numbers
- Build guide and troubleshooting tips
- Configuration template (WiFi, MQTT, etc.)
Share your thoughts
Worked with this in production and have a story to share, or disagree with a tradeoff? Email us at support@mybytenest.com — we read everything.