Finish socket operations

This commit is contained in:
Christoph Hagen 2023-12-05 21:31:11 +01:00
parent 9b49c3565d
commit 4c23565b9c
6 changed files with 121 additions and 55 deletions

View File

@ -31,9 +31,6 @@ struct EthernetConfiguration {
// The IP address of the DNS server, if DHCP fails // The IP address of the DNS server, if DHCP fails
uint8_t manualDnsAddress[4]; uint8_t manualDnsAddress[4];
uint32_t socketHeartbeatIntervalMs;
uint32_t socketHeartbeatTimeoutMs;
uint8_t socketHeartbeatFailureReconnectCount;
}; };
struct KeyConfiguration { struct KeyConfiguration {
@ -88,23 +85,11 @@ private:
currentChallengeExpiry = 0; currentChallengeExpiry = 0;
} }
/** // MARK: Local client callbacks
* @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();
void handleLocalMessage(AsyncWebServerRequest *request); void handleLocalMessage(AsyncWebServerRequest *request);
bool convertHexMessageToBinary(const char* str); // MARK: Socket Callbacks
void handleServerMessage(uint8_t* payload, size_t length);
/** /**
* @brief Callback to send an error back to the server via the web socket. * @brief Callback to send an error back to the server via the web socket.
@ -115,13 +100,60 @@ private:
*/ */
void sendServerError(MessageResult event); void sendServerError(MessageResult event);
void processMessage(SignedMessage* message); void handleServerMessage(uint8_t* payload, size_t length);
MessageResult verifyAndProcessReceivedMessage(SignedMessage* message);
// 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); 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); void sendPreparedLocalResponse(AsyncWebServerRequest *request);
/**
* @brief Send the prepared outgoing message to the server
*/
void sendPreparedResponseToServer(); void sendPreparedResponseToServer();
void prepareChallenge(Message* message); // MARK: Helper
void completeUnlockRequest(Message* message);
bool convertHexMessageToBinary(const char* str);
}; };

View File

@ -28,6 +28,10 @@ struct ServerConfiguration {
uint32_t reconnectTime; uint32_t reconnectTime;
uint32_t socketHeartbeatIntervalMs;
uint32_t socketHeartbeatTimeoutMs;
uint8_t socketHeartbeatFailureReconnectCount;
}; };
class ServerConnectionCallbacks { class ServerConnectionCallbacks {
@ -51,11 +55,13 @@ public:
*/ */
void configure(ServerConfiguration configuration, ServerConnectionCallbacks* callbacks); void configure(ServerConfiguration configuration, ServerConnectionCallbacks* callbacks);
void connect(); /**
* @brief Call this function regularly to handle socket operations.
void disconnect(); *
* Connecting and disconnecting is done automatically.
void loop(); *
*/
void loop(uint32_t millis);
/** /**
* @brief Send a response message over the socket * @brief Send a response message over the socket
@ -65,11 +71,24 @@ public:
*/ */
void sendResponse(uint8_t* buffer, uint16_t length); void sendResponse(uint8_t* buffer, uint16_t length);
bool isSocketConnected() { private:
uint32_t currentTime;
bool socketIsConnected() {
return webSocket.isConnected(); return webSocket.isConnected();
} }
private: void connect();
void disconnect();
bool shouldReconnect = true;
uint32_t nextReconnectAttemptMs = 0;
void didDisconnect();
void didConnect();
ServerConfiguration configuration; ServerConfiguration configuration;

View File

@ -20,3 +20,4 @@ lib_deps =
https://github.com/christophhagen/arduinoWebSockets#master https://github.com/christophhagen/arduinoWebSockets#master
monitor_speed = 115200 monitor_speed = 115200
build_flags= -D WEBSOCKETS_NETWORK_TYPE=NETWORK_W5100

View File

@ -62,10 +62,8 @@ void SesameController::configure(ServoConfiguration servoConfig, ServerConfigura
void SesameController::loop(uint32_t millis) { void SesameController::loop(uint32_t millis) {
currentTime = millis; currentTime = millis;
server.loop(); server.loop(millis);
servo.loop(millis); servo.loop(millis);
ensureWebSocketConnection();
} }
// MARK: Local // 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 // MARK: Helper
/** /**

View File

@ -4,6 +4,15 @@
* *
* The code for a simple door unlock mechanism where a servo pushes on an existing * The code for a simple door unlock mechanism where a servo pushes on an existing
* physical button. * physical button.
*
* On compile error:
*
* In <Server.h>
*
* change:
* virtual void begin(uint16_t port=0) =0;
* to:
* virtual void begin() =0;
*/ */
#include <Arduino.h> #include <Arduino.h>
@ -35,6 +44,9 @@ void setup() {
.path = serverPath, .path = serverPath,
.key = serverAccessKey, .key = serverAccessKey,
.reconnectTime = 5000, .reconnectTime = 5000,
.socketHeartbeatIntervalMs = socketHeartbeatIntervalMs,
.socketHeartbeatTimeoutMs = socketHeartbeatTimeoutMs,
.socketHeartbeatFailureReconnectCount = socketHeartbeatFailureReconnectCount,
}; };
EthernetConfiguration ethernetConfig { EthernetConfiguration ethernetConfig {
@ -47,9 +59,6 @@ void setup() {
.dhcpLeaseResponseTimeoutMs = dhcpLeaseResponseTimeoutMs, .dhcpLeaseResponseTimeoutMs = dhcpLeaseResponseTimeoutMs,
.manualIp = manualIpAddress, .manualIp = manualIpAddress,
.manualDnsAddress = manualDnsServerAddress, .manualDnsAddress = manualDnsServerAddress,
.socketHeartbeatIntervalMs = socketHeartbeatIntervalMs,
.socketHeartbeatTimeoutMs = socketHeartbeatTimeoutMs,
.socketHeartbeatFailureReconnectCount = socketHeartbeatFailureReconnectCount,
}; };
KeyConfiguration keyConfig { KeyConfiguration keyConfig {

View File

@ -20,7 +20,8 @@ void ServerConnection::connect() {
return; return;
} }
webSocket.beginSSL(configuration.url, configuration.port, configuration.path); webSocket.begin(configuration.url, configuration.port, configuration.path);
webSocket.setAuthorization(configuration.key);
std::function<void(WStype_t, uint8_t *, size_t)> f = [this](WStype_t type, uint8_t *payload, size_t length) { std::function<void(WStype_t, uint8_t *, size_t)> f = [this](WStype_t type, uint8_t *payload, size_t length) {
this->webSocketEventHandler(type, payload, length); this->webSocketEventHandler(type, payload, length);
@ -29,23 +30,41 @@ void ServerConnection::connect() {
webSocket.setReconnectInterval(configuration.reconnectTime); 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() { void ServerConnection::disconnect() {
webSocket.disconnect(); webSocket.disconnect();
} }
void ServerConnection::loop() { void ServerConnection::loop(uint32_t millis) {
currentTime = millis;
webSocket.loop(); webSocket.loop();
if (shouldReconnect) {
shouldReconnect = false;
connect();
}
} }
void ServerConnection::webSocketEventHandler(WStype_t type, uint8_t * payload, size_t length) { void ServerConnection::webSocketEventHandler(WStype_t type, uint8_t * payload, size_t length) {
switch(type) { switch(type) {
case WStype_DISCONNECTED: case WStype_DISCONNECTED:
Serial.println("[INFO] Socket disconnected."); didDisconnect();
break; break;
case WStype_CONNECTED: case WStype_CONNECTED:
webSocket.sendTXT(configuration.key); didConnect();
Serial.printf("[INFO] Socket connected to url: %s\n", payload);
webSocket.enableHeartbeat(pingInterval, pongTimeout, disconnectTimeoutCount);
break; break;
case WStype_TEXT: case WStype_TEXT:
controller->sendServerError(MessageResult::TextReceived); controller->sendServerError(MessageResult::TextReceived);
@ -67,5 +86,7 @@ switch(type) {
} }
void ServerConnection::sendResponse(uint8_t* buffer, uint16_t length) { void ServerConnection::sendResponse(uint8_t* buffer, uint16_t length) {
if (socketIsConnected()) {
webSocket.sendBIN(buffer, length); webSocket.sendBIN(buffer, length);
}
} }