Two way radio communication

:o I’ve spent the last month trying to figure this out, with limited success. I’m using example code that seems pretty clear: Tx transmits the position of a thumbstick and then transitions into an empty while loop to wait for a response from Rx. Rx receives the message and adjusts a servo motor accordingly, then reads the state of a switch and sends that back to Tx. Tx should then turn on/off an LED according to the switch position received.

The problem I am seeing is that the transmitting Arduino never seems to leave the empty while loop. If I comment out the while loop in question, everything works as expected save that the led will blink on/off randomly when it should be on. This makes sense since Tx is not waiting for a response and so the LED is controlled sometimes before the response is received and sometimes after. I know there are other ways to make this work, but I would like to know where my issue is stemming from as everything I’ve read so far says that this should work. Is it possible that the while loop isn’t the problem?

Hardware: 2 Arduino Nano clones that I’m calling Tx and Rx, each attached to a NRF24L01 radio module.

Receiver Code:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#include <Servo.h>
#define button 19

RF24 radio(7, 8); // CE, CSN
const byte addresses[][6] = {"00001", "00002"};
Servo myServo;
boolean buttonState = 0;


void setup() {
  pinMode(button, INPUT);
  myServo.attach(3);
  radio.begin();
  radio.openWritingPipe(addresses[0]); // 00002
  radio.openReadingPipe(1, addresses[1]); // 00001
  radio.setPALevel(RF24_PA_MIN);
}


void loop() {
  delay(5);
  radio.startListening();
  if ( radio.available()) {
    while (radio.available()) {
      int angleV = 0;
      radio.read(&angleV, sizeof(angleV));
      myServo.write(angleV);
    }
    delay(5);
    radio.stopListening();
    buttonState = digitalRead(button);
    radio.write(&buttonState, sizeof(buttonState));
  }
}

Transmitter Code:

#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define led 3

RF24 radio(7, 8); // CE, CSN
const byte addresses[][6] = {"00001", "00002"};
boolean buttonState = 0;


void setup() {
  pinMode(12, OUTPUT);
  radio.begin();
  radio.openWritingPipe(addresses[1]); // 00001
  radio.openReadingPipe(1, addresses[0]); // 00002
  radio.setPALevel(RF24_PA_MIN);
}


void loop() {
  delay(5);
  radio.stopListening();
  int potValue = analogRead(A5);
  int angleValue = map(potValue, 0, 1023, 0, 180);
  radio.write(&angleValue, sizeof(angleValue));
  delay(5);
  radio.startListening();
  while (!radio.available());                                      //While loop in question
  radio.read(&buttonState, sizeof(buttonState));
  if (buttonState == HIGH) {
    digitalWrite(led, HIGH);
  }
  else {
    digitalWrite(led, LOW);
  }
}

For this type of communication you should use the AckPayload feature of the NRF.

In the process of acknowledgement the roles are switched anyway and the chip is able to append a full sized
packet to the (normally very small) ack-packet.

The data to be sent automatically on reception has to be loaded to the chip before the reception obviously.

If you configure your setup like that you would only need to call startListening in the receiver once in setup,
and never in the transmitter, it will receive packets nevertheless.

You can have up to three pipes configured this way due to the number of transmit buffers.

Robin2 has an example for that type of communication in his famous NRF introduction.

You should study the datatsheet and the documentation of the library for this slightly advanced stuff.

Thanks for the quick reply. I've been reading up on ack payloads and agree that this may be a better solution. I'm still wondering about the empty while loop, however. The website that I got the example from included a Youtube video showing the circuit working flawlessly. As I am new to programming with C/C++ I'd like to know what I'm doing wrong, as empty while loops may be useful to me later.

The website with the tutorial is:

Whatever you do, don't wait for something.

No delays, no waits. Construct you program to be event driven.
Act on a reception, if it occurs, not while.

If there are button events, process them.

That way your other code gets a chance to run and back to back packets are an exeption anyway.

Your application would benefit from using the features of the chip, it would simplify your code a lot.

Have a look at this Simple nRF24L01+ Tutorial. The second example uses the ackPayload feature.

Wireless problems can be very difficult to debug so get the wireless part working on its own before you start adding any other features.

...R

Note that I had to change the NRF hardware to my setup.

There is a lot of debug output in the sketches and I restricted the speed for testing to 2 packets a second.

TX

#include <RF24.h>

#define led 3

RF24 radio(9, 10); // CE, CSN
const byte address[] = "20000";
byte buttonState = LOW;

void setup() {
  Serial.begin(250000);
  pinMode(12, OUTPUT);
  Serial.println(F("AckPayload TX"));
  radio.begin();
  radio.setPALevel(RF24_PA_MIN);
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  radio.openWritingPipe(address);
}

void loop() {
  static uint32_t lastSend;
  if (millis() - lastSend >= 500) {
    lastSend = millis();
    int potValue = analogRead(A5);
    int angleValue = map(potValue, 0, 1023, 0, 180);
    if (radio.write(&angleValue, sizeof(angleValue))) {
      if (radio.isAckPayloadAvailable()) {
        radio.read(&buttonState, sizeof(buttonState));
        if (buttonState == HIGH) {
          digitalWrite(led, HIGH);
        } else {
          digitalWrite(led, LOW);
        }
        Serial.write('A');
      } else {
        Serial.write('W');
      }
    } else {
      Serial.write('-');
    }
  }
}

RX

#include <RF24.h>
#include <Servo.h>

#define button 19

RF24 radio(9, 10);
const byte address[] = "20000";
Servo myServo;
byte buttonState = LOW;

void setup() {
  Serial.begin(250000);
  Serial.println(F("AckPayload RX"));
  pinMode(button, INPUT);
  myServo.attach(3);
  radio.begin();
  radio.enableDynamicPayloads();
  radio.enableAckPayload();
  radio.setPALevel(RF24_PA_MIN);
  radio.openReadingPipe(1, address);
  radio.startListening();
  buttonState = digitalRead(button);
  radio.writeAckPayload(1, &buttonState, sizeof(buttonState));
}

void loop() {
  static uint32_t packs;
  if (radio.available()) {
    int angleV = 0;
    radio.read(&angleV, sizeof(angleV));
    myServo.write(angleV);
    buttonState = digitalRead(button);
    radio.writeAckPayload(1, &buttonState, sizeof(buttonState));
    Serial.print(++packs);
    Serial.println(F(" RXed"));
  }
}