Built-in project · Arduino Uno R3

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.

The circuit runs here once the page has loaded.

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.

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 pinConnects to
Arduino Uno R3 pin GNDGround
Bluetooth serial module, HC-05 pin TXDArduino Uno R3 pin 12
Resistor, 1k Ω pin 2Arduino Uno R3 pin 11
Motor driver module, L298N pin ENBArduino Uno R3 pin 10
Motor driver module, L298N pin IN4Arduino Uno R3 pin 9
Motor driver module, L298N pin IN3Arduino Uno R3 pin 8
Motor driver module, L298N pin IN2Arduino Uno R3 pin 7
Motor driver module, L298N pin IN1Arduino Uno R3 pin 6
Motor driver module, L298N pin ENAArduino Uno R3 pin 5
Bluetooth serial module, HC-05 pin VCCArduino 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 12V7.4 V
Motor driver module, L298N pin GNDGround
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 GNDGround
Resistor, 1k Ω pin 1Bluetooth serial module, HC-05 pin RXD
Resistor, 2k Ω pin 2Bluetooth serial module, HC-05 pin RXD
Resistor, 2k Ω pin 1Ground

The code

The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.

sketch.ino · Arduino Uno R3
// 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 in bluetooth.begin must 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.