ESP-Now support for ESP32 (and ESP8266)

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

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

Post by glenn20 » Fri Dec 25, 2020 10:08 am

davef wrote:
Fri Dec 25, 2020 4:39 am
Hi glenn20,

Thank you for the feedback. Anytime you see the word "seemed" it means that things did not seem to work 100% of the time. When looking at traceback messages sometimes it "seemed" that a programming error later in the program would throw up an error at a earlier line number.
I got into a habit of commenting-out every line and introducing them one at a time, until an error would pop-up.

For the ESP8266:
Today, I do not have to put a value in init() in rx8266.py so remove that issue.

Code: Select all

for msg in e:
of course throws a 'ESPNow' object is not iterable as you can only use this on the ESP32

I do not know other ways of getting the "callee-owned" tuple of bytearrays into a form that I can use. Off to check with Mr. Google.

Dave
Glad to hear there is some progress ;). You use the "callee-owned tuple" just like a normal tuple. On the esp8266 you should be able to use:

Code: Select all

pkt = e.irecv()
print("Peer MAC Address: {}".format(pkt[0]))   # MAC address
print("Message: {}".format(pkt[1]))   # Received message
or

Code: Select all

peer, msg = e.irecv()
print("Peer MAC Adress: {}".format(peer))   # MAC address
print("Message: {}".format(msg))   # Received message
The only difference to a regular tuple is that the same memory is retiurned on every call to irecv().

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 » Fri Dec 25, 2020 10:25 am

Found a snippet of code on how to grab the message content which works for my simple case. I only want to tell a pump to turn off and it would be useful to have a message come back that it has been told to turn off.

The remote end is a ESP8266 and the local end a ESP32.

For ESP8266 receive

Code: Select all

import network
from esp import espnow
import ubinascii


#  A WLAN interface must be active to send()/recv()
w0 = network.WLAN(network.STA_IF)
w0.active(True)
mac = w0.config('mac')
print(mac)
mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
print(mac)

e = espnow.ESPNow()
e.init()
peer = b'$\n\xc4Yd\x88' #  MAC address of ESP32
e.add_peer(peer)

print('waiting for message')

while True:
	msg = e.irecv()
	if msg:
		print(msg[1])
For ESP8266 send

Code: Select all

import network
from esp import espnow


#  A WLAN interface must be active to send()/recv()
w0 = network.WLAN(network.STA_IF) #  or network.AP_IF
w0.active(True)

e = espnow.ESPNow()
e.init()
peer = b'$\n\xc4Yd\x88' #  MAC address of ESP32
e.add_peer(peer)

print('send message')

e.send(peer, 'pump is now off', True)

e.deinit()
For ESP32 receive

Code: Select all

import network
from esp import espnow
import ubinascii


#  A WLAN interface must be active to send()/recv()
w0 = network.WLAN(network.STA_IF)
w0.active(True)
mac = w0.config('mac')
print(mac)
mac = ubinascii.hexlify(network.WLAN().config('mac'),':').decode()
print(mac)

e = espnow.ESPNow()
e.init()
peer = b'\x18\xfe\x34\xde\xea\xf3' #  MAC address of ESP8266
e.add_peer(peer)

print('waiting for message')

while True:
    msg = e.irecv()
    if msg:
        print(msg[1])
For ESP32 send

Code: Select all

import network
from esp import espnow


#  A WLAN interface must be active to send()/recv()
w0 = network.WLAN(network.STA_IF) #  or network.AP_IF
w0.active(True)

e = espnow.ESPNow()
e.init()
peer = b'\x18\xfe\x34\xde\xea\xf3' #  MAC address of ESP8266
e.add_peer(peer)

print('send message')

e.send(peer, "pump off", True)

e.deinit()
I found that recv() didn't work for the ESP8266.

I will now digest your response. Thank you for your help in getting this far.

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 » Sat Dec 26, 2020 7:21 am

ESPNow.irecv([timeout])

As for `recv()<ESPNow.recv()>` except that ``irecv()`` will return a
"callee-owned" tuple of bytearrays.
That is, memory will be allocated once for the tuple and byte strings on
invocation of espnow.ESPNow() and re-used for subsequent calls to
`irecv()<ESPNow.irecv()>`. You must make copies if you
wish to keep the values across calls to ``irecv()``.

Taking the code above for ESP8266 receive, I have this block to process the commands:

Code: Select all

while True:
	msg = e.irecv()
	if msg:
		command = msg[1].decode('utf-8')
		print(command)
If I don't bail-out of the while block, then every 5-10 minutes I get another print(command).

I understand that tuples are immutable and I guess every time it goes around the loop
it has the same data in the tuple so nothing changes, but why the long delay?

Another thing I do not understand is ... memory will be allocated once for the tuple
and byte strings on invocation of espnow.ESPNow() and re-used for subsequent calls to irecv()

If msg is immutable how can a new (different) msg be received?

This seems to sort it:

Code: Select all

while True:
    msg = e.irecv()
    if msg:
        command = msg[1].decode('utf-8')
        print(command)
		
    del msg
I would like to leave the remote ready for new commands.

Appreciate your comments.
Last edited by davef on Sat Dec 26, 2020 8:43 pm, edited 1 time in total.

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 » Sat Dec 26, 2020 8:42 pm

Just an observation:

on the ESP32 image the Wifi needs to be set to AP before you can use WebREPL. On the ESP8266 WebREPL works in STA and AP mode. Maybe it is "finger-trouble" at my end and certainly is not a problem.

Things seem to be working consistently, now I will set up a link and do some serious testing.

User avatar
Roberthh
Posts: 3667
Joined: Sat May 09, 2015 4:13 pm
Location: Rhineland, Europe

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

Post by Roberthh » Sun Dec 27, 2020 10:39 am

On the ESP32 here weprepl works fine in STA mode.

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 » Sun Dec 27, 2020 6:24 pm

With this image?
..and a precompiled image for the ESP32 at https://drive.google.com/file/d/1IIStoi ... UgOrHRe7VF

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

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

Post by glenn20 » Fri Jan 01, 2021 9:19 am

davef wrote:
Fri Dec 25, 2020 10:25 am
Found a snippet of code on how to grab the message content which works for my simple case.

...
Good - these are all consistent with the documentation.
I found that recv() didn't work for the ESP8266.
Yes - as described in the documentation (https://github.com/glenn20/micropython/ ... espnow.rst) and also at https://github.com/micropython/micropyt ... -713368318. I removed the recv() method from the esp8266 to drastically reduce the code size (among other changes). The available code size is MUCH smaller on the esp8266 so I pared the code to fit within the default image settings.

I recommend using send() and irecv() for both the esp32 and esp8266 (if you don't need asyncio support).

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 » Fri Jan 01, 2021 10:00 am

Yes, I have had a few problems understanding the docs. I understood:
.. method:: ESPNow.recv([timeout]) (ESP32 only)
to mean that a timeout was only allowed on the ESP32 I now realise that that was incorrect.

However, as per a previous post:
Something in espnow.rst that I do not understand:

import network

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

.. method:: ESPNow.send(mac, msg, [sync=True]) (ESP32 only)
ESPNow.send(msg) (ESP32 only)
Is the last line in the quote suppose to be: (ESP8266 only) or both?

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

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

Post by glenn20 » Fri Jan 01, 2021 10:16 am

davef wrote:
Sat Dec 26, 2020 7:21 am
...
Taking the code above for ESP8266 receive, I have this block to process the commands:

Code: Select all

while True:
	msg = e.irecv()
	if msg:
		command = msg[1].decode('utf-8')
		print(command)
If I don't bail-out of the while block, then every 5-10 minutes I get another print(command).
I suspect this is due to the irecv() call timeout (which is 5 minutes by default). irecv() will return None on timeout (so you can test the return value to check for timeout). You can change the default timeout in the init() call (on esp8266) or the config() call (ESP32) - or pass a timeout (in milliseconds) as an argument to irecv(). See the docs for details.
I understand that tuples are immutable and I guess every time it goes around the loop
it has the same data in the tuple so nothing changes,
The returned tuple is indeed immutable and the same tuple is returned on each loop. Each element of the tuple is a bytearray. On each call to irecv() new data is written into those bytearrays (peer mac address and the message).
but why the long delay?
As indicated above, you seem to be encountering the read timeout (ie. when no messages are received for more than timeout milliseconds).
If msg is immutable how can a new (different) msg be received?
New data is written into the bytearrays on each call to irecv(). This is a pretty standard approach to looping over received data in memory constrained systems like micropython.
This seems to sort it:

Code: Select all

while True:
    msg = e.irecv()
    if msg:
        command = msg[1].decode('utf-8')
        print(command)
		
    del msg
This is incorrect. Either, 'del(mesg)' will do nothing - or it will deallocate the memory buffers leading to eventual memory corruption (I need to check which is true :-). I suspect it is the former. In either case, you really should remove that line of code.
I would like to leave the remote ready for new commands.
The remote should always be ready for new commands. You don't need to do anything special for that to happen.

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 » Fri Jan 01, 2021 8:01 pm

Thank you for pointing out that:

Code: Select all

del msg
is incorrect.
On each call to irecv() new data is written into those bytearrays (peer mac address and the message).
If I send one message and process it and then I make another call to irecv() should there not be a message to process. The message I sent seems to persist. Hence me trying to "clean" things up.

I think you are suggesting that when you get a timeout just ignore the message.

Appreciate your support as ESPNow seems the best solution for a reliable dedicated control link on the ESP devices.

Post Reply