The Verdict: Which Chassis and Board to Choose
Building an RC car with Arduino is a rite of passage for embedded hobbyists, but most tutorials leave you with a car that stutters, drifts, or runs away when the Bluetooth connection drops. The root cause is almost always underspecified power delivery and lack of software watchdogs. Before buying parts, use this decision path to lock in your hardware.
| If your goal is... | Choose this Board & Driver | Choose this Power Source |
|---|---|---|
| Learning basic logic and PWM | Arduino Uno R3 + L298N | 4x 1.5V AA (6V) - Expect slow speed |
| Reliable outdoor driving (Default Pick) | Arduino Uno R3 + L298N | 2S 7.4V LiPo (1500mAh+) |
| High efficiency / minimal heat | Arduino Nano + TB6612FNG | 2S or 3S LiPo |
| Computer vision / autonomous | Raspberry Pi 4 + ESP32 co-processor | 3S 11.1V LiPo + Step-down buck |
The Concrete Pick: For a robust, debuggable first build, we are terminating on the Arduino Uno R3 (ATmega328P DIP variant) paired with an L298N Dual H-Bridge and a 2S 7.4V LiPo battery. The Uno gives you full-size headers for easy probing, and the 2S LiPo provides the 8.4V peak needed to overcome the L298N's internal voltage drop.
Exact Parts List and Spec Sheet
Do not substitute the motor driver without adjusting the code logic. The L298N uses bipolar junction transistors (BJTs), which drop about 2V to 3V across the H-bridge. If you feed it 6V from AA batteries, your motors only see ~3.5V and will barely turn. A 2S LiPo (nominal 7.4V, fully charged 8.4V) ensures your motors get a healthy 5.5V to 6V.
| Component | Exact Variant / Model | Est. Price (2026) | Critical Notes |
|---|---|---|---|
| Microcontroller | Arduino Uno R3 (ATmega328P) | $24.00 | Use official or high-quality clone with CH340/ATmega16U2 USB IC. |
| Motor Driver | L298N Dual H-Bridge Module | $6.50 | Must have the 5V output terminal block. STMicroelectronics L298 Datasheet. |
| Bluetooth Module | HC-05 (SPP Profile, 6-pin) | $8.00 | Ensure it has the 'KEY' or 'EN' pin for AT command mode access. |
| Chassis Kit | 4WD Acrylic Smart Car Chassis | $18.00 | Includes 4x TT gearmotors, wheels, and battery strap. Wire motors in parallel pairs. |
| Power Source | 2S 7.4V 1500mAh LiPo (XT60) | $16.00 | Minimum 20C discharge rating. Never use unprotected 18650s in parallel without a BMS. |
| Switch | DPDT Rocker Switch (10A) | $2.00 | Wired on the main positive LiPo lead to prevent spark-on-connect. |
Pin Mapping and Wiring Steps
Wiring an RC car with Arduino requires separating your logic ground from your high-current motor ground, while maintaining a common reference. The L298N module handles this via its GND terminal, which must tie back to the Arduino GND.
| Arduino Uno Pin | Destination | Function |
|---|---|---|
| 5V | L298N 5V (if jumper is ON) | Powering the Arduino via the driver's onboard 7805 regulator. |
| GND | L298N GND & HC-05 GND | Common logic ground reference. |
| D5 (PWM) | L298N ENA | Speed control for Left Motors. |
| D6 (PWM) | L298N ENB | Speed control for Right Motors. |
| D7 | L298N IN1 | Left Motor Direction A. |
| D8 | L298N IN2 | Left Motor Direction B. |
| D9 | L298N IN3 | Right Motor Direction A. |
| D10 | L298N IN4 | Right Motor Direction B. |
| D10 (RX) | HC-05 TXD | SoftwareSerial Receive (Cross-wired). |
| D11 (TX) | HC-05 RXD | SoftwareSerial Transmit (Cross-wired). |
- Mount the L298N: Bolt the driver to the rear of the acrylic chassis. Ensure the heatsink faces backward for airflow.
- Wire the Motors: Connect the two left TT motors in parallel to OUT1 and OUT2. Connect the two right TT motors in parallel to OUT3 and OUT4.
- Connect Power: Wire the LiPo positive (red) through the DPDT switch to the L298N 12V terminal. Wire the LiPo negative (black) directly to the L298N GND terminal.
- Establish Common Ground: Run a jumper wire from the L298N GND terminal to the Arduino Uno GND pin. Skip this, and your PWM signals will float, causing erratic motor spasms.
- Wire Bluetooth: Connect HC-05 VCC to Arduino 5V, GND to GND. Cross-wire TX to D10 and RX to D11. Add a 1k/2k resistor voltage divider on the HC-05 RX line if your module is strictly 3.3V logic (most standard HC-05s tolerate 5V on RX, but check your breakout).
Complete Arduino C++ Code with Error Handling
This code targets the Arduino Uno R3. It uses the SoftwareSerial library to communicate with the HC-05, leaving the hardware Serial (pins 0/1) free for USB debugging. Crucially, it implements a watchdog timeout: if the Bluetooth connection drops or the phone app crashes, the car stops automatically after 500ms to prevent runaways.
#include <SoftwareSerial.h>
// --- PIN DEFINITIONS ---
const int ENA = 5; // Left motor PWM
const int IN1 = 7; // Left motor Dir A
const int IN2 = 8; // Left motor Dir B
const int ENB = 6; // Right motor PWM
const int IN3 = 9; // Right motor Dir A
const int IN4 = 10; // Right motor Dir B
// Bluetooth on SoftwareSerial (RX=10, TX=11) - Wait, D10 is IN4!
// Correction for Uno: Move BT RX to D12, BT TX to D13 to avoid conflict with ENB/IN4.
const int BT_RX = 12;
const int BT_TX = 13;
SoftwareSerial bluetooth(BT_RX, BT_TX);
// --- STATE VARIABLES ---
int baseSpeed = 200; // 0-255 PWM value
unsigned long lastCommandTime = 0;
const unsigned long TIMEOUT_MS = 500; // Safety watchdog timeout
void setup() {
Serial.begin(9600); // Hardware serial for USB debugging
bluetooth.begin(9600); // HC-05 default baud rate
pinMode(ENA, OUTPUT);
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENB, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
stopMotors();
Serial.println(F("RC Car Initialized. Waiting for Bluetooth commands..."));
}
void loop() {
// 1. Watchdog Timeout Check (Safety Critical)
if (millis() - lastCommandTime > TIMEOUT_MS) {
stopMotors();
// Prevent serial spam by only printing once per timeout event
if (lastCommandTime != 0) {
Serial.println(F("Error: BT Timeout - Motors stopped"));
lastCommandTime = 0;
}
}
// 2. Process Incoming Commands
if (bluetooth.available() > 0) {
char cmd = bluetooth.read();
lastCommandTime = millis(); // Reset watchdog
// Error handling: Bounds checking for speed commands
if (cmd >= '0' && cmd <= '9') {
baseSpeed = map(cmd - '0', 0, 9, 0, 255);
baseSpeed = constrain(baseSpeed, 0, 255); // Hard limit
Serial.print(F("Speed set to: ")); Serial.println(baseSpeed);
}
// Directional commands (F=Forward, B=Back, L=Left, R=Right, S=Stop)
switch (cmd) {
case 'F': moveForward(baseSpeed); break;
case 'B': moveBackward(baseSpeed); break;
case 'L': turnLeft(baseSpeed); break;
case 'R': turnRight(baseSpeed); break;
case 'S': stopMotors(); break;
default:
Serial.print(F("Warning: Unknown command char: "));
Serial.println(cmd, HEX);
break;
}
}
}
// --- 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);
}
Troubleshooting: When the RC Car with Arduino Fails
When your build fails, do not start rewriting code. Hardware and UART mismatches cause 90% of issues. Here are the first three things to check, followed by exact error strings and their fixes.
The First 3 Things to Check:
- Common Ground: Verify the Arduino GND is physically wired to the L298N GND block. A missing ground causes the L298N to ignore logic signals.
- L298N Jumper Cap: If the Arduino is powered via USB but the L298N 5V terminal is connected to the Arduino 5V pin (with the jumper ON), you are back-feeding the USB bus. Remove the jumper or disconnect the 5V wire when testing on USB.
- TX/RX Cross-Wiring: Bluetooth TX must go to Arduino RX (D12), and Bluetooth RX to Arduino TX (D13). If they are straight-through, the car will never receive data.
| Exact Error String / Symptom | Ranked Causes | The Fix |
|---|---|---|
avrdude: stk500_recv(): programmer is not responding |
1. HC-05 TX/RX connected to Uno Pins 0/1 during upload. 2. Corrupted ATmega16U2 USB IC. |
Always unplug the Bluetooth module from pins 0 and 1 before clicking 'Upload'. Hardware Serial cannot share the USB bus during flashing. |
Serial Monitor outputs: ⸮⸮⸮⸮⸮ (Garbage characters) |
1. Baud rate mismatch in Serial Monitor. 2. HC-05 stuck in AT mode (38400 baud). |
Set Serial Monitor to 9600 baud. If HC-05 LED is blinking slowly (2 sec intervals), it is in AT mode. Power cycle it without holding the KEY button to enter 9600 baud data mode. |
| Motors hum loudly but wheels do not turn | 1. Insufficient current (Brownout). 2. PWM frequency too low for TT motors. |
Check LiPo voltage under load. If it drops below 6.5V, the L298N logic resets. Upgrade to a higher C-rating LiPo or swap to a TB6612FNG MOSFET driver. |
How to Extend or Simplify the Build
Once the baseline RC car with Arduino is driving reliably, you have two distinct paths depending on your end goal.
To Simplify (For younger makers or quick demos):
Drop the HC-05 Bluetooth module and the smartphone app dependency. Swap in an IR Receiver (HX1838) and a 24-button IR remote. It costs under $3, requires only one digital pin, and eliminates all UART baud-rate debugging. You lose range (line-of-sight only), but you gain 100% reliability indoors.
To Extend (For robotics and autonomous navigation):
The L298N is a legacy BJT driver that wastes ~2V as heat. For serious outdoor runs, upgrade to a TB6612FNG MOSFET motor driver. It handles up to 1.2A continuous per channel with almost zero voltage drop, meaning your motors get the full battery voltage. Next, add an MPU6050 IMU (I2C) to the chassis. By reading the accelerometer and gyroscope data, you can implement software traction control, drift detection, or basic dead-reckoning navigation when the Bluetooth signal drops.






