From 021e64b0e5b2a12c5724cd084a4b95b9d52f76e2 Mon Sep 17 00:00:00 2001 From: Veijo Pesonen Date: Tue, 11 Sep 2018 10:35:13 +0300 Subject: [PATCH 1/3] Enables TCP passive mode if FW v1.7 or later --- ESP8266/ESP8266.cpp | 110 +++++++++++++++++++++++++++++++++++-------- ESP8266/ESP8266.h | 24 +++++++++- ESP8266Interface.cpp | 10 ++-- 3 files changed, 118 insertions(+), 26 deletions(-) diff --git a/ESP8266/ESP8266.cpp b/ESP8266/ESP8266.cpp index 7c2305c..a9011a3 100644 --- a/ESP8266/ESP8266.cpp +++ b/ESP8266/ESP8266.cpp @@ -30,6 +30,7 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts) _serial_rts(rts), _serial_cts(cts), _parser(&_serial), + _tcp_passive(false), _packets(0), _packets_end(&_packets), _sdk_v(-1,-1,-1), @@ -37,7 +38,6 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts) _connect_error(0), _fail(false), _closed(false), - _socket_open(), _connection_status(NSAPI_STATUS_DISCONNECTED), _heap_usage(0) { @@ -57,6 +57,11 @@ ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts) _parser.oob("4,CLOSED", callback(this, &ESP8266::_oob_socket4_closed_handler)); _parser.oob("WIFI ", callback(this, &ESP8266::_connection_status_handler)); _parser.oob("UNLINK", callback(this, &ESP8266::_oob_socket_close_error)); + + for(int i= 0; i < SOCKET_COUNT; i++) { + _socket_open[i].id = -1; + _socket_open[i].proto = NSAPI_UDP; + } } bool ESP8266::at_available() @@ -76,9 +81,9 @@ struct ESP8266::fw_sdk_version ESP8266::sdk_version() int patch; _smutex.lock(); - bool done = _parser.send("AT+GMR"); - done &= _parser.recv("SDK version:%d.%d.%d", &major, &minor, &patch); - done &= _parser.recv("OK\n"); + bool done = _parser.send("AT+GMR") + && _parser.recv("SDK version:%d.%d.%d", &major, &minor, &patch) + && _parser.recv("OK\n"); _smutex.unlock(); if(done) { @@ -97,9 +102,9 @@ struct ESP8266::fw_at_version ESP8266::at_version() int nused; _smutex.lock(); - bool done = _parser.send("AT+GMR"); - done &= _parser.recv("AT version:%d.%d.%d.%d", &major, &minor, &patch, &nused); - done &= _parser.recv("OK\n"); + bool done = _parser.send("AT+GMR") + && _parser.recv("AT version:%d.%d.%d.%d", &major, &minor, &patch, &nused) + && _parser.recv("OK\n"); _smutex.unlock(); if(done) { @@ -210,6 +215,23 @@ bool ESP8266::dhcp(bool enabled, int mode) return done; } +bool ESP8266::cond_enable_tcp_passive_mode() +{ + bool done = true; + + if (FW_AT_LEAST_VERSION(_at_v.major, _at_v.minor, _at_v.patch, 0, ESP8266_AT_VERSION_TCP_PASSIVE_MODE)) { + _smutex.lock(); + done = _parser.send("AT+CIPRECVMODE=1") + && _parser.recv("OK\n"); + _smutex.unlock(); + + _tcp_passive = done ? true : false; + } + + return done; +} + + nsapi_error_t ESP8266::connect(const char *ap, const char *passPhrase) { _smutex.lock(); @@ -376,7 +398,7 @@ nsapi_error_t ESP8266::open_udp(int id, const char* addr, int port, int local_po if (id >= SOCKET_COUNT) { return NSAPI_ERROR_PARAMETER; - } else if (_socket_open[id]) { + } else if (_socket_open[id].id == id) { return NSAPI_ERROR_IS_CONNECTED; } @@ -390,7 +412,8 @@ nsapi_error_t ESP8266::open_udp(int id, const char* addr, int port, int local_po } if (done) { - _socket_open[id] = 1; + _socket_open[id].id = id; + _socket_open[id].proto = NSAPI_UDP; } _clear_socket_packets(id); @@ -405,7 +428,7 @@ bool ESP8266::open_tcp(int id, const char* addr, int port, int keepalive) static const char *type = "TCP"; bool done = false; - if (id >= SOCKET_COUNT || _socket_open[id]) { + if (id >= SOCKET_COUNT || _socket_open[id].id == id) { return false; } @@ -419,7 +442,8 @@ bool ESP8266::open_tcp(int id, const char* addr, int port, int keepalive) } if (done) { - _socket_open[id] = 1; + _socket_open[id].id = id; + _socket_open[id].proto = NSAPI_TCP; } _clear_socket_packets(id); @@ -467,8 +491,21 @@ void ESP8266::_packet_handler() int amount; int pdu_len; - // parse out the packet - if (!_parser.recv(",%d,%d:", &id, &amount)) { + // Get socket id + if (!_parser.recv(",%d,", &id)) { + return; + } + // In passive mode amount not used... + if(_tcp_passive + && _socket_open[id].id == id + && _socket_open[id].proto == NSAPI_TCP) { + if (!_parser.recv("%d\n", &amount)) { + MBED_ERROR(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENODATA), \ + "ESP8266::_packet_handler(): Data length missing"); + } + return; + // Amount required in active mode + } else if (!_parser.recv("%d:", &amount)) { return; } @@ -512,8 +549,41 @@ void ESP8266::process_oob(uint32_t timeout, bool all) { setTimeout(); } +int32_t ESP8266::_recv_tcp_passive(int id, void *data, uint32_t amount, uint32_t timeout) +{ + int32_t len; + int32_t ret; + + _smutex.lock(); + + bool done = _parser.send("AT+CIPRECVDATA=%d,%lu", id, amount); + if (!done) { + _smutex.unlock(); + return NSAPI_ERROR_DEVICE_ERROR; + } + // NOTE: documentation v3.0 says '+CIPRECVDATA:,' but it's not how the FW responds... + done = _parser.recv("+CIPRECVDATA,%ld:", &len) + && _parser.read((char*)data, len) + && _parser.recv("OK\n"); + + // Got data? + if (done) { + ret = len; + } else { + // Socket still open? + ret = _socket_open[id].id != id ? 0 : (int32_t)NSAPI_ERROR_WOULD_BLOCK; + } + + _smutex.unlock(); + return ret; +} + int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout) { + if (_tcp_passive) { + return _recv_tcp_passive(id, data, amount, timeout); + } + _smutex.lock(); // No flow control, drain the USART receive register ASAP to avoid data overrun @@ -552,7 +622,7 @@ int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout) } } } - if(!_socket_open[id]) { + if(_socket_open[id].id < 0) { _smutex.unlock(); return 0; } @@ -634,7 +704,7 @@ bool ESP8266::close(int id) if (!_parser.recv("OK\n")) { if (_closed) { // UNLINK ERROR _closed = false; - _socket_open[id] = 0; + _socket_open[id].id = -1; _clear_socket_packets(id); _smutex.unlock(); // ESP8266 has a habit that it might close a socket on its own. @@ -717,27 +787,27 @@ void ESP8266::_oob_socket_close_error() void ESP8266::_oob_socket0_closed_handler() { - _socket_open[0] = 0; + _socket_open[0].id = -1; } void ESP8266::_oob_socket1_closed_handler() { - _socket_open[1] = 0; + _socket_open[1].id = -1; } void ESP8266::_oob_socket2_closed_handler() { - _socket_open[2] = 0; + _socket_open[2].id = -1; } void ESP8266::_oob_socket3_closed_handler() { - _socket_open[3] = 0; + _socket_open[3].id = -1; } void ESP8266::_oob_socket4_closed_handler() { - _socket_open[4] = 0; + _socket_open[4].id = -1; } void ESP8266::_connection_status_handler() diff --git a/ESP8266/ESP8266.h b/ESP8266/ESP8266.h index 88313fe..014f45e 100644 --- a/ESP8266/ESP8266.h +++ b/ESP8266/ESP8266.h @@ -35,6 +35,17 @@ #define ESP8266_MISC_TIMEOUT 2000 #endif +// Firmware version +#define ESP8266_SDK_VERSION 2000000 +#define ESP8266_SDK_VERSION_MAJOR ESP8266_SDK_VERSION/1000000 + +#define ESP8266_AT_VERSION 1000000 +#define ESP8266_AT_VERSION_MAJOR ESP8266_AT_VERSION/1000000 +#define ESP8266_AT_VERSION_TCP_PASSIVE_MODE 1070000 + +#define FW_AT_LEAST_VERSION(MAJOR,MINOR,PATCH,NUSED/*Not used*/,REF) \ + (((MAJOR)*1000000+(MINOR)*10000+(PATCH)*100) >= REF ? true : false) + /** ESP8266Interface class. This is an interface to a ESP8266 radio. */ @@ -321,6 +332,11 @@ class ESP8266 */ bool stop_uart_hw_flow_ctrl(); + /* + * From AT firmware v1.7.0.0 onwards enables TCP passive mode + */ + bool cond_enable_tcp_passive_mode(); + static const int8_t WIFIMODE_STATION = 1; static const int8_t WIFIMODE_SOFTAP = 2; static const int8_t WIFIMODE_STATION_SOFTAP = 3; @@ -332,6 +348,7 @@ class ESP8266 PinName _serial_cts; ATCmdParser _parser; Mutex _smutex; // Protect serial port access + bool _tcp_passive; struct packet { struct packet *next; @@ -355,16 +372,21 @@ class ESP8266 void _oob_socket_close_error(); void _clear_socket_packets(int id); void process_oob(uint32_t timeout, bool all); + int32_t _recv_tcp_passive(int id, void *data, uint32_t amount, uint32_t timeout); char _ip_buffer[16]; char _gateway_buffer[16]; char _netmask_buffer[16]; char _mac_buffer[18]; + struct _sock_info { + int id; + nsapi_protocol_t proto; + }; int _connect_error; bool _fail; bool _closed; - int _socket_open[SOCKET_COUNT]; + struct _sock_info _socket_open[SOCKET_COUNT]; nsapi_connection_status_t _connection_status; Callback _connection_status_cb; size_t _heap_usage; diff --git a/ESP8266Interface.cpp b/ESP8266Interface.cpp index 4bd6e90..a587c9d 100644 --- a/ESP8266Interface.cpp +++ b/ESP8266Interface.cpp @@ -37,10 +37,6 @@ #endif #endif -// Firmware version -#define ESP8266_SDK_VERSION_MAJOR 2 -#define ESP8266_AT_VERSION_MAJOR 1 - ESP8266Interface::ESP8266Interface() : _esp(MBED_CONF_ESP8266_TX, MBED_CONF_ESP8266_RX, MBED_CONF_ESP8266_DEBUG, MBED_CONF_ESP8266_RTS, MBED_CONF_ESP8266_CTS), _initialized(false), @@ -290,9 +286,13 @@ nsapi_error_t ESP8266Interface::_init(void) if (!_get_firmware_ok()) { return NSAPI_ERROR_DEVICE_ERROR; } - if (_disable_default_softap() == false) { + if (!_disable_default_softap()) { + return NSAPI_ERROR_DEVICE_ERROR; + } + if (!_esp.cond_enable_tcp_passive_mode()) { return NSAPI_ERROR_DEVICE_ERROR; } + _initialized = true; } return NSAPI_ERROR_OK; From af54d640431169e813a10895ed0ea4407961311c Mon Sep 17 00:00:00 2001 From: Veijo Pesonen Date: Thu, 13 Sep 2018 16:23:01 +0300 Subject: [PATCH 2/3] Drains TCP socket buffer from ESP8266 on close --- ESP8266/ESP8266.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/ESP8266/ESP8266.cpp b/ESP8266/ESP8266.cpp index a9011a3..202f38d 100644 --- a/ESP8266/ESP8266.cpp +++ b/ESP8266/ESP8266.cpp @@ -552,26 +552,29 @@ void ESP8266::process_oob(uint32_t timeout, bool all) { int32_t ESP8266::_recv_tcp_passive(int id, void *data, uint32_t amount, uint32_t timeout) { int32_t len; - int32_t ret; + int32_t ret = (int32_t)NSAPI_ERROR_WOULD_BLOCK; _smutex.lock(); - bool done = _parser.send("AT+CIPRECVDATA=%d,%lu", id, amount); - if (!done) { - _smutex.unlock(); - return NSAPI_ERROR_DEVICE_ERROR; - } // NOTE: documentation v3.0 says '+CIPRECVDATA:,' but it's not how the FW responds... - done = _parser.recv("+CIPRECVDATA,%ld:", &len) + bool done = _parser.send("AT+CIPRECVDATA=%d,%lu", id, amount) + && _parser.recv("+CIPRECVDATA,%ld:", &len) && _parser.read((char*)data, len) && _parser.recv("OK\n"); - // Got data? if (done) { - ret = len; - } else { - // Socket still open? - ret = _socket_open[id].id != id ? 0 : (int32_t)NSAPI_ERROR_WOULD_BLOCK; + _smutex.unlock(); + return len; + } + + // Socket closed, doesn't mean there couldn't be data left + if (_socket_open[id].id != id) { + done = _parser.send("AT+CIPRECVDATA=%d,%lu", id, amount) + && _parser.recv("+CIPRECVDATA,%ld:", &len) + && _parser.read((char*)data, len) + && _parser.recv("OK\n"); + + ret = done ? len : 0; } _smutex.unlock(); From cbae35b64391f7275f8dd5d926c304ce244ae556 Mon Sep 17 00:00:00 2001 From: Veijo Pesonen Date: Fri, 14 Sep 2018 08:49:16 +0300 Subject: [PATCH 3/3] Reduces OOB processing when flow control enabled and processing UDP packets --- ESP8266/ESP8266.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/ESP8266/ESP8266.cpp b/ESP8266/ESP8266.cpp index 202f38d..b9f71b4 100644 --- a/ESP8266/ESP8266.cpp +++ b/ESP8266/ESP8266.cpp @@ -644,8 +644,9 @@ int32_t ESP8266::recv_udp(int id, void *data, uint32_t amount, uint32_t timeout) _smutex.lock(); setTimeout(timeout); - // Poll for inbound packets - while (_parser.process_oob()) { + // No flow control, drain the USART receive register ASAP to avoid data overrun + if (_serial_rts == NC) { + process_oob(timeout, true); } setTimeout(); @@ -671,6 +672,12 @@ int32_t ESP8266::recv_udp(int id, void *data, uint32_t amount, uint32_t timeout) return len; } } + + // Flow control, read from USART receive register only when no more data is buffered, and as little as possible + if (_serial_rts != NC) { + process_oob(timeout, false); + } + _smutex.unlock(); return NSAPI_ERROR_WOULD_BLOCK;