@@ -58,6 +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+ _sock_sending_id(-1 ),
6162 _conn_status(NSAPI_STATUS_DISCONNECTED)
6263{
6364 _serial.set_baud (MBED_CONF_ESP8266_SERIAL_BAUDRATE);
@@ -89,13 +90,18 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
8990 _parser.oob (" busy " , callback (this , &ESP8266::_oob_busy));
9091 // NOTE: documentation v3.0 says '+CIPRECVDATA:<data_len>,' but it's not how the FW responds...
9192 _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));
9297
9398 for (int i = 0 ; i < SOCKET_COUNT; i++) {
9499 _sock_i[i].open = false ;
95100 _sock_i[i].proto = NSAPI_UDP;
96101 _sock_i[i].tcp_data = NULL ;
97102 _sock_i[i].tcp_data_avbl = 0 ;
98103 _sock_i[i].tcp_data_rcvd = 0 ;
104+ _sock_i[i].send_fail = false ;
99105 }
100106
101107 _scan_r.res = NULL ;
@@ -288,6 +294,7 @@ bool ESP8266::reset(void)
288294 tr_debug (" reset(): Done: %s." , done ? " OK" : " FAIL" );
289295
290296 _clear_socket_packets (ESP8266_ALL_SOCKET_IDS);
297+ _sock_sending_id = -1 ;
291298 set_timeout ();
292299 _smutex.unlock ();
293300
@@ -511,9 +518,17 @@ nsapi_error_t ESP8266::open_udp(int id, const char *addr, int port, int local_po
511518 // process OOB so that _sock_i reflects the correct state of the socket
512519 _process_oob (ESP8266_SEND_TIMEOUT, true );
513520
514- 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) {
515528 _smutex.unlock ();
516529 return NSAPI_ERROR_PARAMETER;
530+ } else if (_sock_i[id].open ) {
531+ close (id);
517532 }
518533
519534 for (int i = 0 ; i < 2 ; i++) {
@@ -562,9 +577,12 @@ nsapi_error_t ESP8266::open_tcp(int id, const char *addr, int port, int keepaliv
562577 // process OOB so that _sock_i reflects the correct state of the socket
563578 _process_oob (ESP8266_SEND_TIMEOUT, true );
564579
565- if (id >= SOCKET_COUNT || _sock_i[id].open ) {
580+ // See the reason above with close()
581+ if (id >= SOCKET_COUNT) {
566582 _smutex.unlock ();
567583 return NSAPI_ERROR_PARAMETER;
584+ } else if (_sock_i[id].open ) {
585+ close (id);
568586 }
569587
570588 for (int i = 0 ; i < 2 ; i++) {
@@ -612,9 +630,24 @@ bool ESP8266::dns_lookup(const char *name, char *ip)
612630 return done;
613631}
614632
615- nsapi_error_t ESP8266::send (int id, const void *data, uint32_t amount)
633+ nsapi_size_or_error_t ESP8266::send (int id, const void *data, uint32_t amount)
616634{
635+ if (_sock_i[id].proto == NSAPI_TCP) {
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+ }
644+ }
645+ }
646+
617647 nsapi_error_t ret = NSAPI_ERROR_DEVICE_ERROR;
648+ int bytes_confirmed = 0 ;
649+ constexpr unsigned int send_ack_retries = 3 ;
650+
618651 // +CIPSEND supports up to 2048 bytes at a time
619652 // Data stream can be truncated
620653 if (amount > 2048 && _sock_i[id].proto == NSAPI_TCP) {
@@ -626,6 +659,10 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
626659 }
627660
628661 _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;
629666 set_timeout (ESP8266_SEND_TIMEOUT);
630667 _busy = false ;
631668 _error = false ;
@@ -635,37 +672,70 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
635672 }
636673
637674 if (!_parser.recv (" >" )) {
675+ // This means ESP8266 hasn't even started to receive data
638676 tr_debug (" send(): Didn't get \" >\" " );
639- ret = NSAPI_ERROR_WOULD_BLOCK;
677+ if (_sock_i[id].proto == NSAPI_TCP) {
678+ ret = NSAPI_ERROR_WOULD_BLOCK; // Not necessarily critical error.
679+ } else if (_sock_i[id].proto == NSAPI_UDP) {
680+ ret = NSAPI_ERROR_NO_MEMORY;
681+ }
640682 goto END;
641683 }
642684
643- if (_parser.write ((char *)data, (int )amount) >= 0 && _parser.recv (" SEND OK" )) {
644- ret = NSAPI_ERROR_OK;
685+ if (_parser.write ((char *)data, (int )amount) < 0 ) {
686+ tr_debug (" send(): Failed to write serial data" );
687+ // Serial is not working, serious error, reset needed.
688+ ret = NSAPI_ERROR_DEVICE_ERROR;
689+ goto END;
690+ }
691+
692+ // The "Recv X bytes" is not documented.
693+ if (!_parser.recv (" Recv %d bytes" , &bytes_confirmed)) {
694+ tr_debug (" send(): Bytes not confirmed." );
695+ if (_sock_i[id].proto == NSAPI_TCP) {
696+ ret = NSAPI_ERROR_WOULD_BLOCK;
697+ } else if (_sock_i[id].proto == NSAPI_UDP) {
698+ ret = NSAPI_ERROR_NO_MEMORY;
699+ }
700+ } else if (bytes_confirmed != amount && _sock_i[id].proto == NSAPI_UDP) {
701+ tr_debug (" send(): Error: confirmed %d bytes, but expected %d." , bytes_confirmed, amount);
702+ ret = NSAPI_ERROR_DEVICE_ERROR;
703+ } else {
704+ // TCP can accept partial writes (if they ever happen)
705+ ret = bytes_confirmed;
645706 }
646707
647708END:
648709 _process_oob (ESP8266_RECV_TIMEOUT, true ); // Drain USART receive register to avoid data overrun
649710
650711 // error hierarchy, from low to high
651- if (_busy) {
652- ret = NSAPI_ERROR_WOULD_BLOCK;
653- tr_debug (" send(): Modem busy. " );
654- }
655-
656- if (ret == NSAPI_ERROR_DEVICE_ERROR) {
712+ // NOTE: We cannot return NSAPI_ERROR_WOULD_BLOCK when "Recv X bytes" has reached, otherwise duplicate data send.
713+ if (_busy && ret < 0 ) {
657714 ret = NSAPI_ERROR_WOULD_BLOCK;
658- tr_debug (" send(): Send failed ." );
715+ tr_debug (" send(): Modem busy ." );
659716 }
660717
661718 if (_error) {
719+ // FIXME: Not sure clear or not of _error. See it as device error and it can recover only via reset?
720+ _sock_sending_id = -1 ;
662721 ret = NSAPI_ERROR_CONNECTION_LOST;
663722 tr_debug (" send(): Connection disrupted." );
664723 }
665724
666- if (!_sock_i[id].open && ret != NSAPI_ERROR_OK) {
725+ if (_sock_i[id].send_fail ) {
726+ _sock_sending_id = -1 ;
727+ if (_sock_i[id].proto == NSAPI_TCP) {
728+ ret = NSAPI_ERROR_DEVICE_ERROR;
729+ } else {
730+ ret = NSAPI_ERROR_NO_MEMORY;
731+ }
732+ tr_debug (" send(): SEND FAIL received." );
733+ }
734+
735+ if (!_sock_i[id].open && ret < 0 ) {
736+ _sock_sending_id = -1 ;
667737 ret = NSAPI_ERROR_CONNECTION_LOST;
668- tr_debug (" send(): Socket closed abruptly." );
738+ tr_debug (" send(): Socket %d closed abruptly." , id );
669739 }
670740
671741 set_timeout ();
@@ -938,6 +1008,14 @@ void ESP8266::_clear_socket_packets(int id)
9381008 }
9391009}
9401010
1011+ void ESP8266::_clear_socket_sending (int id)
1012+ {
1013+ if (id == _sock_sending_id) {
1014+ _sock_sending_id = -1 ;
1015+ }
1016+ _sock_i[id].send_fail = false ;
1017+ }
1018+
9411019bool ESP8266::close (int id)
9421020{
9431021 // May take a second try if device is busy
@@ -949,20 +1027,27 @@ bool ESP8266::close(int id)
9491027 _closed = false ;
9501028 _sock_i[id].open = false ;
9511029 _clear_socket_packets (id);
1030+ // Closed, so this socket escapes from SEND FAIL status.
1031+ _clear_socket_sending (id);
9521032 _smutex.unlock ();
9531033 // ESP8266 has a habit that it might close a socket on its own.
1034+ tr_debug (" close(%d): socket close OK with UNLINK ERROR" , id);
9541035 return true ;
9551036 }
9561037 } else {
9571038 // _sock_i[id].open set to false with an OOB
9581039 _clear_socket_packets (id);
1040+ // Closed, so this socket escapes from SEND FAIL status
1041+ _clear_socket_sending (id);
9591042 _smutex.unlock ();
1043+ tr_debug (" close(%d): socket close OK with AT+CIPCLOSE OK" , id);
9601044 return true ;
9611045 }
9621046 }
9631047 _smutex.unlock ();
9641048 }
9651049
1050+ tr_debug (" close(%d): socket close FAIL'ed (spurious close)" , id);
9661051 return false ;
9671052}
9681053
@@ -1140,34 +1225,42 @@ void ESP8266::_oob_socket0_closed()
11401225{
11411226 static const int id = 0 ;
11421227 _sock_i[id].open = false ;
1228+ // Closed, so this socket escapes from SEND FAIL status
1229+ _clear_socket_sending (id);
11431230 tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
11441231}
11451232
11461233void ESP8266::_oob_socket1_closed ()
11471234{
11481235 static const int id = 1 ;
11491236 _sock_i[id].open = false ;
1237+ // Closed, so this socket escapes from SEND FAIL status
1238+ _clear_socket_sending (id);
11501239 tr_debug (" _oob_socket1_closed(): Socket %d closed." , id);
11511240}
11521241
11531242void ESP8266::_oob_socket2_closed ()
11541243{
11551244 static const int id = 2 ;
11561245 _sock_i[id].open = false ;
1246+ _clear_socket_sending (id);
11571247 tr_debug (" _oob_socket2_closed(): Socket %d closed." , id);
11581248}
11591249
11601250void ESP8266::_oob_socket3_closed ()
11611251{
11621252 static const int id = 3 ;
11631253 _sock_i[id].open = false ;
1254+ _clear_socket_sending (id);
11641255 tr_debug (" _oob_socket3_closed(): %d closed." , id);
11651256}
11661257
11671258void ESP8266::_oob_socket4_closed ()
11681259{
11691260 static const int id = 4 ;
11701261 _sock_i[id].open = false ;
1262+ // Closed, so this socket escapes from SEND FAIL status
1263+ _clear_socket_sending (id);
11711264 tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
11721265}
11731266
@@ -1205,6 +1298,21 @@ void ESP8266::_oob_connection_status()
12051298 _conn_stat_cb ();
12061299}
12071300
1301+ void ESP8266::_oob_send_ok_received ()
1302+ {
1303+ tr_debug (" _oob_send_ok_received called for socket %d" , _sock_sending_id);
1304+ _sock_sending_id = -1 ;
1305+ }
1306+
1307+ void ESP8266::_oob_send_fail_received ()
1308+ {
1309+ tr_debug (" _oob_send_fail_received called for socket %d" , _sock_sending_id);
1310+ if (_sock_sending_id >= 0 && _sock_sending_id < SOCKET_COUNT) {
1311+ _sock_i[_sock_sending_id].send_fail = true ;
1312+ }
1313+ _sock_sending_id = -1 ;
1314+ }
1315+
12081316int8_t ESP8266::default_wifi_mode ()
12091317{
12101318 int8_t mode;
0 commit comments