mirror of
https://github.com/EyeTrackVR/OpenIris.git
synced 2025-09-26 23:29:14 +08:00
feat: Add a PoC implementation of command manager responding to commands send over serial. (#59)
* Add a PoC implementation of command manager responding to commands send over serial. TODO: - refactor web API to make use of the command handler - implement the rest of the commands - remove handwritten json in favour of ArduinoJSON * Move ssid check to iniSTA method
This commit is contained in:
parent
7e459da882
commit
54e4e0abd9
@ -9,6 +9,9 @@ custom_firmware_version = 2.2.11
|
||||
monitor_rts = 0
|
||||
monitor_dtr = 0
|
||||
monitor_filters =
|
||||
--echo
|
||||
--eol
|
||||
send_on_enter
|
||||
log2file
|
||||
time
|
||||
default
|
||||
@ -23,6 +26,7 @@ lib_deps =
|
||||
esp32-camera
|
||||
https://github.com/me-no-dev/ESPAsyncWebServer.git
|
||||
https://github.com/me-no-dev/AsyncTCP.git
|
||||
https://github.com/bblanchon/ArduinoJson.git
|
||||
extra_scripts =
|
||||
pre:tools/customname.py
|
||||
post:tools/createzip.py
|
||||
|
55
ESP/lib/src/data/CommandManager/CommandManager.cpp
Normal file
55
ESP/lib/src/data/CommandManager/CommandManager.cpp
Normal file
@ -0,0 +1,55 @@
|
||||
#include "CommandManager.hpp"
|
||||
|
||||
CommandManager::CommandManager(ProjectConfig *deviceConfig) : deviceConfig(deviceConfig) {}
|
||||
|
||||
|
||||
const CommandType CommandManager::getCommandType(Command &command){
|
||||
if (!command.data.containsKey("command"))
|
||||
return CommandType::None;
|
||||
|
||||
if (auto search = commandMap.find(command.data["command"]); search != commandMap.end())
|
||||
return search->second;
|
||||
|
||||
return CommandType::None;
|
||||
}
|
||||
|
||||
|
||||
void CommandManager::handleCommand(Command command) {
|
||||
auto command_type = this->getCommandType(command);
|
||||
|
||||
if (!command.data.containsKey("data"))
|
||||
// malformed command, lacked data field
|
||||
return;
|
||||
|
||||
switch(command_type)
|
||||
{
|
||||
case CommandType::SET_WIFI: {
|
||||
if(!command.data["data"].containsKey("ssid") || !command.data["data"].containsKey("password"))
|
||||
break;
|
||||
|
||||
std::string customNetworkName = "main";
|
||||
if (command.data["data"].containsKey("network_name"))
|
||||
customNetworkName = command.data["data"]["network_name"].as<std::string>();
|
||||
|
||||
this->deviceConfig->setWifiConfig(
|
||||
customNetworkName,
|
||||
command.data["data"]["ssid"],
|
||||
command.data["data"]["password"],
|
||||
0, // channel, should this be zero?
|
||||
0, // power, should this be zero?
|
||||
false,
|
||||
false
|
||||
);
|
||||
|
||||
// we purposefully save here
|
||||
this->deviceConfig->save();
|
||||
break;
|
||||
}
|
||||
case CommandType::PING: {
|
||||
Serial.println("PONG \n\r");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
35
ESP/lib/src/data/CommandManager/CommandManager.hpp
Normal file
35
ESP/lib/src/data/CommandManager/CommandManager.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
#ifndef TASK_MANAGER_HPP
|
||||
#define TASK_MANAGER_HPP
|
||||
#include <ArduinoJson.h>
|
||||
#include <unordered_map>
|
||||
#include "data/config/project_config.hpp"
|
||||
|
||||
enum CommandType {
|
||||
None,
|
||||
PING,
|
||||
SET_WIFI,
|
||||
};
|
||||
|
||||
|
||||
struct Command {
|
||||
JsonDocument data;
|
||||
};
|
||||
|
||||
class CommandManager{
|
||||
|
||||
private:
|
||||
const std::unordered_map<std::string, CommandType> commandMap = {
|
||||
{"ping", CommandType::PING},
|
||||
{"set_wifi", CommandType::SET_WIFI},
|
||||
};
|
||||
|
||||
ProjectConfig* deviceConfig;
|
||||
|
||||
public:
|
||||
CommandManager(ProjectConfig *deviceConfig);
|
||||
void handleCommand(Command command);
|
||||
const CommandType getCommandType(Command &command);
|
||||
};
|
||||
|
||||
#endif
|
83
ESP/lib/src/io/Serial/SerialManager.cpp
Normal file
83
ESP/lib/src/io/Serial/SerialManager.cpp
Normal file
@ -0,0 +1,83 @@
|
||||
#include "SerialManager.hpp"
|
||||
|
||||
SerialManager::SerialManager(CommandManager* commandManager)
|
||||
: commandManager(commandManager) {}
|
||||
|
||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
||||
void SerialManager::send_frame() {
|
||||
// if we failed to capture the frame, we bail, but we still want to listen to commands
|
||||
if (err != ESP_OK)
|
||||
return;
|
||||
|
||||
if (!last_frame)
|
||||
last_frame = esp_timer_get_time();
|
||||
|
||||
uint8_t len_bytes[2];
|
||||
|
||||
size_t len = 0;
|
||||
uint8_t* buf = NULL;
|
||||
|
||||
auto fb = esp_camera_fb_get();
|
||||
if (fb) {
|
||||
len = fb->len;
|
||||
buf = fb->buf;
|
||||
} else {
|
||||
log_e("Camera capture failed with response: %s", esp_err_to_name(err));
|
||||
err = ESP_FAIL;
|
||||
}
|
||||
|
||||
if (err == ESP_OK)
|
||||
Serial.write(ETVR_HEADER, 2);
|
||||
|
||||
Serial.write(ETVR_HEADER_FRAME, 2);
|
||||
len_bytes[0] = len & 0xFF;
|
||||
len_bytes[1] = (len >> CHAR_BIT) & 0xFF;
|
||||
Serial.write(len_bytes, 2);
|
||||
Serial.write((const char*)buf, len);
|
||||
|
||||
if (fb) {
|
||||
esp_camera_fb_return(fb);
|
||||
fb = NULL;
|
||||
buf = NULL;
|
||||
} else if (buf) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
long request_end = millis();
|
||||
long latency = request_end - last_request_time;
|
||||
last_request_time = request_end;
|
||||
log_d("Size: %uKB, Time: %ums (%ifps)\n", len / 1024, latency,
|
||||
1000 / latency);
|
||||
}
|
||||
#endif
|
||||
|
||||
void SerialManager::init() {
|
||||
Serial.begin(3000000);
|
||||
Serial.flush();
|
||||
}
|
||||
|
||||
void SerialManager::run() {
|
||||
while (true) {
|
||||
// we're getting a command, read it whole and process.
|
||||
// otherwise, send a frame if we're supposed to
|
||||
if (Serial.available()) {
|
||||
JsonDocument doc;
|
||||
DeserializationError deserializationError = deserializeJson(doc, Serial);
|
||||
|
||||
if (deserializationError) {
|
||||
log_e("Command deserialization failed: %s",
|
||||
deserializationError.c_str());
|
||||
continue;
|
||||
}
|
||||
|
||||
Command command = {doc};
|
||||
this->commandManager->handleCommand(command);
|
||||
}
|
||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
||||
else {
|
||||
this->send_frame();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
33
ESP/lib/src/io/Serial/SerialManager.hpp
Normal file
33
ESP/lib/src/io/Serial/SerialManager.hpp
Normal file
@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#ifndef SERIAL_MANAGER
|
||||
#define SERIAL_MANAGER
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <USBCDC.h>
|
||||
#include <esp_camera.h>
|
||||
#include "data/CommandManager/CommandManager.hpp"
|
||||
#include "data/config/project_config.hpp"
|
||||
|
||||
const char* const ETVR_HEADER = "\xff\xa0";
|
||||
const char* const ETVR_HEADER_FRAME = "\xff\xa1";
|
||||
|
||||
class SerialManager {
|
||||
private:
|
||||
esp_err_t err = ESP_OK;
|
||||
CommandManager* commandManager;
|
||||
|
||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
||||
int64_t last_frame = 0;
|
||||
long last_request_time = 0;
|
||||
|
||||
void send_frame();
|
||||
#endif
|
||||
|
||||
public:
|
||||
SerialManager(CommandManager* commandManager);
|
||||
void init();
|
||||
void run();
|
||||
};
|
||||
|
||||
#endif
|
@ -6,8 +6,7 @@ MDNSHandler::MDNSHandler(ProjectConfig& configManager)
|
||||
bool MDNSHandler::startMDNS() {
|
||||
const std::string service = "_openiristracker";
|
||||
auto mdnsConfig = configManager.getMDNSConfig();
|
||||
if (!MDNS.begin(mdnsConfig.hostname
|
||||
.c_str())) // lowercase only - as this will be the url
|
||||
if (!MDNS.begin(mdnsConfig.hostname.c_str())) // lowercase only - as this will be the url
|
||||
{
|
||||
mdnsStateManager.setState(MDNSState_e::MDNSState_Error);
|
||||
log_e("Error initializing MDNS");
|
||||
|
@ -6,13 +6,14 @@
|
||||
WiFiHandler::WiFiHandler(ProjectConfig& configManager,
|
||||
const std::string& ssid,
|
||||
const std::string& password,
|
||||
uint8_t channel)
|
||||
uint8_t channel,
|
||||
bool enable_adhoc)
|
||||
: configManager(configManager),
|
||||
ssid(std::move(ssid)),
|
||||
password(std::move(password)),
|
||||
channel(channel),
|
||||
power(0),
|
||||
_enable_adhoc(false) {}
|
||||
_enable_adhoc(enable_adhoc) {}
|
||||
|
||||
WiFiHandler::~WiFiHandler() {}
|
||||
|
||||
@ -25,9 +26,7 @@ void WiFiHandler::begin() {
|
||||
return;
|
||||
}
|
||||
|
||||
log_d(
|
||||
"ADHOC is disabled, setting up STA network and checking transmission "
|
||||
"power \n\r");
|
||||
log_d("ADHOC is disabled, setting up STA network and checking transmission power \n\r");
|
||||
auto txpower = configManager.getWiFiTxPowerConfig();
|
||||
log_d("Setting Wifi Power to: %d", txpower.power);
|
||||
log_d("Setting WiFi sleep mode to NONE \n\r");
|
||||
@ -40,10 +39,19 @@ void WiFiHandler::begin() {
|
||||
|
||||
if (networks.empty()) {
|
||||
log_i("No networks found in config, trying the default one \n\r");
|
||||
if (this->iniSTA(this->ssid, this->password, this->channel,
|
||||
(wifi_power_t)txpower.power)) {
|
||||
|
||||
// since networks may not have a password, we only need to check if we have an ssid
|
||||
// bail if we don't
|
||||
if (this->iniSTA(
|
||||
this->ssid,
|
||||
this->password,
|
||||
this->channel,
|
||||
(wifi_power_t)txpower.power
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
log_i(
|
||||
"Could not connect to the hardcoded network, setting up ADHOC "
|
||||
"network \n\r");
|
||||
@ -126,6 +134,12 @@ bool WiFiHandler::iniSTA(const std::string& ssid,
|
||||
const std::string& password,
|
||||
uint8_t channel,
|
||||
wifi_power_t power) {
|
||||
|
||||
if (ssid == ""){
|
||||
log_d("ssid missing, bailing");
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned long currentMillis = millis();
|
||||
unsigned long startingMillis = currentMillis;
|
||||
int connectionTimeout = 45000; // 30 seconds
|
||||
@ -147,8 +161,6 @@ bool WiFiHandler::iniSTA(const std::string& ssid,
|
||||
currentMillis = millis();
|
||||
log_i(".");
|
||||
log_d("Progress: %d \n\r", progress);
|
||||
/* Helpers::update_progress_bar(progress, 100);
|
||||
delay(301); */
|
||||
if ((currentMillis - startingMillis) >= connectionTimeout) {
|
||||
wifiStateManager.setState(WiFiState_e::WiFiState_Error);
|
||||
log_e("Connection to: %s TIMEOUT \n\r", ssid.c_str());
|
||||
|
@ -10,14 +10,13 @@ class WiFiHandler : public IObserver<ConfigState_e> {
|
||||
WiFiHandler(ProjectConfig& configManager,
|
||||
const std::string& ssid,
|
||||
const std::string& password,
|
||||
uint8_t channel);
|
||||
uint8_t channel,
|
||||
bool enable_adhoc);
|
||||
virtual ~WiFiHandler();
|
||||
void begin();
|
||||
void update(ConfigState_e event) override;
|
||||
std::string getName() override;
|
||||
|
||||
bool _enable_adhoc;
|
||||
|
||||
private:
|
||||
void setUpADHOC();
|
||||
void adhoc(const std::string& ssid,
|
||||
@ -30,6 +29,8 @@ class WiFiHandler : public IObserver<ConfigState_e> {
|
||||
|
||||
ProjectConfig& configManager;
|
||||
|
||||
|
||||
bool _enable_adhoc;
|
||||
std::string ssid;
|
||||
std::string password;
|
||||
uint8_t channel;
|
||||
|
@ -7,6 +7,8 @@
|
||||
#include <io/LEDManager/LEDManager.hpp>
|
||||
#include <io/camera/cameraHandler.hpp>
|
||||
#include <logo/logo.hpp>
|
||||
#include <io/Serial/SerialManager.hpp>
|
||||
#include <data/CommandManager/CommandManager.hpp>
|
||||
|
||||
#ifndef ETVR_EYE_TRACKER_USB_API
|
||||
#include <network/api/webserverHandler.hpp>
|
||||
|
@ -1,5 +1,4 @@
|
||||
#include <openiris.hpp>
|
||||
|
||||
/**
|
||||
* @brief ProjectConfig object
|
||||
* @brief This is the main configuration object for the project
|
||||
@ -7,6 +6,8 @@
|
||||
* @param mdnsName The mDNS hostname to use
|
||||
*/
|
||||
ProjectConfig deviceConfig("openiris", MDNS_HOSTNAME);
|
||||
CommandManager commandManager(&deviceConfig);
|
||||
SerialManager serialManager(&commandManager);
|
||||
|
||||
#ifdef CONFIG_CAMERA_MODULE_ESP32S3_XIAO_SENSE
|
||||
LEDManager ledManager(LED_BUILTIN);
|
||||
@ -19,7 +20,7 @@ CameraHandler cameraHandler(deviceConfig);
|
||||
#endif // SIM_ENABLED
|
||||
|
||||
#ifndef ETVR_EYE_TRACKER_USB_API
|
||||
WiFiHandler wifiHandler(deviceConfig, WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL);
|
||||
WiFiHandler wifiHandler(deviceConfig, WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL, ENABLE_ADHOC);
|
||||
MDNSHandler mdnsHandler(deviceConfig);
|
||||
#ifdef SIM_ENABLED
|
||||
APIServer apiServer(deviceConfig, wifiStateManager, "/control");
|
||||
@ -30,9 +31,6 @@ StreamServer streamServer;
|
||||
|
||||
void etvr_eye_tracker_web_init() {
|
||||
log_d("[SETUP]: Starting Network Handler");
|
||||
// deviceConfig.attach(wifiHandler);
|
||||
log_d("[SETUP]: Checking ADHOC Settings");
|
||||
wifiHandler._enable_adhoc = ENABLE_ADHOC;
|
||||
deviceConfig.attach(mdnsHandler);
|
||||
log_d("[SETUP]: Starting WiFi Handler");
|
||||
wifiHandler.begin();
|
||||
@ -78,7 +76,6 @@ void setup() {
|
||||
setCpuFrequencyMhz(240);
|
||||
Serial.begin(115200);
|
||||
Logo::printASCII();
|
||||
// Serial.flush();
|
||||
ledManager.begin();
|
||||
|
||||
#ifndef SIM_ENABLED
|
||||
@ -86,17 +83,16 @@ void setup() {
|
||||
#endif // SIM_ENABLED
|
||||
deviceConfig.load();
|
||||
|
||||
serialManager.init();
|
||||
|
||||
#ifndef ETVR_EYE_TRACKER_USB_API
|
||||
etvr_eye_tracker_web_init();
|
||||
#else // ETVR_EYE_TRACKER_WEB_API
|
||||
WiFi.disconnect(true);
|
||||
etvr_eye_tracker_usb_init();
|
||||
#endif // ETVR_EYE_TRACKER_WEB_API
|
||||
}
|
||||
|
||||
void loop() {
|
||||
ledManager.handleLED();
|
||||
#ifdef ETVR_EYE_TRACKER_USB_API
|
||||
etvr_eye_tracker_usb_loop();
|
||||
#endif // ETVR_EYE_TRACKER_USB_API
|
||||
serialManager.run();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user