Bluetooth RC car with an HC-05 on an Arduino Uno
An HC-05 and a 2WD car on an L298N: type F, B, L, R or S in the module’s box, as a phone app sends them, and the car drives; 0 to 9 set the speed. The whole build, an Arduino Uno and 5 more parts, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
Teaching? Assign it to your class in one click.
Intermediate About 30 minutes to build and run. Runs in your browser. Free, and no account needed.
The parts retail for about $47. On Mokxi they're free.
What you'll learn
- Serial text commands over Bluetooth
- Driving two DC motors with an H-bridge driver
- Controlling motor speed with PWM
- Dropping 5 V to 3.3 V with a resistor divider
- Software serial on spare pins
- Difficulty: Intermediate
- Time: About 30 minutes
- Board: Arduino Uno R3
- Parts: 7
How it works
The HC-05 module turns a Bluetooth link into plain serial text. The Uno listens on pins 12 and 11 with SoftwareSerial. Each letter that arrives is a driving command: F forward, B back, L spin left, R spin right, S stop.
The L298N motor driver does the heavy lifting. The Uno sets its direction pins and a speed on its enable pins, and the driver feeds the 7.4 V battery to the two motors. Pins 5 and 10 set the speed of each side.
The HC-05 wants 3.3 V on its RXD pin, but the Uno sends 5 V. A 1k and 2k resistor pair divides the voltage down.
Parts list
7 parts, plus the jumper wires. They retail for about $47; on Mokxi they're free.
- 1 × Arduino Uno R3In Starter Kit, Claw Machine Kit, Vending Machine Kit.
- 1 × Motor driver module, L298N
- 1 × Robot car chassis, 2WD
- 1 × Power, 7.4 V
- 1 × Bluetooth serial module, HC-05
- 1 × Resistor, 1k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
- 1 × Resistor, 2k ΩIn Starter Kit, Claw Machine Kit, Halloween Prop Kit.
Wiring table
Every connection, pin by pin, read from the circuit itself. Each row joins the pin on the left to the pin on the right, by a jumper wire or a breadboard strip.
| Part and pin | Connects to |
|---|---|
| Arduino Uno R3 pin GND | Ground |
| Bluetooth serial module, HC-05 pin TXD | Arduino Uno R3 pin 12 |
| Resistor, 1k Ω pin 2 | Arduino Uno R3 pin 11 |
| Motor driver module, L298N pin ENB | Arduino Uno R3 pin 10 |
| Motor driver module, L298N pin IN4 | Arduino Uno R3 pin 9 |
| Motor driver module, L298N pin IN3 | Arduino Uno R3 pin 8 |
| Motor driver module, L298N pin IN2 | Arduino Uno R3 pin 7 |
| Motor driver module, L298N pin IN1 | Arduino Uno R3 pin 6 |
| Motor driver module, L298N pin ENA | Arduino Uno R3 pin 5 |
| Bluetooth serial module, HC-05 pin VCC | Arduino Uno R3 pin 5V |
| Robot car chassis, 2WD pin L+ | Motor driver module, L298N pin OUT1 |
| Robot car chassis, 2WD pin L- | Motor driver module, L298N pin OUT2 |
| Motor driver module, L298N pin 12V | 7.4 V |
| Motor driver module, L298N pin GND | Ground |
| Robot car chassis, 2WD pin R+ | Motor driver module, L298N pin OUT3 |
| Robot car chassis, 2WD pin R- | Motor driver module, L298N pin OUT4 |
| Bluetooth serial module, HC-05 pin GND | Ground |
| Resistor, 1k Ω pin 1 | Bluetooth serial module, HC-05 pin RXD |
| Resistor, 2k Ω pin 2 | Bluetooth serial module, HC-05 pin RXD |
| Resistor, 2k Ω pin 1 | Ground |
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Bluetooth RC car: an HC-05, an L298N and a 2WD chassis, driven by the
// letters the phone apps send.
//
// HC-05 TXD -> pin 12 (SoftwareSerial RX)
// RXD <- pin 11 through 1k, with 2k from RXD to GND (5 V down to 3.3 V)
// VCC 5V, GND
// L298N ENA pin 5, IN1 pin 6, IN2 pin 7 left motor (ENA jumper off)
// ENB pin 10, IN3 pin 8, IN4 pin 9 right motor (ENB jumper off)
// 12V from the 7.4 V pack, GND to the Uno's GND
//
// The "Arduino Bluetooth RC Car" style apps send one letter while a button
// is held, and the same letters are what most car sketches expect:
//
// F forward B back L spin left R spin right S stop
// G forward left I forward right H back left J back right
// 0 to 9 and q the speed slider: 0 is stopped, q is full speed
//
// In Mokxi the far end of the HC-05 is the module's own box in the serial
// monitor, so type a letter there (F, then Enter) and the car drives across
// the floor drawn in the chassis. There is no radio here: what the board
// sees is the UART, which is all it ever sees of a paired phone.
//
// The HC-05's RXD is a 3.3 V input. The Uno's pin 11 is 5 V, so the 1k and 2k
// divider brings it down to 3.3 V. Its TXD is 3.3 V, which the Uno reads as
// HIGH without help.
//
// A real app keeps sending the letter while the button is held and sends S
// when it is let go. If the link drops mid-drive the car keeps going, so the
// STATE pin or a timeout is worth adding before it goes near the stairs.
#include <SoftwareSerial.h>
SoftwareSerial bluetooth(12, 11); // RX, TX
const int ENA = 5;
const int IN1 = 6;
const int IN2 = 7;
const int ENB = 10;
const int IN3 = 8;
const int IN4 = 9;
int speed = 200; // 0 to 255, set by the app's slider
char moving = 'S';
// One side of the car, as a signed speed from -255 to 255. Zero brakes.
void side(int en, int a, int b, int s) {
digitalWrite(a, s > 0 ? HIGH : LOW);
digitalWrite(b, s < 0 ? HIGH : LOW);
if (s < 0) s = -s;
analogWrite(en, s == 0 ? 255 : (s > 255 ? 255 : s));
}
void drive(int left, int right) {
side(ENA, IN1, IN2, left);
side(ENB, IN3, IN4, right);
}
// The inner wheel of a curve turns at a third of the speed.
void move(char c) {
int s = speed;
switch (c) {
case 'F': drive(s, s); break;
case 'B': drive(-s, -s); break;
case 'L': drive(-s, s); break;
case 'R': drive(s, -s); break;
case 'G': drive(s / 3, s); break;
case 'I': drive(s, s / 3); break;
case 'H': drive(-s / 3, -s); break;
case 'J': drive(-s, -s / 3); break;
default: drive(0, 0); c = 'S'; break;
}
moving = c;
}
void setup() {
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(IN3, OUTPUT);
pinMode(IN4, OUTPUT);
drive(0, 0);
Serial.begin(9600);
bluetooth.begin(9600); // the HC-05's factory baud
Serial.println("Bluetooth car ready: F B L R G I H J S, 0-9 and q for speed");
}
void loop() {
if (!bluetooth.available()) return;
char c = bluetooth.read();
if (c == '\r' || c == '\n') return;
if (c >= '0' && c <= '9') {
speed = (c - '0') * 25; // 0, 25, 50 ... 225
} else if (c == 'q') {
speed = 255;
} else {
move(c);
Serial.print("drive ");
Serial.print(moving);
Serial.print(" at ");
Serial.println(speed);
return;
}
// A new speed applies to whatever the car is doing now.
move(moving);
Serial.print("speed ");
Serial.println(speed);
bluetooth.print("speed ");
bluetooth.println(speed);
}
Code walkthrough
The key functions of the sketch, and what each one does.
side sketch.ino
Runs one side of the car. The sign of the speed picks the direction by setting one of the two input pins HIGH. The size of the speed goes to the enable pin with analogWrite. A speed of zero sets both inputs low and the enable fully on, which brakes the motor.
// One side of the car, as a signed speed from -255 to 255. Zero brakes.
void side(int en, int a, int b, int s) {
digitalWrite(a, s > 0 ? HIGH : LOW);
digitalWrite(b, s < 0 ? HIGH : LOW);
if (s < 0) s = -s;
analogWrite(en, s == 0 ? 255 : (s > 255 ? 255 : s));
}move sketch.ino
Turns a letter into a pair of wheel speeds. F and B drive both sides together. L and R spin the sides in opposite directions. G, I, H and J curve by running the inner wheel at a third of the speed. Any other letter stops the car.
// The inner wheel of a curve turns at a third of the speed.
void move(char c) {
int s = speed;
switch (c) {
case 'F': drive(s, s); break;
case 'B': drive(-s, -s); break;
case 'L': drive(-s, s); break;
case 'R': drive(s, -s); break;
case 'G': drive(s / 3, s); break;
case 'I': drive(s, s / 3); break;
case 'H': drive(-s / 3, -s); break;
case 'J': drive(-s, -s / 3); break;
default: drive(0, 0); c = 'S'; break;
}
moving = c;
}loop sketch.ino
Waits for a character from the Bluetooth module and skips line endings. Digits 0 to 9 set the speed in steps of 25, and q sets full speed. Any other letter goes to move. It prints what it did to the serial monitor.
void loop() {
if (!bluetooth.available()) return;
char c = bluetooth.read();
if (c == '\r' || c == '\n') return;
if (c >= '0' && c <= '9') {
speed = (c - '0') * 25; // 0, 25, 50 ... 225
} else if (c == 'q') {
speed = 255;
} else {
move(c);
Serial.print("drive ");
Serial.print(moving);
Serial.print(" at ");
Serial.println(speed);
return;
}
// A new speed applies to whatever the car is doing now.
move(moving);
Serial.print("speed ");
Serial.println(speed);
bluetooth.print("speed ");
bluetooth.println(speed);
}Troubleshooting
The usual mistakes with this circuit, and the fix for each.
- The car does not move at all This project
Make sure the L298N ground is joined to the Uno ground, and that the speed is not 0. Typing a digit like 8 first sets a speed. - Letters sent to the module do nothing This project
The HC-05 TXD must go to pin 12, and the baud rate inbluetooth.beginmust match the module's 9600. - One side turns the wrong way This project
Swap that motor's two wires on the driver outputs, or swap its two input pins. - The motors do not run or run weakly. Motor driver module
Check the power jumpers and supply. Motor voltage goes on the +12V terminal, and ground must join the Arduino's ground. The driver drops about 2 V, so use a supply above the motor voltage. - Speed control with analogWrite does nothing. Motor driver module
Remove the ENA and ENB jumpers and connect those pins to PWM pins. With the jumpers on, the channel is always at full speed. - The car drives in circles or goes backwards. Robot car chassis
Motor polarity decides the direction. Swap the two wires on a motor that spins the wrong way. For a straight run, the motors are never identical, so trim the speed on one side. - The motors do not run, or the board resets when they start. Robot car chassis
Motors draw far more current than a pin can give. Use a motor driver such as an L298N or L293D. Power the motors from a separate battery pack and join its ground with the Arduino ground. - Garbled text or nothing arrives. Bluetooth serial module
Cross the wires: module TX to Arduino RX, module RX to Arduino TX. Match the baud rate. The default data rate is 9600 and the pairing code is usually 1234.
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.