diff --git a/ESP8266/ESP8266.cpp b/ESP8266/ESP8266.cpp index e1652cb..de96bab 100644 --- a/ESP8266/ESP8266.cpp +++ b/ESP8266/ESP8266.cpp @@ -15,24 +15,29 @@ */ #include "ESP8266.h" -#include "mbed_debug.h" +#include "Callback.h" +#include "mbed_error.h" #include "nsapi_types.h" +#include "PinNames.h" #include #define ESP8266_DEFAULT_BAUD_RATE 115200 #define ESP8266_ALL_SOCKET_IDS -1 -ESP8266::ESP8266(PinName tx, PinName rx, bool debug) - : _serial(tx, rx, ESP8266_DEFAULT_BAUD_RATE), - _parser(&_serial), - _packets(0), +ESP8266::ESP8266(PinName tx, PinName rx, bool debug, PinName rts, PinName cts) + : _serial(tx, rx, ESP8266_DEFAULT_BAUD_RATE), + _serial_rts(rts), + _serial_cts(cts), + _parser(&_serial), + _packets(0), _packets_end(&_packets), _connect_error(0), _fail(false), _closed(false), _socket_open(), - _connection_status(NSAPI_STATUS_DISCONNECTED) + _connection_status(NSAPI_STATUS_DISCONNECTED), + _heap_usage(0) { _serial.set_baud( ESP8266_DEFAULT_BAUD_RATE ); _parser.debug_on(debug); @@ -70,6 +75,34 @@ int ESP8266::get_firmware_version() } } +bool ESP8266::start_uart_hw_flow_ctrl(void) +{ + bool done = true; + + if (_serial_rts != NC && _serial_cts != NC) { + // Start board's flow control + _serial.set_flow_control(SerialBase::RTSCTS, _serial_rts, _serial_cts); + + // Start ESP8266's flow control + done = _parser.send("AT+UART_CUR=%u,8,1,0,3", ESP8266_DEFAULT_BAUD_RATE) + && _parser.recv("OK\n"); + + } else if (_serial_rts != NC) { + _serial.set_flow_control(SerialBase::RTS, _serial_rts, NC); + + done = _parser.send("AT+UART_CUR=%u,8,1,0,2", ESP8266_DEFAULT_BAUD_RATE) + && _parser.recv("OK\n"); + + } else if (_serial_cts != NC) { + done = _parser.send("AT+UART_CUR=%u,8,1,0,1", ESP8266_DEFAULT_BAUD_RATE) + && _parser.recv("OK\n"); + + _serial.set_flow_control(SerialBase::CTS, NC, _serial_cts); + } + + return done; +} + bool ESP8266::startup(int mode) { if (!(mode == WIFIMODE_STATION || mode == WIFIMODE_SOFTAP @@ -361,7 +394,10 @@ nsapi_error_t ESP8266::send(int id, const void *data, uint32_t amount) if (_parser.send("AT+CIPSEND=%d,%lu", id, amount) && _parser.recv(">") && _parser.write((char*)data, (int)amount) >= 0) { - while (_parser.process_oob()); // multiple sends in a row require this + // No flow control, data overrun is possible + if (_serial_rts == NC) { + while (_parser.process_oob()); // Drain USART receive register + } _smutex.unlock(); return NSAPI_ERROR_OK; } @@ -376,25 +412,37 @@ void ESP8266::_packet_handler() { int id; int amount; + int pdu_len; // parse out the packet if (!_parser.recv(",%d,%d:", &id, &amount)) { return; } - struct packet *packet = (struct packet*)malloc( - sizeof(struct packet) + amount); + pdu_len = sizeof(struct packet) + amount; + + if ((_heap_usage + pdu_len) > MBED_CONF_ESP8266_SOCKET_BUFSIZE) { + MBED_WARNING(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOBUFS), \ + "ESP8266::_packet_handler(): \"esp8266.socket-bufsize\"-limit exceeded, packet dropped"); + return; + } + + struct packet *packet = (struct packet*)malloc(pdu_len); if (!packet) { - debug("ESP8266: could not allocate memory for RX data\n"); + MBED_WARNING(MBED_MAKE_ERROR(MBED_MODULE_DRIVER, MBED_ERROR_CODE_ENOMEM), \ + "ESP8266::_packet_handler(): Could not allocate memory for RX data"); return; } + _heap_usage += pdu_len; packet->id = id; packet->len = amount; + packet->alloc_len = amount; packet->next = 0; if (_parser.read((char*)(packet + 1), amount) < amount) { free(packet); + _heap_usage -= pdu_len; return; } @@ -403,17 +451,23 @@ void ESP8266::_packet_handler() _packets_end = &packet->next; } +void ESP8266::process_oob(uint32_t timeout, bool all) { + setTimeout(timeout); + // Poll for inbound packets + while (_parser.process_oob() && all) { + } + setTimeout(); +} + int32_t ESP8266::recv_tcp(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(); - // check if any packets are ready for us for (struct packet **p = &_packets; *p; p = &(*p)->next) { if ((*p)->id == id) { @@ -426,10 +480,13 @@ int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout) _packets_end = p; } *p = (*p)->next; + _smutex.unlock(); + uint32_t pdu_len = sizeof(struct packet) + q->alloc_len; uint32_t len = q->len; free(q); + _heap_usage -= pdu_len; return len; } else { // return only partial packet memcpy(data, q+1, amount); @@ -446,6 +503,11 @@ int32_t ESP8266::recv_tcp(int id, void *data, uint32_t amount, uint32_t timeout) _smutex.unlock(); return 0; } + + // 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; @@ -477,7 +539,9 @@ int32_t ESP8266::recv_udp(int id, void *data, uint32_t amount, uint32_t timeout) *p = (*p)->next; _smutex.unlock(); + uint32_t pdu_len = sizeof(struct packet) + q->alloc_len; free(q); + _heap_usage -= pdu_len; return len; } } @@ -493,13 +557,14 @@ void ESP8266::_clear_socket_packets(int id) while (*p) { if ((*p)->id == id || id == ESP8266_ALL_SOCKET_IDS) { struct packet *q = *p; + int pdu_len = sizeof(struct packet) + q->alloc_len; if (_packets_end == &(*p)->next) { _packets_end = p; // Set last packet next field/_packets } *p = (*p)->next; - free(q); + _heap_usage -= pdu_len; } else { // Point to last packet next field p = &(*p)->next; diff --git a/ESP8266/ESP8266.h b/ESP8266/ESP8266.h index 60f8524..e47ce69 100644 --- a/ESP8266/ESP8266.h +++ b/ESP8266/ESP8266.h @@ -41,7 +41,7 @@ class ESP8266 { public: - ESP8266(PinName tx, PinName rx, bool debug=false); + ESP8266(PinName tx, PinName rx, bool debug=false, PinName rts=NC, PinName cts=NC); /** * Check firmware version of ESP8266 @@ -265,6 +265,13 @@ class ESP8266 */ nsapi_connection_status_t get_connection_status() const; + /** + * Start board's and ESP8266's UART flow control + * + * @return true if started + */ + bool start_uart_hw_flow_ctrl(); + static const int8_t WIFIMODE_STATION = 1; static const int8_t WIFIMODE_SOFTAP = 2; static const int8_t WIFIMODE_STATION_SOFTAP = 3; @@ -272,13 +279,16 @@ class ESP8266 private: UARTSerial _serial; + PinName _serial_rts; + PinName _serial_cts; ATCmdParser _parser; Mutex _smutex; // Protect serial port access struct packet { struct packet *next; int id; - uint32_t len; + uint32_t len; // Remaining length + uint32_t alloc_len; // Original length // data follows } *_packets, **_packets_end; void _packet_handler(); @@ -292,6 +302,7 @@ class ESP8266 void _connection_status_handler(); void _oob_socket_close_error(); void _clear_socket_packets(int id); + void process_oob(uint32_t timeout, bool all); char _ip_buffer[16]; char _gateway_buffer[16]; @@ -304,6 +315,7 @@ class ESP8266 int _socket_open[SOCKET_COUNT]; nsapi_connection_status_t _connection_status; Callback _connection_status_cb; + size_t _heap_usage; }; #endif diff --git a/ESP8266Interface.cpp b/ESP8266Interface.cpp index 56a4ec1..4796030 100644 --- a/ESP8266Interface.cpp +++ b/ESP8266Interface.cpp @@ -41,7 +41,7 @@ #define ESP8266_VERSION 2 ESP8266Interface::ESP8266Interface() - : _esp(MBED_CONF_ESP8266_TX, MBED_CONF_ESP8266_RX, MBED_CONF_ESP8266_DEBUG), + : _esp(MBED_CONF_ESP8266_TX, MBED_CONF_ESP8266_RX, MBED_CONF_ESP8266_DEBUG, MBED_CONF_ESP8266_RTS, MBED_CONF_ESP8266_CTS), _initialized(false), _started(false) { @@ -57,8 +57,8 @@ ESP8266Interface::ESP8266Interface() } // ESP8266Interface implementation -ESP8266Interface::ESP8266Interface(PinName tx, PinName rx, bool debug) - : _esp(tx, rx, debug), +ESP8266Interface::ESP8266Interface(PinName tx, PinName rx, bool debug, PinName rts, PinName cts) + : _esp(tx, rx, debug, rts, cts), _initialized(false), _started(false) { @@ -267,6 +267,9 @@ bool ESP8266Interface::_disable_default_softap() nsapi_error_t ESP8266Interface::_init(void) { if (!_initialized) { + if (!_esp.start_uart_hw_flow_ctrl()) { + return NSAPI_ERROR_DEVICE_ERROR; + } if (!_esp.reset()) { return NSAPI_ERROR_DEVICE_ERROR; } diff --git a/ESP8266Interface.h b/ESP8266Interface.h index 4d19dd6..20d5813 100644 --- a/ESP8266Interface.h +++ b/ESP8266Interface.h @@ -40,7 +40,7 @@ class ESP8266Interface : public NetworkStack, public WiFiInterface * @param rx RX pin * @param debug Enable debugging */ - ESP8266Interface(PinName tx, PinName rx, bool debug = false); + ESP8266Interface(PinName tx, PinName rx, bool debug=false, PinName rts=NC, PinName cts=NC); /** Start the interface * diff --git a/mbed_lib.json b/mbed_lib.json index 4d8cd41..5ed0b2d 100644 --- a/mbed_lib.json +++ b/mbed_lib.json @@ -3,11 +3,19 @@ "config": { "tx": { "help": "TX pin for serial connection", - "value": null + "value": "D1" }, "rx": { "help": "RX pin for serial connection", - "value": null + "value": "D0" + }, + "rts": { + "help": "RTS pin for serial connection", + "value": "NC" + }, + "cts": { + "help": "CTS pin for serial connection", + "value": "NC" }, "debug": { "help": "Enable debug logs", @@ -16,6 +24,10 @@ "provide-default": { "help": "Provide default WifiInterface. [true/false]", "value": false + }, + "socket-bufsize": { + "help": "Size of the socket data buffer", + "value": 102400 } }, "target_overrides": { @@ -30,6 +42,10 @@ "NUCLEO_F411RE": { "tx": "D8", "rx": "D2" + }, + "NUCLEO_F429ZI": { + "rts": "NC", + "cts": "NC" } } }