nRF24 / RF24

OSI Layer 2 driver for nRF24L01 on Arduino & Raspberry Pi/Linux Devices
https://nrf24.github.io/RF24
GNU General Public License v2.0
2.21k stars 1.02k forks source link

Not all writeAckPayload reaches the sender. #560

Closed jimann100 closed 4 years ago

jimann100 commented 4 years ago

I modified Arduino's "Pong" script to send an acknowledge payload ever 5th time. With this setup, I get a constant stream of "Blank Payload Received" messages from the "Ping" side. If I, however, add an additional acknowledge payload, one after the other, it is received on the "Ping" side.

Avamander commented 4 years ago

If this behaviour doesn't exist in the examples then there's a bug in your code.

jimann100 commented 4 years ago

This is my first experience submitting an issue to github. Is it ok, to simply reply to your response?

I could have an error in my code, so a just reinstall the pingpair_ack example and ran it. Sure enough, it behaved properly. I then add my one-in-five response by adding the following around the writeAckPayload.

  ackCount++;
  if(ackCount>5){
     radio.writeAckPayload(pipeNo,&gotByte, 1 );
     ackCount=0;
  }

It also worked properly. I then dropped the bandwidth from default to 250kb and changed the retry statement to the following, to allow for the slower connection.

radio.setRetries(15,15); // Smallest time between retries, max no. of retries radio.setDataRate(RF24_250KBPS);

This is when I start seeing missing Acknowledge Payloads.

The slower bandwidth gives me more range, which is why I want to use it.

Jim

TMRh20 commented 4 years ago

With ack payloads the only way to ensure consistent delivery is to ensure that you have written the ack-payload to the radio BEFORE a payload is received. With your modified code, this is not the case.

jimann100 commented 4 years ago

I agree, the payload has to be present prior to receiving an incoming packet on the “Ping” side. Given my code setup, I expect 5 "Blank Payload Received." Messages, on the ping side, followed by a message packet referring to the previous “Ping” outgoing message, not the current one, to account for the requirement you just mentioned. All of this happens when I run at the default bandwidth, but not at 256 kbaud.

I know this is a very strange setup, but I only occasionally need to return a payload from the “Pong”, but when I do I don’t want any extraneous stuff in the 3 deep fifo, returning obsolete timing info, which is part of all returned packets in my application.

I’m using EBytes E01 transceiver and am beginning to wonder if they are using clone parts in their module.

jimann100 commented 4 years ago

It appears that writing out an acknowledge payload while the nrf24 transceiver sends out a blank payload can trick it into actually sending out the current payload or a blank one, but clearing the FIFO in both cases, which will either result in the potential loss of a packet in the “blank case” and returning the acknowledge packet a write cycle early, in the other case..

To validate this assumption, I performed the following timing test on the Ping_Back loop: When(radio.available == false) loop time=6 microseconds.

When (radio.available == true) Start of radio.available to start of radio.writeAckPayload: 100 microseconds.

On the Pong_Out side: Write/Acknowledge loop time at 256 kbits/sec: 1100 usec. Assume Acknowledge half is 1100/2=550 usec

Write/Acknowledge loop time at 1 Mbits/sec: 580 usec. Assume Acknowledge half is 580/2=290 usec

As can be seen, without any added timing delays, min to max time loop goes from 100 to 106 microseconds, on the Pong_back site. Where the acknowledge time is 290 usec at 1 Mbits/sec rate and 500 usec at 256 Kbit/sec rate. As you can see there is a lot of potential overlap between writeAckPayload and the transceivers transmission of a acknowledge packet. When this happens, as mentioned above, you get either a “blank packet on the “Ping_back” side or the current acknowledge packet. In both cases, however, the FIFO is emptied, which results in the loss of the packet in the “blank packet” case and returning the current packet a write cycle early, in the other case.

My Basic “Pong_Back” loop looks this:

void loop(void) { byte pipeNo; byte gotByte;

if( radio.available(&pipeNo)){

    radio.read( &gotByte, 1 );
    delayMicroseconds(400);
    count++;
    if(count>5){

      radio.writeAckPayload(pipeNo,&gotByte,1 );
      Serial.println(count);
      count=0;
    }

  }

}

I used the delayMicroseconds function to adjust the amount of potential overlap between the transceiver transmission and the radio.writeAckPayload operation. As you can see, I can take both the 256 kbit/sec and 1 Mbit/sec configuration from returning an erroneous “current acknowledge” or blank payload, with loss of packet case, to a working “previous acknowledge” case by simply adjusting the delay from zero to 400 usec.

My fix for the issued will to add a delayMicroseconds(500) between the radio.read and writeAckPayload, which should prevent overlap between this function and the transceiver acknowledge operation.

Zero delay. Both 256 kbits/sec and 1Mbits/sec return current acknowledge payload.

50usec delay At 256 Kbit/sec, returns current payload. At 1 Mbit/sec, returns mostly blank payload, but sometimes returns current payload.

100usec delay At 256 kbit/sec, returns current payload. At 1 Mbit/sec, returns previous payload.

150usec delay At 256 kbits/sec, returns current payload. At 1M bit/sec, returns previous payload.

300 usec delay. At 256 kbits/sec, return either “blank payload” or previous payload. At 1Mbit/sec, returns previous acknowledge payload.

400 usec delay. At 256 kbits/sec, return previous payload. At 1 Mbit/sec, returns previous acknowledge payload.