#ESP-NOW
1 messages · Page 1 of 1 (latest)
Figured it was probably better to stop capitalising the channel etc.
Are you finding it possible to run both ESPNOW and WiFi at the same time? Last timed I tried it was a buggy mess.
yes, wifi can be any channel, but still espnow only works on channel 1 afaict
I wrap any wifi activity in wifi.radio.enabled = False|True to release the channel used by wifi so that espnow can use the default
Ah, so you're roughly doing what I am, but without the deinit?
I'm tracking some things here that may need to become issues https://gist.github.com/anecdata/f46a1d07add5fc60cfbcf42dc7be6528?permalink_comment_id=4520305#gistcomment-4520305
well in the example code I posted, the sender uses with so implicit deinit
wifi can't be deinit-ed, but enabled seems to do enough
I assume that means a full wifi reconnect etc?
Yeah, I remember it working but as above, it was a hot mess
in theory, wifi and espnow should be able to be used simultaneously, as long as the channel is the same for all
in theory
so, for now, channel 1
It'd be nicest if you could use them both properly simultaneously, though I think for most use cases this should be fine, though I am now low-key going "..there is an Ethernet Featherwing"
or some kind of Adafruit Centipede combo of two QT Pys smushed together
hm, nope... espidf.IDFError: ESP-NOW error 0x306a unless I'm doing something wrong (using channel 1 for wifi and espnow, but not de-init-ing espnow)
well, it's a bug I'm pretty sure, so if we file an issue...
maybe try deiniting it and see if that works?
now all I can think of is this https://www.youtube.com/watch?v=ozezG1zpxXQ
though am wondering again about if an IO relay could be a library
if I deinit espnow (or use with), then it works regardless of whether wifi channel is same or different
oh wait, forgot to disable the enabled = False...
I figure the battery life improvements for a lot of projects mean that even if you have to buy a QT Py and leave it plugged in, it's going to be cheaper and probably a bigger improvement than buying a bigger battery pack.
something still quirky if I don't enabled = False when done with wifi
like it only gets some of the packets and seems to get laggy and weird?
it just doesn't work, I'll keep playing, maybe I have something wrong in my code still
I'm trying to remember what I had that slightly worked, but badly
it hangs, no control-C, so that's an issue
yeah, that sounds about right
oh and there's a hard crash i should check if it's still there
the best combo I've found is: use channel 1 (for now) for espnow, wifi can be any channel but you have to reconnect and disconnect around any wifi activity (wifi.radio.enabled = False)
oh, I haven't sen a hardfault
yeah that's what I'm doing, plus deinit/reinit
I think it was encrypted = False that used to do it, probably got fixed when it got turned into encrypted
oh, it didn't hang permanently, just hung for a while in requests
so yeah, there are some limitations that should make it into issues filed
but if you tiptoe, it works
Agree it'd be good to nail down how the LMK stuff is meant to work, though I'm back to wondering if we could make the MAC address harvesting/encryption key creation bit less unpleasant
I spent a silly amount of time trying to get it to accept MAC addresses formatted in the way they're formatted everywhere else
I was thinking about some wifi-accessible way for the espnow devices to know their peers... some server or file
there's a broadcast, haven't tried that at all
not even sure if it's implemented in CP
Wondering if there could be initial setup code you'd run on your devices that'd get them to basically wireless pair to each other, like ESPNOW WPS
nope, the dreaded 0x3066 if I make the peer address FF:FF:FF:FF:FF:FF
oh, that's a different eror code
I wonder if the power saving mode is why it gets buggy: https://micropython-glenn20.readthedocs.io/en/latest/library/espnow.html#espnow-and-wifi-operation
btw, to your earlier comment... yes various networking wings can be added 😉 https://gist.github.com/anecdata/8152eba03eefd0a587747e551c683543?permalink_comment_id=3982278#gistcomment-3982278
Hmm, I'm getting the memory error again just after more runs
I can try the toml thing but I'm wondering if my code is just bad or something?
sometimes it’s recoverable, you could wrap the offending code chunk in a try / except
the espidf module has some functions for keeping track of espidf memory use
Hmm, the toml thing didn't work
not sure why... did it last longer?
you could post your code here or in the main channel and ask for suggestions (I'm not necessarily the best one to review for that)
what's your board... how ,much PSRAM?
S2 TFT
2MB of PSRAM, was going to order another S2 board and am now wondering if I should get the UM one which has 8MB of it
@outer plover why not two boards
One receive from espnow
Then tx rx to another and then another sends mqtt message
@outer plover just tried your espnow_sender/receiver and they are working well. Now I will see if I can get it to relay received espnow packets to AIO. I won't be able to work on that until later today. Thanks for the example code.
I am trying to do something very similar to @rare glade
Two boards is what we’re doing this based on, though your message also sounds like you’re saying 3 boards?
Can give you my code for that though I’m hitting a memory issue if I don’t just soft reset to handle switching between them
I am running my own code, but I do intermittently get Memory errors ```Sending to Adafruit IO...
Traceback (most recent call last):
File "adafruit_requests.py", line 534, in _get_socket
espidf.MemoryError:
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "espnow_aio.py", line 46, in do_wifi
File "adafruit_io/adafruit_io.py", line 596, in send_data
File "adafruit_io/adafruit_io.py", line 541, in _post
File "adafruit_requests.py", line 732, in post
File "adafruit_requests.py", line 668, in request
File "adafruit_requests.py", line 515, in _get_socket
RuntimeError: Sending request failed
yeah that's the kind of error I got
What's interesting to me so far is that I can't actually see any espidf PSRAM getting used as it functions so far, still waiting to see it use a single byte
FWIW, this seems to handle the errors ok and lets me keep going ```
wifi.radio.enabled = True
connect()
# Initialize an Adafruit IO HTTP API object
io = IO_HTTP(aio_username, aio_key, requests)
try:
print("Sending to Adafruit IO...")
io.send_data(test_feed["key"], packet.msg)
except Exception as ex:
traceback.print_exception(ex, ex, ex.traceback)
wifi.radio.enabled = False # lose the wifi channel
the usage of the traceback and connect() are from the examples by @outer plover
This is running well with occasional memory errors (once in 10 or 20 tries)
Think I've ended up with something similar, partly just because I was getting random WiFi errors
Though currently running a general memory test to see how it works out over time and it seems like it just uses more and more with each run
I'm trying to come up with a way to open an issue for this, but I need to spend more time creating a clear example. I don't have much more time today, but will try again tomorrow. In any case, this has been great progress.
You are welcome to open an issue if you want and I can just add comments to it.
Ahh, on a longer run it seems to be sorting itself out and memory comes back
Was confused because I was manually GCing and it didn't seem to make any difference
it's hard to know where to collect and where to measure memory
ideally, heap should stay constant through each loop, as should espidf largest free and the others
can someone clarify exactly what the if e: thing in the loop does? is it "if we get an ESPNOW packet"?
am getting some weird behaviour and I'm wondering if the deinit maybe isn't working
e has a boolean property that's True if there is a non-zero length of packets https://docs.circuitpython.org/en/latest/shared-bindings/espnow/index.html#espnow.ESPNow.__bool__
I added print("if e loop triggered") to the start of the if e: part, and after it's meant to have deinited ESPNOW, while it's connecting to WiFi connecting to WiFi, I get "if e loop triggered" printed
implemented that way specifically to have an easy way to know if there';s somthing to read
there could be something left in the buffer unread??
or some code logic that's off
e.deinit() is only if there's legit T & H... is that always true? the logic could wrong potentially
It's meant to keep listening until it gets both T&H, then deinit and switch over to WiFi, did I mess up it doing that?
I was just thinking that there is a potential (though unlikely) path where if temperature is not None and humidity is not None: isn't true and it doesn't deinit, but I think the block before that ensures that will never be false so do you need the second if?
oh n/m, I see, separate sends
yeah, I thought I wrote it so that if they aren't both not None, it won't move onto doing WiFi anyway
do you always want to block when trying to read a packet? (I think that's easiest, unless you're after something different)
I don't know what that means heh
maybe put the ESPNow stuff in a function, something likepy def ESPNow_Receive(): with espnow.ESPNow() as e: e.set_pmk(PMK) peer = espnow.Peer(mac=node1_MAC, lmk=LMK, encrypted=True) e.peers.append(peer) while True: if e: packet = e.read() break return packet then process and error-check the packet (the with does init and deinit implicitly)
(you wouldn't need the ESPNow setup at the top of code)
getting a bit confused as to how to refactor it this way
I want it to wait until the data has come in and then deinit espnow and switch over to wifi
not sure why that doesn't seem to be happening in practice
unless the if e thing basically overrides the deinit?
if e: only works when ESPNow has been (re)init'ed, and will only enter that loop if there are packet(s)
I'm not seeing why the code doesn't do what you want
as in, my code should work and not get interrupted by new packets?
yeah, you deinit espnow before doing wifi, then reinit espnow and back at the top of loop check for packet(s)
and it definitely is a new packet that's triggering it
I don't think I used to have this issue, but I do now I've pulled the WiFi and IO stuff into functions
can you post the few lines of print output that show the issue?
I added print(packet) just under the deinit
Starting ESPIDF PSRAM: 253952
Starting CP RAM 1733408
if e loop triggered
Free RAM: 99.93%
if e loop triggered
Free RAM: 99.91%
time to deinit ESPNOW
ESPNowPacket(mac=b'\x84\xf7\x03\xf6X\xe8', msg=b'Humidity: 54', rssi=0, time=10732003)
1st time Connected to the WiFi network
Trying to send to IO
if e loop triggered
Free RAM: 95.61%
time to deinit ESPNOW
ESPNowPacket(mac=b'\x84\xf7\x03\xf6X\xe8', msg=b'Temp: 21.0234', rssi=0, time=10742005)
1st time Connected to the WiFi network
Trying to send to IO
if e loop triggered
Free RAM: 91.30%
time to deinit ESPNOW
ESPNowPacket(mac=b'\x84\xf7\x03\xf6X\xe8', msg=b'Temp: 21.0234', rssi=0, time=10752008)```
it's like ESPNOW is crashing through the WiFi wall like the Kool-Aid man
it's like humidity isn't getting set to None, but I don't see how
(and missing packets maybe)
OH....
humidity and temperature are local variables in the function
I reverted the code to a previous version and WiFi is at least working again (NFI why), but the deinit still isn't actually working
you can read variables outside the function, but if you want to write them, you have to declare them as global inside the function
oh, n/m, they are params to the function
d'oh
it's like I need to run this to see what's happening, but I don't have the sensor and IO set up
you don't need an actual sensor FWIW, the humidity value is just being sent fixed
and you could replace the IO sending with something else
NFI, will try to tidy my current code
code.py output:
if e start triggered
Temperature: 20.8984
Humidity: None
if e start triggered
Temperature: 20.8984
Humidity: 54
Got both values so deiniting ESPNOW
ESPNOW should be deinited, so no packets should be below
ESPNowPacket(mac=b'\x84\xf7\x03\xf6X\xe8', msg=b'Humidity: 54', rssi=0, time=815340)
Connected to the WiFi network
It's WiFi time. Connecting to WiFi
Temperature sent
Humidity sent
IO done
Disabling WiFi
Re-enabling ESPNOW
if e start triggered
Temperature: 20.9063
Humidity: 54
Got both values so deiniting ESPNOW
ESPNOW should be deinited, so no packets should be below
ESPNowPacket(mac=b'\x84\xf7\x03\xf6X\xe8', msg=b'Temp: 20.9063', rssi=0, time=835346)
Connected to the WiFi network
It's WiFi time. Connecting to WiFi
Temperature sent
Humidity sent
IO done
Disabling WiFi
Re-enabling ESPNOW```
could the packets be ones that aren't actually fresh new ones? like ones stuck in a buffer?
once you e.read, they should be gone (from ther espnow internal buffer)
but packet isn't cleared until you re-assign it
what if there are multiple packets not just 2?
ESPNOW seems to be a bit "spray and pray"
the example in readthedocs shows a way to handle multiple packets that are part of a group
Hmm, I guess my code isn't getting pulled back into the ESPNOW loop anymore
lol I just had to say that
packets can go missing too, espressif suggests returning an ACK packet and retransmitting if no ACK before moving on to the next send packet. or you could do like you're doing and check that all expected packets arrived
if 2 out of 3 arrive, discard and cross fingers for next time... I suppose it depends on interval, criticality, etc
Just realised what might have messed it up so much, I had "except Exception as e" in the fails. Right to assume that meant the code went "oh by e you mean ESPNOW right?"?
"this'll help me troubleshoot the errors!"
monkey paw closes
Any idea why I keep having to whack command + c a couple of times? is this a function of using functions?
no, just some things block a little in the core until it can get around to registering the control-C *typ. of network operations)
I beefed up the buffer size (to 4 packets) and the phy-rate (to 22Mbps). Multiple senders to one receiver works, with acknowledgement packets sent back from the receiver to the correct sender. Sender waits for ACK before sending again, with a timeout on the ACK receive at the sender so it doesn't wait forever for the ACK. All devices use same PMK. Each sender has it's own LMK with the receiver. Since we're single-threaded, timing can get tricky with a device sending and receiving.
esp-now node serial TX to Mqtt board
then Mqtt board send to mqtt
So 2 boards?
yes
but theoretically, you can run espnow and mqtt on the same board... if it isn't possible right now it's a bug
that also needs multithreading
certainly would make it easier, but you could devise a scheme where it's not required
I mean, anything can potentially block an espnow receiver, so getting around that is essential for many non-trivial applications
the buffer, I believe, can get filled in the background
read it when you get around to it
actually, I'm prety sure the buffer can fill in the background... it uses a callback in the IDF
easy to test
but isn't wifi and espnow use the same channel
you would need to disconnect wifi before that
there's no way around either using the same channel for espnow and wifi, or having espnow / wifi being unavailable when the device is on the other protocol on a different channel... ESP chips can only use one channel at a time, for anything
so my solution work the best
no need to disconnect + reinit
or just use the same channel
espnow can also be used with wifi as an AP, rather than connecting to an external AP
basically espnow opens its own ap?
no, wifi opens an AP, and espnow then runs independently but on the AP channel
so connect to router and it uses the router as a distributor
that's how it works if the espnow device is in Station mode rather than AP mode
but mqtt ?
yeah, wifi station w/mqtt + espnow on same channel
or use diff channels and count on the buffers, or coordinate time slots, or...
so connect to a access point and doing espnow the same time
yeah
wouldn't that miss some packets while wifi is doing its job
background buffering 🙂 depoends on the frequency and number of packets, size of buffers, etc.
a two board solution is better
plenty of options, so each can pick what's best for them... flexibility ftw 🙂
If I tidied up my sender/relay code and refactored it so you could fill in the relevant stuff from a settings toml, do you think anyone would find that useful?
Working examples are always useful!
Did we confirm/agree that ESPNOW and WiFi can't really happen at the same time without switching between them?
Though I am once again wishing Adafruit had made that ABLIC S-35710 breakout board they teased on their blog after the Eye on NPI about it. Getting deep sleep current down to less than 1μA would be so nice now.
wonders if he could just 'borrow' the image here to try to make his own https://blog.adafruit.com/2022/07/25/how-an-eye-on-npi-becomes-a-newnewnew-product-at-adafruit/
@rare glade Espressif says it should work. Coordinating channel is the tricky part. I can go back and test it, I've just setled on cleaner and more reliable if I deinit whatever I'm not using.
Yeah, same.
so... it's a bug if it doesn't work?
That's what I'm wondering. I ended up just deiniting it and turning wifi on and off between, are you doing the latter too?
yeah, I use ESP-NOW in two function (one for send, one for receive) with a with...:, and I end the wifi function by wifi.radio.enabled = False
btw, someone should enter issue(s) on the quirks we've found... all one issue? several? if you want to start, be my guest, or I'm happy to also
Not sure why but I'm weirdly nervous about filing issues heh
I guess probably a good chunk of "is this a bug or am I just doing it wrong?"
your call, the template steps you through the info to include
Is setting channels still broken?
I believe so
Was weird how I was definitely able to set channel 14 originally but then that broke at some point
I tested channels some days ago, haven't seen any PRs that would fix it
Has anyone tried with two node devices? If I remember correctly, you can/are meant to have a different LMK for each node?
yes, I have two senders and one receiver, each sender has a unique LMK it uses with the receiver. I think PMK has to be common among all.
(the receiver sends an acknowledgement to the appropriate sender, and the sender receives the acknowledgement, so they are all essentially sending and receiving)
Here's my relay code. I tried to get ESP-NOW into a function but kept hitting issues so shrug.
suddenly remembers the Ethernet Featherwing exists
Thanks for sharing your code. I won’t be able to do any more esp now testing for a few weeks as I’ll be away from my hardware…Good luck!
How about something like this? Didn't test since I don't have the sensors or IO set up, might have a couple bugs. espnow init-read-deinit is in its own function, wifi_enable-connect-send to IO-wifi_disable in its own function. Setup in its own function so it can be wrapped with enable and connect like the send to IO. Main loop is simple. Added tracebacks so you can see what exceptions are occurring.
At first glance I guess I'm a bit confused as to why the initial IO setup makes sense as a function, as it only needs to be called once?
no need really, it may as well be inline
I was thinking to some strange potential future where various wifi functions might be wrapped by enable-connect-do_stuff-disable, but not worth it for that
but keeping the enable-connect-do_wifi_stuff-disable wrapped around each wifi activity somewhere will help prevent things like being connected or not, or being enabled or not, when the code gets more complex with nested loops with break, or nested try/ except, etc
is it bad my brain broke reading this? 😉
lolnope I can be pretty obtuse
I guess I'm trying to balance efficiency like using functions to make a concise looking loop, with making it easy to follow and mess with for others.
my point was just that having the wifi enables and disables spread out, some in main loop and some in functions, could lead to unexpected side effects later... it's a lot to keep track of as you trace the possible code paths
cleaner to keep them where they're needed: enable, connect, do some wifi, disable.
anyway, that's a lot of words for a minor point o_O
ahh
Also, don't quite know why, but even after fixing line 69 (new line, nice), it's not moving on from Connected to the WiFi network
what's the sequence of messages until it stops?
Literally just:
Connected to the WiFi network```
like it's hanging in io.get_feed or something? what line does it control-C at?
time.sleep(15)
so it's looping but printing nothing... maybe not receiving for some reason... add more prints ein there to see what's not happening?
Where would you suggest I add them?
is this line needed? if packet.mac == node1_MAC: I think if channel and peer and encryption don't match, nothing will be received anyway
the joys of print-debugging, maybe...```py
def espnow_read():
temperature = None
humidity = None
with espnow.ESPNow() as e:
e.set_pmk(PMK)
peer1 = espnow.Peer(mac=node1_MAC, lmk=LMK1, encrypted=True)
e.peers.append(peer1)
print("DEBUG espnow is set up")
if e:
packet = e.read()
print("DEBUG e is truthy and packet was read")
# If the packet is from the MAC address of node 1 that we set earlier we decode the message, then check the message text and see if it contains "Temp:" or "Humidity:" which are what node 1 should be sending. If they do, it sets the variables we created earlier to those values.
if packet.mac == node1_MAC:
print("DEBUG packet is from node1")
msg_text = packet.msg.decode('utf-8')
for line in msg_text.split('\n'):
if 'Temp:' in line:
temperature = float(line.split('Temp: ')[1])
elif 'Humidity:' in line:
humidity = int(line.split('Humidity: ')[1])
print("Temperature:", temperature)
print("Humidity:", humidity)
return temperature, humidity
It's there so if you have multiple nodes sending data formatted the same way, it can tell them apart
sprinkles temperature and humidity sensors everywhere
lol, I have them all over too, but each is currently a FeatherS2 doing full wifi to a server
btw, what's the time.sleep(15) for anyway?
DEBUG espnow is set up
my mistake, probably can take out both time.sleep() from the main loop?
don't want to block the read
so it's not receiving at all for some reason
you're watching the serial of the sender node?
yeah, it's sending, my code works fine with it
are you checking the send/recv stats from espnow in the sender? print(f"send=[{e.send_success} {e.send_failure}] read=[{e.read_success} {e.read_failure}]")
if I just want to break out the ESP-NOW setup bit into a function, how do I get that to work? Just:
# Set up and start ESP-NOW and add the first node.
e = espnow.ESPNow()
e.set_pmk(PMK)
peer1 = espnow.Peer(mac=node1_MAC, lmk=LMK1, encrypted=True)
e.peers.append(peer1)```
No, though to be clear, I don't have any confirmation/acknowledgement code anywhere in this
ok for now, but those stats will tell you if the sender "thinks" it delivered or not
different than in the code I posted earlier?
oh, that, sorry...
send=[20 0] read=[0 0]```
etc.
sending code:
something like```py
def setup_espnow():
with espnow.ESPNow() as e:
e.set_pmk(PMK)
peer1 = espnow.Peer(mac=node1_MAC, lmk=LMK1, encrypted=True)
e.peers.append(peer1)
return e
Define a function to read espnow data
...could later split the basic read and the packet contents handling into separate functions...
...anticipating a common read for different nodes with different data...
def espnow_read():
temperature = None
humidity = None
e = setup_espnow()
print("DEBUG espnow is set up")
well send=[20 0] is good
hmm, should that work if I just add it to my original code and replace the two existing blocks with just calling setup_espnow()?
it should, but I'm working blind here... give me a minute, I'll see if I can set up with your code, but just not do the IO and fudge sensor data
oh, duh, that won't work because of the with context manager, it automatically deinits e when exits the function, lemme think...
getting rid of with and initing and deiniting in separate functions or loops is... error-prone
you could split off this part:```py
def setup_espnow(e):
e.set_pmk(PMK)
peer1 = espnow.Peer(mac=node1_MAC, lmk=LMK1, encrypted=True)
e.peers.append(peer1)
...snip...
def espnow_read():
temperature = None
humidity = None
with espnow.ESPNow() as e:
setup_espnow(e)
print("DEBUG espnow is set up")
if e:
(setting up with your code...)
missing a while True:, sorry my mistake
I swapped out my Connect, so you'll need yours back
and the constants at the top I fudged
but really, this is the main change:```py
def setup_espnow(e):
e.set_pmk(PMK)
peer1 = espnow.Peer(mac=node1_MAC, lmk=LMK1, encrypted=True)
e.peers.append(peer1)
print(f"mac={peer1.mac} lmk={peer1.lmk} ch={peer1.channel} if={peer1.interface} enc={peer1.encrypted}")
def espnow_read():
temperature = None
humidity = None
with espnow.ESPNow() as e:
setup_espnow(e)
while True:
if e:
packet = e.read()
(without the while True: it inits, quickly checks and odds are nothing is there at that instant, then deinits, so nothing is ever received)
Trying to remember if I ever tested any of this/what specifically these changes did..
Mostly as I'm wondering what the best way of doing a relay that also sends its own readings from locally attached sensors, might be, especially if the frequency of external sensors checking in is lower than the internal ones, and in order to not accidentally miss them hmm
Though I guess the nodes could confirm receipt and then if not, deep sleep for a minute and then try agian?