5
5
*
6
6
* Copyright (c) 2017-2020 Nick Moore
7
7
* Copyright (c) 2018 shawwwn <shawwwn1@gmail.com>
8
+ * Copyright (c) 2020 Glenn Moloney
8
9
*
9
10
* Permission is hereby granted, free of charge, to any person obtaining a copy
10
11
* of this software and associated documentation files (the "Software"), to deal
45
46
46
47
#include "modnetwork.h"
47
48
48
- // XXX There's exactly one buffer and so sending or receiving
49
- // frames too quickly can therefore cause data to be lost *and* repeated.
50
- // TODO make this a ring buffer or something.
49
+ #include "ring_buffer.h"
51
50
52
- uint8_t send_mac [ESP_NOW_ETH_ALEN ];
53
- esp_now_send_status_t send_status ;
51
+ // The receive buffer
52
+ static buffer_t recv_buffer = NULL ;
53
+ static size_t recv_buffer_size = (
54
+ 2 * (ESP_NOW_ETH_ALEN + sizeof (uint8_t ) + ESP_NOW_MAX_DATA_LEN )
55
+ ); // Enough for 2 full-size packets: 2 * (6 + 1 + 255) = 534 bytes
54
56
55
- uint8_t recv_mac [ESP_NOW_ETH_ALEN ];
56
- int recv_len ;
57
- uint8_t recv_dat [ESP_NOW_MAX_DATA_LEN ];
57
+ // We also need a small send buffer as send_cb() may be called out of
58
+ // order with the send() calls.
59
+ static buffer_t send_buffer = NULL ;
60
+ static size_t send_buffer_size = (
61
+ 20 * (ESP_NOW_ETH_ALEN + sizeof (esp_now_send_status_t ))
62
+ ); // Up to 20 callbacks = 140 bytes
63
+
64
+ static size_t espnow_sent_packets = 0 ;
65
+ static size_t espnow_recv_packets = 0 ;
66
+ static size_t espnow_dropped_packets = 0 ;
67
+
68
+ // The maximum number of packets to process in each recv_cb_wrapper()
69
+ static int callback_max_packets = 20 ;
58
70
59
71
typedef struct _esp_espnow_obj_t {
60
72
mp_obj_base_t base ;
@@ -73,7 +85,7 @@ STATIC mp_obj_t get_esp_espnow(size_t n_args, const mp_obj_t *args) {
73
85
STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (get_esp_espnow_obj , 0 , 1 , get_esp_espnow );
74
86
75
87
NORETURN void _esp_espnow_exceptions (esp_err_t e ) {
76
- switch (e ) {
88
+ switch (e ) {
77
89
case ESP_ERR_ESPNOW_NOT_INIT :
78
90
mp_raise_msg (& mp_type_OSError , MP_ERROR_TEXT ("ESP-Now Not Initialized" ));
79
91
case ESP_ERR_ESPNOW_ARG :
@@ -92,7 +104,7 @@ NORETURN void _esp_espnow_exceptions(esp_err_t e) {
92
104
nlr_raise (mp_obj_new_exception_msg_varg (
93
105
& mp_type_RuntimeError , MP_ERROR_TEXT ("ESP-Now Unknown Error 0x%04x" ), e
94
106
));
95
- }
107
+ }
96
108
}
97
109
98
110
static inline void esp_espnow_exceptions (esp_err_t e ) {
@@ -108,67 +120,111 @@ static inline void _get_bytes(mp_obj_t str, size_t len, uint8_t *dst) {
108
120
memcpy (dst , data , len );
109
121
}
110
122
111
- // send_cb_wrapper and recv_cb_wrapper exist as MicroPython function
112
- // objects so that they can be added to the mp_sched_schedule.
113
- // send_cb and recv_cb are called from the ESP-Now library but might
114
- // be in an interrupt, so can't allocate memory.
115
-
116
- STATIC mp_obj_t send_cb_wrapper (mp_obj_t dummy ) {
117
- const esp_espnow_obj_t * self = & esp_espnow_obj ;
118
- mp_obj_tuple_t * msg = mp_obj_new_tuple (2 , NULL
8000
);
119
- msg -> items [0 ] = mp_obj_new_bytes (send_mac , ESP_NOW_ETH_ALEN );
120
- msg -> items [1 ] = (send_status == ESP_NOW_SEND_SUCCESS ) ? mp_const_true : mp_const_false ;
121
- mp_call_function_1 (self -> send_cb_obj , msg );
122
- return mp_const_none ;
123
- }
124
- STATIC MP_DEFINE_CONST_FUN_OBJ_1 (send_cb_wrapper_obj , send_cb_wrapper );
123
+ // callback_wrapper exists as MicroPython function object so that it
124
+ // can be added to the mp_sched_schedule. send_cb and recv_cb are called
125
+ // from the ESP-Now library but might be in an interrupt, so can't
126
+ // allocate memory.
127
+ // The mp_sched_schedule stack is quite shallow (8), so it is safest and most
128
+ // efficient to process all buffered send and receive in the same callback
129
+ // to minimise packet loss.
125
130
126
- STATIC mp_obj_t recv_cb_wrapper (mp_obj_t dummy ) {
131
+ STATIC mp_obj_t callback_wrapper (mp_obj_t dummy ) {
127
132
const esp_espnow_obj_t * self = & esp_espnow_obj ;
128
- mp_obj_tuple_t * msg = mp_obj_new_tuple (2 , NULL );
129
- msg -> items [0 ] = mp_obj_new_bytes (recv_mac , ESP_NOW_ETH_ALEN );
130
- msg -> items [1 ] = mp_obj_new_bytes (recv_dat , recv_len );
131
- mp_call_function_1 (self -> recv_cb_obj , msg );
133
+ static size_t last_dropped_packets = 0 ;
134
+ uint8_t mac_addr [ESP_NOW_ETH_ALEN ];
135
+ uint8_t data [ESP_NOW_MAX_DATA_LEN ];
136
+ uint8_t data_len ;
137
+ // Process multiple packets as a work around for the finite
138
+ // size (8) of the mp_sched_schedule stack. This means some of our
139
+ // calls might have no packets to process.
140
+ // This reduces the number of dropped packets from bursts.
141
+ int i = 0 ;
142
+ while (!buffer_empty (recv_buffer ) && i ++ < callback_max_packets &&
143
+ buffer_get (recv_buffer , mac_addr , ESP_NOW_ETH_ALEN ) &&
144
+ buffer_get (recv_buffer , & data_len , sizeof (data_len )) &&
145
+ buffer_get (recv_buffer , & data , data_len )) {
146
+ mp_obj_tuple_t * msg = mp_obj_new_tuple (3 , NULL );
147
+ msg -> items [0 ] = mp_obj_new_bytes (mac_addr , ESP_NOW_ETH_ALEN );
148
+ msg -> items [1 ] = mp_obj_new_bytes (data , data_len );
149
+ // Add the number of packets dropped since the last call to the tuple
150
+ size_t dropped_packets = espnow_dropped_packets ;
151
+ msg -> items [2 ] = mp_obj_new_int (dropped_packets - last_dropped_packets );
152
+ last_dropped_packets = dropped_packets ;
153
+ mp_call_function_1 (self -> recv_cb_obj , msg );
154
+ }
155
+ esp_now_send_status_t send_status ;
156
+ i = 0 ;
157
+ while (!buffer_empty (send_buffer ) && i ++ < callback_max_packets &&
158
+ buffer_get (send_buffer , mac_addr , ESP_NOW_ETH_ALEN ) &&
159
+ buffer_get (send_buffer , & send_status , sizeof (send_status ))) {
160
+ mp_obj_tuple_t * msg = mp_obj_new_tuple (2 , NULL );
161
+ msg -> items [0 ] = mp_obj_new_bytes (mac_addr , ESP_NOW_ETH_ALEN );
162
+ msg -> items [1 ] = (send_status == ESP_NOW_SEND_SUCCESS ) ? mp_const_true : mp_const_false ;
163
+ mp_call_function_1 (self -> send_cb_obj , msg );
164
+ }
132
165
return mp_const_none ;
133
166
}
134
- STATIC MP_DEFINE_CONST_FUN_OBJ_1 (recv_cb_wrapper_obj , recv_cb_wrapper );
167
+ STATIC MP_DEFINE_CONST_FUN_OBJ_1 (callback_wrapper_obj , callback_wrapper );
135
168
136
- STATIC void IRAM_ATTR send_cb (const uint8_t * macaddr , esp_now_send_status_t status )
169
+ STATIC void IRAM_ATTR send_cb (const uint8_t * mac_addr , esp_now_send_status_t status )
137
170
{
138
- if (esp_espnow_obj .send_cb_obj != mp_const_none ) {
139
- memcpy (send_mac , macaddr , ESP_NOW_ETH_ALEN );
140
- send_status = status ;
141
- mp_sched_schedule ((const mp_obj_t )& send_cb_wrapper_obj , mp_const_none );
171
+ if (esp_espnow_obj .send_cb_obj == mp_const_none ||
172
+ ESP_NOW_ETH_ALEN + sizeof (status ) >= buffer_free (send_buffer )) {
173
+ return ;
142
174
}
175
+ buffer_put (send_buffer , & mac_addr , ESP_NOW_ETH_ALEN );
176
+ buffer_put (send_buffer , & status , sizeof (status ));
177
+ espnow_sent_packets ++ ;
178
+ mp_sched_schedule ((const mp_obj_t )& callback_wrapper_obj , mp_const_none );
143
179
}
144
180
145
- STATIC void IRAM_ATTR recv_cb (const uint8_t * macaddr , const uint8_t * data , int len )
181
+ STATIC void IRAM_ATTR recv_cb (const uint8_t * mac_addr , const uint8_t * data , int len )
146
182
{
147
- if (esp_espnow_obj .recv_cb_obj != mp_const_none ) {
148
- memcpy (recv_mac , macaddr , ESP_NOW_ETH_ALEN );
149
- recv_len = len ;
150
- memcpy (recv_dat , data , len );
151
- mp_sched_schedule ((const mp_obj_t )& recv_cb_wrapper_obj , mp_const_none );
183
+ if (esp_espnow_obj .recv_cb_obj == mp_const_none ||
184
+ ESP_NOW_ETH_ALEN + sizeof (uint8_t ) + len >= buffer_free (recv_buffer )) {
185
+ espnow_dropped_packets ++ ;
186
+ return ;
152
187
}
153
- }
188
+ uint8_t data_len = len ;
189
+ buffer_put (recv_buffer , mac_addr , ESP_NOW_ETH_ALEN );
190
+ buffer_put (recv_buffer , & data_len , sizeof (data_len ));
191
+ buffer_put (recv_buffer , data , len );
192
+ espnow_recv_packets ++ ;
193
+ mp_sched_schedule ((const mp_obj_t )& callback_wrapper_obj , mp_const_none );
194
+ }
154
195
155
196
static int initialized = 0 ;
156
197
157
- STATIC mp_obj_t espnow_init (mp_obj_t self ) {
198
+ STATIC mp_obj_t espnow_init (size_t n_args , const mp_obj_t * args ) {
158
199
if (!initialized ) {
200
+ if (n_args > 1 && args [1 ] != mp_const_none ) {
201
+ recv_buffer_size = mp_obj_get_int (args [1 ]);
202
+ }
203
+ if (n_args > 2 && args [2 ] != mp_const_none ) {
204
+ send_buffer_size = mp_obj_get_int (args [2 ]);
205
+ }
206
+ if (n_args > 3 && args [3 ] != mp_const_none ) {
207
+ callback_max_packets = mp_obj_get_int (args [3 ]);
208
+ }
209
+ recv_buffer = buffer_init (recv_buffer_size );
210
+ send_buffer = buffer_init (send_buffer_size );
159
211
ESPNOW_EXCEPTIONS (esp_now_init ());
160
212
initialized = 1 ;
161
213
162
214
ESPNOW_EXCEPTIONS (esp_now_register_recv_cb (recv_cb ));
163
215
ESPNOW_EXCEPTIONS (esp_now_register_send_cb (send_cb ));
216
+
164
217
}
165
218
return mp_const_none ;
166
219
}
167
- STATIC MP_DEFINE_CONST_FUN_OBJ_1 (espnow_init_obj , espnow_init );
220
+ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (espnow_init_obj , 1 , 4 , espnow_init );
168
221
169
222
STATIC mp_obj_t espnow_deinit (mp_obj_t self ) {
170
223
if (initialized ) {
171
224
ESPNOW_EXCEPTIONS (esp_now_deinit ());
225
+ buffer_release (recv_buffer );
226
+ buffer_release (send_buffer );
227
+ buffer_release (send_buffer );
172
228
initialized = 0 ;
173
229
}
174
230
return mp_const_none ;
@@ -334,6 +390,15 @@ STATIC mp_obj_t espnow_peer_count(mp_obj_t _) {
334
390
}
335
391
STATIC MP_DEFINE_CONST_FUN_OBJ_1 (espnow_peer_count_obj , espnow_peer_count );
336
392
393
+ STATIC mp_obj_t espnow_stats (mp_obj_t _ ) {
394
+ mp_obj_t tuple [3 ];
395
+ tuple [0 ] = mp_obj_new_int (espnow_sent_packets );
396
+ tuple [1 ] = mp_obj_new_int (espnow_recv_packets );
397
+ tuple [2 ] = mp_obj_new_int (espnow_dropped_packets );
398
+ return mp_obj_new_tuple (3 , tuple );
399
+ }
400
+ STATIC MP_DEFINE_CONST_FUN_OBJ_1 (espnow_stats_obj , espnow_stats );
401
+
337
402
STATIC mp_obj_t espnow_version (mp_obj_t _ ) {
338
403
uint32_t version ;
339
404
ESPNOW_EXCEPTIONS (esp_now_get_version (& version ));
@@ -353,6 +418,7 @@ STATIC const mp_rom_map_elem_t esp_espnow_locals_dict_table[] = {
353
418
{ MP_ROM_QSTR (MP_QSTR_on_send ), MP_ROM_PTR (& espnow_on_send_obj ) },
354
419
{ MP_ROM_QSTR (MP_QSTR_on_recv ), MP_ROM_PTR (& espnow_on_recv_obj ) },
355
420
{ MP_ROM_QSTR (MP_QSTR_peer_count ), MP_ROM_PTR (& espnow_peer_count_obj ) },
421
+ { MP_ROM_QSTR (MP_QSTR_stats ), MP_ROM_PTR (& espnow_stats_obj ) },
356
422
};
357
423
STATIC MP_DEFINE_CONST_DICT (esp_espnow_locals_dict , esp_espnow_locals_dict_table );
358
424
0 commit comments