Inconsistent Return Values For RF24 Library's RF24::write() Function

Hi all.

I have unsuccessfully searched the forums for a related topic. My apologies if this post is redundant.

I have two NRF24L01+ transceivers that talk to each other. The first NRF24L01+ (transmitter) is connected to a stand-alone Atmel ATTiny84, and the second NRF24L01+ (receiver) is connected to a Raspberry Pi B+. An infrared L.E.D. and infrared detector are also connected to the Atmel so that it reflects the beam status on PA0. An unbroken beam produces a logic 0 on PA0, and a broken beam produces a logic 1 on PA0.

The transceivers communicate with each other successfully, but I have noticed an anomaly that I greatly need to understand before I continue with my security project.

At issue is the fact that the RF24::write() function is supposed to return a logic 1 (true) when it succesfully sends a packet. However, there are instances when a packet is successfully transmitted but the RF24::write() function returns a logic 0 (false). After much debugging, I have narrowed down the code that causes the discrepancy. I need help understanding how the code affects the return value.

In this first code excerpt, the RF24::write() function returns 1 for the first four packet transmissions when the beam is broken. Then, it will always return 0 for future packets when the beam is broken, even though those packets are successfully transmitted from the ATTiny84-connected NRF24L01+ to the Raspberry Pi-connected NRF24L01+:

void loop()
{
if(!(IRpin_PIN & (1 << IRpin)))
{
digitalWrite(redLed, LOW);
}
else
{
digitalWrite(redLed, HIGH);
if(value=radio.write((void *) &value,8) == 1)
{
}
}
}

However, in this second code excerpt, the RF24::write() function will always return 1 upon a successful packet transmission when the beam is broken (which seems normal):

void loop(void)
{
if (!(IRpin_PIN & (1 << IRpin)))
{
digitalWrite(redLed, LOW);
}
else
{
digitalWrite(redLed, HIGH);

uint64_t gotByte;
unsigned long time = micros();

if(value=radio.write((void *) &value,8))
{

if(!radio.available())
{
printf("Got blank response. round-trip delay: %lu microseconds\n\r",micros()-time);
}
else
{
while(radio.available())
{
radio.read( &gotByte, 8);
printf("Got response %d, round-trip delay: %lu microseconds\n\r",gotByte,micros()-time);
counter1++;
if(counter1 < 0)
counter1 = 0;
}
}
}
else
{
++failed1;
if(failed1 < 0)
failed1 = 0;
printf("Sending failed for %d. Failure count = %d.\n\r", counter1, failed1);
}
}
}

I'm not sure if the lack of functionality in the first code example is influencing a 0 response from the RF24::write() function. I notice that, if I comment out the extra lines in the second code example so that it looks exactly like the first code example, the RF24::write() function in the second code example also returns 0 despite a successful packet transmission.

From researching this phenomenon on the Internet, the RF24::write() function returns 1 only if it receives an acknowledgement packet after transmitting. However, my coding and testing shows that packets are successfully being received despite the RF24::write() function's returning 0.

If anyone can assist me in figuring out this inconsistency, I would greatly appreciate it. My project depends highly on accurate packet transmission acknowledgement. Therefore, a successful transmission must be accurately reflected by a 1, and an unsuccessful transmission must accurately be reflected by a 0.

Thanks in advance.

-Z

Which library are you using (link)? And use code tags! As you can see your code is interpreted by the forum system and isn't correct anymore.

maniacbug's RF24 library doesn't return an integer but a boolean value. Boolean "True" doesn't have to be equal integer 1 but boolean "False" is always equal 0.

What do you expect value to be in that expression?

if(value=radio.write((void *) &value,8) == 1)

value is set to "True" if the return value of radio.write() was equal to 1 and is false otherwise. It is not equal to the return value of radio.write() (although in many cases it will be the same)!

Hi all.

pylon, thanks for replying. I will use code tags from now on. This is my first post on an Arduino forum, and I did look for a way to quote my code, but it was not obvious at the time. Apologies.

Originally, the code that you quoted in your reply was the following while statement:

while(value=radio.write((void *) &value,8) == 0)

The thinking was that the while loop would repeatedly cause radio.write() to send packets until it received a true. However, I found that the program would get stuck in the while loop. That is when I switched the statement to the following if statement:

if(value=radio.write((void *) &value,8) == 1)

This is how I discovered that radio.write() is not returning true even though its packets are successfully being received by the Raspberry Pi-connected NRF24L01+.

Having read your suggestion about using true/false instead of 1/0, I have switched to the following statement:

while(value=radio.write((void *) &value,8) == false)

Still, radio.write() still returns "false" although it successfully sends packets to the Pi-connected NRF24L01+. The proof of this is that the program gets stuck in the while loop. If radio.write() were working properly, it would generate a "true" for a successfully sent packet and break out of the while loop. In my case, it is successfully sending packets but generating "false," thus staying in the while loop.

From other research I have read, the RF24::write() function is a blocking function and not asynchronous. In other words, when executing, it will hold up a program until it receives an acknowledgement or a timeout. Also, both NRF24L01+ devices have auto acknowledgement and acknowledgement payload enabled.

I wil try to do more debugging on my end. However, any assistance anyone can provide in helping me to resolve this riddle would be greatly appreciated.

Thanks in advance.

-Z

Hi Z

if(value=radio.write((void *) &value,8) == 1)

If value is the data you are sending, why are you putting the return code from the write() method into it?

What happens if you change the statement to this?

if (radio.write((void *) &value,8))

And could you post your complete program, so that we can see how the variables have been declared.

Regards

Ray

Hi all.

Ray, thanks for replying.

Because the sending NRF24L01+ is connected to a stand-alone Atmel ATTiny84, I have no way to wire the ATTiny84 so that the program output can be viewed via the Arduino IDE Monitor. Therefore, I was using the "value" variable to capture the result of radio.write() so that I can see the result on the Raspberry Pi's output screen.

I have changed the line of code to what you suggested.

Here is my test sketch:

//#include <SPI.h>
#include "nRF24L01.h"
#include "RF24.h"
#include "printf.h"



#define IRpin_PIN PINA
#define IRpin 0
int redLed = 0;
uint64_t addresses[4] = {0x123456789ALL, 0x123456789BLL, 0x223456789ALL, 0x223456789BLL};
volatile uint64_t counter = 0;
uint64_t heartbeat = 0x07LL;
volatile uint64_t value=0;
RF24 radio(9,3);



/*ISR(PCINT0_vect)
{
  if(!(IRpin_PIN & (1 << IRpin)))
  //if(digitalRead(A0) == LOW)
  {
    digitalWrite(redLed, LOW);
  }
  else
  {
    digitalWrite(redLed, HIGH);
    while(value=radio.write((void *) &value,8) == 1)
  }
}*/



/*ISR(TIM1_COMPA_vect)
{
  radio.write((void *) &heartbeat,8);
}*/



void setup()
{
  DDRA = 0x00;
  DDRB = 0x05;
  digitalWrite(A0, HIGH);
  
  TCCR0A = (0 << COM0A1) | (1 << COM0A0) | (0 << COM0B1) | (0 << COM0B0) | (1 << WGM01) | (1 << WGM00);
  TCCR0B = (0 << FOC0A) | (0 << FOC0B) | (1 << WGM02) | (0 << CS02) | (1 << CS01) | (0 << CS00);
  OCR0A = 0x0C;
  /*
  TCCR1A = 0x03;
  TCCR1B = 0x1D;
  TCCR1C = 0x00;
  OCR1A = 0x1E83;
  TIMSK1 = 0x02;
  */
  
  
  
/*
  MCUCR &= (1<<ISC00);
  GIMSK &= (1<<PCIE0);
  PCMSK1 &= 0x00;
  PCMSK0 &= (1<<PCINT0);
  //SREG |= 0x80;
  GIFR = 0x70;
  sei();
  */
 /*
  MCUCR &= 0xFC; 
  MCUCR |= 0x01;
  GIMSK = 0x00;
  GIMSK |= 0x10;
  PCMSK1 = 0x00;
  PCMSK0 = 0x00;
  PCMSK0 |= 0x01;
  SREG |= 0x80;
  */
  radio.begin();
  radio.setDataRate(RF24_2MBPS);
  radio.setChannel(80);
  radio.setAutoAck(1);                    // Ensure autoACK is enabled
  radio.enableAckPayload();               // Allow optional ack payloads
  radio.setRetries(0,15);                 // Smallest time between retries, max no. of retries
  radio.setPayloadSize(8);                // Here we are sending 1-byte payloads to test the call-response speed
  radio.openWritingPipe(addresses[0]);        // Both radios listen on the same pipes by default, and switch when writing
  radio.openReadingPipe(1, addresses[1]);      // Open a reading pipe on address 0, pipe 1
  radio.stopListening();
  radio.powerUp();
  radio.printDetails();                   // Dump the configuration of the rf unit for debugging
}



void loop()
{
if(!(IRpin_PIN & (1 << IRpin)))
  {
    digitalWrite(redLed, LOW);
  }
  else
  {
    digitalWrite(redLed, HIGH);
    if(radio.write((void *)&value,8))
    {
      ++value;
    }
  }
}

Here is the output screen from the Raspberry Pi's output for each quick break of the beam:

PI Configuration ================
CSN Pin  	 = CE1 (PI Hardware Driven)
CE Pin  	 = Custom GPIO24
Clock Speed	 = 8 Mhz
================ NRF Configuration ================
STATUS		 = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
RX_ADDR_P0-1	 = 0x223456789b 0x223456789a
RX_ADDR_P2-5	 = 0xc3 0xc4 0xc5 0xc6
TX_ADDR		 = 0x223456789b
RX_PW_P0-6	 = 0x08 0x08 0x00 0x00 0x00 0x00
EN_AA		 = 0x3f
EN_RXADDR	 = 0x03
RF_CH		 = 0x50
RF_SETUP	 = 0x0f
CONFIG		 = 0x0f
DYNPD/FEATURE	 = 0x03 0x06
Data Rate	 = 2MBPS
Model		 = nRF24L01+
CRC Length	 = 16 bits
PA Power	 = PA_MAX


Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 0
Sent response 0 to pipe 1

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 1
Sent response 1 to pipe 1

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 2
Sent response 2 to pipe 1

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 3
Sent response 3 to pipe 1

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 4
Sent response 4 to pipe 1

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 4
Sent response 4 to pipe 1

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 4
Sent response 4 to pipe 1

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 4
Sent response 4 to pipe 1

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 4
Sent response 4 to pipe 1

As you can see from above, I break the beam 9 times. However, after the fifth time, you notice that the "Got data 4" is repeated. This means that, after the fifth beam break, the following line keeps returning false:

 if(radio.write((void *)&value,8))

If the above line were not returning false, value would be continously incremented. Instead, the line continuously returns false after the fifth beam break. Hence, value is no longer incremented.

If I permanently break the beam, the Raspberry Pi will continue to repeat the "Got data 4" messaage, which means that radio.write() is continuously returning false.

Any ideas?

Thanks in advance.

-Z

Does it alter the results if you change loop() to this?

  static boolean messageSent = false;
  if(!(IRpin_PIN & (1 << IRpin)))
  {
    digitalWrite(redLed, LOW);
    messageSent = false;
  }
  else
  {
    digitalWrite(redLed, HIGH);
    if (!messageSent)
    {
      if(radio.write((void *)&value,8))
      {
        ++value;
      }
      messageSent = true;
    }
  }

This should send one message each time the beam becomes broken, rather than sending messages for as long as the beam is broken. Since your original program with the extra code worked, but the cutdown version did not, I'm wondering if there is an overflow / timing issue here.

Hackscribble:
I'm wondering if there is an overflow / timing issue here.

I'm willing to bet there is.
Judging by the output from the RPi:

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 1
Sent response 1 to pipe 1

If the RPi is sending back responses, and they are not being read, the RX FIFO will fill up. When this happens, autoACK will fail to work, and all writes will appear to fail, even if they are successful. This would also eplain why only the first few responses are ok.

Adding in the following or similar should clear up the issue:

while(radio.available()){
  uint64_t blank;
  radio.read(&blank,sizeof(blank));
}

The return value of RF24::write() is what the hardware tells in the status register's TX_DS bit. If the data was sent but no acknowledge was received the method returns false although the value may be transmitted.
If you cry to your neighbor and he doesn't react, would you expect he heard it? He might have heard it but if he doesn't tell you so, you better assume he didn't, don't you think so?

Hi all.

Thanks to all who have replied. Your thoughts have been quite helpful.

Hackscribble, I have modified your suggested code so that messageSent is set to true only if radio.write() returns true:

void loop(void)
{
  uint8_t gotByte;
  
  if (!(IRpin_PIN & (1 << IRpin)))
  {
    digitalWrite(redLed, LOW);
    messageSent = false;
  }
  else
  {
    digitalWrite(redLed, HIGH);  
    if(!messageSent)
    {
      if(radio.write((void *) &value,8))
      {
        ++value;
        messageSent = true;
      }
    }
  }
}

Actually, I like this better, as the message is sent only once if the beam is continuously broken. Unfortunately, the same behavior happens. After the fifth beam break, the output from Raspberry Pi is still stuck on the following:

Radio 1.
Pipe 1 has data.
Reading data on pipeNo 1
Got data 4
Sent response 4 to pipe 1

TMRh20, the following is the loop() code on the Raspberry Pi that generates the output:

void loop(void)
{
  while(1)
  {
    bcm2835_spi_chipSelect(0);
    while(radio1.available(&pipeNo))
    {              // Read all available payloads
      printf("Radio 1.\n\r");
      printf("Pipe %d has data.\n\r", pipeNo);
      printf("Reading data on pipeNo %d\n\r", pipeNo);
      radio1.read(&gotByte, 8);
      printf("Got data %llu\n\r", gotByte);
      radio1.writeAckPayload(pipeNo, &gotByte, 8);
      printf("Sent response %llu to pipe %d\n\r\n\r", gotByte, pipeNo);
    }

    bcm2835_spi_chipSelect(1);
    while(radio2.available(&pipeNo))
    {              // Read all available payloads
      printf("Radio 2.\n\r");
      printf("Pipe %d has data.\n\r", pipeNo);
      printf("Reading data on pipeNo %d\n\r", pipeNo);
      radio2.read(&gotByte, 8);
      printf("Got data %llu\n\r", gotByte);
      radio2.writeAckPayload(pipeNo, &gotByte, 8);
      printf("Sent response %llu to pipe %d\n\r\n\r", gotByte, pipeNo);
    }
  }
}

The Pi actually has two NRF24L01+ radios. It does use the radio.available() function to empty the RX buffer. Also, gotByte is of type uint64_t. Even with radio2's code commented out, I still experience repeated output after the fifth beam break. What is interesting is that, even after power reset of both the sender and receiver, the Pi output will always repeat after the fifth beam break. This is a clue that I must investigate deeply.

pylon, your argument is valid. Even with all of the suggested changes, the output of the Raspberry Pi will always start repeating after the fifth beam break.

I do not understand why I am having these issues. In aother testing scenario (Scenario 1), with the Raspberry Pi set up the same way, I have two sending NRF24L01+ devices. The first is directly connected to the Tiny84 stand-alone. The second is directly connected to an Arduino Uno. The Tiny84 stand-alone uses the beam break sketch to repeatedly send messages to radio 1 on the Pi (beam permanently broken). The Uno uses another sketch to transmit repeated messages in a loop to radio 2 on the Pi without interruption. In this scenario, the communications between both senders and the Pi are near flawless. I have let this scenario run for 24 hours, and with the Tiny84 beam break sketch, less than 100 messages fail in 10 million sent messages.

The only difference between Scenario 1 and this new scenario (Scenario 2) is that the code is much shorter. Therefore, as Hackscribble suggested, perhaps there is a timing issue that has been introduced with the shorter code.

I will continue to dig deeper until I hopefully find out why the shorter code fails but the longer code works.

Take care.

-Z

Actually, I like this better, as the message is sent only once if the beam is continuously broken.

Not sure it's relevant to the issue you are facing, but this is what my code did before you modified it :slight_smile:

With your change, if radio.write() returns false, you will get repeat transmissions until the beam reconnects or radio.write() returns true.

About the possible timing issue, have you tried using the cutdown code (that gives the problem) but with a short delay() after the radioWrite()? You could try varying the length of the delay to see if the problem goes away at some point.

zx11ninja:
In this first code excerpt, the RF24::write() function returns 1 for the first four packet transmissions when the beam is broken. Then, it will always return 0 for future packets when the beam is broken, even though those packets are successfully transmitted from the ATTiny84-connected NRF24L01+ to the Raspberry Pi-connected NRF24L01+:

void loop()
{
if(!(IRpin_PIN & (1 << IRpin)))
{
digitalWrite(redLed, LOW);
}
else
{
digitalWrite(redLed, HIGH);
if(value=radio.write((void *) &value,8) == 1)
{
}
}
}

I was referring to this, and your example "test sketch". Neither of these sketches are emptying the RX buffer, so sending will begin to fail after the RX FIFO fills up on this device, not the RPi.

Hi all.

TMRh20, thank you for your clarification. I have now changed the loop code to the following:

void loop(void)
{
  uint64_t gotByte;
  uint8_t pipeNo;
  
  if (!(IRpin_PIN & (1 << IRpin)))
  {
    digitalWrite(redLed, LOW);
    messageSent = false;
  }
  else
  {
    digitalWrite(redLed, HIGH);  
    if(!messageSent)
    {
      if(radio.write((void *) &value,8))
      {
        ++value;
        messageSent = true;
      }
      while(radio.available(&pipeNo)) // Empty received auto-ACKs from PTX's RX FIFOs.
      {
        radio.read(&gotByte, 8);
      }
    }
  }
}

The beam breaks now work the way I expect them to work, and the output from the Pi no longer repeats. radio.write() no longer repeatedly returns false.

In section 6.1.4 (RX Mode) on page 22 of 75 in the NRF24L01+ data sheet, it explains exactly the information which you were implying. Before reading this section, I did not realize that the PTX's received auto acknowledgement from the PRX had to be manually emptied from the the PTX's RX FIFOs. The longer version of my sketch had code that did this. By not doing this in the shorter version, I introduced the problem for which this post was created. You live, you learn.

Thank you all for helping me resolve this issue. I can now move forward on my project

Take care.

-Z