dparson55 / NRFLite

nRF24L01+ library with AVR 2 pin support, requiring very little code along with YouTube videos showing all available features. It can be installed via the Arduino IDE Library Manager
MIT License
155 stars 25 forks source link

ACK packets short range only, TX packets OK with NRF24L01+PA+LNA modules #77

Closed barjac closed 2 years ago

barjac commented 2 years ago

Hi, I have been working on this project for some time, but only recently have the transmitter in a portable state for doing range checks. I am building a system for remote control of electronics mounted at the top of an amateur radio mast.

I am using two arduino nano mega328 (v3 I think - Chinese) with two NRF24L01 + LNA + PA.

I need about 100m reliable range line of sight outdoors which works fine on transmit, however the received ackdata dies at a few meters, so even high gain yagi beam antennas would probably not compensate.

Data rate is 2Mb/s and dropping this either makes no difference or seems worse.

I have checked the frequency in use with a scanning sketch and it is clear.

I wondered about receiver sensitivity at the transmitter, but after swapping the two transceivers over there is no improvement. The transmitted data packet is one 32bit byte and the ackdata packet is one 8bit byte. The ack is rock solid with the units side by side. I am not using the 2 pin system on the nanos.

Both NRF24L01 units have their own dedicated 3.3V regulators and extra decoupling on a main PCB as well as the proverbial 10uF chip capacitor between GND and Vcc on the NRF cards.

The transmitter is in a steel box with the antenna protruding through the top panel such that the SMA connector is just below the panel. I can't imagine that any significant RX noise will be picked up from the arduino. The TX is battery powered so mains borne noise cannot be an issue.

I have also tested with a basic (non-amplified) NRF24 at the RX end, with the same range issue albeit shorter on both TX and RX.

Sorry for the long preamble, but I wanted to explain in some detail what I have already tried.

After reading all the info that I can find related to these, my most recent thought is about TX/RX timing. Is there any way to increase the delay between RX of the data packet and TX of the ack at the RX end? I have seen a discussion about this relating to the NRF24 library but not for NRFLite. Maybe if the TX receiver takes time to become fully active then with such short packets, maybe the packet arrives back before the TX receiver AGC has settled. I'm now clutching at straws :) Possibly similar issue with the LNA switching on these cards? I'm not too bothered about speed, more reliability :)

Any ideas would be appreciated and I can send the TX and RX sketches if you think they will help, although simplified test cases for each would probably make more sense, as it's a rather specialized application.

dparson55 commented 2 years ago

Hi Barry, thanks for all the detail about the issue, it is a huge help. I had two ideas: increase the wait time between retries when sending ACK packets, or do not use the ACK feature of the radio and instead manually switch the radios to RX and TX mode as needed to send data back and forth.

NRFLite doesn't expose the wait time setting so you'll need to edit your local NRFLite.cpp file directly. The datasheet suggests that 250us is needed for the largest ACK packet (32 bytes) when using the 1 and 2Mbps bitrates, and 500us for the 250Kbps bitrate.

https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_Preliminary_Product_Specification_v1_0.pdf image

In NRFLite I configure the radio to use 500us for the 1 and 2 Mbps bitrates and 1500us for 250Kbps, but maybe longer wait times will help improve the distance with the LNA+PA radio modules you're using. The wait time can be controlled in increments of 250us up to 4000us. If you find a higher wait time fixes the problem let me know and I will include the change in the library.

For an example, to set a 750us wait time you would change the SETUP_RETR register from B00011111 to B00101111 and then change _transmissionRetryWaitMicros from 600 to 850. There's a large if statement that assigns this register based on the bitrate and the code comments add more context.

https://github.com/dparson55/NRFLite/blob/master/src/NRFLite.cpp#L382 image

image

If the wait time increase doesn't help then you can bypass using the automatic ACK feature of the radio and just do the data sends back and forth manually. It's not hard to do and the TwoWayCom_SoftwareBased examples can be used as a starting point. These examples don't include retry logic but for reference, the automatic ACK feature of the radio attempts sending and waiting for a response up to 16 times. Also to avoid requesting an ACK, you need to perform a NO_ACK send, e.g. _radio.send(ID, &_data, sizeof(_data), NRFLite::NO_ACK)

barjac commented 2 years ago

Hi Dave, Many thanks for your rapid response, much appreciated. I am in the UK so we have a time difference, I guess from your accent on youtube :)

I have been reading all your comments and the links you have provided.

As I understand it the ARD sets the delay 'between' retries at the TX if an ACK has not been received, not a delay between the receipt of a packet and the transmit of the ACK by the RX. I assume that the ACK does not retry :)

I will try increasing the ARD, but I do suspect that not using the ACK feature will be the way to go, so I can time the return data to always occur when the TX receiver is receiving properly.

EDIT: I have tried increasing the ARD right up to 4000 with the _transmissionRetryWaitMicros at 4100 and it's making no difference at all. :( I restarted the Arduino IDE between re-builds in case it cached the library. Using the standard NRF at the receive end (I can't change the TX one as it won't physically fit) the ack starts to fail at about ~500mm separation, which is handy for local testing.

dparson55 commented 2 years ago

Ok too bad that didn't help and sorry I didn't mention the specific timing setting you asked about doesn't exist, the only control we have over timings related to the ACK feature is the automatic retransmit delay. It's also correct that the ACK transmission part performed by the primary RX radio is not retried, that ACK send is a fire and forget type of transmission that is performed as quickly as the radio designers deemed possible.

It seems like many PA+LNA versions of the radio are incompatible with the automatic ACK feature of the nRF24L01, maybe for the very hardware timing limitation you mentioned. Your observation about the ACK failing at ~500mm separation is interesting as well. With this additional information and the existing knowledge base of PA+LNA issues (#44, #56, #63, #66), I'm going to update the readme to mention the ACK feature of the radio probably won't work if using PA+LNA modules and suggest people consider RFM69 modules when needing longer range wireless capability.

Thinking more through it, adding retry logic isn't super straightforward and could easily result in some very messy code. Maybe a dedicated example is warranted. Let me know if you get stuck and would like an example.

barjac commented 2 years ago

Hi Dave, Wow! What a struggle I have had! Good news first though, I now have a working system with 200m range using a really crude checksum to force resends. The RX end is simple, but the TX has been a nightmare, mainly because I declared an int in a function and globally as well and got myself into all sorts of trouble! I just could not understand why it was empty! It's been a steep learning curve as I only really started to code for the Arduino at the start of this project, although I have been playing with Clipper and bash for years. Never C or C++ though.

My initial range tests were through double glazing and I now know that it attenuates 2.5GHz considerably. 30m now through glass and 200m outside. Line of sight is not quite what you might expect!

This is the through glass test: NRF24L01+LNA+PA_glass_dist This is outside, with RX on a dry stone wall and the TX handheld at head height. NRF24L01+LNA+PA_max_dist

The RX has two 8 relay opto-isolated boards attached with LEDs on each relay feed, so in the dark I could set up different light combinations to see the response at the RX and see a count incrementing at each loop of the RX on the LCD of the TX. (when in range!) The RX was running from a phone USB battery power bank for the test. No power on the relays of course, and all on breadboard at the RX end. The TX is a lot more complete, which is why I have persevered with the NRF24L01! PCBs are time consuming. TX

Sketches are here: http://mtf.duckdns.org/pub/linux/barjac/amateur/Arduino/RX_noack_35 http://mtf.duckdns.org/pub/linux/barjac/amateur/Arduino/TX_noack_35

Many thanks for your work on NRFLite! Without it I would probably have given up. I still don't understand classes, so some of your code has me lost. structs were also totally new, I now grasp the concept, but don't understand why or if they need to be used in NRFLite. Pointers are another thing that was new but I am coping with them. Whether their use actually makes a difference for this application I don't know. At 75 years of age new concepts take some hammering into the grey matter! Cheers, Barry

dparson55 commented 2 years ago

Congrats, the project looks great! Hopefully that range will work for you but if not, those RFM69 modules would be easy to integrate since they use SPI just like the nRF24 modules, so an adapter could be made for your existing PCB.

Structs are just a handy way to pass around data in code to make the life of the programmer easier. NRFLite doesn't care what the variable is that you send, internally it just assumes whatever variable you give it doesn't exceed the 32 byte limit of the nRF24 chip. So you can send an int, a float, an array of bytes, or anything else up to that 32 byte limit. Using a struct is purely for the programmer's ease of use when you want to access individual pieces of data contained within a single variable.

One note about the checksum, NRFLite enables the 1-byte CRC feature of the nRF24. If the CRC fails the packet is discarded by the radio before NRFLite knows it was received. So if NRFLite reports a packet was received, the check passed and you can simply send back an ACK. Having your own checksum doesn't hurt obviously since this 1-byte CRC isn't foolproof, but I just wanted to let you know. image image

Continued luck, I'll close this issue.

cenders77 commented 2 years ago

Maybe the incompatibility can be resolved by keeping CE active (and hence PA+LNA) until transmission with ACK is finished, not just CE_TRANSMISSION_MICROS to trigger a single transmission.