Two-way transmission
If the slave is required to send data back to the master it would seem necessary for the two devices to swap roles. Among other things that will mean that the "master" will need an address as well as the slave. The question of timing also needs to be considered - for example, how long should the master continue listening for the reply from the slave? And, if the slave is trying to write rather than listen it may miss a transmission from the master.
Two-way transmission using the ackPayload concept
The complications of swapping roles can be completely avoided by using the ackPayload concept. The idea is that the slave puts data in the ackPayload buffer BEFORE it receives a message from the master and then the data in the ackPayload buffer is sent automatically as part of the normal acknowledgment process without your code needing to make the devices swap roles.
The ackPayload concept can only be used when the amount of data transferring from slave to master is 32 bytes or less.
It also means that the data sent from the slave is always a step behind the data received by the slave. For example the ackPayoad cannot take account of the data received in the message for which it is the acknowledgment.
ackPayload example
In this example the slave will send a pair of numbers back to the master.
SimpleTxAckPayload.ino
// SimpleTxAckPayload - the master or the transmitter
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define CE_PIN 9
#define CSN_PIN 10
const byte slaveAddress[5] = {'R','x','A','A','A'};
RF24 radio(CE_PIN, CSN_PIN); // Create a Radio
char dataToSend[10] = "Message 0";
char txNum = '0';
int ackData[2] = {-1, -1}; // to hold the two values coming from the slave
bool newData = false;
unsigned long currentMillis;
unsigned long prevMillis;
unsigned long txIntervalMillis = 1000; // send once per second
//===============
void setup() {
Serial.begin(9600);
Serial.println(F("Source File /mnt/sdb1/SGT-Prog/Arduino/ForumDemos/nRF24Tutorial/SimpleTxAckPayload.ino"));
Serial.println("SimpleTxAckPayload Starting");
radio.begin();
radio.setDataRate( RF24_250KBPS );
radio.enableAckPayload();
radio.setRetries(5,5); // delay, count
// 5 gives a 1500 µsec delay which is needed for a 32 byte ackPayload
radio.openWritingPipe(slaveAddress);
}
//=============
void loop() {
currentMillis = millis();
if (currentMillis - prevMillis >= txIntervalMillis) {
send();
}
showData();
}
//================
void send() {
bool rslt;
rslt = radio.write( &dataToSend, sizeof(dataToSend) );
// Always use sizeof() as it gives the size as the number of bytes.
// For example if dataToSend was an int sizeof() would correctly return 2
Serial.print("Data Sent ");
Serial.print(dataToSend);
if (rslt) {
if ( radio.isAckPayloadAvailable() ) {
radio.read(&ackData, sizeof(ackData));
newData = true;
}
else {
Serial.println(" Acknowledge but no data ");
}
updateMessage();
}
else {
Serial.println(" Tx failed");
}
prevMillis = millis();
}
//=================
void showData() {
if (newData == true) {
Serial.print(" Acknowledge data ");
Serial.print(ackData[0]);
Serial.print(", ");
Serial.println(ackData[1]);
Serial.println();
newData = false;
}
}
//================
void updateMessage() {
// so you can see that new data is being sent
txNum += 1;
if (txNum > '9') {
txNum = '0';
}
dataToSend[8] = txNum;
}
SimpleRxAckPayload.ino
// SimpleRxAckPayload- the slave or the receiver
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>
#define CE_PIN 9
#define CSN_PIN 10
const byte thisSlaveAddress[5] = {'R','x','A','A','A'};
RF24 radio(CE_PIN, CSN_PIN);
char dataReceived[10]; // this must match dataToSend in the TX
int ackData[2] = {109, -4000}; // the two values to be sent to the master
bool newData = false;
//==============
void setup() {
Serial.begin(9600);
Serial.println("SimpleRxAckPayload Starting");
radio.begin();
radio.setDataRate( RF24_250KBPS );
radio.openReadingPipe(1, thisSlaveAddress);
radio.enableAckPayload();
radio.startListening();
radio.writeAckPayload(1, &ackData, sizeof(ackData)); // pre-load data
}
//==========
void loop() {
getData();
showData();
}
//============
void getData() {
if ( radio.available() ) {
radio.read( &dataReceived, sizeof(dataReceived) );
updateReplyData();
newData = true;
}
}
//================
void showData() {
if (newData == true) {
Serial.print("Data received ");
Serial.println(dataReceived);
Serial.print(" ackPayload sent ");
Serial.print(ackData[0]);
Serial.print(", ");
Serial.println(ackData[1]);
newData = false;
}
}
//================
void updateReplyData() {
ackData[0] -= 1;
ackData[1] -= 1;
if (ackData[0] < 100) {
ackData[0] = 109;
}
if (ackData[1] < -4009) {
ackData[1] = -4000;
}
radio.writeAckPayload(1, &ackData, sizeof(ackData)); // load the payload for the next time
}
Edit 05 Dec 2016 to correct the wrong pipe reference in the function updateReplyData(). Apologies for any confusion.
Edit 18 Mar 2017 - see Reply #17 for a version that works with multiple slaves
Edit 21 Feb 2019 to change setup() in RxAckPayload so the payload is uploaded after startListening()
Edit 20 Jan 2020 to increase delay in setRetries() to allow for longer ackPayloads. Details in section 7.4.2 of the Nordic nRF24L01+ datasheet
Continues in next Post