feat: Refactor commands and serial manager to support updated commands payload

Refactor commands and serial manager to support updated commands payload with initial PoC of better state logging
This commit is contained in:
Lorow 2024-06-10 18:59:22 +02:00 committed by GitHub
commit 811a9f898a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 164 additions and 180 deletions

View File

@ -1,74 +1,81 @@
#include "CommandManager.hpp"
CommandManager::CommandManager(ProjectConfig *deviceConfig) : deviceConfig(deviceConfig) {}
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;
const CommandType CommandManager::getCommandType(JsonVariant& command) {
if (!command.containsKey("command"))
return CommandType::None;
if (auto search = commandMap.find(command["command"]);
search != commandMap.end())
return search->second;
return CommandType::None;
}
bool CommandManager::hasHasDataField(Command &command) {
return command.data.containsKey("data");
bool CommandManager::hasDataField(JsonVariant& command) {
return command.containsKey("data");
}
void CommandManager::handleCommand(Command command) {
auto command_type = this->getCommandType(command);
void CommandManager::handleCommands(CommandsPayload commandsPayload) {
if (!commandsPayload.data.containsKey("commands")) {
log_e("Json data sent not supported, lacks commands field");
return;
}
switch(command_type)
{
case CommandType::SET_WIFI: {
if (!this->hasHasDataField(command))
// malformed command, lacked data field
break;
for (JsonVariant commandData :
commandsPayload.data["commands"].as<JsonArray>()) {
this->handleCommand(commandData);
}
this->deviceConfig->save();
}
if(!command.data["data"].containsKey("ssid") || !command.data["data"].containsKey("password"))
break;
void CommandManager::handleCommand(JsonVariant command) {
auto command_type = this->getCommandType(command);
std::string customNetworkName = "main";
if (command.data["data"].containsKey("network_name"))
customNetworkName = command.data["data"]["network_name"].as<std::string>();
switch (command_type) {
case CommandType::SET_WIFI: {
if (!this->hasDataField(command))
// malformed command, lacked data field
break;
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::SET_MDNS: {
if (!this->hasHasDataField(command))
break;
if (!command["data"].containsKey("ssid") ||
!command["data"].containsKey("password"))
break;
if(!command.data["data"].containsKey("hostname") || !strlen(command.data["data"]["hostname"]))
break;
std::string customNetworkName = "main";
if (command["data"].containsKey("network_name"))
customNetworkName = command["data"]["network_name"].as<std::string>();
this->deviceConfig->setMDNSConfig(
command.data["data"]["hostname"],
"openiristracker",
false
);
this->deviceConfig->setWifiConfig(customNetworkName,
command["data"]["ssid"],
command["data"]["password"],
0, // channel, should this be zero?
0, // power, should this be zero?
false, false);
break;
}
case CommandType::PING: {
Serial.println("PONG \n\r");
break;
}
default:
break;
break;
}
case CommandType::SET_MDNS: {
if (!this->hasDataField(command))
break;
if (!command["data"].containsKey("hostname") ||
!strlen(command["data"]["hostname"]))
break;
this->deviceConfig->setMDNSConfig(command["data"]["hostname"],
"openiristracker", false);
break;
}
case CommandType::PING: {
Serial.println("PONG \n\r");
break;
}
default:
break;
}
}

View File

@ -6,34 +6,33 @@
#include "data/config/project_config.hpp"
enum CommandType {
None,
PING,
SET_WIFI,
SET_MDNS,
None,
PING,
SET_WIFI,
SET_MDNS,
};
struct Command {
JsonDocument data;
struct CommandsPayload {
JsonDocument data;
};
class CommandManager{
private:
const std::unordered_map<std::string, CommandType> commandMap = {
{"ping", CommandType::PING},
{"set_wifi", CommandType::SET_WIFI},
{"set_mdns", CommandType::SET_MDNS},
};
class CommandManager {
private:
const std::unordered_map<std::string, CommandType> commandMap = {
{"ping", CommandType::PING},
{"set_wifi", CommandType::SET_WIFI},
{"set_mdns", CommandType::SET_MDNS},
};
ProjectConfig* deviceConfig;
ProjectConfig* deviceConfig;
bool hasHasDataField(Command &command);
bool hasDataField(JsonVariant& command);
void handleCommand(JsonVariant command);
const CommandType getCommandType(JsonVariant& command);
public:
CommandManager(ProjectConfig *deviceConfig);
void handleCommand(Command command);
const CommandType getCommandType(Command &command);
public:
CommandManager(ProjectConfig* deviceConfig);
void handleCommands(CommandsPayload commandsPayload);
};
#endif

View File

@ -1,14 +1,12 @@
#include "SerialManager.hpp"
SerialManager::SerialManager(CommandManager* commandManager)
: commandManager(commandManager) {}
: commandManager(commandManager) {
this->queryManager = new QueryManager();
}
#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();
@ -21,14 +19,17 @@ void SerialManager::send_frame() {
if (fb) {
len = fb->len;
buf = fb->buf;
} else {
log_e("Camera capture failed with response: %s", esp_err_to_name(err));
} else
err = ESP_FAIL;
// if we failed to capture the frame, we bail, but we still want to listen to
// commands
if (err != ESP_OK) {
log_e("Camera capture failed with response: %s", esp_err_to_name(err));
return;
}
if (err == ESP_OK)
Serial.write(ETVR_HEADER, 2);
Serial.write(ETVR_HEADER, 2);
Serial.write(ETVR_HEADER_FRAME, 2);
len_bytes[0] = len & 0xFF;
len_bytes[1] = (len >> CHAR_BIT) & 0xFF;
@ -54,27 +55,42 @@ void SerialManager::send_frame() {
void SerialManager::init() {
Serial.begin(3000000);
if (SERIAL_FLUSH_ENABLED){
if (SERIAL_FLUSH_ENABLED) {
Serial.flush();
}
this->sendQuery(QueryAction::READY_TO_RECEIVE, QueryStatus::NONE, "");
}
void SerialManager::(QueryAction action,
QueryStatus status,
std::string additional_info) {
JsonDocument doc;
doc["action"] = queryActionMap.at(action);
doc["status"] = status;
doc["additional_info"] = additional_info;
doc.shrinkToFit(); // optional
serializeJson(doc, Serial);
}
void SerialManager::run() {
if (Serial.available()) {
JsonDocument doc;
DeserializationError deserializationError = deserializeJson(doc, Serial);
if (Serial.available()) {
JsonDocument doc;
DeserializationError deserializationError = deserializeJson(doc, Serial);
if (deserializationError) {
log_e("Command deserialization failed: %s",
deserializationError.c_str());
}
if (deserializationError) {
log_e("Command deserialization failed: %s", deserializationError.c_str());
Command command = {doc};
this->commandManager->handleCommand(command);
return;
}
CommandsPayload commands = {doc};
this->commandManager->handleCommands(commands);
}
#ifdef ETVR_EYE_TRACKER_USB_API
else {
this->send_frame();
}
else {
this->send_frame();
}
#endif
}

View File

@ -12,6 +12,24 @@
const char* const ETVR_HEADER = "\xff\xa0";
const char* const ETVR_HEADER_FRAME = "\xff\xa1";
enum QueryAction {
READY_TO_RECEIVE,
PARSE_COMMANDS,
CONNECT_TO_WIFI,
};
enum QueryStatus {
NONE,
SUCCESS,
ERROR,
};
const std::unordered_map<QueryAction, std::string> queryActionMap = {
{QueryAction::READY_TO_RECEIVE, "ready_to_receive"},
{QueryAction::PARSE_COMMANDS, "parse_commands"},
{QueryAction::CONNECT_TO_WIFI, "connect_to_wifi"},
};
class SerialManager {
private:
esp_err_t err = ESP_OK;
@ -26,6 +44,9 @@ class SerialManager {
public:
SerialManager(CommandManager* commandManager);
void sendQuery(QueryAction action,
QueryStatus status,
std::string additional_info);
void init();
void run();
};

View File

@ -18,6 +18,13 @@ WiFiHandler::WiFiHandler(ProjectConfig& configManager,
WiFiHandler::~WiFiHandler() {}
void WiFiHandler::begin() {
// just to be sure, we reeset everything before we do anything, some boards were having problems otherwise
WiFi.disconnect();
// we purposefully set the lowest min required security level, some boards have problems connecting otherwise
// https://github.com/espressif/arduino-esp32/issues/8770
WiFi.setMinSecurity(WIFI_AUTH_WEP);
log_i("Starting WiFi Handler \n\r");
if (this->_enable_adhoc ||
wifiStateManager.getCurrentState() == WiFiState_e::WiFiState_ADHOC) {
@ -40,8 +47,6 @@ void WiFiHandler::begin() {
if (networks.empty()) {
log_i("No networks found in config, trying the default one \n\r");
// 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,
@ -135,6 +140,8 @@ bool WiFiHandler::iniSTA(const std::string& ssid,
uint8_t channel,
wifi_power_t power) {
// since networks may not have a password, we only need to check if we have an ssid
// bail if we don't
if (ssid == ""){
log_d("ssid missing, bailing");
return false;
@ -148,13 +155,14 @@ bool WiFiHandler::iniSTA(const std::string& ssid,
wifiStateManager.setState(WiFiState_e::WiFiState_Connecting);
log_i("Trying to connect to: %s \n\r", ssid.c_str());
auto mdnsConfig = configManager.getMDNSConfig();
log_d("Setting hostname %s \n\r");
WiFi.config(INADDR_NONE, INADDR_NONE, INADDR_NONE,
INADDR_NONE); // need to call before setting hostname
log_d("Setting hostname %s \n\r");
WiFi.setHostname(mdnsConfig.hostname.c_str());
log_i("Setting TX power to: %d \n\r", (uint8_t)power);
WiFi.setTxPower(power); // https://github.com/espressif/arduino-esp32/issues/5698
WiFi.begin(ssid.c_str(), password.c_str(), channel);
WiFi.setTxPower(power);
log_d("Waiting for WiFi to connect... \n\r");
while (WiFi.status() != WL_CONNECTED) {
progress++;
@ -169,7 +177,6 @@ bool WiFiHandler::iniSTA(const std::string& ssid,
}
wifiStateManager.setState(WiFiState_e::WiFiState_Connected);
log_i("Successfully connected to %s \n\r", ssid.c_str());
log_i("Setting TX power to: %d \n\r", (uint8_t)power);
return true;
}

View File

@ -3,20 +3,18 @@
#include <Arduino.h>
#include <data/CommandManager/CommandManager.hpp>
#include <data/config/project_config.hpp>
#include <io/LEDManager/LEDManager.hpp>
#include <io/Serial/SerialManager.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>
#include <network/mDNS/MDNSManager.hpp>
#include <network/stream/streamServer.hpp>
#include <network/wifihandler/wifihandler.hpp>
#else
#include <usb/etvr_eye_tracker_usb.hpp>
#endif // ETVR_EYE_TRACKER_WEB_API
#endif // OPENIRIS_HPP

View File

@ -1,61 +0,0 @@
#include "etvr_eye_tracker_usb.hpp"
#include <Arduino.h>
#include <USBCDC.h>
#include <esp_camera.h>
const char* const ETVR_HEADER = "\xff\xa0";
const char* const ETVR_HEADER_FRAME = "\xff\xa1";
void etvr_eye_tracker_usb_init() {
Serial.begin(3000000);
Serial.flush();
}
void etvr_eye_tracker_usb_loop() {
int64_t last_frame = 0;
if (!last_frame)
last_frame = esp_timer_get_time();
long last_request_time = 0;
camera_fb_t* fb = NULL;
esp_err_t err = ESP_OK;
size_t len = 0;
uint8_t* buf = NULL;
uint8_t len_bytes[2];
while (true) {
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;
}
if (err != ESP_OK)
break;
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);
}
}

View File

@ -1,7 +0,0 @@
#ifndef INCLUDED_ETVR_EYE_TRACKER_USB_HPP
#define INCLUDED_ETVR_EYE_TRACKER_USB_HPP
void etvr_eye_tracker_usb_init();
void etvr_eye_tracker_usb_loop();
#endif // INCLUDED_ETVR_EYE_TRACKER_USB_HPP

View File

@ -20,7 +20,11 @@ CameraHandler cameraHandler(deviceConfig);
#endif // SIM_ENABLED
#ifndef ETVR_EYE_TRACKER_USB_API
WiFiHandler wifiHandler(deviceConfig, WIFI_SSID, WIFI_PASSWORD, WIFI_CHANNEL, ENABLE_ADHOC);
WiFiHandler wifiHandler(deviceConfig,
WIFI_SSID,
WIFI_PASSWORD,
WIFI_CHANNEL,
ENABLE_ADHOC);
MDNSHandler mdnsHandler(deviceConfig);
#ifdef SIM_ENABLED
APIServer apiServer(deviceConfig, wifiStateManager, "/control");
@ -83,7 +87,7 @@ void setup() {
#endif // SIM_ENABLED
deviceConfig.load();
serialManager.init();
serialManager.init();
#ifndef ETVR_EYE_TRACKER_USB_API
etvr_eye_tracker_web_init();