Most tutorials for an arduino car project leave you with a stuttering, brownout-resetting mess that can't climb a carpet. The culprit is usually poor power management and inefficient motor drivers. This guide skips the toy-grade advice and builds a robust, Bluetooth-controlled 2WD rover with ultrasonic obstacle avoidance using professional-grade hobby components.

Difficulty Rating: Intermediate (3/5)
Time Required: 3-4 Hours
Core Skills: Serial communication, PWM motor control, non-blocking sensor reads.

Hardware Spec Sheet & Parts List

Before wiring, we need to address the motor driver. Most beginner kits include the L298N. We are using the TB6612FNG instead. The L298N uses bipolar junction transistors, dropping roughly 1.4V to 2V across the H-bridge, which wastes battery life and generates massive heat. The TB6612FNG uses MOSFETs, dropping only ~0.5V, delivering more torque to your TT motors and running cool to the touch.

ComponentExact Variant / ModelEstimated CostWhy This Variant?
MicrocontrollerArduino Uno R3 (ATmega328P)$22 (Official)5V logic, robust voltage regulator, standard shield footprint.
Motor DriverTB6612FNG Carrier (Pololu/SparkFun)$12MOSFET-based, 1.2A continuous per channel, minimal voltage drop.
Bluetooth ModuleHC-05 (with button)$8Supports AT commands for baud rate changes, reliable SPP profile.
Ultrasonic SensorHC-SR04 (5V Logic)$3Standard 2cm-400cm range, direct 5V compatibility with Uno.
ServoSG90 Micro Servo (9g)$4Sufficient torque to pan the lightweight HC-SR04.
Power Source2S LiPo (7.4V, 1500mAh)$18High discharge rate (C-rating) prevents voltage sag during stalls.
⚠️ LiPo Safety Warning: Never discharge a 2S LiPo below 6.0V total (3.0V per cell). Use a battery with a built-in low-voltage cutoff or monitor your voltage via an analog pin. Always charge LiPos in a fireproof bag with a balance charger. Do not use 3S (11.1V) batteries directly with the Uno's barrel jack; the onboard linear regulator will overheat and fail.

Pin Mapping & Wiring Guide

The Arduino serial communication pins (0 and 1) are shared with the USB-to-serial chip. To avoid upload conflicts, we use SoftwareSerial or wire the HC-05 to the hardware RX/TX but disconnect it during code uploads. For this build, we will use hardware Serial for the HC-05 to ensure reliable Bluetooth timing, meaning you must unplug the HC-05 TX/RX wires before uploading code.

Critical Voltage Divider: The Arduino Uno outputs 5V on its TX pin. The HC-05 RX pin expects 3.3V logic. Feeding 5V into the HC-05 RX pin will degrade or destroy the module over time. Use a voltage divider (1kΩ resistor from Uno TX to HC-05 RX, and 2kΩ resistor from HC-05 RX to GND) to step the 5V down to a safe ~3.3V.

Component PinArduino Uno PinNotes / Wiring Details
TB6612FNG VCC5VLogic power for the driver chip.
TB6612FNG VMOTLiPo + (7.4V)Direct battery power for the motors. Add a 100µF decoupling capacitor across VMOT and GND.
TB6612FNG PWMA / PWMBD5 / D6Must use PWM-capable pins (~ symbol on Uno).
TB6612FNG AIN1/AIN2D4 / D7Digital direction control for Motor A (Left).
TB6612FNG BIN1/BIN2D8 / D9Digital direction control for Motor B (Right).
TB6612FNG STBY5VTie directly to 5V to keep the driver always enabled.
HC-SR04 Trig / EchoD10 / D11Standard digital I/O.
SG90 Servo SignalD3PWM pin required for Servo library.
HC-05 TX / RXD0 (RX) / D1 (TX)**RX goes through the 1k/2k voltage divider.

Complete Arduino Code & Logic

This firmware targets the Arduino Uno R3 (ATmega328P). It implements a non-blocking ultrasonic read using a timeout to prevent the code from hanging if the sound wave never bounces back. It also includes a safety stop threshold.

#include <Servo.h>

// --- PIN DEFINITIONS ---
const int PWMA = 5;
const int AIN1 = 4;
const int AIN2 = 7;
const int PWMB = 6;
const int BIN1 = 8;
const int BIN2 = 9;

const int TRIG_PIN = 10;
const int ECHO_PIN = 11;
const int SERVO_PIN = 3;

// --- CONSTANTS ---
const int MOTOR_SPEED = 200; // 0-255 PWM
const int SAFE_DISTANCE_CM = 20;
const long PULSE_TIMEOUT_US = 20000; // Prevents blocking forever

Servo panServo;

void setup() {
  Serial.begin(9600); // HC-05 default baud rate
  
  pinMode(PWMA, OUTPUT); pinMode(AIN1, OUTPUT); pinMode(AIN2, OUTPUT);
  pinMode(PWMB, OUTPUT); pinMode(BIN1, OUTPUT); pinMode(BIN2, OUTPUT);
  pinMode(TRIG_PIN, OUTPUT); pinMode(ECHO_PIN, INPUT);
  
  panServo.attach(SERVO_PIN);
  panServo.write(90); // Center the servo
  delay(500);
  
  stopMotors();
  Serial.println("Arduino Car Project Ready. Awaiting BT Commands.");
}

void loop() {
  // Safety Check: Read distance continuously
  long distance = readDistance();
  
  if (distance > 0 && distance < SAFE_DISTANCE_CM) {
    stopMotors();
    // Optional: Add reverse/turn logic here for autonomous mode
    delay(100); 
    return; // Skip Bluetooth processing while in danger zone
  }

  // Process Bluetooth Commands
  if (Serial.available() > 0) {
    char cmd = Serial.read();
    switch(cmd) {
      case 'F': moveForward(); break;
      case 'B': moveBackward(); break;
      case 'L': turnLeft(); break;
      case 'R': turnRight(); break;
      case 'S': stopMotors(); break;
    }
  }
}

long readDistance() {
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  
  // Error handling: pulseIn timeout prevents infinite hang
  long duration = pulseIn(ECHO_PIN, HIGH, PULSE_TIMEOUT_US);
  
  if (duration == 0) return -1; // Timeout or error
  return duration * 0.034 / 2;
}

void moveForward() {
  digitalWrite(AIN1, HIGH); digitalWrite(AIN2, LOW);
  digitalWrite(BIN1, HIGH); digitalWrite(BIN2, LOW);
  analogWrite(PWMA, MOTOR_SPEED); analogWrite(PWMB, MOTOR_SPEED);
}

void moveBackward() {
  digitalWrite(AIN1, LOW); digitalWrite(AIN2, HIGH);
  digitalWrite(BIN1, LOW); digitalWrite(BIN2, HIGH);
  analogWrite(PWMA, MOTOR_SPEED); analogWrite(PWMB, MOTOR_SPEED);
}

void turnLeft() {
  digitalWrite(AIN1, LOW); digitalWrite(AIN2, HIGH);
  digitalWrite(BIN1, HIGH); digitalWrite(BIN2, LOW);
  analogWrite(PWMA, MOTOR_SPEED); analogWrite(PWMB, MOTOR_SPEED);
}

void turnRight() {
  digitalWrite(AIN1, HIGH); digitalWrite(AIN2, LOW);
  digitalWrite(BIN1, LOW); digitalWrite(BIN2, HIGH);
  analogWrite(PWMA, MOTOR_SPEED); analogWrite(PWMB, MOTOR_SPEED);
}

void stopMotors() {
  digitalWrite(AIN1, LOW); digitalWrite(AIN2, LOW);
  digitalWrite(BIN1, LOW); digitalWrite(BIN2, LOW);
  analogWrite(PWMA, 0); analogWrite(PWMB, 0);
}

Debugging: First 3 Things to Check When It Fails

Embedded robotics rarely work perfectly on the first power-up. When your rover fails, follow this ranked diagnostic path before rewriting code.

1. Motors Twitch and the Arduino Resets (Brownout)

The Symptom: You send the 'F' command, the motors jerk, and the onboard LED flickers or the serial connection drops.

The Cause: TT gear motors can draw 1.2A+ each when starting or stalling. If you are attempting to power the motors from the Arduino's 5V pin, or if your battery lacks the C-rating to handle a 2.5A transient spike, the voltage drops below 4.3V. The ATmega328P brownout detector triggers a hardware reset.

The Fix: Ensure the TB6612FNG VMOT pin is wired directly to the battery positive terminal, bypassing the Arduino's regulator. Verify your 2S LiPo is rated for at least 20C discharge.

2. Serial Monitor Shows '⸮⸮⸮' or Garbage Characters

The Exact Error String: ⸮⸮⸮ or random ASCII symbols when sending commands.

The Cause: Baud rate mismatch or TX/RX cross-wiring. The HC-05 defaults to 9600 baud. If your Serial Monitor is set to 115200, or if you wired Uno TX to HC-05 TX (instead of RX), the data stream is corrupted.

The Fix: Set your Serial Monitor to 9600 baud. Verify that Uno TX goes to HC-05 RX (through the voltage divider), and Uno RX goes to HC-05 TX. If using SoftwareSerial, ensure you aren't using pins that lack Pin Change Interrupts.

3. Ultrasonic Sensor Reads Constant '0' or '400'

The Cause: The readDistance() function is timing out or receiving a false echo. This happens if the Trig and Echo pins are swapped, or if the sensor is powered from the 3.3V pin instead of the 5V pin (the HC-SR04 requires 5V to generate a loud enough acoustic pulse).

The Fix: Measure the voltage at the HC-SR04 VCC pin with a multimeter; it must read 4.8V to 5.2V. Swap the Trig/Echo wires if the reading is stuck at exactly 0cm.

Extending and Simplifying the Build

Depending on your end goal, you can scale this arduino car project up or down.

  • To Simplify (Pure Autonomous): Remove the HC-05 Bluetooth module and the serial switch statement. Replace the manual drive logic with a state machine that sweeps the servo left/right, compares distances, and autonomously navigates away from the shortest reading.
  • To Extend (PID & Odometry): Add magnetic encoders to the TT motor shafts. By counting encoder ticks, you can implement a PID control loop to drive in a perfectly straight line (compensating for motor manufacturing variances) and execute exact 90-degree point turns.
  • To Upgrade (FPV Video): Swap the Uno R3 for an ESP32-CAM. You will need to use the ESP32's hardware PWM channels and UART pins, but you gain a live Wi-Fi video feed to your phone.

FAQ: Arduino Car Project Long-Tail Questions

How do I power an Arduino car project without draining the battery in 10 minutes?

Stop using 4x AA alkaline batteries. Alkaline cells have high internal resistance; under a 2A motor load, their voltage sags drastically, and they deplete in minutes. Switch to a 2S LiPo (7.4V) with a capacity of at least 1500mAh, or a 6-cell NiMH pack (7.2V) using low-self-discharge (LSD) cells like Eneloop Pro. Furthermore, replace the L298N driver with the TB6612FNG to reclaim the 1.5V usually lost as heat.

Why is the L298N motor driver overheating in my Arduino car project?

The L298N uses older BJT (Bipolar Junction Transistor) H-bridge technology. It has a fixed voltage drop of about 1.4V to 2V across the driver, regardless of your motor speed. At 2A of combined motor current, the L298N dissipates nearly 3 to 4 watts of heat directly into its silicon junction without a massive heatsink. The MOSFET-based TB6612FNG operates with a fraction of that resistance, staying cool even under continuous load.

Can I use an ESP32 instead of an Uno for this Arduino car project?

Yes, but you must account for logic levels. The ESP32 operates at 3.3V logic. While the HC-SR04 ultrasonic sensor requires 5V power, its 5V Echo pin will fry the ESP32's 3.3V GPIO if connected directly. You will need a logic level converter or a resistor divider on the Echo pin. Additionally, the ESP32 does not have a default 5V output pin capable of powering the Uno-style shields, so you will need a dedicated 5V buck converter from the battery to power the ESP32 and sensor logic.

What is the best Bluetooth controller app for an Arduino car project?

For Android, Arduino Bluetooth Controller (by gpb01) or Kai Morich's Serial Bluetooth Terminal are the most reliable. They allow you to map custom ASCII characters (like 'F', 'B', 'L', 'R') to on-screen D-pad buttons. For iOS, Apple's MFi Bluetooth restrictions block standard HC-05 SPP connections; iOS users must either use an HM-10 BLE module with a custom CoreBluetooth app, or switch to Wi-Fi control using an ESP8266/ESP32 and a generic TCP socket app.