nRF24 / CircuitPython_nRF24L01

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

Ultra slow speeds #24

Closed Martincic closed 3 years ago

Martincic commented 3 years ago

Problem

I am aware of nrf240L1+ module is capable of 2mb/s speeds and I pretty sure both of my raspberry pi's on each end can handle them, but the actual transfer time is 40 minutes to send a 2.6Mb file. Could this be faster in some way, what am I missing?

How

I'm trying to stream an image from one NRF240L+ module to another and the current setup is only a couple of meters apart pointing directly at each other. I am using your example (nrf24l01_stream_test) which I've modified in the following manner:

This is working with some data loss and image disruption which is precisely what I need. I tried manually setting the speed to 2Mb/s and turning off auto_ack but it had no noticeable effect.

Transmission log

Code

make_buffers()

def make_buffers(size=32):
    """return a list of payloads"""
    # we'll use `size` for the number of payloads in the list and the
    # payloads' length
    buffers = []
    with open("coal.jpeg", "rb") as image:
      f = image.read()     
      b = bytearray(binascii.hexlify(f))

    counter = 0
    while counter < sys.getsizeof(b):
        counter += 32
        if not counter > sys.getsizeof(b):
            buffers.append(b[counter:counter+32])
    return buffers

slave()

def slave(timeout=5):
    """Stops listening after a `timeout` with no response"""
    bytes = b''
    nrf.listen = True  # put radio into RX mode and power up
    count = 0  # keep track of the number of received payloads
    start_timer = time.monotonic()  # start timer
    while time.monotonic() < start_timer + timeout:
        if nrf.available():
            count += 1
            # retreive the received packet's payload
            buffer = nrf.read()  # clears flags & empties RX FIFO
            print("Received: {} - {}".format(buffer, count))
            bytes += buffer
            start_timer = time.monotonic()  # reset timer on every RX payload

    # recommended behavior is to keep in TX mode while idle
    nrf.listen = False  # put the nRF24L01 is in TX mode
    print(bytes[0:64])
    f = open("image.jpeg", 'wb')
    f.write(binascii.unhexlify(bytes))
    f.close()
2bndy5 commented 3 years ago
  1. What version of the library are you using? v2.00 or v1.3.0 had significant speed ups implemented.
  2. master_fifo() had much better performance on a RPi compared to master() in the streaming _test.py
  3. SPI implementation on the RPI is noticeably slow. At this point, I can't think of another way to speed up the library without re-writting adafruit's PureIO libary in C++ (not an easy task).

BTW, data_rate is in bits per second, not bytes per seconds. Turning off auto-ack will most definitely cause data loss, especially for that much data being streamed so quickly.

2bndy5 commented 3 years ago

I can tell by the code you posted, you're using the speed boost included with v2.0.0. I'm sorry, but if you're looking for speed, then python isn't your best friend. Try using TMRh20's RF24 library, which is much faster because its written in C++ (comes also with a python wrapper that is just as fast as C++).

I recently (& coincidentally) made a refactoring change (on my dev branch) that would allow passing in whatever spi/csn/ce_pin objects you want as long as there's a new HWMixin class that can take exactly the objects you passed.

In short, I think this library is starting to outgrow just circuitpython. In the future I may be able to support the popular ~pigpio,~ RPi.GPIO + spidev, etc... (not to mention more native support for MicroPython). This also opens up an opportunity to offer NerdRalph's 3 and 4 pin solutions.

2bndy5 commented 3 years ago

update from my dev branch testing: I've been testing the library on RPi2 and RPi4 using spidev (written in C; wrapped into python) instead of Adafruit's busio for SPI implementation. Consequently, I've started using GPIO8 for the CSN pin (on the default SPI bus) because its faster to let the spidev kernel manage that.

These results are from a modified version of master() from stream_test.py. (on 1Mbps data_rate)

Test result sending a large-ish (1.24 MB) file from RPi4 to RPi2:

1307379 bytes split into 40857 payloads Transmission took 56.005716284999835 s successfully sent 100.0% (40857/40857) of LogoLarge.xcf

Mirrored test sending the same file to RPi4 from RPi2:

1307379 bytes split into 40857 payloads Transmission took 55.499714189999395 s successfully sent 100.0% (40857/40857) of LogoLarge.xcf

I have made a few small code changes that should speed things up, but using spidev seems to be the best speed-up. I doubt it would take more than 5 minutes to transfer a 2.5 MB file now 🎉 (upon next release). Also, I have not verified data is un-corrupted on RX side.