nRF24 / CircuitPython_nRF24L01

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

MemoryError: memory allocation failed on ATSAMD21 M0 boards #10

Closed markt22 closed 4 years ago

markt22 commented 4 years ago

Auto-reload is on. Simply save files over USB to run them or enter REPL to disable.

Press any key to enter the REPL. Use CTRL-D to reload. Adafruit CircuitPython 4.1.2 on 2019-12-18; Adafruit ItsyBitsy M0 Express with samd21g18

from circuitpython_nrf24l01 import RF24 Traceback (most recent call last): File "", line 1, in File "circuitpython_nrf24l01/init.py", line 3, in File "circuitpython_nrf24l01/rf24.py", line 48, in File "circuitpython_nrf24l01/rf24.py", line 1239, in RF24 MemoryError: memory allocation failed, allocating 376 bytes

SeeTheBridges commented 4 years ago

Alright, after banging my head against the wall all day trying to get this library working on my Trinket M0, I rolled back version 1.1.0. I get similar isses as you in my MU terminal, but after rolling back, I FINALLY have at least a functioning example program giving me SOMETHING in the terminal that isnt errors. Not sure entirely what changed between the 2 versions, but something is wrong with SAMD21 support

EDIT: After another hour of troubleshooting, I've run into a new error finally! I noticed that the init.mpy file had 3kb size which I thought was odd. Pretty sure somewhere in this repo it said that file needed to be there and it needed to be empty. So I stole the one from the adafruit bus dependency. Now it's just spitting out:

main.py output:
Traceback (most recent call last):
  File "main.py", line 11, in <module>
RuntimeError: Corrupt .mpy file

Press any key to enter the REPL. Use CTRL-D to reload.
SeeTheBridges commented 4 years ago

More info in case it's useful to anyone: Ran the example sketch with the cross compatibility to arduino. listed out the available memory and was given this in response:

main.py output:
gc 19.6094
time 19.5469
struct 19.5
board 19.4375
dio 19.3906
Traceback (most recent call last):
  File "main.py", line 16, in <module>
  File "circuitpython_nrf24l01/rf24.py", line 48, in <module>
  File "circuitpython_nrf24l01/rf24.py", line 1239, in RF24
MemoryError: memory allocation failed, allocating 376 bytes

So theres PLENTY of memory left.

2bndy5 commented 4 years ago

So sorry for the delayed response (i didn't get notified for some reason) To be honest this library is just too large to be running on an M0. I would recommend using an M0 express because it includes an extra 2 MB storage via an SPI flash IC chip. I recently gave up on trying to back-port this to the esp8266 for the same memory allocation error. I am open to the idea of a lite version of the rf24 module, but it would mean sacrificing some features (maybe a bare-bones version that's compatible with TMRh20's arduino library defaults)

2bndy5 commented 4 years ago

Coincidentally, I've ordered some M0 boards (express and trinket), so maybe I'll get around to exploring this

2bndy5 commented 4 years ago

I started a ~new branch called lite-beta~ in which I migrated most of the extra descriptive docstrings out of rf24.py (now lives in api.rst) and removed all optional keyword arguments from __init__(). This reduced the files size from 84kB to ~41kB (from 1500 lines to 855 lines), but be aware that the examples needed to be modified as well because the modifications to __init__() and send(). I also implemented recursion on send() when passing a list or tuple of payloads. Keep in mind this branch includes all the latest changes to master since the last release, so I encourage you to ~read the docs for this branch~. Testing this branch is pending the shipment of M0 boards that I recently ordered (from mouser.com)

@SeeTheBridges __init__.py is not a blank file and contains some code for backward compatibility with the import statement for version 1.0 of this library. That said, it will remain like that until I start adding more modules (like fake_ble.py or logitech_mouse.py). It is easier to have the user just import directly from the packaged module instead of relying on the __all__ list in __init__.py.

# for v1.0
from circuitpython_nrf24l01 import RF24

# for v1.1 and up
from circuitpython_nrf24l01.rf24 import RF24

Note

you could use a blank __init__.py file if you use the later import statement which is not the case in the examples (currently). Copying a __init__.py file from another library is NOT RECOMMENDED as I don't know what is in those files for other libraries. Circuitpython does support using a __init__.py with a [module_name].mpy file in the same folder (last time I tried during the release of v1.0). *.mpy files must be built for the version of circuitpython that your board's firmware was built for.

EDIT links removed as development noted here is now released as of v1.2.0

2bndy5 commented 4 years ago

original revisions (on ~lite-beta branch~) of rf24.py didn't work on M0 boards (tested mpy versions too).

working solution

So I created a file specifically for the M0 called rf24_m0.py -- took out all the comments (including docstrings, so help() won't actually have anything to print about the RF24 object), took out all the constants, removed the deprecated read_ack() in favor of using the synonymous recv(), removed __enter__() and __exit__() (meaning no more with statement advantage for different configurations), and cut down timeout calculations in resend() & send() under presumption that timeout should be maximum per spec sheet. Then I compiled the rf24_m0.py into a rf24_m0.mpy (using circuitpython v5), and the resulting file size is 13.7KB (totaling 494 lines). Using a Feather M0 Express, gc.mem_free() shows about 5-6KB free after instantiating the object for a nRF24L01 (about 2KB on the Trinket M0). I know it's not a lot, but if you are mindful of your import statements (during my testing, importing a package vs importing a package's module had varying affects on available memory), there's still opportunity to do things (like use the usb_hid library on a Trinket M0).

EDIT links removed as development noted here is now released as of v1.2.0

2bndy5 commented 4 years ago

during additional testing, I kept running into "Memory Errors" and/or unresponsive freezing situations.

My findings

Of the 32KB of RAM on the M0, the user code space is limited to about 20KB (after import gc), and rf24_lite.py (the "lite" version with all possible features cut out -- doc link for the ~lite-beta branch~) takes about 10KB (after instantiation using the compiled rf24_lite.mpy bytecode). This leaves too little space for user application code which makes CircuitPython an unreliable solution for deploying nRF24L01 applications with the ATSAMD21 M0.

Conclusion

Use the Arduino IDE (or platformIO) to program the M0 in C++. Besides application speed being slightly faster, you don't have to contend with CircuitPython's "OS"/firmware hogging up a sizable chunk of RAM. I personally recommend TMRh20's Arduino library for interfacing with the nRF24L01. I'm sorry if this is not the result you were hoping to get. Due to CircuitPython's occupation in RAM, this issue cannot be addressed/fixed, and I have to close it out.

EDIT some links removed as development noted here is now released as of v1.2.0

2bndy5 commented 3 years ago

@markt22 @SeeTheBridges You can try out the new rf24_lite.mpy module found in the new release. I have tested it on my Feather M0 Express, but be aware that some attributes/features are not available in the lite version. Any user-space code running on the ATSAMD21 must be rather short & simple (due to how much RAM that the CircuitPython firmware occupies). I'm calling this issue closed, but I'm keeping it pinned since I highly recommend using the Arduino-based TMRh20 RF24 library for any complex applications (it is OTA compatible with this library anyway).