Guides · 18 min read

How to Make an Arduino Robot Car: Bluetooth Control Step by Step

Build an Arduino robot car step by step: parts and cost, L298N and HC-05 wiring, 18650 batteries, drive code, phone control and fixes for drifting and resets.

Building an Arduino car is one of the most enjoyable ways to learn electronics and coding in a single project. With two geared DC motors, an L298N motor driver, two 18650 batteries, an HC-05 Bluetooth module and an Arduino Uno, you can build a robot car you drive from your phone in one weekend. Along the way you'll see for yourself why motors can't be connected straight to an Arduino, why the choice of battery matters so much and how a few lines of code move a real vehicle.

In this guide we build a 2WD (two-motor) robot car together from start to finish: parts list and cost breakdown, chassis assembly, wiring tables, forward-backward-turn code and Bluetooth control from your phone. The two problems beginners struggle with most, the car not driving straight and the Arduino resetting when the motors start, each get their own section with measured, practical fixes. In the last sections you'll find how to drive the same car with an ESP32 or a Raspberry Pi Pico, and how to turn it into an obstacle-avoiding or line-following robot.

Arduino Uno
Arduino Uno
L298N
L298N
HC-05
HC-05
18650 Li-ion
18650 Li-ion

How does an Arduino robot car work?

You can split a robot car into four parts. The Arduino is the brain: it reads the command from the phone and decides which motor turns in which direction and how fast. The L298N motor driver is the muscle: it turns the Arduino's weak 5 V signals into the high current the motors need. The geared motors turn the wheels and the battery pack powers everything. These cars have no steering; they turn by spinning the two wheels at different speeds or in different directions. This is called differential drive: if the left wheel turns forward and the right wheel backward, the car spins to the right on the spot. A small free-swivelling caster wheel at the front or back keeps the body balanced.

Yellow geared TT motor and 65 mm wheel set
The standard robot car motor: a TT motor with a yellow plastic gearbox and a 65 mm wheel. The gearbox reduces the motor speed 48 times and increases its torque.
FeatureYellow TT motor (1:48)
Operating voltage3–6 V
Gear ratio1:48 (plastic gears)
No-load speedAbout 90–120 RPM at 3 V, 200–250 RPM at 6 V
No-load currentAbout 150–160 mA
Stall currentAbout 1.1–1.5 A
Wheel diameter (in kits)65 mm
Car speed at 6 VAbout 0.7 m/s (π × 0.065 m × 200 / 60)

The most important row in the table is the stall current: when a wheel pushes against an obstacle or the car pulls away, each motor briefly draws more than 1 amp. An Arduino pin can supply 20–40 mA at most, so motors are always connected through a driver. While doing this job the L298N loses about 1.5–2.5 V; with a 7.4 V battery pack that leaves 5–6 V for the motors, which is exactly the ideal range for TT motors. We explain the driver's internals and the H-bridge idea in detail in DC Motor Control with the L298N.

Parts list

PartQtyNote
2WD acrylic robot car chassis kit1Usually includes 2 TT motors, 2 wheels, a caster wheel, a switch, motor brackets and screws
Arduino Uno R3 (a clone is fine) + USB cable1A Nano also works; the pin numbers are the same
L298N motor driver module12 A per channel; drives two motors independently
HC-05 or HC-06 Bluetooth module1For talking to an Android phone
18650 Li-ion cell2Branded cells that can deliver at least 5 A
2-slot 18650 battery holder (series)1Connects the two cells in series for 7.4 V
18650 charger1A charger that charges each cell separately
Jumper wires (male-female and female-female)20+Between the Arduino, L298N and HC-05
1 kΩ and 2 kΩ resistors1 + 1Voltage divider for the HC-05 RXD input
100 nF ceramic capacitor2–4Across the motor terminals to suppress noise
470–1000 µF electrolytic capacitor (16 V or higher)1Recommended; on the L298N power input
Cable ties, double-sided tape, M3 standoffsA fewTo fix the boards and wires in place

Most chassis kits also include a 4×AA battery holder; as you'll see shortly, that holder is too weak in combination with the L298N, which is why we added 18650 cells to the list. The HC-06 is the HC-05's simpler sibling that only works as a slave, and it's perfectly fine for controlling a car from a phone. The slotted plastic discs in the kit are encoder discs for a speed sensor that you can use later to make the car drive perfectly straight; don't throw them away, just put them aside for now.

Cost: where does the budget go?

Prices change all the time depending on the seller and the exchange rate, so we don't quote figures; instead we show which items take up the budget and where you can save. The general rule is: electronic modules (L298N, HC-05, resistors) are cheap, while most of the budget goes on the board, the chassis kit and above all the batteries and charger. The battery is the last place to save money: good 18650 cells can be recharged hundreds of times and reused in every robot project you build next.

ItemPrice levelSaving tip
Chassis kit (motors and wheels included)MediumA kit is usually cheaper than buying motors, wheels and the caster separately
Arduino UnoMedium (clone) – high (original)A clone board is fine for learning; you can also reuse the Uno or Nano you already have
L298N motor driverLowOne module drives two motors; one is enough for 4WD too
HC-05 / HC-06Low – mediumIf you only drive from a phone, an HC-06 is enough
2× 18650 cells + chargerBiggest itemVery cheap cells labelled "9900 mAh" are usually fake; don't save here
Wires, resistors, capacitors, screwsLowMost of these are already in starter kits
If you already have an Arduino starter kit, the board, jumper wires and resistors are covered; you only need the chassis kit, an L298N, an HC-05 and the batteries. Ready-made "smart car kits" that come with everything in one box are convenient because they bring their own motor board and ready-made code, but they cost more and take away the chance to learn the wiring.

2WD or 4WD?

Feature2WD (2 motors + caster)4WD (4 motors)
TurningSpins on the spot, sharply and easilyTurns by skidding the wheels sideways (skid steer); struggles on carpet
Current drawAbout 0.3–0.6 A while drivingAbout twice as much
Battery lifeLongerShorter
Rough groundThe caster can catch on edgesBetter grip
Driving straightThe caster can push the car off course when it swivelsMore stable
L298N connectionEach motor on its own channelThe two motors on each side in parallel on one channel
Best forA first car, a line-following robotOutdoors, heavier loads

We recommend 2WD for your first car: it's cheaper, draws less current, spins easily on the spot and suits follow-up projects such as a line-following robot better. If you build a 4WD car, connect the two motors on each side in parallel to the same OUT terminals and check that both turn in the same direction. One L298N channel then carries the current of two motors; that's fine in normal driving, but when the wheels get stuck the 2 A per channel limit can be exceeded and the driver's thermal protection cuts the motors.

Power supply: why two 18650 cells?

Most robot car problems come from the wrong battery. The right source has to meet two conditions at once: it must leave enough voltage for the motors after the L298N's loss, and it must not collapse when the motors draw amps at start-up. Two 18650 Li-ion cells in series (2S) give 8.4 V when fully charged and 7.4 V on average, and they easily deliver several amps. The Arduino Uno recommends 7–12 V on its VIN input, so the same pack can power both the motors and the Arduino. With 2500 mAh cells, a 2WD car drawing an average of 0.8 A while driving (motors plus boards) runs for about 3 hours in theory; since you never drain the pack completely, 2–2.5 hours is a more realistic figure. To calculate with your own values, use the Battery life tool.

18650 Li-ion rechargeable cell
18650 Li-ion cell: 18 mm in diameter and 65 mm long; 4.2 V when full and 3.6–3.7 V on average. Two of them in series make the ideal 7.4 V pack for a robot car.
Power sourceVoltageCurrent capabilityFor a robot car
9 V PP3 battery9 VVery lowNot suitable; the most common cause of resets
4× AA alkaline6 V (drops quickly under load)MediumWeak; only about 4 V reaches the motors after the L298N
4× AA NiMH (rechargeable)4.8 VGoodNot enough voltage with the L298N
2× 18650 Li-ion (2S)7.4 V (8.4 V full)High (several amps)Best choice
3× 18650 Li-ion (3S)11.1 V (12.6 V full)HighToo much for TT motors; exceeds the 12 V limit of the 5V jumper
USB power bank5 VGoodOnly for powering the Arduino separately
Li-ion cells are powerful but need care. Check the plus and minus orientation twice when inserting the cells; an 18650 cell that is inserted backwards or short-circuited gets very hot and can start a fire. The voltage per cell should never drop below 3.0 V; charge when your car starts to slow down (around 6.6 V for the pack). TP4056 modules only charge a single cell and can't charge a series pack directly; take the cells out of the holder, charge them in an 18650 charger and never leave them unattended while charging.

Instead of guessing how much charge is left, you can measure it. Connect the battery plus (after the switch) to A0 through a voltage divider made of two 10 kΩ resistors: an 8.4 V pack appears as 4.2 V on A0, below the Arduino's 5 V reading limit. The divider only draws about 0.4 mA, so it doesn't affect battery life.

Arduino (C++)
1// Pil + (anahtardan sonra) → 10 kΩ → A0 → 10 kΩ → GND / battery + (after the switch) → 10 kΩ → A0 → 10 kΩ → GND
2const float DIVIDER = 2.0;   // iki eşit direnç gerilimi yarıya böler / two equal resistors halve the voltage
3
4void setup() {
5  Serial.begin(9600);
6}
7
8void loop() {
9  float v = analogRead(A0) * 5.0 / 1023.0 * DIVIDER;
10  Serial.print(F("Pil / Battery: "));
11  Serial.print(v, 2);
12  Serial.println(F(" V"));
13  if (v < 6.6) Serial.println(F("Sarj et / Charge now"));   // hücre başına 3,3 V / 3.3 V per cell
14  delay(1000);
15}

Step-by-step assembly

  1. Peel the protective paper off the acrylic parts. If the motors came without wires, solder two wires to each motor and reinforce the solder joints with hot glue; a motor wire that breaks off from vibration is the most common fault.
  2. Fix the motors to the underside of the chassis with the T-shaped acrylic brackets and long M3 screws from the kit. The two motor shafts must be aligned, and the motors' wire terminals should face inwards.
  3. If you plan to use the encoder discs, fit them on the inner side of the motor shaft now; it's hard to do once the wheels are on.
  4. Mount the caster wheel in the middle of the other end of the chassis using standoffs. Choose a standoff height that keeps the chassis parallel to the floor.
  5. Push the wheels all the way on, lining them up with the D-shaped shaft; a loose wheel makes the car drift constantly.
  6. Place the battery holder with double-sided tape or Velcro where its weight sits close to the wheel axle. If most of the weight rests on the caster, the drive wheels will slip.
  7. Mount the Arduino and the L298N on the top deck with plastic standoffs. You should be able to reach the Arduino's USB port easily and the L298N's heatsink should be in the open air; make sure metal screws don't touch the solder joints under the boards.
  8. Wire the switch in series with the battery's positive lead so you can turn the car on and off without removing the cells.
  9. Twist each motor's two wires together on the way to the L298N, keep all wires short and bundle them with cable ties. Before inserting the cells, check with a multimeter that there's no short circuit between the battery terminals.
Before mounting the motors on the chassis, briefly touch each motor's leads to 2 AA batteries to check the direction of rotation and mark the wires. If you mark both motors so that "red wire on plus means forward", you won't make direction mistakes during wiring.

Wiring diagram

Think of the wiring in two parts: the power side (battery, switch, L298N, motors) and the signal side (the thin wires from the Arduino to the L298N and the HC-05). Remove the small jumpers on the L298N's ENA and ENB pins; we'll connect these pins to the Arduino's PWM outputs to set the speed in code. Leave the L298N's 5V jumper in place: since the pack is below 12 V, the board's own regulator powers the driver's logic.

L298N dual-channel motor driver module
L298N motor driver: the motors go to the blue screw terminals on the sides and the battery to the three-way terminal at the front; the ENA, IN1–IN4 and ENB pins go to the Arduino.
L298N terminalConnection
OUT1, OUT2Left motor (on 4WD, the two left motors in parallel)
OUT3, OUT4Right motor (on 4WD, the two right motors in parallel)
12VBattery plus through the switch
GNDBattery minus and the Arduino's GND pin (common ground)
5VLeft unconnected with the Arduino Uno; powers the board with the ESP32 and Pico
5V jumperStays on (battery below 12 V)
ENA and ENB jumpersRemoved; the pins go to the Arduino's PWM outputs
ConnectionArduino UnoESP32Raspberry Pi Pico
L298N ENA (left motor speed)D5 (PWM)GPIO 25GP15
L298N IN1D7GPIO 27GP14
L298N IN2D8GPIO 33GP13
L298N ENB (right motor speed)D6 (PWM)GPIO 26GP10
L298N IN3D9GPIO 32GP12
L298N IN4D4GPIO 4GP11
HC-05 TXDD10 (SoftwareSerial RX)Not needed (built-in Bluetooth)GP1 (UART0 RX)
HC-05 RXDD11, through a 1 kΩ / 2 kΩ dividerNot neededGP0 (UART0 TX), direct
HC-05 VCC5VNot neededL298N 5V output
Board powerVIN ← battery plus after the switch5V (VIN) ← L298N 5V outputVSYS ← L298N 5V output
GNDShared with L298N GND and battery minusSharedShared
Never break three rules: all GNDs (battery minus, L298N, Arduino, HC-05) must be connected together; the motors must never be powered from the Arduino's 5V pin; and the L298N's 5V output must not go to the Arduino's VIN pin. VIN needs at least 7 V; with 5 V the Arduino's regulator can't work properly and the board becomes unstable. The HC-05's RXD pin is a 3.3 V input; bring the Uno's 5 V TX signal down with a 1 kΩ and 2 kΩ divider: D11 → 1 kΩ → RXD, RXD → 2 kΩ → GND (5 V × 2/3 ≈ 3.3 V).

We chose D5 and D6 as speed pins on purpose. On the Uno, the Servo library disables PWM on pins 9 and 10, and the tone() function disables it on pins 3 and 11. With ENA and ENB on 5 and 6, your motor speed control keeps working when you add a servo or a buzzer later. D2 and D3 are reserved for encoders because they support interrupts. You can see your board's pin layout in the Pinout tool.

First sketch: forward, backward, left and right

Before moving on to Bluetooth, let's test the motors and the wiring on their own. The heart of the code is the drive(left, right) function: it takes the speed of each motor separately from -255 to 255, where a positive value means forward, a negative value backward and zero stop. We explain how the speed value turns into voltage in What Is PWM?. Before uploading, put the car on a box so that the wheels spin freely; the 3-second wait in setup() gives you time for that.

Arduino (C++)
1// Sol motor / left motor: ENA=5, IN1=7, IN2=8 — Sağ motor / right motor: ENB=6, IN3=9, IN4=4
2const int ENA = 5, IN1 = 7, IN2 = 8;
3const int ENB = 6, IN3 = 9, IN4 = 4;
4
5const int SPEED = 180;   // temel hız 0-255 / base speed 0-255
6const int TRIM = 0;      // % düzeltme: + sol, - sağ motoru yavaşlatır / % trim: + slows left, - slows right
7
8void drive(int left, int right) {               // her biri -255 … 255 / each -255 … 255
9  if (TRIM > 0) left = left * (100 - TRIM) / 100;
10  if (TRIM < 0) right = right * (100 + TRIM) / 100;
11  digitalWrite(IN1, left > 0);  digitalWrite(IN2, left < 0);
12  digitalWrite(IN3, right > 0); digitalWrite(IN4, right < 0);
13  analogWrite(ENA, abs(left));
14  analogWrite(ENB, abs(right));
15}
16
17void forward()   { drive(SPEED, SPEED); }
18void backward()  { drive(-SPEED, -SPEED); }
19void turnLeft()  { drive(-SPEED, SPEED); }     // yerinde sola / spin left in place
20void turnRight() { drive(SPEED, -SPEED); }     // yerinde sağa / spin right in place
21void stopCar()   { drive(0, 0); }
22
23void setup() {
24  int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4};
25  for (int p : pins) pinMode(p, OUTPUT);
26  stopCar();
27  delay(3000);             // tekerlekleri kaldırmak için zaman / time to lift the wheels
28}
29
30void loop() {
31  forward();   delay(1500);
32  stopCar();   delay(500);
33  backward();  delay(1500);
34  stopCar();   delay(500);
35  turnLeft();  delay(600);
36  stopCar();   delay(500);
37  turnRight(); delay(600);
38  stopCar();   delay(2000);
39}
MovementLeft motorRight motorCode
ForwardForwardForwarddrive(180, 180)
BackwardBackwardBackwarddrive(-180, -180)
Spin left on the spotBackwardForwarddrive(-180, 180)
Spin right on the spotForwardBackwarddrive(180, -180)
Wide left turnSlow forwardForwarddrive(90, 180)
StopStopsStopsdrive(0, 0)

With the wheels in the air, the car should go forward, backward, left and right in turn. If one wheel turns the wrong way, don't change the code; swap that motor's two wires at the L298N's OUT terminals. If both wheels turn the wrong way, swap the wires of both motors. If the motors hum but don't turn, the SPEED value is too low: most TT motors need a PWM value of at least around 80–120 to start moving, and this threshold rises as the battery drains.

Bluetooth control from your phone (HC-05)

The HC-05 works like a wireless serial cable between the phone and the Arduino: the app on the phone sends a letter, the Arduino reads it and performs the matching movement. Most of the controller apps you find on the Play Store by searching for "Bluetooth RC Car" or "Bluetooth RC Controller" send F for forward, B for backward, L for left, R for right and S for stop when you release a button; some also use G, I, H and J for the diagonals and 0–9 plus q for speed. The module's pins, LED patterns and AT commands are covered in HC-05 Bluetooth Module.

HC-05 Bluetooth module
HC-05: connects to the phone over classic Bluetooth (SPP); the default speed is 9600 baud and the pairing PIN is 1234 or 0000.
  1. The car's switch can stay off while you upload the sketch below; the Arduino and HC-05 are powered over USB. If the HC-05's LED blinks quickly, the module is waiting for a connection.
  2. Find the HC-05 in your Android phone's Bluetooth settings and pair it with PIN 1234 (or 0000 if that fails).
  3. Open the controller app, grant the Bluetooth and nearby devices permissions and select the HC-05 from the device list. Once connected, the module's LED gives two short blinks every two seconds.
  4. Keep the Arduino connected over USB, open the Serial Monitor at 9600 baud and press the buttons in the app. The letter each button sends appears on screen; if your app sends different letters, change the case lines in the code to match.
  5. Unplug the USB cable, turn on the switch, put the car on the floor and drive.
Arduino (C++)
1#include <SoftwareSerial.h>
2
3SoftwareSerial bt(10, 11);               // RX, TX → HC-05 TXD, RXD (RXD bölücüyle / RXD via divider)
4
5const int ENA = 5, IN1 = 7, IN2 = 8;     // sol motor / left motor
6const int ENB = 6, IN3 = 9, IN4 = 4;     // sağ motor / right motor
7const int TRIM = 0;                      // % düzeltme / % trim
8const unsigned long TIMEOUT = 600;       // ms; 0 = kapalı / 0 = disabled
9
10int speed = 200;                         // 0-255
11unsigned long lastCmd = 0;
12
13void drive(int left, int right) {
14  if (TRIM > 0) left = left * (100 - TRIM) / 100;
15  if (TRIM < 0) right = right * (100 + TRIM) / 100;
16  digitalWrite(IN1, left > 0);  digitalWrite(IN2, left < 0);
17  digitalWrite(IN3, right > 0); digitalWrite(IN4, right < 0);
18  analogWrite(ENA, abs(left));
19  analogWrite(ENB, abs(right));
20}
21
22void setup() {
23  int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4};
24  for (int p : pins) pinMode(p, OUTPUT);
25  drive(0, 0);
26  Serial.begin(9600);                    // gelen harfleri görmek için / to see incoming letters
27  bt.begin(9600);                        // HC-05 veri modu hızı / HC-05 data mode baud rate
28}
29
30void loop() {
31  if (bt.available()) {
32    char c = bt.read();
33    Serial.println(c);
34    lastCmd = millis();
35    int half = speed / 2;
36    switch (c) {
37      case 'F': drive(speed, speed);   break;   // ileri / forward
38      case 'B': drive(-speed, -speed); break;   // geri / backward
39      case 'L': drive(-speed, speed);  break;   // yerinde sola / spin left
40      case 'R': drive(speed, -speed);  break;   // yerinde sağa / spin right
41      case 'G': drive(half, speed);    break;   // ileri-sol / forward-left
42      case 'I': drive(speed, half);    break;   // ileri-sağ / forward-right
43      case 'H': drive(-half, -speed);  break;   // geri-sol / back-left
44      case 'J': drive(-speed, -half);  break;   // geri-sağ / back-right
45      case 'S': drive(0, 0);           break;   // dur / stop
46      case 'q': speed = 255;           break;   // tam hız / full speed
47      default:
48        if (c >= '0' && c <= '9') speed = map(c - '0', 0, 9, 100, 240);   // hız kademesi / speed level
49    }
50  }
51  if (TIMEOUT > 0 && millis() - lastCmd > TIMEOUT) drive(0, 0);   // komut yoksa dur / no command: stop
52}
LetterMovementLeft / right motor
FForward+speed / +speed
BBackward−speed / −speed
LSpin left−speed / +speed
RSpin right+speed / −speed
G, IForward-left, forward-rightOne side at half speed
H, JBack-left, back-rightOne side at half speed
SStop0 / 0
0–9, qSpeed level0 → 100, 9 → 240, q → 255

The TIMEOUT in the code is a safety feature: these apps keep sending the same letter as long as you hold a button, so if no letter arrives for 600 ms the code assumes the connection is lost and stops the car. That way the car doesn't keep heading for the wall when the phone goes out of range or the app closes. If your app sends the letter only once per button press, the car will stop after 600 ms; in that case set TIMEOUT to 0 to turn this feature off. We also didn't use delay() anywhere in loop(); long waits make commands lag, and you'll find out why in millis() vs delay().

The HC-05 and HC-06 use classic Bluetooth, so they don't work with an iPhone; iOS doesn't allow this kind of serial connection. If you want to drive with an iPhone, use a BLE module such as the HM-10 or an ESP32 with BLE. If you want to build your own controller app, take a look at the Arduino Bluetooth with App Inventor guide.

The car doesn't drive straight: causes and fixes

If the car slowly drifts to one side even though you give both motors the same PWM value for "forward", don't worry; this is the most common robot car problem. Cheap geared motors turn at different speeds even at the same voltage: differences in gear friction, brushes and magnets make every motor a little different. Add a loose wheel, a caster that keeps swivelling or weight piled on one side, and a drift of 20–30 cm over 2 metres is perfectly normal. Fix the mechanical causes first, then correct the remaining difference in software.

SymptomLikely causeFix
Always drifts to the same sideThe motors run at different speedsSoftware correction with TRIM, or encoders
Swerves at start-up, then straightensOne motor starts later, or the caster swivelsSoft start; a ball caster instead of a swivel caster
Drift is different every timeLoose wheel, slipping tyre or bent motor bracketSeat the wheel fully on the shaft, tighten the screws
Spins on the spot instead of going forwardOne motor's wires are reversedSwap that motor's wires at the OUT terminal
Drift grows as the battery drainsThe difference between motors grows at low voltageCharge the battery; set TRIM with a full battery
Pulls to one side as it speeds upWeight is on one sideMove the battery and boards to the middle, close to the wheel axle

Once the mechanical problems are fixed, fine-tune in software. The TRIM constant in the code slows the faster motor down by a percentage: a positive value slows the left motor, a negative value the right motor. Calibrate it like this:

  1. Fully charge the cells; calibrating with a full battery gives consistent results.
  2. Stick a straight 2-metre strip of tape on the floor and place the car at its start, pointing along the tape.
  3. In the first sketch's loop(), keep only forward() and a 3-second wait, then let the car drive.
  4. If the car drifts right, the left motor is faster: increase TRIM by 3; if it drifts left, decrease TRIM by 3.
  5. Repeat until the drift over 2 metres is down to a few centimetres, then copy the same TRIM value into the Bluetooth sketch.
  6. Calibrate at the speed you use most; the difference between motors changes slightly with speed.

The next level for perfectly straight driving and measuring distance is the encoder. You fit the 20-slot discs from the kit on the motor shafts and count each slot with an LM393-based slotted speed sensor. Connect the sensor outputs to the interrupt-capable D2 and D3 pins and compare the pulse counts of the two wheels: if the left side gets ahead, slow the left motor down a little. This simple proportional (P) control keeps the car straight regardless of the battery level and the floor. A 65 mm wheel covers about 20.4 cm per turn, so each pulse means roughly 1 cm of travel. We explain how interrupts work in Arduino Interrupts.

The Arduino resets when the motors start

If the Arduino restarts or the HC-05 loses its connection (the LED starts blinking quickly again) when the car pulls away, changes direction or hits an obstacle, the problem is almost always on the power side. At start-up two motors together can draw 2–3 A; a weak battery can't deliver that and its voltage collapses for a moment. The Uno's processor resets itself for safety when its supply drops below 2.7 V (brown-out). On top of that comes the electrical noise from the motor brushes and the voltage drop in thin wires. Here are the fixes, starting with the most effective:

  • Change the battery: use two charged 18650 cells instead of a 9 V battery or tired AA cells; most problems end here.
  • Use the right power path: power the Arduino from the battery through VIN; don't connect the L298N's 5V output to VIN.
  • Motor capacitors: solder a 100 nF ceramic capacitor across the two terminals of each motor; it suppresses brush noise at the source.
  • Reservoir capacitor: connect a 470–1000 µF electrolytic capacitor to the L298N's 12V and GND terminals (plus lead to 12V). It covers the brief current demand at start-up.
  • Soft start: instead of jumping to 255, raise the speed step by step as shown below; the current surge gets smaller.
  • Stop before reversing: switching straight from forward to reverse pushes the motor current to its peak; stop for 100–200 ms before changing direction.
  • Thick, short power wires: don't use jumper wires or a breadboard between the battery and the L298N; use at least 0.5 mm² wire and screw terminals.
  • Separate supplies: if the problem persists, power the Arduino from a power bank and the motors from the battery, and connect the two GNDs.
Arduino (C++)
1// Yumuşak kalkış: ilk koddaki drive() ile kullan / soft start: use with drive() from the first sketch
2void rampTo(int target) {
3  for (int s = 60; s < target; s += 20) {
4    drive(s, s);
5    delay(15);              // yaklaşık 10 adımda tam hız / full speed in about 10 steps
6  }
7  drive(target, target);
8}
9
10// Kullanım: forward() yerine rampTo(SPEED) / usage: rampTo(SPEED) instead of forward()
An easy way to tell whether the problem is power or code: disconnect the VIN wire, keep the Arduino powered over USB from your computer (leave the GND connection in place) and run the motors from the battery. If the resets stop, the problem is definitely on the power side. Connecting a multimeter between VIN and GND and watching the voltage as the car pulls away also shows the dip directly. For other causes, see Arduino Keeps Resetting.

Robot car with an ESP32 and a Raspberry Pi Pico

The ESP32 has classic Bluetooth built in, so you don't need an HC-05: the BluetoothSerial library creates the same serial connection with the phone and the controller apps above work unchanged. The L298N's inputs treat anything above about 2.3 V as HIGH, so the ESP32's 3.3 V signals are enough. Power the ESP32 board from the L298N's 5V output into the board's 5V (VIN) pin; don't connect the battery straight to that pin. This code doesn't compile for the ESP32-S3 and ESP32-C3, because they only have BLE.

Arduino (C++)
1#include "BluetoothSerial.h"
2
3BluetoothSerial bt;
4
5const int ENA = 25, IN1 = 27, IN2 = 33;   // sol motor / left motor
6const int ENB = 26, IN3 = 32, IN4 = 4;    // sağ motor / right motor
7
8int speed = 200;                          // 0-255
9unsigned long lastCmd = 0;
10
11void drive(int left, int right) {
12  digitalWrite(IN1, left > 0);  digitalWrite(IN2, left < 0);
13  digitalWrite(IN3, right > 0); digitalWrite(IN4, right < 0);
14  analogWrite(ENA, abs(left));
15  analogWrite(ENB, abs(right));
16}
17
18void setup() {
19  int pins[] = {ENA, IN1, IN2, ENB, IN3, IN4};
20  for (int p : pins) pinMode(p, OUTPUT);
21  drive(0, 0);
22  bt.begin("BlueGrays-Car");              // telefonda görünecek ad / name shown on the phone
23}
24
25void loop() {
26  if (bt.available()) {
27    char c = bt.read();
28    lastCmd = millis();
29    switch (c) {
30      case 'F': drive(speed, speed);   break;   // ileri / forward
31      case 'B': drive(-speed, -speed); break;   // geri / backward
32      case 'L': drive(-speed, speed);  break;   // yerinde sola / spin left
33      case 'R': drive(speed, -speed);  break;   // yerinde sağa / spin right
34      case 'S': drive(0, 0);           break;   // dur / stop
35      case 'q': speed = 255;           break;   // tam hız / full speed
36      default:
37        if (c >= '0' && c <= '9') speed = map(c - '0', 0, 9, 100, 240);
38    }
39  }
40  if (millis() - lastCmd > 600) drive(0, 0);    // komut yoksa dur / no command: stop
41}

The standard Raspberry Pi Pico has no Bluetooth, so you connect the HC-05 to the UART0 pins. Since the Pico runs at 3.3 V, no voltage divider is needed this time. Power the Pico through its VSYS pin and the HC-05 directly from the L298N's 5V output; when running on batteries without USB there is no voltage on the VBUS pin. In the Pico code the speed is given as a percentage from -100 to 100:

MicroPython
1from machine import Pin, PWM, UART
2import time
3
4bt = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))   # GP0 → HC-05 RXD, GP1 ← HC-05 TXD
5
6ena = PWM(Pin(15)); ena.freq(1000)                  # sol motor hızı / left motor speed
7enb = PWM(Pin(10)); enb.freq(1000)                  # sağ motor hızı / right motor speed
8in1, in2 = Pin(14, Pin.OUT), Pin(13, Pin.OUT)
9in3, in4 = Pin(12, Pin.OUT), Pin(11, Pin.OUT)
10
11def drive(left, right):                             # -100 … 100 (%)
12    in1.value(left > 0); in2.value(left < 0)
13    in3.value(right > 0); in4.value(right < 0)
14    ena.duty_u16(int(abs(left) * 655))
15    enb.duty_u16(int(abs(right) * 655))
16
17speed = 80
18last = time.ticks_ms()
19drive(0, 0)
20
21while True:
22    if bt.any():
23        c = bt.read(1)
24        last = time.ticks_ms()
25        if c == b'F':   drive(speed, speed)          # ileri / forward
26        elif c == b'B': drive(-speed, -speed)        # geri / backward
27        elif c == b'L': drive(-speed, speed)         # yerinde sola / spin left
28        elif c == b'R': drive(speed, -speed)         # yerinde sağa / spin right
29        elif c == b'S': drive(0, 0)                  # dur / stop
30        elif c == b'q': speed = 100                  # tam hız / full speed
31        elif c and 48 <= c[0] <= 57:                 # '0'…'9' hız kademesi / speed level
32            speed = 40 + (c[0] - 48) * 6
33    if time.ticks_diff(time.ticks_ms(), last) > 600:
34        drive(0, 0)                                  # komut yoksa dur / no command: stop

Next steps: upgrade your car

  • Obstacle-avoiding robot: add an HC-SR04 distance sensor at the front and a servo motor that turns it left and right; details in the Obstacle-Avoiding Robot guide.
  • Line-following robot: fit 2–3 TCRT5000 infrared sensors under the front and follow a black line on the floor; see Line-Following Robot.
  • Joystick remote: build a real, phone-free remote with a second Arduino, a joystick module and an nRF24L01.
  • Infrared remote: drive with the buttons of your TV remote; map the codes from IR Receiver and Remote to driving commands.
  • Wi-Fi driving: use an ESP32 to serve a controller page that opens in your phone's browser; the ESP32 Web Server and WebSocket guide covers the basics.
  • Encoders and distance: use speed sensors to drive exactly 1 metre and turn exactly 90 degrees; this is the basis of maze-solving robots.
  • Robot arm: add a small servo-driven robot arm on top of the car to carry objects.
HC-SR04
HC-SR04
TCRT5000
TCRT5000
SG90 servo
SG90 servo

Common mistakes and fixes

  • No motor turns: the GNDs aren't shared, the switch is off, the ENA and ENB jumpers were removed but the pins aren't connected, or the battery is flat.
  • The L298N's LED is on but the motors only hum: the PWM value is below the motor's starting threshold or the battery voltage is low; increase the speed and charge the battery.
  • The sketch won't upload: the wrong port or board may be selected; if you connected the HC-05 to pins 0 and 1, disconnect it while uploading. Other causes are in Arduino Sketch Won't Upload.
  • The phone sees the HC-05 but won't connect: the module is connected to another phone, the app hasn't been granted the "nearby devices" permission, or the pairing PIN is wrong.
  • The car reacts to commands late: there's a delay() in loop() or too much is being printed to the Serial Monitor; do your waiting with millis().
  • The L298N's heatsink gets very hot: the wheels are jammed, or on a 4WD car the current per channel is too high; check for mechanical friction.
  • The car twitches for a moment at power-on: the pins are undefined for a short time at start-up; set the pins as outputs and call drive(0, 0) in the very first lines of setup().

An Arduino robot car is a great starting point that teaches motor control, power management, wireless communication and debugging in a single project. When you finish, you'll have a chassis that gets a little smarter with every new sensor. In the BlueGrays app you'll find the L298N Bluetooth-controlled car, the obstacle-avoiding robot and the TCRT5000 black-and-white surface detection projects step by step, with circuit diagrams and code.

Frequently asked questions

What do you need to build an Arduino car, and how much does it cost?

For a basic Bluetooth car you need a 2WD chassis kit (2 TT motors, wheels, a caster), an Arduino Uno, an L298N motor driver, an HC-05 Bluetooth module, two 18650 cells with a holder and a few jumper wires. Prices vary by seller; the modules are cheap, and most of the budget goes on the board, the chassis kit and the batteries with a charger. If you already own a starter kit, the cost drops noticeably.

What battery should I use for an Arduino robot car?

The best option is two 18650 Li-ion cells in series: they give 7.4 V, leave 5–6 V for the motors after the L298N's roughly 2 V loss and supply the high current needed at start-up. A 9 V PP3 battery can't deliver enough current and causes resets, while 4 AA cells make the motors run slowly with an L298N.

Why doesn't my Arduino car drive straight?

Cheap geared motors turn at different speeds even at the same PWM value, and loose wheels, the caster and uneven weight add to the drift. Fix the mechanical problems first, then slow the faster motor slightly with a trim value in the code. For truly straight driving you need encoders to measure and match the speed of both wheels.

Why does my Arduino reset when the motors start?

The motors draw several amps at start-up and the voltage of a weak battery collapses for a moment; the Uno's processor resets itself when its supply drops. Use charged 18650 cells, add 100 nF capacitors across the motor terminals and a 470–1000 µF capacitor on the L298N input, and ramp the speed up gradually. If the problem persists, power the Arduino from a separate source and connect the GNDs.

Does an HC-05 Bluetooth car work with an iPhone?

No. The HC-05 and HC-06 use the serial port profile of classic Bluetooth, and iOS doesn't let ordinary apps open that kind of connection. To control the car from an iPhone, use a BLE module such as the HM-10 or an ESP32 with BLE; Android phones connect to the HC-05 without problems.

Can I use an L293D motor shield instead of the L298N?

Yes. The L293D motor shield plugs on top of the Arduino Uno and can drive four DC motors, which simplifies wiring on 4WD cars. However, its continuous current is only about 0.6 A per channel, so it struggles sooner than the L298N when the motors stall, and the shield uses many pins. If you want something more efficient, MOSFET drivers such as the TB6612FNG lose much less voltage.

See all projects
Bluetooth Controlled Car with L298N
#80Advanced
Premium

Bluetooth Controlled Car with L298N

4WD (four-motor, four-wheel-drive) robot car project that can be controlled from the phone with Arduino and HC-05 Bluetooth module. A total of four wheels are driven simultaneously by connecting two m

5.0(38)
64
Obstacle Avoidance Robot
#81Advanced
Premium

Obstacle Avoidance Robot

How about building a smart vehicle that finds its own way? The Obstacle Avoidance Robot constantly scans its surroundings thanks to the ultrasonic sensor in front of it. When it encounters an obstacle

5.0(38)
49
Control an LED from Your Phone via Bluetooth
#34Beginner
Premium

Control an LED from Your Phone via Bluetooth

Wireless control project that turns LEDs on and off with commands sent from the phone with HC-06 Bluetooth module. Teaches Bluetooth data exchange over serial communication.

4.8(68)
88
TCRT5000 Black and White Surface Detection
#78Intermediate
Premium

TCRT5000 Black and White Surface Detection

Project that detects the color of the ground with the TCRT5000 sensor and lights red LED on black ground and green LED on white ground. It teaches the color threshold detection logic that forms the ba

4.7(40)
49
TCRT5000 Distance Measurement
#77Intermediate
Premium

TCRT5000 Distance Measurement

Project that measures the amount of light reflected from the surface with the TCRT5000 infrared sensor and displays it as an analog value on the I2C LCD screen. Teaches the basics of optical distance/

4.6(33)
43
HC-SR04 Distance Measurement (Serial Display)
#27Beginner

HC-SR04 Distance Measurement (Serial Display)

Project that measures distance with HC-SR04 ultrasonic sensor and displays the result in cm on Serial Monitor. It teaches the principle of distance calculation with sound wave.

4.6(44)
66
Parking Sensor with HC-SR04
#28Intermediate
Premium

Parking Sensor with HC-SR04

Parking sensor project with HC-SR04 sensor that increases the buzzer beep rate as the object approaches and warns with LED. Teaches feedback logic proportional to distance.

4.8(64)
83
Using a Servo Motor
#35Beginner
Premium

Using a Servo Motor

Basic usage project that positions the servo motor to 0°, 90° and 180° angles in sequence. Teaches servo library and angle control logic.

4.7(48)
66