-
Notifications
You must be signed in to change notification settings - Fork 43
Add mode switching without reflash #88
base: master
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| PythonExamples/__pycache__/mode_switch_test.cpython-311.pyc |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,13 @@ | ||
| #include "CommandManager.hpp" | ||
| #include "data/DeviceMode/DeviceMode.hpp" | ||
| #include "tasks/tasks.hpp" | ||
|
|
||
|
|
||
| CommandManager::CommandManager(ProjectConfig* deviceConfig) | ||
| : deviceConfig(deviceConfig) {} | ||
|
|
||
| const CommandType CommandManager::getCommandType(JsonVariant& command) { | ||
| if (!command.containsKey("command")) | ||
| if (!command["command"].is<const char*>()) | ||
| return CommandType::None; | ||
|
|
||
| if (auto search = commandMap.find(command["command"]); | ||
|
|
@@ -15,11 +18,11 @@ const CommandType CommandManager::getCommandType(JsonVariant& command) { | |
| } | ||
|
|
||
| bool CommandManager::hasDataField(JsonVariant& command) { | ||
| return command.containsKey("data"); | ||
| return command["data"].is<JsonObject>(); | ||
| } | ||
|
|
||
| void CommandManager::handleCommands(CommandsPayload commandsPayload) { | ||
| if (!commandsPayload.data.containsKey("commands")) { | ||
| if (!commandsPayload.data["commands"].is<JsonArray>()) { | ||
| log_e("Json data sent not supported, lacks commands field"); | ||
| return; | ||
| } | ||
|
|
@@ -41,12 +44,12 @@ void CommandManager::handleCommand(JsonVariant command) { | |
| // malformed command, lacked data field | ||
| break; | ||
|
|
||
| if (!command["data"].containsKey("ssid") || | ||
| !command["data"].containsKey("password")) | ||
| if (!command["data"]["ssid"].is<const char*>() || | ||
| !command["data"]["password"].is<const char*>()) | ||
| break; | ||
|
|
||
| std::string customNetworkName = "main"; | ||
| if (command["data"].containsKey("network_name")) | ||
| if (command["data"]["network_name"].is<const char*>()) | ||
| customNetworkName = command["data"]["network_name"].as<std::string>(); | ||
|
|
||
| this->deviceConfig->setWifiConfig(customNetworkName, | ||
|
|
@@ -55,14 +58,24 @@ void CommandManager::handleCommand(JsonVariant command) { | |
| 0, // channel, should this be zero? | ||
| 0, // power, should this be zero? | ||
| false, false); | ||
|
|
||
| DeviceModeManager* deviceModeManager = DeviceModeManager::getInstance(); | ||
| if (deviceModeManager) { | ||
| deviceModeManager->setHasWiFiCredentials(true); | ||
|
|
||
| deviceModeManager->setMode(DeviceMode::WIFI_MODE); | ||
| log_i("[CommandManager] Switching to WiFi mode after receiving credentials"); | ||
|
|
||
| OpenIrisTasks::ScheduleRestart(2000); | ||
|
||
| } | ||
|
|
||
| break; | ||
| } | ||
| case CommandType::SET_MDNS: { | ||
| if (!this->hasDataField(command)) | ||
| break; | ||
|
|
||
| if (!command["data"].containsKey("hostname") || | ||
| if (!command["data"]["hostname"].is<const char*>() || | ||
| !strlen(command["data"]["hostname"])) | ||
| break; | ||
|
|
||
|
|
@@ -75,6 +88,60 @@ void CommandManager::handleCommand(JsonVariant command) { | |
| Serial.println("PONG \n\r"); | ||
| break; | ||
| } | ||
| case CommandType::SWITCH_MODE: { | ||
| if (!this->hasDataField(command)) | ||
| break; | ||
|
|
||
| if (!command["data"]["mode"].is<int>()) | ||
| break; | ||
|
|
||
| int modeValue = command["data"]["mode"]; | ||
| DeviceMode newMode = static_cast<DeviceMode>(modeValue); | ||
| DeviceMode currentMode; | ||
|
|
||
| DeviceModeManager* deviceModeManager = DeviceModeManager::getInstance(); | ||
| if (deviceModeManager) { | ||
| currentMode = deviceModeManager->getMode(); | ||
|
|
||
| // If switching to USB mode from WiFi or AP mode, disconnect WiFi immediately | ||
| if (newMode == DeviceMode::USB_MODE && | ||
| (currentMode == DeviceMode::WIFI_MODE || currentMode == DeviceMode::AP_MODE)) { | ||
| log_i("[CommandManager] Immediately switching to USB mode"); | ||
| WiFi.disconnect(true); | ||
| } | ||
|
|
||
| deviceModeManager->setMode(newMode); | ||
| log_i("[CommandManager] Switching to mode: %d", modeValue); | ||
|
|
||
| // Only schedule a restart if not switching to USB mode during WiFi/AP initialization | ||
| if (!(newMode == DeviceMode::USB_MODE && | ||
| (currentMode == DeviceMode::WIFI_MODE || currentMode == DeviceMode::AP_MODE) && | ||
| wifiStateManager.getCurrentState() == WiFiState_e::WiFiState_Connecting)) { | ||
| OpenIrisTasks::ScheduleRestart(2000); | ||
| } | ||
| } | ||
|
|
||
| break; | ||
| } | ||
| case CommandType::WIPE_WIFI_CREDS: { | ||
|
|
||
| auto networks = this->deviceConfig->getWifiConfigs(); | ||
| for (auto& network : networks) { | ||
| this->deviceConfig->deleteWifiConfig(network.name, false); | ||
| } | ||
|
|
||
| DeviceModeManager* deviceModeManager = DeviceModeManager::getInstance(); | ||
| if (deviceModeManager) { | ||
|
||
| deviceModeManager->setHasWiFiCredentials(false); | ||
|
|
||
| deviceModeManager->setMode(DeviceMode::USB_MODE); | ||
| log_i("[CommandManager] Switching to USB mode after wiping credentials"); | ||
|
|
||
| OpenIrisTasks::ScheduleRestart(2000); | ||
|
||
| } | ||
|
|
||
| break; | ||
| } | ||
| default: | ||
| break; | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| #include "DeviceMode.hpp" | ||
|
|
||
| DeviceModeManager* DeviceModeManager::instance = nullptr; | ||
|
|
||
| DeviceModeManager::DeviceModeManager() : currentMode(DeviceMode::USB_MODE) {} | ||
|
|
||
| DeviceModeManager::~DeviceModeManager() { | ||
| preferences.end(); | ||
| } | ||
|
|
||
| void DeviceModeManager::init() { | ||
| preferences.begin(PREF_NAMESPACE, false); | ||
|
||
|
|
||
| // Load the saved mode or use default (USB_MODE) | ||
| int savedMode = preferences.getInt(MODE_KEY, static_cast<int>(DeviceMode::AUTO_MODE)); | ||
| currentMode = static_cast<DeviceMode>(savedMode); | ||
|
|
||
| // If in AUTO_MODE, determine the appropriate mode based on saved credentials | ||
| if (currentMode == DeviceMode::AUTO_MODE) { | ||
| currentMode = determineMode(); | ||
| } | ||
|
|
||
| log_i("[DeviceModeManager] Initialized with mode: %d", static_cast<int>(currentMode)); | ||
| } | ||
|
|
||
| DeviceMode DeviceModeManager::getMode() { | ||
| return currentMode; | ||
| } | ||
|
|
||
| void DeviceModeManager::setMode(DeviceMode mode) { | ||
| currentMode = mode; | ||
| preferences.putInt(MODE_KEY, static_cast<int>(mode)); | ||
| log_i("[DeviceModeManager] Mode set to: %d", static_cast<int>(mode)); | ||
| } | ||
|
|
||
| bool DeviceModeManager::hasWiFiCredentials() { | ||
| return preferences.getBool(HAS_WIFI_CREDS_KEY, false); | ||
| } | ||
|
|
||
| void DeviceModeManager::setHasWiFiCredentials(bool hasCredentials) { | ||
| preferences.putBool(HAS_WIFI_CREDS_KEY, hasCredentials); | ||
| log_i("[DeviceModeManager] WiFi credentials status set to: %d", hasCredentials); | ||
| } | ||
|
|
||
| DeviceMode DeviceModeManager::determineMode() { | ||
| // If WiFi credentials are saved, use WiFi mode, otherwise use AP mode | ||
| return hasWiFiCredentials() ? DeviceMode::WIFI_MODE : DeviceMode::AP_MODE; | ||
| } | ||
|
|
||
| DeviceModeManager* DeviceModeManager::getInstance() { | ||
| if (instance == nullptr) { | ||
| createInstance(); | ||
| } | ||
| return instance; | ||
| } | ||
|
|
||
| void DeviceModeManager::createInstance() { | ||
| if (instance == nullptr) { | ||
| instance = new DeviceModeManager(); | ||
| instance->init(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| #pragma once | ||
| #ifndef DEVICE_MODE_HPP | ||
| #define DEVICE_MODE_HPP | ||
|
|
||
| #include <Arduino.h> | ||
| #include <Preferences.h> | ||
| #include <string> | ||
|
|
||
| // Enum to represent the device operating mode | ||
| enum class DeviceMode { | ||
| USB_MODE, // Device operates in USB mode only | ||
| WIFI_MODE, // Device operates in WiFi mode only | ||
| AP_MODE, // Device operates in AP mode with serial commands enabled | ||
| AUTO_MODE // Device automatically selects mode based on saved credentials | ||
| }; | ||
|
|
||
| class DeviceModeManager { | ||
| private: | ||
| static DeviceModeManager* instance; | ||
| Preferences preferences; | ||
| DeviceMode currentMode; | ||
| const char* PREF_NAMESPACE = "device_mode"; | ||
| const char* MODE_KEY = "mode"; | ||
| const char* HAS_WIFI_CREDS_KEY = "has_wifi_creds"; | ||
|
|
||
| public: | ||
| DeviceModeManager(); | ||
| ~DeviceModeManager(); | ||
|
|
||
| static DeviceModeManager* getInstance(); | ||
|
|
||
| static void createInstance(); | ||
|
|
||
| void init(); | ||
|
|
||
| DeviceMode getMode(); | ||
|
|
||
| void setMode(DeviceMode mode); | ||
|
|
||
| bool hasWiFiCredentials(); | ||
|
|
||
| void setHasWiFiCredentials(bool hasCredentials); | ||
|
|
||
| DeviceMode determineMode(); | ||
| }; | ||
|
|
||
| #endif // DEVICE_MODE_HPP |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,9 +1,32 @@ | ||
| #include "SerialManager.hpp" | ||
| #include "data/DeviceMode/DeviceMode.hpp" | ||
|
|
||
| SerialManager::SerialManager(CommandManager* commandManager) | ||
| : commandManager(commandManager) {} | ||
| : commandManager(commandManager) {} | ||
|
|
||
| void SerialManager::sendQuery(QueryAction action, | ||
| QueryStatus status, | ||
| std::string additional_info) { | ||
| JsonDocument doc; | ||
| doc["action"] = queryActionMap.at(action); | ||
| doc["status"] = static_cast<int>(status); | ||
| if (!additional_info.empty()) { | ||
| doc["info"] = additional_info; | ||
| } | ||
|
|
||
| String output; | ||
| serializeJson(doc, output); | ||
| Serial.println(output); | ||
| } | ||
|
|
||
| void SerialManager::checkUSBMode() { | ||
| DeviceMode currentMode = DeviceModeManager::getInstance()->getMode(); | ||
| if (currentMode == DeviceMode::USB_MODE) { | ||
| log_i("[SerialManager] USB mode active - auto-streaming enabled"); | ||
|
|
||
| } | ||
| } | ||
|
|
||
| #ifdef ETVR_EYE_TRACKER_USB_API | ||
| void SerialManager::send_frame() { | ||
| if (!last_frame) | ||
| last_frame = esp_timer_get_time(); | ||
|
|
@@ -49,7 +72,6 @@ void SerialManager::send_frame() { | |
| log_d("Size: %uKB, Time: %ums (%ifps)\n", len / 1024, latency, | ||
| 1000 / latency); | ||
| } | ||
| #endif | ||
|
|
||
| void SerialManager::init() { | ||
| #ifdef SERIAL_MANAGER_USE_HIGHER_FREQUENCY | ||
|
|
@@ -58,25 +80,28 @@ void SerialManager::init() { | |
| if (SERIAL_FLUSH_ENABLED) { | ||
| Serial.flush(); | ||
| } | ||
|
|
||
| // Check if we're in USB mode and set up accordingly | ||
| checkUSBMode(); | ||
| } | ||
|
|
||
| void SerialManager::run() { | ||
| // Process any available commands first to ensure mode changes are detected immediately | ||
| if (Serial.available()) { | ||
| JsonDocument doc; | ||
| DeserializationError deserializationError = deserializeJson(doc, Serial); | ||
|
|
||
| if (deserializationError) { | ||
| log_e("Command deserialization failed: %s", deserializationError.c_str()); | ||
|
|
||
| return; | ||
| } else { | ||
| CommandsPayload commands = {doc}; | ||
| this->commandManager->handleCommands(commands); | ||
|
Comment on lines
+96
to
+98
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why the change? |
||
| } | ||
|
|
||
| CommandsPayload commands = {doc}; | ||
| this->commandManager->handleCommands(commands); | ||
| } | ||
| #ifdef ETVR_EYE_TRACKER_USB_API | ||
| else { | ||
|
|
||
| // Check if we're in USB mode and automatically send frames | ||
| DeviceMode currentMode = DeviceModeManager::getInstance()->getMode(); | ||
| if (currentMode == DeviceMode::USB_MODE) { | ||
|
||
| this->send_frame(); | ||
| } | ||
| #endif | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering, why the change? Is
.is<T>is faster / more memory efficient than.containsKey()?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
command["command"].is<const char*>()it's the recommended approach in ArduinoJson becausecontainsKey()is deprecated lol and i didn't like the warnings