Skip to content

esp32/esp8266: add support for ESP-NOW #4115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from

Conversation

shawwwn
Copy link

@shawwwn shawwwn commented Sep 10, 2018

ESP-Now enables direct communication from one ESP device to another.

This PR is originally based on issue micropython/micropython-esp32#197 and @nickzoic's branch.

I rewrote most of the functions to make it compatible between esp32 and esp8266.
Several workarounds were introduced to get rid of inherent bugs with the SDK.

SDK Ref

https://www.espressif.com/sites/default/files/documentation/esp-now_user_guide_en.pdf
https://www.espressif.com/sites/default/files/2C-ESP8266_Non_OS_SDK_API_Reference__EN.pdf
https://www.espressif.com/en/products/software/esp-now/overview

API Docs

espnow.init()
espnow.deinit()

Initialize/deinitialize ESP-NOW


espnow.pmk(master_key)

Setting the primary master key -- some sort of key management(?).
Key must be 16 characters.
According to the documentation, pmk is used to encrypt peer's lmk. Then the encrypted lmk will be used to encrypt the data frame.
When transmitting between 2 peers, both have to have the same pmks.


espnow.lmk(peer_mac, local_key)

Setting the local master key.
16 characters
When transmitting between 2 peers, both have to have the same lmks.
Setting the local_key = None to disable encryption.


espnow.add_peer(peer_mac, [local_key])

Self-explanatory


def send_cb(peer_mac, success)
def recv_cb(peer_mac, msg)

espnow.send(peer_mac, msg)
espnow.on_send(send_cb)
espnow.on_recv(recv_cb)

When peer_mac is set to None, broadcast to all peers


peer_count()

Return (all_count, encrypted_count)


version()

Return 1 for esp32, 0 for esp8266


Bugs/Caveats:

  • Upon changing the pmk, all local peers' lmks have to be re-applied. Otherwise the system will continue to use the old encryption setup by the previous pmk.
  • Role/interface index(esp8266)/channel setting in SDK don't have effect. Although you need to personally make sure both devices are on the same wifi channel.
  • Device who add peer with lmk can still receive unencrypted message from the added peer
  • There is no get_lmk() from peer because of bugs in the SDK.
  • Other API bugs and their workarounds are documented in the code.

IMHO documentations from Espressif are terrible. Both SDKs are buggy as hell. API behave not as described. Many settings don't even have effect...

nickzoic and others added 12 commits September 9, 2018 23:04
(cherry picked from commit 00b709d2c0846b76d2b8614df39ff160e1044011)
(cherry picked from commit 6177511a5a4c5cd0ea25e7bd6999fa8c7353372e)
(cherry picked from commit 0918f0516f6906d167c3deed101cff9c993db2a8)
(cherry picked from commit 3036a8bcacca75300010e0e1ceec9f20c3880023)
workarounds:

* esp_now_send() will send from whatever IF that is active/available
* esp_now_mod_peer() will not crash the system
@nickzoic
Copy link
Contributor

nickzoic commented Sep 10, 2018 via email

@shawwwn
Copy link
Author

shawwwn commented Sep 10, 2018

@nickzoic no problem & thank you for the initial contribution!

@shawwwn
Copy link
Author

shawwwn commented Sep 10, 2018

Attaching a minimal example:

import network
w = network.WLAN(network.AP_IF) # doesn't matter if AP or STA
w.active(True)
w.config(channel=1) # do make sure all devices are on the same channel
print('self mac addr:', w.config('mac'))

def tx(result):
	mac, sent = result
	print("send: %s - %r" % (mac,sent))
def rx(result):
	mac, msg = result
	print("recv: %s - %s" % (mac,msg))

PEER_MAC1 = b'0\xad\xa3?>\x98'
PEER_MAC2 = b'b\x02\x95)\xda\x8c'

from esp import espnow
espnow.init()
espnow.on_send(tx)
espnow.on_recv(rx)
espnow.pmk('0123456789abcdef')
espnow.add_peer(PEER_MAC1, "1111222233334444")
espnow.add_peer(PEER_MAC2)
espnow.send(PEER_MAC1, 'hello encrypted peer')
espnow.send(PEER_MAC2, 'hello unencrypted peer')
espnow.send(None, "hello all peers")

@shawwwn shawwwn closed this Sep 26, 2018
@dpgeorge
Copy link
Member

dpgeorge commented Oct 5, 2018

@shawwwn I think this is a nice addition, it just might need a little bit of tidying up before merging. What's the reason to close it?

@eroncastro
Copy link

Any chance we could have this PR reopened and merged?

@nickzoic
Copy link
Contributor

nickzoic commented Jan 15, 2019 via email

@ThomasWaldmann
Copy link

I'ld also like to play with ESP-NOW (in Python, not being forced to do C). I just need a simple broadcaster and an arbitrary amount of no-setup receivers. Encryption does not matter, info is public and broadcasted anyway.

So, any chance to accelerate this and not let it rot for ages?

I can help, but not much with C stuff.

@ThomasWaldmann
Copy link

BTW, is there any way to receive this stuff without using an ESP, e.g. on a smartphone?

Copy link

@ThomasWaldmann ThomasWaldmann left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

some comments, take them rather as questions (i have no experience with espnow nor with micropython C programming).

maybe the answers could make it directly into the sourcecode as comments?

STATIC mp_obj_t espnow_init() {
if (!initialized) {
ESPNOW_EXCEPTIONS(esp_now_init());
ESPNOW_EXCEPTIONS(esp_now_set_self_role(ESP_NOW_ROLE_COMBO));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it have any disadvantages to use the COMBO mode if I only want to send (or receive)?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option is available only in esp8266 (always COMBO mode in esp32).
Just trying to be a bit more uniform :)

u8 *addr_buf;
mp_uint_t msg_len;
u8 *msg_buf = (u8 *)mp_obj_str_get_data(msg, &msg_len);
if (msg_len > ESP_NOW_MAX_DATA_LEN) mp_raise_ValueError("msg too long");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about "msg too long (%d bytes, but maximum is %d bytes)"?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine too me.


if (addr == mp_const_none) {
// send to all
ESPNOW_EXCEPTIONS(esp_now_send(NULL, msg_buf, msg_len));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esp8266: this is a broadcast? to everybody, not just registered peers, right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, to every registered peer if first argument is NULL.
So far it is the same as in esp32, however in esp32 you have to take care of IF first.
Please see my comment above.

bool first = true;
int new_if = -1;
if (addr == mp_const_none) {
// send to all

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esp32: hmm, this does not look like a broadcast (in the sense of "everybody, not just registered peers"), right?

see also the esp8266 implementation, which seems different.

is this a esp32 limitation?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

found the answer:

first one needs to explicitly add the broadcast "peer" like a unicast peer (so it will be in the list of peers):

https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/network/esp_now.html#add-paired-device

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is still the question somehow whether this is different between esp32 and esp8266 as the docs seem to describe the api as a unified thing for esp32 and esp8266, but this code deals differently with the "None/broadcast" case depending on the device.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esp32: hmm, this does not look like a broadcast (in the sense of "everybody, not just registered peers"), right?

see also the esp8266 implementation, which seems different.

is this a esp32 limitation?

What you are looking at is actually a workaround for a bug(?) in the api.

esp32:
When registering a peer, that peer will automatically bind to an available IF.
If, for some reason, that IF is down at the time when sending messages, then message won't be sent.

These extra code just making sure that message will be sent through as long as one IF is available.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is still the question somehow whether this is different between esp32 and esp8266 as the docs seem to describe the api as a unified thing for esp32 and esp8266, but this code deals differently with the "None/broadcast" case depending on the device.

Being a proprietary protocol, they differ drastically. I typically call the esp32 version of espnow a V2.

Copy link
Author

@shawwwn shawwwn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA

u8 *addr_buf;
mp_uint_t msg_len;
u8 *msg_buf = (u8 *)mp_obj_str_get_data(msg, &msg_len);
if (msg_len > ESP_NOW_MAX_DATA_LEN) mp_raise_ValueError("msg too long");
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks fine too me.

STATIC mp_obj_t espnow_init() {
if (!initialized) {
ESPNOW_EXCEPTIONS(esp_now_init());
ESPNOW_EXCEPTIONS(esp_now_set_self_role(ESP_NOW_ROLE_COMBO));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This option is available only in esp8266 (always COMBO mode in esp32).
Just trying to be a bit more uniform :)


if (addr == mp_const_none) {
// send to all
ESPNOW_EXCEPTIONS(esp_now_send(NULL, msg_buf, msg_len));
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, to every registered peer if first argument is NULL.
So far it is the same as in esp32, however in esp32 you have to take care of IF first.
Please see my comment above.

bool first = true;
int new_if = -1;
if (addr == mp_const_none) {
// send to all
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

esp32: hmm, this does not look like a broadcast (in the sense of "everybody, not just registered peers"), right?

see also the esp8266 implementation, which seems different.

is this a esp32 limitation?

What you are looking at is actually a workaround for a bug(?) in the api.

esp32:
When registering a peer, that peer will automatically bind to an available IF.
If, for some reason, that IF is down at the time when sending messages, then message won't be sent.

These extra code just making sure that message will be sent through as long as one IF is available.

bool first = true;
int new_if = -1;
if (addr == mp_const_none) {
// send to all
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there is still the question somehow whether this is different between esp32 and esp8266 as the docs seem to describe the api as a unified thing for esp32 and esp8266, but this code deals differently with the "None/broadcast" case depending on the device.

Being a proprietary protocol, they differ drastically. I typically call the esp32 version of espnow a V2.

@KKawase0104
Copy link

Hello,
I'm a MicroPython beginner and I have a question.
I added or changed the following code to the MicroPython(commit ddc9346).

master...shawwwn:espnow

Core panic occurred when run the following script on two ESP32.

Example:

import time
import network
from esp import espnow

def __tx(result):
    mac, sent = result
    print("send: %s - %r" % (mac,sent))

def __rx(result):
    mac, msg = result
    print("recv: %s - %s" % (mac,msg))

wlan = network.WLAN(network.AP_IF)
wlan.active(True)

espnow.init()
espnow.add_peer(b'\xff\xff\xff\xff\xff\xff')
espnow.on_send(__tx)
espnow.on_recv(__rx)

count = 0
while True:
    espnow.send(b'\xff\xff\xff\xff\xff\xff', str(count))
    count += 1
    time.sleep_ms(10)

Result:

Guru Meditation Error: Core  0 panic'ed (InstrFetchProhibited). Exception was unhandled.
Core 0 register dump:
PC      : 0x00810000  PS      : 0x00060e30  A0      : 0x800dcbcc  A1      : 0x3ffbfc00
A2      : 0x3f844040  A3      : 0x3ffbfc30  A4      : 0x785c6666  A5      : 0x3f8191f4
A6      : 0x0000000a  A7      : 0x3ffbfe50  A8      : 0x800dc800  A9      : 0x00810000
A10     : 0x3f844040  A11     : 0x3ffbfc30  A12     : 0x00060420  A13     : 0x00000001
A14     : 0x00000002  A15     : 0x3ffbfe30  SAR     : 0x00000020  EXCCAUSE: 0x00000014
EXCVADDR: 0x00810000  LBEG    : 0x40096348  LEND    : 0x40096353  LCOUNT  : 0x00000000

Backtrace: 0x00810000:0x3ffbfc00 0x400dcbc9:0x3ffbfc30 0x400e9d9a:0x3ffbfc70 0x400e0574:0x3ffbfd10 0x400dc51d:0x3ffbfd80 0x400dc561:0x3ffbfda0 0x400dd438:0x3ffbfdd0 0x400dd519:0x3ffbfe50 0x400ea3f5:0x3ffbfe70 0x400e0574:0x3ffbff10 0x400dc51d:0x3ffbff80 0x400dc54a:0x3ffbffa0 0x400dcfef:0x3ffbffc0 0x400e7335:0x3ffc0050 0x400e765e:0x3ffc0080 0x400dce9b:0x3ffc0160 0x400ea26a:0x3ffc01a0 0x400e0574:0x3ffc0240 0x400dc51d:0x3ffc02a0 0x400dc54a:0x3ffc02c0 0x400fa436:0x3ffc02e0 0x400fa690:0x3ffc0380 0x400f15ec:0x3ffc03c0 0x40091e35:0x3ffc03f0

Please let me know if I was using espnow module incorrectly.
Do you know what might be causing this?

Thanks for your time.

@KKawase0104
Copy link

Hello

I confirmed that the same error appears in the following version.
21e3426 refs/remotes/origin/espnow

Thank you

@KKawase0104
Copy link

@dpgeorge @shawwwn

Hello
I want to run this ESP-NOW for long time but core panic occur at receive side.
The reason looks like collection timing by garbage collector.

I changed recv_cb function in esp_espnow.c as bellow.

STATIC void IRAM_ATTR recv_cb(const uint8_t *macaddr, const uint8_t *data, int len) 
{
    if (recv_cb_obj != mp_const_none) {
#if 0
        mp_obj_tuple_t *msg = mp_obj_new_tuple(2, NULL);
        msg->items[0] = mp_obj_new_bytes(macaddr, ESP_NOW_ETH_ALEN);
        msg->items[1] = mp_obj_new_bytes(data, len);
        mp_sched_schedule(recv_cb_obj, msg);
#endif
        mp_sched_schedule(recv_cb_obj, mp_const_none);
    }
} 

It didn't stop in collection timing by garbage collector. After I tried some case, though I do not know if recv_cb is being called from an interrupt handler,
I want to know if it is correct to copy data by mp_obj_new_* and pass it to mp_sched_schedule, in interrupt handler.

Thank you very much for your time.

@KKawase0104
Copy link

@shawwwn @dpgeorge

Hello,

It has been stable by excluding "msg" and "items" from the target of garbage collection.

I compare with other source code, this way to use mp_obj_new_tuple method and mp_obj_new_bytes method is maybe correct.

I will check a little more.

Thank you for your time.

@shawwwn
Copy link
Author

shawwwn commented Apr 16, 2019

It has been stable by excluding "msg" and "items" from the target of garbage collection.

@KKawase0104 I think you maybe onto something.

There is no way to know for sure if the callback was indeed originated from an interrupt handler since espnow module is closed source.

Again, I don't know how garbage collector functions in regard to objects created inside interrupt handlers. Summoning @dpgeorge

@shawwwn shawwwn reopened this Apr 16, 2019
glenn20 added a commit to glenn20/micropython that referenced this pull request Nov 14, 2022
- Micropython v1.13 - v1.19 compatible
- Ring buffers in shared/runtime/ringbuffer.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers)
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class:
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.
- Add note on automatic channel assignment on routers.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.

Extend py/ringbuf.c and use that instead of my own implementation.
glenn20 added a commit to glenn20/micropython that referenced this pull request Nov 14, 2022
- Micropython v1.13 - v1.19 compatible
- Ring buffers in shared/runtime/ringbuffer.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers)
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class:
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.
- Add note on automatic channel assignment on routers.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.

Extend py/ringbuf.c and use that instead of my own implementation.
glenn20 added a commit to glenn20/micropython that referenced this pull request Dec 4, 2022
- Micropython v1.13 - v1.19 compatible
- Ring buffers in shared/runtime/ringbuffer.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers)
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class:
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.
- Add note on automatic channel assignment on routers.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.

Extend py/ringbuf.c and use that instead of my own implementation.
glenn20 added a commit to glenn20/micropython that referenced this pull request Dec 17, 2022
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Dec 17, 2022
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Jan 23, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Mar 4, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Mar 4, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Mar 4, 2023
Progress on ESPNow from
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

espnow: Support peer wifi signal strength (RSSI).

- Maintain a peer device table (espnow_singleton->peers_table):
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}
    where:
      peerX are bytes strings containing the mac address;
      rssi values of last msg received (dBm from -128 to 0); and
      time_ms is time message received (in milliseconds since boot)
- User access to the peers table via attribute: ESPNow.peers.
- The peer addresses returned by irecv() and recv() are now references
  to the keys in peers_dict (instead of new copies of the peer address).
- Add RSSI docs to docs/library/espnow.rst.
- MAC address values returned by irecv()/recv() are now refernces to
  the peerX byte strings in the peer device table.
- Add tests for the rssi functions.
- Also - fixes to make asyncio tests more robust.
- Rename get_singleton() _get_singleton().
- Tests: Disconnect STA before running tests: ESP8266 devices
  automatically re-connect to previous Access Points when STA_IF is set
  active. This makes receipt of messages unreliable.

esp/espnow: Add aioespnow.py module for asyncio.

- Add aioespnow.py to support asyncio.
  - Provides class AIOESPNow which extends the ESPNow interface with
     methods: airecv(), arecv() and asend(). Also supports "async for"
     iteration.
  - Docs and tests updated for AIO support.
- Buffer Protocol (Stream IO) support is deprecated with the new async
  interface.

esp/espnow: Mv ring_buffer.[ch] to shared/runtime.

De-duplicate the ring_buffer.[ch] code in esp32 and esp8266 by moving
them to shared/runtime/.

esp/espnow: Code rationalisation for esp32.

- Remove espnow_print() and espnow_version().
- Fix espnow_ioctl signature.

esp/espnow: Remove buffer protocol support code.

esp/espnow: Fix docs: RSSI is only on ESP32.

- Fix docs to clarify that the peers table (supporting RSSI monitoring
  is not availablle on the ESP8266.
- Minor re-ordering of sections (more logical).
- Add advice on call to w0.disconnect() after w0.active(True) for
  esp8266.
- Minor fixes to example code.

esp/espnow: aioespnow.py: Support user extensions.

- Use self.irecv() instead of super().irecv() so derived classes
  can override irecv()/recv()/send().
- Bugfix: drop call to self.init() in __aiter__() method.
- tests/multi_espnow/: Arg to echo_test should be a bytestring.
- tests/multi_espnow/: Add new test for memory leaks.

esp/espnow: esp_espnow.c: More code streamlining.

- Add NEW_TUPLE macro as convenience 'function' for creating tuples.
- Remove unnecessary dummy espnow_stream_read()/wite() functions.
- Minor reorg of code for better logical grouping.

esp/espnow: Reimplement RSSI support.

Streamline the RSSI support. Do not update the device-table from
recv_cb(). Instead, save the rssi and timestamp in the packet buffer
and update the device-table when the packet is read from
irecv()/recv(). Also simplifies the device-table handling code.
Docs updated to reflect the changes:
 - RSSI values of first messages from peer are no longer lost
 - Device-table is only updated when irecv()/recv() read the message
    from the buffer.
Also for IDF < 4.2.0, rssi needs to be offset by 100.

esp/espnow: Disallow user changes to peer table.

Set map->is_fixed=1 to prevent changes by user code. Unset it only when
we are inserting new peers.

Also clean out a small bit of cruft in esp_espnow.c.

esp/espnow: Bugfix in get_peers() with multicast addresses.

get_peers() would cause reboot if a multi-cast address was registered
with add_peer() due to inconsistent use of peer_count.

esp/espnow: Wait forever in irecv()/recv() if timeout_ms < 0.

ring_buffer.c: Disable the timeout if timeout_ms < 0.
irecv() and recv() will wait forever if timeout is < 0.

esp/espnow: Set espnow TX rate with config(rate=).

Use ESPNow.config(rate=n) to set the bit rate for espnow transmissions,
where n is the numeric value of one of the rates defined in
"enum wifi_phy_rate_t" (see espressif wifi docs). Supported for
ESP IDF version >= v4.3.0.

esp/espnow: Irecv() returns msg as byte string.

Make msg returned from irecv() a byte string (instead of bytearray)
for consistency with recv() and convenience of users.

esp/espnow: Minor code reduction for espnow_ioctl().

Also tweaks to espnow_iternext() and espnow_attr().

esp/espnow: AIOEspnow: drop singleton decorator.

We can let AIOESPNow be a regular class rather than a singleton since
it holds no additional state (over the ESPNow base class singleton).
Singleton by decorator function makes it difficult to derive child
classes from AIOESPNow and micropython does not support common method
for creating singletons (overriding __new__() method).

esp/espnow: Code reduction on esp8266.

Rationalisation of espnow_irecv() code for some minor code size savings.

esp/espnow: Fix misleading comment.

esp/espnow: ioctl() return 0 if not initialised.

Previously, would throw a NOT_INIT exception if called before init()
or after deinit(). Now returns 0 if not initialised - ie. not ready for
read or write.

espnow: API changes on ESP32 before merge with main branch.

Significant overhaul to address API change requests before merge into
main branch. This has required some significant rewrites to maintain
as much compatibility with ESP8266 as possible (due to code space
constraints on ESP8266).

- Replace init()/deinit() with active(True/False).
- Change .config(on_recv) to .irq(cb).
- Change .poll() to .any(). Following existing practice in uart,
  stm32/usb and pyb_can.
- Extend recv() method to support alloc-free reads by passing storage
  buffers as optional second argument (buffers) (following approach
  by pyb/can module).
- Delete irecv() method. Alloc-free reads are now supported with the
  recv() method.
- Add optional size argument to send() which specifies the number of
  bytes from `message` which will be sent. If not provided or set to
  None, all the bytes in `message` will be sent.
- Change .peers attribute to .peers_table in attempt to reduce
  confusion with get_peers() method.
- Remove iterator support as requested pre merge with main branch.
- Add missing ESP_NOW_ETH_ALEN as module constant.

Minor refactoring:
- _get_singleton() functions for compatibility with changes for the
   esp8266.
- Minor changes to rssi update functions.
- Refactor for send() method to eliminate _do_espnow_send().

espnow: API changes on ESP8266 before merge with main branch.

Significant overhaul to address API change requests. This has required
some substantial rewrites to fit within the code space constraints of
the ESP8266.

- Change init()/deinit() to active(T/F) and config(). Replace
  init()/deinit() with active(True/False) and reintroduce the config()
  method.
- recv(timeout) returns [peer, msg] where peer and msg are newly
  alloced byte strings.
- recv(timeout, buffers) where buffers is a list of bytearrays which
  will be used to hold the peer and message data.
- Remove irecv() method.

Other changes to reduce text size to accommodate changes required
above.

espnow: Add espnowio python module.

Defines ESPNowIO child class with irecv() method to support alloc-free
recv() calls (backward compatible with pre-release irecv() method).

Also includes:
- init() and deinit() methods for backward compatibility with
  pre-release API, and
- Iterator support: "for peer, msg in e: print(peer, msg)"

espnow: Update tests to new API.

Depends on aioespnow module for convenient alloc-free read API.

espnow: Update docs for new API changes.

espnow: Docs: note that size arg of send() is ESP32 only.

espnow: Tests: Bugfix for rssi test.

Use time.ticks_ms() instead of time.time() as sanity check on rssi
timestamp. It seems that since v1.19.1 time.time() is not synched
with time.ticks_ms()/mp_hal_ticks_ms().

espnow: Change recv() to recvinto() on ESP32/8266.

Reduce code complexity by replacing recv() method with recvinto()
so that buffers *must* be supplied to read messages:
  recvinto([peer, msg], timeout_ms)
where peer and msg are bytearrays of length 6 and 250 respectively.
Timeout_ms is an optional timeout.
If the first arg to recvinto() is a list with at least 4 elements, the
rssi and timestamp values will be inserted as the 3rd and 4th elements
(after peer and masg).

Use a convenience method in the espnow.py module for a convenient
user interface: recv() and irecv().

Necessitates that we revert irq() method to simpler form. support
for the callback form will be moved to espnow.py.

Also, remove size argument for send().

Use MICROPY_ESPNOW_RSSI pre-processor directive to include/exclude code
to compute and track rssi.

Use MICROPY_ESPNOW_EXTRA_PEER_METHODS pre-processor macro to
conditionally compile in the mod_peer(), get_peer() and peer_count()
methods.

espnow: Add asyncio support for ESP8266 GENERIC target.

- esp_espnow.c: add stream ioctl() support (espnow_stream_ioctl()) for
  asyncio support.
- Add aioespnow.py to ports/esp8266/modules

espnow: Move espnow module to top-level and rename _espnow.

Move the espnow module from esp.espnow to _espnow. As a submodule
of the esp module we lost some flexibility.

Also rename espnowio.py to espnow.py so that "import espnow" will
import the python wrapper module, which will then import _espnow.

espnow: Update tests and docs for recent changes.

Document recvinto() method and methods exposed in espnow.py:
- recv(), irecv(), ...

Update tests for new API.

espnow: Restore workable .irq() and .on_recv() apis.

- Rename _espnow.irq() to _espnow.on_recv() as low-level callback api.
- Add ESPNow.irq() method to espnow.py module which supports api
  requested by @dpgeorge.
- For on_recv(): can supply argument to callback as optional second
  argument eg:
    def cb(e):
        print(e.irecv(0))
    e.on_recv(cb, e)
- Tests: 15_irq_test.py: add tests for .irq() and .on_recv().
- Docs: Add section for "Callback Methods".

espnow: Tests: rename and resequence tests following recent changes.

espnow: Drop get argument of config().

espnow: Restore None argument handling for add_peer().

add_peer() is documented to permit usage:
- add_peer(mac, None, None, network.AP_IF)
Restore the permitted use of None to indicate that the arg should be left
as is.

espnow: Docs: clarify section on Espnow and Wifi Operation.

Clarify the issues associated with the power saving mode and channel
settings when using ESPNow while also connected to a Wifi Access Point.

espnow: Docs: Include advice about wifi AP disconnects.

espnow: ESP8266 fix for changes to gc_pool_start/end.

espnow: Docs: Document issue with receiving msgs from esp8266 devices.

Add advice on receiving ESPNow messages from esp8266 devices while
connected to a wifi network on STA_IF.

espnow: ESP8266: Add wifi ps_mode support for esp8266.

espnow: Change ps_mode to pm.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.

espnow: Rebase against main branch.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 1, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 1, 2023
Progress on ESPNow from
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

espnow: Support peer wifi signal strength (RSSI).

- Maintain a peer device table (espnow_singleton->peers_table):
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}
    where:
      peerX are bytes strings containing the mac address;
      rssi values of last msg received (dBm from -128 to 0); and
      time_ms is time message received (in milliseconds since boot)
- User access to the peers table via attribute: ESPNow.peers.
- The peer addresses returned by irecv() and recv() are now references
  to the keys in peers_dict (instead of new copies of the peer address).
- Add RSSI docs to docs/library/espnow.rst.
- MAC address values returned by irecv()/recv() are now refernces to
  the peerX byte strings in the peer device table.
- Add tests for the rssi functions.
- Also - fixes to make asyncio tests more robust.
- Rename get_singleton() _get_singleton().
- Tests: Disconnect STA before running tests: ESP8266 devices
  automatically re-connect to previous Access Points when STA_IF is set
  active. This makes receipt of messages unreliable.

esp/espnow: Add aioespnow.py module for asyncio.

- Add aioespnow.py to support asyncio.
  - Provides class AIOESPNow which extends the ESPNow interface with
     methods: airecv(), arecv() and asend(). Also supports "async for"
     iteration.
  - Docs and tests updated for AIO support.
- Buffer Protocol (Stream IO) support is deprecated with the new async
  interface.

esp/espnow: Mv ring_buffer.[ch] to shared/runtime.

De-duplicate the ring_buffer.[ch] code in esp32 and esp8266 by moving
them to shared/runtime/.

esp/espnow: Code rationalisation for esp32.

- Remove espnow_print() and espnow_version().
- Fix espnow_ioctl signature.

esp/espnow: Remove buffer protocol support code.

esp/espnow: Fix docs: RSSI is only on ESP32.

- Fix docs to clarify that the peers table (supporting RSSI monitoring
  is not availablle on the ESP8266.
- Minor re-ordering of sections (more logical).
- Add advice on call to w0.disconnect() after w0.active(True) for
  esp8266.
- Minor fixes to example code.

esp/espnow: aioespnow.py: Support user extensions.

- Use self.irecv() instead of super().irecv() so derived classes
  can override irecv()/recv()/send().
- Bugfix: drop call to self.init() in __aiter__() method.
- tests/multi_espnow/: Arg to echo_test should be a bytestring.
- tests/multi_espnow/: Add new test for memory leaks.

esp/espnow: esp_espnow.c: More code streamlining.

- Add NEW_TUPLE macro as convenience 'function' for creating tuples.
- Remove unnecessary dummy espnow_stream_read()/wite() functions.
- Minor reorg of code for better logical grouping.

esp/espnow: Reimplement RSSI support.

Streamline the RSSI support. Do not update the device-table from
recv_cb(). Instead, save the rssi and timestamp in the packet buffer
and update the device-table when the packet is read from
irecv()/recv(). Also simplifies the device-table handling code.
Docs updated to reflect the changes:
 - RSSI values of first messages from peer are no longer lost
 - Device-table is only updated when irecv()/recv() read the message
    from the buffer.
Also for IDF < 4.2.0, rssi needs to be offset by 100.

esp/espnow: Disallow user changes to peer table.

Set map->is_fixed=1 to prevent changes by user code. Unset it only when
we are inserting new peers.

Also clean out a small bit of cruft in esp_espnow.c.

esp/espnow: Bugfix in get_peers() with multicast addresses.

get_peers() would cause reboot if a multi-cast address was registered
with add_peer() due to inconsistent use of peer_count.

esp/espnow: Wait forever in irecv()/recv() if timeout_ms < 0.

ring_buffer.c: Disable the timeout if timeout_ms < 0.
irecv() and recv() will wait forever if timeout is < 0.

esp/espnow: Set espnow TX rate with config(rate=).

Use ESPNow.config(rate=n) to set the bit rate for espnow transmissions,
where n is the numeric value of one of the rates defined in
"enum wifi_phy_rate_t" (see espressif wifi docs). Supported for
ESP IDF version >= v4.3.0.

esp/espnow: Irecv() returns msg as byte string.

Make msg returned from irecv() a byte string (instead of bytearray)
for consistency with recv() and convenience of users.

esp/espnow: Minor code reduction for espnow_ioctl().

Also tweaks to espnow_iternext() and espnow_attr().

esp/espnow: AIOEspnow: drop singleton decorator.

We can let AIOESPNow be a regular class rather than a singleton since
it holds no additional state (over the ESPNow base class singleton).
Singleton by decorator function makes it difficult to derive child
classes from AIOESPNow and micropython does not support common method
for creating singletons (overriding __new__() method).

esp/espnow: Code reduction on esp8266.

Rationalisation of espnow_irecv() code for some minor code size savings.

esp/espnow: Fix misleading comment.

esp/espnow: ioctl() return 0 if not initialised.

Previously, would throw a NOT_INIT exception if called before init()
or after deinit(). Now returns 0 if not initialised - ie. not ready for
read or write.

espnow: API changes on ESP32 before merge with main branch.

Significant overhaul to address API change requests before merge into
main branch. This has required some significant rewrites to maintain
as much compatibility with ESP8266 as possible (due to code space
constraints on ESP8266).

- Replace init()/deinit() with active(True/False).
- Change .config(on_recv) to .irq(cb).
- Change .poll() to .any(). Following existing practice in uart,
  stm32/usb and pyb_can.
- Extend recv() method to support alloc-free reads by passing storage
  buffers as optional second argument (buffers) (following approach
  by pyb/can module).
- Delete irecv() method. Alloc-free reads are now supported with the
  recv() method.
- Add optional size argument to send() which specifies the number of
  bytes from `message` which will be sent. If not provided or set to
  None, all the bytes in `message` will be sent.
- Change .peers attribute to .peers_table in attempt to reduce
  confusion with get_peers() method.
- Remove iterator support as requested pre merge with main branch.
- Add missing ESP_NOW_ETH_ALEN as module constant.

Minor refactoring:
- _get_singleton() functions for compatibility with changes for the
   esp8266.
- Minor changes to rssi update functions.
- Refactor for send() method to eliminate _do_espnow_send().

espnow: API changes on ESP8266 before merge with main branch.

Significant overhaul to address API change requests. This has required
some substantial rewrites to fit within the code space constraints of
the ESP8266.

- Change init()/deinit() to active(T/F) and config(). Replace
  init()/deinit() with active(True/False) and reintroduce the config()
  method.
- recv(timeout) returns [peer, msg] where peer and msg are newly
  alloced byte strings.
- recv(timeout, buffers) where buffers is a list of bytearrays which
  will be used to hold the peer and message data.
- Remove irecv() method.

Other changes to reduce text size to accommodate changes required
above.

espnow: Add espnowio python module.

Defines ESPNowIO child class with irecv() method to support alloc-free
recv() calls (backward compatible with pre-release irecv() method).

Also includes:
- init() and deinit() methods for backward compatibility with
  pre-release API, and
- Iterator support: "for peer, msg in e: print(peer, msg)"

espnow: Update tests to new API.

Depends on aioespnow module for convenient alloc-free read API.

espnow: Update docs for new API changes.

espnow: Docs: note that size arg of send() is ESP32 only.

espnow: Tests: Bugfix for rssi test.

Use time.ticks_ms() instead of time.time() as sanity check on rssi
timestamp. It seems that since v1.19.1 time.time() is not synched
with time.ticks_ms()/mp_hal_ticks_ms().

espnow: Change recv() to recvinto() on ESP32/8266.

Reduce code complexity by replacing recv() method with recvinto()
so that buffers *must* be supplied to read messages:
  recvinto([peer, msg], timeout_ms)
where peer and msg are bytearrays of length 6 and 250 respectively.
Timeout_ms is an optional timeout.
If the first arg to recvinto() is a list with at least 4 elements, the
rssi and timestamp values will be inserted as the 3rd and 4th elements
(after peer and masg).

Use a convenience method in the espnow.py module for a convenient
user interface: recv() and irecv().

Necessitates that we revert irq() method to simpler form. support
for the callback form will be moved to espnow.py.

Also, remove size argument for send().

Use MICROPY_ESPNOW_RSSI pre-processor directive to include/exclude code
to compute and track rssi.

Use MICROPY_ESPNOW_EXTRA_PEER_METHODS pre-processor macro to
conditionally compile in the mod_peer(), get_peer() and peer_count()
methods.

espnow: Add asyncio support for ESP8266 GENERIC target.

- esp_espnow.c: add stream ioctl() support (espnow_stream_ioctl()) for
  asyncio support.
- Add aioespnow.py to ports/esp8266/modules

espnow: Move espnow module to top-level and rename _espnow.

Move the espnow module from esp.espnow to _espnow. As a submodule
of the esp module we lost some flexibility.

Also rename espnowio.py to espnow.py so that "import espnow" will
import the python wrapper module, which will then import _espnow.

espnow: Update tests and docs for recent changes.

Document recvinto() method and methods exposed in espnow.py:
- recv(), irecv(), ...

Update tests for new API.

espnow: Restore workable .irq() and .on_recv() apis.

- Rename _espnow.irq() to _espnow.on_recv() as low-level callback api.
- Add ESPNow.irq() method to espnow.py module which supports api
  requested by @dpgeorge.
- For on_recv(): can supply argument to callback as optional second
  argument eg:
    def cb(e):
        print(e.irecv(0))
    e.on_recv(cb, e)
- Tests: 15_irq_test.py: add tests for .irq() and .on_recv().
- Docs: Add section for "Callback Methods".

espnow: Tests: rename and resequence tests following recent changes.

espnow: Drop get argument of config().

espnow: Restore None argument handling for add_peer().

add_peer() is documented to permit usage:
- add_peer(mac, None, None, network.AP_IF)
Restore the permitted use of None to indicate that the arg should be left
as is.

espnow: Docs: clarify section on Espnow and Wifi Operation.

Clarify the issues associated with the power saving mode and channel
settings when using ESPNow while also connected to a Wifi Access Point.

espnow: Docs: Include advice about wifi AP disconnects.

espnow: ESP8266 fix for changes to gc_pool_start/end.

espnow: Docs: Document issue with receiving msgs from esp8266 devices.

Add advice on receiving ESPNow messages from esp8266 devices while
connected to a wifi network on STA_IF.

espnow: ESP8266: Add wifi ps_mode support for esp8266.

espnow: Change ps_mode to pm.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.

espnow: Rebase against main branch.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 2, 2023
- Micropython v1.13 - v1.19 compatible
- Ring buffers in shared/runtime/ringbuffer.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers)
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class:
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.
- Add note on automatic channel assignment on routers.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.

Extend py/ringbuf.c and use that instead of my own implementation.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 9, 2023
Progress on ESPNow from
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

espnow: Support peer wifi signal strength (RSSI).

- Maintain a peer device table (espnow_singleton->peers_table):
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}
    where:
      peerX are bytes strings containing the mac address;
      rssi values of last msg received (dBm from -128 to 0); and
      time_ms is time message received (in milliseconds since boot)
- User access to the peers table via attribute: ESPNow.peers.
- The peer addresses returned by irecv() and recv() are now references
  to the keys in peers_dict (instead of new copies of the peer address).
- Add RSSI docs to docs/library/espnow.rst.
- MAC address values returned by irecv()/recv() are now refernces to
  the peerX byte strings in the peer device table.
- Add tests for the rssi functions.
- Also - fixes to make asyncio tests more robust.
- Rename get_singleton() _get_singleton().
- Tests: Disconnect STA before running tests: ESP8266 devices
  automatically re-connect to previous Access Points when STA_IF is set
  active. This makes receipt of messages unreliable.

esp/espnow: Add aioespnow.py module for asyncio.

- Add aioespnow.py to support asyncio.
  - Provides class AIOESPNow which extends the ESPNow interface with
     methods: airecv(), arecv() and asend(). Also supports "async for"
     iteration.
  - Docs and tests updated for AIO support.
- Buffer Protocol (Stream IO) support is deprecated with the new async
  interface.

esp/espnow: Mv ring_buffer.[ch] to shared/runtime.

De-duplicate the ring_buffer.[ch] code in esp32 and esp8266 by moving
them to shared/runtime/.

esp/espnow: Code rationalisation for esp32.

- Remove espnow_print() and espnow_version().
- Fix espnow_ioctl signature.

esp/espnow: Remove buffer protocol support code.

esp/espnow: Fix docs: RSSI is only on ESP32.

- Fix docs to clarify that the peers table (supporting RSSI monitoring
  is not availablle on the ESP8266.
- Minor re-ordering of sections (more logical).
- Add advice on call to w0.disconnect() after w0.active(True) for
  esp8266.
- Minor fixes to example code.

esp/espnow: aioespnow.py: Support user extensions.

- Use self.irecv() instead of super().irecv() so derived classes
  can override irecv()/recv()/send().
- Bugfix: drop call to self.init() in __aiter__() method.
- tests/multi_espnow/: Arg to echo_test should be a bytestring.
- tests/multi_espnow/: Add new test for memory leaks.

esp/espnow: esp_espnow.c: More code streamlining.

- Add NEW_TUPLE macro as convenience 'function' for creating tuples.
- Remove unnecessary dummy espnow_stream_read()/wite() functions.
- Minor reorg of code for better logical grouping.

esp/espnow: Reimplement RSSI support.

Streamline the RSSI support. Do not update the device-table from
recv_cb(). Instead, save the rssi and timestamp in the packet buffer
and update the device-table when the packet is read from
irecv()/recv(). Also simplifies the device-table handling code.
Docs updated to reflect the changes:
 - RSSI values of first messages from peer are no longer lost
 - Device-table is only updated when irecv()/recv() read the message
    from the buffer.
Also for IDF < 4.2.0, rssi needs to be offset by 100.

esp/espnow: Disallow user changes to peer table.

Set map->is_fixed=1 to prevent changes by user code. Unset it only when
we are inserting new peers.

Also clean out a small bit of cruft in esp_espnow.c.

esp/espnow: Bugfix in get_peers() with multicast addresses.

get_peers() would cause reboot if a multi-cast address was registered
with add_peer() due to inconsistent use of peer_count.

esp/espnow: Wait forever in irecv()/recv() if timeout_ms < 0.

ring_buffer.c: Disable the timeout if timeout_ms < 0.
irecv() and recv() will wait forever if timeout is < 0.

esp/espnow: Set espnow TX rate with config(rate=).

Use ESPNow.config(rate=n) to set the bit rate for espnow transmissions,
where n is the numeric value of one of the rates defined in
"enum wifi_phy_rate_t" (see espressif wifi docs). Supported for
ESP IDF version >= v4.3.0.

esp/espnow: Irecv() returns msg as byte string.

Make msg returned from irecv() a byte string (instead of bytearray)
for consistency with recv() and convenience of users.

esp/espnow: Minor code reduction for espnow_ioctl().

Also tweaks to espnow_iternext() and espnow_attr().

esp/espnow: AIOEspnow: drop singleton decorator.

We can let AIOESPNow be a regular class rather than a singleton since
it holds no additional state (over the ESPNow base class singleton).
Singleton by decorator function makes it difficult to derive child
classes from AIOESPNow and micropython does not support common method
for creating singletons (overriding __new__() method).

esp/espnow: Code reduction on esp8266.

Rationalisation of espnow_irecv() code for some minor code size savings.

esp/espnow: Fix misleading comment.

esp/espnow: ioctl() return 0 if not initialised.

Previously, would throw a NOT_INIT exception if called before init()
or after deinit(). Now returns 0 if not initialised - ie. not ready for
read or write.

espnow: API changes on ESP32 before merge with main branch.

Significant overhaul to address API change requests before merge into
main branch. This has required some significant rewrites to maintain
as much compatibility with ESP8266 as possible (due to code space
constraints on ESP8266).

- Replace init()/deinit() with active(True/False).
- Change .config(on_recv) to .irq(cb).
- Change .poll() to .any(). Following existing practice in uart,
  stm32/usb and pyb_can.
- Extend recv() method to support alloc-free reads by passing storage
  buffers as optional second argument (buffers) (following approach
  by pyb/can module).
- Delete irecv() method. Alloc-free reads are now supported with the
  recv() method.
- Add optional size argument to send() which specifies the number of
  bytes from `message` which will be sent. If not provided or set to
  None, all the bytes in `message` will be sent.
- Change .peers attribute to .peers_table in attempt to reduce
  confusion with get_peers() method.
- Remove iterator support as requested pre merge with main branch.
- Add missing ESP_NOW_ETH_ALEN as module constant.

Minor refactoring:
- _get_singleton() functions for compatibility with changes for the
   esp8266.
- Minor changes to rssi update functions.
- Refactor for send() method to eliminate _do_espnow_send().

espnow: API changes on ESP8266 before merge with main branch.

Significant overhaul to address API change requests. This has required
some substantial rewrites to fit within the code space constraints of
the ESP8266.

- Change init()/deinit() to active(T/F) and config(). Replace
  init()/deinit() with active(True/False) and reintroduce the config()
  method.
- recv(timeout) returns [peer, msg] where peer and msg are newly
  alloced byte strings.
- recv(timeout, buffers) where buffers is a list of bytearrays which
  will be used to hold the peer and message data.
- Remove irecv() method.

Other changes to reduce text size to accommodate changes required
above.

espnow: Add espnowio python module.

Defines ESPNowIO child class with irecv() method to support alloc-free
recv() calls (backward compatible with pre-release irecv() method).

Also includes:
- init() and deinit() methods for backward compatibility with
  pre-release API, and
- Iterator support: "for peer, msg in e: print(peer, msg)"

espnow: Update tests to new API.

Depends on aioespnow module for convenient alloc-free read API.

espnow: Update docs for new API changes.

espnow: Docs: note that size arg of send() is ESP32 only.

espnow: Tests: Bugfix for rssi test.

Use time.ticks_ms() instead of time.time() as sanity check on rssi
timestamp. It seems that since v1.19.1 time.time() is not synched
with time.ticks_ms()/mp_hal_ticks_ms().

espnow: Change recv() to recvinto() on ESP32/8266.

Reduce code complexity by replacing recv() method with recvinto()
so that buffers *must* be supplied to read messages:
  recvinto([peer, msg], timeout_ms)
where peer and msg are bytearrays of length 6 and 250 respectively.
Timeout_ms is an optional timeout.
If the first arg to recvinto() is a list with at least 4 elements, the
rssi and timestamp values will be inserted as the 3rd and 4th elements
(after peer and masg).

Use a convenience method in the espnow.py module for a convenient
user interface: recv() and irecv().

Necessitates that we revert irq() method to simpler form. support
for the callback form will be moved to espnow.py.

Also, remove size argument for send().

Use MICROPY_ESPNOW_RSSI pre-processor directive to include/exclude code
to compute and track rssi.

Use MICROPY_ESPNOW_EXTRA_PEER_METHODS pre-processor macro to
conditionally compile in the mod_peer(), get_peer() and peer_count()
methods.

espnow: Add asyncio support for ESP8266 GENERIC target.

- esp_espnow.c: add stream ioctl() support (espnow_stream_ioctl()) for
  asyncio support.
- Add aioespnow.py to ports/esp8266/modules

espnow: Move espnow module to top-level and rename _espnow.

Move the espnow module from esp.espnow to _espnow. As a submodule
of the esp module we lost some flexibility.

Also rename espnowio.py to espnow.py so that "import espnow" will
import the python wrapper module, which will then import _espnow.

espnow: Update tests and docs for recent changes.

Document recvinto() method and methods exposed in espnow.py:
- recv(), irecv(), ...

Update tests for new API.

espnow: Restore workable .irq() and .on_recv() apis.

- Rename _espnow.irq() to _espnow.on_recv() as low-level callback api.
- Add ESPNow.irq() method to espnow.py module which supports api
  requested by @dpgeorge.
- For on_recv(): can supply argument to callback as optional second
  argument eg:
    def cb(e):
        print(e.irecv(0))
    e.on_recv(cb, e)
- Tests: 15_irq_test.py: add tests for .irq() and .on_recv().
- Docs: Add section for "Callback Methods".

espnow: Tests: rename and resequence tests following recent changes.

espnow: Drop get argument of config().

espnow: Restore None argument handling for add_peer().

add_peer() is documented to permit usage:
- add_peer(mac, None, None, network.AP_IF)
Restore the permitted use of None to indicate that the arg should be left
as is.

espnow: Docs: clarify section on Espnow and Wifi Operation.

Clarify the issues associated with the power saving mode and channel
settings when using ESPNow while also connected to a Wifi Access Point.

espnow: Docs: Include advice about wifi AP disconnects.

espnow: ESP8266 fix for changes to gc_pool_start/end.

espnow: Docs: Document issue with receiving msgs from esp8266 devices.

Add advice on receiving ESPNow messages from esp8266 devices while
connected to a wifi network on STA_IF.

espnow: ESP8266: Add wifi ps_mode support for esp8266.

espnow: Change ps_mode to pm.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.

espnow: Rebase against main branch.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 9, 2023
Progress on ESPNow from
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

espnow: Support peer wifi signal strength (RSSI).

- Maintain a peer device table (espnow_singleton->peers_table):
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}
    where:
      peerX are bytes strings containing the mac address;
      rssi values of last msg received (dBm from -128 to 0); and
      time_ms is time message received (in milliseconds since boot)
- User access to the peers table via attribute: ESPNow.peers.
- The peer addresses returned by irecv() and recv() are now references
  to the keys in peers_dict (instead of new copies of the peer address).
- Add RSSI docs to docs/library/espnow.rst.
- MAC address values returned by irecv()/recv() are now refernces to
  the peerX byte strings in the peer device table.
- Add tests for the rssi functions.
- Also - fixes to make asyncio tests more robust.
- Rename get_singleton() _get_singleton().
- Tests: Disconnect STA before running tests: ESP8266 devices
  automatically re-connect to previous Access Points when STA_IF is set
  active. This makes receipt of messages unreliable.

esp/espnow: Add aioespnow.py module for asyncio.

- Add aioespnow.py to support asyncio.
  - Provides class AIOESPNow which extends the ESPNow interface with
     methods: airecv(), arecv() and asend(). Also supports "async for"
     iteration.
  - Docs and tests updated for AIO support.
- Buffer Protocol (Stream IO) support is deprecated with the new async
  interface.

esp/espnow: Mv ring_buffer.[ch] to shared/runtime.

De-duplicate the ring_buffer.[ch] code in esp32 and esp8266 by moving
them to shared/runtime/.

esp/espnow: Code rationalisation for esp32.

- Remove espnow_print() and espnow_version().
- Fix espnow_ioctl signature.

esp/espnow: Remove buffer protocol support code.

esp/espnow: Fix docs: RSSI is only on ESP32.

- Fix docs to clarify that the peers table (supporting RSSI monitoring
  is not availablle on the ESP8266.
- Minor re-ordering of sections (more logical).
- Add advice on call to w0.disconnect() after w0.active(True) for
  esp8266.
- Minor fixes to example code.

esp/espnow: aioespnow.py: Support user extensions.

- Use self.irecv() instead of super().irecv() so derived classes
  can override irecv()/recv()/send().
- Bugfix: drop call to self.init() in __aiter__() method.
- tests/multi_espnow/: Arg to echo_test should be a bytestring.
- tests/multi_espnow/: Add new test for memory leaks.

esp/espnow: esp_espnow.c: More code streamlining.

- Add NEW_TUPLE macro as convenience 'function' for creating tuples.
- Remove unnecessary dummy espnow_stream_read()/wite() functions.
- Minor reorg of code for better logical grouping.

esp/espnow: Reimplement RSSI support.

Streamline the RSSI support. Do not update the device-table from
recv_cb(). Instead, save the rssi and timestamp in the packet buffer
and update the device-table when the packet is read from
irecv()/recv(). Also simplifies the device-table handling code.
Docs updated to reflect the changes:
 - RSSI values of first messages from peer are no longer lost
 - Device-table is only updated when irecv()/recv() read the message
    from the buffer.
Also for IDF < 4.2.0, rssi needs to be offset by 100.

esp/espnow: Disallow user changes to peer table.

Set map->is_fixed=1 to prevent changes by user code. Unset it only when
we are inserting new peers.

Also clean out a small bit of cruft in esp_espnow.c.

esp/espnow: Bugfix in get_peers() with multicast addresses.

get_peers() would cause reboot if a multi-cast address was registered
with add_peer() due to inconsistent use of peer_count.

esp/espnow: Wait forever in irecv()/recv() if timeout_ms < 0.

ring_buffer.c: Disable the timeout if timeout_ms < 0.
irecv() and recv() will wait forever if timeout is < 0.

esp/espnow: Set espnow TX rate with config(rate=).

Use ESPNow.config(rate=n) to set the bit rate for espnow transmissions,
where n is the numeric value of one of the rates defined in
"enum wifi_phy_rate_t" (see espressif wifi docs). Supported for
ESP IDF version >= v4.3.0.

esp/espnow: Irecv() returns msg as byte string.

Make msg returned from irecv() a byte string (instead of bytearray)
for consistency with recv() and convenience of users.

esp/espnow: Minor code reduction for espnow_ioctl().

Also tweaks to espnow_iternext() and espnow_attr().

esp/espnow: AIOEspnow: drop singleton decorator.

We can let AIOESPNow be a regular class rather than a singleton since
it holds no additional state (over the ESPNow base class singleton).
Singleton by decorator function makes it difficult to derive child
classes from AIOESPNow and micropython does not support common method
for creating singletons (overriding __new__() method).

esp/espnow: Code reduction on esp8266.

Rationalisation of espnow_irecv() code for some minor code size savings.

esp/espnow: Fix misleading comment.

esp/espnow: ioctl() return 0 if not initialised.

Previously, would throw a NOT_INIT exception if called before init()
or after deinit(). Now returns 0 if not initialised - ie. not ready for
read or write.

espnow: API changes on ESP32 before merge with main branch.

Significant overhaul to address API change requests before merge into
main branch. This has required some significant rewrites to maintain
as much compatibility with ESP8266 as possible (due to code space
constraints on ESP8266).

- Replace init()/deinit() with active(True/False).
- Change .config(on_recv) to .irq(cb).
- Change .poll() to .any(). Following existing practice in uart,
  stm32/usb and pyb_can.
- Extend recv() method to support alloc-free reads by passing storage
  buffers as optional second argument (buffers) (following approach
  by pyb/can module).
- Delete irecv() method. Alloc-free reads are now supported with the
  recv() method.
- Add optional size argument to send() which specifies the number of
  bytes from `message` which will be sent. If not provided or set to
  None, all the bytes in `message` will be sent.
- Change .peers attribute to .peers_table in attempt to reduce
  confusion with get_peers() method.
- Remove iterator support as requested pre merge with main branch.
- Add missing ESP_NOW_ETH_ALEN as module constant.

Minor refactoring:
- _get_singleton() functions for compatibility with changes for the
   esp8266.
- Minor changes to rssi update functions.
- Refactor for send() method to eliminate _do_espnow_send().

espnow: API changes on ESP8266 before merge with main branch.

Significant overhaul to address API change requests. This has required
some substantial rewrites to fit within the code space constraints of
the ESP8266.

- Change init()/deinit() to active(T/F) and config(). Replace
  init()/deinit() with active(True/False) and reintroduce the config()
  method.
- recv(timeout) returns [peer, msg] where peer and msg are newly
  alloced byte strings.
- recv(timeout, buffers) where buffers is a list of bytearrays which
  will be used to hold the peer and message data.
- Remove irecv() method.

Other changes to reduce text size to accommodate changes required
above.

espnow: Add espnowio python module.

Defines ESPNowIO child class with irecv() method to support alloc-free
recv() calls (backward compatible with pre-release irecv() method).

Also includes:
- init() and deinit() methods for backward compatibility with
  pre-release API, and
- Iterator support: "for peer, msg in e: print(peer, msg)"

espnow: Update tests to new API.

Depends on aioespnow module for convenient alloc-free read API.

espnow: Update docs for new API changes.

espnow: Docs: note that size arg of send() is ESP32 only.

espnow: Tests: Bugfix for rssi test.

Use time.ticks_ms() instead of time.time() as sanity check on rssi
timestamp. It seems that since v1.19.1 time.time() is not synched
with time.ticks_ms()/mp_hal_ticks_ms().

espnow: Change recv() to recvinto() on ESP32/8266.

Reduce code complexity by replacing recv() method with recvinto()
so that buffers *must* be supplied to read messages:
  recvinto([peer, msg], timeout_ms)
where peer and msg are bytearrays of length 6 and 250 respectively.
Timeout_ms is an optional timeout.
If the first arg to recvinto() is a list with at least 4 elements, the
rssi and timestamp values will be inserted as the 3rd and 4th elements
(after peer and masg).

Use a convenience method in the espnow.py module for a convenient
user interface: recv() and irecv().

Necessitates that we revert irq() method to simpler form. support
for the callback form will be moved to espnow.py.

Also, remove size argument for send().

Use MICROPY_ESPNOW_RSSI pre-processor directive to include/exclude code
to compute and track rssi.

Use MICROPY_ESPNOW_EXTRA_PEER_METHODS pre-processor macro to
conditionally compile in the mod_peer(), get_peer() and peer_count()
methods.

espnow: Add asyncio support for ESP8266 GENERIC target.

- esp_espnow.c: add stream ioctl() support (espnow_stream_ioctl()) for
  asyncio support.
- Add aioespnow.py to ports/esp8266/modules

espnow: Move espnow module to top-level and rename _espnow.

Move the espnow module from esp.espnow to _espnow. As a submodule
of the esp module we lost some flexibility.

Also rename espnowio.py to espnow.py so that "import espnow" will
import the python wrapper module, which will then import _espnow.

espnow: Update tests and docs for recent changes.

Document recvinto() method and methods exposed in espnow.py:
- recv(), irecv(), ...

Update tests for new API.

espnow: Restore workable .irq() and .on_recv() apis.

- Rename _espnow.irq() to _espnow.on_recv() as low-level callback api.
- Add ESPNow.irq() method to espnow.py module which supports api
  requested by @dpgeorge.
- For on_recv(): can supply argument to callback as optional second
  argument eg:
    def cb(e):
        print(e.irecv(0))
    e.on_recv(cb, e)
- Tests: 15_irq_test.py: add tests for .irq() and .on_recv().
- Docs: Add section for "Callback Methods".

espnow: Tests: rename and resequence tests following recent changes.

espnow: Drop get argument of config().

espnow: Restore None argument handling for add_peer().

add_peer() is documented to permit usage:
- add_peer(mac, None, None, network.AP_IF)
Restore the permitted use of None to indicate that the arg should be left
as is.

espnow: Docs: clarify section on Espnow and Wifi Operation.

Clarify the issues associated with the power saving mode and channel
settings when using ESPNow while also connected to a Wifi Access Point.

espnow: Docs: Include advice about wifi AP disconnects.

espnow: ESP8266 fix for changes to gc_pool_start/end.

espnow: Docs: Document issue with receiving msgs from esp8266 devices.

Add advice on receiving ESPNow messages from esp8266 devices while
connected to a wifi network on STA_IF.

espnow: ESP8266: Add wifi ps_mode support for esp8266.

espnow: Change ps_mode to pm.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.

espnow: Rebase against main branch.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 9, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 9, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 9, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 26, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq()/on_recv() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the esp32 espnow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ESP_NOW_ETH_ALEN     (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (eps8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 27, 2023
Progress on ESPNow from
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

espnow: Support peer wifi signal strength (RSSI).

- Maintain a peer device table (espnow_singleton->peers_table):
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}
    where:
      peerX are bytes strings containing the mac address;
      rssi values of last msg received (dBm from -128 to 0); and
      time_ms is time message received (in milliseconds since boot)
- User access to the peers table via attribute: ESPNow.peers.
- The peer addresses returned by irecv() and recv() are now references
  to the keys in peers_dict (instead of new copies of the peer address).
- Add RSSI docs to docs/library/espnow.rst.
- MAC address values returned by irecv()/recv() are now refernces to
  the peerX byte strings in the peer device table.
- Add tests for the rssi functions.
- Also - fixes to make asyncio tests more robust.
- Rename get_singleton() _get_singleton().
- Tests: Disconnect STA before running tests: ESP8266 devices
  automatically re-connect to previous Access Points when STA_IF is set
  active. This makes receipt of messages unreliable.

esp/espnow: Add aioespnow.py module for asyncio.

- Add aioespnow.py to support asyncio.
  - Provides class AIOESPNow which extends the ESPNow interface with
     methods: airecv(), arecv() and asend(). Also supports "async for"
     iteration.
  - Docs and tests updated for AIO support.
- Buffer Protocol (Stream IO) support is deprecated with the new async
  interface.

esp/espnow: Mv ring_buffer.[ch] to shared/runtime.

De-duplicate the ring_buffer.[ch] code in esp32 and esp8266 by moving
them to shared/runtime/.

esp/espnow: Code rationalisation for esp32.

- Remove espnow_print() and espnow_version().
- Fix espnow_ioctl signature.

esp/espnow: Remove buffer protocol support code.

esp/espnow: Fix docs: RSSI is only on ESP32.

- Fix docs to clarify that the peers table (supporting RSSI monitoring
  is not availablle on the ESP8266.
- Minor re-ordering of sections (more logical).
- Add advice on call to w0.disconnect() after w0.active(True) for
  esp8266.
- Minor fixes to example code.

esp/espnow: aioespnow.py: Support user extensions.

- Use self.irecv() instead of super().irecv() so derived classes
  can override irecv()/recv()/send().
- Bugfix: drop call to self.init() in __aiter__() method.
- tests/multi_espnow/: Arg to echo_test should be a bytestring.
- tests/multi_espnow/: Add new test for memory leaks.

esp/espnow: esp_espnow.c: More code streamlining.

- Add NEW_TUPLE macro as convenience 'function' for creating tuples.
- Remove unnecessary dummy espnow_stream_read()/wite() functions.
- Minor reorg of code for better logical grouping.

esp/espnow: Reimplement RSSI support.

Streamline the RSSI support. Do not update the device-table from
recv_cb(). Instead, save the rssi and timestamp in the packet buffer
and update the device-table when the packet is read from
irecv()/recv(). Also simplifies the device-table handling code.
Docs updated to reflect the changes:
 - RSSI values of first messages from peer are no longer lost
 - Device-table is only updated when irecv()/recv() read the message
    from the buffer.
Also for IDF < 4.2.0, rssi needs to be offset by 100.

esp/espnow: Disallow user changes to peer table.

Set map->is_fixed=1 to prevent changes by user code. Unset it only when
we are inserting new peers.

Also clean out a small bit of cruft in esp_espnow.c.

esp/espnow: Bugfix in get_peers() with multicast addresses.

get_peers() would cause reboot if a multi-cast address was registered
with add_peer() due to inconsistent use of peer_count.

esp/espnow: Wait forever in irecv()/recv() if timeout_ms < 0.

ring_buffer.c: Disable the timeout if timeout_ms < 0.
irecv() and recv() will wait forever if timeout is < 0.

esp/espnow: Set espnow TX rate with config(rate=).

Use ESPNow.config(rate=n) to set the bit rate for espnow transmissions,
where n is the numeric value of one of the rates defined in
"enum wifi_phy_rate_t" (see espressif wifi docs). Supported for
ESP IDF version >= v4.3.0.

esp/espnow: Irecv() returns msg as byte string.

Make msg returned from irecv() a byte string (instead of bytearray)
for consistency with recv() and convenience of users.

esp/espnow: Minor code reduction for espnow_ioctl().

Also tweaks to espnow_iternext() and espnow_attr().

esp/espnow: AIOEspnow: drop singleton decorator.

We can let AIOESPNow be a regular class rather than a singleton since
it holds no additional state (over the ESPNow base class singleton).
Singleton by decorator function makes it difficult to derive child
classes from AIOESPNow and micropython does not support common method
for creating singletons (overriding __new__() method).

esp/espnow: Code reduction on esp8266.

Rationalisation of espnow_irecv() code for some minor code size savings.

esp/espnow: Fix misleading comment.

esp/espnow: ioctl() return 0 if not initialised.

Previously, would throw a NOT_INIT exception if called before init()
or after deinit(). Now returns 0 if not initialised - ie. not ready for
read or write.

espnow: API changes on ESP32 before merge with main branch.

Significant overhaul to address API change requests before merge into
main branch. This has required some significant rewrites to maintain
as much compatibility with ESP8266 as possible (due to code space
constraints on ESP8266).

- Replace init()/deinit() with active(True/False).
- Change .config(on_recv) to .irq(cb).
- Change .poll() to .any(). Following existing practice in uart,
  stm32/usb and pyb_can.
- Extend recv() method to support alloc-free reads by passing storage
  buffers as optional second argument (buffers) (following approach
  by pyb/can module).
- Delete irecv() method. Alloc-free reads are now supported with the
  recv() method.
- Add optional size argument to send() which specifies the number of
  bytes from `message` which will be sent. If not provided or set to
  None, all the bytes in `message` will be sent.
- Change .peers attribute to .peers_table in attempt to reduce
  confusion with get_peers() method.
- Remove iterator support as requested pre merge with main branch.
- Add missing ESP_NOW_ETH_ALEN as module constant.

Minor refactoring:
- _get_singleton() functions for compatibility with changes for the
   esp8266.
- Minor changes to rssi update functions.
- Refactor for send() method to eliminate _do_espnow_send().

espnow: API changes on ESP8266 before merge with main branch.

Significant overhaul to address API change requests. This has required
some substantial rewrites to fit within the code space constraints of
the ESP8266.

- Change init()/deinit() to active(T/F) and config(). Replace
  init()/deinit() with active(True/False) and reintroduce the config()
  method.
- recv(timeout) returns [peer, msg] where peer and msg are newly
  alloced byte strings.
- recv(timeout, buffers) where buffers is a list of bytearrays which
  will be used to hold the peer and message data.
- Remove irecv() method.

Other changes to reduce text size to accommodate changes required
above.

espnow: Add espnowio python module.

Defines ESPNowIO child class with irecv() method to support alloc-free
recv() calls (backward compatible with pre-release irecv() method).

Also includes:
- init() and deinit() methods for backward compatibility with
  pre-release API, and
- Iterator support: "for peer, msg in e: print(peer, msg)"

espnow: Update tests to new API.

Depends on aioespnow module for convenient alloc-free read API.

espnow: Update docs for new API changes.

espnow: Docs: note that size arg of send() is ESP32 only.

espnow: Tests: Bugfix for rssi test.

Use time.ticks_ms() instead of time.time() as sanity check on rssi
timestamp. It seems that since v1.19.1 time.time() is not synched
with time.ticks_ms()/mp_hal_ticks_ms().

espnow: Change recv() to recvinto() on ESP32/8266.

Reduce code complexity by replacing recv() method with recvinto()
so that buffers *must* be supplied to read messages:
  recvinto([peer, msg], timeout_ms)
where peer and msg are bytearrays of length 6 and 250 respectively.
Timeout_ms is an optional timeout.
If the first arg to recvinto() is a list with at least 4 elements, the
rssi and timestamp values will be inserted as the 3rd and 4th elements
(after peer and masg).

Use a convenience method in the espnow.py module for a convenient
user interface: recv() and irecv().

Necessitates that we revert irq() method to simpler form. support
for the callback form will be moved to espnow.py.

Also, remove size argument for send().

Use MICROPY_ESPNOW_RSSI pre-processor directive to include/exclude code
to compute and track rssi.

Use MICROPY_ESPNOW_EXTRA_PEER_METHODS pre-processor macro to
conditionally compile in the mod_peer(), get_peer() and peer_count()
methods.

espnow: Add asyncio support for ESP8266 GENERIC target.

- esp_espnow.c: add stream ioctl() support (espnow_stream_ioctl()) for
  asyncio support.
- Add aioespnow.py to ports/esp8266/modules

espnow: Move espnow module to top-level and rename _espnow.

Move the espnow module from esp.espnow to _espnow. As a submodule
of the esp module we lost some flexibility.

Also rename espnowio.py to espnow.py so that "import espnow" will
import the python wrapper module, which will then import _espnow.

espnow: Update tests and docs for recent changes.

Document recvinto() method and methods exposed in espnow.py:
- recv(), irecv(), ...

Update tests for new API.

espnow: Restore workable .irq() and .on_recv() apis.

- Rename _espnow.irq() to _espnow.on_recv() as low-level callback api.
- Add ESPNow.irq() method to espnow.py module which supports api
  requested by @dpgeorge.
- For on_recv(): can supply argument to callback as optional second
  argument eg:
    def cb(e):
        print(e.irecv(0))
    e.on_recv(cb, e)
- Tests: 15_irq_test.py: add tests for .irq() and .on_recv().
- Docs: Add section for "Callback Methods".

espnow: Tests: rename and resequence tests following recent changes.

espnow: Drop get argument of config().

espnow: Restore None argument handling for add_peer().

add_peer() is documented to permit usage:
- add_peer(mac, None, None, network.AP_IF)
Restore the permitted use of None to indicate that the arg should be left
as is.

espnow: Docs: clarify section on Espnow and Wifi Operation.

Clarify the issues associated with the power saving mode and channel
settings when using ESPNow while also connected to a Wifi Access Point.

espnow: Docs: Include advice about wifi AP disconnects.

espnow: ESP8266 fix for changes to gc_pool_start/end.

espnow: Docs: Document issue with receiving msgs from esp8266 devices.

Add advice on receiving ESPNow messages from esp8266 devices while
connected to a wifi network on STA_IF.

espnow: ESP8266: Add wifi ps_mode support for esp8266.

espnow: Change ps_mode to pm.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.

espnow: Rebase against main branch.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 27, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv()/recvinto() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ADDR_LEN             (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (ESP8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 27, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv()/recvinto() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ADDR_LEN             (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (ESP8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 27, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv()/recvinto() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ADDR_LEN             (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (ESP8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 28, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv()/recvinto() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ADDR_LEN             (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

esp32/8266: WLAN(): add support for set/get wifi power saving mode.

For esp32 and esp8266: adds:
- 'pm' option to WLAN.config() to set/get the wifi power saving mode; and
- PM_NONE, PM_MIN_MODEM, PM_MAX_MODEM and PM_LIGHT_SLEEP (ESP8266 only)
  constants to the WLAN class.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 28, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv()/recvinto() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ADDR_LEN             (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.
glenn20 added a commit to glenn20/micropython that referenced this pull request Apr 30, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv()/recvinto() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ADDR_LEN             (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.
glenn20 added a commit to glenn20/micropython that referenced this pull request May 1, 2023
- Micropython v1.13 - v1.19 compatible
- Use (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring (see ESPNow.peers).
- Core support in "_espnow" C module (extended by espnow.py module).
- Asyncio support via aioespnow.py module.
- Docs provided at docs/library/espnow.rst.
- PR6515.

Methods in espnow.ESPNow class (modules/espnow.py):
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate.
- recv()/irecv()/recvinto() to read incoming messages from peers.
- send() to send messages to peer devices
- any() to test if a message is ready to read.
- irq() to set callback for received messages.
- stats(): Returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts).
- add_peer(mac, ...): register peer before sending messages.
- get_peer(mac): Return peer info: (mac,lmk,channel,ifidx,encrypt)
- mod_peer(mac, ...) to change peer info parameters.
- get_peers(): to return all peer info tuples.
- peers_table: to support RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

aioespnow.AIOESPNow class provides coroutines:
- airecv(), arecv() and asend().

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API. See docs for
details.

Add constants as attributes of the espnow module:
- espnow.MAX_DATA_LEN         (=ESP_NOW_MAX_DATA_LEN) (250)
- espnow.KEY_LEN              (=ESP_NOW_KEY_LEN) (16)
- espnow.MAX_TOTAL_PEER_NUM   (=ESP_NOW_MAX_TOTAL_PEER_NUM) (20)
- espnow.MAX_ENCRYPT_PEER_NUM (=ESP_NOW_MAX_ENCRYPT_PEER_NUM) (6)
- espnow.ADDR_LEN             (=ESP_NOW_ETH_ALEN) (6)

Test suite in tests/multi_espnow:
- Tests basic espnow data transfer, multiple transfers various message
  sizes, encrypted messages (pmk and lmk), asyncio support.

Initial PR at:
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.
glenn20 added a commit to glenn20/micropython that referenced this pull request May 1, 2023
Progress on ESPNow from
micropython#4115.
Initial import of code from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Including contributions from @nickzoic @shawwwn and @zoland.

espnow: Support peer wifi signal strength (RSSI).

- Maintain a peer device table (espnow_singleton->peers_table):
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}
    where:
      peerX are bytes strings containing the mac address;
      rssi values of last msg received (dBm from -128 to 0); and
      time_ms is time message received (in milliseconds since boot)
- User access to the peers table via attribute: ESPNow.peers.
- The peer addresses returned by irecv() and recv() are now references
  to the keys in peers_dict (instead of new copies of the peer address).
- Add RSSI docs to docs/library/espnow.rst.
- MAC address values returned by irecv()/recv() are now refernces to
  the peerX byte strings in the peer device table.
- Add tests for the rssi functions.
- Also - fixes to make asyncio tests more robust.
- Rename get_singleton() _get_singleton().
- Tests: Disconnect STA before running tests: ESP8266 devices
  automatically re-connect to previous Access Points when STA_IF is set
  active. This makes receipt of messages unreliable.

esp/espnow: Add aioespnow.py module for asyncio.

- Add aioespnow.py to support asyncio.
  - Provides class AIOESPNow which extends the ESPNow interface with
     methods: airecv(), arecv() and asend(). Also supports "async for"
     iteration.
  - Docs and tests updated for AIO support.
- Buffer Protocol (Stream IO) support is deprecated with the new async
  interface.

esp/espnow: Mv ring_buffer.[ch] to shared/runtime.

De-duplicate the ring_buffer.[ch] code in esp32 and esp8266 by moving
them to shared/runtime/.

esp/espnow: Code rationalisation for esp32.

- Remove espnow_print() and espnow_version().
- Fix espnow_ioctl signature.

esp/espnow: Remove buffer protocol support code.

esp/espnow: Fix docs: RSSI is only on ESP32.

- Fix docs to clarify that the peers table (supporting RSSI monitoring
  is not availablle on the ESP8266.
- Minor re-ordering of sections (more logical).
- Add advice on call to w0.disconnect() after w0.active(True) for
  esp8266.
- Minor fixes to example code.

esp/espnow: aioespnow.py: Support user extensions.

- Use self.irecv() instead of super().irecv() so derived classes
  can override irecv()/recv()/send().
- Bugfix: drop call to self.init() in __aiter__() method.
- tests/multi_espnow/: Arg to echo_test should be a bytestring.
- tests/multi_espnow/: Add new test for memory leaks.

esp/espnow: esp_espnow.c: More code streamlining.

- Add NEW_TUPLE macro as convenience 'function' for creating tuples.
- Remove unnecessary dummy espnow_stream_read()/wite() functions.
- Minor reorg of code for better logical grouping.

esp/espnow: Reimplement RSSI support.

Streamline the RSSI support. Do not update the device-table from
recv_cb(). Instead, save the rssi and timestamp in the packet buffer
and update the device-table when the packet is read from
irecv()/recv(). Also simplifies the device-table handling code.
Docs updated to reflect the changes:
 - RSSI values of first messages from peer are no longer lost
 - Device-table is only updated when irecv()/recv() read the message
    from the buffer.
Also for IDF < 4.2.0, rssi needs to be offset by 100.

esp/espnow: Disallow user changes to peer table.

Set map->is_fixed=1 to prevent changes by user code. Unset it only when
we are inserting new peers.

Also clean out a small bit of cruft in esp_espnow.c.

esp/espnow: Bugfix in get_peers() with multicast addresses.

get_peers() would cause reboot if a multi-cast address was registered
with add_peer() due to inconsistent use of peer_count.

esp/espnow: Wait forever in irecv()/recv() if timeout_ms < 0.

ring_buffer.c: Disable the timeout if timeout_ms < 0.
irecv() and recv() will wait forever if timeout is < 0.

esp/espnow: Set espnow TX rate with config(rate=).

Use ESPNow.config(rate=n) to set the bit rate for espnow transmissions,
where n is the numeric value of one of the rates defined in
"enum wifi_phy_rate_t" (see espressif wifi docs). Supported for
ESP IDF version >= v4.3.0.

esp/espnow: Irecv() returns msg as byte string.

Make msg returned from irecv() a byte string (instead of bytearray)
for consistency with recv() and convenience of users.

esp/espnow: Minor code reduction for espnow_ioctl().

Also tweaks to espnow_iternext() and espnow_attr().

esp/espnow: AIOEspnow: drop singleton decorator.

We can let AIOESPNow be a regular class rather than a singleton since
it holds no additional state (over the ESPNow base class singleton).
Singleton by decorator function makes it difficult to derive child
classes from AIOESPNow and micropython does not support common method
for creating singletons (overriding __new__() method).

esp/espnow: Code reduction on esp8266.

Rationalisation of espnow_irecv() code for some minor code size savings.

esp/espnow: Fix misleading comment.

esp/espnow: ioctl() return 0 if not initialised.

Previously, would throw a NOT_INIT exception if called before init()
or after deinit(). Now returns 0 if not initialised - ie. not ready for
read or write.

espnow: API changes on ESP32 before merge with main branch.

Significant overhaul to address API change requests before merge into
main branch. This has required some significant rewrites to maintain
as much compatibility with ESP8266 as possible (due to code space
constraints on ESP8266).

- Replace init()/deinit() with active(True/False).
- Change .config(on_recv) to .irq(cb).
- Change .poll() to .any(). Following existing practice in uart,
  stm32/usb and pyb_can.
- Extend recv() method to support alloc-free reads by passing storage
  buffers as optional second argument (buffers) (following approach
  by pyb/can module).
- Delete irecv() method. Alloc-free reads are now supported with the
  recv() method.
- Add optional size argument to send() which specifies the number of
  bytes from `message` which will be sent. If not provided or set to
  None, all the bytes in `message` will be sent.
- Change .peers attribute to .peers_table in attempt to reduce
  confusion with get_peers() method.
- Remove iterator support as requested pre merge with main branch.
- Add missing ESP_NOW_ETH_ALEN as module constant.

Minor refactoring:
- _get_singleton() functions for compatibility with changes for the
   esp8266.
- Minor changes to rssi update functions.
- Refactor for send() method to eliminate _do_espnow_send().

espnow: API changes on ESP8266 before merge with main branch.

Significant overhaul to address API change requests. This has required
some substantial rewrites to fit within the code space constraints of
the ESP8266.

- Change init()/deinit() to active(T/F) and config(). Replace
  init()/deinit() with active(True/False) and reintroduce the config()
  method.
- recv(timeout) returns [peer, msg] where peer and msg are newly
  alloced byte strings.
- recv(timeout, buffers) where buffers is a list of bytearrays which
  will be used to hold the peer and message data.
- Remove irecv() method.

Other changes to reduce text size to accommodate changes required
above.

espnow: Add espnowio python module.

Defines ESPNowIO child class with irecv() method to support alloc-free
recv() calls (backward compatible with pre-release irecv() method).

Also includes:
- init() and deinit() methods for backward compatibility with
  pre-release API, and
- Iterator support: "for peer, msg in e: print(peer, msg)"

espnow: Update tests to new API.

Depends on aioespnow module for convenient alloc-free read API.

espnow: Update docs for new API changes.

espnow: Docs: note that size arg of send() is ESP32 only.

espnow: Tests: Bugfix for rssi test.

Use time.ticks_ms() instead of time.time() as sanity check on rssi
timestamp. It seems that since v1.19.1 time.time() is not synched
with time.ticks_ms()/mp_hal_ticks_ms().

espnow: Change recv() to recvinto() on ESP32/8266.

Reduce code complexity by replacing recv() method with recvinto()
so that buffers *must* be supplied to read messages:
  recvinto([peer, msg], timeout_ms)
where peer and msg are bytearrays of length 6 and 250 respectively.
Timeout_ms is an optional timeout.
If the first arg to recvinto() is a list with at least 4 elements, the
rssi and timestamp values will be inserted as the 3rd and 4th elements
(after peer and masg).

Use a convenience method in the espnow.py module for a convenient
user interface: recv() and irecv().

Necessitates that we revert irq() method to simpler form. support
for the callback form will be moved to espnow.py.

Also, remove size argument for send().

Use MICROPY_ESPNOW_RSSI pre-processor directive to include/exclude code
to compute and track rssi.

Use MICROPY_ESPNOW_EXTRA_PEER_METHODS pre-processor macro to
conditionally compile in the mod_peer(), get_peer() and peer_count()
methods.

espnow: Add asyncio support for ESP8266 GENERIC target.

- esp_espnow.c: add stream ioctl() support (espnow_stream_ioctl()) for
  asyncio support.
- Add aioespnow.py to ports/esp8266/modules

espnow: Move espnow module to top-level and rename _espnow.

Move the espnow module from esp.espnow to _espnow. As a submodule
of the esp module we lost some flexibility.

Also rename espnowio.py to espnow.py so that "import espnow" will
import the python wrapper module, which will then import _espnow.

espnow: Update tests and docs for recent changes.

Document recvinto() method and methods exposed in espnow.py:
- recv(), irecv(), ...

Update tests for new API.

espnow: Restore workable .irq() and .on_recv() apis.

- Rename _espnow.irq() to _espnow.on_recv() as low-level callback api.
- Add ESPNow.irq() method to espnow.py module which supports api
  requested by @dpgeorge.
- For on_recv(): can supply argument to callback as optional second
  argument eg:
    def cb(e):
        print(e.irecv(0))
    e.on_recv(cb, e)
- Tests: 15_irq_test.py: add tests for .irq() and .on_recv().
- Docs: Add section for "Callback Methods".

espnow: Tests: rename and resequence tests following recent changes.

espnow: Drop get argument of config().

espnow: Restore None argument handling for add_peer().

add_peer() is documented to permit usage:
- add_peer(mac, None, None, network.AP_IF)
Restore the permitted use of None to indicate that the arg should be left
as is.

espnow: Docs: clarify section on Espnow and Wifi Operation.

Clarify the issues associated with the power saving mode and channel
settings when using ESPNow while also connected to a Wifi Access Point.

espnow: Docs: Include advice about wifi AP disconnects.

espnow: ESP8266 fix for changes to gc_pool_start/end.

espnow: Docs: Document issue with receiving msgs from esp8266 devices.

Add advice on receiving ESPNow messages from esp8266 devices while
connected to a wifi network on STA_IF.

espnow: ESP8266: Add wifi ps_mode support for esp8266.

espnow: Change ps_mode to pm.

espnow: Reduce code size on ESP8266.

Minor code reduction on ESP8266 to accommodate recent upstream changes:
- Delete buffer conistency check in espnow_recvinto().
- espnow_send(): only check for ROM addresses higher than GC pool.

espnow: Docs minor update.

- ESPNow.config(rate=XX) is only available on ESP32 and IDF>=4.3.0; and
- Add pointer to the espnow Issues page.

espnow: Rebase against main branch.

Use MP_DEFINE_CONST_OBJ_TYPE() to initialise esp_espnow_type.
dpgeorge pushed a commit to glenn20/micropython that referenced this pull request May 1, 2023
ESP-NOW is a proprietary wireless communication protocol which supports
connectionless communication between ESP32 and ESP8266 devices, using
vendor specific WiFi frames.  This commit adds support for this protocol
through a new `espnow` module.

This commit builds on original work done by @nickzoic, @shawwwn and with
contributions from @zoland.  Features include:
- Use of (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring.
- Core support in `_espnow` C module, extended by `espnow.py` module.
- Asyncio support via `aioespnow.py` module (separate to this commit).
- Docs provided at `docs/library/espnow.rst`.

Methods available in espnow.ESPNow class are:
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate
- recv()/irecv()/recvinto() to read incoming messages from peers
- send() to send messages to peer devices
- any() to test if a message is ready to read
- irq() to set callback for received messages
- stats() returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts)
- add_peer(mac, ...) registers a peer before sending messages
- get_peer(mac) returns peer info: (mac, lmk, channel, ifidx, encrypt)
- mod_peer(mac, ...) changes peer info parameters
- get_peers() returns all peer info tuples
- peers_table supports RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API.  See docs for
details.

Also included is a test suite in tests/multi_espnow.  This tests basic
espnow data transfer, multiple transfers, various message sizes, encrypted
messages (pmk and lmk), and asyncio support.

Initial work is from micropython#4115.
Initial import of code is from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
alphonse82 pushed a commit to alphonse82/micropython-wch-ch32v307 that referenced this pull request May 8, 2023
ESP-NOW is a proprietary wireless communication protocol which supports
connectionless communication between ESP32 and ESP8266 devices, using
vendor specific WiFi frames.  This commit adds support for this protocol
through a new `espnow` module.

This commit builds on original work done by @nickzoic, @shawwwn and with
contributions from @zoland.  Features include:
- Use of (extended) ring buffers in py/ringbuf.[ch] for robust IO.
- Signal strength (RSSI) monitoring.
- Core support in `_espnow` C module, extended by `espnow.py` module.
- Asyncio support via `aioespnow.py` module (separate to this commit).
- Docs provided at `docs/library/espnow.rst`.

Methods available in espnow.ESPNow class are:
- active(True/False)
- config(): set rx buffer size, read timeout and tx rate
- recv()/irecv()/recvinto() to read incoming messages from peers
- send() to send messages to peer devices
- any() to test if a message is ready to read
- irq() to set callback for received messages
- stats() returns transfer stats:
    (tx_pkts, tx_pkt_responses, tx_failures, rx_pkts, lost_rx_pkts)
- add_peer(mac, ...) registers a peer before sending messages
- get_peer(mac) returns peer info: (mac, lmk, channel, ifidx, encrypt)
- mod_peer(mac, ...) changes peer info parameters
- get_peers() returns all peer info tuples
- peers_table supports RSSI signal monitoring for received messages:
    {peer1: [rssi, time_ms], peer2: [rssi, time_ms], ...}

ESP8266 is a pared down version of the ESP32 ESPNow support due to code
size restrictions and differences in the low-level API.  See docs for
details.

Also included is a test suite in tests/multi_espnow.  This tests basic
espnow data transfer, multiple transfers, various message sizes, encrypted
messages (pmk and lmk), and asyncio support.

Initial work is from micropython#4115.
Initial import of code is from:
https://github.com/nickzoic/micropython/tree/espnow-4115.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.