@@ -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++) {
@@ -562,9 +577,12 @@ nsapi_error_t ESP8266::open_tcp(int id, const char *addr, int port, int keepaliv
562
577
// process OOB so that _sock_i reflects the correct state of the socket
563
578
_process_oob (ESP8266_SEND_TIMEOUT, true );
564
579
565
- if (id >= SOCKET_COUNT || _sock_i[id].open ) {
580
+ // See the reason above with close()
581
+ if (id >= SOCKET_COUNT) {
566
582
_smutex.unlock ();
567
583
return NSAPI_ERROR_PARAMETER;
584
+ } else if (_sock_i[id].open ) {
585
+ close (id);
568
586
}
569
587
570
588
for (int i = 0 ; i < 2 ; i++) {
@@ -612,9 +630,24 @@ bool ESP8266::dns_lookup(const char *name, char *ip)
612
630
return done;
613
631
}
614
632
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)
616
634
{
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
+
617
647
nsapi_error_t ret = NSAPI_ERROR_DEVICE_ERROR;
648
+ int bytes_confirmed = 0 ;
649
+ constexpr unsigned int send_ack_retries = 3 ;
650
+
618
651
// +CIPSEND supports up to 2048 bytes at a time
619
652
// Data stream can be truncated
620
653
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)
626
659
}
627
660
628
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;
629
666
set_timeout (ESP8266_SEND_TIMEOUT);
630
667
_busy = false ;
631
668
_error = false ;
@@ -635,37 +672,70 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount)
635
672
}
636
673
637
674
if (!_parser.recv (" >" )) {
675
+ // This means ESP8266 hasn't even started to receive data
638
676
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
+ }
640
682
goto END;
641
683
}
642
684
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;
645
706
}
646
707
647
708
END:
648
709
_process_oob (ESP8266_RECV_TIMEOUT, true ); // Drain USART receive register to avoid data overrun
649
710
650
711
// 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 ) {
657
714
ret = NSAPI_ERROR_WOULD_BLOCK;
658
- tr_debug (" send(): Send failed ." );
715
+ tr_debug (" send(): Modem busy ." );
659
716
}
660
717
661
718
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 ;
662
721
ret = NSAPI_ERROR_CONNECTION_LOST;
663
722
tr_debug (" send(): Connection disrupted." );
664
723
}
665
724
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 ;
667
737
ret = NSAPI_ERROR_CONNECTION_LOST;
668
- tr_debug (" send(): Socket closed abruptly." );
738
+ tr_debug (" send(): Socket %d closed abruptly." , id );
669
739
}
670
740
671
741
set_timeout ();
@@ -938,6 +1008,14 @@ void ESP8266::_clear_socket_packets(int id)
938
1008
}
939
1009
}
940
1010
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
+
941
1019
bool ESP8266::close (int id)
942
1020
{
943
1021
// May take a second try if device is busy
@@ -949,20 +1027,27 @@ bool ESP8266::close(int id)
949
1027
_closed = false ;
950
1028
_sock_i[id].open = false ;
951
1029
_clear_socket_packets (id);
1030
+ // Closed, so this socket escapes from SEND FAIL status.
1031
+ _clear_socket_sending (id);
952
1032
_smutex.unlock ();
953
1033
// 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);
954
1035
return true ;
955
1036
}
956
1037
} else {
957
1038
// _sock_i[id].open set to false with an OOB
958
1039
_clear_socket_packets (id);
1040
+ // Closed, so this socket escapes from SEND FAIL status
1041
+ _clear_socket_sending (id);
959
1042
_smutex.unlock ();
1043
+ tr_debug (" close(%d): socket close OK with AT+CIPCLOSE OK" , id);
960
1044
return true ;
961
1045
}
962
1046
}
963
1047
_smutex.unlock ();
964
1048
}
965
1049
1050
+ tr_debug (" close(%d): socket close FAIL'ed (spurious close)" , id);
966
1051
return false ;
967
1052
}
968
1053
@@ -1140,34 +1225,42 @@ void ESP8266::_oob_socket0_closed()
1140
1225
{
1141
1226
static const int id = 0 ;
1142
1227
_sock_i[id].open = false ;
1228
+ // Closed, so this socket escapes from SEND FAIL status
1229
+ _clear_socket_sending (id);
1143
1230
tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
1144
1231
}
1145
1232
1146
1233
void ESP8266::_oob_socket1_closed ()
1147
1234
{
1148
1235
static const int id = 1 ;
1149
1236
_sock_i[id].open = false ;
1237
+ // Closed, so this socket escapes from SEND FAIL status
1238
+ _clear_socket_sending (id);
1150
1239
tr_debug (" _oob_socket1_closed(): Socket %d closed." , id);
1151
1240
}
1152
1241
1153
1242
void ESP8266::_oob_socket2_closed ()
1154
1243
{
1155
1244
static const int id = 2 ;
1156
1245
_sock_i[id].open = false ;
1246
+ _clear_socket_sending (id);
1157
1247
tr_debug (" _oob_socket2_closed(): Socket %d closed." , id);
1158
1248
}
1159
1249
1160
1250
void ESP8266::_oob_socket3_closed ()
1161
1251
{
1162
1252
static const int id = 3 ;
1163
1253
_sock_i[id].open = false ;
1254
+ _clear_socket_sending (id);
1164
1255
tr_debug (" _oob_socket3_closed(): %d closed." , id);
1165
1256
}
1166
1257
1167
1258
void ESP8266::_oob_socket4_closed ()
1168
1259
{
1169
1260
static const int id = 4 ;
1170
1261
_sock_i[id].open = false ;
1262
+ // Closed, so this socket escapes from SEND FAIL status
1263
+ _clear_socket_sending (id);
1171
1264
tr_debug (" _oob_socket0_closed(): Socket %d closed." , id);
1172
1265
}
1173
1266
@@ -1205,6 +1298,21 @@ void ESP8266::_oob_connection_status()
1205
1298
_conn_stat_cb ();
1206
1299
}
1207
1300
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
+
1208
1316
int8_t ESP8266::default_wifi_mode ()
1209
1317
{
1210
1318
int8_t mode;
0 commit comments