diff --git a/examples/WebSocketsServer/WebSocketsServer.ino b/examples/WebSocketsServer/WebSocketsServer.ino new file mode 100644 index 0000000..c0895ca --- /dev/null +++ b/examples/WebSocketsServer/WebSocketsServer.ino @@ -0,0 +1,78 @@ +/* + * WebSocketsServer.ino + * + * Created on: 22.05.2015 + * + */ + +#include + +#include +#include +#include +#include + +ESP8266WiFiMulti WiFiMulti; + +WebSocketsServer webSocket = WebSocketsServer(81); + +void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t lenght) { + + switch(type) { + case WStype_DISCONNECTED: + Serial1.printf("[%d] Disconnected!\n", num); + break; + case WStype_CONNECTED: + Serial1.printf("[%d] Connected.\n", num); + break; + case WStype_TEXT: + Serial1.printf("[%d] get Text: %s\n", num, payload); + + // echo data back to browser + webSocket.sendTXT(num, payload, lenght); + + // send data to all connected clients + webSocket.broadcastTXT(payload, lenght); + break; + case WStype_BIN: + Serial1.printf("[%d] get binary.\n", num); + hexdump(payload, lenght); + + // echo data back to browser + webSocket.sendBIN(num, payload, lenght); + break; + } + +} + +void setup() { + Serial.begin(921600); + Serial1.begin(921600); + + //Serial.setDebugOutput(true); + Serial1.setDebugOutput(true); + + Serial1.println(); + Serial1.println(); + Serial1.println(); + + for(uint8_t t = 4; t > 0; t--) { + Serial1.printf("[SETUP] BOOT WAIT %d...\n", t); + Serial1.flush(); + delay(1000); + } + + WiFiMulti.addAP("SSID", "passpasspass"); + + while(WiFiMulti.run() != WL_CONNECTED) { + delay(100); + } + + webSocket.begin(); + webSocket.onEvent(webSocketEvent); +} + +void loop() { + webSocket.loop(); +} + diff --git a/src/WebSockets.cpp b/src/WebSockets.cpp index b976994..295c6f0 100644 --- a/src/WebSockets.cpp +++ b/src/WebSockets.cpp @@ -27,19 +27,13 @@ /** * * @param client WSclient_t * ptr to the client struct - * @param code + * @param code uint16_t see RFC * @param reason * @param reasonLen */ void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * reason, size_t reasonLen) { if(client->status == WSC_CONNECTED && code) { - //todo send reason to client - - if(reasonLen > 0 && reason) { - - } else { - - } + sendFrame(client, WSop_close, (uint8_t *) reason, reasonLen); } clientDisconnect(client); } @@ -49,9 +43,9 @@ void WebSockets::clientDisconnect(WSclient_t * client, uint16_t code, char * rea * @param client WSclient_t * ptr to the client struct * @param opcode WSopcode_t * @param payload uint8_t * - * @param lenght size_t + * @param length size_t */ -void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) { +void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length) { uint8_t buffer[16] = { 0 }; uint8_t i = 0; @@ -61,31 +55,32 @@ void WebSockets::sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * pay buffer[i] = bit(7); // set Fin buffer[i++] |= opcode; // set opcode - if(lenght < 126) { - buffer[i++] = lenght; + if(length < 126) { + buffer[i++] = length; - } else if(lenght < 0xFFFF) { + } else if(length < 0xFFFF) { buffer[i++] = 126; - buffer[i++] = ((lenght >> 8) & 0xFF); - buffer[i++] = (lenght & 0xFF); + buffer[i++] = ((length >> 8) & 0xFF); + buffer[i++] = (length & 0xFF); } else { + // normaly we never get here (to less memory) buffer[i++] = 127; buffer[i++] = 0x00; buffer[i++] = 0x00; buffer[i++] = 0x00; buffer[i++] = 0x00; - buffer[i++] = ((lenght >> 24) & 0xFF); - buffer[i++] = ((lenght >> 16) & 0xFF); - buffer[i++] = ((lenght >> 8) & 0xFF); - buffer[i++] = (lenght & 0xFF); + buffer[i++] = ((length >> 24) & 0xFF); + buffer[i++] = ((length >> 16) & 0xFF); + buffer[i++] = ((length >> 8) & 0xFF); + buffer[i++] = (length & 0xFF); } // send header client->tcp.write(&buffer[0], i); - if(payload && lenght > 0) { + if(payload && length > 0) { // send payload - client->tcp.write(&payload[0], lenght); + client->tcp.write(&payload[0], length); } } @@ -136,7 +131,7 @@ void WebSockets::handleWebsocket(WSclient_t * client) { } payloadLen = buffer[0] << 8 | buffer[1]; } else if(payloadLen == 127) { - // read 64bit inteager as Lenght + // read 64bit inteager as length if(!readWait(client, buffer, 8)) { //timeout clientDisconnect(client, 1002); @@ -191,32 +186,24 @@ void WebSockets::handleWebsocket(WSclient_t * client) { switch(opCode) { case WSop_text: - DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] text: %s\n", client->num, payload) - ; - // todo API for user to get message may callback - - // send the frame back! - sendFrame(client, WSop_text, payload, payloadLen); - - break; + DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] text: %s\n", client->num, payload); + // no break here! case WSop_binary: - // todo API for user to get message may callback + messageRecived(client, opCode, payload, payloadLen); break; case WSop_ping: - // todo send pong + // send pong back + sendFrame(client, WSop_pong, payload, payloadLen); break; case WSop_pong: - DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] get pong from Client (%s)\n", client->num, payload) - ; + DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] get pong from Client (%s)\n", client->num, payload); break; - case WSop_close: { - uint16_t reasonCode = buffer[0] << 8 | buffer[1]; - - DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] client ask for close. Code: %d (%s)\n", client->num, reasonCode, (payload + 2)); - - // todo send confimation to client - clientDisconnect(client, 1000, (char *) (payload + 2), payloadLen - 2); - } + case WSop_close: + { + uint16_t reasonCode = buffer[0] << 8 | buffer[1]; + DEBUG_WEBSOCKETS("[WS-Server][%d][handleWebsocket] client ask for close. Code: %d (%s)\n", client->num, reasonCode, (payload + 2)); + clientDisconnect(client, 1000); + } break; case WSop_continuation: // continuation is not supported diff --git a/src/WebSockets.h b/src/WebSockets.h index 6ac19f3..ee6aa5a 100644 --- a/src/WebSockets.h +++ b/src/WebSockets.h @@ -37,7 +37,7 @@ #endif #endif -#define DEBUG_WEBSOCKETS(...) Serial1.printf( __VA_ARGS__ ); Serial1.flush() +//#define DEBUG_WEBSOCKETS(...) Serial1.printf( __VA_ARGS__ ); #ifndef DEBUG_WEBSOCKETS #define DEBUG_WEBSOCKETS(...) @@ -95,9 +95,10 @@ class WebSockets { virtual void clientDisconnect(WSclient_t * client); virtual bool clientIsConnected(WSclient_t * client); + virtual void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); void clientDisconnect(WSclient_t * client, uint16_t code, char * reason = NULL, size_t reasonLen = 0); - void sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t lenght = 0); + void sendFrame(WSclient_t * client, WSopcode_t opcode, uint8_t * payload = NULL, size_t length = 0); void handleWebsocket(WSclient_t * client); diff --git a/src/WebSocketsServer.cpp b/src/WebSocketsServer.cpp index feb5936..5048f8d 100644 --- a/src/WebSocketsServer.cpp +++ b/src/WebSocketsServer.cpp @@ -26,7 +26,7 @@ #include "WebSocketsServer.h" extern "C" { - #include "libb64/cencode.h" +#include "libb64/cencode.h" } #include @@ -34,12 +34,18 @@ extern "C" { WebSocketsServer::WebSocketsServer(uint16_t port) { _port = port; _server = new WiFiServer(port); + + _cbEvent = NULL; + } WebSocketsServer::~WebSocketsServer() { // todo how to close server? } +/** + * calles to init the Websockets server + */ void WebSocketsServer::begin(void) { WSclient_t * client; @@ -71,13 +77,116 @@ void WebSocketsServer::loop(void) { } +/** + * set callback function + * @param cbEvent WebSocketServerEvent + */ +void WebSocketsServer::onEvent(WebSocketServerEvent cbEvent) { + WebSocketsServer::_cbEvent = cbEvent; +} + +/** + * send text data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsServer::sendTXT(uint8_t num, uint8_t * payload, size_t length) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + sendFrame(client, WSop_text, payload, length); + } +} + +void WebSocketsServer::sendTXT(uint8_t num, String payload) { + sendTXT(num, (uint8_t *)payload.c_str(), payload.length()); +} + +/** + * send text data to client all + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsServer::broadcastTXT(uint8_t * payload, size_t length) { + WSclient_t * client; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + sendFrame(client, WSop_text, payload, length); + } + } +} + + +void WebSocketsServer::broadcastTXT(String payload) { + broadcastTXT((uint8_t *)payload.c_str(), payload.length()); +} + +/** + * send binary data to client + * @param num uint8_t client id + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsServer::sendBIN(uint8_t num, uint8_t * payload, size_t length) { + if(num >= WEBSOCKETS_SERVER_CLIENT_MAX) { + return; + } + WSclient_t * client = &_clients[num]; + if(clientIsConnected(client)) { + sendFrame(client, WSop_binary, payload, length); + } +} + +/** + * send binary data to client all + * @param payload uint8_t * + * @param length size_t + */ +void WebSocketsServer::broadcastBIN(uint8_t * payload, size_t length) { + WSclient_t * client; + for(uint8_t i = 0; i < WEBSOCKETS_SERVER_CLIENT_MAX; i++) { + client = &_clients[i]; + if(clientIsConnected(client)) { + sendFrame(client, WSop_binary, payload, length); + } + } +} + //################################################################################# //################################################################################# //################################################################################# +/** + * + * @param client WSclient_t * ptr to the client struct + * @param opcode WSopcode_t + * @param payload uint8_t * + * @param lenght size_t + */ +void WebSocketsServer::messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t lenght) { + WStype_t type = WStype_ERROR; + + switch(opcode) { + case WSop_text: + type = WStype_TEXT; + break; + case WSop_binary: + type = WStype_BIN; + break; + } + + if(_cbEvent) { + _cbEvent(client->num, type, payload, lenght); + } +} + /** * Disconnect an client - * @param client WSclients_t * ptr to the client struct + * @param client WSclient_t * ptr to the client struct */ void WebSocketsServer::clientDisconnect(WSclient_t * client) { @@ -98,11 +207,14 @@ void WebSocketsServer::clientDisconnect(WSclient_t * client) { DEBUG_WEBSOCKETS("[WS-Server][%d] client disconnected.\n", client->num); + if(_cbEvent) { + _cbEvent(client->num, WStype_DISCONNECTED, NULL, 0); + } } /** * get client state - * @param client WSclients_t * ptr to the client struct + * @param client WSclient_t * ptr to the client struct * @return true = conneted */ bool WebSocketsServer::clientIsConnected(WSclient_t * client) { @@ -189,7 +301,7 @@ void WebSocketsServer::handleClientData(void) { /** * handle the WebSocket header reading - * @param client WSclients_t * ptr to the client struct + * @param client WSclient_t * ptr to the client struct */ void WebSocketsServer::handleHeader(WSclient_t * client) { @@ -225,13 +337,7 @@ void WebSocketsServer::handleHeader(WSclient_t * client) { } else { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Header read fin.\n", client->num); - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str()); - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade); - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket); - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str()); - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str()); - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str()); - DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion); + DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cURL: %s\n", client->num, client->cUrl.c_str());DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsUpgrade: %d\n", client->num, client->cIsUpgrade);DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cIsWebsocket: %d\n", client->num, client->cIsWebsocket);DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cKey: %s\n", client->num, client->cKey.c_str());DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cProtocol: %s\n", client->num, client->cProtocol.c_str());DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cExtensions: %s\n", client->num, client->cExtensions.c_str());DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] - cVersion: %d\n", client->num, client->cVersion); bool ok = (client->cIsUpgrade && client->cIsWebsocket); @@ -252,7 +358,7 @@ void WebSocketsServer::handleHeader(WSclient_t * client) { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] Websocket connection incomming.\n", client->num); // generate Sec-WebSocket-Accept key - uint8_t sha1HashBin[20] = {0}; + uint8_t sha1HashBin[20] = { 0 }; sha1(client->cKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11", &sha1HashBin[0]); char sha1Base64[64] = { 0 }; @@ -260,7 +366,7 @@ void WebSocketsServer::handleHeader(WSclient_t * client) { base64_encodestate _state; base64_init_encodestate(&_state); - len = base64_encode_block((const char *)&sha1HashBin[0], 20, &sha1Base64[0], &_state); + len = base64_encode_block((const char *) &sha1HashBin[0], 20, &sha1Base64[0], &_state); base64_encode_blockend((sha1Base64 + len), &_state); client->sKey = sha1Base64; @@ -287,6 +393,10 @@ void WebSocketsServer::handleHeader(WSclient_t * client) { // header end client->tcp.write("\r\n"); + if(_cbEvent) { + _cbEvent(client->num, WStype_CONNECTED, NULL, 0); + } + } else { DEBUG_WEBSOCKETS("[WS-Server][%d][handleHeader] no Websocket connection close.\n", client->num); client->tcp.write("HTTP/1.1 400 Bad Request\r\n" @@ -302,5 +412,3 @@ void WebSocketsServer::handleHeader(WSclient_t * client) { } } - - diff --git a/src/WebSocketsServer.h b/src/WebSocketsServer.h index 43a8289..b4dd226 100644 --- a/src/WebSocketsServer.h +++ b/src/WebSocketsServer.h @@ -42,14 +42,38 @@ #define WEBSOCKETS_SERVER_CLIENT_MAX (5) +typedef enum { + WStype_ERROR, + WStype_DISCONNECTED, + WStype_CONNECTED, + WStype_TEXT, + WStype_BIN +} WStype_t; + + class WebSocketsServer: private WebSockets { public: + + typedef void (*WebSocketServerEvent)(uint8_t num, WStype_t type, uint8_t * payload, size_t length); + WebSocketsServer(uint16_t port); ~WebSocketsServer(void); void begin(void); void loop(void); + void onEvent(WebSocketServerEvent cbEvent); + + + void sendTXT(uint8_t num, uint8_t * payload, size_t length); + void broadcastTXT(uint8_t * payload, size_t length); + + void sendTXT(uint8_t num, String payload); + void broadcastTXT(String payload); + + void sendBIN(uint8_t num, uint8_t * payload, size_t length); + void broadcastBIN(uint8_t * payload, size_t length); + private: uint16_t _port; @@ -65,6 +89,10 @@ private: WSclient_t _clients[WEBSOCKETS_SERVER_CLIENT_MAX]; + WebSocketServerEvent _cbEvent; + + void messageRecived(WSclient_t * client, WSopcode_t opcode, uint8_t * payload, size_t length); + void clientConnected(WSclient_t * client); void clientDisconnect(WSclient_t * client); bool clientIsConnected(WSclient_t * client); diff --git a/tests/webSocket.html b/tests/webSocket.html index baeaad0..f05d770 100644 --- a/tests/webSocket.html +++ b/tests/webSocket.html @@ -17,6 +17,10 @@ connection.onmessage = function (e) { console.log('Server: ', e.data); }; +setInterval(function() { + connection.send('Message from Browser to ESP8266 yay its Working!!'); +}, 10000); +