Building the Bluetooth-Controlled Robot Car OmObi with Arduino, HC-05, and Adafruit Motor Shield

This post contains affiliate links, marked with *. As an Amazon Associate I earn from qualifying purchases, at no extra cost to you. It helps keep the tutorials free.

The video loads from YouTube (Google), which may set cookies. Privacy policy

Plexiglas version: an earlier build of this car used a cut Plexiglas chassis. It is described in Building a Bluetooth-Controlled Robot Car with Arduino, HC-05 and Adafruit Motor Shield. Code and app are identical.

Quick summary

OmObi is a four-wheel-drive robot car (all four wheels have a motor) on a 3D printed chassis. An Android app made with MIT App Inventor sends single letters (F, B, L, R, S) over Bluetooth to an HC-05 module. The Arduino Uno reads each letter and drives four DC motors through an Adafruit Motor Shield V1.2.

It suits a first robot build: the motor shield plugs onto the Arduino, and the sketch (Arduino’s name for a program) is under 70 lines. For OmObi I replaced the drilled Plexiglas plate of my first build with a chassis that I designed in Autodesk Inventor (a 3D design program) and 3D printed. It has slots for an ultrasonic sensor, which I closed for this build, and an opening for the USB cable. If Bluetooth with Arduino is new to you, read the HC-05/HC-06 Arduino Bluetooth tutorial first. If the Arduino IDE is new to you, start with Arduino Programming Basics.

OmObi robot car on its 3D printed chassis with four yellow DC gear motors, Arduino Uno, Adafruit Motor Shield V1.2 and HC-05 module

How It Works

Four parts work together: the Android phone, the HC-05 Bluetooth module, the Arduino Uno, and the Adafruit Motor Shield with four DC motors.

The Android phone runs an app built with MIT App Inventor. When you press a direction button, the app sends one letter over Bluetooth: F (forward), B (backward), L (left) or R (right). When you release the button, it sends S (stop).

The HC-05 Bluetooth module receives the letter and passes it to the Arduino over a serial connection, which sends data one bit after another. It connects to the Arduino’s pin 0 (RX, receive) and pin 1 (TX, transmit). It works like a wireless serial cable between the phone and the Arduino.

The Arduino Uno reads the letter from the serial buffer (a small memory that holds received bytes until the sketch reads them). It then calls the matching movement function: forward, backward, turnLeft, turnRight or stop.

The Adafruit Motor Shield V1.2 plugs onto the Arduino. Two L293D H-bridge chips on the shield switch the motor current, so the Arduino pins only send control signals. An H-bridge is a circuit of four switches that can send current through a motor in either direction. Each chip drives two of the four motor terminals M1 to M4. In the sketch, the AFMotor library sets each motor’s direction (FORWARD, BACKWARD, RELEASE) and speed (0 to 255). For a turn, one side runs forward while the other runs backward, so the car turns on the spot (skid steering, the way a tank turns).

Bill of Materials

ComponentQtyRequired?PurposeNotes
Arduino Uno1RequiredMain microcontrollerAny Uno-compatible board works
Adafruit Motor Shield V1.21RequiredDrives four DC motorsPlugs onto the Arduino
HC-05 Bluetooth module1RequiredWireless serial link to the phoneHC-06 also works (see FAQ)
DC gear motors4RequiredDrive the four wheelsYellow TT gear motors (TT is the usual name for this plastic gearbox motor type), 3 to 6 V
Wheels4RequiredFit on the motor shaftsFor TT motor shafts
3D printed chassis1RequiredBase framePlexiglas or acrylic sheet also works
3D printed motor mounts4OptionalFix the motors to the chassisNot in the OmObi STL set; use the motor_holder files from the Advanced Robot Car package, or metal L-brackets
3D printed Arduino holder1OptionalFix the Arduino to the chassisDouble-sided tape or standoffs (spacer posts) also work
7.4 V 2S LiPo battery, 5300 mAh1RequiredPowers motors and ArduinoLithium polymer pack, details and other packs in Step 4
LiPo balance charger1RequiredCharges the LiPo safelySee Step 4
On/off switch1OptionalTurns the car on and offIn the plus lead from the battery to the shield (Step 4)
Jumper wiresSeveralRequiredConnect the HC-05 to the ArduinoWith pin headers for the HC-05
Screws, nuts, threaded inserts, standoffsAssortedRequiredMechanical assemblyM3 (3 mm thread) is common; threaded inserts: see Step 1
Android smartphone1RequiredRuns the control appNeeds Bluetooth Classic, the standard Bluetooth mode the HC-05 uses; iPhones need a different Bluetooth module (see FAQ)

Step 1: Mounting the Motors and Wheels

I first heated threaded inserts (small metal sleeves with an inner thread) and pressed them into the printed parts. The screws then go straight into metal threads and hold more securely. Fix each motor to its printed mount with screws and nuts. Mount the motors with the shafts pointing outward. The left and right motors sit mirrored, so the spin direction is set later by the wire polarity at the shield, not by how you mount them. Without printed mounts, metal L-brackets work, or hot glue as a temporary fix.

Press the wheels onto the motor shafts. Check that they sit firmly and spin without rubbing on the chassis or on each other.

Step 2: Mounting the Arduino and Motor Shield

Attach the 3D printed Arduino holder to the center or rear of the chassis and leave room at the front or sides for the battery pack. I designed the chassis for an Arduino Uno or an Arduino Mega. Without the holder, use standoffs with M3 screws through the Arduino mounting holes, or attach the board with double-sided foam tape.

Set the Arduino Uno into the holder and fix it with screws. Place the motor shield on top of the Arduino and press it down gently until all header pins (the rows of pins under the shield) are aligned and fully seated in the Arduino’s sockets.

Step 3: Wiring

Use the wiring diagram below as a reference. It draws a 9 V (PP3) battery as the power source; do not copy that part (see Step 4). On the car, the 2S LiPo sits on the same power terminal.

Wiring diagram: four DC motors on motor shield terminals M1 to M4, HC-05 on Arduino pins 0 and 1, and a 9 V battery with an on/off switch in the plus lead to the shield's power terminal

Motor Connections

MotorShield terminalPosition on the car
Motor 1M1Left side (front or rear)
Motor 2M2Left side (front or rear)
Motor 3M3Right side (front or rear)
Motor 4M4Right side (front or rear)

Insert both wires of each motor into its screw terminal and tighten the screws. The polarity does not matter yet; you fix the spin direction during the first test.

HC-05 Connections

HC-05 pinArduino pinNotes
VCC5 VPowers the module
GNDGNDCommon ground
TXDRX (pin 0)HC-05 sends data to the Arduino
RXDTX (pin 1)Arduino sends data to the HC-05

The shield covers the Arduino’s pin sockets, so I soldered the TXD and RXD wires to pins 0 and 1 on the shield. The RXD input of the HC-05 uses 3.3 V logic, while the Uno’s TX pin outputs 5 V. In my build, RXD goes straight to pin 1 as in the wiring diagram, and the car worked that way. A voltage divider is the safer option: two resistors, 1 kΩ from Arduino TX to RXD and 2 kΩ from RXD to GND, lower the 5 V signal to about 3.3 V.

Hardware Serial (Pins 0 and 1) and Upload Conflicts

Warning: The HC-05 sits on pins 0 (RX) and 1 (TX), the Uno’s built-in serial interface, called hardware serial. The USB connection uses the same two pins for uploading sketches and for the Serial Monitor (the Arduino IDE window that shows text the board sends over USB). Disconnect the HC-05 (at least its TXD and RXD wires) every time you upload code. If it stays connected, the upload fails because the module interferes with the USB serial data. After the upload, reconnect the HC-05 and reset the Arduino.

You also cannot use the Serial Monitor while the HC-05 is on pins 0 and 1. If you need both at the same time, use the SoftwareSerial library (it creates a serial port on other pins) on two pins the shield leaves free, for example A0 (RX) and A1 (TX). With DC motors, the shield uses pins 3, 4, 5, 6, 7, 8, 11 and 12; pins 9 and 10 go to the servo connectors.

Step 4: Power

OmObi runs on a 7.4 V 2S LiPo with 5300 mAh, the same pack as on OmObiArm: two lithium polymer cells in series, 3.7 V nominal each. Connect it to the EXT_PWR screw terminal on the motor shield, the terminal for the motor supply. Check plus and minus before you tighten the screws. Because the shield plugs onto the Arduino, both boards share ground automatically. Do not use a 9 V (PP3) battery: it cannot deliver the current four DC motors draw under load.

Charge the pack only with a LiPo balance charger. It charges each cell to the same voltage through the pack’s small balance plug. Never leave the pack charging unattended. Stop driving when the pack drops to about 6.6 V (3.3 V per cell); below 3.0 V per cell the cells can be damaged permanently. A LiPo voltage alarm on the balance plug warns you in time. If you store the pack for more than a few days, charge or discharge it to about 7.6 V (3.8 V per cell), the storage voltage; most balance chargers have a storage mode for this.

The shield has a PWR jumper, a small plug that connects two pins. With the jumper fitted, the battery also feeds the Arduino through its VIN pin, the input for an external supply. The Arduino’s onboard voltage regulator turns the VIN voltage into a steady 5 V and needs about 7 V at VIN for that. The 2S LiPo gives 8.4 V when full and 7.4 V nominal, inside the Uno’s recommended 7 to 12 V. Fit the jumper: once the USB cable is unplugged, the battery is the Arduino’s only supply. When the pack is nearly empty, the voltage can drop below 7 V as the motors start, and the Arduino resets: time to recharge.

The TT motors are rated 3 to 6 V. Each L293D chip on the shield drops about 2.6 V, so even a full pack puts at most about 5.8 V on the motors at full speed.

Other packs: a 2S Li-ion pack (7.4 V nominal, 8.4 V fully charged) with a BMS works the same way. A BMS (battery management system) is a small board that protects the cells from overcharge and deep discharge. A 4 × AA pack gives 6 V with fresh alkaline cells. That drives the motors, but it is too low for the regulator at VIN: remove the PWR jumper and power the Arduino separately over USB or its DC jack. Four rechargeable NiMH (nickel-metal hydride) AA cells give only 4.8 V (4 × 1.2 V): after the L293D drop, about 2.2 V reach the motors, below their 3 V minimum.

OmObi has an on/off switch in the plus lead from the battery to EXT_PWR. It sits in the upper plate, within reach even while the car drives.

Step 5: Programming the Arduino

Install the library first. In the Arduino IDE, open Sketch > Include Library > Manage Libraries, search for “Adafruit Motor Shield library” (the V1 library with AFMotor.h) and click Install.

The sketch below receives single letters from the HC-05 over the hardware serial port and drives the four motors. To upload, disconnect the HC-05 from pins 0 and 1. In the Arduino IDE, select Tools > Board > Arduino Uno and your board’s port under Tools > Port, then click Upload. When the IDE shows “Done uploading”, reconnect the HC-05 and press the reset button.

GitHub Repository: App and Code

/**
 * Author: Omar Draidrya
 * Date: 2024/07/03
 * Bluetooth-controlled 4WD robot car (OmObi) using Adafruit Motor Shield V1.2 and HC-05.
 * Receives single-character commands (F/B/L/R/S) via serial from the HC-05
 * Bluetooth module and drives four DC motors on terminals M1-M4 accordingly.
 */
#include <AFMotor.h>
AF_DCMotor motor1(1);  // Create motor #1 using M1 connector
AF_DCMotor motor2(2);  // Create motor #2 using M2 connector
AF_DCMotor motor3(3);  // Create motor #3 using M3 connector
AF_DCMotor motor4(4);  // Create motor #4 using M4 connector

char command;

void setup() {
  Serial.begin(9600);     // Start serial communication at 9600 baud rate
  motor1.setSpeed(255);   // Set initial motor speeds
  motor2.setSpeed(255);
  motor3.setSpeed(255);
  motor4.setSpeed(255);
}

void loop() {
  if (Serial.available() > 0) {
    command = Serial.read();  // Read the incoming command
    if (command == 'F') { forward(); }
    else if (command == 'B') { backward(); }
    else if (command == 'L') { turnLeft(); }
    else if (command == 'R') { turnRight(); }
    else if (command == 'S') { stop(); }
  }
}

void forward() {
  motor1.run(FORWARD);
  motor2.run(FORWARD);
  motor3.run(FORWARD);
  motor4.run(FORWARD);
}

void backward() {
  motor1.run(BACKWARD);
  motor2.run(BACKWARD);
  motor3.run(BACKWARD);
  motor4.run(BACKWARD);
}

void turnLeft() {
  motor1.run(BACKWARD);
  motor2.run(BACKWARD);
  motor3.run(FORWARD);
  motor4.run(FORWARD);
}

void turnRight() {
  motor1.run(FORWARD);
  motor2.run(FORWARD);
  motor3.run(BACKWARD);
  motor4.run(BACKWARD);
}

void stop() {
  motor1.run(RELEASE);
  motor2.run(RELEASE);
  motor3.run(RELEASE);
  motor4.run(RELEASE);
}

AFMotor Library and Motor Setup

The sketch starts by including AFMotor.h, the Adafruit Motor Shield library. Four motor objects are created, motor1(1) to motor4(4), one for each terminal M1 to M4. During setup(), the serial port starts at 9600 baud (9600 bits per second), the HC-05’s default speed. Both sides must use the same baud rate, or the Arduino receives wrong characters. All four motors are set to full speed (255 of 255).

Command Protocol

The app sends one ASCII character (one letter stored as one byte), and the Arduino reads it from the serial buffer in loop(). There are five commands: F (forward), B (backward), L (turn left), R (turn right) and S (stop), so loop() only has to compare a single character.

Movement Functions

forward() runs all four motors FORWARD, and backward() runs them BACKWARD. turnLeft() runs the left motors (M1, M2) backward and the right motors (M3, M4) forward, so the car turns left on the spot. turnRight() does the opposite: left motors forward, right motors backward. stop() calls RELEASE on all motors, which cuts the motor current; the car rolls a short distance and stops.

Changing the Speed

The speed is set in setup() with setSpeed(255). The value ranges from 0 to 255 and sets the PWM duty cycle. PWM (pulse-width modulation) switches the motor voltage on and off many times per second; the duty cycle is the share of time it is on. 150 means about 59 %. With the 2S LiPo, the motors get up to about 5.8 V at 255 (Step 4) and about 59 % of that at 150, roughly 3.4 V on average. The real speed does not scale in proportion, and at low values the motors may not start at all. Lower the value to slow the car down. You can also set different speeds for turning and straight driving by calling setSpeed() inside the movement functions.

Step 6: The Android App

The app is built with MIT App Inventor, a free, browser-based visual programming tool: you assemble the app from blocks instead of writing Java or Kotlin code. It connects to the HC-05 and sends the letters from Step 5.

Create a Project

Go to MIT App Inventor and log in with your Google account. Click “Start New Project” and name it “RobotCarControl”.

Design the User Interface

In the Designer view, add these components to the screen:

  • ListPicker: set its text to “Select Bluetooth Device”. It lets the user choose a paired Bluetooth device.
  • Label (Label1): shows the connection status, “Connected” or “Not Connected”.
  • BluetoothClient: from the Connectivity palette. It is a non-visible component that handles the Bluetooth communication.
  • Four Buttons: ButtonForward, ButtonBackward, ButtonLeft and ButtonRight. Set their text and colors so the directions are clear.

Program the Blocks

Switch to the Blocks view and add this logic:

when ListPicker1.BeforePicking
  set ListPicker1.Elements to BluetoothClient1.AddressesAndNames

when ListPicker1.AfterPicking
  if call BluetoothClient1.Connect address ListPicker1.Selection
    then set Label1.Text to "Connected"
    else set Label1.Text to "Not Connected"

when ButtonForward.TouchDown
  if BluetoothClient1.IsConnected
    call BluetoothClient1.SendText text "F"

when ButtonBackward.TouchDown
  if BluetoothClient1.IsConnected
    call BluetoothClient1.SendText text "B"

when ButtonLeft.TouchDown
  if BluetoothClient1.IsConnected
    call BluetoothClient1.SendText text "L"

when ButtonRight.TouchDown
  if BluetoothClient1.IsConnected
    call BluetoothClient1.SendText text "R"

when ButtonForward.TouchUp
  if BluetoothClient1.IsConnected
    call BluetoothClient1.SendText text "S"

when ButtonBackward.TouchUp
  if BluetoothClient1.IsConnected
    call BluetoothClient1.SendText text "S"

when ButtonLeft.TouchUp
  if BluetoothClient1.IsConnected
    call BluetoothClient1.SendText text "S"

when ButtonRight.TouchUp
  if BluetoothClient1.IsConnected
    call BluetoothClient1.SendText text "S"
MIT App Inventor blocks editor screenshot showing the Bluetooth connection logic, ListPicker event handlers, and TouchDown/TouchUp blocks for the four directional buttons that send F, B, L, R, and S commands

What each block does:

  • ListPicker1.BeforePicking: fills the list with all paired devices (BluetoothClient1.AddressesAndNames).
  • ListPicker1.AfterPicking: connects to the selected device and sets Label1 to “Connected” or “Not Connected”.
  • TouchDown: sends F, B, L or R while a button is pressed, if the connection is active.
  • TouchUp: sends S when the finger leaves the button.

The robot moves only while you hold a button. On release, the app sends S, the motors switch off (RELEASE) and the car rolls a short distance before it stops.

Install the App

In App Inventor, choose Build > Android App (.apk). An .apk file is the installable package of an Android app. Scan the QR code with your phone, or download the .apk and copy it to the phone. I copied the .apk file to my Android tablet and installed it there. Android asks you to allow installs from this source; confirm once. If Android asks for the “Nearby devices” permission, allow it; without it, the app cannot list or connect to the HC-05. To test without installing, run the project live with the MIT AI2 Companion app.

Step 7: Connecting and Testing

Pair the HC-05

Turn on Bluetooth on your phone and search for new devices. The module appears as “HC-05”. Pair with the PIN 1234 (0000 on some modules). You pair once; the phone remembers the device.

First Test

Put the car on a box or a stack of books so the wheels spin freely. Set all four setSpeed() values in setup() to 120 and upload the sketch with the HC-05 disconnected. Reconnect the HC-05 and open the RobotCarControl app. Tap “Select Bluetooth Device” and choose the HC-05 from the list; the status label (Label1) changes to “Connected”. Press the forward, backward, left and right buttons once each and check that all four wheels turn the expected way. If not, see Wrong Direction under Troubleshooting. Then put the car on the floor. Hold a button to drive, release it to stop.

Troubleshooting

Upload Fails

If the Arduino IDE reports “avrdude: stk500_getsync(): not in sync”, the HC-05 is most likely still connected to pins 0 and 1. Disconnect its TXD and RXD wires, upload, then reconnect them. This caused me problems on the first, Plexiglas version of this car.

Connected but the Car Does Not Move

Check that HC-05 TXD goes to Arduino RX (pin 0) and RXD to TX (pin 1). The sketch must use 9600 baud, the HC-05 default. A wrong baud rate does not cause a delay: the Arduino receives wrong characters and the car does not react at all. Also check that the shield is seated on the Arduino and that the LiPo is charged, connected and switched on.

Wrong Direction

If one wheel turns backward on F, swap that motor’s two wires at its screw terminal. If F drives the whole car backward, swap the wires of all four motors, or swap FORWARD and BACKWARD in the code. If L turns the car right, swap the left and right motor pairs (M1/M2 with M3/M4), or swap the contents of turnLeft() and turnRight() in the code.

One Side of the Car Does Not Move

Check the wiring on that side: the motor wires must sit firmly in the screw terminals and the screws must be tight. To find the faulty motor, put // in front of the motor2, motor3 and motor4 lines in forward(), upload the sketch and send F: only the M1 motor should turn. Repeat for each motor. If a motor turns on another terminal, the first terminal is faulty; if it turns on none, check the motor and its wires.

App Connects but Buttons Do Nothing

In the Blocks view, check that every SendText block uses BluetoothClient1 and sends an uppercase letter; the sketch ignores lowercase f, b, l, r and s. Label1 must show “Connected” after you select the HC-05 in the app.

Weak Motors or Arduino Resets

If the motors are weak, the car behaves erratically or the Arduino resets when the motors start, check the battery first. When all four motors start, the current peak pulls the voltage down; below about 7 V at VIN the Arduino can reset, and low voltage also slows the motors. Measure the LiPo with a multimeter: at about 6.6 V (3.3 V per cell), stop and recharge it. With a 4 × AA pack, use fresh cells. Check all power wires for loose contacts. If resets continue with a charged pack, remove the PWR jumper and power the Arduino separately over USB.

Robot Keeps Moving After Button Release

The stop command S is not sent or not received when you lift your finger. In the app, every button needs both a TouchDown block (movement letter) and a TouchUp block (S). Without the TouchUp blocks, the Arduino never receives the stop command.

Frequently Asked Questions

Can I use an HC-06 instead of the HC-05?

Yes. The HC-06 can only accept a connection (slave mode); it cannot start one. That is enough here, because the phone always starts the connection. Wiring and baud rate are the same.

Can I control OmObi from an iPhone?

Not directly. The HC-05 uses SPP (Serial Port Profile, the Bluetooth Classic mode that works like a serial cable), which iOS does not open to third-party apps. For an iPhone, replace the HC-05 with a BLE (Bluetooth Low Energy) module such as the HM-10, or use an ESP32 with BLE, and adapt the app. The ESP32 is a microcontroller board with built-in Bluetooth.

Can I use the Adafruit Motor Shield V2?

Yes, with a different library. The V2 shield uses the Adafruit_MotorShield library instead of AFMotor.h and talks to the Arduino over I2C (a bus that needs only two wires). The wiring and motor terminal layout are similar, but you have to rewrite the motor initialization and control parts of the code for the V2 library.

Next Projects

The next step is OmObiArm: it mounts a robotic arm with six degrees of freedom (6-DOF: five joints plus the gripper) on this same base. Related builds:

If you have questions about your build, leave a comment below.

3D Print Files (STL)

OmArTronics Shop: 3D print files (STL) for OmObi. The code, wiring diagram and parts list are in this tutorial.

1 thought on “Building the Bluetooth-Controlled Robot Car OmObi with Arduino, HC-05, and Adafruit Motor Shield”

Leave a Comment