ESP-Now support for ESP32 (and ESP8266)

All ESP32 boards running MicroPython.
Target audience: MicroPython users with an ESP32 board.
davef
Posts: 813
Joined: Thu Apr 30, 2020 1:03 am
Location: Christchurch, NZ

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

Post by davef » Sat Jul 16, 2022 6:10 am

Example 1, under ESPNow and Wifi in the latest docs.

sender.py

Code: Select all

import network
import espnow
import utime


peer = b'\x08:\xf2\xab\xe2\x0c' #  test receiver STA-mode

e = espnow.ESPNow()
e.active(True)

w0 = network.WLAN(network.STA_IF)
w0.active(True)                         #  set channel will fail unless Active>
w0.connect('HUAWEI-E8231-7bd5', 'my_password')
while not w0.isconnected():             #  wait until connected...
    utime.sleep(1)

print ('connected')

w0.config(ps_mode=network.WIFI_PS_NONE) #  disable power saving

e.add_peer(peer)

if not e.send(peer, b'Water tank level alarm', True):
    print ('Msg send failed')
else:
    print ('Msg send succeeded')

print('Send me messages at:', w0.config('mac'))

#  shut-down, so that a CTRL-D in rshell works
w0.disconnect()
w0.active(False)
e.active(False)
receiver.py

Code: Select all

#  receiver.py for ESPNow testing.

import network
import espnow
import utime
import machine
import my_umail
from mail_functions import send_alarm


CYCLE_TIME = 60             #  seconds
REBOOT_DELAY = 5            #  seconds
remote_mac = b'x!\x84{\xde\x80' #  STA-mode

def reboot(delay = REBOOT_DELAY):
 #  print a message and give time for user to pre-empt reboot
 #  in case we are in a (battery consuming) boot loop
    print (f'Rebooting device in {delay} seconds (Ctrl-C to es>
 #  or just machine.deepsleep(delay) or lightsleep()
    utime.sleep(delay)
    machine.reset()

try:
    print ('you have 5 seconds to do Ctrl-C if you want to edi>
    utime.sleep(5)

    e0 = espnow.ESPNow()
    e0.active(True)

    w0 = network.WLAN(network.STA_IF)
    w0.active(True)
    w0.connect('HUAWEI-E8231-7bd5', 'my_password')

    while not w0.isconnected():            # Wait until connected
        print ('trying to connect')
        utime.sleep(1)

    w0.config(ps_mode=network.WIFI_PS_NONE) # ..then disable power saving

    print (w0.config('mac'))

 #  these functions generate exceptions on error - always return None>
    e0.config(timeout = (CYCLE_TIME) * 1000)
    e0.add_peer(remote_mac)
except KeyboardInterrupt as err:
    raise err #  use Ctrl-C to exit to micropython repl
except Exception as err:
    print ('Error initialising espnow:', err)
    reboot()

while True:
    try:
        print ('waiting for a msg from the remote')

        for mac, msg in e0:
            print (str(mac))

            if mac == remote_mac:
                msg = msg.decode('utf-8')
                print (msg)

#                send_alarm(msg)

                break #  out of the while loop!!
            elif mac == None:
                print ('no peers found')
                utime.sleep(5)
                break
            else:
                print ('Recv from {}: "{}"'.format(mac, msg))
                utime.sleep(5)
                break

    except KeyboardInterrupt as err:
        raise err #  use Ctrl-C to exit to micropython repl
    except Exception as err:
     #  all other exceptions cause a reboot
        print ('Error during execution:', err)
        reboot()
When I comment-out these lines in sender.py:

Code: Select all

w0 = network.WLAN(network.STA_IF)
w0.active(True)                         #  set channel will fail unless Active>
w0.connect('HUAWEI-E8231-7bd5', 'my_password')
while not w0.isconnected():             #  wait until connected...
    utime.sleep(1)

print ('connected')

w0.config(ps_mode=network.WIFI_PS_NONE) #  disable power saving
then I get 'msg send failed'

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

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

Post by glenn20 » Sun Jul 17, 2022 12:59 am

davef wrote:
Sat Jul 16, 2022 6:10 am
When I comment-out these lines in sender.py:

Code: Select all

w0 = network.WLAN(network.STA_IF)
w0.active(True)                         #  set channel will fail unless Active>
w0.connect('HUAWEI-E8231-7bd5', 'my_password')
while not w0.isconnected():             #  wait until connected...
    utime.sleep(1)

print ('connected')

w0.config(ps_mode=network.WIFI_PS_NONE) #  disable power saving
then I get 'msg send failed'
If I am reading this correctly, you should not comment out the first two lines, otherwise the wifi interface will be off. Although, in that case, I believe you should be getting an exception raised (OSError: (-12396, 'ESP_ERR_ESPNOW_IF')), rather than a False return from e.send() - so I'm not sure this is the source of your problems.

I suspect the problem may be that your receiver, which is connected to your wifi AP, will automatically switch to the channel of the AP. When your sender connects to the AP, it also switches to that channel. When your sender is NOT connected to the AP you need to manually change the channel to that of the receiver.

There are two ways to do this:
  • Switch the radio on the sender to the channel of the receiver and AP before you call e.add_peer(): w0.config(channel=X)
  • Set the channel of the receiver when you call add_peer: e.add_peer(peer, channel=X)
Good luck...

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

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

Post by davef » Sun Jul 17, 2022 1:30 am

If I am reading this correctly, you should not comment out the first two lines, otherwise the wifi interface will be off. Although, in that case, I believe you should be getting an exception raised (OSError: (-12396, 'ESP_ERR_ESPNOW_IF')), rather than a False return from e.send() - so I'm not sure this is the source of your problems.
Correct, that is what I observe. As well as the WiFi interface being active, do you also have to connect to it?

From the docs:
There are several options to improve reliability of receiving ESPNow packets when also connected to a wifi network:
My understanding was that using Example 1, that I wouldn't need to worry about AP/channel allocation stuff.

I will try your suggestions.

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

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

Post by glenn20 » Sun Jul 17, 2022 1:45 am

davef wrote:
Sun Jul 17, 2022 1:30 am
If I am reading this correctly, you should not comment out the first two lines, otherwise the wifi interface will be off. Although, in that case, I believe you should be getting an exception raised (OSError: (-12396, 'ESP_ERR_ESPNOW_IF')), rather than a False return from e.send() - so I'm not sure this is the source of your problems.
Correct, that is what I observe. As well as the WiFi interface being active, do you also have to connect to it?

From the docs:
There are several options to improve reliability of receiving ESPNow packets when also connected to a wifi network:
My understanding was that using Example 1, that I wouldn't need to worry about AP/channel allocation stuff.
Yes - Example 1 is an example of how to use espnow and wifi together - ie if you NEED to connect to a wifi access point AND send espnow messages. In that case, by connecting to the access point, all devices are running on the same channel as the access point.

For your sender, you DON'T want to connect to a wifi access point (but found that you have to or your messages are not received).

My suggestion is to find out what channel your wifi access point and receiver are running on, eg. call

Code: Select all

print("Receiver channel =", w0.config('channel'))
after your receiver has connected to the wifi access point.

If the radio on your sender is not also running on that same channel, the receiver will never receive the messages.

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

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

Post by davef » Sun Jul 17, 2022 2:39 am

We are getting closer ... with the w0 connect() commented out the rx channel = 1.
channel: The wifi channel (2.4GHz) to communicate with this peer. Must be an integer from 0 to 14. If channel is set to 0 the current channel of the wifi device will be used. (default=0)
On the sender adding the channel to:

Code: Select all

e.add_peer(peer, channel=1)
gives this error:

Code: Select all

OSError: (-12390, 'ESP_ERR_ESPNOW_ARG')
so, I am guessing that that is not setting the sender to channel 1
or
am I suppose to be doing this at the 'receiver' end?
Last edited by davef on Sun Jul 17, 2022 4:38 am, edited 1 time in total.

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

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

Post by glenn20 » Sun Jul 17, 2022 11:17 am

I think there is still some confusion... ;)
davef wrote:
Sun Jul 17, 2022 2:39 am
We are getting closer ... with the w0 connect() commented out the rx channel = 1.
You want your receiver to connect to the wifi access point. When it does so, it will automatically change channel to the channel used by the access point. Your sender must use this same channel. (Unless I am misunderstanding something in your app).

So, on your receiver, do not comment out the w0.connect() and after the w0.isconnected() loop print out e.config('channel'). ie. print out the channel after the receiver is connected to the access point.

Once you know what channel your receiver is using when it is connected to the access point, you can change your sender to use that same channel.

I should have mentioned that I recommend using w0.config(channel=X) to set the channel on the sender, instead of e.add_peer(peer, channel=X) because the behaviour of add_peer() to set the channel changes with different versions of the espressif IDF (and is broken for some IDF versions).

Note that channel 1 is the default channel the esp devices use if they are NOT connected to an access point.

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

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

Post by davef » Sun Jul 17, 2022 7:31 pm

Wahoo ... using w0.config(channel=X) instead of e.add_peer(peer, channel=X) was the main issue. I had tried w0.config(channel=X) at the sender-end but must have had something else set incorrectly.

I had read articles about changing the router channel to 1 and other attempts to get these two interfaces to work on one platform.

I can now replace the two wires that connect my ESPNow system to my WiFi-connected system :)

This is my working stripped-down sender.py

Code: Select all

import network
import espnow
import utime


local = b'xyz' # receiver macl

print ('you have 5 seconds to do Ctrl C if you want to edit the program')
utime.sleep(5)

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

print (w0.config('mac'))
print (w0.config('channel'))
w0.config(channel=11) #  or whatever channel your router is on 
print (w0.config('channel'))

e0 = espnow.ESPNow()
e0.active(True)
e0.add_peer(local)

while True:
    status = 'test'
    print(status)
    e0.send(local, status)
    utime.sleep(5)

#  if you break out of the while loop
e0.active(False)
w0.active(False)
and the stripped-down receiver.py

Code: Select all

import network
import espnow
import utime


remote = b'xyz' #  remote MAC

print ('you have 5 seconds to do Ctrl-C if you want to edit the program')
utime.sleep(5)

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

print (w0.config('mac'))

w0.connect('your_router', 'your_password')

while not w0.isconnected():            # Wait until connected...
    print ('trying to connect')
    utime.sleep(1)

w0.config(ps_mode=network.WIFI_PS_NONE) # ... then disable power saving

print (w0.config('channel'))

e0 = espnow.ESPNow()
e0.active(True)
e0.add_peer(remote)


while(True):
    print ('waiting for a msg from the repeater')

    for mac, msg in e0:
        print (str(mac))

        if mac == remote:
            msg = msg.decode('utf-8')
            print (msg)
            break

#  if you break out of the while loop
e0.active(False)
w0.active(False)

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

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

Post by glenn20 » Sun Jul 17, 2022 11:09 pm

davef wrote:
Sun Jul 17, 2022 7:31 pm
Wahoo ... using w0.config(channel=X) instead of e.add_peer(peer, channel=X) was the main issue. I had tried w0.config(channel=X) at the sender-end but must have had something else set incorrectly.
Excellent. Glad it is working - the interactions between espnow and wifi can be complicated.

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 Jul 18, 2022 5:24 am

I have updated the docs on ESPNow and Wifi Operation in an attempt to make it clearer what is going on:

https://micropython-glenn20.readthedocs ... -operation

Let me know if it needs further clarification - or if I have just made it more confusing ;).

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

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

Post by davef » Mon Jul 18, 2022 6:31 am

I "vote" for clearer.

A note in ESPNow.config() for setting the channel of the remote ESPNow devices when used with an ESPNow/WiFi-connected device. This would need referencing in the ESPNow/WiFi section.

Post Reply