@@ -58,7 +58,7 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
5858 _error(false ),
5959 _busy(false ),
6060 _reset_done(false ),
61- _send_status(SEND_STATUS_OK ),
61+ _sock_sending_id(- 1 ),
6262 _conn_status(NSAPI_STATUS_DISCONNECTED)
6363{
6464 _serial.set_baud (MBED_CONF_ESP8266_SERIAL_BAUDRATE);
@@ -90,13 +90,18 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
9090 _parser.oob (" busy " , callback (this , &ESP8266::_oob_busy));
9191 // NOTE: documentation v3.0 says '+CIPRECVDATA:<data_len>,' but it's not how the FW responds...
9292 _parser.oob (" +CIPRECVDATA," , callback (this , &ESP8266::_oob_tcp_data_hdlr));
93+ // Register 'SEND OK'/'SEND FAIL' oobs here. Don't get involved in oob management with send status
94+ // because ESP8266 modem possibly doesn't reply these packets on error case.
95+ _parser.oob (" SEND OK" , callback (this , &ESP8266::_oob_send_ok_received));
96+ _parser.oob (" SEND FAIL" , callback (this , &ESP8266::_oob_send_fail_received));
9397
9498 for (int i = 0 ; i < SOCKET_COUNT; i++) {
9599 _sock_i[i].open = false ;
96100 _sock_i[i].proto = NSAPI_UDP;
97101 _sock_i[i].tcp_data = NULL ;
98102 _sock_i[i].tcp_data_avbl = 0 ;
99103 _sock_i[i].tcp_data_rcvd = 0 ;
104+ _sock_i[i].send_fail = false ;
100105 }
101106
102107 _scan_r.res = NULL ;
@@ -289,9 +294,7 @@ bool ESP8266::reset(void)
289294 tr_debug (" reset(): Done: %s." , done ? " OK" : " FAIL" );
290295
291296 _clear_socket_packets (ESP8266_ALL_SOCKET_IDS);
292- _send_status = SEND_STATUS_OK;
293- _parser.remove_oob (" SEND OK" );
294- _parser.remove_oob (" SEND FAIL" );
297+ _sock_sending_id = -1 ;
295298 set_timeout ();
296299 _smutex.unlock ();
297300
@@ -515,9 +518,17 @@ nsapi_error_t ESP8266::open_udp(int id, const char *addr, int port, int local_po
515518 // process OOB so that _sock_i reflects the correct state of the socket
516519 _process_oob (ESP8266_SEND_TIMEOUT, true );
517520
518- if (id >= SOCKET_COUNT || _sock_i[id].open ) {
521+ // Previous close() can fail with busy in sending. Usually, user will ignore the close()
522+ // error code and cause 'spurious close', in which case user has closed the socket but ESP8266 modem
523+ // hasn't yet. Because we don't know how long ESP8266 modem will trap in busy, enlarge retry count
524+ // or timeout in close() isn't a nice way. Here, we actively re-call close() in open() to let the modem
525+ // close the socket. User can re-try open() on failure. Without this active close(), open() can fail forever
526+ // with previous 'spurious close', unless peer closes the socket and so ESP8266 modem closes it accordingly.
527+ if (id >= SOCKET_COUNT) {
519528 _smutex.unlock ();
520529 return NSAPI_ERROR_PARAMETER;
530+ } else if (_sock_i[id].open ) {
531+ close (id);
521532 }
522533
523534 for (int i = 0 ; i < 2 ; i++) {
@@ -566,9 +577,12 @@ nsapi_error_t ESP8266::open_tcp(int id, const char *addr, int port, int keepaliv
566577 // process OOB so that _sock_i reflects the correct state of the socket
567578 _process_oob (ESP8266_SEND_TIMEOUT, true );
568579
569- if (id >= SOCKET_COUNT || _sock_i[id].open ) {
580+ // See the reason above with close()
581+ if (id >= SOCKET_COUNT) {
570582 _smutex.unlock ();
571583 return NSAPI_ERROR_PARAMETER;
584+ } else if (_sock_i[id].open ) {
585+ close (id);
572586 }
573587
574588 for (int i = 0 ; i < 2 ; i++) {
@@ -619,13 +633,14 @@ bool ESP8266::dns_lookup(const char *name, char *ip)
619633nsapi_size_or_error_t ESP8266::send (int id, const void *data, uint32_t amount)
620634{
621635 if (_sock_i[id].proto == NSAPI_TCP) {
622- if (_send_status == SEND_STATUS_PENDING) {
623- tr_debug (" send(): Previous packet was not yet ACK-ed with SEND OK." );
624- return NSAPI_ERROR_WOULD_BLOCK;
625- } else if (_send_status == SEND_STATUS_FAILED) {
626- tr_debug (" send(): Previous packet failed." );
627- _send_status = SEND_STATUS_OK;
628- return NSAPI_ERROR_DEVICE_ERROR;
636+ if (_sock_sending_id >= 0 && _sock_sending_id < SOCKET_COUNT) {
637+ if (!_sock_i[id].send_fail ) {
638+ tr_debug (" send(): Previous packet (socket %d) was not yet ACK-ed with SEND OK." , _sock_sending_id);
639+ return NSAPI_ERROR_WOULD_BLOCK;
640+ } else {
641+ tr_debug (" send(): Previous packet (socket %d) failed." , id);
642+ return NSAPI_ERROR_DEVICE_ERROR;
643+ }
629644 }
630645 }
631646
@@ -644,6 +659,10 @@ nsapi_size_or_error_t ESP8266::send(int id, const void *data, uint32_t amount)
644659 }
645660
646661 _smutex.lock ();
662+ // Mark this socket is sending. We allow only one actively sending socket because:
663+ // 1. ESP8266 AT packets 'SEND OK'/'SEND FAIL' are not associated with socket ID. No way to tell them.
664+ // 2. In original implementation, ESP8266::send() is synchronous, which implies only one actively sending socket.
665+ _sock_sending_id = id;
647666 set_timeout (ESP8266_SEND_TIMEOUT);
648667 _busy = false ;
649668 _error = false ;
@@ -685,44 +704,27 @@ nsapi_size_or_error_t ESP8266::send(int id, const void *data, uint32_t amount)
685704 goto END;
686705 }
687706
688- // We might receive "busy s/p...", "SEND OK" or "SEND FAIL" from modem, so we need to check that also
689- _parser.oob (" SEND FAIL" , callback (this , &ESP8266::_oob_send_fail_received));
690- for (unsigned int i = send_ack_retries; i > 0 ; i--) {
691- if (!_parser.recv (" SEND OK" )) {
692- if (_error || _send_status == SEND_STATUS_FAILED) {
693- _parser.remove_oob (" SEND FAIL" );
694- goto END;
695- }
696- if (_busy) {
697- _busy = false ;
698- tr_debug (" send(): Busy, %d retries left..." , i - 1 );
699- }
700- } else {
701- ret = amount; // Got "SEND OK" - return number of bytes.
702- goto END;
703- }
704- }
705-
706- // ESP8266 ACKed data over serial, but did not ACK with SEND OK or report any error.
707- _send_status = SEND_STATUS_PENDING;
708- _parser.oob (" SEND OK" , callback (this , &ESP8266::_oob_send_ok_received));
709707 ret = amount;
710708
711709END:
712710 _process_oob (ESP8266_RECV_TIMEOUT, true ); // Drain USART receive register to avoid data overrun
713711
714712 // error hierarchy, from low to high
715- if (_busy) {
713+ // NOTE: We cannot return NSAPI_ERROR_WOULD_BLOCK when "Recv X bytes" has reached, otherwise duplicate data send.
714+ if (_busy && ret < 0 ) {
716715 ret = NSAPI_ERROR_WOULD_BLOCK;
717716 tr_debug (" send(): Modem busy." );
718717 }
719718
720719 if (_error) {
720+ // FIXME: Not sure clear or not of _error. See it as device error and it can recover only via reset?
721+ _sock_sending_id = -1 ;
721722 ret = NSAPI_ERROR_CONNECTION_LOST;
722723 tr_debug (" send(): Connection disrupted." );
723724 }
724725
725- if (_send_status == SEND_STATUS_FAILED) {
726+ if (_sock_i[id].send_fail ) {
727+ _sock_sending_id = -1 ;
726728 if (_sock_i[id].proto == NSAPI_TCP) {
727729 ret = NSAPI_ERROR_DEVICE_ERROR;
728730 } else {
@@ -732,6 +734,7 @@ nsapi_size_or_error_t ESP8266::send(int id, const void *data, uint32_t amount)
732734 }
733735
734736 if (!_sock_i[id].open && ret < 0 ) {
737+ _sock_sending_id = -1 ;
735738 ret = NSAPI_ERROR_CONNECTION_LOST;
736739 tr_debug (" send(): Socket %d closed abruptly." , id);
737740 }
@@ -1005,14 +1008,6 @@ void ESP8266::_clear_socket_packets(int id)
10051008 _sock_i[id].tcp_data_avbl = 0 ;
10061009 }
10071010}
1008- void ESP8266::_clear_send_status (void )
1009- {
1010- _smutex.lock (); // remove_oob doesn't use serial, but we don't want to race against it.
1011- _send_status = SEND_STATUS_OK;
1012- _parser.remove_oob (" SEND OK" );
1013- _parser.remove_oob (" SEND FAIL" );
1014- _smutex.unlock ();
1015- }
10161011
10171012bool ESP8266::close (int id)
10181013{
@@ -1025,20 +1020,33 @@ bool ESP8266::close(int id)
10251020 _closed = false ;
10261021 _sock_i[id].open = false ;
10271022 _clear_socket_packets (id);
1023+ // Closed, so this socket escapes from SEND FAIL status.
1024+ if (id == _sock_sending_id) {
1025+ _sock_sending_id = -1 ;
1026+ }
1027+ _sock_i[id].send_fail = false ;
10281028 _smutex.unlock ();
10291029 // ESP8266 has a habit that it might close a socket on its own.
1030+ tr_debug (" close(%d): socket close OK with UNLINK ERROR" , id);
10301031 return true ;
10311032 }
10321033 } else {
10331034 // _sock_i[id].open set to false with an OOB
10341035 _clear_socket_packets (id);
1036+ // Closed, so this socket escapes from SEND FAIL status
1037+ if (id == _sock_sending_id) {
1038+ _sock_sending_id = -1 ;
1039+ }
1040+ _sock_i[id].send_fail = false ;
10351041 _smutex.unlock ();
1042+ tr_debug (" close(%d): socket close OK with AT+CIPCLOSE OK" , id);
10361043 return true ;
10371044 }
10381045 }
10391046 _smutex.unlock ();
10401047 }
10411048
1049+ tr_debug (" close(%d): socket close FAIL'ed (spurious close)" , id);
10421050 return false ;
10431051}
10441052
@@ -1215,39 +1223,59 @@ void ESP8266::_oob_socket0_closed()
12151223{
12161224 static const int id = 0 ;
12171225 _sock_i[id].open = false ;
1218- _clear_send_status ();
1226+ // Closed, so this socket escapes from SEND FAIL status
1227+ if (id == _sock_sending_id) {
1228+ _sock_sending_id = -1 ;
1229+ }
1230+ _sock_i[id].send_fail = false ;
12191231 tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
12201232}
12211233
12221234void ESP8266::_oob_socket1_closed ()
12231235{
12241236 static const int id = 1 ;
12251237 _sock_i[id].open = false ;
1226- _clear_send_status ();
1238+ // Closed, so this socket escapes from SEND FAIL status
1239+ if (id == _sock_sending_id) {
1240+ _sock_sending_id = -1 ;
1241+ }
1242+ _sock_i[id].send_fail = false ;
12271243 tr_debug (" _oob_socket1_closed(): Socket %d closed." , id);
12281244}
12291245
12301246void ESP8266::_oob_socket2_closed ()
12311247{
12321248 static const int id = 2 ;
12331249 _sock_i[id].open = false ;
1234- _clear_send_status ();
1250+ // Closed, so this socket escapes from SEND FAIL status
1251+ if (id == _sock_sending_id) {
1252+ _sock_sending_id = -1 ;
1253+ }
1254+ _sock_i[id].send_fail = false ;
12351255 tr_debug (" _oob_socket2_closed(): Socket %d closed." , id);
12361256}
12371257
12381258void ESP8266::_oob_socket3_closed ()
12391259{
12401260 static const int id = 3 ;
12411261 _sock_i[id].open = false ;
1242- _clear_send_status ();
1262+ // Closed, so this socket escapes from SEND FAIL status
1263+ if (id == _sock_sending_id) {
1264+ _sock_sending_id = -1 ;
1265+ }
1266+ _sock_i[id].send_fail = false ;
12431267 tr_debug (" _oob_socket3_closed(): %d closed." , id);
12441268}
12451269
12461270void ESP8266::_oob_socket4_closed ()
12471271{
12481272 static const int id = 4 ;
12491273 _sock_i[id].open = false ;
1250- _clear_send_status ();
1274+ // Closed, so this socket escapes from SEND FAIL status
1275+ if (id == _sock_sending_id) {
1276+ _sock_sending_id = -1 ;
1277+ }
1278+ _sock_i[id].send_fail = false ;
12511279 tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
12521280}
12531281
@@ -1287,22 +1315,17 @@ void ESP8266::_oob_connection_status()
12871315
12881316void ESP8266::_oob_send_ok_received ()
12891317{
1290- tr_debug (" _oob_send_ok_received called with _send_status %d" , _send_status);
1291- if (_send_status == SEND_STATUS_PENDING) {
1292- _send_status = SEND_STATUS_OK;
1293- }
1294- _parser.remove_oob (" SEND OK" );
1295- _parser.remove_oob (" SEND FAIL" );
1318+ tr_debug (" _oob_send_ok_received called for socket %d" , _sock_sending_id);
1319+ _sock_sending_id = -1 ;
12961320}
12971321
12981322void ESP8266::_oob_send_fail_received ()
12991323{
1300- tr_debug (" _oob_send_fail_received called with _send_status %d" , _send_status );
1301- if (_send_status == SEND_STATUS_PENDING ) {
1302- _send_status = SEND_STATUS_FAILED ;
1324+ tr_debug (" _oob_send_fail_received called for socket %d" , _sock_sending_id );
1325+ if (_sock_sending_id >= 0 && _sock_sending_id < SOCKET_COUNT ) {
1326+ _sock_i[_sock_sending_id]. send_fail = true ;
13031327 }
1304- _parser.remove_oob (" SEND FAIL" );
1305- _parser.remove_oob (" SEND OK" );
1328+ _sock_sending_id = -1 ;
13061329}
13071330
13081331int8_t ESP8266::default_wifi_mode ()
0 commit comments