diff --git a/locale/circuitpython.pot b/locale/circuitpython.pot index 1b44383086..da9e0ac0f2 100644 --- a/locale/circuitpython.pot +++ b/locale/circuitpython.pot @@ -124,7 +124,7 @@ msgstr "" msgid "%q init failed" msgstr "" -#: ports/espressif/bindings/espnow/ESPNow.c shared-bindings/dualbank/__init__.c +#: ports/espressif/bindings/espnow/Peer.c shared-bindings/dualbank/__init__.c msgid "%q is %q" msgstr "" @@ -2431,7 +2431,7 @@ msgstr "" msgid "addresses is empty" msgstr "" -#: ports/espressif/bindings/espnow/ESPNow.c +#: ports/espressif/bindings/espnow/Peers.c #: ports/espressif/common-hal/espnow/ESPNow.c msgid "an error occured" msgstr "" @@ -3816,14 +3816,6 @@ msgstr "" msgid "parameters must be registers in sequence r0 to r3" msgstr "" -#: ports/espressif/bindings/espnow/ESPNow.c -msgid "peer already exists" -msgstr "" - -#: ports/espressif/bindings/espnow/ESPNow.c -msgid "peer not found" -msgstr "" - #: shared-bindings/bitmaptools/__init__.c shared-bindings/displayio/Bitmap.c msgid "pixel coordinates out of bounds" msgstr "" diff --git a/ports/espressif/Makefile b/ports/espressif/Makefile index 76d443dd68..8e326e18ab 100644 --- a/ports/espressif/Makefile +++ b/ports/espressif/Makefile @@ -126,9 +126,13 @@ ifeq ($(DEBUG), 1) # CFLAGS += -fno-inline -fno-ipa-sra else CFLAGS += -DNDEBUG -ggdb3 - OPTIMIZATION_FLAGS ?= -O2 # RISC-V is larger than xtensa # Use -Os for RISC-V when it overflows + ifeq ($(IDF_TARGET_ARCH),riscv) + OPTIMIZATION_FLAGS ?= -Os + else + OPTIMIZATION_FLAGS ?= -O2 + endif endif # option to override compiler optimization level, set in boards/$(BOARD)/mpconfigboard.mk diff --git a/ports/espressif/bindings/espnow/ESPNow.c b/ports/espressif/bindings/espnow/ESPNow.c index b120b2cdff..9a1ddfb80f 100644 --- a/ports/espressif/bindings/espnow/ESPNow.c +++ b/ports/espressif/bindings/espnow/ESPNow.c @@ -27,27 +27,32 @@ * THE SOFTWARE. */ -#include "esp_now.h" - #include "py/runtime.h" #include "py/objarray.h" #include "py/objproperty.h" #include "py/stream.h" -#include "py/ringbuf.h" -#include "mphalport.h" - -#include "bindings/espnow/__init__.h" #include "bindings/espnow/ESPNow.h" #include "shared-bindings/util.h" #include "common-hal/espnow/ESPNow.h" -static void check_esp_err(esp_err_t status) { - if (status != ESP_OK) { - mp_raise_RuntimeError(translate("an error occured")); - } +#include "esp_now.h" + +// Return C pointer to byte memory string/bytes/bytearray in obj. +// Raise ValueError if the length does not match expected len. +static const uint8_t *_get_bytes_len(mp_obj_t obj, size_t len, mp_uint_t rw) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(obj, &bufinfo, rw); + mp_arg_validate_length(bufinfo.len, len, MP_QSTR_buffer); + return (uint8_t *)bufinfo.buf; +} + +// Return C pointer to the MAC address. +// Raise ValueError if mac is wrong type or is not 6 bytes long. +static const uint8_t *_get_peer_addr(mp_obj_t mac) { + return mp_obj_is_true(mac) ? _get_bytes_len(mac, ESP_NOW_ETH_ALEN, MP_BUFFER_READ) : NULL; } // --- Initialisation and Config functions --- @@ -89,6 +94,7 @@ STATIC mp_obj_t espnow_make_new(const mp_obj_type_t *type, size_t n_args, size_t common_hal_espnow_set_buffer_size(self, args[ARG_buffer_size].u_int); common_hal_espnow_set_phy_rate(self, args[ARG_phy_rate].u_int); + self->peers = espnow_peers_new(); self->peers_table = mp_obj_new_dict(0); // Prevent user code modifying the dict @@ -102,14 +108,30 @@ STATIC mp_obj_t espnow_make_new(const mp_obj_type_t *type, size_t n_args, size_t return self; } -// Return C pointer to byte memory string/bytes/bytearray in obj. -// Raise ValueError if the length does not match expected len. -static const uint8_t *_get_bytes_len(mp_obj_t obj, size_t len, mp_uint_t rw) { - mp_buffer_info_t bufinfo; - mp_get_buffer_raise(obj, &bufinfo, rw); - mp_arg_validate_length(bufinfo.len, len, MP_QSTR_buffer); - return (uint8_t *)bufinfo.buf; +//| def deinit(self) -> None: +//| """Deinitializes ESP-NOW and releases it for another program.""" +//| ... +STATIC mp_obj_t espnow_deinit(mp_obj_t self_in) { + espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); + common_hal_espnow_deinit(self); + return mp_const_none; } +STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_deinit_obj, espnow_deinit); + +//| def __enter__(self) -> ESPNow: +//| """No-op used by Context Managers.""" +//| ... +// Provided by context manager helper. + +//| def __exit__(self) -> None: +//| """Automatically deinitializes the hardware when exiting a context. See +//| :ref:`lifetime-and-contextmanagers` for more info.""" +//| ... +STATIC mp_obj_t espnow_obj___exit__(size_t n_args, const mp_obj_t *args) { + (void)n_args; + return espnow_deinit(args[0]); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espnow___exit___obj, 4, 4, espnow_obj___exit__); //| def set_pmk(self, pmk: ReadableBuffer) -> None: //| """Set the ESP-NOW Primary Master Key (pmk) for encrypted communications. @@ -123,27 +145,6 @@ STATIC mp_obj_t espnow_set_pmk(mp_obj_t self_in, mp_obj_t key) { } STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_set_pmk_obj, espnow_set_pmk); -//| active: bool -//| """Initialize or de-initialize the `ESPNow` communication protocol.""" -//| -STATIC mp_obj_t espnow_get_active(const mp_obj_t self_in) { - espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(!common_hal_espnow_deinited(self)); -} -MP_DEFINE_CONST_FUN_OBJ_1(espnow_get_active_obj, espnow_get_active); - -STATIC mp_obj_t espnow_set_active(const mp_obj_t self_in, const mp_obj_t value) { - espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_is_true(value) ? common_hal_espnow_init(self) : common_hal_espnow_deinit(self); - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_2(espnow_set_active_obj, espnow_set_active); - -MP_PROPERTY_GETSET(espnow_active_obj, - (mp_obj_t)&espnow_get_active_obj, - (mp_obj_t)&espnow_set_active_obj); - - //| buffer_size: int //| """The size of the internal ring buffer.""" //| @@ -202,39 +203,8 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_get_stats_obj, espnow_get_stats); MP_PROPERTY_GETTER(espnow_stats_obj, (mp_obj_t)&espnow_get_stats_obj); -// --- Maintaining the peer table and reading RSSI values --- - -// We maintain a peers table for several reasons, to: -// - support monitoring the RSSI values for all peers; and -// - to return unique bytestrings for each peer which supports more efficient -// application memory usage and peer handling. - -// Get the RSSI value from the wifi packet header -static inline int8_t _get_rssi_from_wifi_packet(const uint8_t *msg) { - // Warning: Secret magic to get the rssi from the wifi packet header - // See espnow.c:espnow_recv_cb() at https://github.com/espressif/esp-now/ - // In the wifi packet the msg comes after a wifi_promiscuous_pkt_t - // and a espnow_frame_format_t. - // Backtrack to get a pointer to the wifi_promiscuous_pkt_t. - #define SIZEOF_ESPNOW_FRAME_FORMAT 39 - #pragma GCC diagnostic push - #pragma GCC diagnostic ignored "-Wcast-align" - wifi_promiscuous_pkt_t *wifi_packet = (wifi_promiscuous_pkt_t *)( - msg - SIZEOF_ESPNOW_FRAME_FORMAT - sizeof(wifi_promiscuous_pkt_t)); - #pragma GCC diagnostic pop - return wifi_packet->rx_ctrl.rssi; -} - -// --- Handling espnow packets in the recv buffer --- - // --- Send and Receive ESP-NOW data --- -// Return C pointer to the MAC address. -// Raise ValueError if mac is wrong type or is not 6 bytes long. -static const uint8_t *_get_peer_addr(mp_obj_t mac) { - return mp_obj_is_true(mac) ? _get_bytes_len(mac, ESP_NOW_ETH_ALEN, MP_BUFFER_READ) : NULL; -} - //| def send( //| self, //| message: ReadableBuffer, @@ -284,246 +254,38 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(espnow_send_obj, 2, espnow_send); //| //| :returns: An `ESPNowPacket` if available in the buffer, otherwise `None`.""" //| ... -STATIC mp_obj_t espnow_recv(mp_obj_t self_in, mp_obj_t buffers) { +STATIC mp_obj_t espnow_recv(mp_obj_t self_in) { espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); check_for_deinit(self); return common_hal_espnow_recv(self); } -STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_recv_obj, espnow_recv); - -// --- Peer Management Functions --- - -// Common code for add_peer() and mod_peer() to process the args. -static void _update_peer_info(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args, bool modify) { - enum { ARG_mac, ARG_lmk, ARG_channel, ARG_interface, ARG_encrypt }; - static const mp_arg_t allowed_args[] = { - { MP_QSTR_mac, MP_ARG_OBJ | MP_ARG_REQUIRED }, - { MP_QSTR_lmk, MP_ARG_OBJ, { .u_obj = mp_const_none } }, - { MP_QSTR_channel, MP_ARG_INT, { .u_obj = mp_const_none } }, - { MP_QSTR_interface,MP_ARG_INT, { .u_obj = mp_const_none } }, - { MP_QSTR_encrypt, MP_ARG_BOOL,{ .u_obj = mp_const_none } }, - }; - - mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; - mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - - esp_now_peer_info_t peer = {0}; - memcpy(peer.peer_addr, _get_peer_addr(args[ARG_mac].u_obj), ESP_NOW_ETH_ALEN); - - if (modify) { - if (esp_now_get_peer(peer.peer_addr, &peer) != ESP_OK) { - mp_raise_RuntimeError(translate("peer not found")); - } - } else { - if (esp_now_is_peer_exist(peer.peer_addr)) { - mp_raise_RuntimeError(translate("peer already exists")); - } - peer.channel = 0; - peer.ifidx = WIFI_IF_STA; - peer.encrypt = false; - } - - const mp_obj_t channel = args[ARG_channel].u_obj; - if (channel != mp_const_none) { - peer.channel = mp_arg_validate_int_range(mp_obj_get_int(channel), 0, 14, MP_QSTR_channel); - } - - const mp_obj_t interface = args[ARG_interface].u_obj; - if (interface != mp_const_none) { - peer.ifidx = (wifi_interface_t)mp_arg_validate_int_range(mp_obj_get_int(interface), 0, 1, MP_QSTR_interface); - } - - const mp_obj_t encrypt = args[ARG_encrypt].u_obj; - if (encrypt != mp_const_none) { - peer.encrypt = mp_obj_is_true(encrypt); - } - - const mp_obj_t lmk = args[ARG_lmk].u_obj; - if (lmk != mp_const_none) { - memcpy(peer.lmk, _get_bytes_len(lmk, ESP_NOW_KEY_LEN, MP_BUFFER_READ), ESP_NOW_KEY_LEN); - } else if (peer.encrypt) { - mp_raise_ValueError_varg(translate("%q is %q"), MP_QSTR_lmk, MP_QSTR_None); - } - - check_esp_err((modify) ? esp_now_mod_peer(&peer) : esp_now_add_peer(&peer)); -} - -// Update the cached peer count in self->peers_count; -// The peers_count ignores broadcast and multicast addresses and is used for the -// send() logic and is updated from add_peer(), mod_peer() and del_peer(). -static void _update_peer_count(espnow_obj_t *self) { - esp_now_peer_info_t peer = {0}; - bool from_head = true; - int count = 0; - - // esp_now_fetch_peer() skips over any broadcast or multicast addresses - while (esp_now_fetch_peer(from_head, &peer) == ESP_OK) { - from_head = false; - if (++count >= ESP_NOW_MAX_TOTAL_PEER_NUM) { - break; // Should not happen - } - } - - self->peers_count = count; -} - -//| def add_peer( -//| self, -//| mac: ReadableBuffer, -//| lmk: Optional[ReadableBuffer], -//| channel: int = 0, -//| interface: int = 0, -//| encrypt: bool = False, -//| ) -> None: -//| """Add peer. -//| -//| :param ReadableBuffer mac: The mac address of the peer. -//| :param ReadableBuffer lmk: The Local Master Key (lmk) of the peer. -//| :param int channel: The peer's channel. Default: 0 ie. use the current channel. -//| :param int interface: The WiFi interface to use. Default: 0 ie. STA. -//| :param bool encrypt: Whether or not to use encryption.""" -//| ... -STATIC mp_obj_t espnow_add_peer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - espnow_obj_t *self = pos_args[0]; - check_for_deinit(self); - - _update_peer_info(n_args, pos_args, kw_args, false); - _update_peer_count(self); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(espnow_add_peer_obj, 2, espnow_add_peer); - -//| def mod_peer( -//| self, -//| mac: ReadableBuffer, -//| lmk: Optional[ReadableBuffer], -//| channel: int = 0, -//| interface: int = 0, -//| encrypt: bool = False, -//| ) -> None: -//| """Modify peer. -//| -//| :param ReadableBuffer mac: The mac address of the peer. -//| :param ReadableBuffer lmk: The Local Master Key (lmk) of the peer. -//| :param int channel: The peer's channel. Default: 0 ie. use the current channel. -//| :param int interface: The WiFi interface to use. Default: 0 ie. STA. -//| :param bool encrypt: Whether or not to use encryption.""" -//| ... -STATIC mp_obj_t espnow_mod_peer(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - espnow_obj_t *self = pos_args[0]; - check_for_deinit(self); - - _update_peer_info(n_args, pos_args, kw_args, true); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_KW(espnow_mod_peer_obj, 2, espnow_mod_peer); - -//| def del_peer(self, mac: ReadableBuffer) -> None: -//| """Delete peer. -//| -//| :param ReadableBuffer mac: The mac address of the peer.""" -//| ... -STATIC mp_obj_t espnow_del_peer(mp_obj_t self_in, mp_obj_t mac) { - espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - - uint8_t peer_addr[ESP_NOW_ETH_ALEN]; - memcpy(peer_addr, _get_peer_addr(mac), ESP_NOW_ETH_ALEN); - - check_esp_err(esp_now_del_peer(peer_addr)); - _update_peer_count(self); - - return mp_const_none; -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_del_peer_obj, espnow_del_peer); - -// Convert a peer_info struct to python tuple -// Used by espnow_get_peer() and espnow_get_peers() -static mp_obj_t _peer_info_to_tuple(const esp_now_peer_info_t *peer) { - return MP_OBJ_NEW_TUPLE( - mp_obj_new_bytes(peer->peer_addr, MP_ARRAY_SIZE(peer->peer_addr)), - mp_obj_new_bytes(peer->lmk, MP_ARRAY_SIZE(peer->lmk)), - mp_obj_new_int(peer->channel), - mp_obj_new_int(peer->ifidx), - mp_obj_new_bool(peer->encrypt)); -} - -//| def get_peer(self, mac: ReadableBuffer) -> Tuple[bytes, int, int, bool]: -//| """Get the peer info for mac as a `tuple`. -//| -//| :param ReadableBuffer mac: The mac address of the peer. -//| -//| :returns: A `tuple` of (mac, lmk, channel, interface, encrypt).""" -//| ... -STATIC mp_obj_t espnow_get_peer(mp_obj_t self_in, mp_obj_t mac) { - espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - - esp_now_peer_info_t peer = {0}; - memcpy(peer.peer_addr, _get_peer_addr(mac), ESP_NOW_ETH_ALEN); - check_esp_err(esp_now_get_peer(peer.peer_addr, &peer)); - - return _peer_info_to_tuple(&peer); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_get_peer_obj, espnow_get_peer); +STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_recv_obj, espnow_recv); // --- Peer Related Properties --- -//| peers: Tuple[Tuple[bytes, bytes, int, int, bool], ...] -//| """The peer info records for all registered `ESPNow` peers. (read-only) -//| -//| A `tuple` of tuples: ((mac, lmk, channel, interface, encrypt), ...).""" +//| peers: Peers +//| """The peer info records for all registered `ESPNow` peers. (read-only)""" //| STATIC mp_obj_t espnow_get_peers(mp_obj_t self_in) { espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); check_for_deinit(self); - - // Build and initialize the peer info tuple. - mp_obj_tuple_t *peerinfo_tuple = mp_obj_new_tuple(self->peers_count, NULL); - esp_now_peer_info_t peer = {0}; - - for (size_t i = 0; i < peerinfo_tuple->len; i++) { - esp_err_t status = esp_now_fetch_peer((i == 0), &peer); - peerinfo_tuple->items[i] = (status == ESP_OK ? _peer_info_to_tuple(&peer) : mp_const_none); - } - - return peerinfo_tuple; + return MP_OBJ_FROM_PTR(self->peers); } STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_get_peers_obj, espnow_get_peers); MP_PROPERTY_GETTER(espnow_peers_obj, (mp_obj_t)&espnow_get_peers_obj); -//| peers_count: Tuple[int, int] -//| """The number of registered peers in a `tuple` of (num_total_peers, num_encrypted_peers). (read-only)""" -//| -STATIC mp_obj_t espnow_get_peers_count(mp_obj_t self_in) { - espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); - check_for_deinit(self); - - esp_now_peer_num_t peer_num = {0}; - check_esp_err(esp_now_get_peer_num(&peer_num)); - return MP_OBJ_NEW_TUPLE( - mp_obj_new_int(peer_num.total_num), - mp_obj_new_int(peer_num.encrypt_num)); -} -STATIC MP_DEFINE_CONST_FUN_OBJ_1(espnow_get_peers_count_obj, espnow_get_peers_count); - -MP_PROPERTY_GETTER(espnow_peers_count_obj, - (mp_obj_t)&espnow_get_peers_count_obj); - //| peers_table: Dict[bytes, List[int]] //| """The dictionary of peers we have seen. (read-only) //| //| A `dict` of {peer: [rssi, time], ...} //| //| where: -//| peer is a byte string containing the 6-byte mac address of the peer. -//| rssi is the wifi signal strength from the last msg received (in dBm from -127 to 0). -//| time is the time in milliseconds since device last booted.""" +//| * peer is a byte string containing the 6-byte mac address of the peer. +//| * rssi is the wifi signal strength from the last msg received (in dBm from -127 to 0). +//| * time is the time in milliseconds since device last booted.""" //| STATIC mp_obj_t espnow_get_peers_table(mp_obj_t self_in) { espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); @@ -535,26 +297,25 @@ MP_PROPERTY_GETTER(espnow_peers_table_obj, (mp_obj_t)&espnow_get_peers_table_obj); STATIC const mp_rom_map_elem_t espnow_locals_dict_table[] = { + // Context managers + { MP_ROM_QSTR(MP_QSTR___enter__), MP_ROM_PTR(&mp_identity_obj) }, + { MP_ROM_QSTR(MP_QSTR___exit__), MP_ROM_PTR(&espnow___exit___obj) }, + + { MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&espnow_deinit_obj) }, + // Config parameters { MP_ROM_QSTR(MP_QSTR_set_pmk), MP_ROM_PTR(&espnow_set_pmk_obj) }, - { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&espnow_active_obj) }, { MP_ROM_QSTR(MP_QSTR_buffer_size), MP_ROM_PTR(&espnow_buffer_size_obj) }, { MP_ROM_QSTR(MP_QSTR_phy_rate), MP_ROM_PTR(&espnow_phy_rate_obj) }, + { MP_ROM_QSTR(MP_QSTR_stats), MP_ROM_PTR(&espnow_stats_obj) }, // Send and receive messages { MP_ROM_QSTR(MP_QSTR_send), MP_ROM_PTR(&espnow_send_obj) }, { MP_ROM_QSTR(MP_QSTR_recv), MP_ROM_PTR(&espnow_recv_obj) }, - // Peer management functions - { MP_ROM_QSTR(MP_QSTR_add_peer), MP_ROM_PTR(&espnow_add_peer_obj) }, - { MP_ROM_QSTR(MP_QSTR_mod_peer), MP_ROM_PTR(&espnow_mod_peer_obj) }, - { MP_ROM_QSTR(MP_QSTR_del_peer), MP_ROM_PTR(&espnow_del_peer_obj) }, - { MP_ROM_QSTR(MP_QSTR_get_peer), MP_ROM_PTR(&espnow_get_peer_obj) }, - // Peer related properties { MP_ROM_QSTR(MP_QSTR_peers), MP_ROM_PTR(&espnow_peers_obj) }, - { MP_ROM_QSTR(MP_QSTR_peers_count), MP_ROM_PTR(&espnow_peers_count_obj) }, { MP_ROM_QSTR(MP_QSTR_peers_table), MP_ROM_PTR(&espnow_peers_table_obj) }, }; STATIC MP_DEFINE_CONST_DICT(espnow_locals_dict, espnow_locals_dict_table); @@ -570,7 +331,7 @@ STATIC mp_uint_t espnow_stream_ioctl(mp_obj_t self_in, mp_uint_t request, uintpt } espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); - return (common_hal_espnow_deinited(self)) ? 0 : // If not initialized + return (common_hal_espnow_deinited(self)) ? 0 : // If not initialized arg ^ ( // If no data in the buffer, unset the Read ready flag ((!ringbuf_num_filled(self->recv_buffer)) ? MP_STREAM_POLL_RD : 0) | @@ -590,6 +351,7 @@ STATIC const mp_stream_p_t espnow_stream_p = { //| def __len__(self) -> int: //| """Return the number of `bytes` available to read. Used to implement ``len()``.""" //| ... +//| STATIC mp_obj_t espnow_unary_op(mp_unary_op_t op, mp_obj_t self_in) { espnow_obj_t *self = MP_OBJ_TO_PTR(self_in); size_t len = ringbuf_num_filled(self->recv_buffer); @@ -599,7 +361,7 @@ STATIC mp_obj_t espnow_unary_op(mp_unary_op_t op, mp_obj_t self_in) { case MP_UNARY_OP_LEN: return mp_obj_new_int_from_uint(len); default: - return MP_OBJ_NULL; // op not supported + return MP_OBJ_NULL; // op not supported } } diff --git a/ports/espressif/bindings/espnow/ESPNowPacket.c b/ports/espressif/bindings/espnow/ESPNowPacket.c index 82fcfcce98..9d582a00eb 100644 --- a/ports/espressif/bindings/espnow/ESPNowPacket.c +++ b/ports/espressif/bindings/espnow/ESPNowPacket.c @@ -32,7 +32,7 @@ //| mac: ReadableBuffer //| """The sender's mac address (length = 6 bytes)""" //| -//| msg: RedableBuffer +//| msg: ReadableBuffer //| """The message sent by the peer (length <= 250 bytes)""" //| //| rssi: int diff --git a/ports/espressif/bindings/espnow/Peer.c b/ports/espressif/bindings/espnow/Peer.c new file mode 100644 index 0000000000..6dcf2732aa --- /dev/null +++ b/ports/espressif/bindings/espnow/Peer.c @@ -0,0 +1,254 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 MicroDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/objproperty.h" +#include "py/runtime.h" + +#include "bindings/espnow/Peer.h" + +// TODO: check peer already exist +// TODO: check peer dosen't exist + +// Return C pointer to the ReadableBuffer. +// Raise ValueError if the length does not match expected len. +static const uint8_t *_get_bytes_len(mp_obj_t obj, size_t len, mp_uint_t rw) { + mp_buffer_info_t bufinfo; + mp_get_buffer_raise(obj, &bufinfo, rw); + mp_arg_validate_length(bufinfo.len, len, MP_QSTR_buffer); + return (uint8_t *)bufinfo.buf; +} + +// Return C pointer to the MAC address. +// Raise ValueError if mac is wrong type or is not 6 bytes long. +static const uint8_t *_get_peer_addr(mp_obj_t mac) { + return mp_obj_is_true(mac) ? _get_bytes_len(mac, ESP_NOW_ETH_ALEN, MP_BUFFER_READ) : NULL; +} + +//| class Peer: +//| """A data class to store parameters specific to a peer.""" +//| +//| def __init__( +//| self, +//| mac: bytes, +//| lmk: Optional[bytes], +//| channel: int = 0, +//| interface: int = 0, +//| encrypt: bool = False, +//| ) -> None: +//| """Construct a new peer object. +//| +//| :param bytes mac: The mac address of the peer. +//| :param bytes lmk: The Local Master Key (lmk) of the peer. +//| :param int channel: The peer's channel. Default: 0 ie. use the current channel. +//| :param int interface: The WiFi interface to use. Default: 0 ie. STA. +//| :param bool encrypt: Whether or not to use encryption. +//| """ +//| ... +STATIC mp_obj_t espnow_peer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + enum { ARG_mac, ARG_lmk, ARG_channel, ARG_interface, ARG_encrypt }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_mac, MP_ARG_OBJ | MP_ARG_REQUIRED }, + { MP_QSTR_lmk, MP_ARG_OBJ, { .u_obj = mp_const_none } }, + { MP_QSTR_channel, MP_ARG_INT, { .u_obj = mp_const_none } }, + { MP_QSTR_interface,MP_ARG_INT, { .u_obj = mp_const_none } }, + { MP_QSTR_encrypt, MP_ARG_BOOL,{ .u_obj = mp_const_none } }, + }; + + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all_kw_array(n_args, n_kw, all_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + espnow_peer_obj_t *self = m_new_obj(espnow_peer_obj_t); + self->base.type = &espnow_peer_type; + self->peer_info = (esp_now_peer_info_t) { + .channel = 0, + .ifidx = WIFI_IF_STA, + .encrypt = false + }; + + memcpy(self->peer_info.peer_addr, _get_peer_addr(args[ARG_mac].u_obj), ESP_NOW_ETH_ALEN); + + const mp_obj_t channel = args[ARG_channel].u_obj; + if (channel != mp_const_none) { + self->peer_info.channel = mp_arg_validate_int_range(mp_obj_get_int(channel), 0, 14, MP_QSTR_channel); + } + + const mp_obj_t interface = args[ARG_interface].u_obj; + if (interface != mp_const_none) { + self->peer_info.ifidx = (wifi_interface_t)mp_arg_validate_int_range(mp_obj_get_int(interface), 0, 1, MP_QSTR_interface); + } + + const mp_obj_t encrypt = args[ARG_encrypt].u_obj; + if (encrypt != mp_const_none) { + self->peer_info.encrypt = mp_obj_is_true(encrypt); + } + + const mp_obj_t lmk = args[ARG_lmk].u_obj; + if (lmk != mp_const_none) { + memcpy(self->peer_info.lmk, _get_bytes_len(lmk, ESP_NOW_KEY_LEN, MP_BUFFER_READ), ESP_NOW_KEY_LEN); + } else if (self->peer_info.encrypt && !self->peer_info.lmk) { + mp_raise_ValueError_varg(translate("%q is %q"), MP_QSTR_lmk, MP_QSTR_None); + } + + return self; +} + +//| mac: ReadableBuffer +//| """The WiFi mac to use.""" +//| +STATIC mp_obj_t espnow_peer_get_mac(const mp_obj_t self_in) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bytes(self->peer_info.peer_addr, MP_ARRAY_SIZE(self->peer_info.peer_addr)); +} +MP_DEFINE_CONST_FUN_OBJ_1(espnow_peer_get_mac_obj, espnow_peer_get_mac); + +STATIC mp_obj_t espnow_peer_set_mac(const mp_obj_t self_in, const mp_obj_t value) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + memcpy(self->peer_info.peer_addr, _get_peer_addr(value), ESP_NOW_ETH_ALEN); + esp_now_mod_peer(&self->peer_info); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(espnow_peer_set_mac_obj, espnow_peer_set_mac); + +MP_PROPERTY_GETSET(espnow_peer_mac_obj, + (mp_obj_t)&espnow_peer_get_mac_obj, + (mp_obj_t)&espnow_peer_set_mac_obj); + +//| lmk: ReadableBuffer +//| """The WiFi lmk to use.""" +//| +STATIC mp_obj_t espnow_peer_get_lmk(const mp_obj_t self_in) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bytes(self->peer_info.lmk, MP_ARRAY_SIZE(self->peer_info.lmk)); +} +MP_DEFINE_CONST_FUN_OBJ_1(espnow_peer_get_lmk_obj, espnow_peer_get_lmk); + +STATIC mp_obj_t espnow_peer_set_lmk(const mp_obj_t self_in, const mp_obj_t value) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + memcpy(self->peer_info.lmk, _get_bytes_len(value, ESP_NOW_KEY_LEN, MP_BUFFER_READ), ESP_NOW_KEY_LEN); + esp_now_mod_peer(&self->peer_info); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(espnow_peer_set_lmk_obj, espnow_peer_set_lmk); + +MP_PROPERTY_GETSET(espnow_peer_lmk_obj, + (mp_obj_t)&espnow_peer_get_lmk_obj, + (mp_obj_t)&espnow_peer_set_lmk_obj); + +//| channel: int +//| """The WiFi channel to use.""" +//| +STATIC mp_obj_t espnow_peer_get_channel(const mp_obj_t self_in) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->peer_info.channel); +} +MP_DEFINE_CONST_FUN_OBJ_1(espnow_peer_get_channel_obj, espnow_peer_get_channel); + +STATIC mp_obj_t espnow_peer_set_channel(const mp_obj_t self_in, const mp_obj_t value) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->peer_info.channel = mp_arg_validate_int_range(mp_obj_get_int(value), 0, 14, MP_QSTR_channel); + esp_now_mod_peer(&self->peer_info); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(espnow_peer_set_channel_obj, espnow_peer_set_channel); + +MP_PROPERTY_GETSET(espnow_peer_channel_obj, + (mp_obj_t)&espnow_peer_get_channel_obj, + (mp_obj_t)&espnow_peer_set_channel_obj); + +//| interface: int +//| """The WiFi interface to use.""" +//| +STATIC mp_obj_t espnow_peer_get_interface(const mp_obj_t self_in) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + return MP_OBJ_NEW_SMALL_INT(self->peer_info.ifidx); +} +MP_DEFINE_CONST_FUN_OBJ_1(espnow_peer_get_interface_obj, espnow_peer_get_interface); + +STATIC mp_obj_t espnow_peer_set_interface(const mp_obj_t self_in, const mp_obj_t value) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->peer_info.ifidx = (wifi_interface_t)mp_arg_validate_int_range(mp_obj_get_int(value), 0, 1, MP_QSTR_interface); + esp_now_mod_peer(&self->peer_info); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(espnow_peer_set_interface_obj, espnow_peer_set_interface); + +MP_PROPERTY_GETSET(espnow_peer_interface_obj, + (mp_obj_t)&espnow_peer_get_interface_obj, + (mp_obj_t)&espnow_peer_set_interface_obj); + +//| encrypted: bool +//| """Whether or not to use encryption.""" +//| +STATIC mp_obj_t espnow_peer_get_encrypted(const mp_obj_t self_in) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->peer_info.encrypt); +} +MP_DEFINE_CONST_FUN_OBJ_1(espnow_peer_get_encrypted_obj, espnow_peer_get_encrypted); + +STATIC mp_obj_t espnow_peer_set_encrypted(const mp_obj_t self_in, const mp_obj_t value) { + espnow_peer_obj_t *self = MP_OBJ_TO_PTR(self_in); + + self->peer_info.encrypt = mp_obj_is_true(value); + + if (!self->peer_info.lmk) { + mp_raise_ValueError_varg(translate("%q is %q"), MP_QSTR_lmk, MP_QSTR_None); + } + + esp_now_mod_peer(&self->peer_info); + + return mp_const_none; +} +MP_DEFINE_CONST_FUN_OBJ_2(espnow_peer_set_encrypted_obj, espnow_peer_set_encrypted); + +MP_PROPERTY_GETSET(espnow_peer_encrypted_obj, + (mp_obj_t)&espnow_peer_get_encrypted_obj, + (mp_obj_t)&espnow_peer_set_encrypted_obj); + +STATIC const mp_rom_map_elem_t espnow_peer_locals_dict_table[] = { + // Peer parameters + { MP_ROM_QSTR(MP_QSTR_mac), MP_ROM_PTR(&espnow_peer_mac_obj) }, + { MP_ROM_QSTR(MP_QSTR_lmk), MP_ROM_PTR(&espnow_peer_lmk_obj) }, + { MP_ROM_QSTR(MP_QSTR_channel), MP_ROM_PTR(&espnow_peer_channel_obj) }, + { MP_ROM_QSTR(MP_QSTR_interface), MP_ROM_PTR(&espnow_peer_interface_obj) }, + { MP_ROM_QSTR(MP_QSTR_encrypted), MP_ROM_PTR(&espnow_peer_encrypted_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(espnow_peer_locals_dict, espnow_peer_locals_dict_table); + +const mp_obj_type_t espnow_peer_type = { + { &mp_type_type }, + .name = MP_QSTR_Peer, + .make_new = espnow_peer_make_new, + .locals_dict = (mp_obj_t)&espnow_peer_locals_dict, +}; diff --git a/ports/espressif/bindings/espnow/Peer.h b/ports/espressif/bindings/espnow/Peer.h new file mode 100644 index 0000000000..e4cb828472 --- /dev/null +++ b/ports/espressif/bindings/espnow/Peer.h @@ -0,0 +1,37 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 MicroDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "py/obj.h" +#include "esp_now.h" + +typedef struct { + mp_obj_base_t base; + esp_now_peer_info_t peer_info; +} espnow_peer_obj_t; + +const mp_obj_type_t espnow_peer_type; diff --git a/ports/espressif/bindings/espnow/Peers.c b/ports/espressif/bindings/espnow/Peers.c new file mode 100644 index 0000000000..4f0c310f3d --- /dev/null +++ b/ports/espressif/bindings/espnow/Peers.c @@ -0,0 +1,221 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 MicroDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "py/obj.h" +#include "py/objlist.h" +#include "py/runtime.h" + +#include "bindings/espnow/Peer.h" +#include "bindings/espnow/Peers.h" + +#include "esp_now.h" + +static void check_esp_err(esp_err_t status) { + if (status != ESP_OK) { + mp_raise_RuntimeError(translate("an error occured")); + } +} + +//| class Peers: +//| """A class that provides peer managment functions. Sequence[Peer].""" +//| + +//| def append(self, peer: Peer) -> None: +//| """Append peer. +//| +//| :param Peer peer: The peer object to append. +//| """ +//| ... +STATIC mp_obj_t espnow_peers_append(mp_obj_t self_in, mp_obj_t arg) { + espnow_peer_obj_t *peer = MP_OBJ_TO_PTR(mp_arg_validate_type(arg, &espnow_peer_type, MP_QSTR_Peer)); + check_esp_err(esp_now_add_peer(&peer->peer_info)); + espnow_peers_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_list_append(self->list, arg); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_peers_append_obj, espnow_peers_append); + +//| def remove(self, peer: Peer) -> None: +//| """Remove peer. +//| +//| :param Peer peer: The peer object to remove. +//| """ +//| ... +//| +STATIC mp_obj_t espnow_peers_remove(mp_obj_t self_in, mp_obj_t arg) { + espnow_peer_obj_t *peer = MP_OBJ_TO_PTR(mp_arg_validate_type(arg, &espnow_peer_type, MP_QSTR_Peer)); + check_esp_err(esp_now_del_peer(peer->peer_info.peer_addr)); + espnow_peers_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_list_remove(self->list, arg); +} +STATIC MP_DEFINE_CONST_FUN_OBJ_2(espnow_peers_remove_obj, espnow_peers_remove); + +STATIC const mp_rom_map_elem_t espnow_peers_locals_dict_table[] = { + // Peer management functions + { MP_ROM_QSTR(MP_QSTR_append), MP_ROM_PTR(&espnow_peers_append_obj) }, + { MP_ROM_QSTR(MP_QSTR_remove), MP_ROM_PTR(&espnow_peers_remove_obj) }, +}; +STATIC MP_DEFINE_CONST_DICT(espnow_peers_locals_dict, espnow_peers_locals_dict_table); + +/******************************************************************************/ +/* peers print */ + +STATIC void espnow_peers_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { + espnow_peers_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *list = MP_OBJ_TO_PTR(self->list); + const char *item_separator = ", "; + if (!(MICROPY_PY_UJSON && kind == PRINT_JSON)) { + kind = PRINT_REPR; + } else { + #if MICROPY_PY_UJSON_SEPARATORS + item_separator = MP_PRINT_GET_EXT(print)->item_separator; + #endif + } + mp_print_str(print, "["); + for (size_t i = 0; i < list->len; i++) { + if (i > 0) { + mp_print_str(print, item_separator); + } + mp_obj_print_helper(print, list->items[i], kind); + } + mp_print_str(print, "]"); +} + +/******************************************************************************/ +/* peers unary_op */ + +STATIC mp_obj_t espnow_peers_unary_op(mp_unary_op_t op, mp_obj_t self_in) { + espnow_peers_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *list = MP_OBJ_TO_PTR(self->list); + switch (op) { + case MP_UNARY_OP_BOOL: + return mp_obj_new_bool(list->len != 0); + case MP_UNARY_OP_LEN: + return MP_OBJ_NEW_SMALL_INT(list->len); + #if MICROPY_PY_SYS_GETSIZEOF + case MP_UNARY_OP_SIZEOF: { + size_t sz = sizeof(*list) + sizeof(mp_obj_t) * list->alloc; + return MP_OBJ_NEW_SMALL_INT(sz); + } + #endif + default: + return MP_OBJ_NULL; // op not supported + } +} + +/******************************************************************************/ +/* peers subscript */ + +STATIC mp_obj_t espnow_peers_subscr(mp_obj_t self_in, mp_obj_t index, mp_obj_t value) { + if (value != MP_OBJ_SENTINEL) { + return MP_OBJ_NULL; // op not supported + } + + espnow_peers_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_obj_list_t *list = MP_OBJ_TO_PTR(self->list); + + // load + #if MICROPY_PY_BUILTINS_SLICE + if (mp_obj_is_type(index, &mp_type_slice)) { + mp_bound_slice_t slice; + if (!mp_seq_get_fast_slice_indexes(list->len, index, &slice)) { + return mp_seq_extract_slice(list->len, list->items, &slice); + } + + mp_obj_list_t *res = MP_OBJ_TO_PTR(mp_obj_new_list(slice.stop - slice.start, NULL)); + mp_seq_copy(res->items, list->items + slice.start, res->len, mp_obj_t); + return MP_OBJ_FROM_PTR(res); + } + #endif + size_t index_val = mp_get_index(list->base.type, list->len, index, false); + return list->items[index_val]; +} + +/******************************************************************************/ +/* peers iterator */ + +typedef struct _espnow_peers_it_t { + mp_obj_base_t base; + mp_fun_1_t iternext; + mp_obj_t peers; + size_t cur; +} espnow_peers_it_t; + +STATIC mp_obj_t espnow_peers_it_iternext(mp_obj_t self_in) { + espnow_peers_it_t *self = MP_OBJ_TO_PTR(self_in); + espnow_peers_obj_t *peers = MP_OBJ_TO_PTR(self->peers); + mp_obj_list_t *list = MP_OBJ_TO_PTR(peers->list); + if (self->cur < list->len) { + mp_obj_t o_out = list->items[self->cur]; + self->cur += 1; + return o_out; + } else { + return MP_OBJ_STOP_ITERATION; + } +} + +STATIC mp_obj_t espnow_peers_getiter(mp_obj_t o_in, mp_obj_iter_buf_t *iter_buf) { + assert(sizeof(espnow_peers_it_t) <= sizeof(mp_obj_iter_buf_t)); + espnow_peers_it_t *o = (espnow_peers_it_t *)iter_buf; + o->base.type = &mp_type_polymorph_iter; + o->iternext = espnow_peers_it_iternext; + o->peers = o_in; + o->cur = 0; + return MP_OBJ_FROM_PTR(o); +} + +espnow_peers_obj_t *espnow_peers_new(void) { + espnow_peers_obj_t *self = m_new_obj(espnow_peers_obj_t); + self->base.type = &espnow_peers_type; + self->list = mp_obj_new_list(0, NULL); + return self; +} + +const mp_obj_type_t espnow_peers_type = { + { &mp_type_type }, + .name = MP_QSTR_Peers, + .print = espnow_peers_print, + // .make_new = espnow_peers_make_new, + .locals_dict = (mp_obj_t)&espnow_peers_locals_dict, + .flags = MP_TYPE_FLAG_EXTENDED, + MP_TYPE_EXTENDED_FIELDS( + .unary_op = espnow_peers_unary_op, + .subscr = espnow_peers_subscr, + .getiter = espnow_peers_getiter, + ), +}; + +/* +STATIC mp_obj_t espnow_peers_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) { + (void)type_in; + mp_arg_check_num(n_args, n_kw, 0, 1, false); + + espnow_peers_obj_t *self = m_new_obj(espnow_peers_obj_t); + self->base.type = &espnow_peers_type; + self->list = mp_obj_new_list_from_iter(args[0]); + + return MP_OBJ_FROM_PTR(self); +} +*/ diff --git a/ports/espressif/bindings/espnow/Peers.h b/ports/espressif/bindings/espnow/Peers.h new file mode 100644 index 0000000000..e871ae86c0 --- /dev/null +++ b/ports/espressif/bindings/espnow/Peers.h @@ -0,0 +1,37 @@ +/* + * This file is part of the Micro Python project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2023 MicroDev + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#pragma once + +#include "py/obj.h" + +typedef struct { + mp_obj_base_t base; + mp_obj_t list; +} espnow_peers_obj_t; + +extern const mp_obj_type_t espnow_peers_type; +extern espnow_peers_obj_t *espnow_peers_new(void); diff --git a/ports/espressif/bindings/espnow/__init__.c b/ports/espressif/bindings/espnow/__init__.c index 746e9696a9..d92221c691 100644 --- a/ports/espressif/bindings/espnow/__init__.c +++ b/ports/espressif/bindings/espnow/__init__.c @@ -29,6 +29,8 @@ #include "bindings/espnow/__init__.h" #include "bindings/espnow/ESPNow.h" #include "bindings/espnow/ESPNowPacket.h" +#include "bindings/espnow/Peer.h" +#include "bindings/espnow/Peers.h" //| """ESP-NOW Module //| @@ -44,14 +46,13 @@ //| import espnow //| //| e = espnow.ESPNow() -//| e.active(True) -//| peer = b'\xbb\xbb\xbb\xbb\xbb\xbb' # MAC address of peer's wifi interface -//| e.add_peer(peer) +//| peer = espnow.Peer(mac=b'\xaa\xaa\xaa\xaa\xaa\xaa') +//| e.peers.append(peer) //| -//| e.send("Starting...") # Send to all peers +//| e.send("Starting...") //| for i in range(100): //| e.send(peer, str(i)*20, True) -//| e.send(b'end') +//| e.send(b'end') //| //| **Receiver** //| @@ -60,16 +61,18 @@ //| import espnow //| //| e = espnow.ESPNow() -//| e.active(True) -//| peer = b'\xaa\xaa\xaa\xaa\xaa\xaa' # MAC address of peer's wifi interface -//| e.add_peer(peer) +//| packets = [] //| //| while True: -//| host, msg = e.recv() -//| if msg: # msg == None if timeout in recv() -//| print(host, msg) -//| if msg == b'end': +//| if e: +//| packet = e.recv() +//| packets.append(packet) +//| if packet.msg == b'end': //| break +//| +//| print("packets:", f"length={len(packets)}") +//| for packet in packets: +//| print(packet) //| """ //| ... //| @@ -81,6 +84,8 @@ STATIC const mp_rom_map_elem_t espnow_module_globals_table[] = { // module classes { MP_ROM_QSTR(MP_QSTR_ESPNow), MP_ROM_PTR(&espnow_type) }, { MP_ROM_QSTR(MP_QSTR_ESPNowPacket),MP_ROM_PTR(&espnow_packet_type_obj) }, + { MP_ROM_QSTR(MP_QSTR_Peer), MP_ROM_PTR(&espnow_peer_type) }, + { MP_ROM_QSTR(MP_QSTR_Peers), MP_ROM_PTR(&espnow_peers_type) }, }; STATIC MP_DEFINE_CONST_DICT(espnow_module_globals, espnow_module_globals_table); diff --git a/ports/espressif/common-hal/espnow/ESPNow.c b/ports/espressif/common-hal/espnow/ESPNow.c index 6e49f5f54a..e906c2a9d6 100644 --- a/ports/espressif/common-hal/espnow/ESPNow.c +++ b/ports/espressif/common-hal/espnow/ESPNow.c @@ -123,7 +123,7 @@ static void recv_cb(const uint8_t *mac, const uint8_t *msg, int msg_len) { } bool common_hal_espnow_deinited(espnow_obj_t *self) { - return self->recv_buffer == NULL; + return self == NULL || self->recv_buffer == NULL; } // Initialize the ESP-NOW software stack, @@ -154,7 +154,7 @@ void common_hal_espnow_init(espnow_obj_t *self) { // De-initialize the ESP-NOW software stack, // disable callbacks and deallocate the recv data buffers. void common_hal_espnow_deinit(espnow_obj_t *self) { - if (self == NULL || common_hal_espnow_deinited(self)) { + if (common_hal_espnow_deinited(self)) { return; } @@ -164,7 +164,7 @@ void common_hal_espnow_deinit(espnow_obj_t *self) { self->recv_buffer->buf = NULL; self->recv_buffer = NULL; - self->peers_count = 0; // esp_now_deinit() removes all peers. + // self->peers_count = 0; // esp_now_deinit() removes all peers. self->tx_packets = self->tx_responses; } @@ -278,7 +278,7 @@ mp_obj_t common_hal_espnow_send(espnow_obj_t *self, const bool sync, const uint8 // Increment the sent packet count. // If mac == NULL msg will be sent to all peers EXCEPT any broadcast or multicast addresses. - self->tx_packets += ((mac == NULL) ? self->peers_count : 1); + self->tx_packets += ((mac == NULL) ? ((mp_obj_list_t *)self->peers->list)->len : 1); if (sync) { // Wait for and tally all the expected responses from peers diff --git a/ports/espressif/common-hal/espnow/ESPNow.h b/ports/espressif/common-hal/espnow/ESPNow.h index 54761c00df..85108af7a7 100644 --- a/ports/espressif/common-hal/espnow/ESPNow.h +++ b/ports/espressif/common-hal/espnow/ESPNow.h @@ -24,8 +24,13 @@ * THE SOFTWARE. */ +#pragma once + +#include "py/obj.h" #include "py/ringbuf.h" +#include "bindings/espnow/Peers.h" + #include "esp_wifi.h" // The data structure for the espnow_singleton. @@ -39,7 +44,7 @@ typedef struct _espnow_obj_t { size_t tx_packets; // # of sent packets volatile size_t tx_responses; // # of sent packet responses received volatile size_t tx_failures; // # of sent packet responses failed - size_t peers_count; // Cache the # of peers for send(sync=True) + espnow_peers_obj_t *peers; // Cache the # of peers for send(sync=True) mp_obj_t peers_table; // A dictionary of discovered peers } espnow_obj_t;