Skip to content

Commit de2896c

Browse files
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 5f495db commit de2896c

File tree

2 files changed

+82
-30
lines changed

2 files changed

+82
-30
lines changed

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

Lines changed: 78 additions & 28 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) {
@@ -626,7 +637,6 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
626637
}
627638

628639
_smutex.lock();
629-
RETRY:
630640
set_timeout(ESP8266_SEND_TIMEOUT);
631641
_busy = false;
632642
_error = false;
@@ -635,50 +645,84 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
635645
goto END;
636646
}
637647

638-
//We might receive "busy s/p..." and "OK" from modem, so we need to check that also
639-
_ok_received = false;
640-
_parser.oob("OK", callback(this, &ESP8266::_oob_ok_received));
641-
642648
if (!_parser.recv(">")) {
643-
_parser.remove_oob("OK");
644-
if (_busy) {
645-
if (_ok_received) {
646-
goto RETRY;
647-
} else if (_parser.recv("OK")) {
648-
goto RETRY;
649-
}
650-
}
649+
// This means ESP8266 hasn't even started to receive data
651650
tr_debug("send(): Didn't get \">\"");
652-
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;
663+
goto END;
664+
}
665+
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;
653674
goto END;
654675
}
655-
_ok_received = false;
656-
_parser.remove_oob("OK");
657676

658-
if (_parser.write((char *)data, (int)amount) >= 0 && _parser.recv("SEND OK")) {
659-
ret = NSAPI_ERROR_OK;
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+
}
660695
}
661696

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+
662702
END:
663703
_process_oob(ESP8266_RECV_TIMEOUT, true); // Drain USART receive register to avoid data overrun
664704

665705
// error hierarchy, from low to high
666706
if (_busy) {
667707
ret = NSAPI_ERROR_WOULD_BLOCK;
668-
tr_debug("send(): Modem busy. ");
669-
}
670-
671-
if (ret == NSAPI_ERROR_DEVICE_ERROR) {
672-
ret = NSAPI_ERROR_WOULD_BLOCK;
673-
tr_debug("send(): Send failed.");
708+
tr_debug("send(): Modem busy.");
674709
}
675710

676711
if (_error) {
677712
ret = NSAPI_ERROR_CONNECTION_LOST;
678713
tr_debug("send(): Connection disrupted.");
679714
}
680715

681-
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) {
682726
ret = NSAPI_ERROR_CONNECTION_LOST;
683727
tr_debug("send(): Socket closed abruptly.");
684728
}
@@ -1219,10 +1263,16 @@ void ESP8266::_oob_connection_status()
12191263
_conn_stat_cb();
12201264
}
12211265

1222-
void ESP8266::_oob_ok_received()
1266+
void ESP8266::_oob_send_ok_received()
1267+
{
1268+
tr_debug("_oob_send_ok_received called");
1269+
_prev_send_ok_pending = false;
1270+
}
1271+
1272+
void ESP8266::_oob_send_fail_received()
12231273
{
1224-
tr_debug("_oob_ok_received called");
1225-
_ok_received = true;
1274+
tr_debug("_oob_send_fail_received called");
1275+
_send_fail_received = true;
12261276
}
12271277

12281278
int8_t ESP8266::default_wifi_mode()

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

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

474475
// OOB state variables
475476
int _connect_error;
@@ -480,7 +481,8 @@ class ESP8266 {
480481
bool _error;
481482
bool _busy;
482483
bool _reset_done;
483-
bool _ok_received;
484+
bool _prev_send_ok_pending;
485+
bool _send_fail_received;
484486

485487
// Modem's address info
486488
char _ip_buffer[16];

0 commit comments

Comments
 (0)