Skip to content

Commit 5f4ba9d

Browse files
michalpasztamobicaccli8
authored andcommitted
ESP8266: add a retry mechanism to avoid duplicate data sends
We are now checking if ESP8266 has confirmed receiving data over serial port with an undocumented (but existing) "Recv x bytes" message. Next we are explicitly waiting for an official "SEND OK".
1 parent e4b81f6 commit 5f4ba9d

File tree

2 files changed

+85
-10
lines changed

2 files changed

+85
-10
lines changed

components/wifi/esp8266-driver/ESP8266/ESP8266.cpp

Lines changed: 81 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
5858
_error(false),
5959
_busy(false),
6060
_reset_done(false),
61+
_prev_send_ok_pending(false),
62+
_send_fail_received(false),
6163
_conn_status(NSAPI_STATUS_DISCONNECTED)
6264
{
6365
_serial.set_baud(MBED_CONF_ESP8266_SERIAL_BAUDRATE);
@@ -614,7 +616,16 @@ bool ESP8266::dns_lookup(const char *name, char *ip)
614616

615617
nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
616618
{
619+
if (_prev_send_ok_pending && _sock_i[id].proto == NSAPI_TCP) {
620+
tr_debug("send(): Previous packet was not ACK-ed with SEND OK.");
621+
return NSAPI_ERROR_WOULD_BLOCK;
622+
}
623+
617624
nsapi_error_t ret = NSAPI_ERROR_DEVICE_ERROR;
625+
_send_fail_received = false;
626+
int bytes_confirmed = 0;
627+
constexpr unsigned int send_ack_retries = 3;
628+
618629
// +CIPSEND supports up to 2048 bytes at a time
619630
// Data stream can be truncated
620631
if (amount > 2048 && _sock_i[id].proto == NSAPI_TCP) {
@@ -635,35 +646,83 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
635646
}
636647

637648
if (!_parser.recv(">")) {
649+
// This means ESP8266 hasn't even started to receive data
638650
tr_debug("send(): Didn't get \">\"");
639-
ret = NSAPI_ERROR_WOULD_BLOCK;
651+
if (_sock_i[id].proto == NSAPI_TCP) {
652+
ret = NSAPI_ERROR_WOULD_BLOCK; // Not neccesarily critical error.
653+
} else if (_sock_i[id].proto == NSAPI_UDP) {
654+
ret = NSAPI_ERROR_NO_MEMORY;
655+
}
656+
goto END;
657+
}
658+
659+
if (_parser.write((char *)data, (int)amount) < 0) {
660+
tr_debug("send(): Failed to write serial data");
661+
// Serial is not working, serious error, reset needed.
662+
ret = NSAPI_ERROR_DEVICE_ERROR;
640663
goto END;
641664
}
642665

643-
if (_parser.write((char *)data, (int)amount) >= 0 && _parser.recv("SEND OK")) {
644-
ret = NSAPI_ERROR_OK;
666+
// The "Recv X bytes" is not documented.
667+
if (!_parser.recv("Recv %d bytes", &bytes_confirmed)) {
668+
tr_debug("send(): Bytes not confirmed.");
669+
ret = NSAPI_ERROR_DEVICE_ERROR;
670+
goto END;
671+
} else if (bytes_confirmed != amount) {
672+
tr_debug("send(): Error: confirmed %d bytes, but expected %d.", bytes_confirmed, amount);
673+
ret = NSAPI_ERROR_DEVICE_ERROR;
674+
goto END;
675+
}
676+
677+
//We might receive "busy s/p...", "SEND OK" or "SEND FAIL" from modem, so we need to check that also
678+
_parser.oob("SEND FAIL", callback(this, &ESP8266::_oob_send_fail_received));
679+
for (unsigned int i = send_ack_retries; i > 0; i--) {
680+
if (!_parser.recv("SEND OK")) {
681+
if (_error || _send_fail_received) {
682+
_parser.remove_oob("SEND FAIL");
683+
goto END;
684+
}
685+
if (_busy) {
686+
_busy = false;
687+
tr_debug("send(): Busy, %d retries left...", i - 1);
688+
} else {
689+
tr_debug("send(): Not busy, but no SEND OK. %d retries left...", i - 1);
690+
}
691+
} else {
692+
ret = amount; // Got "SEND OK" - return number of bytes.
693+
goto END;
694+
}
645695
}
646696

697+
// ESP8266 ACKed data over serial, but did not ACK over TCP or report any error.
698+
_prev_send_ok_pending = true;
699+
_parser.oob("SEND OK", callback(this, &ESP8266::_oob_send_ok_received));
700+
ret = amount;
701+
647702
END:
648703
_process_oob(ESP8266_RECV_TIMEOUT, true); // Drain USART receive register to avoid data overrun
649704

650705
// error hierarchy, from low to high
651706
if (_busy) {
652707
ret = NSAPI_ERROR_WOULD_BLOCK;
653-
tr_debug("send(): Modem busy. ");
654-
}
655-
656-
if (ret == NSAPI_ERROR_DEVICE_ERROR) {
657-
ret = NSAPI_ERROR_WOULD_BLOCK;
658-
tr_debug("send(): Send failed.");
708+
tr_debug("send(): Modem busy.");
659709
}
660710

661711
if (_error) {
662712
ret = NSAPI_ERROR_CONNECTION_LOST;
663713
tr_debug("send(): Connection disrupted.");
664714
}
665715

666-
if (!_sock_i[id].open && ret != NSAPI_ERROR_OK) {
716+
if (_send_fail_received) {
717+
if (_sock_i[id].proto == NSAPI_TCP) {
718+
ret = NSAPI_ERROR_DEVICE_ERROR;
719+
} else {
720+
ret = NSAPI_ERROR_NO_MEMORY;
721+
}
722+
tr_debug("send(): SEND FAIL received.");
723+
}
724+
725+
if (!_sock_i[id].open && ret < 0) {
667726
ret = NSAPI_ERROR_CONNECTION_LOST;
668727
tr_debug("send(): Socket closed abruptly.");
669728
}
@@ -1205,6 +1264,18 @@ void ESP8266::_oob_connection_status()
12051264
_conn_stat_cb();
12061265
}
12071266

1267+
void ESP8266::_oob_send_ok_received()
1268+
{
1269+
tr_debug("_oob_send_ok_received called");
1270+
_prev_send_ok_pending = false;
1271+
}
1272+
1273+
void ESP8266::_oob_send_fail_received()
1274+
{
1275+
tr_debug("_oob_send_fail_received called");
1276+
_send_fail_received = true;
1277+
}
1278+
12081279
int8_t ESP8266::default_wifi_mode()
12091280
{
12101281
int8_t mode;

components/wifi/esp8266-driver/ESP8266/ESP8266.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -469,6 +469,8 @@ class ESP8266 {
469469
void _oob_tcp_data_hdlr();
470470
void _oob_ready();
471471
void _oob_scan_results();
472+
void _oob_send_ok_received();
473+
void _oob_send_fail_received();
472474

473475
// OOB state variables
474476
int _connect_error;
@@ -479,6 +481,8 @@ class ESP8266 {
479481
bool _error;
480482
bool _busy;
481483
bool _reset_done;
484+
bool _prev_send_ok_pending;
485+
bool _send_fail_received;
482486

483487
// Modem's address info
484488
char _ip_buffer[16];

0 commit comments

Comments
 (0)