From c0aa5d44294c69552f7c2788b13057685a06e723 Mon Sep 17 00:00:00 2001 From: Jan Jongboom Date: Fri, 28 Jul 2017 12:41:28 +0200 Subject: [PATCH 1/3] Implement socket_bind and socket_accept --- ESP8266/ESP8266.cpp | 40 ++++++++++++++-- ESP8266/ESP8266.h | 39 +++++++++++++-- ESP8266Interface.cpp | 111 ++++++++++++++++++++++++++++++++++--------- ESP8266Interface.h | 13 +++++ 4 files changed, 173 insertions(+), 30 deletions(-) diff --git a/ESP8266/ESP8266.cpp b/ESP8266/ESP8266.cpp index 6751d78..1ccae69 100644 --- a/ESP8266/ESP8266.cpp +++ b/ESP8266/ESP8266.cpp @@ -16,8 +16,8 @@ #include "ESP8266.h" -ESP8266::ESP8266(PinName tx, PinName rx, bool debug) - : _serial(tx, rx, 1024), _parser(_serial) +ESP8266::ESP8266(PinName tx, PinName rx, Callback signalingCallback, bool debug) + : _serial(tx, rx, 1024), _parser(_serial), _signalingCallback(signalingCallback) , _packets(0), _packets_end(&_packets) { _serial.baud(115200); @@ -30,11 +30,11 @@ int ESP8266::get_firmware_version() int version; if(_parser.recv("SDK version:%d", &version) && _parser.recv("OK")) { return version; - } else { + } else { // Older firmware versions do not prefix the version with "SDK version: " return -1; } - + } bool ESP8266::startup(int mode) @@ -51,6 +51,18 @@ bool ESP8266::startup(int mode) _parser.oob("+IPD", this, &ESP8266::_packet_handler); + _parser.oob("0,CONNECT", this, &ESP8266::_incoming_socket_opened0); + _parser.oob("1,CONNECT", this, &ESP8266::_incoming_socket_opened1); + _parser.oob("2,CONNECT", this, &ESP8266::_incoming_socket_opened2); + _parser.oob("3,CONNECT", this, &ESP8266::_incoming_socket_opened3); + _parser.oob("4,CONNECT", this, &ESP8266::_incoming_socket_opened4); + + _parser.oob("0,CLOSED", this, &ESP8266::_incoming_socket_closed0); + _parser.oob("1,CLOSED", this, &ESP8266::_incoming_socket_closed1); + _parser.oob("2,CLOSED", this, &ESP8266::_incoming_socket_closed2); + _parser.oob("3,CLOSED", this, &ESP8266::_incoming_socket_closed3); + _parser.oob("4,CLOSED", this, &ESP8266::_incoming_socket_closed4); + return success; } @@ -320,3 +332,23 @@ bool ESP8266::recv_ap(nsapi_wifi_ap_t *ap) return ret; } + +bool ESP8266::bind(const SocketAddress& address) +{ + return _parser.send("AT+CIPSERVER=1,%d", address.get_port()) + && _parser.recv("OK"); +} + +void ESP8266::_incoming_socket_opened(int8_t socket_id) +{ + printf("Incoming socket opened %d\n", socket_id); + + _signalingCallback(ESP8266_SOCKET_CONNECT, socket_id); +} + +void ESP8266::_incoming_socket_closed(int8_t socket_id) +{ + printf("Incoming socket closed %d\n", socket_id); + + _signalingCallback(ESP8266_SOCKET_CLOSE, socket_id); +} diff --git a/ESP8266/ESP8266.h b/ESP8266/ESP8266.h index 2164526..f786e7d 100644 --- a/ESP8266/ESP8266.h +++ b/ESP8266/ESP8266.h @@ -19,13 +19,18 @@ #include "ATParser.h" +enum SignalingAction { + ESP8266_SOCKET_CONNECT, + ESP8266_SOCKET_CLOSE +}; + /** ESP8266Interface class. This is an interface to a ESP8266 radio. */ class ESP8266 { public: - ESP8266(PinName tx, PinName rx, bool debug=false); + ESP8266(PinName tx, PinName rx, Callback signalingCallback, bool debug=false); /** * Check firmware version of ESP8266 @@ -33,7 +38,7 @@ class ESP8266 * @return integer firmware version or -1 if firmware query command gives outdated response */ int get_firmware_version(void); - + /** * Startup the ESP8266 * @@ -97,7 +102,7 @@ class ESP8266 /** Get the local network mask * - * @return Null-terminated representation of the local network mask + * @return Null-terminated representation of the local network mask * or null if no network mask has been recieved */ const char *getNetmask(); @@ -123,7 +128,7 @@ class ESP8266 * see @a nsapi_error */ int scan(WiFiAccessPoint *res, unsigned limit); - + /**Perform a dns query * * @param name Hostname to resolve @@ -206,9 +211,19 @@ class ESP8266 attach(Callback(obj, method)); } + /** + * Start binding to a port + * + * @param address SocketAddress instance (only port is being used) + */ + bool bind(const SocketAddress& address); + + void ping(); + private: BufferedSerial _serial; ATParser _parser; + Callback _signalingCallback; struct packet { struct packet *next; @@ -217,12 +232,28 @@ class ESP8266 // data follows } *_packets, **_packets_end; void _packet_handler(); + void _incoming_socket_opened(int8_t); + void _incoming_socket_closed(int8_t); bool recv_ap(nsapi_wifi_ap_t *ap); char _ip_buffer[16]; char _gateway_buffer[16]; char _netmask_buffer[16]; char _mac_buffer[18]; + + // The CONNECTED OOB messages start with %d, and patching the ATParser is not worth it + // Max. connections is 5 according to ESP8266_SOCKET_COUNT, if that changes, this has to change too + void _incoming_socket_opened0() { _incoming_socket_opened(0); } + void _incoming_socket_opened1() { _incoming_socket_opened(1); } + void _incoming_socket_opened2() { _incoming_socket_opened(2); } + void _incoming_socket_opened3() { _incoming_socket_opened(3); } + void _incoming_socket_opened4() { _incoming_socket_opened(4); } + + void _incoming_socket_closed0() { _incoming_socket_closed(0); } + void _incoming_socket_closed1() { _incoming_socket_closed(1); } + void _incoming_socket_closed2() { _incoming_socket_closed(2); } + void _incoming_socket_closed3() { _incoming_socket_closed(3); } + void _incoming_socket_closed4() { _incoming_socket_closed(4); } }; #endif diff --git a/ESP8266Interface.cpp b/ESP8266Interface.cpp index dcf4bea..eeb9f3f 100644 --- a/ESP8266Interface.cpp +++ b/ESP8266Interface.cpp @@ -37,10 +37,11 @@ // ESP8266Interface implementation ESP8266Interface::ESP8266Interface(PinName tx, PinName rx, bool debug) - : _esp(tx, rx, debug) + : _esp(tx, rx, callback(this, &ESP8266Interface::signal), false) { memset(_ids, 0, sizeof(_ids)); memset(_cbs, 0, sizeof(_cbs)); + memset(_incoming_sockets, 0, sizeof(_incoming_sockets)); _esp.attach(this, &ESP8266Interface::event); } @@ -59,19 +60,19 @@ int ESP8266Interface::connect(const char *ssid, const char *pass, nsapi_security int ESP8266Interface::connect() { _esp.setTimeout(ESP8266_CONNECT_TIMEOUT); - + if (!_esp.reset()) { return NSAPI_ERROR_DEVICE_ERROR; - } - + } + _esp.setTimeout(ESP8266_MISC_TIMEOUT); - + if (_esp.get_firmware_version() != ESP8266_VERSION) { debug("ESP8266: ERROR: Firmware incompatible with this driver.\ - \r\nUpdate to v%d - https://developer.mbed.org/teams/ESP8266/wiki/Firmware-Update\r\n",ESP8266_VERSION); + \r\nUpdate to v%d - https://developer.mbed.org/teams/ESP8266/wiki/Firmware-Update\r\n",ESP8266_VERSION); return NSAPI_ERROR_DEVICE_ERROR; } - + _esp.setTimeout(ESP8266_CONNECT_TIMEOUT); if (!_esp.startup(3)) { @@ -164,7 +165,7 @@ int ESP8266Interface::socket_open(void **handle, nsapi_protocol_t proto) { // Look for an unused socket int id = -1; - + for (int i = 0; i < ESP8266_SOCKET_COUNT; i++) { if (!_ids[i]) { id = i; @@ -172,16 +173,16 @@ int ESP8266Interface::socket_open(void **handle, nsapi_protocol_t proto) break; } } - + if (id == -1) { return NSAPI_ERROR_NO_SOCKET; } - + struct esp8266_socket *socket = new struct esp8266_socket; if (!socket) { return NSAPI_ERROR_NO_SOCKET; } - + socket->id = id; socket->proto = proto; socket->connected = false; @@ -194,7 +195,7 @@ int ESP8266Interface::socket_close(void *handle) struct esp8266_socket *socket = (struct esp8266_socket *)handle; int err = 0; _esp.setTimeout(ESP8266_MISC_TIMEOUT); - + if (socket->connected && !_esp.close(socket->id)) { err = NSAPI_ERROR_DEVICE_ERROR; } @@ -207,7 +208,7 @@ int ESP8266Interface::socket_close(void *handle) int ESP8266Interface::socket_bind(void *handle, const SocketAddress &address) { - return NSAPI_ERROR_UNSUPPORTED; + return _esp.bind(address); } int ESP8266Interface::socket_listen(void *handle, int backlog) @@ -224,25 +225,49 @@ int ESP8266Interface::socket_connect(void *handle, const SocketAddress &addr) if (!_esp.open(proto, socket->id, addr.get_ip_address(), addr.get_port())) { return NSAPI_ERROR_DEVICE_ERROR; } - + socket->connected = true; return 0; } - + int ESP8266Interface::socket_accept(void *server, void **socket, SocketAddress *addr) { - return NSAPI_ERROR_UNSUPPORTED; + printf("socket_accept\n"); + + while (1) { + // to get the OOB messages we need to send something to the ESP8266 so the ATParser has time to + // grab the packets. Send a bogus message to the module... + _esp.get_firmware_version(); + + // now go through the _incoming_sockets array and see if there are unattached sockets... + for (size_t ix = 0; ix < ESP8266_SOCKET_COUNT; ix++) { + // printf("_incoming_sockets[ix] socket=%p connected=%d accepted=%d\n", + // _incoming_sockets[ix].socket, _incoming_sockets[ix].socket && _incoming_sockets[ix].socket->connected, + // _incoming_sockets[ix].accepted); + if (_incoming_sockets[ix].socket && _incoming_sockets[ix].socket->connected && !_incoming_sockets[ix].accepted) { + printf("accepting socket %d\n", ix); + + *socket = _incoming_sockets[ix].socket; + // addr is not used here I think... + _incoming_sockets[ix].accepted = true; + return 0; + } + } + + wait_ms(20); // socket_accept is blocking + } + return 0; } int ESP8266Interface::socket_send(void *handle, const void *data, unsigned size) { struct esp8266_socket *socket = (struct esp8266_socket *)handle; _esp.setTimeout(ESP8266_SEND_TIMEOUT); - + if (!_esp.send(socket->id, data, size)) { return NSAPI_ERROR_DEVICE_ERROR; } - + return size; } @@ -250,12 +275,12 @@ int ESP8266Interface::socket_recv(void *handle, void *data, unsigned size) { struct esp8266_socket *socket = (struct esp8266_socket *)handle; _esp.setTimeout(ESP8266_RECV_TIMEOUT); - + int32_t recv = _esp.recv(socket->id, data, size); if (recv < 0) { return NSAPI_ERROR_WOULD_BLOCK; } - + return recv; } @@ -278,7 +303,7 @@ int ESP8266Interface::socket_sendto(void *handle, const SocketAddress &addr, con } socket->addr = addr; } - + return socket_send(socket, data, size); } @@ -295,7 +320,7 @@ int ESP8266Interface::socket_recvfrom(void *handle, SocketAddress *addr, void *d void ESP8266Interface::socket_attach(void *handle, void (*callback)(void *), void *data) { - struct esp8266_socket *socket = (struct esp8266_socket *)handle; + struct esp8266_socket *socket = (struct esp8266_socket *)handle; _cbs[socket->id].callback = callback; _cbs[socket->id].data = data; } @@ -307,3 +332,45 @@ void ESP8266Interface::event() { } } } + +void ESP8266Interface::signal(SignalingAction action, int socket_id) { + printf("ESP8266Interface::signal %d %d\n", action, socket_id); + + if (action == ESP8266_SOCKET_CONNECT) { + if (_ids[socket_id]) { + // this should not be possible... + printf("ESP8266_SOCKET_CONNECT for socket that already exists...\n"); + // return; + } + + struct esp8266_socket *socket = new struct esp8266_socket; + if (!socket) { + printf("ESP8266_SOCKET_CONNECT could not create socket\n"); + return; + } + + _ids[socket_id] = true; + + socket->id = socket_id; + socket->proto = NSAPI_TCP; + socket->connected = true; + + _incoming_sockets[socket_id].socket = socket; + _incoming_sockets[socket_id].accepted = false; + } + else if (action == ESP8266_SOCKET_CLOSE) { + // Q: should we be able to delete the socket here? probably segfaults if held in user code + struct esp8266_socket *socket = _incoming_sockets[socket_id].socket; + if (!socket || !socket->connected) { + printf("ESP8266_SOCKET_CLOSE for socket that does not exist or is already closed\n"); + return; + } + + socket->connected = false; + _incoming_sockets[socket_id].accepted = false; + _incoming_sockets[socket_id].socket = NULL; + _ids[socket_id] = false; + + // delete socket? + } +} diff --git a/ESP8266Interface.h b/ESP8266Interface.h index 8376fae..a438ae3 100644 --- a/ESP8266Interface.h +++ b/ESP8266Interface.h @@ -256,6 +256,13 @@ class ESP8266Interface : public NetworkStack, public WiFiInterface } private: + /** Process a signal received from the ESP8266 driver + * Used to react to passive actions, like a socket connecting to the TCPServer + * @param action Signaling action + * @param socket The ESP8266 socket ID involved + */ + void signal(SignalingAction action, int socket); + ESP8266 _esp; bool _ids[ESP8266_SOCKET_COUNT]; @@ -270,6 +277,12 @@ class ESP8266Interface : public NetworkStack, public WiFiInterface void (*callback)(void *); void *data; } _cbs[ESP8266_SOCKET_COUNT]; + + // for incoming sockets this driver handles the memory + struct { + struct esp8266_socket* socket; + bool accepted; + } _incoming_sockets[ESP8266_SOCKET_COUNT]; }; #endif From 6a13d12f2f4ce874ddc8cfa01b7d874bd2c455bc Mon Sep 17 00:00:00 2001 From: Jan Jongboom Date: Mon, 31 Jul 2017 11:06:58 +0200 Subject: [PATCH 2/3] Do not rely on ATParser library for CONNECT and CLOSE commands --- ESP8266/ESP8266.cpp | 113 ++++++++++++++++++++++++++++++++++--------- ESP8266/ESP8266.h | 31 +++++------- ESP8266Interface.cpp | 35 +++++++------- ESP8266Interface.h | 3 +- 4 files changed, 122 insertions(+), 60 deletions(-) diff --git a/ESP8266/ESP8266.cpp b/ESP8266/ESP8266.cpp index 1ccae69..f38bd2e 100644 --- a/ESP8266/ESP8266.cpp +++ b/ESP8266/ESP8266.cpp @@ -51,18 +51,6 @@ bool ESP8266::startup(int mode) _parser.oob("+IPD", this, &ESP8266::_packet_handler); - _parser.oob("0,CONNECT", this, &ESP8266::_incoming_socket_opened0); - _parser.oob("1,CONNECT", this, &ESP8266::_incoming_socket_opened1); - _parser.oob("2,CONNECT", this, &ESP8266::_incoming_socket_opened2); - _parser.oob("3,CONNECT", this, &ESP8266::_incoming_socket_opened3); - _parser.oob("4,CONNECT", this, &ESP8266::_incoming_socket_opened4); - - _parser.oob("0,CLOSED", this, &ESP8266::_incoming_socket_closed0); - _parser.oob("1,CLOSED", this, &ESP8266::_incoming_socket_closed1); - _parser.oob("2,CLOSED", this, &ESP8266::_incoming_socket_closed2); - _parser.oob("3,CLOSED", this, &ESP8266::_incoming_socket_closed3); - _parser.oob("4,CLOSED", this, &ESP8266::_incoming_socket_closed4); - return success; } @@ -253,6 +241,10 @@ void ESP8266::_packet_handler() int32_t ESP8266::recv(int id, void *data, uint32_t amount) { + bool exit_when_not_found = false; + + bool is_incoming_socket = _incoming_socket_status[id]; + while (true) { // check if any packets are ready for us for (struct packet **p = &_packets; *p; p = &(*p)->next) { @@ -281,10 +273,37 @@ int32_t ESP8266::recv(int id, void *data, uint32_t amount) } } - // Wait for inbound packet - if (!_parser.recv("OK")) { + if (exit_when_not_found) { return -1; } + + // Here's a problem... recv() blocks for forever, but it might be that the underlying socket closes in the meantime. + // We know when it happens (due to monitoring the RX IRQ channel) + // but there's no way of signaling this thread and actually abort the request... + + if (is_incoming_socket) { + int timeout = _parser.getTimeout(); + _parser.setTimeout(1000); + + if (!_parser.recv("OK")) { + _parser.setTimeout(timeout); + + // socket gone + if (!_incoming_socket_status[id]) return NSAPI_ERROR_NO_SOCKET; + + // otherwise, just continue trying to get data... + continue; + } + } + else { + // Wait for inbound packet + if (!_parser.recv("OK")) { + // so this is weird... the message just received by the parser could actually be one of ours (in TCPServer mode)... + // so do one more pass... + exit_when_not_found = true; + continue; + } + } } } @@ -316,7 +335,7 @@ bool ESP8266::writeable() return _serial.writeable(); } -void ESP8266::attach(Callback func) +void ESP8266::attach(Callback func) { _serial.attach(func); } @@ -335,20 +354,68 @@ bool ESP8266::recv_ap(nsapi_wifi_ap_t *ap) bool ESP8266::bind(const SocketAddress& address) { + // we need an event queue to dispatch from serial RX IRQ -> non-IRQ thread + event_queue = new EventQueue(); + event_thread = new Thread(osPriorityNormal, 2048); + if (!event_queue || !event_thread) { + return NSAPI_ERROR_NO_MEMORY; + } + event_thread->start(callback(event_queue, &EventQueue::dispatch_forever)); + + // buffer to store RX data in + rx_buffer = (char*)malloc(1024); + rx_ix = 0; + if (!rx_buffer) { + return NSAPI_ERROR_NO_MEMORY; + } + + // clear incoming socket status + memset(_incoming_socket_status, 0, sizeof(_incoming_socket_status)); + + // attach to the serial + _serial.attach(callback(this, &ESP8266::attach_rx)); + + // and start the actual server return _parser.send("AT+CIPSERVER=1,%d", address.get_port()) && _parser.recv("OK"); } -void ESP8266::_incoming_socket_opened(int8_t socket_id) -{ - printf("Incoming socket opened %d\n", socket_id); +void ESP8266::process_command(char* cmd, size_t size) { + if (size == 9 /* 0,CONNECT */ + && (cmd[0] >= '0' && cmd[0] <= '9') + && (cmd[1] == ',') + && (strcmp(&cmd[2], "CONNECT") == 0)) { + + _incoming_socket_status[cmd[0] - '0'] = true; + + _signalingCallback(ESP8266_SOCKET_CONNECT, cmd[0] - '0'); + } + else if (size == 8 /* 0,CLOSED */ + && (cmd[0] >= '0' && cmd[0] <= '9') + && (cmd[1] == ',') + && (strcmp(&cmd[2], "CLOSED") == 0)) { - _signalingCallback(ESP8266_SOCKET_CONNECT, socket_id); + _incoming_socket_status[cmd[0] - '0'] = false; + + _signalingCallback(ESP8266_SOCKET_CLOSE, cmd[0] - '0'); + } + free(cmd); } -void ESP8266::_incoming_socket_closed(int8_t socket_id) -{ - printf("Incoming socket closed %d\n", socket_id); +void ESP8266::attach_rx(int c) { + // store value in buffer + rx_buffer[rx_ix] = c; + rx_buffer[rx_ix + 1] = 0; + + if (rx_ix > 0 && c == '\n') { + // got a whole command + char* cmd = (char*)calloc(rx_ix, 1); + memcpy(cmd, rx_buffer, rx_ix - 1); + event_queue->call(callback(this, &ESP8266::process_command), cmd, rx_ix - 1); + + rx_ix = 0; + return; + } - _signalingCallback(ESP8266_SOCKET_CLOSE, socket_id); + rx_ix++; } diff --git a/ESP8266/ESP8266.h b/ESP8266/ESP8266.h index f786e7d..58f9ba1 100644 --- a/ESP8266/ESP8266.h +++ b/ESP8266/ESP8266.h @@ -198,7 +198,7 @@ class ESP8266 * * @param func A pointer to a void function, or 0 to set as none */ - void attach(Callback func); + void attach(Callback func); /** * Attach a function to call whenever network state has changed @@ -208,7 +208,7 @@ class ESP8266 */ template void attach(T *obj, M method) { - attach(Callback(obj, method)); + attach(Callback(obj, method)); } /** @@ -218,9 +218,10 @@ class ESP8266 */ bool bind(const SocketAddress& address); - void ping(); - private: + void attach_rx(int); + void process_command(char*, size_t); + BufferedSerial _serial; ATParser _parser; Callback _signalingCallback; @@ -232,8 +233,6 @@ class ESP8266 // data follows } *_packets, **_packets_end; void _packet_handler(); - void _incoming_socket_opened(int8_t); - void _incoming_socket_closed(int8_t); bool recv_ap(nsapi_wifi_ap_t *ap); char _ip_buffer[16]; @@ -241,19 +240,13 @@ class ESP8266 char _netmask_buffer[16]; char _mac_buffer[18]; - // The CONNECTED OOB messages start with %d, and patching the ATParser is not worth it - // Max. connections is 5 according to ESP8266_SOCKET_COUNT, if that changes, this has to change too - void _incoming_socket_opened0() { _incoming_socket_opened(0); } - void _incoming_socket_opened1() { _incoming_socket_opened(1); } - void _incoming_socket_opened2() { _incoming_socket_opened(2); } - void _incoming_socket_opened3() { _incoming_socket_opened(3); } - void _incoming_socket_opened4() { _incoming_socket_opened(4); } - - void _incoming_socket_closed0() { _incoming_socket_closed(0); } - void _incoming_socket_closed1() { _incoming_socket_closed(1); } - void _incoming_socket_closed2() { _incoming_socket_closed(2); } - void _incoming_socket_closed3() { _incoming_socket_closed(3); } - void _incoming_socket_closed4() { _incoming_socket_closed(4); } + // TCPServer mode needs a separate thread to dispatch RX IRQ commands from + EventQueue* event_queue; + Thread* event_thread; + char* rx_buffer; + size_t rx_ix; + bool _incoming_socket_status[5]; + }; #endif diff --git a/ESP8266Interface.cpp b/ESP8266Interface.cpp index eeb9f3f..d37fbd1 100644 --- a/ESP8266Interface.cpp +++ b/ESP8266Interface.cpp @@ -37,7 +37,7 @@ // ESP8266Interface implementation ESP8266Interface::ESP8266Interface(PinName tx, PinName rx, bool debug) - : _esp(tx, rx, callback(this, &ESP8266Interface::signal), false) + : _esp(tx, rx, callback(this, &ESP8266Interface::signal), debug) { memset(_ids, 0, sizeof(_ids)); memset(_cbs, 0, sizeof(_cbs)); @@ -208,7 +208,11 @@ int ESP8266Interface::socket_close(void *handle) int ESP8266Interface::socket_bind(void *handle, const SocketAddress &address) { - return _esp.bind(address); + int bind_res = _esp.bind(address); + if (bind_res < -3000) return bind_res; + if (bind_res != 1) return NSAPI_ERROR_DEVICE_ERROR; + + return NSAPI_ERROR_OK; } int ESP8266Interface::socket_listen(void *handle, int backlog) @@ -232,13 +236,7 @@ int ESP8266Interface::socket_connect(void *handle, const SocketAddress &addr) int ESP8266Interface::socket_accept(void *server, void **socket, SocketAddress *addr) { - printf("socket_accept\n"); - while (1) { - // to get the OOB messages we need to send something to the ESP8266 so the ATParser has time to - // grab the packets. Send a bogus message to the module... - _esp.get_firmware_version(); - // now go through the _incoming_sockets array and see if there are unattached sockets... for (size_t ix = 0; ix < ESP8266_SOCKET_COUNT; ix++) { // printf("_incoming_sockets[ix] socket=%p connected=%d accepted=%d\n", @@ -254,7 +252,7 @@ int ESP8266Interface::socket_accept(void *server, void **socket, SocketAddress * } } - wait_ms(20); // socket_accept is blocking + wait_ms(20); } return 0; } @@ -277,7 +275,10 @@ int ESP8266Interface::socket_recv(void *handle, void *data, unsigned size) _esp.setTimeout(ESP8266_RECV_TIMEOUT); int32_t recv = _esp.recv(socket->id, data, size); - if (recv < 0) { + if (recv < -3000) { + return recv; + } + else if (recv < 0) { return NSAPI_ERROR_WOULD_BLOCK; } @@ -325,7 +326,7 @@ void ESP8266Interface::socket_attach(void *handle, void (*callback)(void *), voi _cbs[socket->id].data = data; } -void ESP8266Interface::event() { +void ESP8266Interface::event(int) { for (int i = 0; i < ESP8266_SOCKET_COUNT; i++) { if (_cbs[i].callback) { _cbs[i].callback(_cbs[i].data); @@ -334,12 +335,11 @@ void ESP8266Interface::event() { } void ESP8266Interface::signal(SignalingAction action, int socket_id) { - printf("ESP8266Interface::signal %d %d\n", action, socket_id); - if (action == ESP8266_SOCKET_CONNECT) { + printf("ESP8266::SOCKET CONNECT %d\n", socket_id); if (_ids[socket_id]) { // this should not be possible... - printf("ESP8266_SOCKET_CONNECT for socket that already exists...\n"); + // printf("ESP8266_SOCKET_CONNECT for socket that already exists...\n"); // return; } @@ -359,6 +359,8 @@ void ESP8266Interface::signal(SignalingAction action, int socket_id) { _incoming_sockets[socket_id].accepted = false; } else if (action == ESP8266_SOCKET_CLOSE) { + printf("ESP8266::SOCKET CLOSE %d\n", socket_id); + // Q: should we be able to delete the socket here? probably segfaults if held in user code struct esp8266_socket *socket = _incoming_sockets[socket_id].socket; if (!socket || !socket->connected) { @@ -367,10 +369,9 @@ void ESP8266Interface::signal(SignalingAction action, int socket_id) { } socket->connected = false; + _ids[socket_id] = false; _incoming_sockets[socket_id].accepted = false; _incoming_sockets[socket_id].socket = NULL; - _ids[socket_id] = false; - - // delete socket? + delete socket; } } diff --git a/ESP8266Interface.h b/ESP8266Interface.h index a438ae3..e27c59b 100644 --- a/ESP8266Interface.h +++ b/ESP8266Interface.h @@ -271,7 +271,7 @@ class ESP8266Interface : public NetworkStack, public WiFiInterface uint8_t ap_ch; char ap_pass[64]; /* The longest allowed passphrase */ - void event(); + void event(int); struct { void (*callback)(void *); @@ -283,6 +283,7 @@ class ESP8266Interface : public NetworkStack, public WiFiInterface struct esp8266_socket* socket; bool accepted; } _incoming_sockets[ESP8266_SOCKET_COUNT]; + }; #endif From d152617d320dbbd7a3351fd09f4baae0bbcaf8db Mon Sep 17 00:00:00 2001 From: Jan Jongboom Date: Mon, 31 Jul 2017 15:19:51 +0200 Subject: [PATCH 3/3] Let RxIRQ handle IPD messages. Detect it if the underlying socket changed in recv() call so we don't route back to a closed TCPSocket --- ESP8266/ESP8266.cpp | 72 ++++++++++++++++++++++++++++++++++++++++---- ESP8266/ESP8266.h | 7 +++-- ESP8266Interface.cpp | 12 +++++--- 3 files changed, 78 insertions(+), 13 deletions(-) diff --git a/ESP8266/ESP8266.cpp b/ESP8266/ESP8266.cpp index f38bd2e..4f2842f 100644 --- a/ESP8266/ESP8266.cpp +++ b/ESP8266/ESP8266.cpp @@ -22,6 +22,10 @@ ESP8266::ESP8266(PinName tx, PinName rx, Callback si { _serial.baud(115200); _parser.debugOn(debug); + + _in_server_mode = false; + _ipd_packet = NULL; + _global_socket_counter = 0; } int ESP8266::get_firmware_version() @@ -211,6 +215,8 @@ bool ESP8266::send(int id, const void *data, uint32_t amount) void ESP8266::_packet_handler() { + if (_in_server_mode) return; + int id; uint32_t amount; @@ -243,7 +249,10 @@ int32_t ESP8266::recv(int id, void *data, uint32_t amount) { bool exit_when_not_found = false; - bool is_incoming_socket = _incoming_socket_status[id]; + // see if the underlying socket changed while in the recv() call + // if you don't do this check it might be that a CLOSED,CONNECT happens on the same esp8266 socket id + // and thus we associate the data with the wrong mbed TCPSocket + uint32_t incoming_socket_id = _incoming_socket_status[id]; while (true) { // check if any packets are ready for us @@ -281,7 +290,7 @@ int32_t ESP8266::recv(int id, void *data, uint32_t amount) // We know when it happens (due to monitoring the RX IRQ channel) // but there's no way of signaling this thread and actually abort the request... - if (is_incoming_socket) { + if (incoming_socket_id > 0) { int timeout = _parser.getTimeout(); _parser.setTimeout(1000); @@ -289,7 +298,7 @@ int32_t ESP8266::recv(int id, void *data, uint32_t amount) _parser.setTimeout(timeout); // socket gone - if (!_incoming_socket_status[id]) return NSAPI_ERROR_NO_SOCKET; + if (incoming_socket_id != _incoming_socket_status[id]) return NSAPI_ERROR_NO_SOCKET; // otherwise, just continue trying to get data... continue; @@ -375,18 +384,36 @@ bool ESP8266::bind(const SocketAddress& address) // attach to the serial _serial.attach(callback(this, &ESP8266::attach_rx)); + _in_server_mode = true; + // and start the actual server return _parser.send("AT+CIPSERVER=1,%d", address.get_port()) && _parser.recv("OK"); } void ESP8266::process_command(char* cmd, size_t size) { - if (size == 9 /* 0,CONNECT */ + if (_ipd_packet) { + memcpy(_ipd_packet_data_ptr, cmd, size); + _ipd_packet_data_ptr += size; + + _ipd_packet_data_ptr[0] = '\r'; + _ipd_packet_data_ptr[1] = '\n'; + _ipd_packet_data_ptr += 2; + + if (_ipd_packet_data_ptr == ((char*)(_ipd_packet + 1)) + _ipd_packet->len) { + // append to packet list + *_packets_end = _ipd_packet; + _packets_end = &_ipd_packet->next; + + _ipd_packet = NULL; + } + } + else if (size == 9 /* 0,CONNECT */ && (cmd[0] >= '0' && cmd[0] <= '9') && (cmd[1] == ',') && (strcmp(&cmd[2], "CONNECT") == 0)) { - _incoming_socket_status[cmd[0] - '0'] = true; + _incoming_socket_status[cmd[0] - '0'] = ++_global_socket_counter; _signalingCallback(ESP8266_SOCKET_CONNECT, cmd[0] - '0'); } @@ -395,10 +422,43 @@ void ESP8266::process_command(char* cmd, size_t size) { && (cmd[1] == ',') && (strcmp(&cmd[2], "CLOSED") == 0)) { - _incoming_socket_status[cmd[0] - '0'] = false; + _incoming_socket_status[cmd[0] - '0'] = 0; _signalingCallback(ESP8266_SOCKET_CLOSE, cmd[0] - '0'); } + else if (cmd[0] == '+' && cmd[1] == 'I' && cmd[2] == 'P' && cmd[3] == 'D') { + int id = cmd[5] - '0'; + + // parse out the length param... + size_t length_ix = 6; + while (cmd[length_ix] != ':' && length_ix < size) length_ix++; + char* temp_length_buff = (char*)calloc(length_ix - 7 + 1, 1); + if (!temp_length_buff) return; + memcpy(temp_length_buff, cmd + 7, length_ix - 7); + int amount = atoi(temp_length_buff); + + // alloc a new packet (and store it in a global var. we'll get the data for this packet straight after this msg) + _ipd_packet = (struct packet*)malloc( + sizeof(struct packet) + amount); + if (!_ipd_packet) { + return; + } + + _ipd_packet->id = id; + _ipd_packet->len = amount; + _ipd_packet->next = 0; + + _ipd_packet_data_ptr = (char*)(_ipd_packet + 1); + + size_t data_len = size - length_ix - 1; + memcpy(_ipd_packet_data_ptr, cmd + length_ix + 1, data_len); + _ipd_packet_data_ptr += data_len; + + // re-add the newline \r\n again... + _ipd_packet_data_ptr[0] = '\r'; + _ipd_packet_data_ptr[1] = '\n'; + _ipd_packet_data_ptr += 2; + } free(cmd); } diff --git a/ESP8266/ESP8266.h b/ESP8266/ESP8266.h index 58f9ba1..34cd4ee 100644 --- a/ESP8266/ESP8266.h +++ b/ESP8266/ESP8266.h @@ -245,8 +245,11 @@ class ESP8266 Thread* event_thread; char* rx_buffer; size_t rx_ix; - bool _incoming_socket_status[5]; - + uint32_t _incoming_socket_status[5]; + uint32_t _global_socket_counter; + bool _in_server_mode; + struct packet *_ipd_packet; + char* _ipd_packet_data_ptr; }; #endif diff --git a/ESP8266Interface.cpp b/ESP8266Interface.cpp index d37fbd1..2386a4c 100644 --- a/ESP8266Interface.cpp +++ b/ESP8266Interface.cpp @@ -243,8 +243,6 @@ int ESP8266Interface::socket_accept(void *server, void **socket, SocketAddress * // _incoming_sockets[ix].socket, _incoming_sockets[ix].socket && _incoming_sockets[ix].socket->connected, // _incoming_sockets[ix].accepted); if (_incoming_sockets[ix].socket && _incoming_sockets[ix].socket->connected && !_incoming_sockets[ix].accepted) { - printf("accepting socket %d\n", ix); - *socket = _incoming_sockets[ix].socket; // addr is not used here I think... _incoming_sockets[ix].accepted = true; @@ -262,6 +260,10 @@ int ESP8266Interface::socket_send(void *handle, const void *data, unsigned size) struct esp8266_socket *socket = (struct esp8266_socket *)handle; _esp.setTimeout(ESP8266_SEND_TIMEOUT); + if (!socket->connected) { + return NSAPI_ERROR_NO_SOCKET; + } + if (!_esp.send(socket->id, data, size)) { return NSAPI_ERROR_DEVICE_ERROR; } @@ -336,7 +338,7 @@ void ESP8266Interface::event(int) { void ESP8266Interface::signal(SignalingAction action, int socket_id) { if (action == ESP8266_SOCKET_CONNECT) { - printf("ESP8266::SOCKET CONNECT %d\n", socket_id); + // printf("ESP8266::SOCKET CONNECT %d\n", socket_id); if (_ids[socket_id]) { // this should not be possible... // printf("ESP8266_SOCKET_CONNECT for socket that already exists...\n"); @@ -359,7 +361,7 @@ void ESP8266Interface::signal(SignalingAction action, int socket_id) { _incoming_sockets[socket_id].accepted = false; } else if (action == ESP8266_SOCKET_CLOSE) { - printf("ESP8266::SOCKET CLOSE %d\n", socket_id); + // printf("ESP8266::SOCKET CLOSE %d\n", socket_id); // Q: should we be able to delete the socket here? probably segfaults if held in user code struct esp8266_socket *socket = _incoming_sockets[socket_id].socket; @@ -372,6 +374,6 @@ void ESP8266Interface::signal(SignalingAction action, int socket_id) { _ids[socket_id] = false; _incoming_sockets[socket_id].accepted = false; _incoming_sockets[socket_id].socket = NULL; - delete socket; + // delete socket; } }