8000 esp/espnow: Bugfix and code cleanups · micropython/micropython@4144ba4 · GitHub
[go: up one dir, main page]

Skip to content

Commit 4144ba4

Browse files
committed
esp/espnow: Bugfix and code cleanups
- Delete DEFAULT_SEND_TIMEOUT - irecv(): Clear msg buf on timeout. On timeout, set the length of the callee-owned message buffer to zero. Update docs accordingly. - Unify ring buffer code for esp32/8266. Use common ring_buffer.[ch] code for the esp32 and esp8266 ports. On the ESP8266 the functions in ring_buffer.c will be declared static and "#included" by esp_espnow.c to reduce code size. - Change buffer_get()/put() to pass uint8_t pointers instead of void, because void pointer arithmetic is invalid in standard C. - Docs: add note on peer management. Minor clarification of docs. - De-inline some 8266 buffer funcs. Steps toward common 8266/32 code. - Bugfix: mod_peer(): fix alignment of args.
1 parent 53002c0 commit 4144ba4

File tree

7 files changed

+435
-197
lines changed

7 files changed

+435
-197
lines changed

docs/library/espnow.rst

Lines changed: 70 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
:synopsis: ESP-NOW wireless protocol support
66

77
This module provides an interface to the ESP-NOW protocol provided by Espressif
8-
on ESP32 and ESP8266 devices.
8+
on ESP32 and ESP8266 devices. Some calls are only available on the ESP32 due to
9+
code size restrictions on the ESP8266 and differences in the Espressif API.
910

1011
Load this module from the :doc:`esp` module. A simple example would be:
1112

@@ -42,11 +43,12 @@ Load this module from the :doc:`esp` module. A simple example would be:
4243
peer = b'\xaa\xaa\xaa\xaa\xaa\xaa' # MAC address of peer's wifi interface
4344
e.add_peer(peer)
4445

45-
print(e.irecv())
46-
for msg in e:
47-
print(msg)
48-
if (msg[1] == b'end')
49-
break
46+
while True:
47+
msg = e.irecv() # Available on ESP32 and ESP8266
48+
if msg: # msg == None if timeout in irecv()
49+
print(msg)
50+
if msg[1] == b'end':
51+
break
5052

5153
.. note:: This module is still under development and its classes, functions,
5254
methods and constants are subject to change.
@@ -59,7 +61,8 @@ Constructor
5961

6062
.. class:: ESPNow()
6163

62-
Returns the singleton ESPNow object.
64+
Returns the singleton ESPNow object. As this is a singleton, all calls to
65+
`espnow.ESPNow()` return a reference to the same object.
6366

6467
Configuration
6568
-------------
@@ -76,7 +79,7 @@ Configuration
7679
- register the send and recv callbacks.
7780

7881
**ESP8266**: The recv buffer size may be set as an argument to `init()` as
79-
there is no `config()` method on the ESP8266.
82+
there is no `config()` method on the ESP8266 (due to code size restrictions).
8083

8184
.. method:: ESPNow.deinit()
8285

@@ -86,11 +89,13 @@ Configuration
8689
**Note**: `deinit()` will also deregister all peers which must be
8790
re-registered after `init()`.
8891

89-
.. method:: ESPNow.config() (ESP32 only)
90-
ESPNow.config('param')
92+
.. method:: ESPNow.config('param')
9193
ESPNow.config(param=value, ...)
9294

93-
Get or set configuration values of the ESPNow interface. To get a value the
95+
**Note:** ESP32 only - Use `init([recv_bufsize])<ESPNow.init()>` on the
96+
ESP8266.
97+
98+
Get or set configuration values of the ESPNow interface. To get a value the
9499
parameter name should be quoted as a string, and just one parameter is
95100
queried at a time. To set values use the keyword syntax, and one or more
96101
parameters can be set at a time.
@@ -138,7 +143,7 @@ For example::
138143
w0 = network.WLAN(network.STA_IF)
139144
w0.active(True)
140145

141-
.. method:: ESPNow.send(mac, msg, [sync=True]) (ESP32 only)
146+
.. method:: ESPNow.send(mac, msg, [sync=True])
142147
ESPNow.send(msg) (ESP32 only)
143148

144149
Send the data contained in ``msg`` to the peer with given network ``mac``
@@ -169,15 +174,19 @@ For example::
169174

170175
.. method:: ESPNow.recv([timeout]) (ESP32 only)
171176

172-
Wait for an incoming message and return a newly allocated tuple of
173-
bytestrings: ``(mac, message)``, where:
177+
**Note:** ESP32 only. Use `irecv()` on the esp8266.
178+
179+
Wait for an incoming message and return:
180+
181+
- ``None`` if ``timeout`` is reached before a mes F438 sage is received, or
182+
- a newly allocated tuple of `bytes`: ``(mac, message)``, where:
174183

175-
- ``mac`` is the mac address of the sending device (peer) and
184+
- ``mac`` is the mac address of the sending device (peer) and
176185

177-
- ``msg`` is the message/data sent from the peer.
186+
- ``msg`` is the message/data sent from the peer.
178187

179188
``timeout`` optionally sets a timeout (in milliseconds) for the read. The
180-
default timeout can be set in `ESPNow.config()`.
189+
default timeout (5 minutes) can be set on the ESP32 using `ESPNow.config()`.
181190

182191
**Note**: repeatedly calling `recv()<ESPNow.recv()>` will exercise the
183192
micropython memory allocation as new storage is allocated for each new
@@ -186,12 +195,30 @@ For example::
186195

187196
.. method:: ESPNow.irecv([timeout])
188197

189-
As for `recv()<ESPNow.recv()>` except that ``irecv()`` will return a
190-
"callee-owned" tuple of bytearrays.
191-
That is, memory will be allocated once for the tuple and byte strings on
192-
invocation of espnow.ESPNow() and re-used for subsequent calls to
198+
Wait for an incoming message and return:
199+
200+
- ``None`` if ``timeout`` is reached before a message is received, or
201+
- a "callee-owned" tuple of `bytearray`: ``(mac, message)``, where:
202+
203+
- ``mac`` is the mac address of the sending device (peer) and
204+
205+
- ``msg`` is the message/data sent from the peer.
206+
207+
``timeout`` optionally sets a timeout (in milliseconds) for the read. The
208+
default timeout (5 minutes) can be set on the ESP32 using `ESPNow.config()`.
209+
210+
**Note**: Equivalent to `recv()<ESPNow.recv()>`, except that
211+
`irecv()<ESPNow.irecv()>` will
212+
return a "callee-owned" tuple of bytearrays.
213+
That is, memory will be allocated once for the tuple and bytearrays on
214+
invocation of `espnow.ESPNow()<ESPNow()>` and reused for subsequent calls to
193215
`irecv()<ESPNow.irecv()>`. You must make copies if you
194-
wish to keep the values across calls to ``irecv()``.
216+
wish to keep the values across subsequent calls to `irecv()<ESPNow.irecv()>`.
217+
`irecv()<ESPNow.irecv()>` is more efficient on memory constrained
218+
microcontrollers like the ESP32 and ESP8266.
219+
220+
On timeout, `irecv()` will return `None` and set the length of the
221+
callee-owned ``message`` bytearray to zero.
195222

196223
.. method:: ESPNow.stats() (ESP32 only)
197224

@@ -255,6 +282,27 @@ The Esspresif ESP-Now software requires that other devices (peers) must be
255282

256283
**ESP8266**: Keyword args may not be used on the ESP8266.
257284

285+
**Note**: Managing peers can become complex on the ESP32/8266 if you are
286+
using more than just the STA_IF interface. The ESP32/8266 effectively has two
287+
independent wifi interfaces (STA_IF and AP_IF) and each has their own MAC
288+
address. You must:
289+
290+
- choose the correct MAC address of the remote peer (STA_IF or AP_IF) to
291+
register,
292+
293+
- register it with the correct local interface (``ifidx`` = STA_IF or AP_IF),
294+
and
295+
296+
- ensure the correct interfaces are ``active(True)`` on the local and remote
297+
peer.
298+
299+
`ESPNow.send()<ESPNow.send()>` will raise an
300+
``OSError('ESP_ERR_ESPNOW_IF')``
301+
exception when trying to send a message to a peer which is registered to a
302+
local interface which is not ``active(True)``. Note also that both
303+
interfaces may be active simultaneously, leading to a lot of flexibility
304+
in configuring ESPNow and Wifi networks.
305+
258306
.. method:: ESPNow.get_peer(mac) (ESP32 only)
259307

260308
Return a 5-tuple of the "peer info" associated with the ``mac`` address::

ports/esp32/esp_espnow.c

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,6 @@
4848
#include "modnetwork.h"
4949
#include "ring_buffer.h"
5050

51-
typedef uint8_t packet_size_t;
52-
5351
static const uint8_t ESPNOW_MAGIC = 0x99;
5452

5553
// ESPNow buffer packet format:
@@ -111,7 +109,7 @@ STATIC esp_espnow_obj_t *espnow_singleton = NULL;
111109

112110
// Put received data into the buffer (called from recv_cb()).
113111
static void _buf_put_recv_data(buffer_t buf, const uint8_t *mac,
114-
const uint8_t *data, size_t data_len);
112+
const uint8_t *data, size_t msg_len);
115113
// Get the peer mac address and message from a packet in the buffer.
116114
static bool _buf_get_recv_data(buffer_t buf, uint8_t *mac, uint8_t *msg, int msg_len);
117115
// Validate a recv packet header, check message fits in max size and return
@@ -151,14 +149,15 @@ STATIC mp_obj_t espnow_make_new(const mp_obj_type_t *type, size_t n_args,
151149
self->recv_buffer_size = DEFAULT_RECV_BUFFER_SIZE;
152150
self->recv_timeout = DEFAULT_RECV_TIMEOUT;
153151

152+
// Allocate and initialise the "callee-owned" tuple for irecv().
154153
uint8_t msg_tmp[ESP_NOW_MAX_DATA_LEN], peer_tmp[ESP_NOW_ETH_ALEN];
155154
self->irecv_peer = MP_OBJ_TO_PTR(mp_obj_new_bytearray(ESP_NOW_ETH_ALEN, peer_tmp));
156155
self->irecv_msg = MP_OBJ_TO_PTR(mp_obj_new_bytearray(ESP_NOW_MAX_DATA_LEN, msg_tmp));
157156
self->irecv_tuple = mp_obj_new_tuple(2, NULL);
158157
self->irecv_tuple->items[0] = MP_OBJ_FROM_PTR(self->irecv_peer);
159158
self->irecv_tuple->items[1] = MP_OBJ_FROM_PTR(self->irecv_msg);
160159

161-
// Add to the root pointers to save ourselves and buffers from gc.
160+
// Set the global singleton pointer for the espnow protocol.
162161
espnow_singleton = self;
163162

164163
return self;
@@ -320,8 +319,7 @@ STATIC void IRAM_ATTR recv_cb(
320319
const uint8_t *mac_addr, const uint8_t *msg, int msg_len) {
321320

322321
esp_espnow_obj_t *self = espnow_singleton;
323-
if (!initialized ||
324-
ESPNOW_HDR_LEN + msg_len >= buffer_free(self->recv_buffer)) {
322+
if (ESPNOW_HDR_LEN + msg_len >= buffer_free(self->recv_buffer)) {
325323
self->dropped_rx_pkts++;
326324
return;
327325
}
@@ -409,6 +407,7 @@ STATIC mp_obj_t espnow_irecv(size_t n_args, const mp_obj_t *args) {
409407

410408
int msg_len = _wait_for_recv_packet(n_args, args);
411409
if (msg_len < 0) {
410+
self->irecv_msg->len = 0;
412411
return mp_const_none; // Timed out - just return None
413412
}
414413
if (!_buf_get_recv_data(
@@ -578,13 +577,13 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_KW(espnow_add_peer_obj, 2, espnow_add_peer);
578577
// [channel=1..11|0], [ifidx=0|1], [encrypt=True|False])
579578
// Positional args set to None will be left at current values.
580579
STATIC mp_obj_t espnow_mod_peer(
581-
size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) {
580+
size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) {
582581

583582
esp_now_peer_info_t peer = {0};
584583
memcpy(peer.peer_addr, _get_peer(args[1]), ESP_NOW_ETH_ALEN);
585584
check_esp_err(esp_now_get_peer(peer.peer_addr, &peer));
586585

587-
_update_peer_info(&peer, n_args, args, kwargs);
586+
_update_peer_info(&peer, n_args - 2, args + 2, kw_args);
588587

589588
check_esp_err(esp_now_mod_peer(&peer));
590589
_update_peer_count();
@@ -881,6 +880,7 @@ static const uint8_t *_get_bytes_len(mp_obj_t obj, size_t len) {
881880
return p;
882881
}
883882

883+
// Check obj is a byte string and return ptr to mac address.
884884
static const uint8_t *_get_peer(mp_obj_t obj) {
885885
return mp_obj_is_true(obj)
886886
? _get_bytes_len(obj, ESP_NOW_ETH_ALEN) : NULL;
@@ -892,7 +892,7 @@ _buf_put_recv_data(buffer_t buf, const uint8_t *mac,
892892
const uint8_t *msg, size_t msg_len
893893
) {
894894
uint8_t header[2] = {ESPNOW_MAGIC, msg_len};
895-
buffer_put(buf, &header, sizeof(header));
895+
buffer_put(buf, header, sizeof(header));
896896
buffer_put(buf, mac, ESP_NOW_ETH_ALEN);
897897
buffer_put(buf, msg, msg_len);
898898
}
@@ -904,7 +904,7 @@ static bool
904904
_buf_get_recv_data(buffer_t buf, uint8_t *mac, uint8_t *msg, int msg_len) {
905905
uint8_t header[2]; // Copy out the header and ignore it
906906
return msg_len > 0 &&
907-
buffer_get(buf, &header, sizeof(header)) &&
907+
buffer_get(buf, header, sizeof(header)) &&
908908
buffer_get(buf, mac, ESP_NOW_ETH_ALEN) &&
909909
buffer_get(buf, msg, msg_len);
910910
}
@@ -971,7 +971,7 @@ _buf_get_recv_packet(uint8_t *buf, size_t size) {
971971
// Used by the stream I/O write() function and asyncio.
972972
static int
973973
_buf_get_data_from_packet(const uint8_t *buf, size_t size, const uint8_t **peer,
974-
const uint8_t **msg,size_t *msg_len
974+
const uint8_t **msg, size_t *msg_len
975975
) {
976976
// Get a pointer to the peer MAC address and the message
977977
*peer = buf + ESPNOW_PEER_OFFSET;

ports/esp32/ring_buffer.c

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,19 @@
4242
// - prioritises efficient memory usage at the expense of additional
4343
// memcpy()s (eg. on buffer wrap).
4444

45+
// Reset the buffer pointers discarding any data in the buffer
46+
static inline void buffer_reset(buffer_t buffer) {
47+
assert(buffer);
48+
49+
buffer->head = buffer->tail = 0;
50+
}
51+
4552
// Initialise a buffer of the requested size
4653
// Will allocate an additional 9 bytes for buffer overhead
47-
buffer_t buffer_init(size_t size) {
54+
RB_STATIC buffer_t buffer_init(size_t size) {
4855
assert(size);
4956

50-
// Allocate one extra byte to ensure threadsafety
57+
// Allocate one extra byte to ensure thread safety
5158
buffer_t buffer = m_malloc0(size + sizeof(buffer_real_t) + 1);
5259
assert(buffer);
5360

@@ -60,8 +67,9 @@ buffer_t buffer_init(size_t size) {
6067
return buffer;
6168
}
6269

70+
#ifdef RING_BUFFER_USE
6371
// Use the provided memory as buffer
64-
buffer_t buffer_use(uint8_t *buf, size_t size) {
72+
RB_STATIC buffer_t buffer_use(uint8_t *buf, size_t size) {
6573
assert(size > sizeof(buffer_real_t) + 16);
6674

6775
buffer_t buffer = (buffer_t)buf;
@@ -75,25 +83,19 @@ buffer_t buffer_use(uint8_t *buf, size_t size) {
7583

7684
return buffer;
7785
}
86+
#endif // RING_BUFFER_USE
7887

7988
// Release and free the memory buffer
80-
void buffer_release(buffer_t buffer) {
89+
RB_STATIC void buffer_release(buffer_t buffer) {
8190
assert(buffer);
8291
buffer->size = buffer->head = buffer->tail = 0;
8392
if (buffer->free) {
8493
m_free(buffer);
8594
}
8695
}
8796

88-
// Reset the buffer pointers discarding any data in the buffer
89-
void buffer_reset(buffer_t buffer) {
90-
assert(buffer);
91-
92-
buffer->head = buffer->tail = 0;
93-
}
94-
9597
// Copy some data to the buffer - reject if buffer is full
96-
bool buffer_put(buffer_t buffer, const void *data, size_t len) {
98+
RB_STATIC bool buffer_put(buffer_t buffer, const uint8_t *data, size_t len) {
9799
assert(buffer && buffer->memory && data);
98100

99101
if (buffer_free(buffer) < len) {
@@ -114,7 +116,7 @@ bool buffer_put(buffer_t buffer, const void *data, size_t len) {
114116
}
115117

116118
// Copy data from the buffer - return -1 if error else end index
117-
STATIC int do_buffer_peek(buffer_t buffer, void *data, size_t len) {
119+
static int do_buffer_peek(buffer_t buffer, uint8_t *data, size_t len) {
118120
assert(buffer && buffer->memory && data);
119121

120122
if (buffer_used(buffer) < len) {
@@ -135,12 +137,12 @@ STATIC int do_buffer_peek(buffer_t buffer, void *data, size_t len) {
135137
}
136138

137139
// Peek data from the buffer - return fals CB99 e if buffer is empty
138-
bool buffer_peek(buffer_t buffer, void *data, size_t len) {
140+
RB_STATIC bool buffer_peek(buffer_t buffer, uint8_t *data, size_t len) {
139141
return do_buffer_peek(buffer, data, len) >= 0;
140142
}
141143

142144
// Copy data from the buffer - return false if buffer is empty
143-
bool buffer_get(buffer_t buffer, void *data, size_t len) {
145+
RB_STATIC bool buffer_get(buffer_t buffer, uint8_t *data, size_t len) {
144146
int end = do_buffer_peek(buffer, data, len);
145147
if (end < 0) {
146148
return false;
@@ -149,12 +151,14 @@ bool buffer_get(buffer_t buffer, void *data, size_t len) {
149151
return true;
150152
}
151153

154+
#ifdef RING_BUFFER_DEBUG
152155
// Print the current buffer state
153-
void buffer_print(char *name, buffer_t buffer) {
156+
RB_STATIC void buffer_print(char *name, buffer_t buffer) {
154157
printf("%s: alloc=%3d size=%3d head=%3d, tail=%3d, used=%3d, free=%3d, start=%p\n",
155158
name,
156159
(int)buffer->size + sizeof(buffer_real_t),
157160
(int)buffer->size, (int)buffer->head, (int)buffer->tail,
158161
(int)buffer_used(buffer), (int)buffer_free(buffer), buffer->memory
159162
);
160163
}
164+
#endif

0 commit comments

Comments
 (0)
0