| 
         @@ -1,24 +1,35 @@ 
     | 
|
| 1 | 
         
             
            #include "ledblinker.h"
         
     | 
| 2 | 
         
            -
            #include " 
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 3 | 
         | 
| 4 | 
         
             
            // Network configuration
         
     | 
| 5 | 
         
             
            const char* SSID = "Pomona";
         
     | 
| 6 | 
         
            -
             
     | 
| 7 | 
         
            -
             
     | 
| 
         | 
|
| 8 | 
         | 
| 9 | 
         
             
            // LED Blinking configuration
         
     | 
| 10 | 
         
             
            const unsigned long BLINK_INTERVAL = 250;
         
     | 
| 11 | 
         
            -
             
     | 
| 12 | 
         
            -
            // WSHeartbeater wsHeartbeater(PORT, HEARTBEAT_INTERVAL);
         
     | 
| 13 | 
         
             
            LedBlinker ledBlinker(BLINK_INTERVAL);
         
     | 
| 14 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 15 | 
         
             
            void setup() {
         
     | 
| 16 | 
         
             
              Serial.begin(115200);
         
     | 
| 17 | 
         
            -
               
     | 
| 18 | 
         
             
              ledBlinker.setup();
         
     | 
| 19 | 
         
             
            }
         
     | 
| 20 | 
         | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 21 | 
         
             
            void loop() {
         
     | 
| 22 | 
         
            -
               
     | 
| 23 | 
         
            -
              ledBlinker.loopStep( 
     | 
| 24 | 
         
             
            }
         
     | 
| 
         | 
|
| 1 | 
         
             
            #include "ledblinker.h"
         
     | 
| 2 | 
         
            +
            #include "wscommunicator.h"
         
     | 
| 3 | 
         
            +
             
     | 
| 4 | 
         
            +
            //
         
     | 
| 5 | 
         
            +
            // Global state
         
     | 
| 6 | 
         
            +
            //
         
     | 
| 7 | 
         | 
| 8 | 
         
             
            // Network configuration
         
     | 
| 9 | 
         
             
            const char* SSID = "Pomona";
         
     | 
| 10 | 
         
            +
            const uint16_t PORT = 8181;
         
     | 
| 11 | 
         
            +
            const unsigned long HEARTBEAT_INTERVAL = 1000;
         
     | 
| 12 | 
         
            +
            WSCommunicator wsCommunicator(SSID, PORT, HEARTBEAT_INTERVAL);
         
     | 
| 13 | 
         | 
| 14 | 
         
             
            // LED Blinking configuration
         
     | 
| 15 | 
         
             
            const unsigned long BLINK_INTERVAL = 250;
         
     | 
| 
         | 
|
| 
         | 
|
| 16 | 
         
             
            LedBlinker ledBlinker(BLINK_INTERVAL);
         
     | 
| 17 | 
         | 
| 18 | 
         
            +
            //
         
     | 
| 19 | 
         
            +
            // Setup
         
     | 
| 20 | 
         
            +
            //
         
     | 
| 21 | 
         
            +
             
     | 
| 22 | 
         
             
            void setup() {
         
     | 
| 23 | 
         
             
              Serial.begin(115200);
         
     | 
| 24 | 
         
            +
              wsCommunicator.setup();
         
     | 
| 25 | 
         
             
              ledBlinker.setup();
         
     | 
| 26 | 
         
             
            }
         
     | 
| 27 | 
         | 
| 28 | 
         
            +
            //
         
     | 
| 29 | 
         
            +
            // Loop
         
     | 
| 30 | 
         
            +
            //
         
     | 
| 31 | 
         
            +
             
     | 
| 32 | 
         
             
            void loop() {
         
     | 
| 33 | 
         
            +
              wsCommunicator.loopStep();
         
     | 
| 34 | 
         
            +
              ledBlinker.loopStep(wsCommunicator.isEnabled());
         
     | 
| 35 | 
         
             
            }
         
     | 
| 
         @@ -1,31 +1,51 @@ 
     | 
|
| 1 | 
         
             
            #include <WebSocketsServer.h>
         
     | 
| 2 | 
         
             
            #include <WiFi.h>
         
     | 
| 3 | 
         | 
| 4 | 
         
            -
             
     | 
| 5 | 
         
            -
             
     | 
| 6 | 
         
            -
             
     | 
| 7 | 
         
            -
            } WsState;
         
     | 
| 8 | 
         | 
| 9 | 
         
            -
             
     | 
| 10 | 
         | 
| 11 | 
         
            -
             
     | 
| 12 | 
         
            -
              
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 13 | 
         
             
              unsigned long heartbeatInterval;
         
     | 
| 14 | 
         
             
              unsigned long heartbeatLastTime;
         
     | 
| 
         | 
|
| 
         | 
|
| 15 | 
         
             
              uint16_t port;
         
     | 
| 16 | 
         
             
              WebSocketsServer webSocket;
         
     | 
| 17 | 
         
            -
               
     | 
| 18 | 
         | 
| 19 | 
         
            -
             
     | 
| 20 | 
         
            -
             
     | 
| 21 | 
         
            -
             
     | 
| 22 | 
         
            -
             
     | 
| 23 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 24 | 
         | 
| 25 | 
         
            -
              void setup( 
     | 
| 
         | 
|
| 26 | 
         
             
                // Setup WiFi
         
     | 
| 
         | 
|
| 
         | 
|
| 27 | 
         
             
                WiFi.begin(ssid);
         
     | 
| 28 | 
         
            -
                Serial.printf("\n[SETUP] Connecting to '%s'... 
     | 
| 29 | 
         
             
                while (WiFi.status() != WL_CONNECTED) {
         
     | 
| 30 | 
         
             
                  delay(500);
         
     | 
| 31 | 
         
             
                  Serial.print(".");
         
     | 
| 
         @@ -33,12 +53,19 @@ class WSHeartbeater { 
     | 
|
| 33 | 
         
             
                Serial.print("done\n");
         
     | 
| 34 | 
         | 
| 35 | 
         
             
                IPAddress ip = WiFi.localIP();
         
     | 
| 36 | 
         
            -
                Serial.printf("[SETUP] IP  
     | 
| 37 | 
         | 
| 
         | 
|
| 38 | 
         
             
                // Start WebSockets server
         
     | 
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 
         | 
|
| 39 | 
         
             
                webSocket.begin();
         
     | 
| 40 | 
         
            -
                webSocket.onEvent( 
     | 
| 41 | 
         
            -
                Serial.printf("[SETUP]  
     | 
| 42 | 
         
             
              }
         
     | 
| 43 | 
         | 
| 44 | 
         
             
              void loopStep() {
         
     | 
| 
         @@ -48,54 +75,56 @@ class WSHeartbeater { 
     | 
|
| 48 | 
         
             
                // Update for handling heartbeat
         
     | 
| 49 | 
         
             
                unsigned long now = millis();
         
     | 
| 50 | 
         
             
                if (now - heartbeatLastTime > heartbeatInterval) {
         
     | 
| 51 | 
         
            -
                  if ( 
     | 
| 52 | 
         
            -
                     
     | 
| 53 | 
         
            -
                    Serial.println("[HEARTBEAT] Heartbeat timeout");
         
     | 
| 54 | 
         
             
                  }
         
     | 
| 55 | 
         
             
                }
         
     | 
| 56 | 
         
             
              }
         
     | 
| 
         | 
|
| 
         | 
|
| 57 | 
         
             
            };
         
     | 
| 58 | 
         | 
| 59 | 
         
            -
             
     | 
| 60 | 
         
            -
             
     | 
| 61 | 
         
            -
             
     | 
| 62 | 
         | 
| 63 | 
         
            -
            void  
     | 
| 64 | 
         
             
              IPAddress ip;
         
     | 
| 65 | 
         | 
| 66 | 
         
             
              switch (type) {
         
     | 
| 67 | 
         
             
                case WStype_DISCONNECTED:
         
     | 
| 68 | 
         
            -
                   
     | 
| 69 | 
         
            -
                  Serial.printf("[ 
     | 
| 70 | 
         
             
                  break;
         
     | 
| 71 | 
         | 
| 72 | 
         
             
                case WStype_CONNECTED:
         
     | 
| 73 | 
         
            -
                  ip =  
     | 
| 74 | 
         
            -
                  Serial.printf("[ 
     | 
| 75 | 
         
            -
                   
     | 
| 76 | 
         
             
                  break;
         
     | 
| 77 | 
         | 
| 78 | 
         
             
                case WStype_TEXT:
         
     | 
| 79 | 
         
            -
                  if ( 
     | 
| 80 | 
         
            -
                     
     | 
| 81 | 
         
            -
                     
     | 
| 82 | 
         
             
                  } else {
         
     | 
| 83 | 
         
            -
                    Serial.printf("[ 
     | 
| 84 | 
         
             
                  }
         
     | 
| 85 | 
         
             
                  break;
         
     | 
| 86 | 
         | 
| 87 | 
         
             
                case WStype_BIN:
         
     | 
| 88 | 
         
            -
                  Serial.printf("[ 
     | 
| 89 | 
         
            -
                  //   hexdump(payload, length);
         
     | 
| 90 | 
         
            -
                  // webSocket.sendBIN(num, payload, length);
         
     | 
| 91 | 
         
             
                  break;
         
     | 
| 92 | 
         | 
| 93 | 
         
             
                case WStype_ERROR:
         
     | 
| 
         | 
|
| 94 | 
         
             
                case WStype_FRAGMENT_TEXT_START:
         
     | 
| 95 | 
         
             
                case WStype_FRAGMENT_BIN_START:
         
     | 
| 96 | 
         
            -
                case WStype_FRAGMENT:
         
     | 
| 97 | 
         
             
                case WStype_FRAGMENT_FIN:
         
     | 
| 98 | 
         
            -
             
     | 
| 
         | 
|
| 
         | 
|
| 99 | 
         
             
                  break;
         
     | 
| 100 | 
         
             
              }
         
     | 
| 101 | 
         
             
            }
         
     | 
| 
         | 
|
| 1 | 
         
             
            #include <WebSocketsServer.h>
         
     | 
| 2 | 
         
             
            #include <WiFi.h>
         
     | 
| 3 | 
         | 
| 4 | 
         
            +
            //
         
     | 
| 5 | 
         
            +
            // State of WebSocket heartbeats
         
     | 
| 6 | 
         
            +
            //
         
     | 
| 
         | 
|
| 7 | 
         | 
| 8 | 
         
            +
            typedef enum { HEARTBEAT_ENABLED, HEARTBEAT_DISABLED } WsHeartbeatState;
         
     | 
| 9 | 
         | 
| 10 | 
         
            +
            //
         
     | 
| 11 | 
         
            +
            // Forward declaration of the WebSocket event callback
         
     | 
| 12 | 
         
            +
            //
         
     | 
| 13 | 
         
            +
             
     | 
| 14 | 
         
            +
            class WSCommunicator;
         
     | 
| 15 | 
         
            +
            void wsEventCB(WSCommunicator& wsComm, uint8_t num, WStype_t type, uint8_t* payload, size_t length);
         
     | 
| 16 | 
         
            +
             
     | 
| 17 | 
         
            +
            //
         
     | 
| 18 | 
         
            +
            // WebSocket server configuration, communication, and state
         
     | 
| 19 | 
         
            +
            //
         
     | 
| 20 | 
         
            +
             
     | 
| 21 | 
         
            +
            class WSCommunicator {
         
     | 
| 22 | 
         
            +
              friend void wsEventCB(WSCommunicator& wsComm, uint8_t num, WStype_t type, uint8_t* payload, size_t length);
         
     | 
| 23 | 
         
            +
             
     | 
| 24 | 
         
            +
             private:
         
     | 
| 25 | 
         
             
              unsigned long heartbeatInterval;
         
     | 
| 26 | 
         
             
              unsigned long heartbeatLastTime;
         
     | 
| 27 | 
         
            +
             
     | 
| 28 | 
         
            +
              const char* ssid;
         
     | 
| 29 | 
         
             
              uint16_t port;
         
     | 
| 30 | 
         
             
              WebSocketsServer webSocket;
         
     | 
| 31 | 
         
            +
              WsHeartbeatState hbState;
         
     | 
| 32 | 
         | 
| 33 | 
         
            +
             public:
         
     | 
| 34 | 
         
            +
              WSCommunicator(const char* ssid, uint16_t port, unsigned long interval)
         
     | 
| 35 | 
         
            +
                  : heartbeatInterval(interval)
         
     | 
| 36 | 
         
            +
                  , heartbeatLastTime(0)
         
     | 
| 37 | 
         
            +
                  , ssid(ssid)
         
     | 
| 38 | 
         
            +
                  , port(port)
         
     | 
| 39 | 
         
            +
                  , webSocket(port)
         
     | 
| 40 | 
         
            +
                  , hbState(HEARTBEAT_DISABLED) {}
         
     | 
| 41 | 
         | 
| 42 | 
         
            +
              void setup() {
         
     | 
| 43 | 
         
            +
                //
         
     | 
| 44 | 
         
             
                // Setup WiFi
         
     | 
| 45 | 
         
            +
                //
         
     | 
| 46 | 
         
            +
             
     | 
| 47 | 
         
             
                WiFi.begin(ssid);
         
     | 
| 48 | 
         
            +
                Serial.printf("\n[COMMUNICATOR::SETUP] Connecting to '%s'...", ssid);
         
     | 
| 49 | 
         
             
                while (WiFi.status() != WL_CONNECTED) {
         
     | 
| 50 | 
         
             
                  delay(500);
         
     | 
| 51 | 
         
             
                  Serial.print(".");
         
     | 
| 
         | 
|
| 53 | 
         
             
                Serial.print("done\n");
         
     | 
| 54 | 
         | 
| 55 | 
         
             
                IPAddress ip = WiFi.localIP();
         
     | 
| 56 | 
         
            +
                Serial.printf("[COMMUNICATOR::SETUP] IP address: %d.%d.%d.%d\n", ip[0], ip[1], ip[2], ip[3]);
         
     | 
| 57 | 
         | 
| 58 | 
         
            +
                //
         
     | 
| 59 | 
         
             
                // Start WebSockets server
         
     | 
| 60 | 
         
            +
                //
         
     | 
| 61 | 
         
            +
             
     | 
| 62 | 
         
            +
                auto wrappedCB = [this](uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
         
     | 
| 63 | 
         
            +
                  wsEventCB(*this, num, type, payload, length);
         
     | 
| 64 | 
         
            +
                };
         
     | 
| 65 | 
         
            +
             
     | 
| 66 | 
         
             
                webSocket.begin();
         
     | 
| 67 | 
         
            +
                webSocket.onEvent(wrappedCB);
         
     | 
| 68 | 
         
            +
                Serial.printf("[COMMUNICATOR::SETUP] WebSocket server at ws://%d.%d.%d.%d:%d\n", ip[0], ip[1], ip[2], ip[3], port);
         
     | 
| 69 | 
         
             
              }
         
     | 
| 70 | 
         | 
| 71 | 
         
             
              void loopStep() {
         
     | 
| 
         | 
|
| 75 | 
         
             
                // Update for handling heartbeat
         
     | 
| 76 | 
         
             
                unsigned long now = millis();
         
     | 
| 77 | 
         
             
                if (now - heartbeatLastTime > heartbeatInterval) {
         
     | 
| 78 | 
         
            +
                  if (hbState == HEARTBEAT_ENABLED) {
         
     | 
| 79 | 
         
            +
                    hbState = HEARTBEAT_DISABLED;
         
     | 
| 80 | 
         
            +
                    Serial.println("[COMMUNICATOR::HEARTBEAT] Heartbeat timeout");
         
     | 
| 81 | 
         
             
                  }
         
     | 
| 82 | 
         
             
                }
         
     | 
| 83 | 
         
             
              }
         
     | 
| 84 | 
         
            +
             
     | 
| 85 | 
         
            +
              bool isEnabled() { return hbState == HEARTBEAT_ENABLED; }
         
     | 
| 86 | 
         
             
            };
         
     | 
| 87 | 
         | 
| 88 | 
         
            +
            //
         
     | 
| 89 | 
         
            +
            // WebSocket event callback
         
     | 
| 90 | 
         
            +
            //
         
     | 
| 91 | 
         | 
| 92 | 
         
            +
            void wsEventCB(WSCommunicator& wsComm, uint8_t num, WStype_t type, uint8_t* payload, size_t length) {
         
     | 
| 93 | 
         
             
              IPAddress ip;
         
     | 
| 94 | 
         | 
| 95 | 
         
             
              switch (type) {
         
     | 
| 96 | 
         
             
                case WStype_DISCONNECTED:
         
     | 
| 97 | 
         
            +
                  wsComm.hbState = HEARTBEAT_DISABLED;
         
     | 
| 98 | 
         
            +
                  Serial.printf("[COMMUNICATOR::%u] Disconnected!\n", num);
         
     | 
| 99 | 
         
             
                  break;
         
     | 
| 100 | 
         | 
| 101 | 
         
             
                case WStype_CONNECTED:
         
     | 
| 102 | 
         
            +
                  ip = wsComm.webSocket.remoteIP(num);
         
     | 
| 103 | 
         
            +
                  Serial.printf("[COMMUNICATOR::%u] Client connected: %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]);
         
     | 
| 104 | 
         
            +
                  wsComm.webSocket.sendTXT(num, "Connected");
         
     | 
| 105 | 
         
             
                  break;
         
     | 
| 106 | 
         | 
| 107 | 
         
             
                case WStype_TEXT:
         
     | 
| 108 | 
         
            +
                  if (strncmp((char*)payload, "heartbeat", length) == 0) {
         
     | 
| 109 | 
         
            +
                    wsComm.hbState = HEARTBEAT_ENABLED;
         
     | 
| 110 | 
         
            +
                    wsComm.heartbeatLastTime = millis();
         
     | 
| 111 | 
         
             
                  } else {
         
     | 
| 112 | 
         
            +
                    Serial.printf("[COMMUNICATOR::%u] Received: %s\n", num, payload);
         
     | 
| 113 | 
         
             
                  }
         
     | 
| 114 | 
         
             
                  break;
         
     | 
| 115 | 
         | 
| 116 | 
         
             
                case WStype_BIN:
         
     | 
| 117 | 
         
            +
                  Serial.printf("[COMMUNICATOR::%u] Received %u bytes of data\n", num, length);
         
     | 
| 
         | 
|
| 
         | 
|
| 118 | 
         
             
                  break;
         
     | 
| 119 | 
         | 
| 120 | 
         
             
                case WStype_ERROR:
         
     | 
| 121 | 
         
            +
                case WStype_FRAGMENT:
         
     | 
| 122 | 
         
             
                case WStype_FRAGMENT_TEXT_START:
         
     | 
| 123 | 
         
             
                case WStype_FRAGMENT_BIN_START:
         
     | 
| 
         | 
|
| 124 | 
         
             
                case WStype_FRAGMENT_FIN:
         
     | 
| 125 | 
         
            +
                case WStype_PING:
         
     | 
| 126 | 
         
            +
                case WStype_PONG:
         
     | 
| 127 | 
         
            +
                  Serial.printf("[COMMUNICATOR::%u] Received unhandled event: %d\n", num, type);
         
     | 
| 128 | 
         
             
                  break;
         
     | 
| 129 | 
         
             
              }
         
     | 
| 130 | 
         
             
            }
         
     |