Tilt-controlled robot car with an MPU6050 and nRF24L01 radio: design worksheet and code

A ₹2,710 design worksheet for a robot car you steer by tilting a handheld unit, using an MPU6050 on the transmitter, an nRF24L01 radio link and arcade-drive mixing on the car, with the tilt-to-speed maths and a stop-on-silence failsafe.

About the author · Sources, testing and AI policy

  • Parts₹2,710
  • Bench time8–12 h
  • LevelAdvanced
  • ReviewedNov 2026

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

PartQtyUnitTotal
Arduino Uno R3 (or compatible)2₹550₹1,100
nRF24L01 radio module2₹180₹360
Radio adapter board or 10 µF capacitor across the module power2₹50₹100
MPU6050 breakout (GY-521)1₹150₹150
L298N motor driver board1₹180₹180
2WD chassis kit with two gear motors and wheels1₹450₹450
Battery holder with two 18650 cells1₹210₹210
9 V battery with clip for the handheld1₹60₹60
Jumper wires and breadboards1₹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

PitchRolly (forward)x (turn)LeftRight
8°0°0.0000.0000.0000.000
12°0°0.1250.0000.1250.125
20°0°0.3750.0000.3750.375
30°0°0.6880.0000.6880.688
40°0°1.0000.0001.0001.000
20°15°0.3750.2190.5940.156
Normalised = (|angle| − 8) / 32, limited to 1, sign kept; left = y + x, right = y − x, scaled if over 1. The last row is a forward tilt of 20° with a roll of 15°.

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

PartConnects toNote
Transmitter MPU6050 SDA / SCLA4 / A5Address 0x68
Transmitter nRF24 CE / CSND9 / D10SPI on D11, D12, D13
nRF24 VCC and GND3.3 V and GNDNot 5 V; add a 10 µF capacitor close to the module
Receiver nRF24 CE / CSNA0 / A1SPI on D11, D12, D13
Receiver L298N ENA / IN1 / IN2D5 / D7 / D8Left motor
Receiver L298N ENB / IN3 / IN4D6 / D9 / D4Right motor
Many Uno boards cannot power the nRF24L01 well from their 3.3 V pin; if the link is unreliable, use an adapter board with its own regulator.

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

  1. Radio first: run the library’s ping examples and confirm the two radios talk across a room.
  2. Tilt readings: print the transmitter’s pitch and roll and check they follow your hand.
  3. Wheels up: test forward, back, left and right tilts with the car on a stand.
  4. Dead zone: hold the handheld flat and confirm the car stays still.
  5. Silence stop: turn off the handheld while driving and confirm the car stops within about a third of a second.
  6. Range: test indoors and outdoors and note where packets begin to drop.

What usually goes wrong

SymptomLikely causeFix
No radio linkSupply too weak or wrong pinsAdd a capacitor or an adapter board, check CE and CSN
Car creeps when flatAccelerometer offset or dead zone too smallRaise DEAD_DEG or zero the sensor
Jerky steeringHand shake in the readingsAverage readings
Forward and back swappedSensor orientationFlip the sign of pitch
Short rangeLow power setting or poor antennaRaise the power level and keep the module clear of metal
Car runs on after link lossSilence rule missingKeep 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.

More builds

Next builds to try

Free: the final-year project pack

A shortlist with BOMs, a report outline that matches the usual chapter scheme, a costing sheet and 40 viva questions. Built for the February–April submission season.

The Build Note

One short email when something is worth your bench time.

A new build sheet, a part price that moved, or a mistake worth avoiding. No daily drip, no recycled news.

One short email when a build or a price is worth your time. Unsubscribe in one click.