Community project

T I S Mua Th M 1 Nrf24l01

Arduino
Photo of T I S Mua Th M 1 Nrf24l01
Generated with AI

Đạt Nguyễn Thành

Published October 1, 2026

Build a wireless-controlled robot vehicle powered by lithium-ion batteries and driven by DC gearmotors. This guide covers a complete mecanum-wheel drive system controlled via nRF24L01+ radio modules, with an Arduino Uno receiving motor commands from a handheld Arduino Nano controller.

The project includes a detailed parts list, wiring diagram showing motor controller connections, battery management setup with BMS and charger, and step-by-step assembly instructions. Firmware for both the vehicle receiver and remote transmitter handles joystick input, radio communication, and motor speed control through PWM and shift registers.

Wiring diagram

Wiring diagram for T I S Mua Th M 1 Nrf24l01

Gather all the parts

QtyComponent
1

DFRobot MDV 2x2A DC Motor Controller (L298N)

L298N dual H-bridge motor driver carrier. 2A continuous per channel, 5-46V motor supply, ~1.8V dropout (BJT-based, hot at high currents). Drives 2 brushed DC motors or 1 bipolar stepper. Pair with PWM on EN pins for speed control.

1

DFRobot MDV 2x2A DC Motor Controller (L298N)

L298N dual H-bridge motor driver carrier. 2A continuous per channel, 5-46V motor supply, ~1.8V dropout (BJT-based, hot at high currents). Drives 2 brushed DC motors or 1 bipolar stepper. Pair with PWM on EN pins for speed control.

1

nRF24L01+PA+LNA Wireless Module (External Antenna)

2.4 GHz wireless transceiver module based on Nordic nRF24L01+ IC with integrated PA and LNA for extended range, SMA external antenna, SPI interface, 3.3V operation only. One module acts as Transmitter (paired with Joystick ESP32), one as Receiver (paired with Servo/Laser ESP32). Requires 10–100µF decoupling capacitor on VCC for stable operation. Uses VSPI on ESP32: SCK=GPIO18, MISO=GPIO19, MOSI=GPIO23, CS=GPIO5; CE and IRQ assigned separately.

1

nRF24L01+PA+LNA Wireless Module (External Antenna)

2.4 GHz wireless transceiver module based on Nordic nRF24L01+ IC with integrated PA and LNA for extended range, SMA external antenna, SPI interface, 3.3V operation only. One module acts as Transmitter (paired with Joystick ESP32), one as Receiver (paired with Servo/Laser ESP32). Requires 10–100µF decoupling capacitor on VCC for stable operation. Uses VSPI on ESP32: SCK=GPIO18, MISO=GPIO19, MOSI=GPIO23, CS=GPIO5; CE and IRQ assigned separately.

1

18650 Li-ion Cell

3.7 V Li-ion cell

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

18650 Li-ion Cell

3.7 V Li-ion cell

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

18650 Li-ion Cell

3.7 V Li-ion cell

18650 lithium-ion cell, nominal 3.7 V, ~2500 mAh. Common for higher-capacity portable / battery-bank style projects; needs a holder and protection / charger circuit.

1

24v Buck Converter

Adjusted to 5.0 V

LM2596-based adjustable step-down buck converter module. Commonly used to regulate a higher battery rail, such as a 2S 18650 pack, down to 5V for Arduino logic. It is a regulator, not a charger or battery protection board.

1

3S lithium-ion BMS module

A protection board that keeps three series lithium cells within safe voltage and current limits.

1

12.6 V 3S lithium-ion balance charger

A charger made for a protected three-cell series lithium battery pack.

1

3.3 V regulator module for vehicle radio

A small regulated supply that gives the vehicle nRF24 radio a clean 3.3 V supply.

1

3.3 V regulator module for remote radio

A small regulated supply that gives the hand controller nRF24 radio a clean 3.3 V supply.

1

Arduino Nano hand controller

The small Arduino board that reads two joysticks and sends driving commands by radio.

1

2-axis thumb joystick module

The thumb control that selects forward, backward, left, and right motion.

1

1-axis thumb joystick module

A second thumb control used for turning the mecanum car in place.

1

12 V DC gearmotor — front left

The front-left drive motor that turns its mecanum wheel.

1

12 V DC gearmotor — front right

The front-right drive motor that turns its mecanum wheel.

1

12 V DC gearmotor — rear left

The rear-left drive motor that turns its mecanum wheel.

1

12 V DC gearmotor — rear right

The rear-right drive motor that turns its mecanum wheel.

1

47 µF electrolytic capacitor — vehicle radio

47 µF, 6.3 V or higher

A small capacitor placed beside the vehicle radio to prevent radio resets when the motors make electrical noise.

1

47 µF electrolytic capacitor — remote radio

47 µF, 6.3 V or higher

A small capacitor placed beside the hand-controller radio to prevent radio resets.

1

4-channel 5 V-to-3.3 V logic-level shifter

A small board that protects the vehicle nRF24 radio from the Uno's 5 V control signals.

1

4-channel 5 V-to-3.3 V logic-level shifter

A small board that protects the hand-controller nRF24 radio from the Nano's 5 V control signals.

1

74HC595 8-bit shift-register module

A small module that lets the Uno set the eight L298N direction inputs while saving board pins for motor-speed control.

Assemble it in 7 steps

1. Lắp bánh và mô-tơ

Gắn bốn mô-tơ vào khung xe và gắn bánh mecanum theo đúng cặp trái/phải. Gọi các vị trí là trước-trái, trước-phải, sau-trái và sau-phải để khớp với sơ đồ.

  • Đừng cấp điện khi bánh còn chạm bàn; xe có thể chạy bất ngờ.

2. Nối mô-tơ với hai mạch L298N

Mạch l298n_front: OUT1 và OUT2 tới hai dây mô-tơ trước-trái, OUT3 và OUT4 tới hai dây mô-tơ trước-phải. Mạch l298n_rear: OUT1/OUT2 tới sau-trái, OUT3/OUT4 tới sau-phải. Nếu một bánh quay ngược trong lúc thử, tắt nguồn rồi đổi chéo đúng hai dây của bánh đó.

  • Đây là các dây công suất; dùng dây lớn hơn dây tín hiệu.

3. Lắp bộ pin có bảo vệ

Lắp ba cell cùng loại và cùng tình trạng thành pack 3S: cell 1 âm tới B-, điểm nối cell 1 dương/cell 2 âm tới B1, điểm nối cell 2 dương/cell 3 âm tới B2, và cell 3 dương tới B+. Chỉ lấy điện cho xe ở P+ và P- của BMS.

  • Không trộn cell cũ mới hoặc khác dung lượng — pin có thể nóng, hỏng hoặc cháy.
  • Chỉ cắm adapter đúng loại vào cổng DC IN của bộ sạc 3S.

4. Nối nguồn xe

P+ của BMS đi tới VS của cả hai L298N và VIN+ của buck_5v; P- đi tới GND của cả hai L298N và VIN- của buck_5v. Chỉnh buck_5v đúng 5,0 V trước khi nối Uno hay radio. VOUT+ là 5 V (nguồn), VOUT- là GND (đường về).

  • Đo đầu ra bộ hạ áp trước; điện áp cao hơn 5 V có thể làm hỏng Uno và radio.

5. Nối Uno, radio và mạch điều khiển chiều

Gắn Uno 5 V/GND vào nguồn 5 V/GND chung. D3/D5/D6/D9 lần lượt tới ENA/ENB của L298N trước và ENA/ENB của L298N sau (tốc độ). Mạch 74HC595: A1 tới SER, A2 tới SRCLK, A3 tới RCLK, VCC tới 5 V, GND và OE tới GND; Q0–Q7 đi lần lượt tới IN1–IN4 của L298N trước rồi IN1–IN4 của L298N sau (chiều quay).

  • Tất cả GND của Uno, L298N, radio và bộ hạ áp phải nối chung; nếu không xe sẽ điều khiển thất thường.

6. Cấp nguồn và nối radio 3,3 V

Mỗi nRF24 dùng bộ ổn áp 3,3 V riêng và tụ 47 µF: chân dài tụ tới VCC, chân sọc tới GND. Không bao giờ nối VCC radio vào 5 V. Nối các dây CE, CSN, SCK, MOSI qua mạch đổi mức; MISO từ radio xe tới D12 Uno.

  • Đảo VCC/GND hoặc đưa 5 V vào radio sẽ làm hỏng nRF24.

7. Lắp tay điều khiển

Nối joystick_drive: VCC tới 5 V, GND tới GND, VRx tới A0 Nano và VRy tới A1 Nano. Nối joystick_turn: VCC tới 5 V, GND tới GND, VRx tới A2 Nano. Cấp nguồn Nano bằng USB 5 V khi thử.

  • Dùng joystick_drive để tiến/lùi và ngang; joystick_turn để xoay tại chỗ.

Review all connections

1. Connections between "battery_cell_1" and "Arduino"

Functionbattery_cell_1Arduino
groundGND → 3S lithium-ion BMS module B-EXT
power+V → 3S lithium-ion BMS module B1EXT

2. Connections between "battery_cell_2" and "Arduino"

Functionbattery_cell_2Arduino
groundGND → 3S lithium-ion BMS module B1EXT
power+V → 3S lithium-ion BMS module B2EXT

3. Connections between "battery_cell_3" and "Arduino"

Functionbattery_cell_3Arduino
groundGND → 3S lithium-ion BMS module B2EXT
power+V → 3S lithium-ion BMS module B+EXT

4. Connections between "bms_3s" and "Arduino"

Functionbms_3sArduino
powerP+ → DFRobot MDV 2x2A DC Motor Controller (L298N) VSEXT
powerP+ → DFRobot MDV 2x2A DC Motor Controller (L298N) VSEXT
powerP+ → 24v Buck Converter VIN+EXT
powerP+ → 12.6 V 3S lithium-ion balance charger BAT+EXT
groundP- → DFRobot MDV 2x2A DC Motor Controller (L298N) GNDEXT
groundP- → DFRobot MDV 2x2A DC Motor Controller (L298N) GNDEXT
groundP- → 24v Buck Converter VIN-EXT
groundP- → 12.6 V 3S lithium-ion balance charger BAT-EXT

5. Connections between "charger_3s" and "Arduino"

Functioncharger_3sArduino
powerDC IN → matching 12.6 V 3S charger adapterEXT

6. Connections between "buck_5v" and "Arduino"

Functionbuck_5vArduino
powerVOUT+5V
groundVOUT-GND

7. Connections between "l298n_front" and "Arduino"

Functionl298n_frontArduino
power5V5V
pwmENAGPIO 3
pwmENBGPIO 5

8. Connections between "l298n_rear" and "Arduino"

Functionl298n_rearArduino
power5V5V
pwmENAGPIO 6
pwmENBGPIO 9

9. Connections between "regulator_3v3_vehicle" and "Arduino"

Functionregulator_3v3_vehicleArduino
powerVIN5V
groundGNDGND
power3V3 OUT → nRF24L01+PA+LNA Wireless Module (External Antenna) VCCEXT

10. Connections between "nrf_receiver" and "Arduino"

Functionnrf_receiverArduino
groundGNDGND
spiMISOGPIO 12

11. Connections between "cap_vehicle_radio" and "Arduino"

Functioncap_vehicle_radioArduino
power+ → nRF24L01+PA+LNA Wireless Module (External Antenna) VCCEXT
ground-GND

12. Connections between "level_shifter_vehicle" and "Arduino"

Functionlevel_shifter_vehicleArduino
powerHV5V
powerLV → nRF24L01+PA+LNA Wireless Module (External Antenna) VCCEXT
groundGNDGND
digitalHV1GPIO 14
digitalHV2GPIO 10
digitalHV3GPIO 13
digitalHV4GPIO 11
digitalLV1 → nRF24L01+PA+LNA Wireless Module (External Antenna) CEEXT
digitalLV2 → nRF24L01+PA+LNA Wireless Module (External Antenna) CSNEXT
digitalLV3 → nRF24L01+PA+LNA Wireless Module (External Antenna) SCKEXT
digitalLV4 → nRF24L01+PA+LNA Wireless Module (External Antenna) MOSIEXT

13. Connections between "shift_register" and "Arduino"

Functionshift_registerArduino
powerVCC5V
groundGNDGND
digitalSERGPIO 15
digitalSRCLKGPIO 16
digitalRCLKGPIO 17
groundOEGND
digitalQ0 → DFRobot MDV 2x2A DC Motor Controller (L298N) IN1EXT
digitalQ1 → DFRobot MDV 2x2A DC Motor Controller (L298N) IN2EXT
digitalQ2 → DFRobot MDV 2x2A DC Motor Controller (L298N) IN3EXT
digitalQ3 → DFRobot MDV 2x2A DC Motor Controller (L298N) IN4EXT
digitalQ4 → DFRobot MDV 2x2A DC Motor Controller (L298N) IN1EXT
digitalQ5 → DFRobot MDV 2x2A DC Motor Controller (L298N) IN2EXT
digitalQ6 → DFRobot MDV 2x2A DC Motor Controller (L298N) IN3EXT
digitalQ7 → DFRobot MDV 2x2A DC Motor Controller (L298N) IN4EXT

14. Connections between "motor_front_left" and "Arduino"

Functionmotor_front_leftArduino
digitalM+ → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT1EXT
digitalM- → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT2EXT

15. Connections between "motor_front_right" and "Arduino"

Functionmotor_front_rightArduino
digitalM+ → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT3EXT
digitalM- → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT4EXT

16. Connections between "motor_rear_left" and "Arduino"

Functionmotor_rear_leftArduino
digitalM+ → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT1EXT
digitalM- → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT2EXT

17. Connections between "motor_rear_right" and "Arduino"

Functionmotor_rear_rightArduino
digitalM+ → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT3EXT
digitalM- → DFRobot MDV 2x2A DC Motor Controller (L298N) OUT4EXT

18. Connections between "nano_remote" and "Arduino"

Functionnano_remoteArduino
power5V5V
groundGNDGND

19. Connections between "joystick_drive" and "Arduino"

Functionjoystick_driveArduino
powerVCC5V
groundGNDGND
analogVRx → Arduino Nano hand controller A0EXT
analogVRy → Arduino Nano hand controller A1EXT

20. Connections between "joystick_turn" and "Arduino"

Functionjoystick_turnArduino
powerVCC5V
groundGNDGND
analogVRx → Arduino Nano hand controller A2EXT

21. Connections between "regulator_3v3_remote" and "Arduino"

Functionregulator_3v3_remoteArduino
powerVIN5V
groundGNDGND
power3V3 OUT → nRF24L01+PA+LNA Wireless Module (External Antenna) VCCEXT

22. Connections between "nrf_transmitter" and "Arduino"

Functionnrf_transmitterArduino
groundGNDGND
spiMISO → Arduino Nano hand controller D12 MISOEXT

23. Connections between "cap_remote_radio" and "Arduino"

Functioncap_remote_radioArduino
power+ → nRF24L01+PA+LNA Wireless Module (External Antenna) VCCEXT
ground-GND

24. Connections between "level_shifter_remote" and "Arduino"

Functionlevel_shifter_remoteArduino
powerHV5V
powerLV → nRF24L01+PA+LNA Wireless Module (External Antenna) VCCEXT
groundGNDGND
digitalHV1 → Arduino Nano hand controller D8 CEEXT
digitalHV2 → Arduino Nano hand controller D10 CSNEXT
digitalHV3 → Arduino Nano hand controller D13 SCKEXT
digitalHV4 → Arduino Nano hand controller D11 MOSIEXT
digitalLV1 → nRF24L01+PA+LNA Wireless Module (External Antenna) CEEXT
digitalLV2 → nRF24L01+PA+LNA Wireless Module (External Antenna) CSNEXT
digitalLV3 → nRF24L01+PA+LNA Wireless Module (External Antenna) SCKEXT
digitalLV4 → nRF24L01+PA+LNA Wireless Module (External Antenna) MOSIEXT

Deploy the firmware

#include <Arduino.h>
#include <SPI.h>
#include <RF24.h>

struct CommandPacket {
  int16_t x;       // left/right strafe
  int16_t y;       // forward/reverse
  int16_t turn;    // rotate left/right
  uint8_t sequence;
};


// Forward declarations
int clampMotor(int value);
void setDirectionBits(uint8_t &bits, uint8_t motorIndex, int value);
void writeDirections(uint8_t bits);
void setSpeeds(int fl, int fr, int rl, int rr);
void stopAllMotors();
void driveMecanum(const CommandPacket &command);

const uint8_t NRF_CE_PIN = A0;
const uint8_t NRF_CSN_PIN = 10;
const uint8_t FL_EN = 3;
const uint8_t FR_EN = 5;
const uint8_t RL_EN = 6;
const uint8_t RR_EN = 9;
const uint8_t SHIFT_DATA = A1;
const uint8_t SHIFT_CLOCK = A2;
const uint8_t SHIFT_LATCH = A3;
const byte RADIO_ADDRESS[6] = "MEC01";
const unsigned long RADIO_TIMEOUT_MS = 300;

RF24 radio(NRF_CE_PIN, NRF_CSN_PIN);
unsigned long lastPacketMs = 0;

int clampMotor(int value) {
  if (value > 255) return 255;
  if (value < -255) return -255;
  return value;
}

void setDirectionBits(uint8_t &bits, uint8_t motorIndex, int value) {
  const uint8_t forwardBit = motorIndex * 2;
  const uint8_t reverseBit = forwardBit + 1;
  if (value > 0) bits |= _BV(forwardBit);
  else if (value < 0) bits |= _BV(reverseBit);
}

void writeDirections(uint8_t bits) {
  digitalWrite(SHIFT_LATCH, LOW);
  shiftOut(SHIFT_DATA, SHIFT_CLOCK, MSBFIRST, bits);
  digitalWrite(SHIFT_LATCH, HIGH);
}

void setSpeeds(int fl, int fr, int rl, int rr) {
  analogWrite(FL_EN, abs(clampMotor(fl)));
  analogWrite(FR_EN, abs(clampMotor(fr)));
  analogWrite(RL_EN, abs(clampMotor(rl)));
  analogWrite(RR_EN, abs(clampMotor(rr)));
}

void stopAllMotors() {
  setSpeeds(0, 0, 0, 0);
  writeDirections(0);
}

void driveMecanum(const CommandPacket &command) {
  int fl = clampMotor(command.y + command.x + command.turn);
  int fr = clampMotor(command.y - command.x - command.turn);
  int rl = clampMotor(command.y - command.x + command.turn);
  int rr = clampMotor(command.y + command.x - command.turn);

  uint8_t directions = 0;
  setDirectionBits(directions, 0, fl); // Q0/Q1 -> front-left IN1/IN2
  setDirectionBits(directions, 1, fr); // Q2/Q3 -> front-right IN3/IN4
  setDirectionBits(directions, 2, rl); // Q4/Q5 -> rear-left IN1/IN2
  setDirectionBits(directions, 3, rr); // Q6/Q7 -> rear-right IN3/IN4
  writeDirections(directions);
  setSpeeds(fl, fr, rl, rr);
}

void setup() {
  pinMode(FL_EN, OUTPUT); pinMode(FR_EN, OUTPUT);
  pinMode(RL_EN, OUTPUT); pinMode(RR_EN, OUTPUT);
  pinMode(SHIFT_DATA, OUTPUT); pinMode(SHIFT_CLOCK, OUTPUT); pinMode(SHIFT_LATCH, OUTPUT);
  stopAllMotors();

  radio.begin();
  radio.setPALevel(RF24_PA_LOW);
  radio.setDataRate(RF24_250KBPS);
  radio.setChannel(108);
  radio.openReadingPipe(1, RADIO_ADDRESS);
  radio.startListening();
}

void loop() {
  if (radio.available()) {
    CommandPacket command;
    while (radio.available()) radio.read(&command, sizeof(command));
    lastPacketMs = millis();
    driveMecanum(command);
  }
  if (millis() - lastPacketMs > RADIO_TIMEOUT_MS) stopAllMotors();
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace — diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik