@@ -58,6 +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
+ _sock_sending_id(-1 ),
61
62
_conn_status(NSAPI_STATUS_DISCONNECTED)
62
63
{
63
64
_serial.set_baud (MBED_CONF_ESP8266_SERIAL_BAUDRATE);
@@ -89,13 +90,18 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts)
89
90
_parser.oob (" busy " , callback (this , &ESP8266::_oob_busy));
90
91
// NOTE: documentation v3.0 says '+CIPRECVDATA:<data_len>,' but it's not how the FW responds...
91
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));
92
97
93
98
for (int i = 0 ; i < SOCKET_COUNT; i++) {
94
99
_sock_i[i].open = false ;
95
100
_sock_i[i].proto = NSAPI_UDP;
96
101
_sock_i[i].tcp_data = NULL ;
97
102
_sock_i[i].tcp_data_avbl = 0 ;
98
103
_sock_i[i].tcp_data_rcvd = 0 ;
104
+ _sock_i[i].send_fail = false ;
99
105
}
100
106
101
107
_scan_r.res = NULL ;
@@ -288,6 +294,7 @@ bool ESP8266::reset(void)
288
294
tr_debug (" reset(): Done: %s." , done ? " OK" : " FAIL" );
289
295
290
296
_clear_socket_packets (ESP8266_ALL_SOCKET_IDS);
297
+ _sock_sending_id = -1 ;
291
298
set_timeout ();
292
299
_smutex.unlock ();
293
300
@@ -511,9 +518,17 @@ nsapi_error_t ESP8266::open_udp(int id, const char *addr, int port, int local_po
511
518
// process OOB so that _sock_i reflects the correct state of the socket
512
519
_process_oob (ESP8266_SEND_TIMEOUT, true );
513
520
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) {
515
528
_smutex.unlock ();
516
529
return NSAPI_ERROR_PARAMETER;
530
+ } else if (_sock_i[id].open ) {
531
+ close (id);
517
532
}
518
533
519
534
for (int i = 0 ; i < 2 ; i++) {
@@ -565,9 +580,12 @@ nsapi_error_t ESP8266::open_tcp(int id, const char *addr, int port, int keepaliv
565
580
// process OOB so that _sock_i reflects the correct state of the socket
566
581
_process_oob (ESP8266_SEND_TIMEOUT, true );
567
582
568
- if (id >= SOCKET_COUNT || _sock_i[id].open ) {
583
+ // See the reason above with close()
584
+ if (id >= SOCKET_COUNT) {
569
585
_smutex.unlock ();
570
586
return NSAPI_ERROR_PARAMETER;
587
+ } else if (_sock_i[id].open ) {
588
+ close (id);
571
589
}
572
590
573
591
for (int i = 0 ; i < 2 ; i++) {
@@ -615,9 +633,24 @@ bool ESP8266::dns_lookup(const char *name, char *ip)
615
633
return done;
616
634
}
617
635
618
- nsapi_error_t ESP8266::send (int id, const void *data, uint32_t amount)
636
+ nsapi_size_or_error_t ESP8266::send (int id, const void *data, uint32_t amount)
619
637
{
638
+ if (_sock_i[id].proto == NSAPI_TCP) {
639
+ if (_sock_sending_id >= 0 && _sock_sending_id < SOCKET_COUNT) {
640
+ if (!_sock_i[id].send_fail ) {
641
+ tr_debug (" send(): Previous packet (socket %d) was not yet ACK-ed with SEND OK." , _sock_sending_id);
642
+ return NSAPI_ERROR_WOULD_BLOCK;
643
+ } else {
644
+ tr_debug (" send(): Previous packet (socket %d) failed." , id);
645
+ return NSAPI_ERROR_DEVICE_ERROR;
646
+ }
647
+ }
648
+ }
649
+
620
650
nsapi_error_t ret = NSAPI_ERROR_DEVICE_ERROR;
651
+ int bytes_confirmed = 0 ;
652
+ constexpr unsigned int send_ack_retries = 3 ;
653
+
621
654
// +CIPSEND supports up to 2048 bytes at a time
622
655
// Data stream can be truncated
623
656
if (amount > 2048 && _sock_i[id].proto == NSAPI_TCP) {
@@ -629,7 +662,10 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
629
662
}
630
663
631
664
_smutex.lock ();
632
- RETRY:
665
+ // Mark this socket is sending. We allow only one actively sending socket because:
666
+ // 1. ESP8266 AT packets 'SEND OK'/'SEND FAIL' are not associated with socket ID. No way to tell them.
667
+ // 2. In original implementation, ESP8266::send() is synchronous, which implies only one actively sending socket.
668
+ _sock_sending_id = id;
633
669
set_timeout (ESP8266_SEND_TIMEOUT);
634
670
_busy = false ;
635
671
_error = false ;
@@ -638,52 +674,71 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
638
674
goto END;
639
675
}
640
676
641
- // We might receive "busy s/p..." and "OK" from modem, so we need to check that also
642
- _ok_received = false ;
643
- _parser.oob (" OK" , callback (this , &ESP8266::_oob_ok_received));
644
-
645
677
if (!_parser.recv (" >" )) {
646
- _parser.remove_oob (" OK" );
647
- if (_busy) {
648
- if (_ok_received) {
649
- goto RETRY;
650
- } else if (_parser.recv (" OK" )) {
651
- goto RETRY;
652
- }
653
- }
678
+ // This means ESP8266 hasn't even started to receive data
654
679
tr_debug (" send(): Didn't get \" >\" " );
655
- ret = NSAPI_ERROR_WOULD_BLOCK;
680
+ if (_sock_i[id].proto == NSAPI_TCP) {
681
+ ret = NSAPI_ERROR_WOULD_BLOCK; // Not necessarily critical error.
682
+ } else if (_sock_i[id].proto == NSAPI_UDP) {
683
+ ret = NSAPI_ERROR_NO_MEMORY;
684
+ }
685
+ goto END;
686
+ }
687
+
688
+ if (_parser.write ((char *)data, (int )amount) < 0 ) {
689
+ tr_debug (" send(): Failed to write serial data" );
690
+ // Serial is not working, serious error, reset needed.
691
+ ret = NSAPI_ERROR_DEVICE_ERROR;
656
692
goto END;
657
693
}
658
- _ok_received = false ;
659
- _parser.remove_oob (" OK" );
660
694
661
- if (_parser.write ((char *)data, (int )amount) >= 0 && _parser.recv (" SEND OK" )) {
662
- ret = NSAPI_ERROR_OK;
695
+ // The "Recv X bytes" is not documented.
696
+ if (!_parser.recv (" Recv %d bytes" , &bytes_confirmed)) {
697
+ tr_debug (" send(): Bytes not confirmed." );
698
+ if (_sock_i[id].proto == NSAPI_TCP) {
699
+ ret = NSAPI_ERROR_WOULD_BLOCK;
700
+ } else if (_sock_i[id].proto == NSAPI_UDP) {
701
+ ret = NSAPI_ERROR_NO_MEMORY;
702
+ }
703
+ } else if (bytes_confirmed != amount && _sock_i[id].proto == NSAPI_UDP) {
704
+ tr_debug (" send(): Error: confirmed %d bytes, but expected %d." , bytes_confirmed, amount);
705
+ ret = NSAPI_ERROR_DEVICE_ERROR;
706
+ } else {
707
+ // TCP can accept partial writes (if they ever happen)
708
+ ret = bytes_confirmed;
663
709
}
664
710
665
711
END:
666
712
_process_oob (ESP8266_RECV_TIMEOUT, true ); // Drain USART receive register to avoid data overrun
667
713
668
714
// error hierarchy, from low to high
669
- if (_busy) {
670
- ret = NSAPI_ERROR_WOULD_BLOCK;
671
- tr_debug (" send(): Modem busy. " );
672
- }
673
-
674
- if (ret == NSAPI_ERROR_DEVICE_ERROR) {
715
+ // NOTE: We cannot return NSAPI_ERROR_WOULD_BLOCK when "Recv X bytes" has reached, otherwise duplicate data send.
716
+ if (_busy && ret < 0 ) {
675
717
ret = NSAPI_ERROR_WOULD_BLOCK;
676
- tr_debug (" send(): Send failed ." );
718
+ tr_debug (" send(): Modem busy ." );
677
719
}
678
720
679
721
if (_error) {
722
+ // FIXME: Not sure clear or not of _error. See it as device error and it can recover only via reset?
723
+ _sock_sending_id = -1 ;
680
724
ret = NSAPI_ERROR_CONNECTION_LOST;
681
725
tr_debug (" send(): Connection disrupted." );
682
726
}
683
727
684
- if (!_sock_i[id].open && ret != NSAPI_ERROR_OK) {
728
+ if (_sock_i[id].send_fail ) {
729
+ _sock_sending_id = -1 ;
730
+ if (_sock_i[id].proto == NSAPI_TCP) {
731
+ ret = NSAPI_ERROR_DEVICE_ERROR;
732
+ } else {
733
+ ret = NSAPI_ERROR_NO_MEMORY;
734
+ }
735
+ tr_debug (" send(): SEND FAIL received." );
736
+ }
737
+
738
+ if (!_sock_i[id].open && ret < 0 ) {
739
+ _sock_sending_id = -1 ;
685
740
ret = NSAPI_ERROR_CONNECTION_LOST;
686
- tr_debug (" send(): Socket closed abruptly." );
741
+ tr_debug (" send(): Socket %d closed abruptly." , id );
687
742
}
688
743
689
744
set_timeout ();
@@ -956,6 +1011,14 @@ void ESP8266::_clear_socket_packets(int id)
956
1011
}
957
1012
}
958
1013
1014
+ void ESP8266::_clear_socket_sending (int id)
1015
+ {
1016
+ if (id == _sock_sending_id) {
1017
+ _sock_sending_id = -1 ;
1018
+ }
1019
+ _sock_i[id].send_fail = false ;
1020
+ }
1021
+
959
1022
bool ESP8266::close (int id)
960
1023
{
961
1024
// May take a second try if device is busy
@@ -967,20 +1030,27 @@ bool ESP8266::close(int id)
967
1030
_closed = false ;
968
1031
_sock_i[id].open = false ;
969
1032
_clear_socket_packets (id);
1033
+ // Closed, so this socket escapes from SEND FAIL status.
1034
+ _clear_socket_sending (id);
970
1035
_smutex.unlock ();
971
1036
// ESP8266 has a habit that it might close a socket on its own.
1037
+ tr_debug (" close(%d): socket close OK with UNLINK ERROR" , id);
972
1038
return true ;
973
1039
}
974
1040
} else {
975
1041
// _sock_i[id].open set to false with an OOB
976
1042
_clear_socket_packets (id);
1043
+ // Closed, so this socket escapes from SEND FAIL status
1044
+ _clear_socket_sending (id);
977
1045
_smutex.unlock ();
1046
+ tr_debug (" close(%d): socket close OK with AT+CIPCLOSE OK" , id);
978
1047
return true ;
979
1048
}
980
1049
}
981
1050
_smutex.unlock ();
982
1051
}
983
1052
1053
+ tr_debug (" close(%d): socket close FAIL'ed (spurious close)" , id);
984
1054
return false ;
985
1055
}
986
1056
@@ -1157,34 +1227,42 @@ void ESP8266::_oob_socket0_closed()
1157
1227
{
1158
1228
static const int id = 0 ;
1159
1229
_sock_i[id].open = false ;
1230
+ // Closed, so this socket escapes from SEND FAIL status
1231
+ _clear_socket_sending (id);
1160
1232
tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
1161
1233
}
1162
1234
1163
1235
void ESP8266::_oob_socket1_closed ()
1164
1236
{
1165
1237
static const int id = 1 ;
1166
1238
_sock_i[id].open = false ;
1239
+ // Closed, so this socket escapes from SEND FAIL status
1240
+ _clear_socket_sending (id);
1167
1241
tr_debug (" _oob_socket1_closed(): Socket %d closed." , id);
1168
1242
}
1169
1243
1170
1244
void ESP8266::_oob_socket2_closed ()
1171
1245
{
1172
1246
static const int id = 2 ;
1173
1247
_sock_i[id].open = false ;
1248
+ _clear_socket_sending (id);
1174
1249
tr_debug (" _oob_socket2_closed(): Socket %d closed." , id);
1175
1250
}
1176
1251
1177
1252
void ESP8266::_oob_socket3_closed ()
1178
1253
{
1179
1254
static const int id = 3 ;
1180
1255
_sock_i[id].open = false ;
1256
+ _clear_socket_sending (id);
1181
1257
tr_debug (" _oob_socket3_closed(): %d closed." , id);
1182
1258
}
1183
1259
1184
1260
void ESP8266::_oob_socket4_closed ()
1185
1261
{
1186
1262
static const int id = 4 ;
1187
1263
_sock_i[id].open = false ;
1264
+ // Closed, so this socket escapes from SEND FAIL status
1265
+ _clear_socket_sending (id);
1188
1266
tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
1189
1267
}
1190
1268
@@ -1222,10 +1300,19 @@ void ESP8266::_oob_connection_status()
1222
1300
_conn_stat_cb ();
1223
1301
}
1224
1302
1225
- void ESP8266::_oob_ok_received ()
1303
+ void ESP8266::_oob_send_ok_received ()
1226
1304
{
1227
- tr_debug (" _oob_ok_received called" );
1228
- _ok_received = true ;
1305
+ tr_debug (" _oob_send_ok_received called for socket %d" , _sock_sending_id);
1306
+ _sock_sending_id = -1 ;
1307
+ }
1308
+
1309
+ void ESP8266::_oob_send_fail_received ()
1310
+ {
1311
+ tr_debug (" _oob_send_fail_received called for socket %d" , _sock_sending_id);
1312
+ if (_sock_sending_id >= 0 && _sock_sending_id < SOCKET_COUNT) {
1313
+ _sock_i[_sock_sending_id].send_fail = true ;
1314
+ }
1315
+ _sock_sending_id = -1 ;
1229
1316
}
1230
1317
1231
1318
int8_t ESP8266::default_wifi_mode ()
0 commit comments