Short/summary
I expected that the round trip delay of a nRF24L01+ would be less than 1 ms. In practice I see around 3 to 5 ms and other sources have similar values. Does anybody know why this round trip delay is so long? In short: nRF24L01+, adapters for them, tmrh20 RF24 library, 16 byte payload, highest speed.
Below is the full problem and related info.
Problem description
I received my nRF24L01+ radios and they work much better so far than my 433 MHz radios.
However, still with my nRF24L01 I get response times which are much lower than I expected.
I used the sketch of Example using dynamic payloads (which I increased to 16). The library I use is from tmrh20.github.
I also reduced the characters written to serial (and increased it to 250K) and some other small changes.
Transmitter code
Below is the code for the transmitter (manually removed receiver code and role switching code since that works/was not used except for first time)
/*
Dec 2014 - TMRh20 - Updated
Derived from examples by J. Coliz <maniacbug@ymail.com>
http://tmrh20.github.io/RF24/pingpair_ack_8ino-example.html
Results:
PA MAX
Retries 2,8 2,4 2,8
AutoAck True False True
EnableAckPayLoad True
PayLoadSize 16 16 8 32
DynamicPayLoad True False True
CRC Length 16 8
Failures / 25 iterations 0-1 3-4 ??? 24??
write min [us] 812 (792) 625 652 644
write avg [us] 2618 2106 2251
write max [us] 6224 (804) 5756 5756 7724
logdata[m][n] = millis() 4 us
write: 812..6224 (avg 2617) us
available: 28.. 32 us
read 100 us
*/
#include <SPI.h>
#include "RF24.h"
RF24 radio(7, 8);
uint32_t counter = 0;
static const int NR_OF_LOG_ITEMS = 10;
static const int LOG_LENGTH = 25;
uint32_t logData[LOG_LENGTH][NR_OF_LOG_ITEMS];
uint8_t currentLogLine = 0;
void setup()
{
for (int m = 0; m < LOG_LENGTH; m++)
{
for (int n = 0; n < NR_OF_LOG_ITEMS; n++)
{
logData[m][n] = -1;
}
}
Serial.begin(250000);
Serial.println(F("TRANSMITTER SPEED TEST"));
radio.begin();
radio.setPALevel(RF24_PA_MAX);
radio.setRetries(2, 8);
radio.setAutoAck(true);
radio.enableAckPayload();
radio.setPayloadSize(16);
radio.enableDynamicPayloads();
radio.setCRCLength(RF24_CRC_16);
byte addresses[][6] = {"1Node", "2Node"};
radio.openWritingPipe(addresses[0]);
radio.openReadingPipe(1, addresses[1]);
radio.startListening();
radio.writeAckPayload(1, &counter, 16);
//radio.printDetails();
radio.stopListening(); // Takes 336..348 us
}
uint32_t response = 0;
void loop(void)
{
uint32_t response;
uint32_t time = micros();
logData[currentLogLine][1] = micros();
if (radio.write(&counter, 16))
{
logData[currentLogLine][2] = micros();
if (!radio.available())
{
logData[currentLogLine][3] = micros();
Serial.print(F("Got blank response. round-trip delay: "));
Serial.print(micros() - time);
Serial.println(F(" microseconds"));
}
else
{
logData[currentLogLine][4] = micros();
while (radio.available())
{
logData[currentLogLine][5] = micros();
unsigned long timer = micros();
radio.read(&response, 16);
logData[currentLogLine][6] = micros();
logData[currentLogLine][7] = response;
logData[currentLogLine][8]++;
Serial.print(F("Got response "));
Serial.print(response);
Serial.print(F(" round-trip delay: "));
Serial.print(timer - time);
Serial.println(F(" microseconds"));
counter++;
}
}
}
else
{
logData[currentLogLine][9] = micros();
Serial.println(F("Sending failed.")); // If no ack response, sending failed
}
if (counter == LOG_LENGTH)
{
for (int m = 0; m < LOG_LENGTH; m++)
{
Serial.print("Location ");
Serial.print(m);
Serial.print(": ");
for (int n = 0; n < NR_OF_LOG_ITEMS; n++)
{
Serial.print(logData[m][n]);
Serial.print(" ");
}
Serial.println("");
}
}
currentLogLine++;
delay(100); // Try again later
}
Receiver code
Below is the code for the receiver (manually removed transmitter code and role switching code since that was not used)
/*
Dec 2014 - TMRh20 - Updated
Derived from examples by J. Coliz <maniacbug@ymail.com>
*/
#include <SPI.h>
#include "RF24.h"
RF24 radio(7, 8);
uint32_t counter = 0;
void setup() {
Serial.begin(250000);
Serial.println(F("RECEIVER SPEED TEST"));
radio.begin();
radio.setPALevel(RF24_PA_MAX);
radio.setRetries(2, 8);
radio.setAutoAck(true);
radio.enableAckPayload();
radio.setPayloadSize(16);
radio.enableDynamicPayloads();
radio.setCRCLength(RF24_CRC_16);
byte addresses[][6] = {"1Node", "2Node"};
radio.openWritingPipe(addresses[1]);
radio.openReadingPipe(1, addresses[0]);
radio.startListening();
radio.writeAckPayload(1, &counter, 16);
//radio.printDetails();
}
void loop(void)
{
byte pipeNo = 0;
uint32_t response = 0;
while ( radio.available(&pipeNo))
{
radio.read( &response, 16);
// Since this is a call-response. Respond directly with an ack payload.
response++;
radio.writeAckPayload(pipeNo, &response, 16); // This can be commented out to send empty payloads.
Serial.print(F("Loaded next response "));
Serial.println(response);
}
}
Resulting log fragment
Below are the results:
TRANSMITTER SPEED TEST
Got response 26 round-trip delay: 1960 microseconds
Got response 1 round-trip delay: 1964 microseconds
Got response 2 round-trip delay: 876 microseconds
Got response 3 round-trip delay: 880 microseconds
Got response 4 round-trip delay: 1960 microseconds
Got response 5 round-trip delay: 4128 microseconds
Got response 6 round-trip delay: 6296 microseconds
Got response 7 round-trip delay: 880 microseconds
Got response 8 round-trip delay: 884 microseconds
Got response 9 round-trip delay: 3044 microseconds
Got response 10 round-trip delay: 880 microseconds
Got response 11 round-trip delay: 880 microseconds
Got response 12 round-trip delay: 884 microseconds
Got response 13 round-trip delay: 876 microseconds
Got response 14 round-trip delay: 1964 microseconds
Got response 15 round-trip delay: 1956 microseconds
Got response 16 round-trip delay: 4128 microseconds
Got response 17 round-trip delay: 3044 microseconds
Got response 18 round-trip delay: 4128 microseconds
Got response 19 round-trip delay: 1964 microseconds
Got response 20 round-trip delay: 4124 microseconds
Got response 21 round-trip delay: 8452 microseconds
Got response 22 round-trip delay: 880 microseconds
Got response 23 round-trip delay: 1960 microseconds
Got response 24 round-trip delay: 884 microseconds
Location 0: 4294967295 12328 14216 4294967295 14248 14280 14384 26 0 4294967295
Location 1: 4294967295 115260 117152 4294967295 117184 117216 117316 1 0 4294967295
Location 2: 4294967295 218152 218956 4294967295 218988 219020 219120 2 0 4294967295
Location 3: 4294967295 319916 320728 4294967295 320760 320788 320888 3 0 4294967295
Location 4: 4294967295 421684 423576 4294967295 423608 423636 423736 4 0 4294967295
Location 5: 4294967295 524572 528628 4294967295 528660 528692 528792 5 0 4294967295
Location 6: 4294967295 629624 635844 4294967295 635876 635912 636012 6 0 4294967295
Location 7: 4294967295 736844 737660 4294967295 737688 737720 737820 7 0 4294967295
Location 8: 4294967295 838616 839428 4294967295 839460 839492 839592 8 0 4294967295
Location 9: 4294967295 940388 943364 4294967295 943396 943424 943524 9 0 4294967295
Location 10: 4294967295 1044356 1045168 4294967295 1045200 1045232 1045332 10 0 4294967295
Location 11: 4294967295 1146168 1146980 4294967295 1147012 1147040 1147140 11 0 4294967295
Location 12: 4294967295 1247972 1248784 4294967295 1248816 1248848 1248948 12 0 4294967295
Location 13: 4294967295 1349784 1350588 4294967295 1350620 1350652 1350756 13 0 4294967295
Location 14: 4294967295 1451592 1453484 4294967295 1453516 1453548 1453648 14 0 4294967295
Location 15: 4294967295 1554524 1556412 4294967295 1556440 1556472 1556580 15 0 4294967295
Location 16: 4294967295 1657448 1661508 4294967295 1661536 1661568 1661668 16 0 4294967295
Location 17: 4294967295 1762544 1765516 4294967295 1765548 1765580 1765680 17 0 4294967295
Location 18: 4294967295 1866552 1870612 4294967295 1870640 1870672 1870772 18 0 4294967295
Location 19: 4294967295 1971648 1973540 4294967295 1973572 1973604 1973704 19 0 4294967295
Location 20: 4294967295 2074580 2078636 4294967295 2078668 2078696 2078804 20 0 4294967295
Location 21: 4294967295 2179680 2188060 4294967295 2188092 2188124 2188224 21 0 4294967295
Location 22: 4294967295 2289096 2289912 4294967295 2289940 2289972 2290072 22 0 4294967295
Location 23: 4294967295 2390904 2392800 4294967295 2392832 2392860 2392960 23 0 4294967295
Location 24: 4294967295 2493832 2494644 4294967295 2494676 2494708 2494808 24 0 4294967295
And here some processed (with Excel) results:
write available available read
us us us us
1..2 2..4 4..5 5..6
4060 32 28 100
816 32 28 100
6224 32 32 100
2980 32 28 100
1896 32 28 100
816 32 32 100
1896 32 32 100
812 32 32 100
2976 32 32 100
2976 32 32 100
816 28 32 100
4060 32 32 100
816 32 28 100
4060 28 32 100
816 32 32 100
816 28 32 100
1896 32 32 100
5140 32 28 100
6220 32 36 100
816 32 32 100
4060 32 32 100
2980 28 32 100
4060 32 28 100
816 32 28 100
min 812 28 28 100
avg 2617.666667 31.33333333 30.83333333 100
max 6224 32 36 100
The main chunk is the write which takes 812..6224 us (with an average of 2617 us). Probably this big variance is due to retries, but still the lowest 812 us is more than expected:
Summary of results
I did some more tests, I will not put all log/intermediate results here, but the following table shows some tests:
PA MAX
Restries 2,8 2,4 2,8
AutoAck True False True
EnableAckPayLoad True
PayLoadSize 16 16 8 32
DynamicPayLoad True False True
CRC Length 16 8
Failures / 25 iterations 0-1 3-4 ??? 24??
write min [us] 812 (792) 625 652 644
write avg [us] 2618 2106 2251
write max [us] 6224 (804) 5756 5756 7724
millis() [us] 4
available [us] 24/32
read [us] 100
Conclusion of results
- Changing the number of retries lower than 2 (= 500 us), 8 (retries) results in too many errors, and does not lower the minimum, just the maximum, but with errors so useless
- AutoAck always returns in 7u92-804 us, but I need AutoAck in my project
- Changing payload to 8 results in a minimum of 625 us
- Changing payload to 32 resulted in too many errors
- Disabling dynamic payload resulted in a minimum of 652 us
- Changing CRC length to 8 results in 644 us
Expectations
When I use the calculation in the datasheet, I come to the following results:
Datasheet https://www.nordicsemi.com/eng/nordic/download_resource/8765/2/84238108/2726
Table 15
Item Value Unit Item Value Unit
T_OA Time on-air 0.0001005 s Preamble 1 byte
T_ACK Time on-air Ack 0.0001005 s Address 5 bytes
T_UL Time upload 0.000512 s Payload 16 bytes
T_ESB Time Enh Shock-Burst Cycle 0.0008785 s CRC 2 bytes
Back and forth? 0.001757 s packet Control field 9 bits
Air data rate 2000000 bits/s
SPI data rate 250000 bps (guess)
T_stby2a 0.00013 s
T_IRQ 0.000006 s
According to the formulas, I would get around 0,875 ms (probably one way) and about double back-and-forth. Btw, I don't understand why T_OA is not in the formula.
However, I get as minimum 0,812 which may be some rounding/timing inaccuracy.
Conclusion
The retries are causing the longer writes. The minimum is similar to the calculated value from the datasheet.
Question
- Home come there are so many retries?
- Should I check for noise on channels?
Background
What I hoped for was to send a 32 (but 16 is ok too) byte message, than after 5 ms another message, and have the ack of the second message back within like 6 ms or so (including the 5 ms delay between the first and second message to be sent). This means the sending and receiving of an ack should take about 1 ms (which is well within the 0,256 ms calculated above).
Setup
Note my Uno's with nRF24L01 are about 1 meter apart, settings: 2 mbps, set retries(2, 4), enable ackpayload/dynamic payloads.
RF Radio hardware
The RF radios I'm using are nRF24L01+ like this:
Datasheet: PDF Download
Update: I see people use various capacitors and/or a voltage regulator. I did not do that, but I have a an adapter from 5V->3.3V specifically for the nRF24L01 (like this). I think there is already a voltage regulator and needed capacitors on this board. As far as I know the IC on the adapter is the voltage regulator and the capacitor (one) is probably just above the GND/VCC connection in the picture above. Or a second capacitor left of the MI/IRQ
Results
radio.enableAckPayload();
Disabling ack (or well, not enabling it) will improve the speed of transmission. – Avamander Jul 31 '17 at 21:17