A ₹2,710 robot car you drive by tilting your hand. A handheld unit with an MPU6050 measures its own pitch and roll, sends them over a 2.4 GHz nRF24L01 radio link, and the car mixes them into left and right wheel speeds. A dead zone stops it creeping, and if the radio goes quiet the car stops by itself.
Scope: advanced. This is a design worksheet with illustrative costs. It has not been bench-tested by this site. The tilt table below was computed by script. This build has two Arduino boards, two radios and a sensor, so it has more places to go wrong than most worksheets here; the wiring, library versions and the radio’s power supply cause most of the problems. nRF24L01 modules are 3.3 V parts, and the bare module is known to be unhappy on a noisy supply; add a capacitor across its power pins.
How it works
On the handheld, pitch is the forward and back tilt and roll the sideways tilt, both worked out from the accelerometer as angles of the gravity vector, for example pitch = atan2(ax, az). The sketch ignores the first 8 degrees on each axis as a dead zone, then maps 8 to 40 degrees onto 0 to 1: normalised = (|angle| − 8) ÷ 32, limited to 1, with the sign kept. A pitch of 20 degrees gives 0.375, and 40 degrees or more gives 1.
The two numbers y (forward) and x (turn) are sent as two signed bytes. On the car the arcade mixing is left = y + x and right = y − x, scaled back if either is above 1, the same mixing as in the joystick worksheet. If no packet arrives for 300 ms the car stops, which covers the handheld going out of range or its battery dying.
Bill of materials
| Part | Qty | Unit | Total |
|---|---|---|---|
| Arduino Uno R3 (or compatible) | 2 | ₹550 | ₹1,100 |
| nRF24L01 radio module | 2 | ₹180 | ₹360 |
| Radio adapter board or 10 µF capacitor across the module power | 2 | ₹50 | ₹100 |
| MPU6050 breakout (GY-521) | 1 | ₹150 | ₹150 |
| L298N motor driver board | 1 | ₹180 | ₹180 |
| 2WD chassis kit with two gear motors and wheels | 1 | ₹450 | ₹450 |
| Battery holder with two 18650 cells | 1 | ₹210 | ₹210 |
| 9 V battery with clip for the handheld | 1 | ₹60 | ₹60 |
| Jumper wires and breadboards | 1 | ₹100 | ₹100 |
| Parts total | ₹2,710 | ||
Scroll the table sideways to see every column.
Prices checked Nov 2026. Shipping and tools are not included.
Prices are illustrative figures for Indian sellers in November 2026, before shipping, and have not been checked against current listings.
The numbers
| Pitch | Roll | y (forward) | x (turn) | Left | Right |
|---|---|---|---|---|---|
| 8° | 0° | 0.000 | 0.000 | 0.000 | 0.000 |
| 12° | 0° | 0.125 | 0.000 | 0.125 | 0.125 |
| 20° | 0° | 0.375 | 0.000 | 0.375 | 0.375 |
| 30° | 0° | 0.688 | 0.000 | 0.688 | 0.688 |
| 40° | 0° | 1.000 | 0.000 | 1.000 | 1.000 |
| 20° | 15° | 0.375 | 0.219 | 0.594 | 0.156 |
The accelerometer-only angle is fine for slow hand tilts, but it also reacts to shaking and to sudden accelerations of your hand. If the car twitches, average the last few readings. The radio link can lose packets, so sending the command every 50 ms means a few losses go unnoticed while the 300 ms silence rule covers longer ones.
Power and wiring
| Part | Connects to | Note |
|---|---|---|
| Transmitter MPU6050 SDA / SCL | A4 / A5 | Address 0x68 |
| Transmitter nRF24 CE / CSN | D9 / D10 | SPI on D11, D12, D13 |
| nRF24 VCC and GND | 3.3 V and GND | Not 5 V; add a 10 µF capacitor close to the module |
| Receiver nRF24 CE / CSN | A0 / A1 | SPI on D11, D12, D13 |
| Receiver L298N ENA / IN1 / IN2 | D5 / D7 / D8 | Left motor |
| Receiver L298N ENB / IN3 / IN4 | D6 / D9 / D4 | Right motor |
The sketch
// TRANSMITTER: handheld Uno + MPU6050 + nRF24L01
// Design worksheet: both sketches need the RF24 library. Two separate boards.
#include <Wire.h>
#include <SPI.h>
#include <RF24.h>
RF24 radio(9, 10); // CE, CSN
const byte ADDRESS[6] = "ROBO1";
const int MPU = 0x68;
const float DEAD_DEG = 8.0, FULL_DEG = 40.0;
struct Packet { int8_t y; int8_t x; } pkt; // -100..100
float norm(float a) {
float m = (fabs(a) - DEAD_DEG) / (FULL_DEG - DEAD_DEG);
m = constrain(m, 0.0, 1.0);
return a < 0 ? -m : m;
}
void setup() {
Wire.begin();
Wire.beginTransmission(MPU); Wire.write(0x6B); Wire.write(0); Wire.endTransmission(true);
radio.begin();
radio.setPALevel(RF24_PA_LOW);
radio.openWritingPipe(ADDRESS);
radio.stopListening();
}
void loop() {
Wire.beginTransmission(MPU); Wire.write(0x3B); Wire.endTransmission(false);
Wire.requestFrom(MPU, 6, true);
int16_t ax = Wire.read() << 8 | Wire.read();
int16_t ay = Wire.read() << 8 | Wire.read();
int16_t az = Wire.read() << 8 | Wire.read();
float pitch = atan2((float)ax, (float)az) * 180.0 / PI; // forward tilt
float roll = atan2((float)ay, (float)az) * 180.0 / PI; // sideways tilt
pkt.y = (int8_t)(norm(pitch) * 100);
pkt.x = (int8_t)(norm(roll) * 100);
radio.write(&pkt, sizeof(pkt));
delay(50);
}
// =====================================================================
// RECEIVER: car Uno + nRF24L01 + L298N (a separate sketch)
// =====================================================================
/*
#include <SPI.h>
#include <RF24.h>
RF24 radio(A0, A1); // CE, CSN
const byte ADDRESS[6] = "ROBO1";
struct Packet { int8_t y; int8_t x; } pkt;
unsigned long lastPacket = 0;
const int L_EN = 5, L_IN1 = 7, L_IN2 = 8;
const int R_EN = 6, R_IN3 = 9, R_IN4 = 4;
void drive(int left, int right) { // -255..255 per side
digitalWrite(L_IN1, left >= 0); digitalWrite(L_IN2, left < 0);
digitalWrite(R_IN3, right >= 0); digitalWrite(R_IN4, right < 0);
analogWrite(L_EN, constrain(abs(left), 0, 255));
analogWrite(R_EN, constrain(abs(right), 0, 255));
}
void motorsInit() {
pinMode(L_EN, OUTPUT); pinMode(L_IN1, OUTPUT); pinMode(L_IN2, OUTPUT);
pinMode(R_EN, OUTPUT); pinMode(R_IN3, OUTPUT); pinMode(R_IN4, OUTPUT);
}
void setup() {
motorsInit();
radio.begin();
radio.setPALevel(RF24_PA_LOW);
radio.openReadingPipe(1, ADDRESS);
radio.startListening();
}
void loop() {
if (radio.available()) {
radio.read(&pkt, sizeof(pkt));
lastPacket = millis();
float y = pkt.y / 100.0, x = pkt.x / 100.0;
float l = y + x, r = y - x;
float m = max(fabs(l), fabs(r));
if (m > 1.0) { l /= m; r /= m; }
drive((int)(l * 255), (int)(r * 255));
}
if (millis() - lastPacket > 300) drive(0, 0); // stop on silence
}
*/The two sketches are shown together in one block for reading: the receiver part is commented out here, so copy it into its own file, remove the comment markers, and upload each to its own board. Sign conventions depend on how the MPU6050 is held; if forward is backward, flip the sign of the pitch. Check the RF24 library’s own examples first to confirm the radios talk before adding the car. The sketch has been proofread but not compiled on this site.
Test plan
- Radio first: run the library’s ping examples and confirm the two radios talk across a room.
- Tilt readings: print the transmitter’s pitch and roll and check they follow your hand.
- Wheels up: test forward, back, left and right tilts with the car on a stand.
- Dead zone: hold the handheld flat and confirm the car stays still.
- Silence stop: turn off the handheld while driving and confirm the car stops within about a third of a second.
- Range: test indoors and outdoors and note where packets begin to drop.
What usually goes wrong
| Symptom | Likely cause | Fix |
|---|---|---|
| No radio link | Supply too weak or wrong pins | Add a capacitor or an adapter board, check CE and CSN |
| Car creeps when flat | Accelerometer offset or dead zone too small | Raise DEAD_DEG or zero the sensor |
| Jerky steering | Hand shake in the readings | Average readings |
| Forward and back swapped | Sensor orientation | Flip the sign of pitch |
| Short range | Low power setting or poor antenna | Raise the power level and keep the module clear of metal |
| Car runs on after link loss | Silence rule missing | Keep the 300 ms stop |
Upgrades
- Calibration: record the flat-hand offsets at start-up.
- Speed limiter: add a button for a slow mode.
- Acknowledgements: use the radio’s auto-acknowledge to detect a lost link on the handheld.
- Gyro filter: fuse the gyro for steadier angles, as in the balancing worksheet.
Cost and parts notes
The ₹2,710 total is an illustration and does not include shipping. Use the cost calculator to compare sellers.
Common questions
Why does the nRF24L01 cause so much trouble?
The bare module is a 3.3 V part that is sensitive to supply noise, so a capacitor or its own regulator is the usual fix.
Why a dead zone?
Hands never hold perfectly flat, so without one the car would creep.
Is a radio link better than Bluetooth?
It has lower latency and no pairing, but needs a second board at the handheld.
What if the batteries die in the handheld?
The packets stop and the car’s silence rule stops it.
Last reviewed: November 2026. This is a design worksheet with illustrative costs. It has not been bench-tested by this site; library names, pins and limits must be checked against your board and the current library versions, and measurements from your build are welcome.