ESP-Now support for ESP32 (and ESP8266)

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
Post Reply
User avatar
glenn20
Posts: 132
Joined: Mon Jun 04, 2018 10:09 am

Re: ESP-Now support for ESP32 (and ESP8266)

Post by glenn20 » Mon Feb 08, 2021 2:31 am

I have updated the espnow-g20 branch attached to PR6515 with an importantt bugfix. The posts from @davef showed the issue, but I was too dense to see it at first :(. Thanks to @davef. There were two issues:
- I was not resetting the espnow_singleton on soft reset, and
- I was not sufficiently protecting the espnow_singleton from gc (in the event all references to ESPNow objects go out of scope.

These are fixed by adding the espnow_singleton to the micropython root pointers and adding code to main.c to reset the espnow_singleton root pointer to NULL on soft (or hard) reset.

I'm hoping we can get this PR accepted soon.

Cheers,
Glenn.

davef
Posts: 811
Joined: Thu Apr 30, 2020 1:03 am
Location: Christchurch, NZ

Re: ESP-Now support for ESP32 (and ESP8266)

Post by davef » Thu Feb 11, 2021 8:54 am

Running the ESP32 image as local and the ESP8266 image as remote. Will do some "stress" testing tomorrow. Looks good here.

AJB2K3
Posts: 44
Joined: Wed Mar 06, 2019 5:20 pm
Location: @nd Star on the Right.
Contact:

Re: ESP-Now support for ESP32 (and ESP8266)

Post by AJB2K3 » Mon Mar 08, 2021 6:58 am

Nevermind, i'm an idiot that should read his own book!



Where is the most up to date documentation on espnow as i'm working on trying to record this in my book?

I'm still a bit confused on sending and receiving data.

I have a duel joystick circuit controlled by and esp 32pico d1 and I want to send the reading from the two joysticks but am lost on how to make sure the receiving esp can tell the readings from the different joysticks apart I.E
J1 X, J1Y, J2X, J2Y.

User avatar
glenn20
Posts: 132
Joined: Mon Jun 04, 2018 10:09 am

Re: ESP-Now support for ESP32 (and ESP8266)

Post by glenn20 » Thu Apr 15, 2021 4:36 am

Just a heads-up. I have posted a proposal for a small breaking change to the espnow API on the github page for the PR (https://github.com/micropython/micropyt ... -819954276).

I understand the disruption an API change can be for users, so let me know if you have any objections or support for the change.

Summary:
- That irecv() and recv() return (None, None) on timeout, rather than None.

User avatar
glenn20
Posts: 132
Joined: Mon Jun 04, 2018 10:09 am

Re: ESP-Now support for ESP32 (and ESP8266)

Post by glenn20 » Thu Apr 15, 2021 4:41 am

Update to Handle ESP-Now Broadcasts correctly.

I have posted a new commit to the espnow branch to fix a bug in handling messages sent to the broadcast address. The espnow module now correctly (hopefully) deals with the idiosyncratic way the Espressif ESP-Now stack deals with broadcast addresses.

For more info see: https://micropython-glenn20.readthedocs ... broadcasts

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: ESP-Now support for ESP32 (and ESP8266)

Post by pythoncoder » Thu Apr 15, 2021 7:21 am

@glenn20 This looks great and you've written some excellent docs :D

Re uasyncio support, is it possible to instantiate a StreamWriter associated with a specific peer?

This only makes sense where the underlying send has sync=True - otherwise you'd just use a synchronous send. But I can see a use case for awaiting the peer's response.
Peter Hinch
Index to my micropython libraries.

User avatar
glenn20
Posts: 132
Joined: Mon Jun 04, 2018 10:09 am

Re: ESP-Now support for ESP32 (and ESP8266)

Post by glenn20 » Thu Apr 15, 2021 12:55 pm

@peterhinch Thanks - I've got a lot out of your copious docs, tutorials and code on asyncio :-).

No - it's not currently possible to instantiate a StreamWriter (or Reader) associated with a particular peer. I have been giving that thought myself and have on my TODO list a project to implement something like that in python atop the espnow module.

It seems that the ESPNow protocol times out on waiting for a response within something less than 50 milliseconds (it generates a send_cb() with a false status within something less than 25 milliseconds usually - but sometimes takes 50ms). I'm not sure if it is useful to handle such a small delay with asyncio. A non-sync send() returns in less than a millisecond. As the ESP-Now docs say, these responses come from very low down in the network stack and don't guarantee the message has been or will ever be received by an application on the host.

One thing though - if you send a burst of packets with sync=False (much faster), immediately followed by one with sync=True, there can be a significant delay while the final send call waits for all the pending send responses. I do implement support for ioctl(MP_STREAM_POLL, MP_STREAM_POLL_WR) which checks if there are any pending responses, which means that poll.poll(POLLOUT) is supported, which will support a StreamWriter. Also, if the ESPNow buffers fill up, send()s fail and one has to wait till some buffers clear up. Currently I retry the espnow_send() till it succeeds, but it may be better handled using asyncio methods.

PS. I am also working on another project based on your mqtt-as bridge code to implement an MQTT Proxy on an ESP32 which proxies multiple connection channels via ESPNow (as well as your syncom channel - and also bluetooth if I get that far). Early days for now.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: ESP-Now support for ESP32 (and ESP8266)

Post by pythoncoder » Fri Apr 16, 2021 11:38 am

glenn20 wrote:
Thu Apr 15, 2021 12:55 pm
...I'm not sure if it is useful to handle such a small delay with asyncio.
Fair point.
A non-sync send() returns in less than a millisecond. As the ESP-Now docs say, these responses come from very low down in the network stack and don't guarantee the message has been or will ever be received by an application on the host...
That does rather limit its usefulness. I'll leave you to figure out this stuff ;)
Peter Hinch
Index to my micropython libraries.

mhepp63
Posts: 8
Joined: Wed Apr 28, 2021 2:17 pm

Re: ESP-Now support for ESP32 (and ESP8266)

Post by mhepp63 » Wed Apr 28, 2021 3:41 pm

Hi,

first, I must say big thanks to glenn20...

And second... I have a problem with ESPNow and WiFi working together. I need receive messages, handle them and send via wifi, but I am not able to get working code with wifi and espnow together. I have esp32-wroom-32e as a receiver, your latest code (with wifi powersave).

Sender code is here:

Code: Select all

channel=11
receiver=b'\xff\xff\xff\xff\xff\xff' # or b'\x08:\xf2;\x1f\xa1'
e = espnow.ESPNow()
e.init()
w0 = network.WLAN(network.STA_IF)
w0.active(True)
e.set_pmk(b'TheKey')
e.add_peer(reciever, None, channel, network.STA_IF)
e.send(reciever, message.encode())
...and this works. I am able to receive this messages.

Receiver code is here:

Code: Select all

e = espnow.ESPNow()
e.init()

w0 = network.WLAN(network.STA_IF)
w0.active(True)
w0.connect('ssid', 'S3cr3tP455wd')  #this wifi is on channel 4
while not w0.isconnected():
    pass
print('network config:', w0.ifconfig())

# MAC: b'\x08:\xf2;\x1f\xa1' (08:3A:F2:3B:1F:A1)
w1 = network.WLAN(network.AP_IF)
w1.active(True)
w1.config(channel=11, hidden=True)

e.set_pmk(b'TheKey') #same like before
peers = [ b"sender'sMAC",  b"..."]
for peer in peers:
    e.add_peer(peer, None, 11, network.AP_IF)

print('Receiving...')
while True:
    msg = e.irecv()
    if msg:
        print(msg)
And here is working code:

Code: Select all

e = espnow.ESPNow()
e.init()

w1 = network.WLAN(network.STA_IF)
w1.active(True)

e.set_pmk(b'TheKey')
peers = [ b"sender'sMAC",  b"..."]
for peer in peers:
    e.add_peer(peer, None, 11, network.STA_IF)

print('Receiving...')
while True:
    msg = e.irecv()
    if msg:
        print(msg)
Chip is ESP32-D0WD-V3 (revision 3) or ESP32-D0WDQ6 (revision 1)

Update: Tried with power=network.WIFI_PS_NONE, no success. Working example tried on AP_IF - still working.

User avatar
glenn20
Posts: 132
Joined: Mon Jun 04, 2018 10:09 am

Re: ESP-Now support for ESP32 (and ESP8266)

Post by glenn20 » Thu Apr 29, 2021 2:33 am

Thanks for posting this @mhepp63 - I have found some very "interesting" quirks when investigating this issue :roll:
mhepp63 wrote:
Wed Apr 28, 2021 3:41 pm
And second... I have a problem with ESPNow and WiFi working together. I need receive messages, handle them and send via wifi, but I am not able to get working code with wifi and espnow together. I have esp32-wroom-32e as a receiver, your latest code (with wifi powersave).
TL/DR: When using AP and STA together, the AP will always change channel to that of the STA (See "Attention" note 3 at https://docs.espressif.com/projects/esp ... i_config_t). That is, your AP is running on channel 4, not channel 11 as you expect. You can verify this with w0.scan() on your sender node.

w1.config('channel') will also report the incorrect channel - adding to the confusion (I have fixed that in forthcoming commit - see below).

A few other notes:
  • Setting the pmk on it's own does nothing. You MUST set the lmk in add_peer() before messages will be encrypted (see https://docs.espressif.com/projects/esp ... l#security). I will update my docs to clarify that. I'm trying not to end up re-explaining/clarifying everything in the Espressif docs, but I see the advantage of useful self-contained docs up to a point).
Interestingly, your "Sender" code did not work for me at all. (It does contain typos, so it seems to be an almost copy of the actual code you are running):
  • On e.send(), I would get an ESP_ERR_ESPNOW_ARG exception and the Espressif stack would print:

    Code: Select all

    E (6613) ESPNOW: Peer channel is not equal to the home channel, send fail!
    It seems that at some point Espressif started enforcing this restriction in an update to the IDF (since I originally tested this behaviour last year with IDF3).

    So, I tried to set the STA_IF channel first with w0.config(channel=11) only to find that micropython disallows this on STA_IF (raises OSError). I fixed that in modnetwork.c only to find that the Espressif code ignores the channel in STA config. ie. there appears to be no way to set the channel on the STA_IF (other than to connect to an access point)!!!!

    Then, I discovered that Espressif allowed the esp_wifi_set_channel() call to work in STA_IF mode to address the limitations caused by their previous change (https://www.esp32.com/viewtopic.php?t=14542).

    I updated modnetwork.c:config() so that:
    • config(channel=n) uses esp_wifi_set_channel() on the STA_IF - and now I can set the ESPNow channel on STA_IF :-); and
    • config('channel') returns the actual channel of the radio with esp_wifi_get_channel() rather than the configured channel.
    I'll do a little more testing and commit that patch in the next few days.

    NOTE: I am building with ESP IDF 4.0.2. You may see different behaviour with earlier (or later) IDF builds.
Once again - thanks for uncovering this changed and very inconvenient behaviour!!!!

Post Reply