nRF24 / CircuitPython_nRF24L01

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

networking example discussion #35

Closed folksman closed 2 years ago

folksman commented 2 years ago

HI, i tested almost all examples and they working great except the nrf24l01_network_test.py. i started 1 node as master by default settings and the 2nd i do Y > node '1' and on try connecting Mesh Network it always fail. The master node is acting as master node so ther should be no problem. Any fast help would be great!

2bndy5 commented 2 years ago

Is the master node driven by c++ or by circuitpython?

I had trouble getting consistent results with mesh networking. There are commented out lines that I use to debug the problem, but calling print() while the mesh network layer is executing tends to slow things down significantly.

folksman commented 2 years ago

i use circuitpython on device. BTW i tested now as well Network mode and i see that only send 2-3 messages ok then it failed. this is on both sides

folksman commented 2 years ago

complete using circuitpython example

folksman commented 2 years ago

oh forget the issue on Network mode. looks like it was my mistake. i must idle on 1 node, correct? ohterwise the sender sends just 2-3 message and the failed But mesh issue still there :(

2bndy5 commented 2 years ago

Yes all nodes in the network/mesh (including master) must be idling if they are not emitting. The first 2 or 3 messages might be acknowledged by the radio itself, but the MCU must call network.update() (or mesh.update()) to keep the radio's RX FIFO unoccupied.

The mesh connecting process is very sensitive to timing because it doesn't really use the radio's auto_ack feature. What hardware are you using? RPi is somewhat faster than MCUs driven by circuitpython firmware.

folksman commented 2 years ago

Yeah network works fine as i see - no problems so far :)

Mesh: I use a Adafruit Express father board and a Adafruit RP2040 father board. both drive with actual firmware and your lib by simple use your nrf24l01_network_test.py example. I really don´t know why just only this example with mesh not working :/

folksman commented 2 years ago

ohhhh please excuse my issue. i found out what happened. on mesh after create the master i must start as well idle to get it working. now all works fine :))

folksman commented 2 years ago

btw: impressive fast reply from you! love this kind of help

2bndy5 commented 2 years ago

When testing this mesh layer, I mainly used a RPi as mesh master and my feather RP2040 board as a child node. I should note that it was better when there was already a mesh node connected to the mesh master because the middle-man node would allow auto_ack to be used for msg forwarding.

It's 7am here so, I'm going to try and get some sleep, but I'll spin up some MCUs to test when I'm fully awake.

folksman commented 2 years ago

i found already the problem as i mentioned. i not started idle before connect a child node.. now it works well :) Only 1 question more.. how i can get the RX open always to not have any timeout for idle? in my project i not want interact with console

2bndy5 commented 2 years ago

Oops. Didn't see that post while I was writing mine. Good to hear you got it working! I suppose I should add something to the example prompts to make it clear that idle() must be called... If you have any suggestions, I'd be very thankful.

2bndy5 commented 2 years ago

how i can get the RX open always to not have any timeout for idle? in my project i not want interact with console

while True:
    idle()

There are other ways to do it, but this was the first idea that came to mind. The importance of using idle() is the network.update() call. So, you could do

while True:
    network.update()

As long as you don't want to emit() anything from that node.

folksman commented 2 years ago

Only 1 question more.. how i can get the RX open always to not have any timeout for idle? in my project i not want interact with console

2bndy5 commented 2 years ago

You could pass idle() an enormous number... idle(5000) will put the node in idle for 5000 seconds.

2bndy5 commented 2 years ago

I use timeouts in the examples because a keyboard interrupt (like ctrl + c) can leave the SPI bus in a locked state (which would then require a hard reset to unlock).

folksman commented 2 years ago

not easy to understand. lets say i have a master node which always want receive messages without any timeout or initiation to listen. the other node as example send a button command to the master. how i can then make that without set any timeout for RX?

folksman commented 2 years ago

oh i see your reply bit too late. you already answered my question. Does it mean it can´t send and receive dual in same time ?

2bndy5 commented 2 years ago

Does it mean it can´t send and receive dual in same time ?

It all depends on how you write your application. Using the examples: no, because they were designed to show 2 basic roles (emit and idle) separately. In practice, you could write an app that uses these roles however you want. Just remember that the network layers (including mesh) need to be updated regularly (using network.update()). So, you can send() whenever you need to, and the message received (after calling network.update()) can be acted upon accordingly.

folksman commented 2 years ago

ok understand. how many nodes can be handle into the Mesh Network with 1 master?

2bndy5 commented 2 years ago

I've written some very in-depth docs, but the info is rather spread out.

how many nodes can be handle into the Mesh Network with 1 master?

Answer is found in docs' topology page. TL;DR - "A single network can potentially have a maximum of 781 nodes (all operating on the same channel)"

folksman commented 2 years ago

oh yes ..i see. sorry!

Since i have as well a Esp32 (which not support circuitpython BUT micropython), can this libs (py code) also used in Micropython of is there a MESH compatible version somewhere available?

2bndy5 commented 2 years ago

It takes some doing, but this lib should work with microPython if you can get all the files into the ESP32 file system (in a lib folder preferably located at the root of the file system). I haven't tested this though because I don't know a good way of transferring multiple files (using a mandatory directory structure) into my ESP32 (tiny Pico board). As far as I know, there is no microPython equivalent lib that can do everything this lib does. Let me know if you attempt this; I can offer guidance on differences with instantiating the radio object in microPython (the examples won't work in microPython as they are currently written).

There are ESP32-S2 boards that run CircuitPython. I have my eye on Adafruit's new QtPy ESP32-S2 (which sells for $10 American). 😍

folksman commented 2 years ago

using my M5 Atom Esp32 and Thonny Editor its easy to create lib folder and upload multiple files. It would be great if you can help me with Micropython. It would be really nice if we can get this running in mesh too. That opens a lot new possiblities then. As example using a websocket server interacting with ;)

The QtPy ESP32-S2 looks promising but i have a lot of the mentioned devices which i would be use to.

2bndy5 commented 2 years ago

ok, I'd just like to remind you: you're in untested territory here.

  1. Replace from digitalio import DigitalInOut with
    from circuitpython_nrf24l01.wrapper import DigitalInOut

    Now you can declare the CE & CSN pin using the pin number like you would when using machine.Pin.

    # change these (digital output) pins accordingly
    CE_PIN = DigitalInOut(4)
    CSN_PIN = DigitalInOut(5)
  2. replace SPI_BUS = busio.SPI() with

    SPI_BUS = machine.SPI(0)  # could pass `1` if you're using a secondary HW SPI bus

    Keep in mind that this line will also require import machine at the top of the script.

    • Despite what the microPython docs say about specifying the baudrate here, you should actually specify the baudrate in the constructor for the radio object (only if you need to change it from the default value)
  3. you should be able to declare the radio obj like normal now:

    nrf = Network(SPI_BUS, CSN_PIN, CE_PIN, THIS_NODE)
    
    # or if you need to change the baudrate
    baudrate = 4000000  # a safe default value for troubleshooting
    nrf = Network(SPI_BUS, CSN_PIN, CE_PIN, THIS_NODE, spi_frequency=baudrate)

All together now

import time
import struct
import machine
from circuitpython_nrf24l01.wrapper import DigitalInOut
from circuitpython_nrf24l01.network.constants import MAX_FRAG_SIZE, NETWORK_DEFAULT_ADDR

IS_MESH = (
    input(
        "    nrf24l01_network_test example\n"
        "Would you like to run as a mesh network node (y/n)? Defaults to 'Y' "
    ) or "Y"
).upper().startswith("Y")

# to use different addresses on a set of radios, we need a variable to
# uniquely identify which address this radio will use
THIS_NODE = 0
print(
    "Remember, the master node always uses `0` as the node_address and node_id."
    "\nWhich node is this? Enter",
    end=" ",
)
if IS_MESH:
    # node_id must be less than 255
    THIS_NODE = int(input("a unique int. Defaults to '0' ") or "0") & 0xFF
else:
    # logical node_address is in octal
    THIS_NODE = int(input("an octal int. Defaults to '0' ") or "0", 8)

if IS_MESH:
    if THIS_NODE:  # if this is not a mesh network master node
        from circuitpython_nrf24l01.rf24_mesh import RF24MeshNoMaster as Network
    else:
        from circuitpython_nrf24l01.rf24_mesh import RF24Mesh as Network
    print("Using RF24Mesh{} class".format("" if not THIS_NODE else "NoMaster"))
else:
    from circuitpython_nrf24l01.rf24_network import RF24Network as Network

    # we need to construct frame headers for RF24Network.send()
    from circuitpython_nrf24l01.network.structs import RF24NetworkHeader

    # we need to construct entire frames for RF24Network.write() (not for this example)
    # from circuitpython_nrf24l01.network.structs import RF24NetworkFrame
    print("Using RF24Network class")

# change these (digital output) pins accordingly
CE_PIN = DigitalInOut(4)
CSN_PIN = DigitalInOut(5)

SPI_BUS = machine.SPI(0)  # could pass `1` if you're using a secondary HW SPI bus

# initialize this node as the network
nrf = Network(SPI_BUS, CSN_PIN, CE_PIN, THIS_NODE)

# the rest of the example remains the same
2bndy5 commented 2 years ago

I tried using Thonny to get the lib onto my tinyPico... Thonny only allows installing python libs from pypi to the microPython board's file system (using a tool called minipip). Unfortunately, this does not help when trying to test an unreleased development version of the lib ☹️ . Digging deeper, I found rshell now supports recursive copying of files to the microPython file system 😄 .

I tried running the code I suggested above on my tinyPico with the current release of this lib copied to the microPython file system. Unfortunately, I got a SyntaxError on line 221 of mixins.py. https://github.com/nRF24/CircuitPython_nRF24L01/blob/c800869415275f960f97605c28529f9fa5e679b9/circuitpython_nrf24l01/network/mixins.py#L221

I'm guessing the latest version of microPython (v1.17) does not fully support f-strings; v1.17 release notes say:

This release of MicroPython adds support for f-strings (PEP-498), with a few limitations compared to normal Python. F-strings are essentially syntactic sugar for "".format() and make formatting strings a lot more convenient.

So, I'm going to start changing the lib to not use f-strings and try again using rshell... @folksman Any success on your end? You're the first one to provide feedback since I released the networking features, so I'm eager to hear about your experience.

2bndy5 commented 2 years ago

It looks like I'll have to write a wrapper for micropython's utime module as it is not compatible with CPython's and CircuitPython's time module... TBH, MicroPython support seems like more trouble than its worth; I generally don't like MicroPython because it tries too hard to support processors with very little memory or no native USB support.

I already had to ignore support for CircuitPython on ATSAMD21 based boards due to not enough memory, and that was before I started porting the C++ network libs.

@folksman You might be better off using TMRh20's RF24Mesh lib (written in C++ for arduino like boards) on the ESP32 boards. This python lib was written to be OTA compatible with nodes running the original C++ libs for the nrf24l01.

folksman commented 2 years ago

hi! i tested today but got this:

MPY: soft reboot nrf24l01_network_test example Would you like to run as a mesh network node (y/n)? Defaults to 'Y' Remember, the master node always uses 0 as the node_address and node_id. Which node is this? Enter a unique int. Defaults to '0' Traceback (most recent call last): File "boot.py", line 33, in File "circuitpython_nrf24l01/rf24_mesh.py", line 49, in File "circuitpython_nrf24l01/network/mixins.py", line 221 SyntaxError: invalid syntax MicroPython v1.17 on 2021-09-02; ESP32 module with ESP32

folksman commented 2 years ago

Are you still work on it?

2bndy5 commented 2 years ago

The error that you're seeming is the first of many I found when trying to get this lib to work on ESP32. At the end of the day, microPython is too different from normal CPython. At least CircuitPython tries to be compatible with CPython... You'll have to use C++ on the ESP32 boards.

  1. C++ is faster than any Python.
  2. The RF24, RF24Network, & RF24Mesh libs (written in C++) can support a wider variety of boards.

Unfortunately, this lib's API has to be a little different from the C++ API because of differences in C++ vs Python syntax/features.

I'll probably just drop microPython support since I'd have to write wrappers for the entire microPython API (which could change for different boards or future versions).

folksman commented 2 years ago

ok i see. very harm but may usage of other boards (as you mentioned) is may a option. Well, thank you for the great help! During this time i also got running the C++ samples on my boards and its working well with your library :) Its not so nice as work with python but should be good for my usage too.