8CH Transmitter & Receiver using Arduino Nano & NRF24L01 + PA

8CH Transmitter & Receiver using Arduino Nano & NRF24L01 + PA

8CH Transmitter & Receiver using Arduino Nano & NRF24L01 + PA

This project creates a professional 8-channel wireless transmitter and receiver system using Arduino Nano and NRF24L01+PA modules. The system features custom PCBs, failsafe mechanisms, BLDC motor control with safety features, and supports multiple servo outputs for RC applications.

8 Channels
Control Inputs
1000m+
Range with PA+LNA
50Hz
Update Rate
2.4GHz
Operating Frequency

System Overview

Key Features:

  • Professional PCBs: Custom designed transmitter and receiver boards
  • Long Range: NRF24L01+PA+LNA modules with 1000m+ range capability
  • 8 Channels: 4 analog joystick axes + 2 switches + 2 potentiometers
  • Failsafe Protection: Automatic neutral position on signal loss
  • BLDC Motor Control: Safety arming sequence with throttle limit
  • Visual Feedback: LED indicators and buzzer alerts
  • Compact Design: Integrated Arduino Nano and power management

Materials Required

Core Components:

For Testing (Optional):


PCB Gerber Files

Download the custom PCB design files for both transmitter and receiver:

Download Gerber Files

Manufacture your PCBs using JLCPCB's professional service:

Order PCBs Now

Channel Configuration

CH1 & CH2

Left Joystick X/Y

Analog

CH3 & CH4

Right Joystick X/Y

Analog

CH5 & CH6

Toggle Switches

Digital

CH7 & CH8

Potentiometers

Analog

Step-by-Step Assembly

Transmitter PCB
Receiver PCB
Testing

Transmitter Assembly:

  1. Solder all headers and connectors to the transmitter PCB
  2. Install Arduino Nano in the designated socket
  3. Mount NRF24L01 adapter and connect the module
  4. Install joystick modules and connect to analog pins
  5. Solder toggle switches with proper pull-up resistors
  6. Install potentiometers for CH7 and CH8
  7. Connect buzzer and LED with current-limiting resistor
  8. Add power switch and battery connector
  9. Install 470µF capacitor near power input for stability

Receiver Assembly:

  1. Solder all servo output headers (CH1-CH8)
  2. Install Arduino Nano in the designated socket
  3. Mount NRF24L01 adapter and connect the module
  4. Add power input connector and smoothing capacitors
  5. Install status LED for connection indication
  6. Add optional voltage regulator if using higher voltage batteries
  7. Test all servo outputs with a multimeter

Testing & Calibration:

  1. Upload transmitter code to the first Arduino Nano
  2. Upload receiver code to the second Arduino Nano
  3. Power both units and check NRF24L01 module status LEDs
  4. Test each channel using servo tester or multimeter
  5. Calibrate joystick center points in the code if needed
  6. Test failsafe by disconnecting power from transmitter
  7. Verify BLDC safety arming sequence
  8. Test range in open area with obstacles

⚠️ Safety Warning:

BLDC MOTORS CAN BE DANGEROUS! Always test with propeller removed. Ensure failsafe is working properly before connecting to aircraft or vehicles. Keep fingers and loose clothing away from rotating parts.


Arduino Code

Transmitter Code
Receiver Code
Transmitter Code (Upload to Transmitter Arduino Nano)
#include 
#include 
#include 

// Define NRF24L01 pins
#define CE_PIN 7
#define CSN_PIN 8

// Define channel pins
#define SWITCH_1 2
#define SWITCH_2 3
#define POT_1 A4
#define POT_2 A5
#define JOY_L_X A2
#define JOY_L_Y A3
#define JOY_R_X A0
#define JOY_R_Y A1
#define BUZZER_PIN 9

// Create radio object
RF24 radio(CE_PIN, CSN_PIN);

// Define the data structure to be transmitted
struct DataPacket {
  uint16_t ch1; // Left Joystick X
  uint16_t ch2; // Left Joystick Y  
  uint16_t ch3; // Right Joystick X
  uint16_t ch4; // Right Joystick Y
  uint16_t ch5; // Switch 1
  uint16_t ch6; // Switch 2
  uint16_t ch7; // Potentiometer 1
  uint16_t ch8; // Potentiometer 2
  uint8_t connectionStatus; // Connection status flag
};

DataPacket txData;

// Address for communication
const byte address[6] = "00001";

// Connection monitoring variables
unsigned long lastConnectionTime = 0;
bool connectionLost = false;

void setup() {
  Serial.begin(9600);
  
  // Initialize pins
  pinMode(SWITCH_1, INPUT_PULLUP);
  pinMode(SWITCH_2, INPUT_PULLUP);
  pinMode(BUZZER_PIN, OUTPUT);
  digitalWrite(BUZZER_PIN, LOW);
  
  // Initialize radio
  if (!radio.begin()) {
    Serial.println("Radio initialization failed!");
    while (1);
  }
  
  radio.setPALevel(RF24_PA_MAX);
  radio.setDataRate(RF24_250KBPS);
  radio.setChannel(100);
  radio.openWritingPipe(address);
  radio.stopListening();
  
  // Initialize data structure
  memset(&txData, 0, sizeof(txData));
  
  Serial.println("Transmitter Initialized");
}

void readControls() {
  // Read joysticks (map from 0-1023 to 1000-2000 like RC standard)
  txData.ch1 = map(analogRead(JOY_L_X), 0, 1023, 1000, 2000);
  txData.ch2 = map(analogRead(JOY_L_Y), 0, 1023, 1000, 2000);
  txData.ch3 = map(analogRead(JOY_R_X), 0, 1023, 1000, 2000);
  txData.ch4 = map(analogRead(JOY_R_Y), 0, 1023, 1000, 2000);
  
  // Read switches (invert because INPUT_PULLUP gives HIGH when open)
  txData.ch5 = digitalRead(SWITCH_1) ? 1000 : 2000;
  txData.ch6 = digitalRead(SWITCH_2) ? 1000 : 2000;
  
  // Read potentiometers
  txData.ch7 = map(analogRead(POT_1), 0, 1023, 1000, 2000);
  txData.ch8 = map(analogRead(POT_2), 0, 1023, 1000, 2000);
  
  // Add connection status
  txData.connectionStatus = 1;
}

void connectionBeep() {
  // Beep pattern: two short beeps
  digitalWrite(BUZZER_PIN, HIGH);
  delay(100);
  digitalWrite(BUZZER_PIN, LOW);
  delay(50);
  digitalWrite(BUZZER_PIN, HIGH);
  delay(100);
  digitalWrite(BUZZER_PIN, LOW);
}

void loop() {
  static unsigned long lastBeepTime = 0;
  static unsigned long lastTxTime = 0;
  
  readControls();
  
  // Try to send data
  bool txSuccess = radio.write(&txData, sizeof(txData));
  
  if (txSuccess) {
    lastConnectionTime = millis();
    connectionLost = false;
    digitalWrite(BUZZER_PIN, LOW); // Ensure buzzer is off when connected
  }
  
  // Check connection status
  if (millis() - lastConnectionTime > 1000) { // 1 second timeout
    connectionLost = true;
  }
  
  // Handle beeping when connection is lost
  if (connectionLost) {
    if (millis() - lastBeepTime > 2000) { // Beep every 2 seconds
      connectionBeep();
      lastBeepTime = millis();
    }
  }
  
  // Transmission rate control (approx 50Hz)
  unsigned long currentMillis = millis();
  if (currentMillis - lastTxTime < 20) {
    delay(20 - (currentMillis - lastTxTime));
  }
  lastTxTime = millis();
  
  // Debug output (optional)
  if (millis() % 1000 < 20) {
    Serial.print("CH1:");
    Serial.print(txData.ch1);
    Serial.print(" CH2:");
    Serial.print(txData.ch2);
    Serial.print(" Status:");
    Serial.println(connectionLost ? "Lost" : "Connected");
  }
}
Receiver Code (Upload to Receiver Arduino Nano)
#include 
#include 
#include 
#include 

// Define NRF24L01 pins
#define CE_PIN A3
#define CSN_PIN A2

// Define output pins for channels
#define CH1_PIN 2  // Left Joystick X -> BLDC (ESC)
#define CH2_PIN 3  // Left Joystick Y -> Servo
#define CH3_PIN 4  // Right Joystick X -> Servo
#define CH4_PIN 5  // Right Joystick Y -> Servo
#define CH5_PIN 6  // Switch 1 -> Servo
#define CH6_PIN 9  // Switch 2 -> Servo
#define CH7_PIN A0 // Potentiometer 1 -> Servo
#define CH8_PIN A1 // Potentiometer 2 -> Servo

// Create radio object
RF24 radio(CE_PIN, CSN_PIN);

// Create servo objects
Servo ch1_servo;  // Will be used for BLDC ESC
Servo ch2_servo;
Servo ch3_servo;
Servo ch4_servo;
Servo ch5_servo;
Servo ch6_servo;
Servo ch7_servo;
Servo ch8_servo;

// Define the data structure to be received
struct DataPacket {
  uint16_t ch1; // Left Joystick X
  uint16_t ch2; // Left Joystick Y  
  uint16_t ch3; // Right Joystick X
  uint16_t ch4; // Right Joystick Y
  uint16_t ch5; // Switch 1
  uint16_t ch6; // Switch 2
  uint16_t ch7; // Potentiometer 1
  uint16_t ch8; // Potentiometer 2
  uint8_t connectionStatus; // Connection status flag
};

DataPacket rxData;

// Address for communication
const byte address[6] = "00001";

// Connection monitoring variables
unsigned long lastPacketTime = 0;
bool connectionLost = true;

// Safety variables for BLDC
uint16_t lastCh1Value = 1500; // Neutral position for BLDC
bool bldcArmed = false;
unsigned long bldcArmTime = 0;

void setup() {
  Serial.begin(9600);
  
  // Attach servos to pins
  ch1_servo.attach(CH1_PIN);  // BLDC ESC on CH1
  ch2_servo.attach(CH2_PIN);
  ch3_servo.attach(CH3_PIN);
  ch4_servo.attach(CH4_PIN);
  ch5_servo.attach(CH5_PIN);
  ch6_servo.attach(CH6_PIN);
  ch7_servo.attach(CH7_PIN);
  ch8_servo.attach(CH8_PIN);
  
  // Initialize all outputs to neutral position (1500µs)
  ch1_servo.writeMicroseconds(1500); // Critical for BLDC safety
  ch2_servo.writeMicroseconds(1500);
  ch3_servo.writeMicroseconds(1500);
  ch4_servo.writeMicroseconds(1500);
  ch5_servo.writeMicroseconds(1500);
  ch6_servo.writeMicroseconds(1500);
  ch7_servo.writeMicroseconds(1500);
  ch8_servo.writeMicroseconds(1500);
  
  // Initialize radio
  if (!radio.begin()) {
    Serial.println("Radio initialization failed!");
    while (1);
  }
  
  radio.setPALevel(RF24_PA_MAX);
  radio.setDataRate(RF24_250KBPS);
  radio.setChannel(100);
  radio.openReadingPipe(0, address);
  radio.startListening();
  
  // BLDC safety initialization
  delay(1000); // Wait for ESC to initialize
  ch1_servo.writeMicroseconds(1000); // Send minimum throttle
  delay(2000); // Wait for ESC to recognize
  ch1_servo.writeMicroseconds(1500); // Back to neutral
  
  Serial.println("Receiver Initialized - Waiting for connection...");
}

void processBLDCSafety(uint16_t ch1Value) {
  // BLDC/ESC Safety logic
  static uint16_t safeThrottle = 1000;
  
  if (connectionLost) {
    // If connection lost, set to minimum throttle
    safeThrottle = 1000;
    bldcArmed = false;
  } else {
    // Arm sequence: throttle must go to minimum first
    if (!bldcArmed) {
      if (ch1Value <= 1100) { // Throttle at minimum
        bldcArmed = true;
        bldcArmTime = millis();
        safeThrottle = 1000;
      } else {
        safeThrottle = 1000; // Keep at minimum until armed
      }
    } else {
      // After arming, apply throttle with limits
      if (millis() - bldcArmTime < 2000) {
        // Slow start for 2 seconds after arming
        safeThrottle = constrain(ch1Value, 1000, 1200);
      } else {
        // Normal operation with full range
        safeThrottle = constrain(ch1Value, 1000, 2000);
      }
    }
  }
  
  // Apply the safe throttle value
  ch1_servo.writeMicroseconds(safeThrottle);
}

void updateOutputs() {
  // Update all channels independently
  
  // Channel 1: BLDC with safety processing
  processBLDCSafety(rxData.ch1);
  
  // Channel 2-8: Servos with constraints
  ch2_servo.writeMicroseconds(constrain(rxData.ch2, 1000, 2000));
  ch3_servo.writeMicroseconds(constrain(rxData.ch3, 1000, 2000));
  ch4_servo.writeMicroseconds(constrain(rxData.ch4, 1000, 2000));
  ch5_servo.writeMicroseconds(constrain(rxData.ch5, 1000, 2000));
  ch6_servo.writeMicroseconds(constrain(rxData.ch6, 1000, 2000));
  ch7_servo.writeMicroseconds(constrain(rxData.ch7, 1000, 2000));
  ch8_servo.writeMicroseconds(constrain(rxData.ch8, 1000, 2000));
}

void loop() {
  // Check for incoming data
  if (radio.available()) {
    radio.read(&rxData, sizeof(rxData));
    lastPacketTime = millis();
    
    if (connectionLost) {
      connectionLost = false;
      Serial.println("Connection established!");
    }
    
    // Update outputs
    updateOutputs();
    
    // Optional debug output
    static unsigned long lastPrintTime = 0;
    if (millis() - lastPrintTime > 1000) {
      Serial.print("CH1:");
      Serial.print(rxData.ch1);
      Serial.print(" CH2:");
      Serial.print(rxData.ch2);
      Serial.print(" CH7:");
      Serial.print(rxData.ch7);
      Serial.print(" Connected:");
      Serial.println(rxData.connectionStatus);
      lastPrintTime = millis();
    }
  }
  
  // Check for connection timeout (2 seconds)
  if (millis() - lastPacketTime > 2000 && !connectionLost) {
    connectionLost = true;
    Serial.println("Connection lost! Entering failsafe mode.");
    
    // Failsafe: set all channels to neutral/safe positions
    ch1_servo.writeMicroseconds(1000); // BLDC to minimum throttle
    ch2_servo.writeMicroseconds(1500);
    ch3_servo.writeMicroseconds(1500);
    ch4_servo.writeMicroseconds(1500);
    ch5_servo.writeMicroseconds(1500);
    ch6_servo.writeMicroseconds(1500);
    ch7_servo.writeMicroseconds(1500);
    ch8_servo.writeMicroseconds(1500);
    
    bldcArmed = false; // Disarm BLDC
  }
}

Code Features:

  • 50Hz Update Rate: Smooth control with minimal latency
  • Connection Monitoring: Buzzer alerts for signal loss
  • BLDC Safety: Arming sequence prevents accidental throttle
  • Failsafe Protection: Auto-neutral on signal loss
  • Standard PWM: 1000-2000µs pulse width for compatibility
  • Error Handling: Robust connection recovery

Wiring Connections

Transmitter (TX) Connections:

Component Arduino Nano Pin Connection Details
NRF24L01 CE D7 Direct connection
NRF24L01 CSN D8 Direct connection
Left Joystick X A2 VRx pin
Left Joystick Y A3 VRy pin
Right Joystick X A0 VRx pin
Right Joystick Y A1 VRy pin
Toggle Switch 1 D2 Middle pin with INPUT_PULLUP
Toggle Switch 2 D3 Middle pin with INPUT_PULLUP
Potentiometer 1 A4 Middle pin
Potentiometer 2 A5 Middle pin
Buzzer + LED D9 Through 330Ω resistor

Receiver (RX) Connections:

Channel Arduino Nano Pin Output Type
CH1 (ESC/BLDC) D2 Servo PWM (1000-2000µs)
CH2 (Servo) D3 Servo PWM (1000-2000µs)
CH3 (Servo) D4 Servo PWM (1000-2000µs)
CH4 (Servo) D5 Servo PWM (1000-2000µs)
CH5 (Servo) D6 Servo PWM (1000-2000µs)
CH6 (Servo) D9 Servo PWM (1000-2000µs)
CH7 (Servo) A0 Servo PWM (1000-2000µs)
CH8 (Servo) A1 Servo PWM (1000-2000µs)
NRF24L01 CE A3 Direct connection
NRF24L01 CSN A2 Direct connection

Applications

✈️ Drones & Quadcopters

Complete flight control system

🏎️ RC Cars & Boats

Vehicle control with multiple channels

🤖 Robotics

Wireless robot control

🎮 Custom Controllers

Game development interfaces

🔧 Note: This system is compatible with most RC receivers and ESCs. The NRF24L01+PA+LNA module provides significantly better range than standard NRF24L01 modules. Always test in open areas and maintain line of sight for optimal performance.

Comments

Popular posts from this blog

Solar Tracking System

Arduino Code

Arduino Code Car Parking System