blob: 91510a3f9df148e22aa2f37ba5fe24d968b5be2a [file] [log] [blame]
Glenn Moloney7fa322a2020-09-24 15:37:04 +10001/*
2 * This file is part of the MicroPython project, http://micropython.org/
3 *
4 * The MIT License (MIT)
5 *
6 * Copyright (c) 2017-2020 Nick Moore
7 * Copyright (c) 2018 shawwwn <shawwwn1@gmail.com>
8 * Copyright (c) 2020-2021 Glenn Moloney @glenn20
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 * THE SOFTWARE.
27 */
28
29
30#include <stdio.h>
31#include <stdint.h>
32#include <string.h>
33
34#include "py/runtime.h"
35
Glenn Moloney9f835df2023-10-10 13:06:59 +110036#if MICROPY_PY_ESPNOW
Glenn Moloney7fa322a2020-09-24 15:37:04 +100037
38#include "c_types.h"
39#include "espnow.h"
40
41#include "py/mphal.h"
42#include "py/mperrno.h"
43#include "py/qstr.h"
44#include "py/objstr.h"
45#include "py/objarray.h"
46#include "py/stream.h"
47#include "py/binary.h"
48#include "py/ringbuf.h"
49
50#include "mpconfigport.h"
51
52#include "modespnow.h"
53
54// For the esp8266
55#define ESP_NOW_MAX_DATA_LEN (250)
56#define ESP_NOW_KEY_LEN (16)
57#define ESP_NOW_ETH_ALEN (6)
58#define ESP_NOW_SEND_SUCCESS (0)
59#define ESP_ERR_ESPNOW_NO_MEM (-77777)
60#define ESP_OK (0)
61#define ESP_NOW_MAX_TOTAL_PEER_NUM (20)
62#define ESP_NOW_MAX_ENCRYPT_PEER_NUM (6)
63#define ESP_ERR_ESPNOW_NOT_INIT (0x300 + 100 + 1)
64typedef int esp_err_t;
65
66static const uint8_t ESPNOW_MAGIC = 0x99;
67
68// Use this for peeking at the header of the next packet in the buffer.
69typedef struct {
70 uint8_t magic; // = ESPNOW_MAGIC
71 uint8_t msg_len; // Length of the message
72} __attribute__((packed)) espnow_hdr_t;
73
74// ESPNow packet format for the receive buffer.
75typedef struct {
76 espnow_hdr_t hdr; // The header
77 uint8_t peer[6]; // Peer address
78 uint8_t msg[0]; // Message is up to 250 bytes
79} __attribute__((packed)) espnow_pkt_t;
80
81// The maximum length of an espnow packet (bytes)
82static const size_t MAX_PACKET_LEN = (
83 sizeof(espnow_pkt_t) + ESP_NOW_MAX_DATA_LEN);
84
85// Enough for 2 full-size packets: 2 * (6 + 2 + 250) = 516 bytes
86// Will allocate an additional 7 bytes for buffer overhead
87#define DEFAULT_RECV_BUFFER_SIZE \
88 (2 * (sizeof(espnow_pkt_t) + ESP_NOW_MAX_DATA_LEN))
89
90// Default timeout (millisec) to wait for incoming ESPNow messages (5 minutes).
91#define DEFAULT_RECV_TIMEOUT_MS (5 * 60 * 1000)
92
93// Number of milliseconds to wait for pending responses to sent packets.
94// This is a fallback which should never be reached.
95#define PENDING_RESPONSES_TIMEOUT_MS 100
96
97// The data structure for the espnow_singleton.
98typedef struct _esp_espnow_obj_t {
99 mp_obj_base_t base;
100 ringbuf_t *recv_buffer; // A buffer for received packets
101 size_t recv_buffer_size; // Size of recv buffer
102 size_t recv_timeout_ms; // Timeout for irecv()
103 size_t tx_packets; // Count of sent packets
104 volatile size_t tx_responses; // # of sent packet responses received
105 volatile size_t tx_failures; // # of sent packet responses failed
106} esp_espnow_obj_t;
107
108// Initialised below.
109const mp_obj_type_t esp_espnow_type;
110
111static esp_espnow_obj_t espnow_singleton = {
112 .base.type = &esp_espnow_type,
113 .recv_buffer = NULL,
114 .recv_buffer_size = DEFAULT_RECV_BUFFER_SIZE,
115 .recv_timeout_ms = DEFAULT_RECV_TIMEOUT_MS,
116};
117
118// ### Initialisation and Config functions
119//
120
121static void check_esp_err(int e) {
122 if (e != 0) {
123 mp_raise_OSError(e);
124 }
125}
126
127// Return a pointer to the ESPNow module singleton
128// If state == INITIALISED check the device has been initialised.
129// Raises OSError if not initialised and state == INITIALISED.
130static esp_espnow_obj_t *_get_singleton() {
131 return &espnow_singleton;
132}
133
134static esp_espnow_obj_t *_get_singleton_initialised() {
135 esp_espnow_obj_t *self = _get_singleton();
136 if (self->recv_buffer == NULL) {
137 // Throw an espnow not initialised error
138 check_esp_err(ESP_ERR_ESPNOW_NOT_INIT);
139 }
140 return self;
141}
142
143// Allocate and initialise the ESPNow module as a singleton.
144// Returns the initialised espnow_singleton.
Angus Grattondecf8e62024-02-27 15:32:29 +1100145static mp_obj_t espnow_make_new(const mp_obj_type_t *type, size_t n_args,
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000146 size_t n_kw, const mp_obj_t *all_args) {
147
148 return _get_singleton();
149}
150
151// Forward declare the send and recv ESPNow callbacks
Angus Grattondecf8e62024-02-27 15:32:29 +1100152static void send_cb(uint8_t *mac_addr, uint8_t status);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000153
Angus Grattondecf8e62024-02-27 15:32:29 +1100154static void recv_cb(uint8_t *mac_addr, uint8_t *data, uint8_t len);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000155
156// ESPNow.deinit(): De-initialise the ESPNOW software stack, disable callbacks
157// and deallocate the recv data buffers.
158// Note: this function is called from main.c:mp_task() to cleanup before soft
Angus Grattondecf8e62024-02-27 15:32:29 +1100159// reset, so cannot be declared static and must guard against self == NULL;.
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000160mp_obj_t espnow_deinit(mp_obj_t _) {
161 esp_espnow_obj_t *self = _get_singleton();
162 if (self->recv_buffer != NULL) {
163 // esp_now_unregister_recv_cb();
164 esp_now_deinit();
165 self->recv_buffer->buf = NULL;
166 self->recv_buffer = NULL;
167 self->tx_packets = self->tx_responses;
168 }
169 MP_STATE_PORT(espnow_buffer) = NULL;
170 return mp_const_none;
171}
172
173// ESPNow.active(): Initialise the data buffers and ESP-NOW functions.
174// Initialise the Espressif ESPNOW software stack, register callbacks and
175// allocate the recv data buffers.
176// Returns True if interface is active, else False.
Angus Grattondecf8e62024-02-27 15:32:29 +1100177static mp_obj_t espnow_active(size_t n_args, const mp_obj_t *args) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000178 esp_espnow_obj_t *self = args[0];
179 if (n_args > 1) {
180 if (mp_obj_is_true(args[1])) {
181 if (self->recv_buffer == NULL) { // Already initialised
182 self->recv_buffer = m_new_obj(ringbuf_t);
183 ringbuf_alloc(self->recv_buffer, self->recv_buffer_size);
184 MP_STATE_PORT(espnow_buffer) = self->recv_buffer;
185 esp_now_init();
186 esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
187 esp_now_register_recv_cb(recv_cb);
188 esp_now_register_send_cb(send_cb);
189 }
190 } else {
191 espnow_deinit(self);
192 }
193 }
194 return mp_obj_new_bool(self->recv_buffer != NULL);
195}
Angus Grattondecf8e62024-02-27 15:32:29 +1100196static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_active_obj, 1, 2, espnow_active);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000197
198// ESPNow.config(): Initialise the data buffers and ESP-NOW functions.
199// Initialise the Espressif ESPNOW software stack, register callbacks and
200// allocate the recv data buffers.
201// Returns True if interface is active, else False.
Angus Grattondecf8e62024-02-27 15:32:29 +1100202static mp_obj_t espnow_config(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000203 esp_espnow_obj_t *self = _get_singleton();
204 enum { ARG_rxbuf, ARG_timeout_ms };
205 static const mp_arg_t allowed_args[] = {
206 { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
207 { MP_QSTR_timeout_ms, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} },
208 };
209 mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
210 mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args,
211 MP_ARRAY_SIZE(allowed_args), allowed_args, args);
212 if (args[ARG_rxbuf].u_int >= 0) {
213 self->recv_buffer_size = args[ARG_rxbuf].u_int;
214 }
215 if (args[ARG_timeout_ms].u_int >= 0) {
216 self->recv_timeout_ms = args[ARG_timeout_ms].u_int;
217 }
218 return mp_const_none;
219}
Angus Grattondecf8e62024-02-27 15:32:29 +1100220static MP_DEFINE_CONST_FUN_OBJ_KW(espnow_config_obj, 1, espnow_config);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000221
222// ### The ESP_Now send and recv callback routines
223//
224
225// Callback triggered when a sent packet is acknowledged by the peer (or not).
226// Just count the number of responses and number of failures.
227// These are used in the send()/write() logic.
Angus Grattondecf8e62024-02-27 15:32:29 +1100228static void send_cb(uint8_t *mac_addr, uint8_t status) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000229 esp_espnow_obj_t *self = _get_singleton();
230 self->tx_responses++;
231 if (status != ESP_NOW_SEND_SUCCESS) {
232 self->tx_failures++;
233 }
234}
235
236// Callback triggered when an ESP-Now packet is received.
237// Write the peer MAC address and the message into the recv_buffer as an
238// ESPNow packet.
239// If the buffer is full, drop the message and increment the dropped count.
240// Schedules the user callback if one has been registered (ESPNow.config()).
Angus Grattondecf8e62024-02-27 15:32:29 +1100241static void recv_cb(uint8_t *mac_addr, uint8_t *msg, uint8_t msg_len) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000242 esp_espnow_obj_t *self = _get_singleton();
243 ringbuf_t *buf = self->recv_buffer;
244 // TODO: Test this works with ">".
245 if (buf == NULL || sizeof(espnow_pkt_t) + msg_len >= ringbuf_free(buf)) {
246 return;
247 }
248 espnow_hdr_t header;
249 header.magic = ESPNOW_MAGIC;
250 header.msg_len = msg_len;
251
252 ringbuf_put_bytes(buf, (uint8_t *)&header, sizeof(header));
253 ringbuf_put_bytes(buf, mac_addr, ESP_NOW_ETH_ALEN);
254 ringbuf_put_bytes(buf, msg, msg_len);
255}
256
257// Return C pointer to byte memory string/bytes/bytearray in obj.
258// Raise ValueError if the length does not match expected len.
259static uint8_t *_get_bytes_len_rw(mp_obj_t obj, size_t len, mp_uint_t rw) {
260 mp_buffer_info_t bufinfo;
261 mp_get_buffer_raise(obj, &bufinfo, rw);
262 if (bufinfo.len != len) {
263 mp_raise_ValueError(MP_ERROR_TEXT("invalid buffer length"));
264 }
265 return (uint8_t *)bufinfo.buf;
266}
267
268static uint8_t *_get_bytes_len(mp_obj_t obj, size_t len) {
269 return _get_bytes_len_rw(obj, len, MP_BUFFER_READ);
270}
271
272static uint8_t *_get_bytes_len_w(mp_obj_t obj, size_t len) {
273 return _get_bytes_len_rw(obj, len, MP_BUFFER_WRITE);
274}
275
276// ### Handling espnow packets in the recv buffer
277//
278
279// Copy data from the ring buffer - wait if buffer is empty up to timeout_ms
280// 0: Success
281// -1: Not enough data available to complete read (try again later)
282// -2: Requested read is larger than buffer - will never succeed
283static int ringbuf_get_bytes_wait(ringbuf_t *r, uint8_t *data, size_t len, mp_int_t timeout_ms) {
284 mp_uint_t start = mp_hal_ticks_ms();
285 int status = 0;
286 while (((status = ringbuf_get_bytes(r, data, len)) == -1)
287 && (timeout_ms < 0 || (mp_uint_t)(mp_hal_ticks_ms() - start) < (mp_uint_t)timeout_ms)) {
Angus Gratton73879732023-12-07 10:05:39 +1100288 mp_event_wait_ms(1);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000289 }
290 return status;
291}
292
293// ESPNow.recvinto([timeout_ms, []]):
294// Returns a list of byte strings: (peer_addr, message) where peer_addr is
295// the MAC address of the sending peer.
296// Arguments:
297// timeout_ms: timeout in milliseconds (or None).
298// buffers: list of bytearrays to store values: [peer, message].
299// Default timeout is set with ESPNow.config(timeout=milliseconds).
300// Return (None, None) on timeout.
Angus Grattondecf8e62024-02-27 15:32:29 +1100301static mp_obj_t espnow_recvinto(size_t n_args, const mp_obj_t *args) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000302 esp_espnow_obj_t *self = _get_singleton_initialised();
303
304 size_t timeout_ms = ((n_args > 2 && args[2] != mp_const_none)
305 ? mp_obj_get_int(args[2]) : self->recv_timeout_ms);
306
307 mp_obj_list_t *list = MP_OBJ_TO_PTR(args[1]);
308 if (!mp_obj_is_type(list, &mp_type_list) || list->len < 2) {
309 mp_raise_ValueError(MP_ERROR_TEXT("ESPNow.recvinto(): Invalid argument"));
310 }
311 mp_obj_array_t *msg = MP_OBJ_TO_PTR(list->items[1]);
312 size_t msg_size = msg->len + msg->free;
313 if (mp_obj_is_type(msg, &mp_type_bytearray)) {
314 msg->len = msg_size; // Make all the space in msg array available
315 msg->free = 0;
316 }
317 uint8_t *peer_buf = _get_bytes_len_w(list->items[0], ESP_NOW_ETH_ALEN);
318 uint8_t *msg_buf = _get_bytes_len_w(msg, ESP_NOW_MAX_DATA_LEN);
319
320 // Read the packet header from the incoming buffer
321 espnow_hdr_t hdr;
322 if (ringbuf_get_bytes_wait(self->recv_buffer, (uint8_t *)&hdr, sizeof(hdr), timeout_ms) < 0) {
323 return MP_OBJ_NEW_SMALL_INT(0); // Timeout waiting for packet
324 }
325 int msg_len = hdr.msg_len;
326
327 // Check the message packet header format and read the message data
328 if (hdr.magic != ESPNOW_MAGIC
329 || msg_len > ESP_NOW_MAX_DATA_LEN
330 || ringbuf_get_bytes(self->recv_buffer, peer_buf, ESP_NOW_ETH_ALEN) < 0
331 || ringbuf_get_bytes(self->recv_buffer, msg_buf, msg_len) < 0) {
332 mp_raise_ValueError(MP_ERROR_TEXT("ESPNow.recv(): buffer error"));
333 }
334 if (mp_obj_is_type(msg, &mp_type_bytearray)) {
335 // Set the length of the message bytearray.
336 msg->len = msg_len;
337 msg->free = msg_size - msg_len;
338 }
339
340 return MP_OBJ_NEW_SMALL_INT(msg_len);
341}
Angus Grattondecf8e62024-02-27 15:32:29 +1100342static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_recvinto_obj, 2, 3, espnow_recvinto);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000343
344// Used by espnow_send() for sends() with sync==True.
345// Wait till all pending sent packet responses have been received.
346// ie. self->tx_responses == self->tx_packets.
347// Return the number of responses where status != ESP_NOW_SEND_SUCCESS.
348static void _wait_for_pending_responses(esp_espnow_obj_t *self) {
349 for (int i = 0; i < PENDING_RESPONSES_TIMEOUT_MS; i++) {
350 if (self->tx_responses >= self->tx_packets) {
351 return;
352 }
353 mp_hal_delay_ms(1); // Allow other tasks to run
354 }
355 // Note: the loop timeout is just a fallback - in normal operation
356 // we should never reach that timeout.
357}
358
359// ESPNow.send(peer_addr, message, [sync (=true)])
360// ESPNow.send(message)
361// Send a message to the peer's mac address. Optionally wait for a response.
362// If sync == True, wait for response after sending.
363// Returns:
364// True if sync==False and message sent successfully.
365// True if sync==True and message is received successfully by all recipients
366// False if sync==True and message is not received by at least one recipient
367// Raises: EAGAIN if the internal espnow buffers are full.
Angus Grattondecf8e62024-02-27 15:32:29 +1100368static mp_obj_t espnow_send(size_t n_args, const mp_obj_t *args) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000369 esp_espnow_obj_t *self = _get_singleton_initialised();
370
371 bool sync = n_args <= 3 || args[3] == mp_const_none || mp_obj_is_true(args[3]);
372 // Get a pointer to the buffer of obj
373 mp_buffer_info_t message;
374 mp_get_buffer_raise(args[2], &message, MP_BUFFER_READ);
375
376 // Bugfix: esp_now_send() generates a panic if message buffer points
377 // to an address in ROM (eg. a statically interned QSTR).
378 // Fix: if message is not in gc pool, copy to a temp buffer.
379 static char temp[ESP_NOW_MAX_DATA_LEN]; // Static to save code space
380 byte *p = (byte *)message.buf;
381 // if (p < MP_STATE_MEM(area.gc_pool_start) || MP_STATE_MEM(area.gc_pool_end) < p) {
382 if (MP_STATE_MEM(area.gc_pool_end) < p) {
383 // If buffer is not in GC pool copy from ROM to stack
384 memcpy(temp, message.buf, message.len);
385 message.buf = temp;
386 }
387
388 if (sync) {
389 // If the last call was sync==False there may be outstanding responses.
390 // We need to wait for all pending responses if this call has sync=True.
391 _wait_for_pending_responses(self);
392 }
393 int saved_failures = self->tx_failures;
394
395 check_esp_err(
396 esp_now_send(_get_bytes_len(args[1], ESP_NOW_ETH_ALEN), message.buf, message.len));
397 self->tx_packets++;
398 if (sync) {
399 // Wait for message to be received by peer
400 _wait_for_pending_responses(self);
401 }
402 // Return False if sync and any peers did not respond.
403 return mp_obj_new_bool(!(sync && self->tx_failures != saved_failures));
404}
Angus Grattondecf8e62024-02-27 15:32:29 +1100405static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_send_obj, 3, 4, espnow_send);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000406
407// ### Peer Management Functions
408//
409
410// Set the ESP-NOW Primary Master Key (pmk) (for encrypted communications).
411// Raise OSError if not initialised.
412// Raise ValueError if key is not a bytes-like object exactly 16 bytes long.
Angus Grattondecf8e62024-02-27 15:32:29 +1100413static mp_obj_t espnow_set_pmk(mp_obj_t _, mp_obj_t key) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000414 check_esp_err(esp_now_set_kok(_get_bytes_len(key, ESP_NOW_KEY_LEN), ESP_NOW_KEY_LEN));
415 return mp_const_none;
416}
Angus Grattondecf8e62024-02-27 15:32:29 +1100417static MP_DEFINE_CONST_FUN_OBJ_2(espnow_set_pmk_obj, espnow_set_pmk);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000418
419// ESPNow.add_peer(peer_mac, [lmk, [channel, [ifidx, [encrypt]]]])
420// Positional args set to None will be left at defaults.
421// Raise OSError if not initialised.
422// Raise ValueError if mac or LMK are not bytes-like objects or wrong length.
423// Raise TypeError if invalid keyword args or too many positional args.
424// Return None.
Angus Grattondecf8e62024-02-27 15:32:29 +1100425static mp_obj_t espnow_add_peer(size_t n_args, const mp_obj_t *args) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000426 check_esp_err(
427 esp_now_add_peer(
428 _get_bytes_len(args[1], ESP_NOW_ETH_ALEN),
429 ESP_NOW_ROLE_COMBO,
430 (n_args > 3) ? mp_obj_get_int(args[3]) : 0,
431 (n_args > 2) ? _get_bytes_len(args[2], ESP_NOW_KEY_LEN) : NULL,
432 ESP_NOW_KEY_LEN));
433
434 return mp_const_none;
435}
Angus Grattondecf8e62024-02-27 15:32:29 +1100436static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow_add_peer_obj, 2, 4, espnow_add_peer);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000437
438// ESPNow.del_peer(peer_mac): Unregister peer_mac.
439// Raise OSError if not initialised.
440// Raise ValueError if peer is not a bytes-like objects or wrong length.
441// Return None.
Angus Grattondecf8e62024-02-27 15:32:29 +1100442static mp_obj_t espnow_del_peer(mp_obj_t _, mp_obj_t peer) {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000443 esp_now_del_peer(_get_bytes_len(peer, ESP_NOW_ETH_ALEN));
444 return mp_const_none;
445}
Angus Grattondecf8e62024-02-27 15:32:29 +1100446static MP_DEFINE_CONST_FUN_OBJ_2(espnow_del_peer_obj, espnow_del_peer);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000447
Angus Grattondecf8e62024-02-27 15:32:29 +1100448static const mp_rom_map_elem_t esp_espnow_locals_dict_table[] = {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000449 { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&espnow_active_obj) },
450 { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&espnow_config_obj) },
451 { MP_ROM_QSTR(MP_QSTR_recvinto), MP_ROM_PTR(&espnow_recvinto_obj) },
452 { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&espnow_send_obj) },
453
454 // Peer management functions
455 { MP_ROM_QSTR(MP_QSTR_set_pmk), MP_ROM_PTR(&espnow_set_pmk_obj) },
456 { MP_ROM_QSTR(MP_QSTR_add_peer), MP_ROM_PTR(&espnow_add_peer_obj) },
457 { MP_ROM_QSTR(MP_QSTR_del_peer), MP_ROM_PTR(&espnow_del_peer_obj) },
458};
Angus Grattondecf8e62024-02-27 15:32:29 +1100459static MP_DEFINE_CONST_DICT(esp_espnow_locals_dict, esp_espnow_locals_dict_table);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000460
Angus Grattondecf8e62024-02-27 15:32:29 +1100461static const mp_rom_map_elem_t espnow_globals_dict_table[] = {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000462 { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR__espnow) },
463 { MP_ROM_QSTR(MP_QSTR_ESPNowBase), MP_ROM_PTR(&esp_espnow_type) },
464 { MP_ROM_QSTR(MP_QSTR_MAX_DATA_LEN), MP_ROM_INT(ESP_NOW_MAX_DATA_LEN)},
465 { MP_ROM_QSTR(MP_QSTR_ADDR_LEN), MP_ROM_INT(ESP_NOW_ETH_ALEN)},
466 { MP_ROM_QSTR(MP_QSTR_KEY_LEN), MP_ROM_INT(ESP_NOW_KEY_LEN)},
467 { MP_ROM_QSTR(MP_QSTR_MAX_TOTAL_PEER_NUM), MP_ROM_INT(ESP_NOW_MAX_TOTAL_PEER_NUM)},
468 { MP_ROM_QSTR(MP_QSTR_MAX_ENCRYPT_PEER_NUM), MP_ROM_INT(ESP_NOW_MAX_ENCRYPT_PEER_NUM)},
469};
Angus Grattondecf8e62024-02-27 15:32:29 +1100470static MP_DEFINE_CONST_DICT(espnow_globals_dict, espnow_globals_dict_table);
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000471
472// ### Dummy Buffer Protocol support
473// ...so asyncio can poll.ipoll() on this device
474
475// Support ioctl(MP_STREAM_POLL, ) for asyncio
Angus Grattondecf8e62024-02-27 15:32:29 +1100476static mp_uint_t espnow_stream_ioctl(mp_obj_t self_in, mp_uint_t request,
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000477 uintptr_t arg, int *errcode) {
478 if (request != MP_STREAM_POLL) {
479 *errcode = MP_EINVAL;
480 return MP_STREAM_ERROR;
481 }
482 esp_espnow_obj_t *self = _get_singleton();
483 return (self->recv_buffer == NULL) ? 0 : // If not initialised
484 arg ^ ((ringbuf_avail(self->recv_buffer) == 0) ? MP_STREAM_POLL_RD : 0);
485}
486
Angus Grattondecf8e62024-02-27 15:32:29 +1100487static const mp_stream_p_t espnow_stream_p = {
Glenn Moloney7fa322a2020-09-24 15:37:04 +1000488 .ioctl = espnow_stream_ioctl,
489};
490
491MP_DEFINE_CONST_OBJ_TYPE(
492 esp_espnow_type,
493 MP_QSTR_ESPNowBase,
494 MP_TYPE_FLAG_NONE,
495 make_new, espnow_make_new,
496 protocol, &espnow_stream_p,
497 locals_dict, &esp_espnow_locals_dict
498 );
499
500const mp_obj_module_t mp_module_espnow = {
501 .base = { &mp_type_module },
502 .globals = (mp_obj_dict_t *)&espnow_globals_dict,
503};
504
505MP_REGISTER_MODULE(MP_QSTR__espnow, mp_module_espnow);
506MP_REGISTER_ROOT_POINTER(void *espnow_buffer);
507#endif