nRF24 / CircuitPython_nRF24L01

CircuitPython driver library for the nRF24L01 transceiver.
http://circuitpython-nrf24l01.rtfd.io/
MIT License
45 stars 11 forks source link

update master branch from lite-beta branch #14

Closed 2bndy5 closed 3 years ago

2bndy5 commented 3 years ago

many optimizations and fixes. Once this PR is merged, there will be a new release. Updated docs for this branch are live. Changes include:

New Modules

Optimizations:

Changes

Testing 4-byte payloads on an ItsyBitsy M4 Express has shown that transmissions now take about 26-55 milliseconds (60-65 milliseconds for ACK packets with customized payloads).

jerryneedell commented 3 years ago

Thanks for the updates -- FYI -- I ran several of the examples (simple_test,interrupt_test,ack_payload_test,stream_test) between n RPi 4 and a feather-m4_express. All worked. I need to better understand the stream test. It worked much better sending from the RPi to the feather-m4. Sending from the feather-m4 to the pi, the pi only received few lines before stopping.

jerryneedell commented 3 years ago

got this error with the nrf24l01_context_test on bot the RPi and the Feather M4

Python 3.7.3 (default, Jul 25 2020, 13:03:44) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from nrf24l01_context_test import *
... # working output removed to just show traceback error. edited by @2bndy5
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/pi/projects/blinka/nrf24/nrf24l01_context_test.py", line 61, in <module>
    nerf.open_rx_pipe(2, b'?') # again only uses the first character
  File "/home/pi/projects/blinka/nrf24/circuitpython_nrf24l01/rf24.py", line 217, in open_rx_pipe
    " {})".format(self._addr_len)
ValueError: address must be a buffer protocol object with a byte length
equal to the address_length attribute (currently set to 3)

changing the line to

nerf.open_rx_pipe(3, b'???') # again only uses the first character

seems to work


settings configured by the nrf object
Channel___________________76 ~ 2.476 GHz
RF Data Rate______________1 Mbps
RF Power Amplifier________0 dbm
CRC bytes_________________2
Address length____________5 bytes
Payload lengths___________32 bytes
Auto retry delay__________1500 microseconds
Auto retry attempts_______3 maximum
Packets lost on current channel_____________________0
Retry attempts made for last transmission___________0
IRQ - Data Ready_______True    Data Ready___________False
IRQ - Data Fail________True    Data Failed__________False
IRQ - Data Sent________True    Data Sent____________False
TX FIFO full__________False    TX FIFO empty________True
RX FIFO full__________False    RX FIFO empty________True
Ask no ACK__________Allowed    Custom ACK Payload___Enabled
Dynamic Payloads____Enabled    Auto Acknowledgment__Enabled
Primary Mode_____________TX    Power Mode___________Off
TX address____________ bytearray(b'1Node')
Pipe 0 (closed) bound: bytearray(b'1Node')
Pipe 1 (closed) bound: bytearray(b'\xc2\xc2\xc2\xc2\xc2')
Pipe 2 (closed) bound: b'\xc3\xc2\xc2\xc2\xc2'
Pipe 3 (closed) bound: b'\xc4\xc2\xc2\xc2\xc2'
Pipe 4 (closed) bound: b'\xc5\xc2\xc2\xc2\xc2'
Pipe 5 ( open ) bound: b'1\xc2\xc2\xc2\xc2'
        expecting 32 byte static payloads

settings configured by the basicRF object
Channel___________________2 ~ 2.402 GHz
RF Data Rate______________2 Mbps
RF Power Amplifier________-12 dbm
CRC bytes_________________1
Address length____________3 bytes
Payload lengths___________8 bytes
Auto retry delay__________1000 microseconds
Auto retry attempts_______15 maximum
Packets lost on current channel_____________________0
Retry attempts made for last transmission___________0
IRQ - Data Ready______False    Data Ready___________False
IRQ - Data Fail_______False    Data Failed__________False
IRQ - Data Sent________True    Data Sent____________False
TX FIFO full__________False    TX FIFO empty________True
RX FIFO full__________False    RX FIFO empty________True
Ask no ACK__________Allowed    Custom ACK Payload___Disabled
Dynamic Payloads___Disabled    Auto Acknowledgment__Enabled
Primary Mode_____________TX    Power Mode___________Off
TX address____________ bytearray(b'1Node')
Pipe 0 (closed) bound: bytearray(b'1Node')
Pipe 1 (closed) bound: bytearray(b'\xc2\xc2\xc2\xc2\xc2')
Pipe 2 (closed) bound: b'\xc3\xc2\xc2\xc2\xc2'
Pipe 3 ( open ) bound: b'?\xc2\xc2\xc2\xc2'
        expecting 8 byte static payloads
Pipe 4 (closed) bound: b'\xc5\xc2\xc2\xc2\xc2'
Pipe 5 (closed) bound: b'\xc6\xc2\xc2\xc2\xc2'
2bndy5 commented 3 years ago

I think I should get rid of that check for address_length to exactly match the length of the address parameter being passed. The nrf24l01 doesn't really care since it only uses whatever number of bytes address_length is set to from the respective register.

@jerryneedell I can't thank you enough for unprompted testing on hardware I don't own. You're help is very much appreciated!

As for the stream test on the rpi. My experience has been that the rpi will receive an arbitrary number of payloads (not all) then stop receiving for some reason. I also noticed the the success stats returned by the sender are always 3 more than the rpi shows received. @jerryneedell does that description fit your experience using the stream test? I have a feeling the RX FIFO buffer on the nrf24l01 fills up (maxes out at 3 pending payloads to be read) while the rpi reads a payload then clears the irq_dr flag (all performed by recv()) at a certain point.

jerryneedell commented 3 years ago

"I also noticed the the success stats returned by the sender are always 3 more than the rpi shows received. @jerryneedell does that description fit your experience using the stream test? "

yes - exactly.

Glad to help test -- small price for all the work you have done providing this library. I just need to find more time to play with these radios.

2bndy5 commented 3 years ago

rf24_lite.py seems to recv() well, but send() seems broken. tested using simple_test on a M0 Feather express.

2bndy5 commented 3 years ago

not sure what's going wrong, but nrf24l01_2arduino_handling_data.py isn't properly communicating to my Feather M0 running TMRh20's GettingStarted_HandlingData.ino example sketch.

2bndy5 commented 3 years ago

Datasheet outlines typical receiving behavior in Appendix A. It dictates that the CE pin is pulled low before reading payload from RX FIFO. This means the nRF24 is forced to exit active RX mode before fetching received payload from the FIFO. Currently, recv() does not do this.

Implementing this seems to be important when receiving a stream of payloads on the Raspberry Pi. However testing on my ItsyBisty M4 shows that it's less important to implement. My main concern is the mandated wait time to enter active RX mode, about 130 microseconds per datasheet (add 150 microseconds if nRF24 is powered down). Not to mention the 5 milliseconds wait for every SPI transaction to let the CSN pin settle. While the 5 millisecond and the 130 microseconds wait times is something I learned from the TMRh20 library, it seems that library does not make sure that the CE pin is LOW before fetching payload from the RX FIFO in its read() function.

2bndy5 commented 3 years ago

@jerryneedell stream_test's slave() works on the RPi now. I had to make RF24.any() check the status byte data about what pipe received the next available payload in RX FIFO (RF24.pipe) instead of using the interrupt flag signifying that data was received (RF24.irq_dr). Toggling with the CE pin while fetching a payload didn't help at all.

Stats returned from master() on my ItsyBisty M4:

Transmission took 1475.06 ms successfully sent 100.0% (32/32)

Stats returned from master() on my RPi2:

Transmission took 1359.3282849993557 ms successfully sent 100.0% (32/32)

Only test left to debug is the broken compatibility with TMRh20's library, but I'm starting to think that is not a problem with my library. I tested 2 nodes talking to each other using the same example sketch from TMRh20's library (altering radioNumber accordingly), and it didn't work. I might scrap the rf24_lite.py file since it's half broken (receives but doesn't send)

P.S. I also want to implement a carrier wave test (just for good measure as they say). I already implemented carrier wave detection via RF24.rpd; now i have to create a carrier wave to detect.

jerryneedell commented 3 years ago

@2bndy5 Thanks for the updates! I can confirm that the stream test does work both ways between my RPi 4 and feather_m4_express.

2bndy5 commented 3 years ago

OK, the lite version passes the simple & stream tests on my Feather M0 Express. However, the lite version is having trouble appending the ACK payload and is receiving garbage/garbled payload during my ACK test's slave(). Although, master() works fine in the ACK test. TBH, if simple_test works, so should the IRQ test; I haven't wired the IRQ pin to any of my MCUs (except on my RPi2, which uses a customized shield), so I haven't been actually testing it.

I finally realized that my 2 nRF24L01+PA/LNA radios (each came with a detachable antennae) were the problem 👎 . They'd receive fine, but wouldn't send hardly anything (including ACK packet) -- success rate was less than 1 out of every 5 calls to send(). I'm looking into it tomorrow, but I have a feeling they might be counterfeits (bought them on amazon.com last year). Worth noting that I didn't get any success using send() until I increased the SPI frequency in the lite version (since that was the code running on my nRF24L01+PA/LNA with Feather M0 Express). The nRF24L01+PA/LNA did pass the carrier wave test using my new start_carrier_wave() & stop_carrier_wave() functions in tandem with RF24.rpd flag. 👍

I also have to look into the compatibility with TMRh20's library. This library can receive from TMRh20's library, but the ACK payload isn't getting through as well as the TX'd echo response in slave(). The 2arduino_handling_data test's master() isn't working at all.

jerryneedell commented 3 years ago

FYI - I have two types of nrf24l01 These have been working fine nRF24L01+ pack

but I have not had much success with these - it's been awhile since I tried them -- i'll try them again later today nRF24L01+PA+LNA modules

2bndy5 commented 3 years ago

much appreciated! These are the ones that aren't behaving for me. The datasheet says that the plus variants handle 250kbps better than the non-plus variants. Which is why I bought these -- so I could test that data rate. Wishing I documented those trials now, but that's what this PR is mainly for.

These are the ones that practically sing for me.

jerryneedell commented 3 years ago

I am having the asme issue with the module with attached antenna- receives fine, but does not transmit or I should say - it's transmissions are not received but he other module (working type). Note that for the ones I listed, both are identified a nRF24l01+

jerryneedell commented 3 years ago

FYI - I tried running the stream and interrupt tests between 2 RPi 4's both worked fine. Prior to this I has always only had a Pi on one end of the test.

2bndy5 commented 3 years ago

@jerryneedell thanks for confirming. I was under the mistaken impression that the plus variant was the modules that use an external antenna. You are correct; both on board and external antenna are in fact plus variants of the nRF24L01 family.

My googling (about the PA/LNA modules) led me to a few mysensor forums that consistently recommended using an external power source (in relation to the Arduino nano's inadequate 250mA peak from the 3V regulator -- my Adafruit boards boast a peak of 500mA from the 3V regulator) and implement shielding. Now the shielding recommended was most commonly adhesive aluminum foil wrapped around the nRF24L01 (excluding the antenna), but I'm concerned that might introduce a short circuit. Someone also recommended using plastic wrap as an alternative (I'll try this first, then add foil if no improvement). Some users also recommended lowering the pa_level to -18 dBm, but that is kind of a hack to compensate for a lower power 3V regulator. During my tests, I did lower the pa_level, but it didn't help any. The power problem has to be related to the extra circuitry involved in adding PA/LNA muxer to the antenna. Unfortunately, I don't have any higher power 3V regulators to play with.

It may not need to be said, but I am using a 100 microfarad capacitor in parallel with the VCC & GND pins.

2bndy5 commented 3 years ago

found this issue from TMRh20's library about Si24R1 chips. Also looks like they finally updated the python install instructions for the TMRh20 library. Apparently TMRh20 has transferred ownership of the RF24 libraries to a new RF24 organization. Another issue about a relic ACTIVATE SPI command used by nRF24L01 (non-plus variant) and possibly Si24R1, but raised for (yet another) nRF24 clone called "bk2425 chip".

found a store page that names the RFX2401C as the IC handling the PA/LNA muxing on the nRF24L01+PA/LNA. Did a search and found this datasheet on mouser.com, but the store page also has a link to a different datasheet among other resourceful links. This instructable notes (in conclusion step) that shielding is required and needs to be tied to ground (makes more sense to me now).

Uereka!

Found a dead link on Nordic semi forums. Plugged it into the wayback machine, and it was a wiki page with some useful details: Specification Value
Emission mode current(peak) 115 mA
Receive Mode Current(peak) 45 mA
Power-down mode current 4.2 uA

It even had downloadable example code (thank you wayback machine)!

SPI_RW_Reg(WRITE_REG + RF_SETUP, 0x07);   // TX_PWR:0dBm, Datarate:2Mbps, LNA:HCURR

Found out that the nRF24L01 (non-plus variant)'s datasheet includes info about a LNA_HCURR bit in the RF_SETUP register. Furthermore the same bit in the same register is used for expanded pa_level options for the Si24R1. This vital code snippet makes me think that the PA/LNA (with detachable antenna) are actually not plus variants of the nRF24L01 family. Counterfeit idea is still a possible conclusion as the they are not a product of Nordic Semi (per forum post with dead link).

2bndy5 commented 3 years ago

I had some success using an Arduino Nano w/ TMRh20's library running a nRF24L01+ module. Turns out that the address assigned to pipes 0 & 1 were backwards in the nrf24l01_2arduino_handling_data.py test as compared to the TMRh20's GettingStarted_HandlingData.ino (without any alteration). I should mention that my previously failed attempts used a nRF24L01-PA/LNA module attached to the Arduino nano & Feather M0 Express. I have proof:

>>> slave()
Responding: 37150676, -1.96827
successful response took 25.5 ms
Responding: 38222684, -1.95827
successful response took 25.0 ms
Responding: 39295060, -1.94827
successful response took 24.5 ms
>>> master()
Now Sending
Sent (1850756, 0.01) Got Response: (1850756, 0.02)
Round-trip delay: 50.5 ms
Now Sending
Sent (1851832, 0.02) Got Response: (1851832, 0.03)
Round-trip delay: 49.5 ms
Now Sending
Sent (1852906, 0.03) Got Response: (1852906, 0.04)
Round-trip delay: 51.0 ms
Now Sending
Sent (1853981, 0.04) Got Response: (1853981, 0.05)
Round-trip delay: 50.5 ms
Now Sending
Sent (1855056, 0.05) Got Response: (1855056, 0.06)
Round-trip delay: 50.5 ms

I'll try the TMRh20 library on the Feather M0 Express again tomorrow, but the Arduino IDE outputs an error saying that the library doesn't directly support it (during compilation).

2bndy5 commented 3 years ago

TMRh20's library's GettingStarted_HandlingData.ino example on my Adafruit Feather M0 w/ nrf24l01+ tested OTA compatible with my library running on the ItsyBitsy M4. I'm almost ready to merge this and publish a new release. I just want to try and get the nRF24L01-PA/LNA modules working. Also , I want to add in the use of that LNA_HCURR bit in the RF_SETUP register via the pa_level attribute.

2bndy5 commented 3 years ago

@jerryneedell wrapping the nRF24L01-PA/LNA modules with plastic wrap/foil didn't help. In fact it actually caused the SPI bus to crash (probably from static electricity). Providing the "shielding" a path to GND immediately froze the SPI bus. 😕 So I tried the 80's McGyver way and wrapped it with only electrical tape. The first try (using slave() in 2arduino test) immediately worked. ACK packets were sent back and the echo TX packet (including corresponding ACK packet) was received by the RX node. Then it went back to receiving only; it stopped sending anything again. I also noticed that a packet did get through when I pressed the MCU reset button (as well as when pulling/disconnecting the USB cable from my computer). After some thought, I figure the voltage spikes + the capacitor = enough juice to send a single payload (expected behavior if CE is HIGH and data exists in TX FIFO). So, I'm guessing that the 3V regulator on the ItsyBisty isn't supplying enough current (given the stats I posted above). I found some 250mA peak 3V regulators laying around, and I'll wire that to the USB pin on the ItsyBitsy. p.s. playing with the LNA_HCURR bit doesn't have an affect, but its in the code anyway.

jerryneedell commented 3 years ago

@2bndy5 are you using one of the nRF24L01-PA/LNA modules and a nRF24L01+ together or both the same for your tests? Just curious. One of my nRF24L01-PA/LNA seem to receive but the other does not receive or transmit. I have 2 more from a different manufacturer (I hope) https://www.amazon.com/gp/product/B01IK78PQA/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1 arriving soon and will try them. At least these have some documentation. I'll also try some shielding (carefully). I'll like be tomorrow before I can get to it. I do have the 100 microfarad caps in place as well. I am trying to find out if it is powered at 5V, will it drive the data lines to 5V as well. I don't want to be putting 5V on my inputs.

2bndy5 commented 3 years ago

I've always (inadvertently) had a nRF24L01-PA/LNA on 1 end and a reliable nRF24L01+ on the other. I've never had any receiving problems with the PA/LNA modules though (except it can't send an ACK packet)

jerryneedell commented 3 years ago

interesting -- my new radios https://www.amazon.com/gp/product/B01IK78PQA/ref=ppx_yo_dt_b_asin_title_o02_s00?ie=UTF8&psc=1 arrived - I connected one to my feather_m4_express and ran the stream test with my RPi (with nRF24L01+) with the RPi as master() it worked fine -- as with the other unit, BUT when I tried the new radio as master() only a few packets got through 3/32 -- better than before, but not great. Then I tried "shielding" it with my hand and it repeatedly can send all 32 packets! So shielding seems to be important.

This is not completely reproducible -- sometimes the Pi master() is reporting 0/32 even when all packets are received by the slave(). lots more testing to do, but this is encouraging

I am often seeing the case where the packets are all received, but the master is reporting 0/32 ... curious

2bndy5 commented 3 years ago

I am often seeing the case where the packets are all received, but the master is reporting 0/32 ... curious

Not really. If packets are received and reported as failed (using library defaults), it means the radio is having trouble TX-ing the ACK which matches the "master() is reporting as failed" behavior.

Then I tried "shielding" it with my hand and it repeatedly can send all 32 packets!

  1. I'm jealous
  2. I thought electrical tape was an 80's McGyver move. You make it look like a Millennial McGyver move. Well done.
2bndy5 commented 3 years ago

I did a stream_test between RPi & ItsyBitsy M4 with dynamic_payloads, arc, and auto_ack all disabled. Results were a bit better than v1.1.2 of this library:

using send(buf, force_retry=2) produced duplicate transmissions (greater than 100%) with auto_ack enabled on RX node (arc still disabled on TX node). This isn't a bug really, it just shows that TX transmissions got through, but some ACK packets didn't. <- won't/can't fix; requires user understanding of force_retry parameter and ACK behavior while listen = True & auto_ack > 1 on RX node and arc = 0 on TX node.

2bndy5 commented 3 years ago

BLE coming this way

I took another swing at making the nRF24L01 into a BLE beacon, and IT WORKS! Clearly the BLEfake branch needs some touch ups like adding a function to de-whiten received payloads from other BLE beacons (receiving beacon advertisements are not implemented yet). Tested with "nRF connect" app for android using ItsyBitsy M4 and a nRF24L01+ module. @jerryneedell Do you have an iPhone? I think there might be an issue trying to advertise to different SmartPhones (iOS vs Android), and I don't have a iPhone to confirm or not.

jerryneedell commented 3 years ago

BLE coming this way

I took another swing at making the nRF24L01 into a BLE beacon, and IT WORKS! Clearly the BLEfake branch needs some touch ups like adding a function to de-whiten received payloads from other BLE beacons (receiving beacon advertisements are not implemented yet). Tested with "nRF connect" app for android using ItsyBitsy M4 and a nRF24L01+ module. @jerryneedell Do you have an iPhone? I think there might be an issue trying to advertise to different SmartPhones (iOS vs Android), and I don't have a iPhone to confirm or not.

Yes, I do have a iPhone and will give it a try later today. Thanks!

jerryneedell commented 3 years ago

I have been trying the nrf24l01_fake_ble_test from a Raspberry Pi 4.
So far, I can see RFtest from a Chromebook (via nrfConnect and adafruit Bluefruit Connect) I also can see it from my Mac Desktop machine using gate Adafruit BluefruitConnect app

I have not been able to see it from my iPhone with with either the nrfConnect or Adafruit BluefruitConnect apps.

I can also see from an Android Phone with the Adafruit Bluefruit App and nrfConnect

2bndy5 commented 3 years ago

Thanks again for the thorough testing! I was afraid the iPhone might not pick it up. One of the resources that I'm going off of mentioned something about needing a modified flag for advertising to an iPhone. I'll give the FakeBLE class a boolean attribute to try and accommodate for this.

2bndy5 commented 3 years ago

my resource about this conflict originates from this comment:

buf[L++] = 0x42; //PDU type, given address is random; 0x42 for Android and 0x40 for iPhone

@jerryneedell I added a to_iphone attribute (on my FakeBLE branch) and set it to True by default (for your convenience). This showed successful advertisements to my Windows PC, but not to my Android phone. Setting this attribute to False showed successful broadcasts to my Windows PC & Android phone.

I should say that I'm quite bias against iPhones (for personal reasons). With that said, I'm partial to have this to_iphone flag set to False by default, and augment the example script accordingly. I do realize that this flag could be looked at as a to_android flag, but again: I'm biased. I'm hoping your testing will help indicate which direction to take (biases be damned).

EDIT: This lite-beta branch now has all progress from the FakeBLE branch due to #16 PR merger.

jerryneedell commented 3 years ago

@2bndy5 no debate from me. It's your code. You make the rules. It'll be tomorrow before I can do more testing but will give it a shot then.

2bndy5 commented 3 years ago

I like to adhere to an opensource standard where all "voices" are considered. @jerryneedell I cannot express my gratitude for your generous testing. Your voice is welcome.

2bndy5 commented 3 years ago

@jerryneedell I wrapped the PA/LNA module with electrical tape and then foil around that (for shielding). Then I wired up a PA/LNA module with a 3V regulator (L4931 with a 2.2 µF capacitor between Vout & GND) using my ItsyBitsy M4 5V (AKA USB) pin (going directly to the L4931 Vin pin). The following are experiences from running simple, ack, & stream tests with a reliable nRF24L01+ (no PA/LNA) on the other end (driven by a RPi2):

Ordered by different pa_level settings

Side notes

Conclusion (finally)

The PA/LNA modules seem to require quite a bit more power to transmit. The L4931 regulator that I used in the tests boasts a 300 mA current limit and a typical current of 250 mA. While the ItsyBitsy M4 boasts a 500 mA max, it would seem that much of that is consumed internally. Since playing with the pa_level is a current saving hack (as noted in the datasheet), I can only imagine that a higher power 3V regulator may enable sending transmissions from PA/LNA modules (including ACK packets -- with or without ACK payloads attached) for higher pa_level settings. More testing is called for, but I don't have an oscilloscope ( ☹️ ) to measure the peak current draws.

I'll make the examples instigate -12 dBm pa_level for better exhibition (usually example scripts are run on modules in close proximity anyway). The TMRh20 Arduino library's examples also do this while that library's default setting (upon instantiation) is also 0 dBm.

jerryneedell commented 3 years ago

@2bndy5 Interesting results. Thanks for all the examples. The PA/LNA units sure are complex! I won't be able to do any more testing until this weekend, but will try to do some when I can.

2bndy5 commented 3 years ago

Today, using the L4931 regulator still wired up, I swapped out the shielded PA/LNA module with an un-shielded (bare) PA/LNA module (purchased from the same stock -- if that means anything) and ran the simple_test on all available pa_level settings. It failed to transmit anything (again); it could receive, but ACK packets did not get through (again). So, it would seem that shielding is equally important to higher power sourcing.

jerryneedell commented 3 years ago

Back home and trying a few more tests. The 3 BLE tests all worked well with my iPhone. The "Physical Web" App see the URL beacon. Thank you for getting this working!

Still having issues transmitting with the PA/LNA modules. I have not tried any alternative ways of powering it yet. 2 nRF24L01+ modules working well the stream test.

Will try to explore more of the examples later today and tomorrow.

2bndy5 commented 3 years ago

@jerryneedell that's great! I'll probably modify the BLE example to alternate advertisements between the different "PDU types" (basically that's what it boils down to). I also modified the context test to demonstrate how to use 1 nRF24L01 as a BLE device and a normal RF24 device in the same script. With your iPhone confirmation, I'm ready to release, but I want to give it another proofread and make sure it passes the all the tests again (you know -- "for good measure").

Heads up, I'll be changing the default setting of the to_iphone attribute to False.