diff --git a/include/controller.h b/include/controller.h index 62f2a06..8b30979 100644 --- a/include/controller.h +++ b/include/controller.h @@ -31,9 +31,6 @@ struct EthernetConfiguration { // The IP address of the DNS server, if DHCP fails uint8_t manualDnsAddress[4]; - uint32_t socketHeartbeatIntervalMs; - uint32_t socketHeartbeatTimeoutMs; - uint8_t socketHeartbeatFailureReconnectCount; }; struct KeyConfiguration { @@ -88,23 +85,11 @@ private: currentChallengeExpiry = 0; } - /** - * @brief Send an error Response over the web socket. - * - * @param result The error result to send - * @param discardMessage Indicate if the stored message should be cleared. - * - * Note: Only clear the message if no other operation is in progress. - */ - void sendErrorResponseToServer(MessageResult result, bool discardMessage = true); - - void ensureWebSocketConnection(); + // MARK: Local client callbacks void handleLocalMessage(AsyncWebServerRequest *request); - bool convertHexMessageToBinary(const char* str); - - void handleServerMessage(uint8_t* payload, size_t length); + // MARK: Socket Callbacks /** * @brief Callback to send an error back to the server via the web socket. @@ -115,13 +100,60 @@ private: */ void sendServerError(MessageResult event); - void processMessage(SignedMessage* message); - MessageResult verifyAndProcessReceivedMessage(SignedMessage* message); + void handleServerMessage(uint8_t* payload, size_t length); + // MARK: Message processing + + /** + * @brief Process a received message (local or socket). + * + * @param message The message to process. + * + * Note: Prepares the response in the outgoing message buffer. + */ + void processMessage(SignedMessage* message); + + /** + * @brief Prepare a server challenge for a local or socket message. + * + * @param message The message to respond to + * + * Note: Prepares the response in the outgoing message buffer. + */ + void prepareChallenge(Message* message); + + /** + * @brief Complete an unlock request for a local or socket message. + * + * @param message The message to respond to + * + * Note: Prepares the response in the outgoing message buffer. + */ + void completeUnlockRequest(Message* message); + + // MARK: Responses + + /** + * @brief Prepare the outgoing message buffer for both socket and local responses. + * + * @param event The resulting state to transmit + * @param message An optional message to echo + */ void prepareResponseBuffer(MessageResult event, Message* message = NULL); + + /** + * @brief Send the prepared outgoing message to a locally connected client + * + * @param request The original request of the client + */ void sendPreparedLocalResponse(AsyncWebServerRequest *request); + + /** + * @brief Send the prepared outgoing message to the server + */ void sendPreparedResponseToServer(); - void prepareChallenge(Message* message); - void completeUnlockRequest(Message* message); + // MARK: Helper + + bool convertHexMessageToBinary(const char* str); }; \ No newline at end of file diff --git a/include/server.h b/include/server.h index a2dc177..35b2223 100644 --- a/include/server.h +++ b/include/server.h @@ -28,6 +28,10 @@ struct ServerConfiguration { uint32_t reconnectTime; + uint32_t socketHeartbeatIntervalMs; + uint32_t socketHeartbeatTimeoutMs; + uint8_t socketHeartbeatFailureReconnectCount; + }; class ServerConnectionCallbacks { @@ -51,11 +55,13 @@ public: */ void configure(ServerConfiguration configuration, ServerConnectionCallbacks* callbacks); - void connect(); - - void disconnect(); - - void loop(); + /** + * @brief Call this function regularly to handle socket operations. + * + * Connecting and disconnecting is done automatically. + * + */ + void loop(uint32_t millis); /** * @brief Send a response message over the socket @@ -65,11 +71,24 @@ public: */ void sendResponse(uint8_t* buffer, uint16_t length); - bool isSocketConnected() { +private: + + uint32_t currentTime; + + bool socketIsConnected() { return webSocket.isConnected(); } -private: + void connect(); + + void disconnect(); + + bool shouldReconnect = true; + uint32_t nextReconnectAttemptMs = 0; + + void didDisconnect(); + void didConnect(); + ServerConfiguration configuration; diff --git a/platformio.ini b/platformio.ini index e2fa657..74f509b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -20,3 +20,4 @@ lib_deps = https://github.com/christophhagen/arduinoWebSockets#master monitor_speed = 115200 +build_flags= -D WEBSOCKETS_NETWORK_TYPE=NETWORK_W5100 \ No newline at end of file diff --git a/src/controller.cpp b/src/controller.cpp index 27f76d5..4c92609 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -62,10 +62,8 @@ void SesameController::configure(ServoConfiguration servoConfig, ServerConfigura void SesameController::loop(uint32_t millis) { currentTime = millis; - server.loop(); + server.loop(millis); servo.loop(millis); - - ensureWebSocketConnection(); } // MARK: Local @@ -199,20 +197,6 @@ void SesameController::prepareResponseBuffer(MessageResult result, Message* mess } } -void SesameController::ensureWebSocketConnection() { - /* - if (isReconnecting && WiFi.status() == WL_CONNECTED) { - isReconnecting = false; - Serial.print("WiFi IP address: "); - Serial.println(WiFi.localIP()); - server.connect(); - timeCheck.startNTP(); - timeCheck.printLocalTime(); - localWebServer.begin(); - } - */ -} - // MARK: Helper /** diff --git a/src/main.cpp b/src/main.cpp index e07658a..4b10374 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,15 @@ * * The code for a simple door unlock mechanism where a servo pushes on an existing * physical button. + * + * On compile error: + * + * In + * + * change: + * virtual void begin(uint16_t port=0) =0; + * to: + * virtual void begin() =0; */ #include @@ -35,6 +44,9 @@ void setup() { .path = serverPath, .key = serverAccessKey, .reconnectTime = 5000, + .socketHeartbeatIntervalMs = socketHeartbeatIntervalMs, + .socketHeartbeatTimeoutMs = socketHeartbeatTimeoutMs, + .socketHeartbeatFailureReconnectCount = socketHeartbeatFailureReconnectCount, }; EthernetConfiguration ethernetConfig { @@ -47,9 +59,6 @@ void setup() { .dhcpLeaseResponseTimeoutMs = dhcpLeaseResponseTimeoutMs, .manualIp = manualIpAddress, .manualDnsAddress = manualDnsServerAddress, - .socketHeartbeatIntervalMs = socketHeartbeatIntervalMs, - .socketHeartbeatTimeoutMs = socketHeartbeatTimeoutMs, - .socketHeartbeatFailureReconnectCount = socketHeartbeatFailureReconnectCount, }; KeyConfiguration keyConfig { diff --git a/src/server.cpp b/src/server.cpp index 882a53a..499d823 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -20,7 +20,8 @@ void ServerConnection::connect() { return; } - webSocket.beginSSL(configuration.url, configuration.port, configuration.path); + webSocket.begin(configuration.url, configuration.port, configuration.path); + webSocket.setAuthorization(configuration.key); std::function f = [this](WStype_t type, uint8_t *payload, size_t length) { this->webSocketEventHandler(type, payload, length); @@ -29,23 +30,41 @@ void ServerConnection::connect() { webSocket.setReconnectInterval(configuration.reconnectTime); } +void ServerConnection::didDisconnect() { + if (shouldReconnect) { + return; // Disconnect already registered. + } + Serial.println("[INFO] Socket disconnected"); + nextReconnectAttemptMs = currentTime + configuration.socketHeartbeatIntervalMs; + shouldReconnect = true; +} + +void ServerConnection::didConnect() { + Serial.println("[INFO] Socket connected"); + webSocket.sendTXT(configuration.key); + webSocket.enableHeartbeat(configuration.socketHeartbeatIntervalMs, configuration.socketHeartbeatTimeoutMs, configuration.socketHeartbeatFailureReconnectCount); +} + void ServerConnection::disconnect() { webSocket.disconnect(); } -void ServerConnection::loop() { +void ServerConnection::loop(uint32_t millis) { + currentTime = millis; webSocket.loop(); + if (shouldReconnect) { + shouldReconnect = false; + connect(); + } } void ServerConnection::webSocketEventHandler(WStype_t type, uint8_t * payload, size_t length) { switch(type) { case WStype_DISCONNECTED: - Serial.println("[INFO] Socket disconnected."); + didDisconnect(); break; case WStype_CONNECTED: - webSocket.sendTXT(configuration.key); - Serial.printf("[INFO] Socket connected to url: %s\n", payload); - webSocket.enableHeartbeat(pingInterval, pongTimeout, disconnectTimeoutCount); + didConnect(); break; case WStype_TEXT: controller->sendServerError(MessageResult::TextReceived); @@ -67,5 +86,7 @@ switch(type) { } void ServerConnection::sendResponse(uint8_t* buffer, uint16_t length) { - webSocket.sendBIN(buffer, length); + if (socketIsConnected()) { + webSocket.sendBIN(buffer, length); + } } \ No newline at end of file