PaulStoffregen / RadioHead

Version of RadioHead library for Teensy boards
http://www.airspayce.com/mikem/arduino/RadioHead/
Other
257 stars 156 forks source link

NRF24L01 Packet Queueing Problem #13

Open JamesLenz opened 7 years ago

JamesLenz commented 7 years ago

I am trying to create a simple TCP-like sending protocol by sending, waiting for an ack, and if no ack is received in a certain amount of time, try again.

I got it mostly working except for a frequent, but random occurring problem. The recipient will randomly miss a packet, but when the next packet is received, it receives the previous missed packet. I have tried clearing and flusing the rx buffer, but to no avail. It appears as if the rx queue is receiving it, but it cannot be detected until another packet is received.

Perhaps a packet is available, but available() is returning an incorrect value?

Here is thread from the arduino forum that mentions a similar issue:

Here is the output that demonstrates this issue screen shot 2017-06-01 at 1 16 01 pm

Sender Code

#include <SPI.h>
#include <RH_NRF24.h>

// Singleton instance of the radio driver
RH_NRF24 nrf24(9, 8); //my implementation

void setup() {
  Serial.begin(9600);
  while (!Serial) 
    ; // wait for serial port to connect. Needed for Leonardo only
  if (!nrf24.init())
    Serial.println("init failed");
  // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm
  if (!nrf24.setChannel(1))
    Serial.println("setChannel failed");
  if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm))
    Serial.println("setRF failed");
}

void loop() {
  delay(2000);
  uint8_t data1[] = "testtesttest";
  sendTCP(data1, sizeof(data1));
}

int timeout = 200;
byte maxAttempts = 1;
bool sendTCP(uint8_t *data, uint8_t len) {
  static uint8_t packetId = 0;
  byte attempts = 0;

  Serial.print(millis());
  Serial.println(": TCP sending");
  Serial.print("  packet id: ");
  Serial.println(packetId);

  while(attempts < maxAttempts) {
    //send data
    nrf24.setHeaderId(packetId);  //set packet id in order to differentiate between packets and their ack
    nrf24.send(data, len);        //send data
    nrf24.waitPacketSent();       //block while data is sending

    //wait for ack
    //Serial.println("  waiting for ack");
    uint8_t temp_buf[RH_NRF24_MAX_MESSAGE_LEN];
    uint8_t temp_len = sizeof(temp_buf);
    if(nrf24.waitAvailableTimeout(timeout) && nrf24.recv(temp_buf, &temp_len)) {
      Serial.print("  ack received...");
      if(nrf24.headerId() == packetId) {
        Serial.println("id match");
        break;
      }
      Serial.print("no id match ");
      Serial.print(nrf24.headerId());
      Serial.println(", clearing buffer");
      while(nrf24.available() && nrf24.recv(temp_buf, &temp_len)) { //clear buffer in case there is more than 1 packet
        ; //no-op
      }
    }
    attempts++;

    Serial.print("  ack timeout, sending again ");
    Serial.print(attempts);
    Serial.print(" / ");
    Serial.println(maxAttempts);
  }

  packetId++;

  if(attempts >= maxAttempts) {
    Serial.println("  sending failed");
    return false;
  }
  Serial.println("  sending succeeded");
  return true;
}

Receiver Code

#include <SPI.h>
#include <RH_NRF24.h>

// Singleton instance of the radio driver
RH_NRF24 nrf24(9, 8); //my implementation

void setup() {
  Serial.begin(9600);
  while (!Serial) 
    ; // wait for serial port to connect. Needed for Leonardo only
  if (!nrf24.init())
    Serial.println("init failed");
  // Defaults after init are 2.402 GHz (channel 2), 2Mbps, 0dBm
  if (!nrf24.setChannel(1))
    Serial.println("setChannel failed");
  if (!nrf24.setRF(RH_NRF24::DataRate2Mbps, RH_NRF24::TransmitPower0dBm))
    Serial.println("setRF failed");
}

void loop() {
  uint8_t buf[RH_NRF24_MAX_MESSAGE_LEN];
  uint8_t len = sizeof(buf);

  if(nrf24.available() && recvTCP(buf, &len)) {
    ;
  }
}

bool recvTCP(uint8_t* buf, uint8_t* len) {
  static int packetId = -1;
  Serial.print(millis());
  Serial.print(": Checking if data available...");
  if(nrf24.recv(buf, len)) {
    //send ack
    uint8_t data[] = "";
    nrf24.setHeaderId(nrf24.headerId()); //set packet id to received packet's header id
    nrf24.send(data, sizeof(data));      //send data (ack)
    nrf24.waitPacketSent();              //block while ack is sending

    //repeat packet
    if(nrf24.headerId() == packetId) {   //server didn't get our ack, so it sent the packet again, just ack again and ignore
      Serial.print("yes, repeat packet: ");
      Serial.println(packetId);
      nrf24.flushRx();
      nrf24.clearRxBuf();
      return false;
    }

    packetId = nrf24.headerId();         //remember our last received packet id

    Serial.print("yes: ");
    Serial.println(packetId);
    Serial.print("   Received message: ");
    Serial.println((char*)buf);
  } else {
    Serial.println("no");
    nrf24.flushRx();
    nrf24.clearRxBuf();
    return false;
  }
  nrf24.flushRx();
  nrf24.clearRxBuf();
  return true;
}
dangerverma commented 6 years ago

Don't ask my why, i am just a technology enthusiast but when i put in

TX: nrf24.setChannel(1); (in setup())

and RX: nrf24.setChannel(5); (again in setup())

it works brilliantly and consistently each time without any queuing.

Some radio genius should be able to answer it.

Try and see if it works for you!

JamesLenz commented 6 years ago

I will probably give it another go after finals, I have NO time to mess with this right now but if this does indeed fix the problem, thank you

iwasz commented 6 years ago

I had similar problem. I wired two nRFs to a STM32F4 board, where one was sending and the other receiving and I compared what was sent with the data received. And to my surprise data I was receiving sometimes would arrive in wrong order (BTW the same code on much slower STM32F0 board was fine)! My problem proved to be due to wrong CE pin code in my transmit function (I use my own nRF lib). So faulty code looked like that :

void Nrf24L01P::transmit (uint8_t *data, size_t len)
{
        setCe (true);
        spi->setNss (false);
        spi->transmit8 (W_TX_PAYLOAD);
        spi->transmit8 (data, len, nullptr, bogoDelay);
        spi->setNss (true);
        setCe (false);
}

And the code that fixed my problem :

void Nrf24L01P::transmit (uint8_t *data, size_t len)
{
        spi->setNss (false);
        spi->transmit8 (W_TX_PAYLOAD);
        spi->transmit8 (data, len, nullptr, bogoDelay);
        spi->setNss (true);
        setCe (true);
        HAL_Delay (1);
        setCe (false);
}

Only after the data to be transmitted is put into the RX FIFO, I make a HI pulse on CE pin. Diagram on page 35 of nRF24L01+ pointed me in the right direction. PS of course 1ms is too long and this delay is to be modified. Full code is here : https://github.com/iwasz/libmicro/tree/master/src/rf

rmolinowski commented 1 year ago

Greetings, great library, but I have this issue as well. Since I'm sending a joystick control position several times a second over RF, it's not too big an issue, but the "solution" by dangerverma, above makes no sense, and Radiohead uses spiburstwrite and not the method shown by iwasz. Any assistance on this would be helpful.