1

I'm going to explain better: I'm new to Arduino and was experimenting with nRF24L01 in order to make an RC controller.

I have 2 circuits, one is the sender and the other one is the receiver (obviously).

The sender has just 7 pushbuttons and an nRF24L01:

The receiver has a PCA9685 with servos and an nRF24L01:

On each button I push on the sender, the relative servo (from 0 to 6) on the receiver must run from 0 to 180 degrees, wait 500 ms and return to 0 degrees... And yes, this works, until something happens and the receiver seems to receive the last part of the previously sent message and acts like I pushed another button.

e.g: I push btn1, wait for servo1, then btn2, wait for servo2, then btn3, wait for servo3... But if I push btn1 then servo3 makes its run instead of servo1. If I push again btn1, then servo1 does its run. This issue happens randomly so I suppose my approach on receiving messages from NRF24L01 is wrong.

This is confusing, I know, so I think a video can explain it better:

  1. This video (slow OneDrive) shows how the receiver should act when I press btn1,btn2,btn3
  2. While this other one (slower OneDrive) shows the issue when I press buttons in this order:
    1 - 2 - 3 - 3 - 1 - 1 (after that the video seems to be broken)

And finally the code, the below one is for sender:

#include <SPI.h>

#include <RH_NRF24.h>

// Singleton instance of the radio driver
RH_NRF24 nrf24;

int btn[] = {2, 3, 4, 5, 6, 7, 9};
String msg[] = {"1", "2", "3", "4", "5", "6", "7"};

int i = 0;

void setup() {
  Serial.begin(9600);
  pinMode(btn[0], INPUT);
  pinMode(btn[1], INPUT);
  pinMode(btn[2], INPUT);
  pinMode(btn[3], INPUT);
  pinMode(btn[4], INPUT);
  pinMode(btn[5], INPUT);
  pinMode(btn[6], INPUT);
  while (!Serial) 
    ; // wait for serial port to connect. Needed for Leonardo only
  if (!nrf24.init())
    Serial.println("init failed");
  // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm
  if (!nrf24.setChannel(1))
    Serial.println("setChannel failed");
  if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm))
    Serial.println("setRF failed");    
}

void loop() {
  if(digitalRead(btn[0])) { sendMessage(0);}
  else if(digitalRead(btn[1])) { sendMessage(1);}
  else if(digitalRead(btn[2])) { sendMessage(2);}
  else if(digitalRead(btn[3])) { sendMessage(3);}
  else if(digitalRead(btn[4])) { sendMessage(4);}
  else if(digitalRead(btn[5])) { sendMessage(5);}
  else if(digitalRead(btn[6])) { sendMessage(6);}
}

void sendMessage(int id) {
  //Send a message based on the id
  Serial.println(id);
  uint8_t data[] = "def";
  msg[id].toCharArray(data, sizeof(data));
  nrf24.send(data, sizeof(data));
  nrf24.waitPacketSent();
  }

And this one is for the receiver, where I think the issue happens (inside the while loop in the receiveMessage() function).

#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <SPI.h>
#include <RH_NRF24.h>

#define MIN_PULSE_WIDTH       650
#define MAX_PULSE_WIDTH       2350
#define DEFAULT_PULSE_WIDTH   1500
#define FREQUENCY             50

// Singleton instance of the radio driver
RH_NRF24 nrf24;
int servo[] = {0, 1, 2, 3, 4, 5, 6};
String msg[] = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "10"};
Adafruit_PWMServoDriver pwm = Adafruit_PWMServoDriver();

#define SERVOMIN  150 // this is the 'minimum' pulse length count (out of 4096)
#define SERVOMAX  600 // this is the 'maximum' pulse length count (out of 4096)
// our servo # counter
uint8_t servonum = 0;

void setup() {
  Serial.begin(9600);
  while (!Serial) 
    ; // wait for serial port to connect. Needed for Leonardo only
  if (!nrf24.init())
    Serial.println("init failed");
  // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm
  if (!nrf24.setChannel(1))
    Serial.println("setChannel failed");
  if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm))
    Serial.println("setRF failed");
  pwm.begin();
  pwm.setPWMFreq(60);  // Analog servos run at ~60 Hz updates
}

void loop() {
  receiveMessage();
}

void receiveMessage() {
  // Receive a message and rotate the given motor
  uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
  uint8_t len = sizeof(buf);
  while (nrf24.waitAvailableTimeout(200) && nrf24.recv(buf, &len)) {
    Serial.println((char*)buf);
    pwm.setPWM(determineMotor((char*)buf), 0, pulseWidth(180));
    delay(500);
    pwm.setPWM(determineMotor((char*)buf), 0, pulseWidth(0));
  }
}

int determineMotor(String ms) {
  int ret = -1;
  if(ms == msg[0]) ret = servo[0];
  else if(ms == msg[1]) ret = servo[1];
  else if(ms == msg[2]) ret = servo[2];
  else if(ms == msg[3]) ret = servo[3];
  else if(ms == msg[4]) ret = servo[4];
  else if(ms == msg[5]) ret = servo[5];
  else if(ms == msg[6]) ret = servo[6];
  else if(ms == msg[7]) ret = servo[7];
  else if(ms == msg[8]) ret = servo[8];
  else if(ms == msg[9]) ret = servo[9];
  return ret;
  }

int pulseWidth(int angle) {
  int pulse_wide, analog_value;
  pulse_wide   = map(angle, 0, 180, MIN_PULSE_WIDTH, MAX_PULSE_WIDTH);
  analog_value = int(float(pulse_wide) / 1000000 * FREQUENCY * 4096);
  Serial.println(analog_value);
  return analog_value;
}

So the question is: is there a better approach in receiving messages from an nRF24L01 in order to avoid this issue?

  • Using blocking delays in your receiver is potentially rather problematic, you might want to think about managing the radio more consistently and scheduling events to happen when enough time has elapsed, rather than doing blocking waits on them. – Chris Stratton Mar 27 '17 at 16:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.