Compare commits
7 Commits
2a6db822ff
...
master
Author | SHA1 | Date | |
---|---|---|---|
da22f8a37c | |||
3afe8b16a9 | |||
1504ce6b0c | |||
4a88d1a380 | |||
6256b6ef33 | |||
bd5a8d52cc | |||
be274132d6 |
33
include/configurations/EthernetConfiguration.h
Normal file
33
include/configurations/EthernetConfiguration.h
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct EthernetConfiguration {
|
||||||
|
|
||||||
|
// The MAC address of the ethernet connection
|
||||||
|
uint8_t macAddress[6];
|
||||||
|
|
||||||
|
// The master-in slave-out pin of the SPI connection for the Ethernet module
|
||||||
|
int8_t spiPinMiso;
|
||||||
|
|
||||||
|
// The master-out slave-in pin of the SPI connection for the Ethernet module
|
||||||
|
int8_t spiPinMosi;
|
||||||
|
|
||||||
|
// The slave clock pin of the SPI connection for the Ethernet module
|
||||||
|
int8_t spiPinSclk;
|
||||||
|
|
||||||
|
// The slave-select pin of the SPI connection for the Ethernet module
|
||||||
|
int8_t spiPinSS;
|
||||||
|
|
||||||
|
unsigned long dhcpLeaseTimeoutMs;
|
||||||
|
unsigned long dhcpLeaseResponseTimeoutMs;
|
||||||
|
|
||||||
|
// The static IP address to assign if DHCP fails
|
||||||
|
uint8_t manualIp[4];
|
||||||
|
|
||||||
|
// The IP address of the DNS server, if DHCP fails
|
||||||
|
uint8_t manualDnsAddress[4];
|
||||||
|
|
||||||
|
// The port for the incoming UDP connection
|
||||||
|
uint16_t udpPort;
|
||||||
|
};
|
12
include/configurations/KeyConfiguration.h
Normal file
12
include/configurations/KeyConfiguration.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
struct KeyConfiguration {
|
||||||
|
|
||||||
|
const uint8_t* remoteKey;
|
||||||
|
|
||||||
|
const uint8_t* localKey;
|
||||||
|
|
||||||
|
uint32_t challengeExpiryMs;
|
||||||
|
};
|
@ -3,62 +3,59 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
#include "servo.h"
|
#include "servo.h"
|
||||||
#include "message.h"
|
#include "message.h"
|
||||||
#include <ESPAsyncWebServer.h>
|
#include "configurations/EthernetConfiguration.h"
|
||||||
|
#include "configurations/KeyConfiguration.h"
|
||||||
|
|
||||||
struct EthernetConfiguration {
|
enum class SesameDeviceStatus {
|
||||||
|
|
||||||
// The MAC address of the ethernet connection
|
/**
|
||||||
uint8_t macAddress[6];
|
* @brief The initial state of the device after boot
|
||||||
|
*/
|
||||||
|
initial,
|
||||||
|
|
||||||
// The master-in slave-out pin of the SPI connection for the Ethernet module
|
/**
|
||||||
int8_t spiPinMiso;
|
* @brief The device has configured the individual parts,
|
||||||
|
* but has no the ethernet hardware detected.
|
||||||
|
*/
|
||||||
|
configuredButNoEthernetHardware,
|
||||||
|
|
||||||
// The master-out slave-in pin of the SPI connection for the Ethernet module
|
/**
|
||||||
int8_t spiPinMosi;
|
* @brief The device has ethernet hardware, but no ethernet link
|
||||||
|
*/
|
||||||
|
ethernetHardwareButNoLink,
|
||||||
|
|
||||||
// The slave clock pin of the SPI connection for the Ethernet module
|
/**
|
||||||
int8_t spiPinSclk;
|
* @brief The device has an ethernet link, but no IP address.
|
||||||
|
*/
|
||||||
|
ethernetLinkButNoIP,
|
||||||
|
|
||||||
// The slave-select pin of the SPI connection for the Ethernet module
|
/**
|
||||||
int8_t spiPinSS;
|
* @brief The device has an IP address, but no socket connection
|
||||||
|
*/
|
||||||
unsigned long dhcpLeaseTimeoutMs;
|
ipAddressButNoSocketConnection,
|
||||||
unsigned long dhcpLeaseResponseTimeoutMs;
|
|
||||||
|
|
||||||
// The static IP address to assign if DHCP fails
|
|
||||||
uint8_t manualIp[4];
|
|
||||||
|
|
||||||
// The IP address of the DNS server, if DHCP fails
|
|
||||||
uint8_t manualDnsAddress[4];
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
struct KeyConfiguration {
|
|
||||||
|
|
||||||
const uint8_t* remoteKey;
|
|
||||||
|
|
||||||
const uint8_t* localKey;
|
|
||||||
|
|
||||||
uint32_t challengeExpiryMs;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class SesameController: public ServerConnectionCallbacks {
|
class SesameController: public ServerConnectionCallbacks {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
SesameController(uint16_t localWebServerPort);
|
SesameController(ServoConfiguration servoConfig, ServerConfiguration serverConfig, EthernetConfiguration ethernetConfig, KeyConfiguration keyConfig);
|
||||||
|
|
||||||
void configure(ServoConfiguration servoConfig, ServerConfiguration serverConfig, EthernetConfiguration ethernetConfig, KeyConfiguration keyConfig);
|
|
||||||
|
|
||||||
void loop(uint32_t millis);
|
void loop(uint32_t millis);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
SesameDeviceStatus status = SesameDeviceStatus::initial;
|
||||||
|
|
||||||
uint32_t currentTime = 0;
|
uint32_t currentTime = 0;
|
||||||
|
|
||||||
ServerConnection server;
|
ServerConnection server;
|
||||||
ServoController servo;
|
ServoController servo;
|
||||||
AsyncWebServer localWebServer;
|
|
||||||
|
|
||||||
|
// UDP
|
||||||
|
|
||||||
|
// An EthernetUDP instance to send and receive packets over UDP
|
||||||
|
EthernetUDP udp;
|
||||||
|
EthernetHardwareStatus ethernetStatus;
|
||||||
EthernetConfiguration ethernetConfig;
|
EthernetConfiguration ethernetConfig;
|
||||||
bool ethernetIsConfigured = false;
|
bool ethernetIsConfigured = false;
|
||||||
|
|
||||||
@ -75,6 +72,31 @@ private:
|
|||||||
|
|
||||||
SignedMessage outgoingMessage;
|
SignedMessage outgoingMessage;
|
||||||
|
|
||||||
|
// MARK: Ethernet
|
||||||
|
|
||||||
|
void initializeSpiBusForEthernetModule();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Checks to ensure that Ethernet hardware is available
|
||||||
|
*
|
||||||
|
* @return true The hardware is available
|
||||||
|
* @return false The hardware is missing
|
||||||
|
*/
|
||||||
|
bool hasAvailableEthernetHardware();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check that an active ethernet link is available
|
||||||
|
*
|
||||||
|
* @return true Link is available
|
||||||
|
* @return false Link is absent
|
||||||
|
*/
|
||||||
|
bool hasEthernetLink();
|
||||||
|
|
||||||
|
void configureEthernet();
|
||||||
|
|
||||||
|
void startUDP();
|
||||||
|
void stopUDP();
|
||||||
|
|
||||||
bool hasCurrentChallenge() {
|
bool hasCurrentChallenge() {
|
||||||
return currentChallengeExpiry > currentTime;
|
return currentChallengeExpiry > currentTime;
|
||||||
}
|
}
|
||||||
@ -87,7 +109,7 @@ private:
|
|||||||
|
|
||||||
// MARK: Local client callbacks
|
// MARK: Local client callbacks
|
||||||
|
|
||||||
void handleLocalMessage(AsyncWebServerRequest *request);
|
void checkLocalMessage();
|
||||||
|
|
||||||
// MARK: Socket Callbacks
|
// MARK: Socket Callbacks
|
||||||
|
|
||||||
@ -111,7 +133,7 @@ private:
|
|||||||
*
|
*
|
||||||
* Note: Prepares the response in the outgoing message buffer.
|
* 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.
|
* @brief Checks that the message is valid and prepares a challenge.
|
||||||
@ -140,7 +162,7 @@ private:
|
|||||||
*
|
*
|
||||||
* Note: Prepares the response in the outgoing message buffer.
|
* Note: Prepares the response in the outgoing message buffer.
|
||||||
*/
|
*/
|
||||||
void completeUnlockRequest(Message* message);
|
void completeUnlockRequest(Message* message, bool shouldPerformUnlock);
|
||||||
|
|
||||||
// MARK: Responses
|
// MARK: Responses
|
||||||
|
|
||||||
@ -152,12 +174,18 @@ private:
|
|||||||
*/
|
*/
|
||||||
void prepareResponseBuffer(MessageResult event, Message* message = NULL);
|
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
|
* @brief Send the prepared outgoing message to a locally connected client
|
||||||
*
|
*
|
||||||
* @param request The original request of the client
|
* @param request The original request of the client
|
||||||
*/
|
*/
|
||||||
void sendPreparedLocalResponse(AsyncWebServerRequest *request);
|
void sendPreparedLocalResponse();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Send the prepared outgoing message to the server
|
* @brief Send the prepared outgoing message to the server
|
||||||
@ -166,5 +194,5 @@ private:
|
|||||||
|
|
||||||
// MARK: Helper
|
// MARK: Helper
|
||||||
|
|
||||||
bool convertHexMessageToBinary(const char* str);
|
bool convertHexMessageToBinary(const char* str);
|
||||||
};
|
};
|
@ -3,7 +3,7 @@
|
|||||||
#include "relay/interface/CryptoSource.h"
|
#include "relay/interface/CryptoSource.h"
|
||||||
#include "ESP32NoiseSource.h"
|
#include "ESP32NoiseSource.h"
|
||||||
|
|
||||||
class ESP32CryptoSource: CryptoSource {
|
class ESP32CryptoSource: public CryptoSource {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
#include "relay/interface/StorageSource.h"
|
#include "relay/interface/StorageSource.h"
|
||||||
|
|
||||||
class ESP32StorageSource: StorageSource {
|
class ESP32StorageSource: public StorageSource {
|
||||||
|
|
||||||
bool writeByteAtIndex(uint8_t byte, uint16_t index) override;
|
bool writeByteAtIndex(uint8_t byte, uint16_t index) override;
|
||||||
|
|
||||||
|
@ -10,10 +10,18 @@
|
|||||||
struct PrivateKey {
|
struct PrivateKey {
|
||||||
|
|
||||||
/// @brief The size of a private key
|
/// @brief The size of a private key
|
||||||
static const int size = 32;
|
static constexpr int size = 32;
|
||||||
|
|
||||||
uint8_t bytes[size];
|
uint8_t bytes[size];
|
||||||
|
|
||||||
|
bool isUnset() {
|
||||||
|
for (uint8_t i = 0; i < size; i += 1) {
|
||||||
|
if (bytes[i] != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -22,10 +30,18 @@ struct PrivateKey {
|
|||||||
struct PublicKey {
|
struct PublicKey {
|
||||||
|
|
||||||
/// @brief The size of a public key
|
/// @brief The size of a public key
|
||||||
static const int size = 32;
|
static constexpr int size = 32;
|
||||||
|
|
||||||
uint8_t bytes[size];
|
uint8_t bytes[size];
|
||||||
|
|
||||||
|
bool isUnset() {
|
||||||
|
for (uint8_t i = 0; i < size; i += 1) {
|
||||||
|
if (bytes[i] != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,7 +50,7 @@ struct PublicKey {
|
|||||||
struct Signature {
|
struct Signature {
|
||||||
|
|
||||||
/// @brief The size of a message signature
|
/// @brief The size of a message signature
|
||||||
static const int size = 64;
|
static constexpr int size = 64;
|
||||||
|
|
||||||
uint8_t bytes[size];
|
uint8_t bytes[size];
|
||||||
|
|
||||||
|
@ -46,14 +46,16 @@ class ServerConnection {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
ServerConnection();
|
ServerConnection(ServerConfiguration configuration);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Set the configuration and the callback handler
|
* @brief Set the configuration and the callback handler
|
||||||
*
|
*
|
||||||
* @param callback The handler to handle messages and errors
|
* @param callback The handler to handle messages and errors
|
||||||
*/
|
*/
|
||||||
void configure(ServerConfiguration configuration, ServerConnectionCallbacks* callbacks);
|
void setCallbacks(ServerConnectionCallbacks* callbacks);
|
||||||
|
|
||||||
|
void shouldConnect(bool connect);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Call this function regularly to handle socket operations.
|
* @brief Call this function regularly to handle socket operations.
|
||||||
@ -73,7 +75,8 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint32_t currentTime;
|
uint32_t currentTime = 0;
|
||||||
|
bool shouldBeConnected = false;
|
||||||
|
|
||||||
bool socketIsConnected() {
|
bool socketIsConnected() {
|
||||||
return webSocket.isConnected();
|
return webSocket.isConnected();
|
||||||
@ -81,15 +84,12 @@ private:
|
|||||||
|
|
||||||
void connect();
|
void connect();
|
||||||
|
|
||||||
void disconnect();
|
|
||||||
|
|
||||||
bool shouldReconnect = true;
|
|
||||||
bool isConnecting = false;
|
bool isConnecting = false;
|
||||||
|
bool isDisconnecting = false;
|
||||||
uint32_t connectionTimeout = 0;
|
uint32_t connectionTimeout = 0;
|
||||||
uint32_t nextReconnectAttemptMs = 0;
|
uint32_t nextReconnectAttemptMs = 0;
|
||||||
|
|
||||||
void didDisconnect();
|
void didChangeConnectionState(bool isConnected);
|
||||||
void didConnect();
|
|
||||||
|
|
||||||
|
|
||||||
ServerConfiguration configuration;
|
ServerConfiguration configuration;
|
||||||
|
@ -52,20 +52,15 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Construct a new servo controller
|
* @brief Construct a new servo controller
|
||||||
*/
|
|
||||||
ServoController();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Configure the servo
|
|
||||||
*
|
*
|
||||||
* @param The configuration for the servo
|
* @param The configuration for the servo
|
||||||
*/
|
*/
|
||||||
void configure(ServoConfiguration configuration);
|
ServoController(ServoConfiguration configuration);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Update the servo state periodically
|
* @brief Update the servo state periodically
|
||||||
*
|
*
|
||||||
* This function should be periodically called to update the servo state,
|
* This function will be periodically called to update the servo state,
|
||||||
* specifically to release the button after the opening time has elapsed.
|
* specifically to release the button after the opening time has elapsed.
|
||||||
*
|
*
|
||||||
* There is no required interval to call this function, but the accuracy of
|
* There is no required interval to call this function, but the accuracy of
|
||||||
@ -78,16 +73,14 @@ public:
|
|||||||
*/
|
*/
|
||||||
void pressButton();
|
void pressButton();
|
||||||
|
|
||||||
/**
|
|
||||||
* Release the door opener button by moving the servo arm.
|
|
||||||
*/
|
|
||||||
void releaseButton();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Indicator that the door button is pushed
|
// Indicator that the door button is pushed
|
||||||
bool buttonIsPressed = false;
|
bool buttonIsPressed = false;
|
||||||
|
|
||||||
|
// Indicate that the button should be pressed
|
||||||
|
bool shouldPressButton = false;
|
||||||
|
|
||||||
uint32_t openDuration = 0;
|
uint32_t openDuration = 0;
|
||||||
|
|
||||||
int pressedValue = 0;
|
int pressedValue = 0;
|
||||||
@ -103,4 +96,12 @@ private:
|
|||||||
// PWM Module needed for the servo
|
// PWM Module needed for the servo
|
||||||
ESP32PWM pwm;
|
ESP32PWM pwm;
|
||||||
|
|
||||||
|
// The task on core 1 that resets the servo
|
||||||
|
TaskHandle_t servoResetTask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the door opener button by moving the servo arm.
|
||||||
|
*/
|
||||||
|
void releaseButton();
|
||||||
|
|
||||||
};
|
};
|
@ -13,9 +13,7 @@ platform = espressif32
|
|||||||
board = az-delivery-devkit-v4
|
board = az-delivery-devkit-v4
|
||||||
framework = arduino
|
framework = arduino
|
||||||
lib_deps =
|
lib_deps =
|
||||||
; links2004/WebSockets@^2.4.0
|
|
||||||
madhephaestus/ESP32Servo@^1.1.0
|
madhephaestus/ESP32Servo@^1.1.0
|
||||||
ottowinter/ESPAsyncWebServer-esphome@^3.0.0
|
|
||||||
arduino-libraries/Ethernet@^2.0.2
|
arduino-libraries/Ethernet@^2.0.2
|
||||||
https://github.com/christophhagen/arduinoWebSockets#master
|
https://github.com/christophhagen/arduinoWebSockets#master
|
||||||
rweather/Crypto@^0.4.0
|
rweather/Crypto@^0.4.0
|
||||||
|
@ -5,88 +5,172 @@
|
|||||||
#include <SPI.h>
|
#include <SPI.h>
|
||||||
#include <Ethernet.h>
|
#include <Ethernet.h>
|
||||||
|
|
||||||
SesameController::SesameController(uint16_t localWebServerPort) : localWebServer(localWebServerPort) {
|
SesameController::SesameController(ServoConfiguration servoConfig, ServerConfiguration serverConfig, EthernetConfiguration ethernetConfig, KeyConfiguration keyConfig)
|
||||||
|
: ethernetConfig(ethernetConfig), keyConfig(keyConfig), servo(servoConfig), server(serverConfig) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SesameController::configure(ServoConfiguration servoConfig, ServerConfiguration serverConfig, EthernetConfiguration ethernetConfig, KeyConfiguration keyConfig) {
|
void SesameController::initializeSpiBusForEthernetModule() {
|
||||||
this->ethernetConfig = ethernetConfig;
|
|
||||||
this->keyConfig = keyConfig;
|
|
||||||
|
|
||||||
// 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
|
SPI.begin(ethernetConfig.spiPinSclk, ethernetConfig.spiPinMiso, ethernetConfig.spiPinMosi, ethernetConfig.spiPinSS); //SCLK, MISO, MOSI, SS
|
||||||
pinMode(ethernetConfig.spiPinSS, OUTPUT);
|
pinMode(ethernetConfig.spiPinSS, OUTPUT);
|
||||||
|
|
||||||
Ethernet.init(ethernetConfig.spiPinSS);
|
Ethernet.init(ethernetConfig.spiPinSS);
|
||||||
|
|
||||||
if (Ethernet.begin(ethernetConfig.macAddress, ethernetConfig.dhcpLeaseTimeoutMs, ethernetConfig.dhcpLeaseResponseTimeoutMs) == 1) {
|
|
||||||
Serial.print("[INFO] DHCP assigned IP ");
|
|
||||||
Serial.println(Ethernet.localIP());
|
|
||||||
ethernetIsConfigured = true;
|
|
||||||
} else {
|
|
||||||
// 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.");
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void SesameController::loop(uint32_t millis) {
|
void SesameController::loop(uint32_t millis) {
|
||||||
currentTime = millis;
|
currentTime = millis;
|
||||||
server.loop(millis);
|
|
||||||
servo.loop(millis);
|
switch (status) {
|
||||||
|
case SesameDeviceStatus::initial:
|
||||||
|
// In initial state, first configure SPI and
|
||||||
|
enableCrypto(); // Ensure source of random numbers without WiFi and Bluetooth
|
||||||
|
initializeSpiBusForEthernetModule();
|
||||||
|
// Direct messages and errors over the websocket to the controller
|
||||||
|
server.setCallbacks(this);
|
||||||
|
configureEthernet();
|
||||||
|
status = SesameDeviceStatus::configuredButNoEthernetHardware;
|
||||||
|
Serial.println("[INFO] State: initial -> noHardware");
|
||||||
|
// Directly check for ethernet hardware
|
||||||
|
// break;
|
||||||
|
case SesameDeviceStatus::configuredButNoEthernetHardware:
|
||||||
|
if (!hasAvailableEthernetHardware()) {
|
||||||
|
// No ethernet hardware found, wait
|
||||||
|
// TODO: Try rebooting after some time as a potential fix?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
status = SesameDeviceStatus::ethernetHardwareButNoLink;
|
||||||
|
Serial.println("[INFO] State: noHardware -> noLink");
|
||||||
|
// Directly check for Ethernet link
|
||||||
|
// break;
|
||||||
|
case SesameDeviceStatus::ethernetHardwareButNoLink:
|
||||||
|
if (!hasEthernetLink()) {
|
||||||
|
if (!hasAvailableEthernetHardware()) {
|
||||||
|
status = SesameDeviceStatus::configuredButNoEthernetHardware;
|
||||||
|
Serial.println("[INFO] State: noLink -> noHardware");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Wait for ethernet link
|
||||||
|
// TODO: Try rebooting after some time as a potential fix?
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
status = SesameDeviceStatus::ethernetLinkButNoIP;
|
||||||
|
Serial.println("[INFO] State: noLink -> noIP");
|
||||||
|
// Directly check for socket connection
|
||||||
|
// break;
|
||||||
|
|
||||||
|
case SesameDeviceStatus::ethernetLinkButNoIP:
|
||||||
|
if (!hasEthernetLink()) {
|
||||||
|
if (hasAvailableEthernetHardware()) {
|
||||||
|
status = SesameDeviceStatus::ethernetHardwareButNoLink;
|
||||||
|
Serial.println("[INFO] State: noIP -> noLink");
|
||||||
|
} else {
|
||||||
|
status = SesameDeviceStatus::configuredButNoEthernetHardware;
|
||||||
|
Serial.println("[INFO] State: noIP -> noHardware");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
startUDP();
|
||||||
|
status = SesameDeviceStatus::ipAddressButNoSocketConnection;
|
||||||
|
Serial.println("[INFO] State: noIP -> noSocket");
|
||||||
|
// Directly check for socket connection
|
||||||
|
// break;
|
||||||
|
|
||||||
|
case SesameDeviceStatus::ipAddressButNoSocketConnection:
|
||||||
|
if (!hasEthernetLink()) {
|
||||||
|
server.shouldConnect(false);
|
||||||
|
stopUDP();
|
||||||
|
if (!hasAvailableEthernetHardware()) {
|
||||||
|
status = SesameDeviceStatus::configuredButNoEthernetHardware;
|
||||||
|
Serial.println("[INFO] State: noSocket -> noHardware");
|
||||||
|
} else {
|
||||||
|
status = SesameDeviceStatus::ethernetHardwareButNoLink;
|
||||||
|
Serial.println("[INFO] State: noSocket -> noLink");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
server.shouldConnect(true);
|
||||||
|
server.loop(millis);
|
||||||
|
checkLocalMessage();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SesameController::hasAvailableEthernetHardware() {
|
||||||
|
EthernetHardwareStatus ethernetStatus = Ethernet.hardwareStatus();
|
||||||
|
|
||||||
|
static bool didNotify = false;
|
||||||
|
|
||||||
|
if (ethernetStatus != EthernetW5500) {
|
||||||
|
if (!didNotify) {
|
||||||
|
Serial.print("[ERROR] No Ethernet hardware found: ");
|
||||||
|
Serial.println(ethernetStatus);
|
||||||
|
didNotify = true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SesameController::hasEthernetLink() {
|
||||||
|
return Ethernet.linkStatus() == EthernetLinkStatus::LinkON;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SesameController::configureEthernet() {
|
||||||
|
if (Ethernet.begin(ethernetConfig.macAddress, ethernetConfig.dhcpLeaseTimeoutMs, ethernetConfig.dhcpLeaseResponseTimeoutMs) == 1) {
|
||||||
|
Serial.print("[INFO] DHCP assigned IP ");
|
||||||
|
Serial.println(Ethernet.localIP());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Ethernet.begin(ethernetConfig.macAddress, ethernetConfig.manualIp, ethernetConfig.manualDnsAddress);
|
||||||
|
Serial.print("[WARNING] DHCP failed, using self-assigned IP ");
|
||||||
|
Serial.println(Ethernet.localIP());
|
||||||
|
}
|
||||||
|
|
||||||
|
void SesameController::startUDP() {
|
||||||
|
udp.begin(ethernetConfig.udpPort);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SesameController::stopUDP() {
|
||||||
|
udp.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Local
|
// MARK: Local
|
||||||
|
|
||||||
void SesameController::handleLocalMessage(AsyncWebServerRequest *request) {
|
void SesameController::checkLocalMessage() {
|
||||||
if (!request->hasParam(messageUrlParameter)) {
|
if (readLocalMessage()) {
|
||||||
Serial.println("Missing url parameter");
|
sendPreparedLocalResponse();
|
||||||
prepareResponseBuffer(MessageResult::InvalidUrlParameter);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
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) {
|
bool SesameController::readLocalMessage() {
|
||||||
request->send_P(200, "application/octet-stream", (uint8_t*) &outgoingMessage, SIGNED_MESSAGE_SIZE);
|
// if there's data available, read a packet
|
||||||
Serial.printf("[INFO] Local response %u,%u\n", outgoingMessage.message.messageType, outgoingMessage.message.result);
|
int packetSize = udp.parsePacket();
|
||||||
|
if (packetSize == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (packetSize != SIGNED_MESSAGE_SIZE) {
|
||||||
|
Serial.print("[WARN] Received UDP 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("[WARN] Failed to read full local message");
|
||||||
|
prepareResponseBuffer(MessageResult::InvalidMessageSizeFromRemote);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
Serial.println("[INFO] 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();
|
||||||
|
Serial.println("[INFO] Sent local response");
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Server
|
// MARK: Server
|
||||||
@ -102,7 +186,7 @@ void SesameController::handleServerMessage(uint8_t* payload, size_t length) {
|
|||||||
sendServerError(MessageResult::InvalidMessageSizeFromRemote);
|
sendServerError(MessageResult::InvalidMessageSizeFromRemote);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processMessage((SignedMessage*) payload);
|
processMessage((SignedMessage*) payload, true);
|
||||||
sendPreparedResponseToServer();
|
sendPreparedResponseToServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,7 +197,7 @@ void SesameController::sendPreparedResponseToServer() {
|
|||||||
|
|
||||||
// MARK: Message handling
|
// MARK: Message handling
|
||||||
|
|
||||||
void SesameController::processMessage(SignedMessage* message) {
|
void SesameController::processMessage(SignedMessage* message, bool shouldPerformUnlock) {
|
||||||
// Result must be empty
|
// Result must be empty
|
||||||
if (message->message.result != MessageResult::MessageAccepted) {
|
if (message->message.result != MessageResult::MessageAccepted) {
|
||||||
prepareResponseBuffer(MessageResult::InvalidMessageResultFromRemote);
|
prepareResponseBuffer(MessageResult::InvalidMessageResultFromRemote);
|
||||||
@ -128,7 +212,7 @@ void SesameController::processMessage(SignedMessage* message) {
|
|||||||
checkAndPrepareChallenge(&message->message);
|
checkAndPrepareChallenge(&message->message);
|
||||||
return;
|
return;
|
||||||
case MessageType::request:
|
case MessageType::request:
|
||||||
completeUnlockRequest(&message->message);
|
completeUnlockRequest(&message->message, shouldPerformUnlock);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
prepareResponseBuffer(MessageResult::InvalidMessageTypeFromRemote);
|
prepareResponseBuffer(MessageResult::InvalidMessageTypeFromRemote);
|
||||||
@ -159,7 +243,7 @@ void SesameController::prepareChallenge(Message* message) {
|
|||||||
prepareResponseBuffer(MessageResult::MessageAccepted, message);
|
prepareResponseBuffer(MessageResult::MessageAccepted, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SesameController::completeUnlockRequest(Message* message) {
|
void SesameController::completeUnlockRequest(Message* message, bool shouldPerformUnlock) {
|
||||||
// Client and server challenge must match
|
// Client and server challenge must match
|
||||||
if (message->clientChallenge != currentClientChallenge) {
|
if (message->clientChallenge != currentClientChallenge) {
|
||||||
prepareResponseBuffer(MessageResult::InvalidClientChallengeFromRemote, message);
|
prepareResponseBuffer(MessageResult::InvalidClientChallengeFromRemote, message);
|
||||||
@ -181,7 +265,9 @@ void SesameController::completeUnlockRequest(Message* message) {
|
|||||||
clearCurrentChallenge();
|
clearCurrentChallenge();
|
||||||
|
|
||||||
// Move servo
|
// Move servo
|
||||||
servo.pressButton();
|
if (shouldPerformUnlock) {
|
||||||
|
servo.pressButton();
|
||||||
|
}
|
||||||
prepareResponseBuffer(MessageResult::MessageAccepted, message);
|
prepareResponseBuffer(MessageResult::MessageAccepted, message);
|
||||||
Serial.println("[INFO] Accepted message");
|
Serial.println("[INFO] Accepted message");
|
||||||
}
|
}
|
||||||
|
81
src/main.cpp
81
src/main.cpp
@ -22,52 +22,51 @@
|
|||||||
#include "controller.h"
|
#include "controller.h"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
SesameController controller(localPort);
|
ServoConfiguration servoConfig {
|
||||||
|
.pwmTimer = pwmTimer,
|
||||||
|
.pwmFrequency = servoFrequency,
|
||||||
|
.pin = servoPin,
|
||||||
|
.openDuration = lockOpeningDuration,
|
||||||
|
.pressedValue = servoPressedState,
|
||||||
|
.releasedValue = servoReleasedState,
|
||||||
|
};
|
||||||
|
|
||||||
|
ServerConfiguration serverConfig {
|
||||||
|
.url = serverUrl,
|
||||||
|
.port = serverPort,
|
||||||
|
.path = serverPath,
|
||||||
|
.key = serverAccessKey,
|
||||||
|
.reconnectTime = 5000,
|
||||||
|
.socketHeartbeatIntervalMs = socketHeartbeatIntervalMs,
|
||||||
|
.socketHeartbeatTimeoutMs = socketHeartbeatTimeoutMs,
|
||||||
|
.socketHeartbeatFailureReconnectCount = socketHeartbeatFailureReconnectCount,
|
||||||
|
};
|
||||||
|
|
||||||
|
EthernetConfiguration ethernetConfig {
|
||||||
|
.macAddress = ethernetMacAddress,
|
||||||
|
.spiPinMiso = spiPinMiso,
|
||||||
|
.spiPinMosi = spiPinMosi,
|
||||||
|
.spiPinSclk = spiPinSclk,
|
||||||
|
.spiPinSS = spiPinSS,
|
||||||
|
.dhcpLeaseTimeoutMs = dhcpLeaseTimeoutMs,
|
||||||
|
.dhcpLeaseResponseTimeoutMs = dhcpLeaseResponseTimeoutMs,
|
||||||
|
.manualIp = manualIpAddress,
|
||||||
|
.manualDnsAddress = manualDnsServerAddress,
|
||||||
|
.udpPort = localUdpPort,
|
||||||
|
};
|
||||||
|
|
||||||
|
KeyConfiguration keyConfig {
|
||||||
|
.remoteKey = remoteKey,
|
||||||
|
.localKey = localKey,
|
||||||
|
.challengeExpiryMs = challengeExpiryMs,
|
||||||
|
};
|
||||||
|
|
||||||
|
SesameController controller(servoConfig, serverConfig, ethernetConfig, keyConfig);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Serial.begin(serialBaudRate);
|
Serial.begin(serialBaudRate);
|
||||||
Serial.setDebugOutput(true);
|
Serial.setDebugOutput(true);
|
||||||
Serial.println("[INFO] Device started");
|
Serial.println("[INFO] Device started");
|
||||||
|
|
||||||
ServoConfiguration servoConfig {
|
|
||||||
.pwmTimer = pwmTimer,
|
|
||||||
.pwmFrequency = servoFrequency,
|
|
||||||
.pin = servoPin,
|
|
||||||
.openDuration = lockOpeningDuration,
|
|
||||||
.pressedValue = servoPressedState,
|
|
||||||
.releasedValue = servoReleasedState,
|
|
||||||
};
|
|
||||||
|
|
||||||
ServerConfiguration serverConfig {
|
|
||||||
.url = serverUrl,
|
|
||||||
.port = serverPort,
|
|
||||||
.path = serverPath,
|
|
||||||
.key = serverAccessKey,
|
|
||||||
.reconnectTime = 5000,
|
|
||||||
.socketHeartbeatIntervalMs = socketHeartbeatIntervalMs,
|
|
||||||
.socketHeartbeatTimeoutMs = socketHeartbeatTimeoutMs,
|
|
||||||
.socketHeartbeatFailureReconnectCount = socketHeartbeatFailureReconnectCount,
|
|
||||||
};
|
|
||||||
|
|
||||||
EthernetConfiguration ethernetConfig {
|
|
||||||
.macAddress = ethernetMacAddress,
|
|
||||||
.spiPinMiso = spiPinMiso,
|
|
||||||
.spiPinMosi = spiPinMosi,
|
|
||||||
.spiPinSclk = spiPinSclk,
|
|
||||||
.spiPinSS = spiPinSS,
|
|
||||||
.dhcpLeaseTimeoutMs = dhcpLeaseTimeoutMs,
|
|
||||||
.dhcpLeaseResponseTimeoutMs = dhcpLeaseResponseTimeoutMs,
|
|
||||||
.manualIp = manualIpAddress,
|
|
||||||
.manualDnsAddress = manualDnsServerAddress,
|
|
||||||
};
|
|
||||||
|
|
||||||
KeyConfiguration keyConfig {
|
|
||||||
.remoteKey = remoteKey,
|
|
||||||
.localKey = localKey,
|
|
||||||
.challengeExpiryMs = challengeExpiryMs,
|
|
||||||
};
|
|
||||||
|
|
||||||
controller.configure(servoConfig, serverConfig, ethernetConfig, keyConfig);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
|
100
src/server.cpp
100
src/server.cpp
@ -1,26 +1,25 @@
|
|||||||
#include "server.h"
|
#include "server.h"
|
||||||
|
|
||||||
constexpr int32_t pingInterval = 10000;
|
ServerConnection::ServerConnection(ServerConfiguration configuration) : configuration(configuration) { }
|
||||||
constexpr uint32_t pongTimeout = 5000;
|
|
||||||
uint8_t disconnectTimeoutCount = 3;
|
|
||||||
|
|
||||||
ServerConnection::ServerConnection() { }
|
void ServerConnection::setCallbacks(ServerConnectionCallbacks *callbacks) {
|
||||||
|
|
||||||
void ServerConnection::configure(ServerConfiguration configuration, ServerConnectionCallbacks *callbacks) {
|
|
||||||
controller = callbacks;
|
controller = callbacks;
|
||||||
this->configuration = configuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerConnection::connect() {
|
void ServerConnection::shouldConnect(bool connect) {
|
||||||
if (webSocket.isConnected()) {
|
if (connect == shouldBeConnected) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controller == NULL) {
|
if (controller == NULL) {
|
||||||
Serial.println("[ERROR] No callbacks set for server");
|
Serial.println("[ERROR] No callbacks set for server");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
shouldBeConnected = connect;
|
||||||
|
nextReconnectAttemptMs = currentTime;
|
||||||
|
}
|
||||||
|
|
||||||
isConnecting = true;
|
void ServerConnection::connect() {
|
||||||
Serial.printf("[INFO] Connecting to %s:%d%s\n", configuration.url, configuration.port, configuration.path);
|
Serial.printf("[INFO] Connecting to %s:%d%s\n", configuration.url, configuration.port, configuration.path);
|
||||||
connectionTimeout = currentTime + configuration.socketHeartbeatIntervalMs;
|
connectionTimeout = currentTime + configuration.socketHeartbeatIntervalMs;
|
||||||
webSocket.begin(configuration.url, configuration.port, configuration.path);
|
webSocket.begin(configuration.url, configuration.port, configuration.path);
|
||||||
@ -33,47 +32,76 @@ void ServerConnection::connect() {
|
|||||||
webSocket.setReconnectInterval(configuration.reconnectTime);
|
webSocket.setReconnectInterval(configuration.reconnectTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerConnection::didDisconnect() {
|
void ServerConnection::didChangeConnectionState(bool isConnected) {
|
||||||
if (shouldReconnect || isConnecting) {
|
static bool wasConnected = false;
|
||||||
return; // Disconnect already registered.
|
if (isConnected) {
|
||||||
|
Serial.println("[INFO] Socket connected, enabling heartbeat");
|
||||||
|
isConnecting = false;
|
||||||
|
webSocket.enableHeartbeat(configuration.socketHeartbeatIntervalMs, configuration.socketHeartbeatTimeoutMs, configuration.socketHeartbeatFailureReconnectCount);
|
||||||
|
} else {
|
||||||
|
isDisconnecting = false;
|
||||||
|
if (!wasConnected && shouldBeConnected && nextReconnectAttemptMs < currentTime) {
|
||||||
|
nextReconnectAttemptMs = currentTime + configuration.socketHeartbeatIntervalMs;
|
||||||
|
Serial.println("[INFO] Socket disconnected, setting reconnect time");
|
||||||
|
} else if (wasConnected) {
|
||||||
|
Serial.println("[INFO] Socket disconnected");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Serial.println("[INFO] Socket disconnected");
|
wasConnected = isConnected;
|
||||||
nextReconnectAttemptMs = currentTime + configuration.socketHeartbeatIntervalMs;
|
|
||||||
shouldReconnect = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerConnection::didConnect() {
|
|
||||||
isConnecting = false;
|
|
||||||
Serial.println("[INFO] Socket connected");
|
|
||||||
webSocket.enableHeartbeat(configuration.socketHeartbeatIntervalMs, configuration.socketHeartbeatTimeoutMs, configuration.socketHeartbeatFailureReconnectCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ServerConnection::disconnect() {
|
|
||||||
webSocket.disconnect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServerConnection::loop(uint32_t millis) {
|
void ServerConnection::loop(uint32_t millis) {
|
||||||
currentTime = millis;
|
currentTime = millis;
|
||||||
webSocket.loop();
|
webSocket.loop();
|
||||||
if (shouldReconnect && !isConnecting) {
|
|
||||||
shouldReconnect = false;
|
if (shouldBeConnected) {
|
||||||
|
if (isDisconnecting) {
|
||||||
|
return; // Wait for disconnect to finish, then it will be reconnected
|
||||||
|
}
|
||||||
|
if (isConnecting) {
|
||||||
|
if (millis > connectionTimeout) {
|
||||||
|
// Cancel connection attempt
|
||||||
|
Serial.println("[INFO] Canceling socket connection attempt");
|
||||||
|
isDisconnecting = true;
|
||||||
|
isConnecting = false;
|
||||||
|
webSocket.disconnect();
|
||||||
|
}
|
||||||
|
return; // Wait for connect to finish
|
||||||
|
}
|
||||||
|
if (webSocket.isConnected()) {
|
||||||
|
return; // Already connected
|
||||||
|
}
|
||||||
|
if (controller == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (millis < nextReconnectAttemptMs) {
|
||||||
|
return; // Wait for next reconnect
|
||||||
|
}
|
||||||
|
isConnecting = true;
|
||||||
connect();
|
connect();
|
||||||
}
|
} else {
|
||||||
if (isConnecting && millis > connectionTimeout) {
|
if (isDisconnecting) {
|
||||||
Serial.println("[INFO] Failed to connect");
|
return; // Wait for disconnect
|
||||||
disconnect();
|
}
|
||||||
shouldReconnect = true;
|
if (isConnecting) {
|
||||||
isConnecting = false;
|
return; // Wait until connection is established, then it will be disconnected
|
||||||
|
}
|
||||||
|
if (!webSocket.isConnected()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
isDisconnecting = true;
|
||||||
|
Serial.println("[INFO] Disconnecting socket");
|
||||||
|
webSocket.disconnect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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:
|
||||||
didDisconnect();
|
didChangeConnectionState(false);
|
||||||
break;
|
break;
|
||||||
case WStype_CONNECTED:
|
case WStype_CONNECTED:
|
||||||
didConnect();
|
didChangeConnectionState(true);
|
||||||
break;
|
break;
|
||||||
case WStype_TEXT:
|
case WStype_TEXT:
|
||||||
controller->sendServerError(MessageResult::TextReceivedOverSocket);
|
controller->sendServerError(MessageResult::TextReceivedOverSocket);
|
||||||
|
@ -2,9 +2,28 @@
|
|||||||
|
|
||||||
#include <esp32-hal.h> // For `millis()`
|
#include <esp32-hal.h> // For `millis()`
|
||||||
|
|
||||||
ServoController::ServoController() { }
|
void performReset(void * pvParameters) {
|
||||||
|
ServoController* servo = (ServoController *) pvParameters;
|
||||||
|
for(;;){
|
||||||
|
servo->loop(millis());
|
||||||
|
delay(50);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ServoController::ServoController(ServoConfiguration configuration) {
|
||||||
|
|
||||||
|
// Create a task that runs on a different core,
|
||||||
|
// So that it's always executed
|
||||||
|
xTaskCreatePinnedToCore(
|
||||||
|
performReset, /* Task function. */
|
||||||
|
"Servo", /* name of task. */
|
||||||
|
1000, /* Stack size of task */
|
||||||
|
this, /* parameter of the task */
|
||||||
|
1, /* priority of the task */
|
||||||
|
&servoResetTask,
|
||||||
|
1); /* pin task to core 1 */
|
||||||
|
|
||||||
|
|
||||||
void ServoController::configure(ServoConfiguration configuration) {
|
|
||||||
openDuration = configuration.openDuration;
|
openDuration = configuration.openDuration;
|
||||||
pressedValue = configuration.pressedValue;
|
pressedValue = configuration.pressedValue;
|
||||||
releasedValue = configuration.releasedValue;
|
releasedValue = configuration.releasedValue;
|
||||||
@ -15,9 +34,7 @@ void ServoController::configure(ServoConfiguration configuration) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServoController::pressButton() {
|
void ServoController::pressButton() {
|
||||||
servo.write(pressedValue);
|
shouldPressButton = true;
|
||||||
buttonIsPressed = true;
|
|
||||||
openingEndTime = millis() + openDuration;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ServoController::releaseButton() {
|
void ServoController::releaseButton() {
|
||||||
@ -26,7 +43,12 @@ void ServoController::releaseButton() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ServoController::loop(uint32_t millis) {
|
void ServoController::loop(uint32_t millis) {
|
||||||
if (buttonIsPressed && millis > openingEndTime) {
|
if (shouldPressButton) {
|
||||||
|
servo.write(pressedValue);
|
||||||
|
openingEndTime = millis + openDuration;
|
||||||
|
buttonIsPressed = true;
|
||||||
|
shouldPressButton = false;
|
||||||
|
} else if (buttonIsPressed && millis > openingEndTime) {
|
||||||
releaseButton();
|
releaseButton();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user