Project Spec Sheet & Exact Parts List
Building a reliable robotic car with Arduino requires moving beyond basic breadboard prototypes and understanding power dynamics, logic level shifting, and serial fail-safes. This guide targets the Arduino Uno R3 (ATmega328P) variant, utilizing an L298N dual H-bridge for motor control and an HC-05 module for Bluetooth telemetry.
Estimated Time: 2.5 Hours
Target Board: Arduino Uno R3 (ATmega328P, 5V Logic)
Required Components (2026 Pricing & Variants)
- Microcontroller: Arduino Uno R3 (Official or licensed clone like SparkFun RedBoard) - ~$25.00
- Motor Driver: L298N Dual H-Bridge Module (Red board variant with onboard 5V regulator) - ~$5.50
- Bluetooth Module: HC-05 (Master/Slave, 6-pin breakout with EN/KEY pin) - ~$7.00
- Chassis & Motors: 4WD Smart Car Kit with TT brushless-style brushed motors (3-6V nominal, 1:48 gear ratio) - ~$18.00
- Power Supply: 2S LiPo Battery (7.4V nominal, 8.4V fully charged, 2200mAh, XT60 connector) - ~$22.00
- Passives: 1kΩ and 2kΩ resistors (for logic level shifting), XT60 to barrel jack adapter.
L298N Power Dynamics & Pin Mapping
The most common point of failure in Arduino robotic car builds is misunderstanding the L298N motor driver's voltage drop. The L298N uses internal Darlington transistor pairs, which introduce a significant voltage drop between the power supply and the motor terminals.
| Parameter | Value / Specification | Practical Impact on Build |
|---|---|---|
| Motor Supply (Vs) Range | 5V to 35V DC | Use a 2S LiPo (8.4V max). Do not exceed 12V for TT motors. |
| Logic Supply (Vss) Range | 4.5V to 7V DC | Powered via the onboard 5V regulator when Vs > 12V, or directly from Arduino 5V. |
| Saturation Voltage Drop (Vce_sat) | ~1.8V to 2.0V (at 1A) | An 8.4V LiPo yields only ~6.4V at the motors. Perfect for 6V TT motors. |
| Max Current per Channel | 2A Continuous / 3A Peak | TT motors stall at ~800mA. The L298N handles this safely without heatsinks. |
| Logic High Threshold (Vih) | 2.3V minimum | Compatible directly with Arduino Uno 5V digital pins. |
Complete Wiring Pinout
Below is the exact pin mapping for the Uno R3. Note the voltage divider on the Bluetooth RX line to prevent frying the HC-05's 3.3V logic pin.
| Arduino Uno R3 Pin | Target Module Pin | Notes & Warnings |
|---|---|---|
| 5V | HC-05 VCC | HC-05 requires 5V power, but 3.3V logic. |
| GND | L298N GND, HC-05 GND, LiPo GND | Common ground is mandatory for serial stability. |
| Pin 10 (RX) | HC-05 TXD | Direct connection (5V to 3.3V input is safe). |
| Pin 11 (TX) | HC-05 RXD (via 1kΩ resistor) | 2kΩ resistor from RXD to GND completes the divider. |
| Pin 5 (PWM) | L298N ENA | Controls left motor speed. |
| Pin 6 (PWM) | L298N ENB | Controls right motor speed. |
| Pins 4, 7, 8, 9 | L298N IN1, IN2, IN3, IN4 | Digital direction control. |
| VIN | L298N 5V Output (Optional) | Only if L298N jumper cap is ON and Vs < 12V. |
Complete Bluetooth Control Code (Arduino C++)
This code targets the Arduino Uno R3. It uses SoftwareSerial to free up the hardware UART (Pins 0/1) for debugging via the USB cable. It includes a critical fail-safe: if the Bluetooth connection drops or the phone app crashes, the car stops automatically after 500ms.
#include <SoftwareSerial.h>
// --- Pin Definitions ---
#define ENA 5 // PWM Left
#define IN1 4 // Dir Left 1
#define IN2 7 // Dir Left 2
#define IN3 8 // Dir Right 1
#define IN4 9 // Dir Right 2
#define ENB 6 // PWM Right
#define BT_RX 10
#define BT_TX 11
// --- Safety & Config ---
const unsigned long COMMAND_TIMEOUT = 500; // ms
const int BASE_SPEED = 200; // PWM value (0-255)
unsigned long lastCommandTime = 0;
SoftwareSerial btSerial(BT_RX, BT_TX);
void setup() {
Serial.begin(9600); // Hardware serial for USB debugging
btSerial.begin(9600); // HC-05 default baud rate
pinMode(ENA, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
stopMotors();
lastCommandTime = millis();
Serial.println("System Ready. Waiting for BT commands...");
}
void loop() {
if (btSerial.available()) {
char command = btSerial.read();
// Error Handling: Strip carriage returns/newlines from Android/iOS apps
if (command == '\r' || command == '\n') return;
lastCommandTime = millis(); // Reset watchdog timer
switch (command) {
case 'F': moveForward(BASE_SPEED); break;
case 'B': moveBackward(BASE_SPEED); break;
case 'L': turnLeft(BASE_SPEED - 50); break;
case 'R': turnRight(BASE_SPEED - 50); break;
case 'S': stopMotors(); break;
default:
// Catch garbage data
Serial.print("Invalid command byte: ");
Serial.println(command, HEX);
break;
}
}
// Fail-safe: Stop motors if connection drops
if (millis() - lastCommandTime > COMMAND_TIMEOUT) {
stopMotors();
}
}
// --- Motor Control Functions ---
void moveForward(int speed) {
digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW);
digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW);
analogWrite(ENA, speed); analogWrite(ENB, speed);
}
void moveBackward(int speed) {
digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH);
digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH);
analogWrite(ENA, speed); analogWrite(ENB, speed);
}
void turnLeft(int speed) {
digitalWrite(IN1, LOW); digitalWrite(IN2, HIGH); // Left backward
digitalWrite(IN3, HIGH); digitalWrite(IN4, LOW); // Right forward
analogWrite(ENA, speed); analogWrite(ENB, speed);
}
void turnRight(int speed) {
digitalWrite(IN1, HIGH); digitalWrite(IN2, LOW); // Left forward
digitalWrite(IN3, LOW); digitalWrite(IN4, HIGH); // Right backward
analogWrite(ENA, speed); analogWrite(ENB, speed);
}
void stopMotors() {
digitalWrite(IN1, LOW); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, LOW);
analogWrite(ENA, 0); analogWrite(ENB, 0);
}
Debugging: The First Three Things to Check When It Fails
When your robotic car refuses to move or behaves erratically, do not rewrite the code immediately. Hardware and serial configuration issues cause 90% of embedded failures. Check these three specific failure modes first.
1. Upload Error: 'avrdude: stk500_getsync() not in sync'
The Symptom: You click upload in the Arduino IDE, and the progress bar stalls, eventually throwing avrdude: stk500_getsync() attempt 10 of 10: not in sync: resp=0x00.
- Cause: The HC-05 TX/RX pins are connected to Arduino hardware Pins 0 and 1. The Bluetooth module is intercepting the USB upload data.
- Fix: Disconnect the HC-05 TX/RX wires from the Arduino before uploading code. Alternatively, use the SoftwareSerial pins (10/11) as defined in the code above, which completely isolates the USB UART.
2. Serial Output: 'Invalid command byte: 0xD' (Erratic Movement)
The Symptom: The car stutters, stops randomly, or the serial monitor spams Invalid command byte: 0xD (Carriage Return) or 0xA (Line Feed).
- Cause: Your Bluetooth terminal app (like Serial Bluetooth Terminal) is configured to append a newline or carriage return to every transmitted character.
- Fix: The provided code includes a filter (
if (command == '\r' || command == '\n') return;) to handle this. If you are writing your own parser, ensure you strip whitespace. Alternatively, change the app's 'Send newline' setting to 'None'.
3. Physical Failure: Arduino Resets When Motors Start
The Symptom: The car sits still. You send the 'F' command. The motors hum for a millisecond, the Arduino onboard LED flashes, and the car stops. The serial monitor reconnects.
- Cause: Voltage brownout. TT motors draw up to 800mA at stall. If your battery cannot supply this current, or if the wiring gauge is too thin (e.g., using breadboard jumper wires for the main power rail), the voltage at the Arduino's VIN pin drops below the 6.5V threshold, triggering the onboard reset circuit.
- Fix: Verify your battery C-rating (a 2200mAh 20C LiPo can deliver 44A, which is plenty). Ensure the main power paths from the battery to the L298N use at least 18 AWG silicone wire, not 24 AWG breadboard wires. Check that the L298N GND is solidly bonded to the Arduino GND.
Extending and Simplifying the Build
Once the base platform is stable, you will likely want to modify the architecture. Here is how to pivot the design based on your end goals.
Simplify: Migrate to ESP32 DevKit V1
The Uno R3 + HC-05 combination requires logic level shifting and relies on the CPU-heavy SoftwareSerial library. To simplify the BOM and improve processing headroom, swap the brain for an ESP32 DevKit V1.
- Why it wins: The ESP32 has built-in Bluetooth Classic and BLE, eliminating the HC-05 entirely. It also features three hardware UARTs, removing the need for SoftwareSerial.
- Migration Cost: ESP32 boards cost ~$6.00. You save $7.00 on the HC-05 and hours of wiring.
- Gotcha: The ESP32 is a 3.3V logic device. The L298N requires 2.3V minimum for a logic HIGH, so the ESP32's 3.3V GPIO pins will drive the L298N directly without level shifters, but you must ensure the L298N 5V rail is stable.
Extend: Add Ultrasonic Obstacle Avoidance
To make the car autonomous, integrate an HC-SR04 Ultrasonic Sensor mounted on a micro-servo (SG90) for scanning.
- Wiring: Connect HC-SR04 VCC to 5V, GND to GND, Trig to Pin 12, Echo to Pin 13.
- Code Integration: Use the
NewPinglibrary (v1.9.1+) instead of standardpulseIn().NewPingis non-blocking and prevents the 20ms timeout delay that causes Bluetooth serial buffer overflows in standard ping implementations. - Power Note: The HC-SR04 draws ~15mA during a ping. This is negligible, but if you add a lidar module (like the TF-Luna, ~70mA), ensure the Arduino's onboard 5V regulator isn't overheating. If it is, power the sensors directly from the L298N's 5V screw terminal output.
For deeper electrical characteristics of the motor driver, refer to the STMicroelectronics L298N Datasheet, specifically the saturation voltage graphs on page 7, which map exact voltage drops against motor current draw.






