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 » Tue Oct 26, 2021 1:20 am

ESP8266 Bugfix

For those who missed it (https://github.com/glenn20/micropython- ... s/issues/7) I have posted some updated pre-compiled micropython espnow images (https://github.com/glenn20/micropython- ... g26f058583) which address a serious bug on the esp8266 platform.

The espressif espnow function esp_now_send() would generate a panic (only on esp8266) if the message buffer pointed to an address in ROM. Passing any ROM-interned string literal to ESPNow.send() would trigger this bug (eg. e.send(peer, 'join') or e.send(peer, '<module>') or e.send(peer, '7')...).

(If you are curious and new to string interning see: https://docs.micropython.org/en/latest/ ... /qstr.html).

Thanks to @danish1963 for the bug report.

User avatar
Zoland
Posts: 20
Joined: Wed Jan 23, 2019 2:12 pm
Location: Russia, Altai

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

Post by Zoland » Sun Oct 31, 2021 2:55 pm

Hi, Glenn!

There is some part of my experience with ESP-Now and creating unified startup for each node.
It's not mesh yet, but I can inform net space about own node and find necessary nodes, just defining their names

Result: dictionary { NAME, MAC, DATA } can be used in future code

Code: Select all

#  ESPNow NODES

import network
import utime
from esp import espnow

class NODE():
    def __init__(self):
        self.MAC  = None
        self.data = None
        

class ESP_NET():
    #              owner_node  critical_nodes  recommended_nodes
    def __init__(self, o_node, c_nodes = None, r_nodes = None ):
        print('Owner         :',o_node)
        print('Critical_nodes:',c_nodes)
        print('Recomend_nodes:',r_nodes)
        print()
        
        self.o_node  = o_node  # owner_node
        self.c_nodes = c_nodes # critical_nodes
        self.r_nodes = r_nodes # recommended_nodes
        
        #  A WLAN interface must be active to send()/recv()
        w0 = network.WLAN(network.STA_IF)
        w0.active(True)
        local = w0.config('mac')
        self.bcast = b'\xff'*6

        self.e0 = espnow.ESPNow()
        self.e0.init()
        self.e0.config(timeout=1000)

        self.e0.add_peer(local)      # add owner MAC first
        self.e0.add_peer(self.bcast) # add broadcast second
        
        self.macs = dict.fromkeys(c_nodes,None) # create NODE register
        
        find_peers = (len(c_nodes) if c_nodes else 0) + (len(r_nodes) if r_nodes else 0)
        i=0
        while True:
            ping = 'Ping'+o_node+' '+str(i)
            for mac, msg in self.e0:
                self.e0.send(self.bcast,ping,True) # I'm here
                print('Send: ',ping)
                    
                if mac:                            # Oy, who is here
                    print('MAC detected: ',mac)
                    new_msg = msg.decode('utf-8')
                    node_name = new_msg[4:5]
                    
                    if node_name in self.macs.keys(): # One of the declared Nodes
                        if self.macs[node_name]:
                            print('_____________Already peered with MAC: ',mac,'stored in ',self.macs)
                            if new_msg[0:4] == 'Pong':
                                self.macs[node_name].data = new_msg
                                print('_____________Pong from ',node_name)
                                find_peers -= 1
                        else: # New peer
                            self.e0.add_peer(mac)
                            new_MAC = self.e0.get_peer(mac)
                            self.macs[node_name] = NODE()
                            self.macs[node_name].MAC = new_MAC[0]
                            print('______________New peer with MAC: ',new_MAC[0])
                            self.e0.send(mac, bytearray('Pong'+o_node+' '+str(i)), True) #  send back answer

                        print ('Received from :{}: "{} peers left {}"'.format(node_name, new_msg,find_peers))
                        print()
                        break

            if not find_peers: break
            print ('..............waiting for msg '+str(i))
            print()
            i += 1


#################################################################################### main                
en = ESP_NET('A',['B','C'])

print('RESULT')
for node in en.macs.keys():
    print (node,en.macs[node].MAC,en.macs[node].data)

User avatar
Zoland
Posts: 20
Joined: Wed Jan 23, 2019 2:12 pm
Location: Russia, Altai

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

Post by Zoland » Wed Nov 03, 2021 1:51 pm

FROM DOC:
ESPNow.peer_count() (ESP32 only)¶
Return the number of peers which have been registered.

REAL RESULT:
2-values tuple

QUESTION:
First element - real number of peers, what is the meaning of the second value?

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

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

Post by glenn20 » Wed Nov 03, 2021 10:51 pm

Zoland wrote:
Wed Nov 03, 2021 1:51 pm
FROM DOC:
ESPNow.peer_count() (ESP32 only)¶
Return the number of peers which have been registered.

REAL RESULT:
2-values tuple

QUESTION:
First element - real number of peers, what is the meaning of the second value?
Doh - that's an oversight in the docs. Thie first number is the total number of peers and the second is the number of encrypted peers. As per the data returned from esp_now_get_peer_num().

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

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

Post by glenn20 » Wed Nov 03, 2021 10:58 pm

Zoland wrote:
Sun Oct 31, 2021 2:55 pm
Hi, Glenn!

There is some part of my experience with ESP-Now and creating unified startup for each node.
It's not mesh yet, but I can inform net space about own node and find necessary nodes, just defining their names
This is really interesting - thanks for your work on this. Sorry I've been so slow to follow up - I've been trying to get some other things cleaned up and been out of action for most of the last few weeks. I will definititely be looking to use this in my node management framework too - which I hope to get back to next week.

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 Nov 04, 2021 12:48 am

ESPNow also supported on ESP32S2

I've uploaded a new micropythom image with ESPNow support for the ESP32S2 at https://github.com/glenn20/micropython-espnow-images.

That image is compiled against my micropython espnow-g20-v1.17 branch and ESP IDF v4.3.1 using BOARD=GENERIC_S2. It also requires PR#7963 to get a functional image on a generi ESP32S2 (withhout extra SPIIRAM).

I will compile and upload some additional images for the various other esp32s2 target boards today. As always, let me know there if there are any issues (I have limited devices available to test) here or raise an issue on github.

Thanks again to everyone who has been testing and raising issues.

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 Nov 05, 2021 1:44 am

Zoland wrote:
Mon Oct 25, 2021 5:41 pm
Accidentally stumbled upon the Expressif ESP-NOW source https://github.com/espressif/esp-now
(Sorry - I forgot to post this when I reviewed this.)

For anyone who hasn't yet looked into this, the code is for a framework with more advanced capabilities (inc, mesh and grouping, OTA, etc) that sits over the existing esp_now functionality. The underlying esp_now code is still closed and only available as binary libs in the ESP IDF.

Confusingly, this interface duplicates much of the api with renamed functions, eg: espnow_send() is implemented over esp_now_send(), etc...

Nonetheless, these capabilites may be interesting/useful for future adoption in micropython.

User avatar
Zoland
Posts: 20
Joined: Wed Jan 23, 2019 2:12 pm
Location: Russia, Altai

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

Post by Zoland » Sat Nov 06, 2021 12:16 pm

From the documentation:

To send() a broadcast message, the broadcast MAC address must first be registered using add_peer(). send() will always return True for broadcasts, regardless of whether any devices receive the message. It is not permitted to encrypt messages sent to the broadcast address or any multicast address.


And what about return value for the multicast messages? For example, I want to check all active peers in my network. If one of the registered peers not response it will return False? So if it's critical for me, next I'll need to find this peer? Or it's easier to check each peer in loop?

User avatar
Zoland
Posts: 20
Joined: Wed Jan 23, 2019 2:12 pm
Location: Russia, Altai

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

Post by Zoland » Sat Nov 06, 2021 12:38 pm

glenn20 wrote:
Wed Nov 03, 2021 10:58 pm
This is really interesting - thanks for your work on this.
My idea is to avoid knowing the real MAC addresses by replacing them with symbolic names. At startup, using broadcast pinging starts in the searching adding pairs with the specified names and the same time making dictionary with additional info about this peers. At the working logic,we continue to monitor the situation of loss of communication by regularly broadcast pingig to suspend work until reconnection occurs. At the same time, I don't want to write a lot of code and not use asyncio to save memory.

Like this code can be added to examples if it's interesting.

User avatar
Zoland
Posts: 20
Joined: Wed Jan 23, 2019 2:12 pm
Location: Russia, Altai

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

Post by Zoland » Sat Nov 06, 2021 3:50 pm

Does anyone know if there is a time difference between broadcast (multicast) and peer-to-peer sending?

Maybe it's easier to send broadcast publications (messages) that the receiving modules just filter the necessary publications as subscriber? For example subscriber send peer-to-peer request to publisher module, that further answer by broadcast publication. In that case we not need to make peers on publisher modules ?

What happens when the received message buffer overflows? Are earlier messages being pushed or are later messages not being logged?

Post Reply