Wire the HC-05 to D2 and D3, the L298N to four direction pins and two PWM pins, pair the phone at 9600 baud, and send single letters. The car moves on F, B, L, R and S, and stops by itself when the phone goes quiet. Three faults cause most failures: a missing voltage divider on the module’s RX pin, a 5 V rail shared with the motors, and ENA or ENB jumpers left in place.
This is a first Bluetooth car, not a competition robot. No encoders, no closed-loop speed control, no app to write. The job is a short parser, a motor driver and power wiring that survives a stall.
Parts you need
- Arduino Uno, or a Nano if you want a lighter board. The Uno guide lists the PWM pins; you need two here.
- HC-05 Bluetooth module. An HC-06 also works; the HC-05 lets you change the name, PIN, baud and role in AT mode.
- L298N module with heatsink. Two channels, screw terminals, onboard 5 V regulator.
- Two TT gear motors with wheels and a 2WD chassis with a caster.
- Battery: a 2S 18650 holder with protected cells, or 4×AA NiMH for a slow first test. A 9 V battery will not drive the motors.
- Switch, 1 kΩ and 2 kΩ resistors, jumper wires, a 100 µF capacitor.
- Optional 5 V 2 A buck converter for a logic rail that does not sag when the motors start.
Wiring the HC-05 and the L298N to an Uno
SoftwareSerial gives the HC-05 its own pair of pins, so the USB port stays free for debugging and the parser never fights with the upload.
| Module pin | Connects to | Note |
|---|---|---|
| HC-05 VCC | 5V | Breakout has a 3.3 V regulator; the radio is not 5 V tolerant |
| HC-05 GND | GND | Common ground with the L298N and the battery |
| HC-05 TXD | D3 | Straight wire; 3.3 V still reads HIGH on a 5 V Uno |
| HC-05 RXD | D2 through a divider | D2 → 1 kΩ → RXD, and RXD → 2 kΩ → GND. This brings 5 V down to about 3.3 V |
| L298N ENA | D5 (PWM) | Remove the ENA jumper or the left motor runs at full speed only |
| L298N IN1, IN2 | D7, D8 | Left motor direction. Swapping these two reverses that motor |
| L298N IN3, IN4 | D9, D10 | Right motor direction |
| L298N ENB | D6 (PWM) | Remove the ENB jumper |
| L298N +12V | Battery + through the switch | Check polarity twice; this input has no reverse protection |
| L298N GND | Battery − and Uno GND | Two ground paths, not one |
| L298N 5V out | Optional source for the HC-05 | Small onboard regulator. Do not feed the Uno 5V pin from it while USB is plugged in |
Why the module’s RX pin needs a divider
The HC-05 radio runs at 3.3 V. Its TXD output is safe to feed into a 5 V Arduino input, because 3.3 V sits above the Uno’s HIGH threshold. Its RXD input is not safe to drive at 5 V. A 1 kΩ series resistor with 2 kΩ to ground puts about 3.3 V on that pin. Without it the module can appear paired and then drop commands under load.
AT mode, baud and pairing
The factory defaults are usually 9600 baud, PIN 1234 or 0000, and a name like HC-05. AT mode runs at its own baud, commonly 38400, separate from the 9600 data baud. You only need AT mode if a default is wrong or you want to rename the module.
- Unplug power. Hold the small button on the module, or tie the EN/KEY pin high, then apply power. The LED slows to a regular blink.
- The module talks to D2 and D3, not the USB port, so upload a short passthrough sketch that copies the hardware serial to SoftwareSerial and back. Open the Serial Monitor at 38400 with “Both NL & CR”. A few clones use 9600 in AT mode, so try that next if you get nothing.
- Type
AT. You should getOK. If you do not, the RX divider is wrong or the pins are swapped. - Set what you need:
AT+NAME=ROBOT1,AT+PSWD="1234",AT+UART=9600,0,0. Each command repliesOK. - Power cycle without the button held. The module is now in data mode at 9600 baud, ready for the sketch.
Pair from the phone’s Bluetooth settings, then open a serial Bluetooth app that sends characters. Apps that send “F” on a button press match this sketch directly. If the app sends words such as FORWARD, either change the app or extend the parser; the sketch ignores anything that is not a single command letter, including carriage returns and line feeds.
The sketch, with a failsafe stop
The parser keeps one command, not a queue, so stale characters cannot build up and keep the car moving after the button is released. The timeout is the real safety feature: if no valid character arrives for 600 ms, the motors stop.
// Bluetooth robot car: Arduino Uno + HC-05 + L298N + two TT motors
// Send F, B, L, R or S from a Bluetooth serial app at 9600 baud.
#include <SoftwareSerial.h>
SoftwareSerial bt(3, 2); // RX = D3 (from HC-05 TXD), TX = D2 (to HC-05 RXD via divider)
const int ENA = 5, IN1 = 7, IN2 = 8;
const int ENB = 6, IN3 = 9, IN4 = 10;
const int START_PWM = 95; // below this a TT motor on an L298N usually just buzzes
const int RUN_PWM = 190;
const unsigned long FAILSAFE_MS = 600;
char cmd = 'S';
unsigned long lastCmdAt = 0;
void setup() {
bt.begin(9600); // data-mode baud, set with AT+UART
Serial.begin(9600); // USB mirror; comment out for a battery-only run
pinMode(ENA, OUTPUT); pinMode(ENB, OUTPUT);
pinMode(IN1, OUTPUT); pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT); pinMode(IN4, OUTPUT);
brake();
}
void loop() {
while (bt.available()) {
char c = toupper(bt.read());
if (c == 'F' || c == 'B' || c == 'L' || c == 'R' || c == 'S') {
cmd = c;
lastCmdAt = millis();
Serial.println(c); // proves the command arrived before you blame the motors
}
}
if (millis() - lastCmdAt > FAILSAFE_MS) cmd = 'S';
drive(cmd);
}
void drive(char c) {
switch (c) {
case 'F': motors(RUN_PWM, true, RUN_PWM, true); break;
case 'B': motors(RUN_PWM, false, RUN_PWM, false); break;
case 'L': motors(START_PWM, false, RUN_PWM, true); break;
case 'R': motors(RUN_PWM, true, START_PWM, false); break;
default: brake(); return;
}
}
void motors(int leftPwm, bool leftFwd, int rightPwm, bool rightFwd) {
digitalWrite(IN1, leftFwd ? HIGH : LOW);
digitalWrite(IN2, leftFwd ? LOW : HIGH);
digitalWrite(IN3, rightFwd ? HIGH : LOW);
digitalWrite(IN4, rightFwd ? LOW : HIGH);
analogWrite(ENA, leftPwm);
analogWrite(ENB, rightPwm);
}
void brake() {
digitalWrite(IN1, LOW); digitalWrite(IN2, LOW);
digitalWrite(IN3, LOW); digitalWrite(IN4, LOW);
analogWrite(ENA, 0); analogWrite(ENB, 0);
}Motor deadband: why 80 on the PWM pin does nothing
Two effects stack up. The L298N drops roughly 1.4–2 V across its output transistors, so the motor never sees the pack voltage. A TT motor also needs a minimum voltage to overcome gearbox friction. On a 7.4 V pack most TT motors begin to turn somewhere between 60 and 100 on the 0–255 PWM scale, and the number shifts with charge, load and floor surface.
Measure your own figure: run a test sketch that steps the PWM up from 0 in tens, with the robot lifted, and note the value where both wheels start cleanly. Use that as START_PWM for pivots. Turning below the deadband does not make the car go slower; it makes one wheel stall and the car lurch.
Which driver should you use?
| Driver | Voltage lost as heat | Current per channel | Pins needed | Pick it when |
|---|---|---|---|---|
| L298N module | About 1.4–2 V | 2 A rated, needs the heatsink | 2 PWM + 4 direction | You already own one and run a 7.4 V or higher pack |
| TB6612FNG module | About 0.5 V | 1.2 A continuous, 3.2 A peak per the datasheet | 2 PWM + 4 direction + standby | You run a 6 V pack, or want longer runtime from the same cells |
When it pairs but the car will not move
| Symptom | Likely cause | Check this first |
|---|---|---|
| Phone pairs, wheels never turn | TXD and RXD not crossed, or the app sends words | D3 must go to TXD and D2 to RXD. Watch the USB serial mirror: does a letter appear? |
| One wheel only | ENA or ENB jumper still fitted, or that motor’s pair is wired to one terminal | Remove both jumpers; confirm both motor pairs on OUT1/OUT2 and OUT3/OUT4 |
| Uno resets when the car starts | Shared 5 V rail sagging under motor current | Power logic from a buck converter; keep grounds common; add 100 µF across the 5 V rail |
| Module disconnects under load | 5 V on RXD, or the module fed from a noisy rail | Fit the divider; feed the module from the L298N 5 V output, not the motor terminals |
| Car keeps rolling after the app closes | No timeout in the parser | Keep the 600 ms failsafe; test it by switching the phone’s Bluetooth off mid-run |
| Commands arrive as garbage | Baud mismatch between the module’s data setting and the sketch | AT+UART=9600,0,0, then bt.begin(9600) |
| Steering works backwards | One motor pair reversed | Swap that motor’s two leads at the terminal block, not the code |
What it costs
| Item | Planning price (INR) |
|---|---|
| Arduino Uno clone, or Nano clone | ₹450–700 (Nano ₹300–450) |
| HC-05 module | ₹250–450 |
| L298N module | ₹150–250 |
| TT gear motors with wheels, pair | ₹250–350 |
| 2WD chassis with caster | ₹150–300 |
| 2S 18650 holder with two protected cells | ₹350–600 |
| Switch, wires, resistors, capacitor | ₹100–200 |
| 5 V 2 A buck converter | ₹200–300 |
| Total | ₹1,900–3,150 |
Runtime is the number students forget to plan. Two TT motors plus the Uno and the module usually average a few hundred milliamps, so a 2,000 mAh pack gives roughly an hour and a half to two hours with reserve. Put your own measured figures into the battery runtime calculator before you promise a demo length.
This build sheet is reviewed against datasheets and vendor listings; test your own robot before relying on it. Check wheel direction with the car lifted, then the failsafe with the phone out of range.
Common questions
Can I use an HC-06 instead of an HC-05?
Yes. The HC-06 is slave-only and usually cheaper, which is all a car needs. You lose the ability to change its role, and some clones accept fewer AT commands. The wiring, divider and sketch are the same.
Why does the car steer the wrong way?
One motor pair is reversed or the IN pins are swapped. Lift the car, send F, watch which wheel runs backwards, then swap that motor’s two leads at the terminal block. Changing the code to compensate hides a wiring fault you will meet again.
Can I control it from an iPhone?
Not with an HC-05. iOS does not expose the Bluetooth SPP profile the module uses, so Android or a laptop is the practical route. If your phone is an iPhone, build the ESP32 robot car instead; it serves a web page over Wi-Fi and needs no app.
Adding a camera stream to the same chassis turns it into a remote-view rover.
When Bluetooth range is not enough, control the robot over Wi-Fi from an app.
When Bluetooth range is not enough, move the same robot to Wi-Fi control on an ESP32.
Sources and further reading
Documents used to check the numbers on this page:
- Arduino Language Reference — pin functions, timings and library behaviour.
- Arduino Nano documentation — pinout and current limits.
- Arduino analog output (PWM) reference — how analogWrite() behaves.
Newer builds and guides
- Voice controlled robot car — Bluetooth voice commands for about ₹2,100.
- HC-05 Bluetooth module — Wiring, AT mode and fixing garbled serial output.


