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.
Update: this car has a 3D printed successor, OmObi. OmObi uses the same code and app; its STL files are in the shop. This post keeps the original build on a plate cut from an acrylic (Plexiglas) sheet.
Quick summary
This four-wheel-drive car takes commands from an Android app over Bluetooth. It runs on an Arduino Uno with an Adafruit Motor Shield V1.2 and an HC-05 Bluetooth module. You cut the acrylic plate, wire the motors and the HC-05, upload the sketch (the Arduino program) and build the control app in MIT App Inventor.
I built this car as the follow-up project to my HC-05/HC-06 Bluetooth tutorial. If you are new to Arduino, start with my Arduino programming basics guide.
How the car works
The system has four parts: the Android phone, the HC-05 Bluetooth module, the Arduino Uno and the Adafruit Motor Shield with four DC motors.
The Android phone runs a control app built with MIT App Inventor, a free browser tool that builds Android apps from drag-and-drop blocks. When you press a direction button, the app sends F, B, L or R over Bluetooth. When you release it, the app sends S (stop).
The HC-05 receives the command and passes it to the Arduino over a serial connection: the bits travel one after another on two wires, RX (receive, pin 0) and TX (transmit, pin 1). The HC-05 acts as a wireless serial bridge between the phone and the Arduino. My HC-05/HC-06 Arduino Bluetooth tutorial explains the module in detail.
The Arduino reads the character from the serial buffer, a small memory area where received bytes wait until the sketch reads them. It then calls the matching movement function: forward, backward, turnLeft, turnRight or stop.
The Adafruit Motor Shield V1.2 is a shield, an add-on board that plugs onto the Arduino’s pin headers. The Arduino pins cannot supply the current a motor needs. The shield’s two L293D motor driver chips switch the battery current to the four motors, up to 0.6 A per motor. Each motor connects to one of the motor terminals M1 to M4, screw terminals that clamp the motor wires. The sketch controls the shield with the AFMotor library, a package of ready-made code that a sketch can include. The library sets the direction (FORWARD, BACKWARD, RELEASE) and the speed (0 to 255). For turning, one side runs forward while the other runs backward, a tank-style turn. For motor driver principles, see my L298N motor driver tutorial.
Components (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 | Stacks on top of the Arduino. Use V1.2 or an L293D clone of it: the newer V2 shield needs the Adafruit_MotorShield library and different code |
| HC-05 Bluetooth module | 1 | Required | Wireless serial link to the phone | HC-06 also works: it can only accept incoming connections (slave mode), which is all this project needs |
| DC motors with gearboxes | 4 | Required | Drive the four wheels | Typically 3 to 6 V geared motors |
| Wheels | 4 | Required | Fit on the motor shafts | Match them to your gearbox shaft size |
| Acrylic plate | 1 | Required | Carries the motors, electronics and battery | Cut to size; a similar sheet material also works |
| 3D printed motor mounts | 4 | Optional | Hold the motors on the acrylic plate | Metal L-brackets also work |
| 3D printed Arduino holder | 1 | Optional | Holds the Arduino on the acrylic plate | Double-sided foam tape or standoffs (threaded spacers between board and acrylic plate) also work |
| Battery pack, 4 × AA or similar | 1 | Required | Powers the motors (and the Arduino via the PWR jumper, see Step 4) | 6 V; use fresh batteries |
| Resistors, 1 kΩ and 2 kΩ | 1 each | Recommended | Voltage divider on the HC-05 RXD pin | See Step 3 |
| On/off switch | 1 | Optional | Switches the car off without unplugging the pack | Goes in the red battery wire |
| Jumper wires | Several | Required | Connect the HC-05 to the Arduino | Female-to-female, plugged onto header pins soldered to the shield (see Step 3) |
| Screws, nuts, standoffs | Assorted | Required | Mechanical assembly | M3 is common |
| Android phone | 1 | Required | Runs the control app | The HC-05 uses Bluetooth Classic (not Bluetooth Low Energy); iPhones cannot connect to it, and App Inventor builds Android apps only |

Step 1: Building the chassis
The chassis, the frame of the car, is a flat acrylic plate. It is light, easy to cut and stiff enough to carry four motors, the electronics and the battery pack.
Cut the acrylic plate
Measure and cut the acrylic plate. A typical size is about 20 cm × 15 cm; adjust it to your motors and battery pack. Use a saw or an acrylic cutter (a hooked scoring knife for plastic sheets) for clean edges. After cutting, I peeled off the protective film, marked the hole positions for the components and drilled the holes. I drilled one more hole to pass the motor wires through the plate.
Print the motor mounts
Print four motor mounts that grip the motor body and let the gearbox shaft point outward through the edge of the acrylic plate. I designed my mounts in Autodesk Inventor. After printing, I heated threaded inserts (small metal sleeves with an inner thread) and pressed them into the plastic, so the screws hold directly in the mounts. Without a 3D printer, use metal L-brackets; hot glue holds only for first tests.
Prepare the Arduino holder
Hold the board with a 3D printed holder or with standoffs and M3 screws through the Arduino’s mounting holes. Double-sided foam tape also works. The holder keeps the board in place while the car drives and keeps the wiring from pulling loose.
Step 2: Mounting the motors and electronics
Attach the motors to the mounts
Fix each motor into its mount (or L-bracket) with screws and nuts. Mount the motors with all shafts pointing outward. The left and right motors are mirror images, so with identical wiring one side may spin backward; you fix that by swapping the two wires of those motors (see Code explanation).
Mount the motor assemblies on the acrylic plate
Screw the motor mounts to the acrylic plate, two motors on each side, as far apart as possible for stability. Check that the wheels spin freely without rubbing on the acrylic plate or on each other.
Mount the Arduino and the shield
Fix the Arduino holder (or the standoffs) to the center or rear of the acrylic plate and leave room at the front or sides for the battery pack. Set the Uno into the holder and screw it down. Press the Motor Shield onto the Arduino headers until it sits flush, and check that no pin is bent or outside its socket.
Step 3: Wiring
The wiring diagram below shows the motors, the battery and the on/off switch; the HC-05 connections follow in their own table. Before connecting the motors, I labeled each motor wire with the name of its motor, so I could match the four pairs to M1 to M4.

Motor connections
| Motor | Motor terminal | Position on the car |
|---|---|---|
| Motor 1 | M1 | Left side (front or rear, order does not matter) |
| Motor 2 | M2 | Left side (front or rear, order does not matter) |
| Motor 3 | M3 | Right side (front or rear, order does not matter) |
| Motor 4 | M4 | Right side (front or rear, order does not matter) |
Insert the two wires of each motor into its motor terminal and tighten the screws.
HC-05 connections
| HC-05 pin | Arduino pin | Notes |
|---|---|---|
| VCC | 5 V | Powers the HC-05 |
| GND | GND | Common ground (the shared 0 V reference of both boards) |
| TXD | RX (pin 0) | HC-05 transmits data to the Arduino |
| RXD | TX (pin 1) | Arduino transmits data to the HC-05 |
The HC-05 RXD pin uses 3.3 V logic, but the Arduino TX pin sends 5 V. Add a voltage divider, two resistors that lower the 5 V signal to about 3.3 V: 1 kΩ from Arduino TX to RXD, 2 kΩ from RXD to GND. You can skip it if the breakout board of your HC-05 (the small carrier board with the pin header) has a level shifter, a circuit that converts 5 V signals to 3.3 V. The Motor Shield covers the Arduino headers. On my car, I soldered short header pins onto the shield, two for pins 0 and 1 and two for 5 V and GND, and plugged the HC-05 wires onto them. I connected the module directly, without a divider; it worked, but the divider is the safer way. I fixed the HC-05 to the acrylic plate with adhesive.
Pins 0/1 and uploads
Warning: pins 0 (RX) and 1 (TX) are also the USB serial pins the Arduino uses for uploads and the Serial Monitor. The Serial Monitor is the window in the Arduino IDE (the program you write and upload sketches with) that shows serial text. Disconnect at least the HC-05 TXD and RXD wires before every upload, otherwise the upload fails. Uploading with the HC-05 still connected caused me problems on this car. Reconnect the wires afterwards and press reset. While the HC-05 is on pins 0/1, the Serial Monitor is not usable.
If you need Bluetooth and the Serial Monitor at the same time, connect the HC-05 through SoftwareSerial, a library that creates a second serial port on other pins. Use pins the Motor Shield leaves free, for example A0 and A1. In the sketch, create a SoftwareSerial port on these pins and use it in place of Serial. Do not use pin 3: the shield uses it for motor M2.
Step 4: Power
Connect the 4 × AA pack (6 V) to the EXT_PWR terminal on the Motor Shield: red wire to the + terminal, black wire to the – terminal. The shield powers the motors from this terminal. Its ground is connected to the Arduino’s ground through the header pins. Do not power the motors from a 9 V (PP3) battery: it cannot deliver the current four DC motors draw under load.
The shield has a jumper labeled PWR, a small removable plug that connects two pins. With the jumper installed, the battery on EXT_PWR also feeds the Arduino’s VIN pin (its raw power input). The Uno’s onboard voltage regulator (a circuit that keeps the output voltage constant) needs about 7 V at VIN for a stable 5 V, so a 6 V pack is too low for that. If the Arduino or the HC-05 resets when the motors start, remove the PWR jumper and power the Uno separately. Use a USB power bank or a 9 V (PP3) battery on the DC jack (the round power socket of the Uno). In this setup the 9 V battery supplies only the Uno and the HC-05; the motors still run from the 4 × AA pack.
I added an on/off switch to the battery line and fixed it to the frame, so I can switch the car off without unplugging the pack. The switch goes in the red wire.
On my car, the battery is a 3S LiPo pack (three lithium-polymer cells in series, 11.1 V, 5200 mAh), held on the acrylic plate with cable ties. With the PWR jumper installed, it gives the Uno enough voltage at VIN. The wiring diagram shows a similar voltage: two 4 × AAA holders in series, about 12 V. The motors are rated 3 to 6 V, though. The L293D drops about 2.6 V, so at full speed they get roughly 8.5 V from the LiPo. OmObi, the 3D printed successor, runs on a 2S LiPo (7.4 V). For a first build, the 4 × AA pack is the safer choice. For longer run time, a rechargeable 2S Li-ion pack also works (two cells in series, 7.4 V nominal, 8.4 V fully charged). Use it with a BMS (battery management system), a small board that protects the cells from overcharge and deep discharge. After the L293D drop, at most about 5.8 V reach the motors.
Step 5: Programming the Arduino
The sketch below reads single-character commands from the HC-05 and drives the four motors. It uses the AFMotor library, Adafruit’s library for the V1 Motor Shield. Install it in the Arduino IDE under Sketch > Include Library > Manage Libraries: search for “Adafruit Motor Shield library” (not the V2 library).
To upload the sketch, connect the Uno by USB, select Tools > Board > Arduino Uno and the matching port under Tools > Port, then click Upload.
Important: disconnect the HC-05 from pins 0 and 1 before uploading this sketch. Reconnect it after the upload completes, then reset the Arduino.
/**
* Author: Omar Draidrya
* Date: 2024/07/03
* Bluetooth-controlled 4WD robot car 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);
}
Code explanation
The sketch creates four motor objects, motor1(1) to motor4(4), one for each motor terminal M1 to M4. In setup(), serial communication starts at 9600 baud (bits per second). Both sides must use the same rate; 9600 is the HC-05 default in data mode (its normal mode, in which it passes data through). All four motors are set to full speed (255 of 255).
Each command is one ASCII character (a letter stored as one byte), so the sketch needs no text parsing. Characters other than F, B, L, R and S are ignored.
forward() runs all four motors FORWARD, so the car drives straight ahead. backward() runs them BACKWARD. turnLeft() runs the left motors backward and the right motors forward, a tank-style left turn. turnRight() does the opposite. stop() calls RELEASE on all motors, which cuts the power; the car rolls out and stops.
The turn functions rely on the left/right split from the motor table in Step 3. If one wheel spins the wrong way, swap the two wires of that motor at its motor terminal. If the whole car drives backward on F, swap the wires of all four motors, or swap FORWARD and BACKWARD in the code.
How to change speed
The speed is set in setup() with motor1.setSpeed(255). The value ranges from 0 (stopped) to 255 (full speed). To slow the car down, reduce this value. A value of 150 switches the motor on for about 59 % of each PWM cycle. PWM (pulse-width modulation) means the shield turns the motor on and off very fast, and the on-time share sets the speed. The real wheel speed also depends on battery voltage and load, and very low values may not start the motors at all. You can set different speeds for turning and straight driving by calling setSpeed() inside the movement functions.
How to test the code safely
For the first test, place the car on a box or a stack of books so the wheels do not touch the ground. That way you can check each direction without the car driving off the table. Connect over Bluetooth, send each command from the app and check that all four wheels spin in the expected direction. When they do, set the car on the floor and test at low speed with setSpeed() around 120.
Step 6: Building the Android app
The phone app is built in MIT App Inventor. It connects to the HC-05 over Bluetooth and sends the direction commands. You build the app from blocks, so you do not need to write any Java or Kotlin, the usual programming languages for Android apps.
Register and create a project
- Go to MIT App Inventor (ai2.appinventor.mit.edu) and log in with your Google account.
- Click “Start New Project” and name it “RobotCarControl”.
Design the user interface
In the Designer view (the App Inventor screen where you place the app’s components), add these components:
- ListPicker: set its text to “Select Bluetooth Device”. The user picks the paired Bluetooth device from this list.
- Label: shows the connection status. The blocks set it to “Connected” or “Not Connected”.
- BluetoothClient: from the Connectivity palette. It is a non-visible component that handles the Bluetooth connection.
- Four Buttons: one for each direction. Set their text and colors so the directions are clear.
App Inventor names new components automatically (ListPicker1, Label1, BluetoothClient1). Keep these default names, because the blocks below use them. Rename only the four buttons (ButtonForward, ButtonBackward, ButtonLeft, ButtonRight) with the Rename button in the Components panel.
Program the blocks
Switch to the Blocks view, the screen where you program the components with blocks. The listing below is a text version of the blocks, not code you can type. Click a component in the Blocks panel (for example ListPicker1) and drag the matching block, such as “when ListPicker1.BeforePicking”, into the workspace. The screenshot below shows the finished blocks. In it, the buttons are named Forward_Button, Backward_Button, Left_Button and Right_Button; any names work if the blocks match. The connect blocks there also set the background of Label1 to green or red.
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"

Understanding the block logic
ListPicker1.BeforePicking: when the user taps the ListPicker, it fills the list with all paired Bluetooth devices (addresses and names) from BluetoothClient1.AddressesAndNames.
ListPicker1.AfterPicking: once the user selects a device, the app connects with BluetoothClient1.Connect and the selected address. On success, Label1.Text shows “Connected”, otherwise “Not Connected”.
TouchDown events: when the user presses (touches down on) a direction button, the app sends the matching command character (F, B, L or R) with BluetoothClient1.SendText, but only while the Bluetooth connection is active.
TouchUp events: when the user releases (lifts their finger from) any direction button, the app sends “S”. The car therefore moves only while you hold a button. On S the motors switch to RELEASE and the car rolls out for a short distance.
Step 7: Connecting and driving
Pair the HC-05
Turn on Bluetooth on the phone, open the Bluetooth settings and search for new devices. Pair with HC-05 using PIN 1234 (0000 on some modules). The phone remembers the pairing, so you do this only once.
Install the app and connect
Install the app on the phone first. In App Inventor, choose Build > Android App (.apk). Download the .apk file (the Android installation file) to the phone and install it; Android asks you to allow installs from this source. For quick tests, you can run the project live with the MIT AI2 Companion app instead. If Android asks for the “Nearby devices” permission, allow it, otherwise the app cannot list or connect to the HC-05. I used the app on an Android tablet and installed it from the downloaded .apk.
Open RobotCarControl, tap “Select Bluetooth Device” and choose HC-05. Label1 should show “Connected”. Press the direction buttons: the car drives forward and backward, turns left and right, and stops when you release the button.
Testing, troubleshooting and FAQ
This car uses the same code, app and wiring as its 3D printed successor, OmObi. The first test, troubleshooting and FAQ are maintained in the OmObi post: first test, troubleshooting and FAQ.
Related tutorials
- HC-05/HC-06 Arduino Bluetooth Module Complete Tutorial: Bluetooth serial communication in detail, LED control and a relay-based lamp with MIT App Inventor.
- Building the Bluetooth-Controlled Robot Car OmObi: the successor of this build with a 3D printed chassis and STL files.
- Building an Obstacle-Avoiding Robot Car with Arduino: one ultrasonic sensor on a servo and autonomous driving, on a two-wheel car with the Arduino Motor Shield Rev3.
- Advanced Autonomous and Bluetooth Robot Car with Arduino Mega: four ultrasonic sensors and combined autonomous and Bluetooth control.
Possible upgrades: an ultrasonic sensor for obstacle avoidance, a camera for FPV (first-person view) driving, or an ESP32 for Wi-Fi control.

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