diff --git a/README.md b/README.md new file mode 100644 index 0000000..7f65a82 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# easyWebSocket +A simple web socket deamon for use with the esp8266 diff --git a/library.json b/library.json index 20d34fe..f3e510d 100644 --- a/library.json +++ b/library.json @@ -1,14 +1,13 @@ { - "name": “easyWebSocket”, - "keywords": “esp8266 web socket websocket”, - "description": “A simple websocket server for esp8266. Creates and manages websocket connections using the esp8266 openSDK tcp functions. Interfaces to Arduino (and other) applications through straight forward callbacks.“, + "name": "easyWebSocket", + "keywords": "esp8266 web socket websocket", + "description": "A simple websocket server for esp8266. Creates and manages websocket connections using the esp8266 openSDK tcp functions. Interfaces to Arduino (and other) applications through straight forward callbacks.", "repository": { "type": "git", "url": "https://github.com/Coopdis/easyWebSocket" }, - "version": “1.0.0”, - "frameworks": “*”, + "version": "1.0.0", + "frameworks": "*", "platforms": "esp8266" } - diff --git a/src/easyWebSocket.cpp b/src/easyWebSocket.cpp index b0c9891..56e7257 100644 --- a/src/easyWebSocket.cpp +++ b/src/easyWebSocket.cpp @@ -14,9 +14,15 @@ extern "C" { espconn webSocketConn; esp_tcp webSocketTcp; +// Global values +// static bool readytosend = true; +// static char txbuffer2[MAX_TXBUFFER]; +// static uint16 txbufferlen = 0; + static void (*wsOnConnectionCallback)(void); static void (*wsOnMessageCallback)( char *payloadData ); +static char txbuffer[WS_MAXCONN][MAX_TXBUFFER]; static WSConnection wsConnections[WS_MAXCONN]; //*********************************************************************** @@ -26,15 +32,16 @@ void ICACHE_FLASH_ATTR webSocketInit( void ) { webSocketConn.proto.tcp = &webSocketTcp; webSocketConn.proto.tcp->local_port = WEB_SOCKET_PORT; espconn_regist_connectcb(&webSocketConn, webSocketConnectCb); - + espconn_set_opt( &webSocketConn, ESPCONN_NODELAY ); // remove nagle for low latency - + sint8 ret = espconn_accept(&webSocketConn); - if ( ret == 0 ) + if ( ret == 0 ) { webSocketDebug("webSocket server established on port %d\n", WEB_SOCKET_PORT ); - else + } else { webSocketDebug("webSocket server on port %d FAILED ret=%d\n", WEB_SOCKET_PORT, ret); - + } + return; } @@ -54,9 +61,9 @@ void ICACHE_FLASH_ATTR webSocketConnectCb(void *arg) { webSocketDebug("\n\nmeshWebSocket received connection !!!\n"); - // set time out for this connection in seconds - espconn_regist_time( connection, 120, 1); - + // set time out for this connection in seconds + espconn_regist_time(connection, 120, 1); + //find an empty slot uint8_t slotId = 0; while (wsConnections[slotId].connection != NULL && wsConnections[slotId].status != STATUS_CLOSED && slotId < WS_MAXCONN) { @@ -65,7 +72,6 @@ void ICACHE_FLASH_ATTR webSocketConnectCb(void *arg) { webSocketDebug("websocketConnectCb slotId=%d\n", slotId); - if (slotId >= WS_MAXCONN) { //no more free slots, close the connection webSocketDebug("No more free slots for WebSockets!\n"); @@ -73,22 +79,23 @@ void ICACHE_FLASH_ATTR webSocketConnectCb(void *arg) { return; } - // webSocketDebug("websocketConnectCb2\n"); + webSocketDebug("websocketConnectCb2\n"); - WSConnection wsConnection; - wsConnection.status = STATUS_UNINITIALISED; - wsConnection.connection = connection; - wsConnection.onMessage = wsOnMessageCallback; - wsConnections[slotId] = wsConnection; + wsConnections[slotId].status = STATUS_UNINITIALISED; + wsConnections[slotId].connection = connection; + wsConnections[slotId].onMessage = wsOnMessageCallback; + wsConnections[slotId].readytosend = true; + wsConnections[slotId].txbufferlen = 0; + wsConnections[slotId].txbuffer = txbuffer[slotId]; - // webSocketDebug("websocketConnectCb3\n"); + webSocketDebug("websocketConnectCb3\n"); espconn_regist_recvcb(connection, webSocketRecvCb); espconn_regist_sentcb(connection, webSocketSentCb); espconn_regist_reconcb(connection, webSocketReconCb); espconn_regist_disconcb(connection, webSocketDisconCb); - // webSocketDebug("leaving websocketConnectCb\n"); + webSocketDebug("leaving websocketConnectCb\n"); } //*********************************************************************** @@ -99,8 +106,8 @@ void ICACHE_FLASH_ATTR webSocketRecvCb(void *arg, char *data, unsigned short len //webSocketDebug("In webSocketRecvCb\n"); // webSocketDebug("webSocket recv--->%s<----\n", data); - WSConnection *wsConnection = getWsConnection(esp_connection); - if (wsConnection == NULL) { + int slotId = getWsConnection(esp_connection); + if (wsConnections[slotId].connection == NULL) { webSocketDebug("webSocket Heh?\n"); return; } @@ -141,7 +148,7 @@ void ICACHE_FLASH_ATTR webSocketRecvCb(void *arg, char *data, unsigned short len //send the response espconn_sent(esp_connection, (uint8_t *)responseMessage, strlen(responseMessage)); - wsConnection->status = STATUS_OPEN; + wsConnections[slotId].status = STATUS_OPEN; //call the connection callback if (wsOnConnectionCallback != NULL) { @@ -162,27 +169,27 @@ void ICACHE_FLASH_ATTR webSocketRecvCb(void *arg, char *data, unsigned short len //we are the server, and need to shut down the connection //if we receive an unmasked packet // webSocketDebug("frame.isMasked=false closing connection\n"); - closeWsConnection(wsConnection); + closeWsConnection(slotId); return; } -// webSocketDebug("frame.payloadData-->%s<--\n", frame.payloadData); + // webSocketDebug("frame.payloadData-->%s<--\n", frame.payloadData); if (frame.opcode == OPCODE_PING) { // webSocketDebug("frame.opcode=OPCODE_PING\n"); - sendWsMessage(wsConnection, frame.payloadData, frame.payloadLength, FLAG_FIN | OPCODE_PONG); + sendWsMessage(slotId, frame.payloadData, frame.payloadLength, FLAG_FIN | OPCODE_PONG); return; } if (frame.opcode == OPCODE_CLOSE) { //gracefully shut down the connection // webSocketDebug("frame.opcode=OPCODE_CLOSE, closeing connection\n"); - closeWsConnection(wsConnection); + closeWsConnection(slotId); return; } - if (wsConnection->onMessage != NULL) { - wsConnection->onMessage(frame.payloadData); + if (wsConnections[slotId].onMessage != NULL) { + wsConnections[slotId].onMessage(frame.payloadData); } } // webSocketDebug("Leaving webSocketRecvCb\n"); @@ -229,22 +236,23 @@ static void ICACHE_FLASH_ATTR parseWsFrame(char *data, WSFrame *frame) { } //*********************************************************************** -WSConnection *ICACHE_FLASH_ATTR getWsConnection(struct espconn *connection) { -// webSocketDebug("In getWsConnecition\n"); +int ICACHE_FLASH_ATTR getWsConnection(struct espconn *connection) { + // webSocketDebug("In getWsConnecition\n"); for (int slotId = 0; slotId < WS_MAXCONN; slotId++) { -// webSocketDebug("slotId=%d, ws.conn*=%x, espconn*=%x<-- ", slotId, wsConnections[slotId].connection, connection); + // webSocketDebug("slotId=%d, ws.conn*=%x, espconn*=%x<-- ", slotId, wsConnections[slotId].connection, connection); - // webSocketDebug("ws.connIP=%d.%d.%d.%d espconnIP=%d.%d.%d.%d --- ", IP2STR( wsConnections[slotId].connection->proto.tcp->remote_ip), IP2STR( connection->proto.tcp->remote_ip) ); -// webSocketDebug("ws.connIP=%x espconnIP=%x\n", *(uint32_t*)wsConnections[slotId].connection->proto.tcp->remote_ip, *(uint32_t*)connection->proto.tcp->remote_ip ) ; + // webSocketDebug("ws.connIP=%d.%d.%d.%d espconnIP=%d.%d.%d.%d --- ", IP2STR( wsConnections[slotId].wsConnections[slotId].proto.tcp->remote_ip), IP2STR( wsConnections[slotId].proto.tcp->remote_ip) ); + // webSocketDebug("ws.connIP=%x espconnIP=%x\n", *(uint32_t*)wsConnections[slotId].wsConnections[slotId].proto.tcp->remote_ip, *(uint32_t*)wsConnections[slotId].proto.tcp->remote_ip ) ; // if (wsConnections[slotId].connection == connection) { if (*(uint32_t*)wsConnections[slotId].connection->proto.tcp->remote_ip == *(uint32_t*)connection->proto.tcp->remote_ip ) { - // webSocketDebug("Leaving getWsConnecition slotID=%d\n", slotId); - return wsConnections + slotId; + // webSocketDebug("Leaving getWsConnecition slotID=%d\n", slotId); + // return wsConnections + slotId; + return slotId; } } -// webSocketDebug("Leaving getWsConnecition w/ NULL\n"); + // webSocketDebug("Leaving getWsConnecition w/ NULL\n"); return NULL; } @@ -267,12 +275,12 @@ static int ICACHE_FLASH_ATTR createWsAcceptKey(const char *key, char *buffer, in } //*********************************************************************** -void ICACHE_FLASH_ATTR closeWsConnection(WSConnection * connection) { +void ICACHE_FLASH_ATTR closeWsConnection(int slotId) { // webSocketDebug("In closeWsConnection\n"); char closeMessage[CLOSE_MESSAGE_LENGTH] = CLOSE_MESSAGE; - espconn_sent(connection->connection, (uint8_t *)closeMessage, sizeof(closeMessage)); - connection->status = STATUS_CLOSED; + espconn_sent(wsConnections[slotId].connection, (uint8_t *)closeMessage, sizeof(closeMessage)); + wsConnections[slotId].status = STATUS_CLOSED; return; } @@ -282,7 +290,8 @@ void ICACHE_FLASH_ATTR broadcastWsMessage(const char *payload, uint32_t payloadL for (int slotId = 0; slotId < WS_MAXCONN; slotId++) { WSConnection connection = wsConnections[slotId]; if (connection.connection != NULL && connection.status == STATUS_OPEN) { - sendWsMessage(&connection, payload, payloadLength, options); + webSocketDebug("broadcastWsMessage enter with conn: %p and add: %p\n", connection, &connection); + sendWsMessage(slotId, payload, payloadLength, options); } } } @@ -300,28 +309,42 @@ uint16_t ICACHE_FLASH_ATTR countWsConnections( void ) { } //*********************************************************************** -void ICACHE_FLASH_ATTR sendWsMessage(WSConnection *connection, +void ICACHE_FLASH_ATTR sendWsMessage(int slotId, const char *payload, uint32_t payloadLength, uint8_t options) { - // webSocketDebug("sendWsMessage-->%s<-- payloadLength=%d\n", payload,payloadLength); + + webSocketDebug("sendWsMessage-->%s<-- payloadLength=%d \n", payload, payloadLength); + // webSocketDebug("sendWsMessage-->conn: %p \n", connection); uint8_t payloadLengthField[9]; uint8_t payloadLengthFieldLength = 0; if (payloadLength > ((1 << 16) - 1)) { payloadLengthField[0] = 127; - os_memcpy(payloadLengthField + 1, &payloadLength, sizeof(uint32_t)); - payloadLengthFieldLength = sizeof(uint32_t) + 1; - } else if (payloadLength > ((1 << 8) - 1)) { + // os_memcpy(payloadLengthField + 1, &payloadLength, sizeof(uint32_t)); + // payloadLengthFieldLength = sizeof(uint32_t) + 1; + for (int i =9; i>0; i--){ + uint8_t b = (payloadLength >>((i-1) *8)) & 0xff; + os_memcpy(payloadLengthField +(10 -i), &b, sizeof(uint8_t)); + } + payloadLengthFieldLength = 9; + } else if (payloadLength > 125) { //((1 << 8) - 1) payloadLengthField[0] = 126; - os_memcpy(payloadLengthField + 1, &payloadLength, sizeof(uint16_t)); - payloadLengthFieldLength = sizeof(uint16_t) + 1; + + for (int i =2; i>0; i--){ + uint8_t b = (payloadLength >>((i-1) *8)) & 0xff; + os_memcpy(payloadLengthField +(3 -i), &b, sizeof(uint8_t)); + } + payloadLengthFieldLength = 3; } else { payloadLengthField[0] = payloadLength; payloadLengthFieldLength = 1; } + // webSocketDebug("%d %d %d %d %d %d %d %d %d\n", payloadLengthField[0], payloadLengthField[1], payloadLengthField[2], payloadLengthField[3], payloadLengthField[4], payloadLengthField[5], payloadLengthField[6],payloadLengthField[7], payloadLengthField[8] ); + // webSocketDebug("%d\n", payloadLengthFieldLength); + uint64_t maximumPossibleMessageSize = 14 + payloadLength; //14 bytes is the biggest frame header size char message[maximumPossibleMessageSize]; message[0] = FLAG_FIN | options; @@ -329,24 +352,82 @@ void ICACHE_FLASH_ATTR sendWsMessage(WSConnection *connection, os_memcpy(message + 1, &payloadLengthField, payloadLengthFieldLength); os_memcpy(message + 1 + payloadLengthFieldLength, payload, strlen(payload)); - espconn_sent(connection->connection, (uint8_t *)&message, payloadLength + 1 + payloadLengthFieldLength); + // Fill up buffer with the data instead of sending it directly + webSocketDebug("message->%s\n", message); + espbuffsent(slotId, (const char *)&message, payloadLength + 1 + payloadLengthFieldLength); + + // sint8 result = ESPCONN_OK; + // ready_send_data = false; + // result = espconn_sent(wsConnections[slotId].connection, (uint8_t *)&message, payloadLength + 1 + payloadLengthFieldLength); + // if (result != ESPCONN_OK) { + // webSocketDebug("sendWsMessage: espconn_sent error %d on conn %p \n", result, connection); + // webSocketDebug("sendWsMessage: millis: %d \n", millis()); + // } + // break; + // } else { + // webSocketDebug("waiting for ready_send_data: %d - millis: %d\n", ready_send_data, millis()); + // static uint32_t tick = 0; + // if ( millis() - tick < 1000) { return; } + // /* Do stuff here every 1 second */ + // i++; + // tick = millis(); + // } + // } + +} + +//*********************************************************************** +static sint8 ICACHE_FLASH_ATTR sendtxbuffer(int slotId) { + // webSocketDebug("sendtxbuffer: conn: %p and readytosend %d\n", connection, wsConnections[slotId].readytosend); + sint8 result = ESPCONN_OK; + if (wsConnections[slotId].txbufferlen != 0) { + wsConnections[slotId].readytosend = false; + result = espconn_sent((espconn*)wsConnections[slotId].connection, (uint8_t*)wsConnections[slotId].txbuffer, wsConnections[slotId].txbufferlen); + wsConnections[slotId].txbufferlen = 0; + if (result != ESPCONN_OK) { + // webSocketDebug("sendtxbuffer: espconn_sent error %d on conn %p\n", result, connection); + } + } + return result; +} + +sint8 ICACHE_FLASH_ATTR espbuffsent(int slotId, const char *data, uint16 len) { + // webSocketDebug("espbuffsent: enter with conn %p\n", connection); + + if (wsConnections[slotId].txbufferlen + len > MAX_TXBUFFER) { + // webSocketDebug("espbuffsent: txbuffer full on conn %p\n", connection); + return -128; + } + + os_memcpy(wsConnections[slotId].txbuffer + wsConnections[slotId].txbufferlen, data, len); + wsConnections[slotId].txbufferlen += len; + webSocketDebug("espbuffsent: txbufferlen update success with readytosend %d\n", wsConnections[slotId].readytosend); + if (wsConnections[slotId].readytosend) { + return sendtxbuffer(slotId); + } + return ESPCONN_OK; } //*********************************************************************** void ICACHE_FLASH_ATTR webSocketSentCb(void *arg) { - //data sent successfully - //webSocketDebug("webSocket sent cb \r\n"); - struct espconn *requestconn = (espconn *)arg; - // espconn_disconnect( requestconn ); + + espconn *esp_connection = (espconn*)arg; + int slotId = getWsConnection(esp_connection); + + // webSocketDebug("Sent callback on conn %p\n", wsConnection); + + if(wsConnections[slotId].connection == NULL) return; + //wswsConnections[slotId].readytosend = true; + wsConnections[slotId].readytosend = true; + sendtxbuffer(slotId); } /***********************************************************************/ void ICACHE_FLASH_ATTR webSocketDisconCb(void *arg) { espconn *esp_connection = (espconn*)arg; - - WSConnection *wsConn = getWsConnection( esp_connection); - if ( wsConn != NULL ) { - wsConn->status = STATUS_CLOSED; + int slotId = getWsConnection(esp_connection); + if ( wsConnections[slotId].connection != NULL ) { + wsConnections[slotId].status = STATUS_CLOSED; webSocketDebug("Leaving webSocket_server_discon_cb found\n"); return; } @@ -359,8 +440,3 @@ void ICACHE_FLASH_ATTR webSocketDisconCb(void *arg) { void ICACHE_FLASH_ATTR webSocketReconCb(void *arg, sint8 err) { webSocketDebug("In webSocket_server_recon_cb err=%d\n", err ); } - - - - - diff --git a/src/easyWebSocket.h b/src/easyWebSocket.h index c66f4d8..832128a 100644 --- a/src/easyWebSocket.h +++ b/src/easyWebSocket.h @@ -1,7 +1,7 @@ // adapted from https://github.com/dangrie158/ESP-8266-WebSocket -#ifndef _MESH_WEB_SOCKET_H_ -#define _MESH_WEB_SOCKET_H_ +#ifndef _EASY_WEB_SOCKET_H_ +#define _EASY_WEB_SOCKET_H_ #define WEB_SOCKET_PORT 2222 @@ -10,9 +10,10 @@ #define WS_RESPONSE "HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: %s\r\n\r\n" #define HTML_HEADER_LINEEND "\r\n" -//we normally dont need that many connection, however a single +//we normally dont need that many connection, however a single //connection only allocates a WSConnection struct and is therefore really small #define WS_MAXCONN 4 +#define MAX_TXBUFFER 1024 #define CONN_TIMEOUT 60*60*12 /* from IEEE RFC6455 sec 5.2 @@ -48,6 +49,11 @@ #define OPCODE_PING 0x9 #define OPCODE_PONG 0xA +#define WS_MASK 0x80 + +#define WS_SIZE16 126 +#define WS_SIZE64 127 + #define FLAGS_MASK ((uint8_t)0xF0) #define OPCODE_MASK ((uint8_t)0x0F) #define IS_MASKED ((uint8_t)(1<<7)) @@ -77,26 +83,29 @@ struct WSFrame { struct WSConnection { uint8_t status; - struct espconn* connection; WSOnMessage onMessage; + struct espconn* connection; + char *txbuffer; //the buffer for the data to send + uint16 txbufferlen; //the length of data in txbuffer + bool readytosend; //true, if txbuffer can send by espconn_sent }; -void inline webSocketDebug( const char* format ... ) { +void inline webSocketDebug( const char* format ... ) { char str[200]; - + va_list args; va_start(args, format); - + vsnprintf(str, sizeof(str), format, args); Serial.print( str ); // broadcastWsMessage(str, strlen(str), OPCODE_TEXT); - + va_end(args); } void ICACHE_FLASH_ATTR webSocketInit( void ); -void ICACHE_FLASH_ATTR sendWsMessage(WSConnection* connection, +void ICACHE_FLASH_ATTR sendWsMessage(int slotId, const char* payload, uint32_t payloadLength, uint8_t options); @@ -104,21 +113,24 @@ void ICACHE_FLASH_ATTR broadcastWsMessage(const char* payload, uint32_t payloadLength, uint8_t options); uint16_t ICACHE_FLASH_ATTR countWsConnections( void ); -WSConnection *ICACHE_FLASH_ATTR getWsConnection(struct espconn *connection); +int ICACHE_FLASH_ATTR getWsConnection(struct espconn *connection); static int ICACHE_FLASH_ATTR createWsAcceptKey(const char *key, char *buffer, int bufferSize); static void ICACHE_FLASH_ATTR parseWsFrame(char *data, WSFrame *frame); static void ICACHE_FLASH_ATTR unmaskWsPayload(char *maskedPayload, uint32_t payloadLength, uint32_t maskingKey); -void closeWsConnection(WSConnection* connection); +void closeWsConnection(int slotId); void webSocketSetReceiveCallback( void (*onMessage)(char *paylodData) ); -void webSocketSetConnectionCallback( void (*onConnection)(void) ); +void webSocketSetConnectionCallback( void (*onConnection)(void) ); void webSocketConnectCb(void *arg); void webSocketRecvCb(void *arg, char *pusrdata, unsigned short length); void webSocketSentCb(void *arg); +void webSocketSentWriteFinishCb(void *arg); void webSocketDisconCb(void *arg); void webSocketReconCb(void *arg, sint8 err); -#endif //_MESH_WEB_SOCKET_H_ +sint8 ICACHE_FLASH_ATTR espbuffsent(int slotId, const char *data, uint16 len); + +#endif //_EASY_WEB_SOCKET_H_