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.
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.

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
| Component | Qty | Required? | Purpose | Notes |
|---|---|---|---|---|
| Arduino Uno | 1 | Required | Main microcontroller | Any Uno-compatible board works |
| Adafruit Motor Shield V1.2 | 1 | Required | Drives four DC motors | Plugs onto the Arduino |
| HC-05 Bluetooth module | 1 | Required | Wireless serial link to the phone | HC-06 also works (see FAQ) |
| DC gear motors | 4 | Required | Drive the four wheels | Yellow TT gear motors (TT is the usual name for this plastic gearbox motor type), 3 to 6 V |
| Wheels | 4 | Required | Fit on the motor shafts | For TT motor shafts |
| 3D printed chassis | 1 | Required | Base frame | Plexiglas or acrylic sheet also works |
| 3D printed motor mounts | 4 | Optional | Fix the motors to the chassis | Not in the OmObi STL set; use the motor_holder files from the Advanced Robot Car package, or metal L-brackets |
| 3D printed Arduino holder | 1 | Optional | Fix the Arduino to the chassis | Double-sided tape or standoffs (spacer posts) also work |
| 7.4 V 2S LiPo battery, 5300 mAh | 1 | Required | Powers motors and Arduino | Lithium polymer pack, details and other packs in Step 4 |
| LiPo balance charger | 1 | Required | Charges the LiPo safely | See Step 4 |
| On/off switch | 1 | Optional | Turns the car on and off | In the plus lead from the battery to the shield (Step 4) |
| Jumper wires | Several | Required | Connect the HC-05 to the Arduino | With pin headers for the HC-05 |
| Screws, nuts, threaded inserts, standoffs | Assorted | Required | Mechanical assembly | M3 (3 mm thread) is common; threaded inserts: see Step 1 |
| Android smartphone | 1 | Required | Runs the control app | Needs 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.

Motor Connections
| Motor | Shield terminal | Position on the car |
|---|---|---|
| Motor 1 | M1 | Left side (front or rear) |
| Motor 2 | M2 | Left side (front or rear) |
| Motor 3 | M3 | Right side (front or rear) |
| Motor 4 | M4 | Right 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 pin | Arduino pin | Notes |
|---|---|---|
| VCC | 5 V | Powers the module |
| GND | GND | Common ground |
| TXD | RX (pin 0) | HC-05 sends data to the Arduino |
| RXD | TX (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"

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
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.
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.
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:
- Building an Obstacle-Avoiding Robot Car with Arduino: an ultrasonic sensor on a servo and autonomous navigation, on a two-wheel car with the Arduino Motor Shield Rev3.
- Advanced Autonomous and Bluetooth Robot Car with Arduino Mega: an Arduino Mega with four ultrasonic sensors and both autonomous and Bluetooth modes.
- Building a Line Following Robot with KY-033 Sensors: a car that follows a line with infrared sensors.
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”