#ESP-NOW

1 messages · Page 1 of 1 (latest)

outer plover
#

voilà

rare glade
#

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.

outer plover
#

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

rare glade
#

Ah, so you're roughly doing what I am, but without the deinit?

outer plover
#

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

rare glade
#

I assume that means a full wifi reconnect etc?

outer plover
#

yes

#

I can try without

rare glade
#

Yeah, I remember it working but as above, it was a hot mess

outer plover
#

in theory, wifi and espnow should be able to be used simultaneously, as long as the channel is the same for all

rare glade
#

in theory

outer plover
#

so, for now, channel 1

rare glade
#

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

outer plover
#

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...

rare glade
#

maybe try deiniting it and see if that works?

rare glade
#

though am wondering again about if an IO relay could be a library

outer plover
#

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...

rare glade
#

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.

outer plover
#

something still quirky if I don't enabled = False when done with wifi

rare glade
#

like it only gets some of the packets and seems to get laggy and weird?

outer plover
#

it just doesn't work, I'll keep playing, maybe I have something wrong in my code still

rare glade
#

I'm trying to remember what I had that slightly worked, but badly

outer plover
#

it hangs, no control-C, so that's an issue

rare glade
#

yeah, that sounds about right

#

oh and there's a hard crash i should check if it's still there

outer plover
#

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

rare glade
#

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

outer plover
#

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

rare glade
#

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

outer plover
#

I was thinking about some wifi-accessible way for the espnow devices to know their peers... some server or file

rare glade
#

ooh

#

I think there's a way they can spam messages without a MAC

outer plover
#

there's a broadcast, haven't tried that at all

#

not even sure if it's implemented in CP

rare glade
#

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

outer plover
#

nope, the dreaded 0x3066 if I make the peer address FF:FF:FF:FF:FF:FF

#

oh, that's a different eror code

rare glade
outer plover
rare glade
#

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?

outer plover
#

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

rare glade
#

Hmm, the toml thing didn't work

outer plover
#

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?

rare glade
rare glade
grim parrot
#

@outer plover why not two boards
One receive from espnow
Then tx rx to another and then another sends mqtt message

trail prairie
#

@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

rare glade
rare glade
trail prairie
# rare glade Can give you my code for that though I’m hitting a memory issue if I don’t just ...

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

rare glade
#

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

trail prairie
#

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)

rare glade
#

Think I've ended up with something similar, partly just because I was getting random WiFi errors

rare glade
#

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

trail prairie
#

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.

rare glade
#

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

outer plover
#

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

rare glade
#

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

outer plover
rare glade
#

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

outer plover
#

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

rare glade
outer plover
#

e.deinit() is only if there's legit T & H... is that always true? the logic could wrong potentially

rare glade
outer plover
#

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

rare glade
#

yeah, I thought I wrote it so that if they aren't both not None, it won't move onto doing WiFi anyway

outer plover
#

do you always want to block when trying to read a packet? (I think that's easiest, unless you're after something different)

rare glade
#

I don't know what that means heh

outer plover
#

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)

rare glade
#

getting a bit confused as to how to refactor it this way

rare glade
#

not sure why that doesn't seem to be happening in practice

#

unless the if e thing basically overrides the deinit?

outer plover
#

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

rare glade
#

as in, my code should work and not get interrupted by new packets?

outer plover
#

yeah, you deinit espnow before doing wifi, then reinit espnow and back at the top of loop check for packet(s)

rare glade
#

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

outer plover
#

can you post the few lines of print output that show the issue?

rare glade
#

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

outer plover
#

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

rare glade
#

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

outer plover
#

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

rare glade
#

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

outer plover
#

true

#

why d you think the deinit isn't working?

rare glade
#
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?

outer plover
#

once you e.read, they should be gone (from ther espnow internal buffer)

#

but packet isn't cleared until you re-assign it

rare glade
#

what if there are multiple packets not just 2?

#

ESPNOW seems to be a bit "spray and pray"

outer plover
#

the example in readthedocs shows a way to handle multiple packets that are part of a group

rare glade
#

Hmm, I guess my code isn't getting pulled back into the ESPNOW loop anymore

#

lol I just had to say that

outer plover
#

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

rare glade
#

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?"?

outer plover
#

oh, in the main loop? I did that yesterday o_O

#

lolz

rare glade
#

"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?

outer plover
#

no, just some things block a little in the core until it can get around to registering the control-C *typ. of network operations)

outer plover
#

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.

grim parrot
#

then Mqtt board send to mqtt

rare glade
#

So 2 boards?

grim parrot
#

yes

outer plover
#

but theoretically, you can run espnow and mqtt on the same board... if it isn't possible right now it's a bug

grim parrot
outer plover
#

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

grim parrot
outer plover
#

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

grim parrot
outer plover
#

or just use the same channel

#

espnow can also be used with wifi as an AP, rather than connecting to an external AP

grim parrot
outer plover
#

no, wifi opens an AP, and espnow then runs independently but on the AP channel

grim parrot
outer plover
#

that's how it works if the espnow device is in Station mode rather than AP mode

outer plover
#

yeah, wifi station w/mqtt + espnow on same channel

#

or use diff channels and count on the buffers, or coordinate time slots, or...

grim parrot
outer plover
#

yeah

grim parrot
outer plover
#

background buffering 🙂 depoends on the frequency and number of packets, size of buffers, etc.

outer plover
#

plenty of options, so each can pick what's best for them... flexibility ftw 🙂

rare glade
#

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?

trail prairie
#

Working examples are always useful!

rare glade
#

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.

outer plover
#

@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.

rare glade
#

Yeah, same.

outer plover
#

so... it's a bug if it doesn't work?

rare glade
#

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?

outer plover
#

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

rare glade
#

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?"

outer plover
#

your call, the template steps you through the info to include

rare glade
#

Is setting channels still broken?

outer plover
#

I believe so

rare glade
#

Was weird how I was definitely able to set channel 14 originally but then that broke at some point

outer plover
#

I tested channels some days ago, haven't seen any PRs that would fix it

rare glade
#

Has anyone tried with two node devices? If I remember correctly, you can/are meant to have a different LMK for each node?

outer plover
#

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)

rare glade
#

suddenly remembers the Ethernet Featherwing exists

trail prairie
outer plover
# rare glade Here's my relay code. I tried to get ESP-NOW into a function but kept hitting is...

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.

rare glade
outer plover
#

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

rare glade
outer plover
#

lolnope I can be pretty obtuse

rare glade
#

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.

outer plover
#

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

rare glade
#

ahh

rare glade
outer plover
#

what's the sequence of messages until it stops?

rare glade
#

Literally just:

Connected to the WiFi network```
outer plover
#

like it's hanging in io.get_feed or something? what line does it control-C at?

outer plover
#

so it's looping but printing nothing... maybe not receiving for some reason... add more prints ein there to see what's not happening?

rare glade
#

Where would you suggest I add them?

outer plover
#

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
rare glade
#

sprinkles temperature and humidity sensors everywhere

outer plover
#

lol, I have them all over too, but each is currently a FeatherS2 doing full wifi to a server

rare glade
#

btw, what's the time.sleep(15) for anyway?

outer plover
#

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?

rare glade
#

yeah, it's sending, my code works fine with it

outer plover
#

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}]")

rare glade
#

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)```
rare glade
outer plover
#

ok for now, but those stats will tell you if the sender "thinks" it delivered or not

outer plover
#

oh, that, sorry...

rare glade
#

etc.

outer plover
#

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

rare glade
outer plover
#

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

rare glade
#

I get NameError: name 'e' is not defined at the start of the loop

outer plover
#

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...)

outer plover
#

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()

outer plover
#

(without the while True: it inits, quickly checks and odds are nothing is there at that instant, then deinits, so nothing is ever received)

rare glade
#

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?

outer plover
#

I 'm running code with acknowledgements, I should update my gists.

#

But there are also e.send_success, e.send_failure, e.read_success, e.read_failure

#

there is a low-level check that operations were successful at the network level