Switch to ethernet, challenge-response
This commit is contained in:
@ -2,23 +2,47 @@
|
||||
#include "crypto.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <WiFi.h>
|
||||
#include <SPI.h>
|
||||
#include <Ethernet.h>
|
||||
|
||||
SesameController::SesameController(uint16_t localWebServerPort) : localWebServer(localWebServerPort) {
|
||||
|
||||
SesameController::SesameController(uint16_t localWebServerPort, uint8_t remoteDeviceCount) :
|
||||
storage(remoteDeviceCount), localWebServer(localWebServerPort) {
|
||||
|
||||
// Set up response buffer
|
||||
responseStatus = (SesameEvent*) responseBuffer;
|
||||
responseMessage = (AuthenticatedMessage*) (responseBuffer + 1);
|
||||
}
|
||||
|
||||
void SesameController::configure(ServoConfiguration servoConfig, ServerConfiguration serverConfig, TimeConfiguration timeConfig, WifiConfiguration wifiConfig, KeyConfiguration keyConfig) {
|
||||
this->wifiConfig = wifiConfig;
|
||||
void SesameController::configure(ServoConfiguration servoConfig, ServerConfiguration serverConfig, EthernetConfiguration ethernetConfig, KeyConfiguration keyConfig) {
|
||||
this->ethernetConfig = ethernetConfig;
|
||||
this->keyConfig = keyConfig;
|
||||
|
||||
// Prepare EEPROM for reading and writing
|
||||
storage.configure();
|
||||
Serial.println("[INFO] Storage configured");
|
||||
// Ensure source of random numbers without WiFi and Bluetooth
|
||||
enableCrypto();
|
||||
|
||||
// Initialize SPI interface to Ethernet module
|
||||
SPI.begin(ethernetConfig.spiPinSclk, ethernetConfig.spiPinMiso, ethernetConfig.spiPinMosi, ethernetConfig.spiPinSS); //SCLK, MISO, MOSI, SS
|
||||
pinMode(ethernetConfig.spiPinSS, OUTPUT);
|
||||
|
||||
Ethernet.init(ethernetConfig.spiPinSS);
|
||||
|
||||
// Check for Ethernet hardware present
|
||||
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||
Serial.println("[ERROR] Ethernet shield not found.");
|
||||
} else if (Ethernet.linkStatus() == LinkOFF) {
|
||||
Serial.println("[ERROR] Ethernet cable is not connected.");
|
||||
} else if (Ethernet.linkStatus() == Unknown) {
|
||||
Serial.println("[ERROR] Ethernet cable status unknown.");
|
||||
} else if (Ethernet.linkStatus() == LinkON) {
|
||||
Serial.println("[INFO] Ethernet cable is connected.");
|
||||
if (Ethernet.begin(ethernetConfig.macAddress, ethernetConfig.dhcpLeaseTimeoutMs, ethernetConfig.dhcpLeaseResponseTimeoutMs) == 1) {
|
||||
Serial.print("[INFO] DHCP assigned IP ");
|
||||
Serial.println(Ethernet.localIP());
|
||||
ethernetIsConfigured = true;
|
||||
} else {
|
||||
// Try to configure using IP address instead of DHCP
|
||||
Ethernet.begin(ethernetConfig.macAddress, ethernetConfig.manualIp, ethernetConfig.manualDnsAddress);
|
||||
Serial.print("[WARNING] DHCP failed, using self-assigned IP ");
|
||||
Serial.println(Ethernet.localIP());
|
||||
ethernetIsConfigured = true;
|
||||
}
|
||||
}
|
||||
|
||||
servo.configure(servoConfig);
|
||||
Serial.println("[INFO] Servo configured");
|
||||
@ -26,9 +50,6 @@ 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");
|
||||
|
||||
timeCheck.configure(timeConfig);
|
||||
|
||||
|
||||
// Direct messages from the local web server to the controller
|
||||
localWebServer.on("/message", HTTP_POST, [this] (AsyncWebServerRequest *request) {
|
||||
@ -37,17 +58,13 @@ void SesameController::configure(ServoConfiguration servoConfig, ServerConfigura
|
||||
});
|
||||
|
||||
Serial.println("[INFO] Local web server configured");
|
||||
|
||||
//storage.resetMessageCounters();
|
||||
storage.printMessageCounters();
|
||||
}
|
||||
|
||||
void SesameController::loop(uint32_t millis) {
|
||||
currentTime = millis;
|
||||
server.loop();
|
||||
servo.loop(millis);
|
||||
|
||||
periodicallyReconnectWifiAndSocket(millis);
|
||||
ensureWiFiConnection(millis);
|
||||
ensureWebSocketConnection();
|
||||
}
|
||||
|
||||
@ -56,126 +73,134 @@ void SesameController::loop(uint32_t millis) {
|
||||
void SesameController::handleLocalMessage(AsyncWebServerRequest *request) {
|
||||
if (!request->hasParam(messageUrlParameter)) {
|
||||
Serial.println("Missing url parameter");
|
||||
prepareResponseBuffer(SesameEvent::InvalidUrlParameter);
|
||||
prepareResponseBuffer(MessageResult::InvalidUrlParameter);
|
||||
return;
|
||||
}
|
||||
String encoded = request->getParam(messageUrlParameter)->value();
|
||||
if (!convertHexMessageToBinary(encoded.c_str())) {
|
||||
Serial.println("Invalid hex encoding");
|
||||
prepareResponseBuffer(SesameEvent::InvalidMessageSize);
|
||||
prepareResponseBuffer(MessageResult::InvalidMessageSize);
|
||||
return;
|
||||
}
|
||||
processMessage((AuthenticatedMessage*) receivedMessageBuffer);
|
||||
processMessage(&receivedLocalMessage);
|
||||
}
|
||||
|
||||
void SesameController::sendPreparedLocalResponse(AsyncWebServerRequest *request) {
|
||||
request->send_P(200, "application/octet-stream", responseBuffer, responseSize);
|
||||
Serial.printf("[INFO] Local response %u (%u bytes)\n", responseBuffer[0], responseSize);
|
||||
request->send_P(200, "application/octet-stream", (uint8_t*) &outgoingMessage, SIGNED_MESSAGE_SIZE);
|
||||
Serial.printf("[INFO] Local response %u\n", outgoingMessage.message.messageType);
|
||||
}
|
||||
|
||||
// MARK: Server
|
||||
|
||||
void SesameController::sendServerError(SesameEvent event) {
|
||||
prepareResponseBuffer(event);
|
||||
sendPreparedServerResponse();
|
||||
void SesameController::sendServerError(MessageResult result) {
|
||||
prepareResponseBuffer(result); // No message to echo
|
||||
sendPreparedResponseToServer();
|
||||
}
|
||||
|
||||
void SesameController::handleServerMessage(uint8_t* payload, size_t length) {
|
||||
if (length != AUTHENTICATED_MESSAGE_SIZE) {
|
||||
prepareResponseBuffer(SesameEvent::InvalidMessageSize);
|
||||
if (length != SIGNED_MESSAGE_SIZE) {
|
||||
// No message saved to discard, don't accidentally delete for other operation
|
||||
sendServerError(MessageResult::InvalidMessageSize);
|
||||
return;
|
||||
}
|
||||
|
||||
processMessage((AuthenticatedMessage*) payload);
|
||||
sendPreparedServerResponse();
|
||||
processMessage((SignedMessage*) payload);
|
||||
sendPreparedResponseToServer();
|
||||
}
|
||||
|
||||
void SesameController::sendPreparedServerResponse() {
|
||||
server.sendResponse(responseBuffer, responseSize);
|
||||
Serial.printf("[INFO] Server response %u (%u bytes)\n", responseBuffer[0], responseSize);
|
||||
void SesameController::sendPreparedResponseToServer() {
|
||||
server.sendResponse((uint8_t*) &outgoingMessage, SIGNED_MESSAGE_SIZE);
|
||||
Serial.printf("[INFO] Server response %u\n", outgoingMessage.message.messageType);
|
||||
}
|
||||
|
||||
// MARK: Message handling
|
||||
|
||||
void SesameController::processMessage(AuthenticatedMessage* message) {
|
||||
SesameEvent event = verifyAndProcessReceivedMessage(message);
|
||||
prepareResponseBuffer(event, message->message.device);
|
||||
void SesameController::processMessage(SignedMessage* message) {
|
||||
// Result must be empty
|
||||
if (message->message.result != MessageResult::MessageAccepted) {
|
||||
prepareResponseBuffer(MessageResult::ClientChallengeInvalid);
|
||||
return;
|
||||
}
|
||||
if (!isAuthenticMessage(message, keyConfig.remoteKey)) {
|
||||
prepareResponseBuffer(MessageResult::MessageAuthenticationFailed);
|
||||
return;
|
||||
}
|
||||
switch (message->message.messageType) {
|
||||
case MessageType::initial:
|
||||
prepareChallenge(&message->message);
|
||||
return;
|
||||
case MessageType::request:
|
||||
completeUnlockRequest(&message->message);
|
||||
return;
|
||||
default:
|
||||
prepareResponseBuffer(MessageResult::InvalidMessageType);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a received message.
|
||||
*
|
||||
* Checks whether the received data is a valid,
|
||||
* and then signals that the motor should move.
|
||||
*
|
||||
* @param message The message received from the remote
|
||||
* @return The response to signal to the server.
|
||||
*/
|
||||
SesameEvent SesameController::verifyAndProcessReceivedMessage(AuthenticatedMessage* message) {
|
||||
if (!isAuthenticMessage(message, keyConfig.remoteKey, keySize)) {
|
||||
return SesameEvent::MessageAuthenticationFailed;
|
||||
void SesameController::prepareChallenge(Message* message) {
|
||||
// Server challenge must be empty
|
||||
if (message->serverChallenge != 0) {
|
||||
prepareResponseBuffer(MessageResult::ClientChallengeInvalid);
|
||||
return;
|
||||
}
|
||||
if (!storage.isDeviceIdValid(message->message.device)) {
|
||||
return SesameEvent::MessageDeviceInvalid;
|
||||
}
|
||||
if (!storage.isMessageCounterValid(message->message.id, message->message.device)) {
|
||||
return SesameEvent::MessageCounterInvalid;
|
||||
}
|
||||
if (!timeCheck.isMessageTimeAcceptable(message->message.time)) {
|
||||
return SesameEvent::MessageTimeMismatch;
|
||||
if (hasCurrentChallenge()) {
|
||||
Serial.println("[INFO] Overwriting old challenge");
|
||||
}
|
||||
|
||||
storage.didUseMessageCounter(message->message.id, message->message.device);
|
||||
// Set challenge and respond
|
||||
currentClientChallenge = message->clientChallenge;
|
||||
currentServerChallenge = randomChallenge();
|
||||
currentChallengeExpiry = currentTime + keyConfig.challengeExpiryMs;
|
||||
|
||||
prepareResponseBuffer(MessageResult::MessageAccepted, message);
|
||||
}
|
||||
|
||||
void SesameController::completeUnlockRequest(Message* message) {
|
||||
if (!hasCurrentChallenge()) {
|
||||
prepareResponseBuffer(MessageResult::ClientChallengeInvalid, message);
|
||||
return;
|
||||
}
|
||||
// Client and server challenge must match
|
||||
if (message->clientChallenge != currentClientChallenge) {
|
||||
prepareResponseBuffer(MessageResult::ClientChallengeInvalid, message);
|
||||
return;
|
||||
}
|
||||
if (message->serverChallenge != currentServerChallenge) {
|
||||
prepareResponseBuffer(MessageResult::ServerChallengeMismatch, message);
|
||||
return;
|
||||
}
|
||||
clearCurrentChallenge();
|
||||
|
||||
// Move servo
|
||||
servo.pressButton();
|
||||
Serial.printf("[Info] Accepted message %d\n", message->message.id);
|
||||
return SesameEvent::MessageAccepted;
|
||||
prepareResponseBuffer(MessageResult::MessageAccepted, message);
|
||||
Serial.println("[INFO] Accepted message");
|
||||
}
|
||||
|
||||
bool allowMessageResponse(SesameEvent event) {
|
||||
switch (event) {
|
||||
case SesameEvent::MessageTimeMismatch:
|
||||
case SesameEvent::MessageCounterInvalid:
|
||||
case SesameEvent::MessageAccepted:
|
||||
case SesameEvent::MessageDeviceInvalid:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
void SesameController::prepareResponseBuffer(MessageResult result, Message* message) {
|
||||
outgoingMessage.message.result = result;
|
||||
if (message != NULL) {
|
||||
outgoingMessage.message.clientChallenge = message->clientChallenge;
|
||||
outgoingMessage.message.serverChallenge = message->serverChallenge;
|
||||
// All outgoing messages are responses, except if an initial message is accepted
|
||||
if (message->messageType == MessageType::initial && result == MessageResult::MessageAccepted) {
|
||||
outgoingMessage.message.messageType = MessageType::challenge;
|
||||
} else {
|
||||
outgoingMessage.message.messageType = MessageType::response;
|
||||
}
|
||||
} else {
|
||||
outgoingMessage.message.clientChallenge = message->clientChallenge;
|
||||
outgoingMessage.message.serverChallenge = message->serverChallenge;
|
||||
outgoingMessage.message.messageType = MessageType::response;
|
||||
}
|
||||
}
|
||||
|
||||
void SesameController::prepareResponseBuffer(SesameEvent event, uint8_t deviceId) {
|
||||
*responseStatus = event;
|
||||
responseSize = 1;
|
||||
if (!allowMessageResponse(event)) {
|
||||
return;
|
||||
}
|
||||
responseMessage->message.time = timeCheck.getEpochTime();
|
||||
responseMessage->message.id = storage.getNextMessageCounter(deviceId);
|
||||
responseMessage->message.device = deviceId;
|
||||
|
||||
if (!authenticateMessage(responseMessage, keyConfig.localKey, keySize)) {
|
||||
*responseStatus = SesameEvent::InvalidResponseAuthentication;
|
||||
return;
|
||||
}
|
||||
responseSize += AUTHENTICATED_MESSAGE_SIZE;
|
||||
}
|
||||
|
||||
// MARK: Reconnecting
|
||||
|
||||
void SesameController::ensureWiFiConnection(uint32_t millis) {
|
||||
static uint32_t nextWifiReconnect = 0;
|
||||
// Reconnect to WiFi
|
||||
if(millis > nextWifiReconnect && WiFi.status() != WL_CONNECTED) {
|
||||
Serial.println("[INFO] Reconnecting WiFi...");
|
||||
WiFi.setHostname(wifiConfig.networkName);
|
||||
WiFi.begin(wifiConfig.ssid, wifiConfig.password);
|
||||
isReconnecting = true;
|
||||
nextWifiReconnect = millis + wifiConfig.reconnectInterval;
|
||||
if (!authenticateMessage(&outgoingMessage, keyConfig.localKey)) {
|
||||
Serial.println("[ERROR] Failed to sign message");
|
||||
}
|
||||
}
|
||||
|
||||
void SesameController::ensureWebSocketConnection() {
|
||||
/*
|
||||
if (isReconnecting && WiFi.status() == WL_CONNECTED) {
|
||||
isReconnecting = false;
|
||||
Serial.print("WiFi IP address: ");
|
||||
@ -185,12 +210,22 @@ void SesameController::ensureWebSocketConnection() {
|
||||
timeCheck.printLocalTime();
|
||||
localWebServer.begin();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
// MARK: Helper
|
||||
|
||||
// Based on https://stackoverflow.com/a/23898449/266720
|
||||
/**
|
||||
* @brief
|
||||
*
|
||||
* Based on https://stackoverflow.com/a/23898449/266720
|
||||
*
|
||||
* @param str
|
||||
* @return true
|
||||
* @return false
|
||||
*/
|
||||
bool SesameController::convertHexMessageToBinary(const char* str) {
|
||||
uint8_t* buffer = (uint8_t*) &receivedLocalMessage;
|
||||
// TODO: Fail if invalid hex values are used
|
||||
uint8_t idx0, idx1;
|
||||
|
||||
@ -203,7 +238,7 @@ bool SesameController::convertHexMessageToBinary(const char* str) {
|
||||
};
|
||||
|
||||
size_t len = strlen(str);
|
||||
if (len != AUTHENTICATED_MESSAGE_SIZE * 2) {
|
||||
if (len != SIGNED_MESSAGE_SIZE * 2) {
|
||||
// Require exact message size
|
||||
return false;
|
||||
}
|
||||
@ -211,17 +246,7 @@ bool SesameController::convertHexMessageToBinary(const char* str) {
|
||||
for (size_t pos = 0; pos < len; pos += 2) {
|
||||
idx0 = ((uint8_t)str[pos+0] & 0x1F) ^ 0x10;
|
||||
idx1 = ((uint8_t)str[pos+1] & 0x1F) ^ 0x10;
|
||||
receivedMessageBuffer[pos/2] = (uint8_t)(hashmap[idx0] << 4) | hashmap[idx1];
|
||||
buffer[pos/2] = (uint8_t)(hashmap[idx0] << 4) | hashmap[idx1];
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
void SesameController::periodicallyReconnectWifiAndSocket(uint32_t millis) {
|
||||
static uint32_t nextWifiReconnect = wifiConfig.periodicReconnectInterval;
|
||||
if (millis > nextWifiReconnect) {
|
||||
nextWifiReconnect += wifiConfig.periodicReconnectInterval;
|
||||
|
||||
server.disconnect();
|
||||
WiFi.disconnect();
|
||||
}
|
||||
}
|
@ -1,8 +1,19 @@
|
||||
#include "crypto.h"
|
||||
#include "config.h"
|
||||
#include <string.h>
|
||||
#include <mbedtls/md.h>
|
||||
#include <esp_random.h>
|
||||
#include <bootloader_random.h>
|
||||
|
||||
bool authenticateData(const uint8_t* data, size_t dataLength, uint8_t* mac, const uint8_t* key, size_t keyLength) {
|
||||
void enableCrypto() {
|
||||
bootloader_random_enable();
|
||||
}
|
||||
|
||||
uint32_t randomChallenge() {
|
||||
return esp_random();
|
||||
}
|
||||
|
||||
bool authenticateData(const uint8_t* data, size_t dataLength, uint8_t* mac, const uint8_t* key) {
|
||||
mbedtls_md_context_t ctx;
|
||||
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
|
||||
int result;
|
||||
@ -12,7 +23,7 @@ bool authenticateData(const uint8_t* data, size_t dataLength, uint8_t* mac, cons
|
||||
if (result) {
|
||||
return false;
|
||||
}
|
||||
result = mbedtls_md_hmac_starts(&ctx, key, keyLength);
|
||||
result = mbedtls_md_hmac_starts(&ctx, key, keySize);
|
||||
if (result) {
|
||||
return false;
|
||||
}
|
||||
@ -28,17 +39,17 @@ bool authenticateData(const uint8_t* data, size_t dataLength, uint8_t* mac, cons
|
||||
return true;
|
||||
}
|
||||
|
||||
bool authenticateMessage(Message* message, uint8_t* mac, const uint8_t* key, size_t keyLength) {
|
||||
return authenticateData((const uint8_t*) message, MESSAGE_CONTENT_SIZE, mac, key, keyLength);
|
||||
bool authenticateMessage(Message* message, uint8_t* mac, const uint8_t* key) {
|
||||
return authenticateData((const uint8_t*) message, MESSAGE_CONTENT_SIZE, mac, key);
|
||||
}
|
||||
|
||||
bool authenticateMessage(AuthenticatedMessage* message, const uint8_t* key, size_t keyLength) {
|
||||
return authenticateMessage(&message->message, message->mac, key, keyLength);
|
||||
bool authenticateMessage(SignedMessage* message, const uint8_t* key) {
|
||||
return authenticateMessage(&message->message, message->mac, key);
|
||||
}
|
||||
|
||||
bool isAuthenticMessage(AuthenticatedMessage* message, const uint8_t* key, size_t keyLength) {
|
||||
bool isAuthenticMessage(SignedMessage* message, const uint8_t* key) {
|
||||
uint8_t mac[SHA256_MAC_SIZE];
|
||||
if (!authenticateMessage(&message->message, mac, key, keyLength)) {
|
||||
if (!authenticateMessage(&message->message, mac, key)) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(mac, message->mac, SHA256_MAC_SIZE) == 0;
|
||||
|
@ -1,49 +0,0 @@
|
||||
#include "fresh.h"
|
||||
|
||||
#include <Arduino.h> // configTime()
|
||||
#include <time.h>
|
||||
|
||||
TimeCheck::TimeCheck() { }
|
||||
|
||||
void TimeCheck::configure(TimeConfiguration configuration) {
|
||||
config = configuration;
|
||||
}
|
||||
|
||||
void TimeCheck::startNTP() {
|
||||
configTime(config.offsetToGMT, config.offsetDaylightSavings, config.ntpServerUrl);
|
||||
}
|
||||
|
||||
void TimeCheck::printLocalTime() {
|
||||
struct tm timeinfo;
|
||||
if (getLocalTime(&timeinfo)) {
|
||||
Serial.println(&timeinfo, "[INFO] Time is %A, %d. %B %Y %H:%M:%S");
|
||||
} else {
|
||||
Serial.println("[WARN] No local time available");
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t TimeCheck::getEpochTime() {
|
||||
time_t now;
|
||||
struct tm timeinfo;
|
||||
if (!getLocalTime(&timeinfo)) {
|
||||
Serial.println("[WARN] Failed to obtain local time");
|
||||
return(0);
|
||||
}
|
||||
time(&now);
|
||||
return now;
|
||||
}
|
||||
|
||||
bool TimeCheck::isMessageTimeAcceptable(uint32_t t) {
|
||||
uint32_t localTime = getEpochTime();
|
||||
if (localTime == 0) {
|
||||
Serial.println("No epoch time available");
|
||||
return false;
|
||||
}
|
||||
if (t > localTime + config.allowedTimeOffset) {
|
||||
return false;
|
||||
}
|
||||
if (t < localTime - config.allowedTimeOffset) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
31
src/main.cpp
31
src/main.cpp
@ -13,7 +13,7 @@
|
||||
#include "controller.h"
|
||||
#include "config.h"
|
||||
|
||||
SesameController controller(localPort, remoteDeviceCount);
|
||||
SesameController controller(localPort);
|
||||
|
||||
void setup() {
|
||||
Serial.begin(serialBaudRate);
|
||||
@ -37,27 +37,28 @@ void setup() {
|
||||
.reconnectTime = 5000,
|
||||
};
|
||||
|
||||
TimeConfiguration timeConfig {
|
||||
.offsetToGMT = timeOffsetToGMT,
|
||||
.offsetDaylightSavings = timeOffsetDaylightSavings,
|
||||
.ntpServerUrl = ntpServerUrl,
|
||||
.allowedTimeOffset = 60,
|
||||
};
|
||||
|
||||
WifiConfiguration wifiConfig {
|
||||
.ssid = wifiSSID,
|
||||
.password = wifiPassword,
|
||||
.networkName = networkName,
|
||||
.reconnectInterval = wifiReconnectInterval,
|
||||
.periodicReconnectInterval = wifiPeriodicReconnectInterval,
|
||||
EthernetConfiguration ethernetConfig {
|
||||
.macAddress = ethernetMacAddress,
|
||||
.spiPinMiso = spiPinMiso,
|
||||
.spiPinMosi = spiPinMosi,
|
||||
.spiPinSclk = spiPinSclk,
|
||||
.spiPinSS = spiPinSS,
|
||||
.dhcpLeaseTimeoutMs = dhcpLeaseTimeoutMs,
|
||||
.dhcpLeaseResponseTimeoutMs = dhcpLeaseResponseTimeoutMs,
|
||||
.manualIp = manualIpAddress,
|
||||
.manualDnsAddress = manualDnsServerAddress,
|
||||
.socketHeartbeatIntervalMs = socketHeartbeatIntervalMs,
|
||||
.socketHeartbeatTimeoutMs = socketHeartbeatTimeoutMs,
|
||||
.socketHeartbeatFailureReconnectCount = socketHeartbeatFailureReconnectCount,
|
||||
};
|
||||
|
||||
KeyConfiguration keyConfig {
|
||||
.remoteKey = remoteKey,
|
||||
.localKey = localKey,
|
||||
.challengeExpiryMs = challengeExpiryMs,
|
||||
};
|
||||
|
||||
controller.configure(servoConfig, serverConfig, timeConfig, wifiConfig, keyConfig);
|
||||
controller.configure(servoConfig, serverConfig, ethernetConfig, keyConfig);
|
||||
}
|
||||
|
||||
void loop() {
|
||||
|
@ -48,7 +48,7 @@ switch(type) {
|
||||
webSocket.enableHeartbeat(pingInterval, pongTimeout, disconnectTimeoutCount);
|
||||
break;
|
||||
case WStype_TEXT:
|
||||
controller->sendServerError(SesameEvent::TextReceived);
|
||||
controller->sendServerError(MessageResult::TextReceived);
|
||||
break;
|
||||
case WStype_BIN:
|
||||
controller->handleServerMessage(payload, length);
|
||||
@ -61,7 +61,7 @@ switch(type) {
|
||||
case WStype_FRAGMENT_BIN_START:
|
||||
case WStype_FRAGMENT:
|
||||
case WStype_FRAGMENT_FIN:
|
||||
controller->sendServerError(SesameEvent::UnexpectedSocketEvent);
|
||||
controller->sendServerError(MessageResult::UnexpectedSocketEvent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1,53 +0,0 @@
|
||||
#include "storage.h"
|
||||
#include "message.h"
|
||||
#include <EEPROM.h>
|
||||
|
||||
void Storage::configure() {
|
||||
EEPROM.begin(messageCounterSize * remoteDeviceCount);
|
||||
}
|
||||
|
||||
bool Storage::isDeviceIdValid(uint8_t deviceId) {
|
||||
return deviceId < remoteDeviceCount;
|
||||
}
|
||||
|
||||
bool Storage::isMessageCounterValid(uint32_t counter, uint8_t deviceId) {
|
||||
return counter >= getNextMessageCounter(deviceId);
|
||||
}
|
||||
|
||||
void Storage::didUseMessageCounter(uint32_t counter, uint8_t deviceId) {
|
||||
// Store the next counter, so that resetting starts at 0
|
||||
setMessageCounter(counter+1, deviceId);
|
||||
}
|
||||
|
||||
void Storage::setMessageCounter(uint32_t counter, uint8_t deviceId) {
|
||||
int offset = deviceId * messageCounterSize;
|
||||
EEPROM.write(offset + 0, (counter >> 24) & 0xFF);
|
||||
EEPROM.write(offset + 1, (counter >> 16) & 0xFF);
|
||||
EEPROM.write(offset + 2, (counter >> 8) & 0xFF);
|
||||
EEPROM.write(offset + 3, counter & 0xFF);
|
||||
EEPROM.commit();
|
||||
}
|
||||
|
||||
uint32_t Storage::getNextMessageCounter(uint8_t deviceId) {
|
||||
int offset = deviceId * messageCounterSize;
|
||||
uint32_t counter = (uint32_t) EEPROM.read(offset + 0) << 24;
|
||||
counter += (uint32_t) EEPROM.read(offset + 1) << 16;
|
||||
counter += (uint32_t) EEPROM.read(offset + 2) << 8;
|
||||
counter += (uint32_t) EEPROM.read(offset + 3);
|
||||
return counter;
|
||||
}
|
||||
|
||||
void Storage::printMessageCounters() {
|
||||
Serial.print("[INFO] Next message numbers:");
|
||||
for (uint8_t i = 0; i < remoteDeviceCount; i += 1) {
|
||||
Serial.printf(" %u", getNextMessageCounter(i));
|
||||
}
|
||||
Serial.println("");
|
||||
}
|
||||
|
||||
void Storage::resetMessageCounters() {
|
||||
for (uint8_t i = 0; i < remoteDeviceCount; i += 1) {
|
||||
setMessageCounter(0, i);
|
||||
}
|
||||
Serial.println("[WARN] Message counters reset");
|
||||
}
|
Reference in New Issue
Block a user