@@ -58,7 +58,7 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
58
58
_error(false ),
59
59
_busy(false ),
60
60
_reset_done(false ),
61
- _send_status(SEND_STATUS_OK ),
61
+ _sock_sending_id(- 1 ),
62
62
_conn_status(NSAPI_STATUS_DISCONNECTED)
63
63
{
64
64
_serial.set_baud (MBED_CONF_ESP8266_SERIAL_BAUDRATE);
@@ -90,13 +90,18 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
90
90
_parser.oob (" busy " , callback (this , &ESP8266::_oob_busy));
91
91
// NOTE: documentation v3.0 says '+CIPRECVDATA:<data_len>,' but it's not how the FW responds...
92
92
_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));
93
97
94
98
for (int i = 0 ; i < SOCKET_COUNT; i++) {
95
99
_sock_i[i].open = false ;
96
100
_sock_i[i].proto = NSAPI_UDP;
97
101
_sock_i[i].tcp_data = NULL ;
98
102
_sock_i[i].tcp_data_avbl = 0 ;
99
103
_sock_i[i].tcp_data_rcvd = 0 ;
104
+ _sock_i[i].send_fail = false ;
100
105
}
101
106
102
107
_scan_r.res = NULL ;
@@ -289,9 +294,7 @@ bool ESP8266::reset(void)
289
294
tr_debug (" reset(): Done: %s." , done ? " OK" : " FAIL" );
290
295
291
296
_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 ;
295
298
set_timeout ();
296
299
_smutex.unlock ();
297
300
@@ -515,9 +518,17 @@ nsapi_error_t ESP8266::open_udp(int id, const char *addr, int port, int local_po
515
518
// process OOB so that _sock_i reflects the correct state of the socket
516
519
_process_oob (ESP8266_SEND_TIMEOUT, true );
517
520
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) {
519
528
_smutex.unlock ();
520
529
return NSAPI_ERROR_PARAMETER;
530
+ } else if (_sock_i[id].open ) {
531
+ close (id);
521
532
}
522
533
523
534
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
566
577
// process OOB so that _sock_i reflects the correct state of the socket
567
578
_process_oob (ESP8266_SEND_TIMEOUT, true );
568
579
569
- if (id >= SOCKET_COUNT || _sock_i[id].open ) {
580
+ // See the reason above with close()
581
+ if (id >= SOCKET_COUNT) {
570
582
_smutex.unlock ();
571
583
return NSAPI_ERROR_PARAMETER;
584
+ } else if (_sock_i[id].open ) {
585
+ close (id);
572
586
}
573
587
574
588
for (int i = 0 ; i < 2 ; i++) {
@@ -619,13 +633,14 @@ bool ESP8266::dns_lookup(const char *name, char *ip)
619
633
nsapi_size_or_error_t ESP8266::send (int id, const void *data, uint32_t amount)
620
634
{
621
635
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
+ }
629
644
}
630
645
}
631
646
@@ -644,6 +659,10 @@ nsapi_size_or_error_t ESP8266::send(int id, const void *data, uint32_t amount)
644
659
}
645
660
646
661
_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;
647
666
set_timeout (ESP8266_SEND_TIMEOUT);
648
667
_busy = false ;
649
668
_error = false ;
@@ -685,44 +704,27 @@ nsapi_size_or_error_t ESP8266::send(int id, const void *data, uint32_t amount)
685
704
goto END;
686
705
}
687
706
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));
709
707
ret = amount;
710
708
711
709
END:
712
710
_process_oob (ESP8266_RECV_TIMEOUT, true ); // Drain USART receive register to avoid data overrun
713
711
714
712
// 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 ) {
716
715
ret = NSAPI_ERROR_WOULD_BLOCK;
717
716
tr_debug (" send(): Modem busy." );
718
717
}
719
718
720
719
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 ;
721
722
ret = NSAPI_ERROR_CONNECTION_LOST;
722
723
tr_debug (" send(): Connection disrupted." );
723
724
}
724
725
725
- if (_send_status == SEND_STATUS_FAILED) {
726
+ if (_sock_i[id].send_fail ) {
727
+ _sock_sending_id = -1 ;
726
728
if (_sock_i[id].proto == NSAPI_TCP) {
727
729
ret = NSAPI_ERROR_DEVICE_ERROR;
728
730
} else {
@@ -732,6 +734,7 @@ nsapi_size_or_error_t ESP8266::send(int id, const void *data, uint32_t amount)
732
734
}
733
735
734
736
if (!_sock_i[id].open && ret < 0 ) {
737
+ _sock_sending_id = -1 ;
735
738
ret = NSAPI_ERROR_CONNECTION_LOST;
736
739
tr_debug (" send(): Socket %d closed abruptly." , id);
737
740
}
@@ -1005,14 +1008,6 @@ void ESP8266::_clear_socket_packets(int id)
1005
1008
_sock_i[id].tcp_data_avbl = 0 ;
1006
1009
}
1007
1010
}
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
- }
1016
1011
1017
1012
bool ESP8266::close (int id)
1018
1013
{
@@ -1025,20 +1020,33 @@ bool ESP8266::close(int id)
1025
1020
_closed = false ;
1026
1021
_sock_i[id].open = false ;
1027
1022
_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 ;
1028
1028
_smutex.unlock ();
1029
1029
// 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);
1030
1031
return true ;
1031
1032
}
1032
1033
} else {
1033
1034
// _sock_i[id].open set to false with an OOB
1034
1035
_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 ;
1035
1041
_smutex.unlock ();
1042
+ tr_debug (" close(%d): socket close OK with AT+CIPCLOSE OK" , id);
1036
1043
return true ;
1037
1044
}
1038
1045
}
1039
1046
_smutex.unlock ();
1040
1047
}
1041
1048
1049
+ tr_debug (" close(%d): socket close FAIL'ed (spurious close)" , id);
1042
1050
return false ;
1043
1051
}
1044
1052
@@ -1215,39 +1223,59 @@ void ESP8266::_oob_socket0_closed()
1215
1223
{
1216
1224
static const int id = 0 ;
1217
1225
_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 ;
1219
1231
tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
1220
1232
}
1221
1233
1222
1234
void ESP8266::_oob_socket1_closed ()
1223
1235
{
1224
1236
static const int id = 1 ;
1225
1237
_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 ;
1227
1243
tr_debug (" _oob_socket1_closed(): Socket %d closed." , id);
1228
1244
}
1229
1245
1230
1246
void ESP8266::_oob_socket2_closed ()
1231
1247
{
1232
1248
static const int id = 2 ;
1233
1249
_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 ;
1235
1255
tr_debug (" _oob_socket2_closed(): Socket %d closed." , id);
1236
1256
}
1237
1257
1238
1258
void ESP8266::_oob_socket3_closed ()
1239
1259
{
1240
1260
static const int id = 3 ;
1241
1261
_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 ;
1243
1267
tr_debug (" _oob_socket3_closed(): %d closed." , id);
1244
1268
}
1245
1269
1246
1270
void ESP8266::_oob_socket4_closed ()
1247
1271
{
1248
1272
static const int id = 4 ;
1249
1273
_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 ;
1251
1279
tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
1252
1280
}
1253
1281
@@ -1287,22 +1315,17 @@ void ESP8266::_oob_connection_status()
1287
1315
1288
1316
void ESP8266::_oob_send_ok_received ()
1289
1317
{
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 ;
1296
1320
}
1297
1321
1298
1322
void ESP8266::_oob_send_fail_received ()
1299
1323
{
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 ;
1303
1327
}
1304
- _parser.remove_oob (" SEND FAIL" );
1305
- _parser.remove_oob (" SEND OK" );
1328
+ _sock_sending_id = -1 ;
1306
1329
}
1307
1330
1308
1331
int8_t ESP8266::default_wifi_mode ()
0 commit comments