diff --git a/components/CommandManager/CommandManager/CommandManager.cpp b/components/CommandManager/CommandManager/CommandManager.cpp index 64cba54..5068496 100644 --- a/components/CommandManager/CommandManager/CommandManager.cpp +++ b/components/CommandManager/CommandManager/CommandManager.cpp @@ -25,66 +25,70 @@ std::unordered_map commandTypeMap = { {"get_device_mode", CommandType::GET_DEVICE_MODE}, }; -std::function CommandManager::createCommand(const CommandType type, std::string_view json) const { +std::function CommandManager::createCommand(const CommandType type, std::string_view json) const +{ switch (type) { case CommandType::PING: - return { PingCommand }; + return {PingCommand}; case CommandType::PAUSE: - return [json] { - PausePayload payload; - cJSON* root = cJSON_Parse(std::string(json).c_str()); - if (root) { - cJSON* pauseItem = cJSON_GetObjectItem(root, "pause"); - if (pauseItem && cJSON_IsBool(pauseItem)) { - payload.pause = cJSON_IsTrue(pauseItem); - } else { - payload.pause = true; // Default to pause if not specified - } - cJSON_Delete(root); - } else { - payload.pause = true; // Default to pause if parsing fails - } - return PauseCommand(payload); - }; + return [json] + { return PauseCommand(json); }; case CommandType::SET_STREAMING_MODE: - return [this, json] {return setDeviceModeCommand(this->registry, json); }; + return [this, json] + { return setDeviceModeCommand(this->registry, json); }; case CommandType::UPDATE_OTA_CREDENTIALS: - return [this, json] { return updateOTACredentialsCommand(this->registry, json); }; + return [this, json] + { return updateOTACredentialsCommand(this->registry, json); }; case CommandType::SET_WIFI: - return [this, json] { return setWiFiCommand(this->registry, json); }; + return [this, json] + { return setWiFiCommand(this->registry, json); }; case CommandType::UPDATE_WIFI: - return [this, json] { return updateWiFiCommand(this->registry, json); }; + return [this, json] + { return updateWiFiCommand(this->registry, json); }; case CommandType::UPDATE_AP_WIFI: - return [this, json] { return updateAPWiFiCommand(this->registry, json); }; + return [this, json] + { return updateAPWiFiCommand(this->registry, json); }; case CommandType::DELETE_NETWORK: - return [this, json] { return deleteWiFiCommand(this->registry, json); }; + return [this, json] + { return deleteWiFiCommand(this->registry, json); }; case CommandType::SET_MDNS: - return [this, json] { return setMDNSCommand(this->registry, json); }; + return [this, json] + { return setMDNSCommand(this->registry, json); }; case CommandType::UPDATE_CAMERA: - return [this, json] { return updateCameraCommand(this->registry, json); }; + return [this, json] + { return updateCameraCommand(this->registry, json); }; case CommandType::RESTART_CAMERA: - return [this, json] { return restartCameraCommand(this->registry, json); }; + return [this, json] + { return restartCameraCommand(this->registry, json); }; case CommandType::GET_CONFIG: - return [this] { return getConfigCommand(this->registry); }; + return [this] + { return getConfigCommand(this->registry); }; case CommandType::SAVE_CONFIG: - return [this] { return saveConfigCommand(this->registry); }; + return [this] + { return saveConfigCommand(this->registry); }; case CommandType::RESET_CONFIG: - return [this, json] { return resetConfigCommand(this->registry, json); }; + return [this, json] + { return resetConfigCommand(this->registry, json); }; case CommandType::RESTART_DEVICE: return restartDeviceCommand; case CommandType::SCAN_NETWORKS: - return [this] { return scanNetworksCommand(this->registry); }; + return [this] + { return scanNetworksCommand(this->registry); }; case CommandType::START_STREAMING: return startStreamingCommand; case CommandType::GET_WIFI_STATUS: - return [this] { return getWiFiStatusCommand(this->registry); }; + return [this] + { return getWiFiStatusCommand(this->registry); }; case CommandType::CONNECT_WIFI: - return [this] { return connectWiFiCommand(this->registry); }; + return [this] + { return connectWiFiCommand(this->registry); }; case CommandType::SWITCH_MODE: - return [this, json] { return switchModeCommand(this->registry, json); }; + return [this, json] + { return switchModeCommand(this->registry, json); }; case CommandType::GET_DEVICE_MODE: - return [this] { return getDeviceModeCommand(this->registry); }; + return [this] + { return getDeviceModeCommand(this->registry); }; default: return nullptr; } @@ -135,7 +139,7 @@ CommandResult CommandManager::executeFromJson(const std::string_view json) const cJSON_AddItemToArray(responses, response); } - char* jsonString = cJSON_Print(responseDocument); + char *jsonString = cJSON_Print(responseDocument); cJSON_Delete(responseDocument); cJSON_Delete(parsedJson); diff --git a/components/CommandManager/CommandManager/CommandResult.hpp b/components/CommandManager/CommandManager/CommandResult.hpp index b73fc8a..8f45abf 100644 --- a/components/CommandManager/CommandManager/CommandResult.hpp +++ b/components/CommandManager/CommandManager/CommandResult.hpp @@ -22,30 +22,30 @@ public: CommandResult(std::string message, const Status status) { this->status = status; - // Escape quotes and backslashes in the message for JSON std::string escapedMessage = message; size_t pos = 0; // First escape backslashes - while ((pos = escapedMessage.find('\\', pos)) != std::string::npos) { + while ((pos = escapedMessage.find('\\', pos)) != std::string::npos) + { escapedMessage.replace(pos, 1, "\\\\"); pos += 2; } // Then escape quotes pos = 0; - while ((pos = escapedMessage.find('"', pos)) != std::string::npos) { + while ((pos = escapedMessage.find('"', pos)) != std::string::npos) + { escapedMessage.replace(pos, 1, "\\\""); pos += 2; } - + if (status == Status::SUCCESS) { - // we gotta do it this way, because if we define it as { "result": " {} " } it crashes the compiler, lol - this->message = std::format("{}\"result\":\"{}\"{}", "{", escapedMessage, "}"); + this->message = std::format("{{\"result\":\"{}\"}}", escapedMessage); } else { - this->message = std::format("{}\"error\":\"{}\"{}", "{", escapedMessage, "}"); + this->message = std::format("{{\"error\":\"{}\"}}", escapedMessage); } } @@ -60,7 +60,7 @@ public: { return CommandResult(message, Status::FAILURE); } - + // Create a result that returns raw JSON without wrapper static CommandResult getRawJsonResult(const std::string &jsonMessage) { @@ -70,7 +70,6 @@ public: } std::string getResult() const { return this->message; } - }; #endif \ No newline at end of file diff --git a/components/CommandManager/CommandManager/commands/device_commands.cpp b/components/CommandManager/CommandManager/commands/device_commands.cpp index fa5d390..2ffc4dd 100644 --- a/components/CommandManager/CommandManager/commands/device_commands.cpp +++ b/components/CommandManager/CommandManager/commands/device_commands.cpp @@ -1,25 +1,24 @@ #include "device_commands.hpp" -#include -#include -#include "esp_timer.h" -#include - // Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite -CommandResult setDeviceModeCommand(std::shared_ptr registry, std::string_view jsonPayload) { +CommandResult setDeviceModeCommand(std::shared_ptr registry, std::string_view jsonPayload) +{ const auto parsedJson = cJSON_Parse(jsonPayload.data()); - if (parsedJson == nullptr) { + if (parsedJson == nullptr) + { return CommandResult::getErrorResult("Invalid payload"); } const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode"); - if (modeObject == nullptr) { + if (modeObject == nullptr) + { return CommandResult::getErrorResult("Invalid payload - missing mode"); } const auto mode = modeObject->valueint; - if (mode < 0 || mode > 2) { + if (mode < 0 || mode > 2) + { return CommandResult::getErrorResult("Invalid payload - unsupported mode"); } @@ -29,10 +28,12 @@ CommandResult setDeviceModeCommand(std::shared_ptr registry, return CommandResult::getSuccessResult("Device mode set"); } -CommandResult updateOTACredentialsCommand(std::shared_ptr registry, std::string_view jsonPayload) { +CommandResult updateOTACredentialsCommand(std::shared_ptr registry, std::string_view jsonPayload) +{ const auto parsedJson = cJSON_Parse(jsonPayload.data()); - if (parsedJson == nullptr) { + if (parsedJson == nullptr) + { return CommandResult::getErrorResult("Invalid payload"); } @@ -42,18 +43,23 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr re auto OTAPassword = oldDeviceConfig.OTAPassword; auto OTAPort = oldDeviceConfig.OTAPort; - if (const auto OTALoginObject = cJSON_GetObjectItem(parsedJson, "login"); OTALoginObject != nullptr) { - if (const auto newLogin = OTALoginObject->valuestring; strcmp(newLogin, "") != 0) { + if (const auto OTALoginObject = cJSON_GetObjectItem(parsedJson, "login"); OTALoginObject != nullptr) + { + if (const auto newLogin = OTALoginObject->valuestring; strcmp(newLogin, "") != 0) + { OTALogin = newLogin; } } - if (const auto OTAPasswordObject = cJSON_GetObjectItem(parsedJson, "password"); OTAPasswordObject != nullptr) { + if (const auto OTAPasswordObject = cJSON_GetObjectItem(parsedJson, "password"); OTAPasswordObject != nullptr) + { OTAPassword = OTAPasswordObject->valuestring; } - if (const auto OTAPortObject = cJSON_GetObjectItem(parsedJson, "port"); OTAPortObject != nullptr) { - if (const auto newPort = OTAPortObject->valueint; newPort >= 82) { + if (const auto OTAPortObject = cJSON_GetObjectItem(parsedJson, "port"); OTAPortObject != nullptr) + { + if (const auto newPort = OTAPortObject->valueint; newPort >= 82) + { OTAPort = newPort; } } @@ -62,70 +68,82 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr re return CommandResult::getSuccessResult("OTA Config set"); } -CommandResult restartDeviceCommand() { +CommandResult restartDeviceCommand() +{ OpenIrisTasks::ScheduleRestart(2000); return CommandResult::getSuccessResult("Device restarted"); } -CommandResult startStreamingCommand() { +CommandResult startStreamingCommand() +{ activateStreaming(false); // Don't disable setup interfaces by default return CommandResult::getSuccessResult("Streaming started"); } -CommandResult switchModeCommand(std::shared_ptr registry, std::string_view jsonPayload) { +CommandResult switchModeCommand(std::shared_ptr registry, std::string_view jsonPayload) +{ const auto parsedJson = cJSON_Parse(jsonPayload.data()); - if (parsedJson == nullptr) { + if (parsedJson == nullptr) + { return CommandResult::getErrorResult("Invalid payload"); } const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode"); - if (modeObject == nullptr) { + if (modeObject == nullptr) + { return CommandResult::getErrorResult("Invalid payload - missing mode"); } - const char* modeStr = modeObject->valuestring; + const char *modeStr = modeObject->valuestring; StreamingMode newMode; - + ESP_LOGI("[DEVICE_COMMANDS]", "Switch mode command received with mode: %s", modeStr); - - if (strcmp(modeStr, "uvc") == 0 || strcmp(modeStr, "UVC") == 0) { + + if (strcmp(modeStr, "uvc") == 0) + { newMode = StreamingMode::UVC; - } else if (strcmp(modeStr, "wifi") == 0 || strcmp(modeStr, "WiFi") == 0 || strcmp(modeStr, "WIFI") == 0) { + } + else if (strcmp(modeStr, "wifi") == 0) + { newMode = StreamingMode::WIFI; - } else if (strcmp(modeStr, "auto") == 0 || strcmp(modeStr, "AUTO") == 0) { + } + else if (strcmp(modeStr, "auto") == 0) + { newMode = StreamingMode::AUTO; - } else { + } + else + { return CommandResult::getErrorResult("Invalid mode - use 'uvc', 'wifi', or 'auto'"); } const auto projectConfig = registry->resolve(DependencyType::project_config); ESP_LOGI("[DEVICE_COMMANDS]", "Setting device mode to: %d", (int)newMode); projectConfig->setDeviceMode(newMode); - + cJSON_Delete(parsedJson); return CommandResult::getSuccessResult("Device mode switched, restart to apply"); } -CommandResult getDeviceModeCommand(std::shared_ptr registry) { +CommandResult getDeviceModeCommand(std::shared_ptr registry) +{ const auto projectConfig = registry->resolve(DependencyType::project_config); StreamingMode currentMode = projectConfig->getDeviceMode(); - - const char* modeStr = "unknown"; - switch (currentMode) { - case StreamingMode::UVC: - modeStr = "UVC"; - break; - case StreamingMode::WIFI: - modeStr = "WiFi"; - break; - case StreamingMode::AUTO: - modeStr = "Auto"; - break; + + const char *modeStr = "unknown"; + switch (currentMode) + { + case StreamingMode::UVC: + modeStr = "UVC"; + break; + case StreamingMode::WIFI: + modeStr = "WiFi"; + break; + case StreamingMode::AUTO: + modeStr = "Auto"; + break; } - - char result[100]; - sprintf(result, "{\"mode\":\"%s\",\"value\":%d}", modeStr, (int)currentMode); - + + auto result = std::format("{{ \"mode\": \"{}\", \"value\": {} }}", modeStr, static_cast(currentMode)); return CommandResult::getSuccessResult(result); } diff --git a/components/CommandManager/CommandManager/commands/device_commands.hpp b/components/CommandManager/CommandManager/commands/device_commands.hpp index 5685f13..4eb1b3d 100644 --- a/components/CommandManager/CommandManager/commands/device_commands.hpp +++ b/components/CommandManager/CommandManager/commands/device_commands.hpp @@ -1,6 +1,13 @@ #include "CommandResult.hpp" +#include "ProjectConfig.hpp" #include "OpenIrisTasks.hpp" #include "DependencyRegistry.hpp" +#include "esp_timer.h" +#include "cJSON.h" +#include "main_globals.hpp" + +#include +#include CommandResult setDeviceModeCommand(std::shared_ptr registry, std::string_view jsonPayload); diff --git a/components/CommandManager/CommandManager/commands/scan_commands.cpp b/components/CommandManager/CommandManager/commands/scan_commands.cpp index 054a621..6844344 100644 --- a/components/CommandManager/CommandManager/commands/scan_commands.cpp +++ b/components/CommandManager/CommandManager/commands/scan_commands.cpp @@ -1,11 +1,10 @@ #include "scan_commands.hpp" -#include "cJSON.h" -#include "esp_log.h" -#include -CommandResult scanNetworksCommand(std::shared_ptr registry) { +CommandResult scanNetworksCommand(std::shared_ptr registry) +{ auto wifiManager = registry->resolve(DependencyType::wifi_manager); - if (!wifiManager) { + if (!wifiManager) + { return CommandResult::getErrorResult("WiFiManager not available"); } @@ -15,7 +14,8 @@ CommandResult scanNetworksCommand(std::shared_ptr registry) cJSON *networksArray = cJSON_CreateArray(); cJSON_AddItemToObject(root, "networks", networksArray); - for (const auto& network : networks) { + for (const auto &network : networks) + { cJSON *networkObject = cJSON_CreateObject(); cJSON_AddStringToObject(networkObject, "ssid", network.ssid.c_str()); cJSON_AddNumberToObject(networkObject, "channel", network.channel); diff --git a/components/CommandManager/CommandManager/commands/scan_commands.hpp b/components/CommandManager/CommandManager/commands/scan_commands.hpp index 7e7df86..df2e0da 100644 --- a/components/CommandManager/CommandManager/commands/scan_commands.hpp +++ b/components/CommandManager/CommandManager/commands/scan_commands.hpp @@ -1,9 +1,12 @@ #ifndef SCAN_COMMANDS_HPP #define SCAN_COMMANDS_HPP -#include "../CommandResult.hpp" -#include "../DependencyRegistry.hpp" +#include "CommandResult.hpp" +#include "DependencyRegistry.hpp" +#include "esp_log.h" +#include #include +#include CommandResult scanNetworksCommand(std::shared_ptr registry); diff --git a/components/CommandManager/CommandManager/commands/simple_commands.cpp b/components/CommandManager/CommandManager/commands/simple_commands.cpp index 909568a..33cd04d 100644 --- a/components/CommandManager/CommandManager/commands/simple_commands.cpp +++ b/components/CommandManager/CommandManager/commands/simple_commands.cpp @@ -1,24 +1,39 @@ #include "simple_commands.hpp" -#include "main_globals.hpp" -#include "esp_log.h" -static const char* TAG = "SimpleCommands"; +static const char *TAG = "SimpleCommands"; CommandResult PingCommand() { return CommandResult::getSuccessResult("pong"); }; -CommandResult PauseCommand(const PausePayload& payload) +CommandResult PauseCommand(std::string_view jsonPayload) { + PausePayload payload; + // pause by default if this command gets executed, even if the payload was invalid + payload.pause = true; + + cJSON *root = cJSON_Parse(std::string(jsonPayload).c_str()); + if (root) + { + cJSON *pauseItem = cJSON_GetObjectItem(root, "pause"); + if (pauseItem && cJSON_IsBool(pauseItem)) + { + payload.pause = cJSON_IsTrue(pauseItem); + } + cJSON_Delete(root); + } + ESP_LOGI(TAG, "Pause command received: %s", payload.pause ? "true" : "false"); - - startupPaused = payload.pause; - - if (payload.pause) { + + setStartupPaused(payload.pause); + if (payload.pause) + { ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode"); return CommandResult::getSuccessResult("Startup paused"); - } else { + } + else + { ESP_LOGI(TAG, "Startup resumed"); return CommandResult::getSuccessResult("Startup resumed"); } diff --git a/components/CommandManager/CommandManager/commands/simple_commands.hpp b/components/CommandManager/CommandManager/commands/simple_commands.hpp index 9db7ea9..23c2972 100644 --- a/components/CommandManager/CommandManager/commands/simple_commands.hpp +++ b/components/CommandManager/CommandManager/commands/simple_commands.hpp @@ -4,8 +4,11 @@ #include #include "CommandResult.hpp" #include "CommandSchema.hpp" +#include "main_globals.hpp" +#include "esp_log.h" +#include CommandResult PingCommand(); -CommandResult PauseCommand(const PausePayload& payload); +CommandResult PauseCommand(std::string_view jsonPayload); #endif \ No newline at end of file diff --git a/components/Helpers/Helpers/main_globals.cpp b/components/Helpers/Helpers/main_globals.cpp index 8ed665d..cab36f8 100644 --- a/components/Helpers/Helpers/main_globals.cpp +++ b/components/Helpers/Helpers/main_globals.cpp @@ -4,37 +4,47 @@ // Forward declarations extern void start_video_streaming(void *arg); -// Global variables to be set by main -static esp_timer_handle_t* g_streaming_timer_handle = nullptr; -static TaskHandle_t* g_serial_manager_handle = nullptr; - -// Functions for main to set the global handles -void setStreamingTimerHandle(esp_timer_handle_t* handle) { - g_streaming_timer_handle = handle; +bool startupCommandReceived = false; +bool getStartupCommandReceived() +{ + return startupCommandReceived; } -void setSerialManagerHandle(TaskHandle_t* handle) { - g_serial_manager_handle = handle; +void setStartupCommandReceived(bool startupCommandReceived) +{ + startupCommandReceived = startupCommandReceived; } -// Functions for components to access the handles -esp_timer_handle_t* getStreamingTimerHandle() { - return g_streaming_timer_handle; -} - -TaskHandle_t* getSerialManagerHandle() { +static TaskHandle_t *g_serial_manager_handle = nullptr; +TaskHandle_t *getSerialManagerHandle() +{ return g_serial_manager_handle; } +void setSerialManagerHandle(TaskHandle_t *serialManagerHandle) +{ + g_serial_manager_handle = serialManagerHandle; +} + // Global pause state bool startupPaused = false; +bool getStartupPaused() +{ + return startupPaused; +} + +void setStartupPaused(bool startupPaused) +{ + startupPaused = startupPaused; +} // Function to manually activate streaming -void activateStreaming(bool disableSetup) { +void activateStreaming(bool disableSetup) +{ ESP_LOGI("[MAIN_GLOBALS]", "Manually activating streaming, disableSetup=%s", disableSetup ? "true" : "false"); - - TaskHandle_t* serialHandle = disableSetup ? g_serial_manager_handle : nullptr; - void* serialTaskHandle = (serialHandle && *serialHandle) ? *serialHandle : nullptr; - + + TaskHandle_t *serialHandle = disableSetup ? g_serial_manager_handle : nullptr; + void *serialTaskHandle = (serialHandle && *serialHandle) ? *serialHandle : nullptr; + start_video_streaming(serialTaskHandle); } \ No newline at end of file diff --git a/components/Helpers/Helpers/main_globals.hpp b/components/Helpers/Helpers/main_globals.hpp index d13f7fc..5602c2c 100644 --- a/components/Helpers/Helpers/main_globals.hpp +++ b/components/Helpers/Helpers/main_globals.hpp @@ -7,22 +7,18 @@ #include "freertos/task.h" // Functions for main to set global handles -void setStreamingTimerHandle(esp_timer_handle_t* handle); -void setSerialManagerHandle(TaskHandle_t* handle); // Functions to access global handles from components -esp_timer_handle_t* getStreamingTimerHandle(); -TaskHandle_t* getSerialManagerHandle(); +TaskHandle_t *getSerialManagerHandle(); +void setSerialManagerHandle(TaskHandle_t *serialManagerHandle); // Function to manually activate streaming void activateStreaming(bool disableSetup = false); -// Function to notify that a command was received during startup -extern void notify_startup_command_received(); +bool getStartupCommandReceived(); +void setStartupCommandReceived(bool startupCommandReceived); -// Global variables for startup state -extern bool startupCommandReceived; -extern esp_timer_handle_t startupTimerHandle; -extern bool startupPaused; +bool getStartupPaused(); +void setStartupPaused(bool startupPaused); #endif \ No newline at end of file diff --git a/components/ProjectConfig/ProjectConfig/ProjectConfig.hpp b/components/ProjectConfig/ProjectConfig/ProjectConfig.hpp index b256c22..045a8a6 100644 --- a/components/ProjectConfig/ProjectConfig/ProjectConfig.hpp +++ b/components/ProjectConfig/ProjectConfig/ProjectConfig.hpp @@ -9,7 +9,6 @@ #include "Models.hpp" #include - int getNetworkCount(Preferences *pref); void saveNetworkCount(Preferences *pref, int count); @@ -60,7 +59,7 @@ public: const std::string &password, uint8_t channel); void setWiFiTxPower(uint8_t power); - void setDeviceMode(StreamingMode deviceMode); + void setDeviceMode(StreamingMode deviceMode); StreamingMode getDeviceMode(); private: diff --git a/components/SerialManager/SerialManager/SerialManager.cpp b/components/SerialManager/SerialManager/SerialManager.cpp index d1c60c9..c666acf 100644 --- a/components/SerialManager/SerialManager/SerialManager.cpp +++ b/components/SerialManager/SerialManager/SerialManager.cpp @@ -4,8 +4,9 @@ #define BUF_SIZE (1024) -SerialManager::SerialManager(std::shared_ptr commandManager, esp_timer_handle_t *timerHandle, std::shared_ptr deviceConfig) - : commandManager(commandManager), timerHandle(timerHandle), deviceConfig(deviceConfig) { +SerialManager::SerialManager(std::shared_ptr commandManager, esp_timer_handle_t *timerHandle, std::shared_ptr deviceConfig) + : commandManager(commandManager), timerHandle(timerHandle), deviceConfig(deviceConfig) +{ this->data = static_cast(malloc(BUF_SIZE)); this->temp_data = static_cast(malloc(256)); } @@ -40,47 +41,62 @@ void SerialManager::try_receive() // Notify main that a command was received during startup notify_startup_command_received(); - + const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast(this->data))); const auto resultMessage = result.getResult(); usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20); } } +// Function to notify that a command was received during startup +void SerialManager::notify_startup_command_received() +{ + setStartupCommandReceived(true); + + // Cancel the startup timer if it's still running + if (timerHandle != nullptr) + { + esp_timer_stop(*timerHandle); + esp_timer_delete(*timerHandle); + timerHandle = nullptr; + ESP_LOGI("[MAIN]", "Startup timer cancelled, staying in heartbeat mode"); + } +} + void SerialManager::send_heartbeat() { // Get the MAC address as unique identifier uint8_t mac[6]; esp_read_mac(mac, ESP_MAC_WIFI_STA); - + // Format as serial number string char serial_number[18]; - sprintf(serial_number, "%02X%02X%02X%02X%02X%02X", + sprintf(serial_number, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); - + // Create heartbeat JSON with serial number char heartbeat[128]; sprintf(heartbeat, "{\"heartbeat\":\"openiris_setup_mode\",\"serial\":\"%s\"}\n", serial_number); - + usb_serial_jtag_write_bytes(heartbeat, strlen(heartbeat), 1000 / 20); } bool SerialManager::should_send_heartbeat() { // Always send heartbeat during startup delay or if no WiFi configured - extern bool startupCommandReceived; - extern esp_timer_handle_t startupTimerHandle; - + // If startup timer is still running, always send heartbeat - if (startupTimerHandle != nullptr) { + if (timerHandle != nullptr) + { return true; } - + // If in heartbeat mode after startup, continue sending - if (startupCommandReceived) { + if (getStartupCommandReceived()) + { return true; } - + // Otherwise, only send if no WiFi credentials configured const auto wifiConfigs = deviceConfig->getWifiConfigs(); return wifiConfigs.empty(); @@ -95,16 +111,16 @@ void HandleSerialManagerTask(void *pvParameters) while (true) { serialManager->try_receive(); - + // Send heartbeat every 2 seconds, but only if no WiFi credentials are set TickType_t currentTime = xTaskGetTickCount(); - if ((currentTime - lastHeartbeat) >= heartbeatInterval) { - if (serialManager->should_send_heartbeat()) { + if ((currentTime - lastHeartbeat) >= heartbeatInterval) + { + if (serialManager->should_send_heartbeat()) + { serialManager->send_heartbeat(); } lastHeartbeat = currentTime; } - - vTaskDelay(pdMS_TO_TICKS(50)); // Small delay to prevent busy waiting } } \ No newline at end of file diff --git a/components/SerialManager/SerialManager/SerialManager.hpp b/components/SerialManager/SerialManager/SerialManager.hpp index a8c92c9..5205a69 100644 --- a/components/SerialManager/SerialManager/SerialManager.hpp +++ b/components/SerialManager/SerialManager/SerialManager.hpp @@ -26,6 +26,7 @@ public: void try_receive(); void send_heartbeat(); bool should_send_heartbeat(); + void notify_startup_command_received(); private: std::shared_ptr commandManager; diff --git a/components/UVCStream/UVCStream/UVCStream.cpp b/components/UVCStream/UVCStream/UVCStream.cpp index e4ad8a7..1c381b9 100644 --- a/components/UVCStream/UVCStream/UVCStream.cpp +++ b/components/UVCStream/UVCStream/UVCStream.cpp @@ -3,25 +3,29 @@ constexpr int UVC_MAX_FRAMESIZE_SIZE(75 * 1024); static const char *UVC_STREAM_TAG = "[UVC DEVICE]"; -extern "C" { +extern "C" +{ static char serial_number_str[13]; - const char *get_uvc_device_name() { + const char *get_uvc_device_name() + { return deviceConfig->getMDNSConfig().hostname.c_str(); } - const char *get_serial_number(void) { - if (serial_number_str[0] == '\0') { + const char *get_serial_number(void) + { + if (serial_number_str[0] == '\0') + { uint8_t mac_address[6]; esp_err_t result = esp_efuse_mac_get_default(mac_address); - if (result != ESP_OK) { + if (result != ESP_OK) + { ESP_LOGE(UVC_STREAM_TAG, "Failed to get MAC address of the board, returning default serial number"); return CONFIG_TUSB_SERIAL_NUM; } sniprintf(serial_number_str, sizeof(serial_number_str), "%02x:%02x:%02x:%02x:%02x:%02x", - mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5] - ); + mac_address[0], mac_address[1], mac_address[2], mac_address[3], mac_address[4], mac_address[5]); } return serial_number_str; } diff --git a/components/wifiManager/wifiManager/WiFiScanner.cpp b/components/wifiManager/wifiManager/WiFiScanner.cpp index 8e7f171..5135c32 100644 --- a/components/wifiManager/wifiManager/WiFiScanner.cpp +++ b/components/wifiManager/wifiManager/WiFiScanner.cpp @@ -1,32 +1,38 @@ #include "WiFiScanner.hpp" #include +static const char *TAG = "WiFiScanner"; + WiFiScanner::WiFiScanner() {} -void WiFiScanner::scanResultCallback(void* arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) { - auto* scanner = static_cast(arg); - if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { +void WiFiScanner::scanResultCallback(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + auto *scanner = static_cast(arg); + if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) + { uint16_t ap_count = 0; esp_wifi_scan_get_ap_num(&ap_count); - - if (ap_count == 0) { + + if (ap_count == 0) + { ESP_LOGI(TAG, "No access points found"); return; } - wifi_ap_record_t* ap_records = new wifi_ap_record_t[ap_count]; + wifi_ap_record_t *ap_records = new wifi_ap_record_t[ap_count]; ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_records)); scanner->networks.clear(); - for (uint16_t i = 0; i < ap_count; i++) { + for (uint16_t i = 0; i < ap_count; i++) + { WiFiNetwork network; - network.ssid = std::string(reinterpret_cast(ap_records[i].ssid)); + network.ssid = std::string(reinterpret_cast(ap_records[i].ssid)); network.channel = ap_records[i].primary; network.rssi = ap_records[i].rssi; memcpy(network.mac, ap_records[i].bssid, 6); network.auth_mode = ap_records[i].authmode; - + scanner->networks.push_back(network); } @@ -35,81 +41,88 @@ void WiFiScanner::scanResultCallback(void* arg, esp_event_base_t event_base, } } -std::vector WiFiScanner::scanNetworks() { +// todo this is garbage +std::vector WiFiScanner::scanNetworks() +{ std::vector scan_results; - + // Check if WiFi is initialized wifi_mode_t mode; esp_err_t err = esp_wifi_get_mode(&mode); - if (err == ESP_ERR_WIFI_NOT_INIT) { + if (err == ESP_ERR_WIFI_NOT_INIT) + { ESP_LOGE(TAG, "WiFi not initialized"); return scan_results; } - - + // Give WiFi more time to be ready vTaskDelay(pdMS_TO_TICKS(500)); - + // Stop any ongoing scan esp_wifi_scan_stop(); - + // Try sequential channel scanning as a workaround - bool try_sequential_scan = true; // Enable sequential scan - - if (!try_sequential_scan) { + bool try_sequential_scan = true; // Enable sequential scan + + if (!try_sequential_scan) + { // Normal all-channel scan wifi_scan_config_t scan_config = { .ssid = nullptr, .bssid = nullptr, - .channel = 0, // 0 means scan all channels + .channel = 0, // 0 means scan all channels .show_hidden = true, - .scan_type = WIFI_SCAN_TYPE_ACTIVE, // Active scan + .scan_type = WIFI_SCAN_TYPE_ACTIVE, // Active scan .scan_time = { .active = { - .min = 120, // Min per channel - .max = 300 // Max per channel + .min = 120, // Min per channel + .max = 300 // Max per channel }, - .passive = 360 - }, - .home_chan_dwell_time = 0, // 0 for default - .channel_bitmap = 0 // 0 for all channels + .passive = 360}, + .home_chan_dwell_time = 0, // 0 for default + .channel_bitmap = 0 // 0 for all channels }; err = esp_wifi_scan_start(&scan_config, false); - if (err != ESP_OK) { + if (err != ESP_OK) + { ESP_LOGE(TAG, "Failed to start scan: %s", esp_err_to_name(err)); return scan_results; } - } else { + } + else + { // Sequential channel scan - scan each channel individually std::vector all_records; - - for (uint8_t ch = 1; ch <= 13; ch++) { + + for (uint8_t ch = 1; ch <= 13; ch++) + { wifi_scan_config_t scan_config = { .ssid = nullptr, .bssid = nullptr, - .channel = ch, // Specific channel + .channel = ch, .show_hidden = true, .scan_type = WIFI_SCAN_TYPE_ACTIVE, .scan_time = { .active = { .min = 100, - .max = 200 - }, - .passive = 300 - }, + .max = 200}, + .passive = 300}, .home_chan_dwell_time = 0, - .channel_bitmap = 0 - }; - + .channel_bitmap = 0}; + err = esp_wifi_scan_start(&scan_config, true); // Blocking scan - if (err == ESP_OK) { + if (err == ESP_OK) + { uint16_t ch_count = 0; esp_wifi_scan_get_ap_num(&ch_count); - if (ch_count > 0) { - wifi_ap_record_t* ch_records = new wifi_ap_record_t[ch_count]; - if (esp_wifi_scan_get_ap_records(&ch_count, ch_records) == ESP_OK) { - for (uint16_t i = 0; i < ch_count; i++) { + if (ch_count > 0) + { + wifi_ap_record_t *ch_records = new wifi_ap_record_t[ch_count]; + if (esp_wifi_scan_get_ap_records(&ch_count, ch_records) == ESP_OK) + { + for (uint16_t i = 0; i < ch_count; i++) + { all_records.push_back(ch_records[i]); } } @@ -118,59 +131,66 @@ std::vector WiFiScanner::scanNetworks() { } vTaskDelay(pdMS_TO_TICKS(50)); } - + // Process all collected records - for (const auto& record : all_records) { + for (const auto &record : all_records) + { WiFiNetwork network; - network.ssid = std::string(reinterpret_cast(record.ssid)); + network.ssid = std::string(reinterpret_cast(record.ssid)); network.channel = record.primary; network.rssi = record.rssi; memcpy(network.mac, record.bssid, 6); network.auth_mode = record.authmode; scan_results.push_back(network); } - + // Skip the normal result processing return scan_results; } - + // Wait for scan completion with timeout int timeout_ms = 15000; // 15 second timeout int elapsed_ms = 0; - - while (elapsed_ms < timeout_ms) { + + while (elapsed_ms < timeout_ms) + { uint16_t temp_count = 0; esp_err_t count_err = esp_wifi_scan_get_ap_num(&temp_count); - - if (count_err == ESP_OK) { + + if (count_err == ESP_OK) + { // Wait a bit longer after finding networks to ensure scan is complete - if (temp_count > 0 && elapsed_ms > 5000) { + if (temp_count > 0 && elapsed_ms > 5000) + { break; } } - + vTaskDelay(pdMS_TO_TICKS(200)); elapsed_ms += 200; } - - if (elapsed_ms >= timeout_ms) { + + if (elapsed_ms >= timeout_ms) + { ESP_LOGE(TAG, "Scan timeout after %d ms", timeout_ms); esp_wifi_scan_stop(); return scan_results; } - + // Get scan results uint16_t ap_count = 0; esp_wifi_scan_get_ap_num(&ap_count); - - if (ap_count == 0) { + + if (ap_count == 0) + { ESP_LOGI(TAG, "No access points found"); return scan_results; } - wifi_ap_record_t* ap_records = new wifi_ap_record_t[ap_count]; + wifi_ap_record_t *ap_records = new wifi_ap_record_t[ap_count]; err = esp_wifi_scan_get_ap_records(&ap_count, ap_records); - if (err != ESP_OK) { + if (err != ESP_OK) + { ESP_LOGE(TAG, "Failed to get scan records: %s", esp_err_to_name(err)); delete[] ap_records; return scan_results; @@ -178,28 +198,26 @@ std::vector WiFiScanner::scanNetworks() { // Build the results vector and track channels found bool channels_found[15] = {false}; // Track channels 0-14 - - for (uint16_t i = 0; i < ap_count; i++) { + + for (uint16_t i = 0; i < ap_count; i++) + { WiFiNetwork network; - network.ssid = std::string(reinterpret_cast(ap_records[i].ssid)); + network.ssid = std::string(reinterpret_cast(ap_records[i].ssid)); network.channel = ap_records[i].primary; network.rssi = ap_records[i].rssi; memcpy(network.mac, ap_records[i].bssid, 6); network.auth_mode = ap_records[i].authmode; - - if (network.channel <= 14) { + + if (network.channel <= 14) + { channels_found[network.channel] = true; } - + scan_results.push_back(network); } - delete[] ap_records; ESP_LOGI(TAG, "Found %d access points", ap_count); - - // Also update the member variable for compatibility - networks = scan_results; - + return scan_results; } \ No newline at end of file diff --git a/components/wifiManager/wifiManager/WiFiScanner.hpp b/components/wifiManager/wifiManager/WiFiScanner.hpp index acb4d6e..f3e23dd 100644 --- a/components/wifiManager/wifiManager/WiFiScanner.hpp +++ b/components/wifiManager/wifiManager/WiFiScanner.hpp @@ -7,7 +7,8 @@ #include "esp_wifi.h" #include "esp_log.h" -struct WiFiNetwork { +struct WiFiNetwork +{ std::string ssid; uint8_t channel; int8_t rssi; @@ -15,14 +16,14 @@ struct WiFiNetwork { wifi_auth_mode_t auth_mode; }; -class WiFiScanner { +class WiFiScanner +{ public: WiFiScanner(); std::vector scanNetworks(); - static void scanResultCallback(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data); + static void scanResultCallback(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data); private: - static constexpr char const* TAG = "WiFiScanner"; std::vector networks; }; diff --git a/components/wifiManager/wifiManager/wifiManager.cpp b/components/wifiManager/wifiManager/wifiManager.cpp index ffbae5c..53b52f5 100644 --- a/components/wifiManager/wifiManager/wifiManager.cpp +++ b/components/wifiManager/wifiManager/wifiManager.cpp @@ -2,7 +2,6 @@ static auto WIFI_MANAGER_TAG = "[WIFI_MANAGER]"; -// Define the global variables declared as extern in the header int s_retry_num = 0; EventGroupHandle_t s_wifi_event_group; @@ -19,9 +18,9 @@ void WiFiManagerHelpers::event_handler(void *arg, esp_event_base_t event_base, } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) { - wifi_event_sta_disconnected_t* disconnected = (wifi_event_sta_disconnected_t*) event_data; + const auto *disconnected = static_cast(event_data); ESP_LOGI(WIFI_MANAGER_TAG, "Disconnect reason: %d", disconnected->reason); - + if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) { esp_wifi_connect(); @@ -51,55 +50,52 @@ void WiFiManager::SetCredentials(const char *ssid, const char *password) { // Clear the config first memset(&_wifi_cfg, 0, sizeof(_wifi_cfg)); - + // Copy SSID with null termination size_t ssid_len = std::min(strlen(ssid), sizeof(_wifi_cfg.sta.ssid) - 1); memcpy(_wifi_cfg.sta.ssid, ssid, ssid_len); _wifi_cfg.sta.ssid[ssid_len] = '\0'; - + // Copy password with null termination size_t pass_len = std::min(strlen(password), sizeof(_wifi_cfg.sta.password) - 1); memcpy(_wifi_cfg.sta.password, password, pass_len); _wifi_cfg.sta.password[pass_len] = '\0'; - + // Set other required fields // Use open auth if no password, otherwise allow any WPA variant - if (strlen(password) == 0) { + if (strlen(password) == 0) + { _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN; - } else { + } + else + { // IMPORTANT: Set threshold to WEP to accept ANY security mode >= WEP // This allows WPA, WPA2, WPA3, etc. The driver will negotiate the highest common mode _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WEP; } - + // CRITICAL: Disable PMF completely - this often causes handshake timeouts _wifi_cfg.sta.pmf_cfg.capable = false; _wifi_cfg.sta.pmf_cfg.required = false; - + // IMPORTANT: Set scan method to ALL channels _wifi_cfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; - _wifi_cfg.sta.bssid_set = 0; // Don't use specific BSSID - _wifi_cfg.sta.channel = 0; // Scan all channels - + _wifi_cfg.sta.bssid_set = 0; // Don't use specific BSSID + _wifi_cfg.sta.channel = 0; // Scan all channels + // Additional settings that might help with compatibility - _wifi_cfg.sta.listen_interval = 0; // Default listen interval - _wifi_cfg.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Connect to strongest signal - + _wifi_cfg.sta.listen_interval = 0; // Default listen interval + _wifi_cfg.sta.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Connect to strongest signal + // IMPORTANT: For WPA/WPA2 Personal networks - _wifi_cfg.sta.threshold.rssi = -127; // Accept any signal strength - _wifi_cfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_UNSPECIFIED; // Let driver decide SAE mode - + _wifi_cfg.sta.threshold.rssi = -127; // Accept any signal strength + _wifi_cfg.sta.sae_pwe_h2e = WPA3_SAE_PWE_UNSPECIFIED; // Let driver decide SAE mode + // Log what we're trying to connect to with detailed debugging ESP_LOGI(WIFI_MANAGER_TAG, "Setting credentials for SSID: '%s' (length: %d)", ssid, (int)strlen(ssid)); ESP_LOGI(WIFI_MANAGER_TAG, "Password: '%s' (length: %d)", password, (int)strlen(password)); - ESP_LOGI(WIFI_MANAGER_TAG, "Auth mode: %d, PMF capable: %d", + ESP_LOGI(WIFI_MANAGER_TAG, "Auth mode: %d, PMF capable: %d", _wifi_cfg.sta.threshold.authmode, _wifi_cfg.sta.pmf_cfg.capable); - - // Print hex dump of SSID to catch any hidden characters - ESP_LOG_BUFFER_HEX_LEVEL(WIFI_MANAGER_TAG, _wifi_cfg.sta.ssid, strlen((char*)_wifi_cfg.sta.ssid), ESP_LOG_INFO); - - // Print hex dump of password to catch any hidden characters - ESP_LOG_BUFFER_HEX_LEVEL(WIFI_MANAGER_TAG, _wifi_cfg.sta.password, strlen((char*)_wifi_cfg.sta.password), ESP_LOG_INFO); } void WiFiManager::ConnectWithHardcodedCredentials() @@ -164,11 +160,11 @@ void WiFiManager::ConnectWithStoredCredentials() // Stop WiFi once before the loop esp_wifi_stop(); vTaskDelay(pdMS_TO_TICKS(100)); - + // Ensure we're in STA mode ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); - for (const auto& network : networks) + for (const auto &network : networks) { // Reset retry counter for each network attempt s_retry_num = 0; @@ -177,19 +173,14 @@ void WiFiManager::ConnectWithStoredCredentials() // Update config without stopping WiFi again ESP_LOGI(WIFI_MANAGER_TAG, "Attempting to connect to SSID: '%s'", network.ssid.c_str()); - - // Double-check the actual config being sent to WiFi driver - ESP_LOGI(WIFI_MANAGER_TAG, "Final SSID in config: '%s' (len: %d)", - (char*)_wifi_cfg.sta.ssid, (int)strlen((char*)_wifi_cfg.sta.ssid)); - ESP_LOGI(WIFI_MANAGER_TAG, "Final password in config: '%s' (len: %d)", - (char*)_wifi_cfg.sta.password, (int)strlen((char*)_wifi_cfg.sta.password)); - + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg)); xQueueSend(this->eventQueue, &event, 10); // Start WiFi if not already started esp_err_t start_err = esp_wifi_start(); - if (start_err != ESP_OK && start_err != ESP_ERR_WIFI_STATE) { + if (start_err != ESP_OK && start_err != ESP_ERR_WIFI_STATE) + { ESP_LOGE(WIFI_MANAGER_TAG, "Failed to start WiFi: %s", esp_err_to_name(start_err)); continue; } @@ -214,7 +205,7 @@ void WiFiManager::ConnectWithStoredCredentials() } ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to SSID:%s, trying next stored network", network.ssid.c_str()); - + // Disconnect before trying next network esp_wifi_disconnect(); vTaskDelay(pdMS_TO_TICKS(100)); @@ -249,107 +240,117 @@ void WiFiManager::SetupAccessPoint() ESP_LOGI(WIFI_MANAGER_TAG, "AP started."); } -std::vector WiFiManager::ScanNetworks() { - wifi_mode_t current_mode; - esp_err_t err = esp_wifi_get_mode(¤t_mode); - - if (err == ESP_ERR_WIFI_NOT_INIT) { - ESP_LOGE(WIFI_MANAGER_TAG, "WiFi not initialized for scanning"); - return std::vector(); +std::vector WiFiManager::ScanNetworks() +{ + wifi_mode_t current_mode; + esp_err_t err = esp_wifi_get_mode(¤t_mode); + + if (err == ESP_ERR_WIFI_NOT_INIT) + { + ESP_LOGE(WIFI_MANAGER_TAG, "WiFi not initialized for scanning"); + return std::vector(); + } + + // If we're in AP-only mode, we need STA interface for scanning + if (current_mode == WIFI_MODE_AP) + { + ESP_LOGI(WIFI_MANAGER_TAG, "AP mode detected, checking for STA interface"); + + // Check if STA netif already exists + esp_netif_t *sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + bool sta_netif_exists = (sta_netif != nullptr); + + if (!sta_netif_exists) + { + ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface for scanning"); + sta_netif = esp_netif_create_default_wifi_sta(); } - - // If we're in AP-only mode, we need STA interface for scanning - if (current_mode == WIFI_MODE_AP) { - ESP_LOGI(WIFI_MANAGER_TAG, "AP mode detected, checking for STA interface"); - - // Check if STA netif already exists - esp_netif_t* sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); - bool sta_netif_exists = (sta_netif != nullptr); - - if (!sta_netif_exists) { - ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface for scanning"); - sta_netif = esp_netif_create_default_wifi_sta(); - } - - ESP_LOGI(WIFI_MANAGER_TAG, "Switching to APSTA mode for scanning"); - err = esp_wifi_set_mode(WIFI_MODE_APSTA); - if (err != ESP_OK) { - ESP_LOGE(WIFI_MANAGER_TAG, "Failed to set APSTA mode: %s", esp_err_to_name(err)); - if (!sta_netif_exists && sta_netif) { - esp_netif_destroy(sta_netif); - } - return std::vector(); - } - - // Configure STA with empty config to prevent auto-connect - wifi_config_t empty_config = {}; - esp_wifi_set_config(WIFI_IF_STA, &empty_config); - - // Ensure STA is disconnected and not trying to connect - esp_wifi_disconnect(); - vTaskDelay(pdMS_TO_TICKS(500)); - - // Longer delay for mode to stabilize and enable all channels - vTaskDelay(pdMS_TO_TICKS(1500)); - - // Perform scan - auto networks = wifiScanner->scanNetworks(); - - // Restore AP-only mode - ESP_LOGI(WIFI_MANAGER_TAG, "Restoring AP-only mode"); - esp_wifi_set_mode(WIFI_MODE_AP); - - // Clean up STA interface if we created it - if (!sta_netif_exists && sta_netif) { - esp_netif_destroy(sta_netif); - } - - return networks; + + ESP_LOGI(WIFI_MANAGER_TAG, "Switching to APSTA mode for scanning"); + err = esp_wifi_set_mode(WIFI_MODE_APSTA); + if (err != ESP_OK) + { + ESP_LOGE(WIFI_MANAGER_TAG, "Failed to set APSTA mode: %s", esp_err_to_name(err)); + if (!sta_netif_exists && sta_netif) + { + esp_netif_destroy(sta_netif); + } + return std::vector(); } - - // If already in STA or APSTA mode, scan directly - return wifiScanner->scanNetworks(); + + // Configure STA with empty config to prevent auto-connect + wifi_config_t empty_config = {}; + esp_wifi_set_config(WIFI_IF_STA, &empty_config); + + // Ensure STA is disconnected and not trying to connect + esp_wifi_disconnect(); + // Longer delay for mode to stabilize and enable all channels + vTaskDelay(pdMS_TO_TICKS(2000)); + + // Perform scan + auto networks = wifiScanner->scanNetworks(); + + // Restore AP-only mode + ESP_LOGI(WIFI_MANAGER_TAG, "Restoring AP-only mode"); + esp_wifi_set_mode(WIFI_MODE_AP); + + // Clean up STA interface if we created it + if (!sta_netif_exists && sta_netif) + { + esp_netif_destroy(sta_netif); + } + + return networks; + } + + // If already in STA or APSTA mode, scan directly + return wifiScanner->scanNetworks(); } -WiFiState_e WiFiManager::GetCurrentWiFiState() { - return this->stateManager->GetWifiState(); +WiFiState_e WiFiManager::GetCurrentWiFiState() +{ + return this->stateManager->GetWifiState(); } -void WiFiManager::TryConnectToStoredNetworks() { - ESP_LOGI(WIFI_MANAGER_TAG, "Manual WiFi connection attempt requested"); - - // Check current WiFi mode - wifi_mode_t current_mode; - esp_err_t err = esp_wifi_get_mode(¤t_mode); - if (err != ESP_OK) { - ESP_LOGE(WIFI_MANAGER_TAG, "Failed to get WiFi mode: %s", esp_err_to_name(err)); - return; +void WiFiManager::TryConnectToStoredNetworks() +{ + ESP_LOGI(WIFI_MANAGER_TAG, "Manual WiFi connection attempt requested"); + + // Check current WiFi mode + wifi_mode_t current_mode; + esp_err_t err = esp_wifi_get_mode(¤t_mode); + if (err != ESP_OK) + { + ESP_LOGE(WIFI_MANAGER_TAG, "Failed to get WiFi mode: %s", esp_err_to_name(err)); + return; + } + + // If in AP mode, we need to properly transition to STA mode + if (current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA) + { + ESP_LOGI(WIFI_MANAGER_TAG, "Currently in AP mode, transitioning to STA mode"); + + // Stop WiFi first + esp_wifi_stop(); + vTaskDelay(pdMS_TO_TICKS(100)); + + // Check if STA interface exists, create if needed + esp_netif_t *sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); + if (sta_netif == nullptr) + { + ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface"); + sta_netif = esp_netif_create_default_wifi_sta(); } - - // If in AP mode, we need to properly transition to STA mode - if (current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA) { - ESP_LOGI(WIFI_MANAGER_TAG, "Currently in AP mode, transitioning to STA mode"); - - // Stop WiFi first - esp_wifi_stop(); - vTaskDelay(pdMS_TO_TICKS(100)); - - // Check if STA interface exists, create if needed - esp_netif_t* sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); - if (sta_netif == nullptr) { - ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface"); - sta_netif = esp_netif_create_default_wifi_sta(); - } - - // Set to STA mode - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); - vTaskDelay(pdMS_TO_TICKS(100)); - } - - // Reset retry counter and clear all event bits - s_retry_num = 0; - xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT); - this->ConnectWithStoredCredentials(); + + // Set to STA mode + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + vTaskDelay(pdMS_TO_TICKS(100)); + } + + // Reset retry counter and clear all event bits + s_retry_num = 0; + xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT); + this->ConnectWithStoredCredentials(); } void WiFiManager::Begin() @@ -363,18 +364,6 @@ void WiFiManager::Begin() wifi_init_config_t esp_wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); ESP_ERROR_CHECK(esp_wifi_init(&esp_wifi_init_config)); - // Set WiFi country to enable all channels (1-14) - wifi_country_t country_config = { - .cc = "JP", // Japan allows channels 1-14 (most permissive) - .schan = 1, - .nchan = 14, - .max_tx_power = 20, - .policy = WIFI_COUNTRY_POLICY_AUTO - }; - ESP_ERROR_CHECK(esp_wifi_set_country(&country_config)); - - - ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &WiFiManagerHelpers::event_handler, @@ -387,8 +376,8 @@ void WiFiManager::Begin() &instance_got_ip)); _wifi_cfg = {}; - _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN; // Start with open, will be set properly by SetCredentials - _wifi_cfg.sta.pmf_cfg.capable = false; // Disable PMF by default + _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN; // Start with open, will be set properly by SetCredentials + _wifi_cfg.sta.pmf_cfg.capable = false; // Disable PMF by default _wifi_cfg.sta.pmf_cfg.required = false; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); diff --git a/components/wifiManager/wifiManager/wifiManager.hpp b/components/wifiManager/wifiManager/wifiManager.hpp index 8a78826..27d9a36 100644 --- a/components/wifiManager/wifiManager/wifiManager.hpp +++ b/components/wifiManager/wifiManager/wifiManager.hpp @@ -19,9 +19,6 @@ #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 -extern int s_retry_num; -extern EventGroupHandle_t s_wifi_event_group; - namespace WiFiManagerHelpers { void event_handler(void *arg, esp_event_base_t event_base, diff --git a/main/openiris_main.cpp b/main/openiris_main.cpp index 6355673..3fdc793 100644 --- a/main/openiris_main.cpp +++ b/main/openiris_main.cpp @@ -30,10 +30,8 @@ #define CONFIG_LED_C_PIN_GPIO (gpio_num_t) CONFIG_LED_C_PIN esp_timer_handle_t timerHandle; -esp_timer_handle_t startupTimerHandle; QueueHandle_t eventQueue = xQueueCreate(10, sizeof(SystemEvent)); QueueHandle_t ledStateQueue = xQueueCreate(10, sizeof(uint32_t)); -bool startupCommandReceived = false; auto *stateManager = new StateManager(eventQueue, ledStateQueue); auto dependencyRegistry = std::make_shared(); @@ -99,22 +97,16 @@ void start_video_streaming(void *arg) { #ifdef CONFIG_WIRED_MODE ESP_LOGI("[MAIN]", "Starting UVC streaming mode."); - // Initialize UVC if not already done - static bool uvcInitialized = false; - if (!uvcInitialized) + ESP_LOGI("[MAIN]", "Initializing UVC hardware..."); + esp_err_t ret = uvcStream.setup(); + if (ret != ESP_OK) { - ESP_LOGI("[MAIN]", "Initializing UVC hardware..."); - esp_err_t ret = uvcStream.setup(); - if (ret != ESP_OK) - { - ESP_LOGE("[MAIN]", "Failed to initialize UVC: %s", esp_err_to_name(ret)); - return; - } - uvcInitialized = true; + ESP_LOGE("[MAIN]", "Failed to initialize UVC: %s", esp_err_to_name(ret)); + return; } uvcStream.start(); #else - ESP_LOGE("[MAIN]", "UVC mode selected but CONFIG_WIRED_MODE not enabled in build!"); + ESP_LOGE("[MAIN]", "UVC mode selected but the board likely does not support it."); ESP_LOGI("[MAIN]", "Falling back to WiFi mode if credentials available"); deviceMode = StreamingMode::WIFI; #endif @@ -159,10 +151,10 @@ void activate_streaming(TaskHandle_t serialTaskHandle = nullptr) void startup_timer_callback(void *arg) { ESP_LOGI("[MAIN]", "Startup timer fired, startupCommandReceived=%s, startupPaused=%s", - startupCommandReceived ? "true" : "false", - startupPaused ? "true" : "false"); + getStartupCommandReceived() ? "true" : "false", + getStartupPaused() ? "true" : "false"); - if (!startupCommandReceived && !startupPaused) + if (!getStartupCommandReceived() && !getStartupPaused()) { ESP_LOGI("[MAIN]", "No command received during startup delay, proceeding with automatic mode startup"); @@ -216,7 +208,7 @@ void startup_timer_callback(void *arg) } else { - if (startupPaused) + if (getStartupPaused()) { ESP_LOGI("[MAIN]", "Startup paused, staying in heartbeat mode"); } @@ -227,23 +219,8 @@ void startup_timer_callback(void *arg) } // Delete the timer after it fires - esp_timer_delete(startupTimerHandle); - startupTimerHandle = nullptr; -} - -// Function to notify that a command was received during startup -void notify_startup_command_received() -{ - startupCommandReceived = true; - - // Cancel the startup timer if it's still running - if (startupTimerHandle != nullptr) - { - esp_timer_stop(startupTimerHandle); - esp_timer_delete(startupTimerHandle); - startupTimerHandle = nullptr; - ESP_LOGI("[MAIN]", "Startup timer cancelled, staying in heartbeat mode"); - } + esp_timer_delete(timerHandle); + timerHandle = nullptr; } extern "C" void app_main(void) @@ -337,9 +314,6 @@ extern "C" void app_main(void) restAPI->begin(); cameraHandler->setupCamera(); - // Don't initialize UVC here - wait for the timer to decide - // UVC will be initialized when streaming actually starts - xTaskCreate( HandleRestAPIPollTask, "HandleRestAPIPollTask", @@ -354,23 +328,6 @@ extern "C" void app_main(void) ESP_LOGI("[MAIN]", "====================================="); ESP_LOGI("[MAIN]", "Device will wait 20 seconds for commands..."); - // Get the stored device mode - StreamingMode deviceMode = deviceConfig->getDeviceMode(); - ESP_LOGI("[MAIN]", "Stored device mode: %s (value: %d)", - deviceMode == StreamingMode::UVC ? "UVC" : deviceMode == StreamingMode::WIFI ? "WiFi" - : "Auto", - (int)deviceMode); - - // If WiFi credentials exist, attempt connection but stay in setup mode - if (!deviceConfig->getWifiConfigs().empty()) - { - ESP_LOGI("[MAIN]", "WiFi credentials found, attempting connection in background"); - // WiFi connection happens in wifiManager->Begin() above - } - - // Reset startup state - startupCommandReceived = false; - // Create a one-shot timer for 20 seconds const esp_timer_create_args_t startup_timer_args = { .callback = &startup_timer_callback, @@ -379,13 +336,10 @@ extern "C" void app_main(void) .name = "startup_timer", .skip_unhandled_events = false}; - ESP_ERROR_CHECK(esp_timer_create(&startup_timer_args, &startupTimerHandle)); - ESP_ERROR_CHECK(esp_timer_start_once(startupTimerHandle, 20000000)); // 20 seconds in microseconds + ESP_ERROR_CHECK(esp_timer_create(&startup_timer_args, &timerHandle)); + ESP_ERROR_CHECK(esp_timer_start_once(timerHandle, 20000000)); // 20 seconds in microseconds ESP_LOGI("[MAIN]", "Started 20-second startup timer"); ESP_LOGI("[MAIN]", "Send any command within 20 seconds to enter heartbeat mode"); - - // Set global handles for component access - setStreamingTimerHandle(&timerHandle); setSerialManagerHandle(&serialManagerHandle); } diff --git a/tools/openiris_setup.py b/tools/openiris_setup.py index e4996eb..c14eddf 100644 --- a/tools/openiris_setup.py +++ b/tools/openiris_setup.py @@ -6,11 +6,13 @@ This tool automatically discovers OpenIris devices via heartbeat, allows WiFi configuration, and monitors the device logs. """ +import re import json import time import threading import argparse import sys +import string from typing import Dict, List, Optional, Tuple import serial import serial.tools.list_ports @@ -98,7 +100,6 @@ class OpenIrisDevice: cmd_obj["commands"][0]["data"] = params cmd_json = json.dumps(cmd_obj) + '\n' - try: # Clear any pending data self.connection.reset_input_buffer() @@ -130,9 +131,6 @@ class OpenIrisDevice: print(f"šŸ“” Raw: {repr(data)}") print(f"šŸ“ Buffer: {repr(response_buffer[-200:])}") - # Clean buffer and look for JSON - import re - # Remove ANSI escape sequences clean_buffer = re.sub(r'\x1b\[[0-9;]*m', '', response_buffer) clean_buffer = clean_buffer.replace('\r', '') @@ -303,6 +301,26 @@ class OpenIrisDevice: print("āœ… WiFi credentials set successfully") return True + def set_mdns_name(self, name: str) -> bool: + """Configure the MDNS hostname""" + + print(f"šŸ”§ Setting MDNS name to '{name}'...") + print(" The device should be accessible under") + print(f"http://{name}.local/") + print("\n Note, this will also modify the name of the UVC device") + + params = { + "hostname": name + } + + response = self.send_command("set_mdns", params) + if "error" in response: + print(f"āŒ MDNS name setup failed: {response['error']}") + return False + + print("āœ… MDNS name set successfully") + return True + def get_wifi_status(self) -> Dict: """Get current WiFi connection status""" response = self.send_command("get_wifi_status") @@ -561,8 +579,89 @@ class OpenIrisDiscovery: pass -def display_networks(networks: List[WiFiNetwork]): +def scan_networks(device: OpenIrisDevice, args = None): + should_use_custom_timeout = input("Use custom scan timeout? (y/N): ").strip().lower() + if should_use_custom_timeout == 'y': + try: + timeout = int(input("Enter timeout in seconds (5-120): ")) + if 5 <= timeout <= 120: + device.scan_networks(timeout) + else: + print("āŒ Timeout must be between 5 and 120 seconds") + device.scan_networks(args.scan_timeout) + except ValueError: + print("āŒ Invalid timeout, using default") + device.scan_networks(args.scan_timeout) + else: + device.scan_networks(args.scan_timeout) + + +def configure_wifi(device: OpenIrisDevice, args = None): + if not device.networks: + print("āŒ No networks available. Please scan first.") + return + + display_networks(device) + + while True: + net_choice = input("\nEnter network number (or 'back'): ").strip() + if net_choice.lower() == 'back': + break + + try: + net_idx = int(net_choice) - 1 + except ValueError: + print("āŒ Please enter a number or 'back'") + continue + + sorted_networks = sorted(device.networks, key=lambda x: x.rssi, reverse=True) + + if 0 <= net_idx < len(sorted_networks): + selected_network = sorted_networks[net_idx] + + print(f"\nšŸ” Selected: {selected_network.ssid}") + print(f"Security: {selected_network.security_type}") + + if selected_network.auth_mode == 0: # Open network + password = "" + print("šŸ”“ Open network - no password required") + else: + password = input("Enter WiFi password: ") + + if device.set_wifi(selected_network.ssid, password): + print("āœ… WiFi configured successfully!") + print("šŸ’” Next steps:") + print(" 4. Check WiFi status") + print(" 5. Connect to WiFi (if needed)") + print(" 6. Start streaming when connected") + break + else: + print("āŒ Invalid network number") + + +def configure_mdns(device: OpenIrisDevice, args = None): + print("šŸ’” Please enter your preferred device name, your board will be accessible under http://.local/") + print("šŸ’” Please avoid spaces and special characters") + print(" To back out, enter `back`") + print("\n Note, this will also modify the name of the UVC device") + while True: + name_choice = input("\nDevice name: ").strip() + if any(space in name_choice for space in string.whitespace): + print("āŒ Please avoid spaces and special characters") + else: + break + + if name_choice.lower() == "back": + return + + if device.set_mdns_name(name_choice): + print("āœ… MDNS configured successfully!") + + +def display_networks(device: OpenIrisDevice, args = None): """Display available WiFi networks in a formatted table""" + networks = device.networks + if not networks: print("āŒ No networks available") return @@ -596,6 +695,65 @@ def display_networks(networks: List[WiFiNetwork]): print() +def check_wifi_status(device: OpenIrisDevice, args = None): + status = device.get_wifi_status() + if status: + print(f"šŸ“¶ WiFi Status: {status.get('status', 'unknown')}") + print(f"šŸ“” Networks configured: {status.get('networks_configured', 0)}") + if status.get('ip_address'): + print(f"🌐 IP Address: {status['ip_address']}") + else: + print("āŒ Unable to get WiFi status") + + +def attempt_wifi_connection(device: OpenIrisDevice, args = None): + device.connect_wifi() + print("šŸ•°ļø Wait a few seconds then check status (option 4)") + + +def start_streaming(device: OpenIrisDevice, args = None): + device.start_streaming() + print("šŸš€ Streaming started! Use option 8 to monitor logs.") + + +def switch_device_mode(device: OpenIrisDevice, args = None): + current_mode = device.get_device_mode() + print(f"\nšŸ“ Current device mode: {current_mode}") + print("\nšŸ”„ Select new device mode:") + print("1. WiFi - Stream over WiFi connection") + print("2. UVC - Stream as USB webcam") + print("3. Auto - Automatic mode selection") + + mode_choice = input("\nSelect mode (1-3): ").strip() + + if mode_choice == "1": + device.switch_mode("wifi") + elif mode_choice == "2": + device.switch_mode("uvc") + elif mode_choice == "3": + device.switch_mode("auto") + else: + print("āŒ Invalid mode selection") + + +def monitor_logs(device: OpenIrisDevice, args = None): + device.monitor_logs() + + +COMMANDS_MAP = { + "1": scan_networks, + "2": display_networks, + "3": configure_wifi, + "4": configure_mdns, + "5": configure_mdns, + "6": check_wifi_status, + "7": attempt_wifi_connection, + "8": start_streaming, + "9": switch_device_mode, + "10": monitor_logs, +} + + def main(): parser = argparse.ArgumentParser(description="OpenIris Setup CLI Tool") parser.add_argument("--timeout", type=int, default=3, @@ -684,123 +842,25 @@ def main(): print("1. šŸ” Scan for WiFi networks") print("2. šŸ“” Show available networks") print("3. šŸ” Configure WiFi") - print("4. šŸ“¶ Check WiFi status") - print("5. šŸ”— Connect to WiFi") - print("6. šŸš€ Start streaming mode") - print("7. šŸ”„ Switch device mode (WiFi/UVC/Auto)") - print("8. šŸ“‹ Monitor logs") - print("9. 🚪 Exit") + print("4. 🌐 Configure MDNS") + print("5. šŸ’» Configure UVC Name") + print("6. šŸ“¶ Check WiFi status") + print("7. šŸ”— Connect to WiFi") + print("8. šŸš€ Start streaming mode") + print("9. šŸ”„ Switch device mode (WiFi/UVC/Auto)") + print("10. šŸ“‹ Monitor logs") + print("exit. 🚪 Exit") + choice = input("\nSelect option (1-10): ").strip() - choice = input("\nSelect option (1-9): ").strip() - - if choice == "1": - # Ask if user wants custom timeout - custom = input("Use custom scan timeout? (y/N): ").strip().lower() - if custom == 'y': - try: - timeout = int(input("Enter timeout in seconds (5-120): ")) - if 5 <= timeout <= 120: - device.scan_networks(timeout) - else: - print("āŒ Timeout must be between 5 and 120 seconds") - device.scan_networks(args.scan_timeout) - except ValueError: - print("āŒ Invalid timeout, using default") - device.scan_networks(args.scan_timeout) - else: - device.scan_networks(args.scan_timeout) - - elif choice == "2": - display_networks(device.networks) - - elif choice == "3": - if not device.networks: - print("āŒ No networks available. Please scan first.") - continue - - display_networks(device.networks) - - while True: - try: - net_choice = input("\nEnter network number (or 'back'): ").strip() - if net_choice.lower() == 'back': - break - - net_idx = int(net_choice) - 1 - sorted_networks = sorted(device.networks, key=lambda x: x.rssi, reverse=True) - - if 0 <= net_idx < len(sorted_networks): - selected_network = sorted_networks[net_idx] - - print(f"\nšŸ” Selected: {selected_network.ssid}") - print(f"Security: {selected_network.security_type}") - - if selected_network.auth_mode == 0: # Open network - password = "" - print("šŸ”“ Open network - no password required") - else: - password = input("Enter WiFi password: ") - - if device.set_wifi(selected_network.ssid, password): - print("āœ… WiFi configured successfully!") - print("šŸ’” Next steps:") - print(" 4. Check WiFi status") - print(" 5. Connect to WiFi (if needed)") - print(" 6. Start streaming when connected") - break - else: - print("āŒ Invalid network number") - except ValueError: - print("āŒ Please enter a number or 'back'") - - elif choice == "4": - # Check WiFi status - status = device.get_wifi_status() - if status: - print(f"šŸ“¶ WiFi Status: {status.get('status', 'unknown')}") - print(f"šŸ“” Networks configured: {status.get('networks_configured', 0)}") - if status.get('ip_address'): - print(f"🌐 IP Address: {status['ip_address']}") - else: - print("āŒ Unable to get WiFi status") - - elif choice == "5": - # Attempt WiFi connection - device.connect_wifi() - print("šŸ•°ļø Wait a few seconds then check status (option 4)") - - elif choice == "6": - device.start_streaming() - print("šŸš€ Streaming started! Use option 8 to monitor logs.") - - elif choice == "7": - # Switch device mode - current_mode = device.get_device_mode() - print(f"\nšŸ“ Current device mode: {current_mode}") - print("\nšŸ”„ Select new device mode:") - print("1. WiFi - Stream over WiFi connection") - print("2. UVC - Stream as USB webcam") - print("3. Auto - Automatic mode selection") - - mode_choice = input("\nSelect mode (1-3): ").strip() - - if mode_choice == "1": - device.switch_mode("wifi") - elif mode_choice == "2": - device.switch_mode("uvc") - elif mode_choice == "3": - device.switch_mode("auto") - else: - print("āŒ Invalid mode selection") - - elif choice == "8": - device.monitor_logs() - - elif choice == "9": + if choice == "exit": break - - else: + + command_to_run = COMMANDS_MAP.get(choice, None) + if not command_to_run: print("āŒ Invalid option") + continue + + command_to_run(device, args) except KeyboardInterrupt: print("\nšŸ›‘ Setup interrupted") diff --git a/tools/wifi_scanner.py b/tools/wifi_scanner.py index d5397f6..de9394a 100644 --- a/tools/wifi_scanner.py +++ b/tools/wifi_scanner.py @@ -28,7 +28,7 @@ class ESPWiFiScanner: self.serial.reset_input_buffer() - command = '{"commands":[{"command":"scan_networks","data":{}}]}\n' + command = '{"commands":[{"command":"scan_networks"}]}\n' self.serial.write(command.encode()) timeout_start = time.time() @@ -92,7 +92,7 @@ def main(): import argparse parser = argparse.ArgumentParser(description='ESP32 WiFi Scanner') - parser.add_argument('port', nargs='?', default='COM15', help='Serial port (default: COM9)') + parser.add_argument('port', nargs='?', help='Serial port - COM1, /dev/ttyUSB0, etc.') parser.add_argument('-t', '--timeout', type=int, default=30, help='Scan timeout in seconds (default: 30)') args = parser.parse_args()