From 1504ce6b0cd14c3293b19d89c071c603ae1aeb2e Mon Sep 17 00:00:00 2001 From: Christoph Hagen Date: Sat, 20 Apr 2024 17:47:44 +0200 Subject: [PATCH] Use local UDP messages instead of web server --- .../configurations/EthernetConfiguration.h | 2 + include/controller.h | 30 ++++++-- platformio.ini | 2 - src/controller.cpp | 73 +++++++++++-------- src/main.cpp | 3 +- 5 files changed, 70 insertions(+), 40 deletions(-) diff --git a/include/configurations/EthernetConfiguration.h b/include/configurations/EthernetConfiguration.h index 7264a35..7dea022 100644 --- a/include/configurations/EthernetConfiguration.h +++ b/include/configurations/EthernetConfiguration.h @@ -28,4 +28,6 @@ struct EthernetConfiguration { // The IP address of the DNS server, if DHCP fails uint8_t manualDnsAddress[4]; + // The port for the incoming UDP connection + uint16_t udpPort; }; \ No newline at end of file diff --git a/include/controller.h b/include/controller.h index 6407a3b..342a648 100644 --- a/include/controller.h +++ b/include/controller.h @@ -3,14 +3,13 @@ #include "server.h" #include "servo.h" #include "message.h" -#include #include "configurations/EthernetConfiguration.h" #include "configurations/KeyConfiguration.h" class SesameController: public ServerConnectionCallbacks { public: - SesameController(uint16_t localWebServerPort); + SesameController(); void configure(ServoConfiguration servoConfig, ServerConfiguration serverConfig, EthernetConfiguration ethernetConfig, KeyConfiguration keyConfig); @@ -22,7 +21,16 @@ private: ServerConnection server; ServoController servo; - AsyncWebServer localWebServer; + + // UDP + + // buffers for receiving and sending data + char udpReceiveBuffer[SIGNED_MESSAGE_SIZE]; // buffer to hold incoming packet + + // An EthernetUDP instance to send and receive packets over UDP + EthernetUDP Udp; + + EthernetConfiguration ethernetConfig; bool ethernetIsConfigured = false; @@ -52,7 +60,7 @@ private: // MARK: Local client callbacks - void handleLocalMessage(AsyncWebServerRequest *request); + void checkLocalMessage(); // MARK: Socket Callbacks @@ -76,7 +84,7 @@ private: * * Note: Prepares the response in the outgoing message buffer. */ - void processMessage(SignedMessage* message); + void processMessage(SignedMessage* message, bool shouldPerformUnlock); /** * @brief Checks that the message is valid and prepares a challenge. @@ -105,7 +113,7 @@ private: * * Note: Prepares the response in the outgoing message buffer. */ - void completeUnlockRequest(Message* message); + void completeUnlockRequest(Message* message, bool shouldPerformUnlock); // MARK: Responses @@ -117,12 +125,18 @@ private: */ void prepareResponseBuffer(MessageResult event, Message* message = NULL); + /** + * @brief Read a message from the UDP port + * + */ + bool readLocalMessage(); + /** * @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(); /** * @brief Send the prepared outgoing message to the server @@ -131,5 +145,5 @@ private: // MARK: Helper - bool convertHexMessageToBinary(const char* str); + bool convertHexMessageToBinary(const char* str); }; \ No newline at end of file diff --git a/platformio.ini b/platformio.ini index 6466228..613c1b7 100644 --- a/platformio.ini +++ b/platformio.ini @@ -13,9 +13,7 @@ platform = espressif32 board = az-delivery-devkit-v4 framework = arduino lib_deps = - ; links2004/WebSockets@^2.4.0 madhephaestus/ESP32Servo@^1.1.0 - ottowinter/ESPAsyncWebServer-esphome@^3.0.0 arduino-libraries/Ethernet@^2.0.2 https://github.com/christophhagen/arduinoWebSockets#master rweather/Crypto@^0.4.0 diff --git a/src/controller.cpp b/src/controller.cpp index 8a82684..399fc15 100644 --- a/src/controller.cpp +++ b/src/controller.cpp @@ -5,7 +5,7 @@ #include #include -SesameController::SesameController(uint16_t localWebServerPort) : localWebServer(localWebServerPort) { +SesameController::SesameController() { } @@ -51,41 +51,54 @@ void SesameController::configure(ServoConfiguration servoConfig, ServerConfigura // Direct messages and errors over the websocket to the controller server.configure(serverConfig, this); Serial.println("[INFO] Server connection configured"); - - // Direct messages from the local web server to the controller - localWebServer.on("/message", HTTP_POST, [this] (AsyncWebServerRequest *request) { - this->handleLocalMessage(request); - this->sendPreparedLocalResponse(request); - }); - Serial.println("[INFO] Local web server configured"); + Udp.begin(ethernetConfig.udpPort); + Serial.println("[INFO] Local UDP connection configured"); } void SesameController::loop(uint32_t millis) { currentTime = millis; - server.loop(millis); + //server.loop(millis); + + checkLocalMessage(); } // MARK: Local -void SesameController::handleLocalMessage(AsyncWebServerRequest *request) { - if (!request->hasParam(messageUrlParameter)) { - Serial.println("Missing url parameter"); - prepareResponseBuffer(MessageResult::InvalidUrlParameter); - return; +void SesameController::checkLocalMessage() { + if (readLocalMessage()) { + sendPreparedLocalResponse(); } - String encoded = request->getParam(messageUrlParameter)->value(); - if (!convertHexMessageToBinary(encoded.c_str())) { - Serial.println("Invalid hex encoding"); - prepareResponseBuffer(MessageResult::InvalidMessageSizeFromRemote); - return; - } - processMessage(&receivedLocalMessage); } -void SesameController::sendPreparedLocalResponse(AsyncWebServerRequest *request) { - request->send_P(200, "application/octet-stream", (uint8_t*) &outgoingMessage, SIGNED_MESSAGE_SIZE); - Serial.printf("[INFO] Local response %u,%u\n", outgoingMessage.message.messageType, outgoingMessage.message.result); +bool SesameController::readLocalMessage() { + // if there's data available, read a packet + int packetSize = Udp.parsePacket(); + if (packetSize == 0) { + return false; + } + if (packetSize != SIGNED_MESSAGE_SIZE) { + Serial.print("Received packet of invalid size "); + Serial.println(packetSize); + prepareResponseBuffer(MessageResult::InvalidMessageSizeFromRemote); + return true; + } + int bytesRead = Udp.read((uint8_t*) &receivedLocalMessage, SIGNED_MESSAGE_SIZE); + if (bytesRead != SIGNED_MESSAGE_SIZE) { + Serial.println("Failed to read full local message"); + prepareResponseBuffer(MessageResult::InvalidMessageSizeFromRemote); + return true; + } + Serial.println("Received local message"); + processMessage(&receivedLocalMessage, true); + return true; +} + +void SesameController::sendPreparedLocalResponse() { + // send a reply to the IP address and port that sent us the packet we received + Udp.beginPacket(Udp.remoteIP(), Udp.remotePort()); + Udp.write((uint8_t*) &outgoingMessage, SIGNED_MESSAGE_SIZE); + Udp.endPacket(); } // MARK: Server @@ -101,7 +114,7 @@ void SesameController::handleServerMessage(uint8_t* payload, size_t length) { sendServerError(MessageResult::InvalidMessageSizeFromRemote); return; } - processMessage((SignedMessage*) payload); + processMessage((SignedMessage*) payload, true); sendPreparedResponseToServer(); } @@ -112,7 +125,7 @@ void SesameController::sendPreparedResponseToServer() { // MARK: Message handling -void SesameController::processMessage(SignedMessage* message) { +void SesameController::processMessage(SignedMessage* message, bool shouldPerformUnlock) { // Result must be empty if (message->message.result != MessageResult::MessageAccepted) { prepareResponseBuffer(MessageResult::InvalidMessageResultFromRemote); @@ -127,7 +140,7 @@ void SesameController::processMessage(SignedMessage* message) { checkAndPrepareChallenge(&message->message); return; case MessageType::request: - completeUnlockRequest(&message->message); + completeUnlockRequest(&message->message, shouldPerformUnlock); return; default: prepareResponseBuffer(MessageResult::InvalidMessageTypeFromRemote); @@ -158,7 +171,7 @@ void SesameController::prepareChallenge(Message* message) { prepareResponseBuffer(MessageResult::MessageAccepted, message); } -void SesameController::completeUnlockRequest(Message* message) { +void SesameController::completeUnlockRequest(Message* message, bool shouldPerformUnlock) { // Client and server challenge must match if (message->clientChallenge != currentClientChallenge) { prepareResponseBuffer(MessageResult::InvalidClientChallengeFromRemote, message); @@ -180,7 +193,9 @@ void SesameController::completeUnlockRequest(Message* message) { clearCurrentChallenge(); // Move servo - servo.pressButton(); + if (shouldPerformUnlock) { + servo.pressButton(); + } prepareResponseBuffer(MessageResult::MessageAccepted, message); Serial.println("[INFO] Accepted message"); } diff --git a/src/main.cpp b/src/main.cpp index 4b10374..c64615e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -22,7 +22,7 @@ #include "controller.h" #include "config.h" -SesameController controller(localPort); +SesameController controller{}; void setup() { Serial.begin(serialBaudRate); @@ -59,6 +59,7 @@ void setup() { .dhcpLeaseResponseTimeoutMs = dhcpLeaseResponseTimeoutMs, .manualIp = manualIpAddress, .manualDnsAddress = manualDnsServerAddress, + .udpPort = localUdpPort, }; KeyConfiguration keyConfig {