RF24 icon indicating copy to clipboard operation
RF24 copied to clipboard

Problems caused by differences between versions v1.4.5 and v1.3.2

Open SuhaylZhao opened this issue 2 years ago • 37 comments

Hi,

I recently updated the rf24 library from v1.3.2 to v1.4.5, but after the update, I found that the original user code did not work properly. With the same user code, it is normal to use the v1.3.2 library, but not normal to use v1.4.5.

The specific behaviors are as follows: the communication between two rf24 is very unstable. When the distance between the two devices is about 20cm, the communication can be stable after power on. However, after a period of time, sometimes several tens of seconds, sometimes several minutes, the connection will be disconnected. When the distance between the two devices is more than two meters, the communication will also be disconnected, and the communication cannot be resumed even if they approach again.

These situations do not exist when using the v1.3.2 library.

This is obviously abnormal, so I checked the rf24.cpp code in v1.4.5 and v1.3.2. I found that the code write_register(NRF_STATUS,_BV(MAX_RT) ); in line 927 in v1.3.2 was deleted. I restored it to the corresponding position in v1.4.5. Then I found that I succeeded and v1.4.5 could work normally. I want to know why? Why is this code deleted?

Thanks for any answers!

Transmitter code:

#include <SPI.h>
#include "RF24.h"
RF24 radio(9, 10);                // define the object to control NRF24L01
const byte addresses[6] = "Free1";// define communication address which should correspond to remote control
// wireless communication
int dataWrite[8];                 // define array used to save the write data
// pin
const int pot1Pin = A0,           // define POT1 Potentiometer
          pot2Pin = A1,           // define POT2 Potentiometer
          joystickXPin = A2,      // define pin for direction X of joystick
          joystickYPin = A3,      // define pin for direction Y of joystick
          joystickZPin = 7,       // define pin for direction Z of joystick
          s1Pin = 4,              // define pin for S1
          s2Pin = 3,              // define pin for S2
          s3Pin = 2,              // define pin for S3
          led1Pin = 6,            // define pin for LED1 which is close to POT1 and used to indicate the state of POT1
          led2Pin = 5,            // define pin for LED2 which is close to POT2 and used to indicate the state of POT2
          led3Pin = 8;            // define pin for LED3 which is close to NRF24L01 and used to indicate the state of NRF24L01

void setup() {
  // NRF24L01
  Serial.begin(9600);
  radio.begin();                      // initialize RF24
  radio.setChannel(1);
  radio.setAutoAck(true);
  radio.setPALevel(RF24_PA_MAX);      // set power amplifier (PA) level
  radio.setDataRate(RF24_1MBPS);      // set data rate through the air
  radio.setRetries(3, 15);            // set the number and delay of retries
  radio.openWritingPipe(addresses);   // open a pipe for writing
  radio.openReadingPipe(1, addresses);// open a pipe for reading
  radio.stopListening();              // stop listening for incoming messages

  // pin
  pinMode(joystickZPin, INPUT);       // set led1Pin to input mode
  pinMode(s1Pin, INPUT);              // set s1Pin to input mode
  pinMode(s2Pin, INPUT);              // set s2Pin to input mode
  pinMode(s3Pin, INPUT);              // set s3Pin to input mode
  pinMode(led1Pin, OUTPUT);           // set led1Pin to output mode
  pinMode(led2Pin, OUTPUT);           // set led2Pin to output mode
  pinMode(led3Pin, OUTPUT);           // set led3Pin to output mode
}
int cnt = 0;
void loop()
{
  // put the values of rocker, switch and potentiometer into the array
  dataWrite[0] = analogRead(pot1Pin); // save data of Potentiometer 1
  dataWrite[1] = analogRead(pot2Pin); // save data of Potentiometer 2
  dataWrite[2] = analogRead(joystickXPin);  // save data of direction X of joystick
  dataWrite[3] = analogRead(joystickYPin);  // save data of direction Y of joystick
  dataWrite[4] = digitalRead(joystickZPin); // save data of direction Z of joystick
  dataWrite[5] = digitalRead(s1Pin);        // save data of switch 1
  dataWrite[6] = digitalRead(s2Pin);        // save data of switch 2
  dataWrite[7] = digitalRead(s3Pin);        // save data of switch 3

  // write radio data
  int res = radio.writeFast(&dataWrite, sizeof(dataWrite));
  if (res)
  {
    Serial.print("1");
    digitalWrite(led3Pin, HIGH);
  }
  else {
    Serial.print("0");
    digitalWrite(led3Pin, LOW);
  }
  delay(20);
  cnt++;
  if (cnt >= 50) {
    cnt = 0;
    Serial.println();
  }
  // make LED emit different brightness of light according to analog of potentiometer
  analogWrite(led1Pin, map(dataWrite[0], 0, 1023, 0, 255));
  analogWrite(led2Pin, map(dataWrite[1], 0, 1023, 0, 255));
}

Receiver code:

#include <SPI.h>
#include "RF24.h"

#define PIN_SPI_CE      9
#define PIN_SPI_CSN     10

RF24 radio(PIN_SPI_CE, PIN_SPI_CSN); // define an object to control NRF24L01
const byte addresses[6] = "Free1";   //set commucation address, same to remote controller
int nrfDataRead[8];            //define an array to save data from remote controller
void setup() {
  Serial.begin(9600);

  // NRF24L01
  if (radio.begin()) {                  // initialize RF24
    radio.setChannel(1);
    radio.setAutoAck(true);
    radio.setPALevel(RF24_PA_MAX);      // set power amplifier (PA) level
    radio.setDataRate(RF24_1MBPS);      // set data rate through the air
    radio.setRetries(0, 15);            // set the number and delay of retries
    radio.openWritingPipe(addresses);   // open a pipe for writing
    radio.openReadingPipe(1, addresses);// open a pipe for reading
    radio.startListening();             // start monitoringtart listening on the pipes opened
    Serial.println("Start listening remote data ... ");
  }
  else {
    Serial.println("Not found the nrf chip!");
  }

}

void loop() {
  delayMicroseconds(1000);
  if (radio.available()) {              // if receive the data
    while (radio.available()) {         // read all the data
      radio.read(nrfDataRead, sizeof(nrfDataRead));   // read data
    }
    Serial.print("P1/P2/X/Y/Z/S1/S2/S3 : ");
    for (int i = 0; i < sizeof(nrfDataRead) / 2; i++) {
      Serial.print(nrfDataRead[i]);
      Serial.print('\t');
    }
    Serial.print('\n');
  }
}

SuhaylZhao avatar Aug 31 '22 10:08 SuhaylZhao

line 927 in v1.3.2 was deleted

It happened between https://github.com/nRF24/RF24/compare/v1.3.6...v1.3.7 More specifically, https://github.com/nRF24/RF24/commit/3af80f8fa4ddc741ef9d2f1b9793da0ab5047b1a

I'm kinda relieved to say this was before my time in this org.

2bndy5 avatar Aug 31 '22 10:08 2bndy5

Concerning your usage:

  int res = radio.writeFast(&dataWrite, sizeof(dataWrite));
  if (res)
  {
    Serial.print("1");
    digitalWrite(led3Pin, HIGH);
  }
  else {
    Serial.print("0");
    digitalWrite(led3Pin, LOW);
  }

There's seems to be some discrepancy in the docs where the return value described is slightly different between the the documented overloads.

In actuality (from reading the src code), the return value doesn't actually describe if the payload was sent and acknowledged. Rather the return value only describes if the payload was written to a level in the TX FIFO. Your choices are:

  1. use a normal write().
  2. call txStandBy() after calling writeFast(). However, this could block the TX node until txStandBy() return the status of transmitting all payloads in the TX FIFO.
  3. flush the TX FIFO when writeFast() returns false, but that may be considered data loss (even if the data is outdated).
  4. use reUseTx() when writeFast() returns false. This might also lead to data loss since the SPI command allows a new payload to overwrite an existing payload in the TX FIFO.

In fact, this comment kinda seems outdated without the MAX_RT flag getting cleared. https://github.com/nRF24/RF24/blob/89358b2df7de37d64f2bba180d61b6d200c54070/RF24.cpp#L1352


I'd be more concerned with why your receiver loses connection because that is why you transmitter's FIFO is filling up.

2bndy5 avatar Aug 31 '22 11:08 2bndy5

Remember that if the receiver's RX FIFO is full, then no incoming payloads are allowed in.

    radio.setChannel(1);

Have tried switching to a higher channel? Lower channels are typically quite noisy (with interference).

2bndy5 avatar Aug 31 '22 11:08 2bndy5

I've taken a quick look at this, and honestly I don't know why it stops working, but two workarounds are:

  1. Disable Auto-Ack - It should work fine without auto-ack enabled
  2. Call radio.txStandBy(); after calling radio.write();

I'll take a closer look at this when I have a chance...

"When transmitting data in rapid succession, it is still recommended by the manufacturer to drop the radio out of TX or STANDBY-II mode if there is time enough between sends for the FIFOs to empty. This is not required if auto-ack is enabled."

Might have to change this comment in the docs...

TMRh20 avatar Aug 31 '22 11:08 TMRh20

We could manually turn off the CE pin when MAX_RT is asserted during writeFast().

2bndy5 avatar Aug 31 '22 11:08 2bndy5

I'm not sure what effect that would have?

TMRh20 avatar Aug 31 '22 11:08 TMRh20

drop the radio out of TX or STANDBY-II mode

2bndy5 avatar Aug 31 '22 11:08 2bndy5

Hmmm, interesting.

TMRh20 avatar Aug 31 '22 11:08 TMRh20

Technically, the radio should auto switch to Standby-I mode if the FIFO is full, but pulling down the CE pin should exit active TX mode entirely.

2bndy5 avatar Aug 31 '22 11:08 2bndy5

I don't really think I like that idea, it could cause data loss, where calling txStandby would ensure that data is sent.

TMRh20 avatar Aug 31 '22 11:08 TMRh20

All in all this looks like kind of a timing issue. It also seems to work if I set the Serial baud to 115200 on the receiver. I think the transmitter is simply sending data faster than the receiver can process it, then hitting MAX_RT. The flag never clears because txStandBy is never called.

TMRh20 avatar Aug 31 '22 11:08 TMRh20

What if the RX node stops printing to Serial? That should speed things up significantly on that end.

This also seems done for console readability:

  delayMicroseconds(1000);

2bndy5 avatar Aug 31 '22 11:08 2bndy5

I think the issue will still happen when/if the receiver goes out of range. Either auto-ack should be disabled or call txStandBy() at some point in the code would be the fix/workaround.

TMRh20 avatar Aug 31 '22 11:08 TMRh20

I need to amend the docs about the writeFast() return value. The one for writeFast(buf, len) (no multicast) is definitely wrong.

2bndy5 avatar Aug 31 '22 12:08 2bndy5

Yeah, we might want to add a comment about hitting MAX_RT if AA is enabled, and the need to call txStandBy();

TMRh20 avatar Aug 31 '22 12:08 TMRh20

@SuhaylZhao

This should do the trick:

 int res = radio.writeFast(&dataWrite, sizeof(dataWrite));
 
 if (res)
  {
    Serial.print("1");
    digitalWrite(led3Pin, HIGH);
  }
  else {
    radio.txStandBy();  //<<------ ADD THIS
    Serial.print("0");
    digitalWrite(led3Pin, LOW);
  }

TMRh20 avatar Aug 31 '22 13:08 TMRh20

Hi @2bndy5 @TMRh20 ,

Thank you!

I tried your suggestion. Here are the test results:

  1. use a normal write().

It doesn't work at all, but when I touch the pin of the rf24 module with my finger, it can work unstably. It looks like the rf24 module is broken (in fact it is good).

  1. call txStandBy() after calling writeFast()

writeFast() always returns true, even in the case of communication failure.

  1. flush the TX FIFO when writeFast() returns false.

It does not always return true, but it also returns true when communication fails.

  1. use reUseTx() when writeFast() returns false

It can work well.

  1. radio.txStandBy(); //<<------ ADD THIS

It can work well.

For the above 4 and 5, I guess the call of write_register(NRF_STATUS, _BV(MAX_RT)); function plays a key role.

I know that the radio.writeFast function return true does not mean that the communication is really successful. However, it returns false, which means that there must be something wrong with the communication. Therefore, I can use an indicator to judge whether the current communication status is normal. In fact, satisfactory results can be achieved by using this method.

Thanks again for your help!

SuhaylZhao avatar Sep 01 '22 01:09 SuhaylZhao

but when I touch the pin of the rf24 module with my finger, it can work unstably.

You could try scraping away any dirt from the antenna. There tends to be a build-up of dust (dirt particulate from the air) on radios that have been running for a while. I recently had to do this to all my test rigged radios (which use a builtin antennas), and it restored reception to what I had expected.

Otherwise, you might need to replace the added capacitor in your power circuit. These don't remain consistent indefinitely (depending on the chemical makeup).

2bndy5 avatar Sep 01 '22 01:09 2bndy5

Hi,

  1. use a normal write().

Rf24 module is a brand-new module I purchased. And I tested several, and they all have the same performance.

SuhaylZhao avatar Sep 01 '22 02:09 SuhaylZhao

Are they PA/LNA type modules (which use an external antenna)?

2bndy5 avatar Sep 01 '22 02:09 2bndy5

Hi,

No, all the above tests are on-board antennas. like this: image

At the same time, I tested the PA/LNA type module and found that everything was normal. The write function can be used, and there is no need to call txStandBy or reUseTX in case of false.

SuhaylZhao avatar Sep 01 '22 02:09 SuhaylZhao

writeFast() always returns true, even in the case of communication failure.

This sounds link you turned off AutoAck. writeFast() can only report a failure if AutoAck is enabled.

2bndy5 avatar Sep 01 '22 02:09 2bndy5

Hi,

radio.setAutoAck(true);

Like the code shown on the first floor, the functions in setup have not been changed.

  1. call txStandBy() after calling writeFast()
  2. flush the TX FIFO when writeFast() returns false.

This does make the module work, but the problem is that writeFast() cannot return the false I expected.

SuhaylZhao avatar Sep 01 '22 03:09 SuhaylZhao

You may have some sort of power supply issues or something going on if the normal write() isn't working. Do you have a 10-100uF capacitor connected between the VCC and GND pins of the radio? If not you should try that.

This does make the module work, but the problem is that writeFast() cannot return the false I expected.

radio.writeFast(&dataWrite, sizeof(dataWrite));
int res = radio.txStandBy();  //<<------ ADD THIS

 if (res)
  {
    Serial.print("1");
    digitalWrite(led3Pin, HIGH);
  }
  else {
    Serial.print("0");
    digitalWrite(led3Pin, LOW);
  }

I think the above code would be what you are looking for. It should return false every time a write fails.

TMRh20 avatar Sep 01 '22 11:09 TMRh20

Touching the radio and getting better results usually indicates a power instability.

2bndy5 avatar Sep 01 '22 11:09 2bndy5

Hi,

Do you have a 10-100uF capacitor connected between the VCC and GND pins of the radio? If not you should try that.

It has a 10 UF tantalum capacitor and a 0.1 UF ceramic capacitor. Using the write() function, and I tried 47uF and 220uF capacitors respectively, but they didn't work. Then I used an oscilloscope to observe the voltage waveform between VCC and GND of the module (using a capacitor of 47uF), and found that the power supply was very stable, with peak to peak jumping between 80-100mv. Whether I touch with my finger to enable him to communicate successfully or fail, the voltage will not change.

In addition, I have also made new discoveries. When I set setPALevel to RF24_PA_MAX and RF24_PA_HIGH, the write() function cannot work. However, when I set setPALevel to RF24_PA_LOW and RF24_PA_MIN, the write() function can work normally. Correspondingly, the transmission distance decreases to a certain extent.

When using write() function, and module does not work, sometimes the touch pin will work, and sometimes the touch antenna will work.

radio.writeFast(&dataWrite, sizeof(dataWrite));
int res = radio.txStandBy();  //<<------ ADD THIS

It doesn't work, and its performance is the same as using only the write() function.

At present, my practice is to use the writefast() function, and call txStandBy() or reUseTX() when it returns false, which can make the module communicate well. The only problem is that I can't judge whether the current communication is good through the LED indicator. Because if it returns true, it does not mean that the communication is successful. If it returns false, it means that the communication must have failed.

SuhaylZhao avatar Sep 02 '22 01:09 SuhaylZhao

In addition, I have also made new discoveries. When I set setPALevel to RF24_PA_MAX and RF24_PA_HIGH, the write() function cannot work. However, when I set setPALevel to RF24_PA_LOW and RF24_PA_MIN, the write() function can work normally. Correspondingly, the transmission distance decreases to a certain extent.

This a current-saving hack we use in the examples because some boards voltage regulators don't provide enough current to transmit. Use your oscilloscope to check the current, and see if it flat lines during transmission. It sounds like you need a stronger power source - stronger meaning higher current.

2bndy5 avatar Sep 02 '22 01:09 2bndy5

Hi,

I tested the PA/LNA type modules yesterday. They worked very well, and the working conditions were the same. Only the modules were replaced in the same slot. Can this show that the power supply is good?

image

SuhaylZhao avatar Sep 02 '22 02:09 SuhaylZhao

That looks like an ebyte module. There are numerous kinds of the nRF24L01 radio from ebyte. I can't say anything else about that without seeing the store link you bought it from.

Side note

I recently had another user tell me that ebyte claimed to not support changing the PA level. But, that same user that directly asked ebyte about the PA level had a contradicting experience. So, I'm not entirely sure the specifications from ebyte are accurate.

There is a possibility that ebyte modules use the lna_enabled parameter to setPALevel() differently than the original nRF24L01. Some Chinese clones are known to do that.

2bndy5 avatar Sep 02 '22 02:09 2bndy5

Hi,

I found the above picture on the Internet. My module is like this. There are no characters on the shield. image

SuhaylZhao avatar Sep 02 '22 02:09 SuhaylZhao