Improvements and refactors after CR, add option to modify mdns name and simplify setup tool

This commit is contained in:
Lorow
2025-08-12 23:48:44 +02:00
parent 9326746e1d
commit 5a86ae042f
21 changed files with 668 additions and 574 deletions

View File

@@ -25,66 +25,70 @@ std::unordered_map<std::string, CommandType> commandTypeMap = {
{"get_device_mode", CommandType::GET_DEVICE_MODE}, {"get_device_mode", CommandType::GET_DEVICE_MODE},
}; };
std::function<CommandResult()> CommandManager::createCommand(const CommandType type, std::string_view json) const { std::function<CommandResult()> CommandManager::createCommand(const CommandType type, std::string_view json) const
{
switch (type) switch (type)
{ {
case CommandType::PING: case CommandType::PING:
return { PingCommand }; return {PingCommand};
case CommandType::PAUSE: case CommandType::PAUSE:
return [json] { return [json]
PausePayload payload; { return PauseCommand(json); };
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);
};
case CommandType::SET_STREAMING_MODE: 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: 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: case CommandType::SET_WIFI:
return [this, json] { return setWiFiCommand(this->registry, json); }; return [this, json]
{ return setWiFiCommand(this->registry, json); };
case CommandType::UPDATE_WIFI: 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: 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: case CommandType::DELETE_NETWORK:
return [this, json] { return deleteWiFiCommand(this->registry, json); }; return [this, json]
{ return deleteWiFiCommand(this->registry, json); };
case CommandType::SET_MDNS: case CommandType::SET_MDNS:
return [this, json] { return setMDNSCommand(this->registry, json); }; return [this, json]
{ return setMDNSCommand(this->registry, json); };
case CommandType::UPDATE_CAMERA: case CommandType::UPDATE_CAMERA:
return [this, json] { return updateCameraCommand(this->registry, json); }; return [this, json]
{ return updateCameraCommand(this->registry, json); };
case CommandType::RESTART_CAMERA: case CommandType::RESTART_CAMERA:
return [this, json] { return restartCameraCommand(this->registry, json); }; return [this, json]
{ return restartCameraCommand(this->registry, json); };
case CommandType::GET_CONFIG: case CommandType::GET_CONFIG:
return [this] { return getConfigCommand(this->registry); }; return [this]
{ return getConfigCommand(this->registry); };
case CommandType::SAVE_CONFIG: case CommandType::SAVE_CONFIG:
return [this] { return saveConfigCommand(this->registry); }; return [this]
{ return saveConfigCommand(this->registry); };
case CommandType::RESET_CONFIG: case CommandType::RESET_CONFIG:
return [this, json] { return resetConfigCommand(this->registry, json); }; return [this, json]
{ return resetConfigCommand(this->registry, json); };
case CommandType::RESTART_DEVICE: case CommandType::RESTART_DEVICE:
return restartDeviceCommand; return restartDeviceCommand;
case CommandType::SCAN_NETWORKS: case CommandType::SCAN_NETWORKS:
return [this] { return scanNetworksCommand(this->registry); }; return [this]
{ return scanNetworksCommand(this->registry); };
case CommandType::START_STREAMING: case CommandType::START_STREAMING:
return startStreamingCommand; return startStreamingCommand;
case CommandType::GET_WIFI_STATUS: case CommandType::GET_WIFI_STATUS:
return [this] { return getWiFiStatusCommand(this->registry); }; return [this]
{ return getWiFiStatusCommand(this->registry); };
case CommandType::CONNECT_WIFI: case CommandType::CONNECT_WIFI:
return [this] { return connectWiFiCommand(this->registry); }; return [this]
{ return connectWiFiCommand(this->registry); };
case CommandType::SWITCH_MODE: 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: case CommandType::GET_DEVICE_MODE:
return [this] { return getDeviceModeCommand(this->registry); }; return [this]
{ return getDeviceModeCommand(this->registry); };
default: default:
return nullptr; return nullptr;
} }
@@ -135,7 +139,7 @@ CommandResult CommandManager::executeFromJson(const std::string_view json) const
cJSON_AddItemToArray(responses, response); cJSON_AddItemToArray(responses, response);
} }
char* jsonString = cJSON_Print(responseDocument); char *jsonString = cJSON_Print(responseDocument);
cJSON_Delete(responseDocument); cJSON_Delete(responseDocument);
cJSON_Delete(parsedJson); cJSON_Delete(parsedJson);

View File

@@ -22,30 +22,30 @@ public:
CommandResult(std::string message, const Status status) CommandResult(std::string message, const Status status)
{ {
this->status = status; this->status = status;
// Escape quotes and backslashes in the message for JSON // Escape quotes and backslashes in the message for JSON
std::string escapedMessage = message; std::string escapedMessage = message;
size_t pos = 0; size_t pos = 0;
// First escape backslashes // First escape backslashes
while ((pos = escapedMessage.find('\\', pos)) != std::string::npos) { while ((pos = escapedMessage.find('\\', pos)) != std::string::npos)
{
escapedMessage.replace(pos, 1, "\\\\"); escapedMessage.replace(pos, 1, "\\\\");
pos += 2; pos += 2;
} }
// Then escape quotes // Then escape quotes
pos = 0; pos = 0;
while ((pos = escapedMessage.find('"', pos)) != std::string::npos) { while ((pos = escapedMessage.find('"', pos)) != std::string::npos)
{
escapedMessage.replace(pos, 1, "\\\""); escapedMessage.replace(pos, 1, "\\\"");
pos += 2; pos += 2;
} }
if (status == Status::SUCCESS) 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 else
{ {
this->message = std::format("{}\"error\":\"{}\"{}", "{", escapedMessage, "}"); this->message = std::format("{{\"error\":\"{}\"}}", escapedMessage);
} }
} }
@@ -60,7 +60,7 @@ public:
{ {
return CommandResult(message, Status::FAILURE); return CommandResult(message, Status::FAILURE);
} }
// Create a result that returns raw JSON without wrapper // Create a result that returns raw JSON without wrapper
static CommandResult getRawJsonResult(const std::string &jsonMessage) static CommandResult getRawJsonResult(const std::string &jsonMessage)
{ {
@@ -70,7 +70,6 @@ public:
} }
std::string getResult() const { return this->message; } std::string getResult() const { return this->message; }
}; };
#endif #endif

View File

@@ -1,25 +1,24 @@
#include "device_commands.hpp" #include "device_commands.hpp"
#include <cJSON.h>
#include <ProjectConfig.hpp>
#include "esp_timer.h"
#include <main_globals.hpp>
// Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite // Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) { CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload)
{
const auto parsedJson = cJSON_Parse(jsonPayload.data()); const auto parsedJson = cJSON_Parse(jsonPayload.data());
if (parsedJson == nullptr) { if (parsedJson == nullptr)
{
return CommandResult::getErrorResult("Invalid payload"); return CommandResult::getErrorResult("Invalid payload");
} }
const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode"); const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode");
if (modeObject == nullptr) { if (modeObject == nullptr)
{
return CommandResult::getErrorResult("Invalid payload - missing mode"); return CommandResult::getErrorResult("Invalid payload - missing mode");
} }
const auto mode = modeObject->valueint; const auto mode = modeObject->valueint;
if (mode < 0 || mode > 2) { if (mode < 0 || mode > 2)
{
return CommandResult::getErrorResult("Invalid payload - unsupported mode"); return CommandResult::getErrorResult("Invalid payload - unsupported mode");
} }
@@ -29,10 +28,12 @@ CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry,
return CommandResult::getSuccessResult("Device mode set"); return CommandResult::getSuccessResult("Device mode set");
} }
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) { CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload)
{
const auto parsedJson = cJSON_Parse(jsonPayload.data()); const auto parsedJson = cJSON_Parse(jsonPayload.data());
if (parsedJson == nullptr) { if (parsedJson == nullptr)
{
return CommandResult::getErrorResult("Invalid payload"); return CommandResult::getErrorResult("Invalid payload");
} }
@@ -42,18 +43,23 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> re
auto OTAPassword = oldDeviceConfig.OTAPassword; auto OTAPassword = oldDeviceConfig.OTAPassword;
auto OTAPort = oldDeviceConfig.OTAPort; auto OTAPort = oldDeviceConfig.OTAPort;
if (const auto OTALoginObject = cJSON_GetObjectItem(parsedJson, "login"); OTALoginObject != nullptr) { if (const auto OTALoginObject = cJSON_GetObjectItem(parsedJson, "login"); OTALoginObject != nullptr)
if (const auto newLogin = OTALoginObject->valuestring; strcmp(newLogin, "") != 0) { {
if (const auto newLogin = OTALoginObject->valuestring; strcmp(newLogin, "") != 0)
{
OTALogin = newLogin; 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; OTAPassword = OTAPasswordObject->valuestring;
} }
if (const auto OTAPortObject = cJSON_GetObjectItem(parsedJson, "port"); OTAPortObject != nullptr) { if (const auto OTAPortObject = cJSON_GetObjectItem(parsedJson, "port"); OTAPortObject != nullptr)
if (const auto newPort = OTAPortObject->valueint; newPort >= 82) { {
if (const auto newPort = OTAPortObject->valueint; newPort >= 82)
{
OTAPort = newPort; OTAPort = newPort;
} }
} }
@@ -62,70 +68,82 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> re
return CommandResult::getSuccessResult("OTA Config set"); return CommandResult::getSuccessResult("OTA Config set");
} }
CommandResult restartDeviceCommand() { CommandResult restartDeviceCommand()
{
OpenIrisTasks::ScheduleRestart(2000); OpenIrisTasks::ScheduleRestart(2000);
return CommandResult::getSuccessResult("Device restarted"); return CommandResult::getSuccessResult("Device restarted");
} }
CommandResult startStreamingCommand() { CommandResult startStreamingCommand()
{
activateStreaming(false); // Don't disable setup interfaces by default activateStreaming(false); // Don't disable setup interfaces by default
return CommandResult::getSuccessResult("Streaming started"); return CommandResult::getSuccessResult("Streaming started");
} }
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) { CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload)
{
const auto parsedJson = cJSON_Parse(jsonPayload.data()); const auto parsedJson = cJSON_Parse(jsonPayload.data());
if (parsedJson == nullptr) { if (parsedJson == nullptr)
{
return CommandResult::getErrorResult("Invalid payload"); return CommandResult::getErrorResult("Invalid payload");
} }
const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode"); const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode");
if (modeObject == nullptr) { if (modeObject == nullptr)
{
return CommandResult::getErrorResult("Invalid payload - missing mode"); return CommandResult::getErrorResult("Invalid payload - missing mode");
} }
const char* modeStr = modeObject->valuestring; const char *modeStr = modeObject->valuestring;
StreamingMode newMode; StreamingMode newMode;
ESP_LOGI("[DEVICE_COMMANDS]", "Switch mode command received with mode: %s", modeStr); 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; 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; newMode = StreamingMode::WIFI;
} else if (strcmp(modeStr, "auto") == 0 || strcmp(modeStr, "AUTO") == 0) { }
else if (strcmp(modeStr, "auto") == 0)
{
newMode = StreamingMode::AUTO; newMode = StreamingMode::AUTO;
} else { }
else
{
return CommandResult::getErrorResult("Invalid mode - use 'uvc', 'wifi', or 'auto'"); return CommandResult::getErrorResult("Invalid mode - use 'uvc', 'wifi', or 'auto'");
} }
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config); const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
ESP_LOGI("[DEVICE_COMMANDS]", "Setting device mode to: %d", (int)newMode); ESP_LOGI("[DEVICE_COMMANDS]", "Setting device mode to: %d", (int)newMode);
projectConfig->setDeviceMode(newMode); projectConfig->setDeviceMode(newMode);
cJSON_Delete(parsedJson); cJSON_Delete(parsedJson);
return CommandResult::getSuccessResult("Device mode switched, restart to apply"); return CommandResult::getSuccessResult("Device mode switched, restart to apply");
} }
CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry) { CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry)
{
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config); const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
StreamingMode currentMode = projectConfig->getDeviceMode(); StreamingMode currentMode = projectConfig->getDeviceMode();
const char* modeStr = "unknown"; const char *modeStr = "unknown";
switch (currentMode) { switch (currentMode)
case StreamingMode::UVC: {
modeStr = "UVC"; case StreamingMode::UVC:
break; modeStr = "UVC";
case StreamingMode::WIFI: break;
modeStr = "WiFi"; case StreamingMode::WIFI:
break; modeStr = "WiFi";
case StreamingMode::AUTO: break;
modeStr = "Auto"; case StreamingMode::AUTO:
break; modeStr = "Auto";
break;
} }
char result[100]; auto result = std::format("{{ \"mode\": \"{}\", \"value\": {} }}", modeStr, static_cast<int>(currentMode));
sprintf(result, "{\"mode\":\"%s\",\"value\":%d}", modeStr, (int)currentMode);
return CommandResult::getSuccessResult(result); return CommandResult::getSuccessResult(result);
} }

View File

@@ -1,6 +1,13 @@
#include "CommandResult.hpp" #include "CommandResult.hpp"
#include "ProjectConfig.hpp"
#include "OpenIrisTasks.hpp" #include "OpenIrisTasks.hpp"
#include "DependencyRegistry.hpp" #include "DependencyRegistry.hpp"
#include "esp_timer.h"
#include "cJSON.h"
#include "main_globals.hpp"
#include <format>
#include <string>
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload); CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);

View File

@@ -1,11 +1,10 @@
#include "scan_commands.hpp" #include "scan_commands.hpp"
#include "cJSON.h"
#include "esp_log.h"
#include <string>
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry) { CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry)
{
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager); auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
if (!wifiManager) { if (!wifiManager)
{
return CommandResult::getErrorResult("WiFiManager not available"); return CommandResult::getErrorResult("WiFiManager not available");
} }
@@ -15,7 +14,8 @@ CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry)
cJSON *networksArray = cJSON_CreateArray(); cJSON *networksArray = cJSON_CreateArray();
cJSON_AddItemToObject(root, "networks", networksArray); cJSON_AddItemToObject(root, "networks", networksArray);
for (const auto& network : networks) { for (const auto &network : networks)
{
cJSON *networkObject = cJSON_CreateObject(); cJSON *networkObject = cJSON_CreateObject();
cJSON_AddStringToObject(networkObject, "ssid", network.ssid.c_str()); cJSON_AddStringToObject(networkObject, "ssid", network.ssid.c_str());
cJSON_AddNumberToObject(networkObject, "channel", network.channel); cJSON_AddNumberToObject(networkObject, "channel", network.channel);

View File

@@ -1,9 +1,12 @@
#ifndef SCAN_COMMANDS_HPP #ifndef SCAN_COMMANDS_HPP
#define SCAN_COMMANDS_HPP #define SCAN_COMMANDS_HPP
#include "../CommandResult.hpp" #include "CommandResult.hpp"
#include "../DependencyRegistry.hpp" #include "DependencyRegistry.hpp"
#include "esp_log.h"
#include <cJSON.h>
#include <wifiManager.hpp> #include <wifiManager.hpp>
#include <string>
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry); CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry);

View File

@@ -1,24 +1,39 @@
#include "simple_commands.hpp" #include "simple_commands.hpp"
#include "main_globals.hpp"
#include "esp_log.h"
static const char* TAG = "SimpleCommands"; static const char *TAG = "SimpleCommands";
CommandResult PingCommand() CommandResult PingCommand()
{ {
return CommandResult::getSuccessResult("pong"); 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"); ESP_LOGI(TAG, "Pause command received: %s", payload.pause ? "true" : "false");
startupPaused = payload.pause; setStartupPaused(payload.pause);
if (payload.pause)
if (payload.pause) { {
ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode"); ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode");
return CommandResult::getSuccessResult("Startup paused"); return CommandResult::getSuccessResult("Startup paused");
} else { }
else
{
ESP_LOGI(TAG, "Startup resumed"); ESP_LOGI(TAG, "Startup resumed");
return CommandResult::getSuccessResult("Startup resumed"); return CommandResult::getSuccessResult("Startup resumed");
} }

View File

@@ -4,8 +4,11 @@
#include <string> #include <string>
#include "CommandResult.hpp" #include "CommandResult.hpp"
#include "CommandSchema.hpp" #include "CommandSchema.hpp"
#include "main_globals.hpp"
#include "esp_log.h"
#include <cJSON.h>
CommandResult PingCommand(); CommandResult PingCommand();
CommandResult PauseCommand(const PausePayload& payload); CommandResult PauseCommand(std::string_view jsonPayload);
#endif #endif

View File

@@ -4,37 +4,47 @@
// Forward declarations // Forward declarations
extern void start_video_streaming(void *arg); extern void start_video_streaming(void *arg);
// Global variables to be set by main bool startupCommandReceived = false;
static esp_timer_handle_t* g_streaming_timer_handle = nullptr; bool getStartupCommandReceived()
static TaskHandle_t* g_serial_manager_handle = nullptr; {
return startupCommandReceived;
// Functions for main to set the global handles
void setStreamingTimerHandle(esp_timer_handle_t* handle) {
g_streaming_timer_handle = handle;
} }
void setSerialManagerHandle(TaskHandle_t* handle) { void setStartupCommandReceived(bool startupCommandReceived)
g_serial_manager_handle = handle; {
startupCommandReceived = startupCommandReceived;
} }
// Functions for components to access the handles static TaskHandle_t *g_serial_manager_handle = nullptr;
esp_timer_handle_t* getStreamingTimerHandle() { TaskHandle_t *getSerialManagerHandle()
return g_streaming_timer_handle; {
}
TaskHandle_t* getSerialManagerHandle() {
return g_serial_manager_handle; return g_serial_manager_handle;
} }
void setSerialManagerHandle(TaskHandle_t *serialManagerHandle)
{
g_serial_manager_handle = serialManagerHandle;
}
// Global pause state // Global pause state
bool startupPaused = false; bool startupPaused = false;
bool getStartupPaused()
{
return startupPaused;
}
void setStartupPaused(bool startupPaused)
{
startupPaused = startupPaused;
}
// Function to manually activate streaming // 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"); ESP_LOGI("[MAIN_GLOBALS]", "Manually activating streaming, disableSetup=%s", disableSetup ? "true" : "false");
TaskHandle_t* serialHandle = disableSetup ? g_serial_manager_handle : nullptr; TaskHandle_t *serialHandle = disableSetup ? g_serial_manager_handle : nullptr;
void* serialTaskHandle = (serialHandle && *serialHandle) ? *serialHandle : nullptr; void *serialTaskHandle = (serialHandle && *serialHandle) ? *serialHandle : nullptr;
start_video_streaming(serialTaskHandle); start_video_streaming(serialTaskHandle);
} }

View File

@@ -7,22 +7,18 @@
#include "freertos/task.h" #include "freertos/task.h"
// Functions for main to set global handles // 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 // 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 // Function to manually activate streaming
void activateStreaming(bool disableSetup = false); void activateStreaming(bool disableSetup = false);
// Function to notify that a command was received during startup bool getStartupCommandReceived();
extern void notify_startup_command_received(); void setStartupCommandReceived(bool startupCommandReceived);
// Global variables for startup state bool getStartupPaused();
extern bool startupCommandReceived; void setStartupPaused(bool startupPaused);
extern esp_timer_handle_t startupTimerHandle;
extern bool startupPaused;
#endif #endif

View File

@@ -9,7 +9,6 @@
#include "Models.hpp" #include "Models.hpp"
#include <Preferences.hpp> #include <Preferences.hpp>
int getNetworkCount(Preferences *pref); int getNetworkCount(Preferences *pref);
void saveNetworkCount(Preferences *pref, int count); void saveNetworkCount(Preferences *pref, int count);
@@ -60,7 +59,7 @@ public:
const std::string &password, const std::string &password,
uint8_t channel); uint8_t channel);
void setWiFiTxPower(uint8_t power); void setWiFiTxPower(uint8_t power);
void setDeviceMode(StreamingMode deviceMode); void setDeviceMode(StreamingMode deviceMode);
StreamingMode getDeviceMode(); StreamingMode getDeviceMode();
private: private:

View File

@@ -4,8 +4,9 @@
#define BUF_SIZE (1024) #define BUF_SIZE (1024)
SerialManager::SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t *timerHandle, std::shared_ptr<ProjectConfig> deviceConfig) SerialManager::SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t *timerHandle, std::shared_ptr<ProjectConfig> deviceConfig)
: commandManager(commandManager), timerHandle(timerHandle), deviceConfig(deviceConfig) { : commandManager(commandManager), timerHandle(timerHandle), deviceConfig(deviceConfig)
{
this->data = static_cast<uint8_t *>(malloc(BUF_SIZE)); this->data = static_cast<uint8_t *>(malloc(BUF_SIZE));
this->temp_data = static_cast<uint8_t *>(malloc(256)); this->temp_data = static_cast<uint8_t *>(malloc(256));
} }
@@ -40,47 +41,62 @@ void SerialManager::try_receive()
// Notify main that a command was received during startup // Notify main that a command was received during startup
notify_startup_command_received(); notify_startup_command_received();
const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char *>(this->data))); const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char *>(this->data)));
const auto resultMessage = result.getResult(); const auto resultMessage = result.getResult();
usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20); 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() void SerialManager::send_heartbeat()
{ {
// Get the MAC address as unique identifier // Get the MAC address as unique identifier
uint8_t mac[6]; uint8_t mac[6];
esp_read_mac(mac, ESP_MAC_WIFI_STA); esp_read_mac(mac, ESP_MAC_WIFI_STA);
// Format as serial number string // Format as serial number string
char serial_number[18]; 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]); mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
// Create heartbeat JSON with serial number // Create heartbeat JSON with serial number
char heartbeat[128]; char heartbeat[128];
sprintf(heartbeat, "{\"heartbeat\":\"openiris_setup_mode\",\"serial\":\"%s\"}\n", serial_number); sprintf(heartbeat, "{\"heartbeat\":\"openiris_setup_mode\",\"serial\":\"%s\"}\n", serial_number);
usb_serial_jtag_write_bytes(heartbeat, strlen(heartbeat), 1000 / 20); usb_serial_jtag_write_bytes(heartbeat, strlen(heartbeat), 1000 / 20);
} }
bool SerialManager::should_send_heartbeat() bool SerialManager::should_send_heartbeat()
{ {
// Always send heartbeat during startup delay or if no WiFi configured // 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 startup timer is still running, always send heartbeat
if (startupTimerHandle != nullptr) { if (timerHandle != nullptr)
{
return true; return true;
} }
// If in heartbeat mode after startup, continue sending // If in heartbeat mode after startup, continue sending
if (startupCommandReceived) { if (getStartupCommandReceived())
{
return true; return true;
} }
// Otherwise, only send if no WiFi credentials configured // Otherwise, only send if no WiFi credentials configured
const auto wifiConfigs = deviceConfig->getWifiConfigs(); const auto wifiConfigs = deviceConfig->getWifiConfigs();
return wifiConfigs.empty(); return wifiConfigs.empty();
@@ -95,16 +111,16 @@ void HandleSerialManagerTask(void *pvParameters)
while (true) while (true)
{ {
serialManager->try_receive(); serialManager->try_receive();
// Send heartbeat every 2 seconds, but only if no WiFi credentials are set // Send heartbeat every 2 seconds, but only if no WiFi credentials are set
TickType_t currentTime = xTaskGetTickCount(); TickType_t currentTime = xTaskGetTickCount();
if ((currentTime - lastHeartbeat) >= heartbeatInterval) { if ((currentTime - lastHeartbeat) >= heartbeatInterval)
if (serialManager->should_send_heartbeat()) { {
if (serialManager->should_send_heartbeat())
{
serialManager->send_heartbeat(); serialManager->send_heartbeat();
} }
lastHeartbeat = currentTime; lastHeartbeat = currentTime;
} }
vTaskDelay(pdMS_TO_TICKS(50)); // Small delay to prevent busy waiting
} }
} }

View File

@@ -26,6 +26,7 @@ public:
void try_receive(); void try_receive();
void send_heartbeat(); void send_heartbeat();
bool should_send_heartbeat(); bool should_send_heartbeat();
void notify_startup_command_received();
private: private:
std::shared_ptr<CommandManager> commandManager; std::shared_ptr<CommandManager> commandManager;

View File

@@ -3,25 +3,29 @@ constexpr int UVC_MAX_FRAMESIZE_SIZE(75 * 1024);
static const char *UVC_STREAM_TAG = "[UVC DEVICE]"; static const char *UVC_STREAM_TAG = "[UVC DEVICE]";
extern "C" { extern "C"
{
static char serial_number_str[13]; static char serial_number_str[13];
const char *get_uvc_device_name() { const char *get_uvc_device_name()
{
return deviceConfig->getMDNSConfig().hostname.c_str(); return deviceConfig->getMDNSConfig().hostname.c_str();
} }
const char *get_serial_number(void) { const char *get_serial_number(void)
if (serial_number_str[0] == '\0') { {
if (serial_number_str[0] == '\0')
{
uint8_t mac_address[6]; uint8_t mac_address[6];
esp_err_t result = esp_efuse_mac_get_default(mac_address); 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"); ESP_LOGE(UVC_STREAM_TAG, "Failed to get MAC address of the board, returning default serial number");
return CONFIG_TUSB_SERIAL_NUM; return CONFIG_TUSB_SERIAL_NUM;
} }
sniprintf(serial_number_str, sizeof(serial_number_str), "%02x:%02x:%02x:%02x:%02x:%02x", 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; return serial_number_str;
} }

View File

@@ -1,32 +1,38 @@
#include "WiFiScanner.hpp" #include "WiFiScanner.hpp"
#include <cstring> #include <cstring>
static const char *TAG = "WiFiScanner";
WiFiScanner::WiFiScanner() {} WiFiScanner::WiFiScanner() {}
void WiFiScanner::scanResultCallback(void* arg, esp_event_base_t event_base, void WiFiScanner::scanResultCallback(void *arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) { int32_t event_id, void *event_data)
auto* scanner = static_cast<WiFiScanner*>(arg); {
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE) { auto *scanner = static_cast<WiFiScanner *>(arg);
if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_SCAN_DONE)
{
uint16_t ap_count = 0; uint16_t ap_count = 0;
esp_wifi_scan_get_ap_num(&ap_count); esp_wifi_scan_get_ap_num(&ap_count);
if (ap_count == 0) { if (ap_count == 0)
{
ESP_LOGI(TAG, "No access points found"); ESP_LOGI(TAG, "No access points found");
return; 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)); ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&ap_count, ap_records));
scanner->networks.clear(); scanner->networks.clear();
for (uint16_t i = 0; i < ap_count; i++) { for (uint16_t i = 0; i < ap_count; i++)
{
WiFiNetwork network; WiFiNetwork network;
network.ssid = std::string(reinterpret_cast<char*>(ap_records[i].ssid)); network.ssid = std::string(reinterpret_cast<char *>(ap_records[i].ssid));
network.channel = ap_records[i].primary; network.channel = ap_records[i].primary;
network.rssi = ap_records[i].rssi; network.rssi = ap_records[i].rssi;
memcpy(network.mac, ap_records[i].bssid, 6); memcpy(network.mac, ap_records[i].bssid, 6);
network.auth_mode = ap_records[i].authmode; network.auth_mode = ap_records[i].authmode;
scanner->networks.push_back(network); scanner->networks.push_back(network);
} }
@@ -35,81 +41,88 @@ void WiFiScanner::scanResultCallback(void* arg, esp_event_base_t event_base,
} }
} }
std::vector<WiFiNetwork> WiFiScanner::scanNetworks() { // todo this is garbage
std::vector<WiFiNetwork> WiFiScanner::scanNetworks()
{
std::vector<WiFiNetwork> scan_results; std::vector<WiFiNetwork> scan_results;
// Check if WiFi is initialized // Check if WiFi is initialized
wifi_mode_t mode; wifi_mode_t mode;
esp_err_t err = esp_wifi_get_mode(&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"); ESP_LOGE(TAG, "WiFi not initialized");
return scan_results; return scan_results;
} }
// Give WiFi more time to be ready // Give WiFi more time to be ready
vTaskDelay(pdMS_TO_TICKS(500)); vTaskDelay(pdMS_TO_TICKS(500));
// Stop any ongoing scan // Stop any ongoing scan
esp_wifi_scan_stop(); esp_wifi_scan_stop();
// Try sequential channel scanning as a workaround // Try sequential channel scanning as a workaround
bool try_sequential_scan = true; // Enable sequential scan bool try_sequential_scan = true; // Enable sequential scan
if (!try_sequential_scan) { if (!try_sequential_scan)
{
// Normal all-channel scan // Normal all-channel scan
wifi_scan_config_t scan_config = { wifi_scan_config_t scan_config = {
.ssid = nullptr, .ssid = nullptr,
.bssid = nullptr, .bssid = nullptr,
.channel = 0, // 0 means scan all channels .channel = 0, // 0 means scan all channels
.show_hidden = true, .show_hidden = true,
.scan_type = WIFI_SCAN_TYPE_ACTIVE, // Active scan .scan_type = WIFI_SCAN_TYPE_ACTIVE, // Active scan
.scan_time = { .scan_time = {
.active = { .active = {
.min = 120, // Min per channel .min = 120, // Min per channel
.max = 300 // Max per channel .max = 300 // Max per channel
}, },
.passive = 360 .passive = 360},
}, .home_chan_dwell_time = 0, // 0 for default
.home_chan_dwell_time = 0, // 0 for default .channel_bitmap = 0 // 0 for all channels
.channel_bitmap = 0 // 0 for all channels
}; };
err = esp_wifi_scan_start(&scan_config, false); 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)); ESP_LOGE(TAG, "Failed to start scan: %s", esp_err_to_name(err));
return scan_results; return scan_results;
} }
} else { }
else
{
// Sequential channel scan - scan each channel individually // Sequential channel scan - scan each channel individually
std::vector<wifi_ap_record_t> all_records; std::vector<wifi_ap_record_t> all_records;
for (uint8_t ch = 1; ch <= 13; ch++) { for (uint8_t ch = 1; ch <= 13; ch++)
{
wifi_scan_config_t scan_config = { wifi_scan_config_t scan_config = {
.ssid = nullptr, .ssid = nullptr,
.bssid = nullptr, .bssid = nullptr,
.channel = ch, // Specific channel .channel = ch,
.show_hidden = true, .show_hidden = true,
.scan_type = WIFI_SCAN_TYPE_ACTIVE, .scan_type = WIFI_SCAN_TYPE_ACTIVE,
.scan_time = { .scan_time = {
.active = { .active = {
.min = 100, .min = 100,
.max = 200 .max = 200},
}, .passive = 300},
.passive = 300
},
.home_chan_dwell_time = 0, .home_chan_dwell_time = 0,
.channel_bitmap = 0 .channel_bitmap = 0};
};
err = esp_wifi_scan_start(&scan_config, true); // Blocking scan err = esp_wifi_scan_start(&scan_config, true); // Blocking scan
if (err == ESP_OK) { if (err == ESP_OK)
{
uint16_t ch_count = 0; uint16_t ch_count = 0;
esp_wifi_scan_get_ap_num(&ch_count); esp_wifi_scan_get_ap_num(&ch_count);
if (ch_count > 0) { 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) { wifi_ap_record_t *ch_records = new wifi_ap_record_t[ch_count];
for (uint16_t i = 0; i < ch_count; i++) { 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]); all_records.push_back(ch_records[i]);
} }
} }
@@ -118,59 +131,66 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks() {
} }
vTaskDelay(pdMS_TO_TICKS(50)); vTaskDelay(pdMS_TO_TICKS(50));
} }
// Process all collected records // Process all collected records
for (const auto& record : all_records) { for (const auto &record : all_records)
{
WiFiNetwork network; WiFiNetwork network;
network.ssid = std::string(reinterpret_cast<const char*>(record.ssid)); network.ssid = std::string(reinterpret_cast<const char *>(record.ssid));
network.channel = record.primary; network.channel = record.primary;
network.rssi = record.rssi; network.rssi = record.rssi;
memcpy(network.mac, record.bssid, 6); memcpy(network.mac, record.bssid, 6);
network.auth_mode = record.authmode; network.auth_mode = record.authmode;
scan_results.push_back(network); scan_results.push_back(network);
} }
// Skip the normal result processing // Skip the normal result processing
return scan_results; return scan_results;
} }
// Wait for scan completion with timeout // Wait for scan completion with timeout
int timeout_ms = 15000; // 15 second timeout int timeout_ms = 15000; // 15 second timeout
int elapsed_ms = 0; int elapsed_ms = 0;
while (elapsed_ms < timeout_ms) { while (elapsed_ms < timeout_ms)
{
uint16_t temp_count = 0; uint16_t temp_count = 0;
esp_err_t count_err = esp_wifi_scan_get_ap_num(&temp_count); 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 // 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; break;
} }
} }
vTaskDelay(pdMS_TO_TICKS(200)); vTaskDelay(pdMS_TO_TICKS(200));
elapsed_ms += 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_LOGE(TAG, "Scan timeout after %d ms", timeout_ms);
esp_wifi_scan_stop(); esp_wifi_scan_stop();
return scan_results; return scan_results;
} }
// Get scan results // Get scan results
uint16_t ap_count = 0; uint16_t ap_count = 0;
esp_wifi_scan_get_ap_num(&ap_count); esp_wifi_scan_get_ap_num(&ap_count);
if (ap_count == 0) { if (ap_count == 0)
{
ESP_LOGI(TAG, "No access points found"); ESP_LOGI(TAG, "No access points found");
return scan_results; 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); 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)); ESP_LOGE(TAG, "Failed to get scan records: %s", esp_err_to_name(err));
delete[] ap_records; delete[] ap_records;
return scan_results; return scan_results;
@@ -178,28 +198,26 @@ std::vector<WiFiNetwork> WiFiScanner::scanNetworks() {
// Build the results vector and track channels found // Build the results vector and track channels found
bool channels_found[15] = {false}; // Track channels 0-14 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; WiFiNetwork network;
network.ssid = std::string(reinterpret_cast<char*>(ap_records[i].ssid)); network.ssid = std::string(reinterpret_cast<char *>(ap_records[i].ssid));
network.channel = ap_records[i].primary; network.channel = ap_records[i].primary;
network.rssi = ap_records[i].rssi; network.rssi = ap_records[i].rssi;
memcpy(network.mac, ap_records[i].bssid, 6); memcpy(network.mac, ap_records[i].bssid, 6);
network.auth_mode = ap_records[i].authmode; network.auth_mode = ap_records[i].authmode;
if (network.channel <= 14) { if (network.channel <= 14)
{
channels_found[network.channel] = true; channels_found[network.channel] = true;
} }
scan_results.push_back(network); scan_results.push_back(network);
} }
delete[] ap_records; delete[] ap_records;
ESP_LOGI(TAG, "Found %d access points", ap_count); ESP_LOGI(TAG, "Found %d access points", ap_count);
// Also update the member variable for compatibility
networks = scan_results;
return scan_results; return scan_results;
} }

View File

@@ -7,7 +7,8 @@
#include "esp_wifi.h" #include "esp_wifi.h"
#include "esp_log.h" #include "esp_log.h"
struct WiFiNetwork { struct WiFiNetwork
{
std::string ssid; std::string ssid;
uint8_t channel; uint8_t channel;
int8_t rssi; int8_t rssi;
@@ -15,14 +16,14 @@ struct WiFiNetwork {
wifi_auth_mode_t auth_mode; wifi_auth_mode_t auth_mode;
}; };
class WiFiScanner { class WiFiScanner
{
public: public:
WiFiScanner(); WiFiScanner();
std::vector<WiFiNetwork> scanNetworks(); std::vector<WiFiNetwork> 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: private:
static constexpr char const* TAG = "WiFiScanner";
std::vector<WiFiNetwork> networks; std::vector<WiFiNetwork> networks;
}; };

View File

@@ -2,7 +2,6 @@
static auto WIFI_MANAGER_TAG = "[WIFI_MANAGER]"; static auto WIFI_MANAGER_TAG = "[WIFI_MANAGER]";
// Define the global variables declared as extern in the header
int s_retry_num = 0; int s_retry_num = 0;
EventGroupHandle_t s_wifi_event_group; 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) 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<wifi_event_sta_disconnected_t *>(event_data);
ESP_LOGI(WIFI_MANAGER_TAG, "Disconnect reason: %d", disconnected->reason); ESP_LOGI(WIFI_MANAGER_TAG, "Disconnect reason: %d", disconnected->reason);
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
{ {
esp_wifi_connect(); esp_wifi_connect();
@@ -51,55 +50,52 @@ void WiFiManager::SetCredentials(const char *ssid, const char *password)
{ {
// Clear the config first // Clear the config first
memset(&_wifi_cfg, 0, sizeof(_wifi_cfg)); memset(&_wifi_cfg, 0, sizeof(_wifi_cfg));
// Copy SSID with null termination // Copy SSID with null termination
size_t ssid_len = std::min(strlen(ssid), sizeof(_wifi_cfg.sta.ssid) - 1); size_t ssid_len = std::min(strlen(ssid), sizeof(_wifi_cfg.sta.ssid) - 1);
memcpy(_wifi_cfg.sta.ssid, ssid, ssid_len); memcpy(_wifi_cfg.sta.ssid, ssid, ssid_len);
_wifi_cfg.sta.ssid[ssid_len] = '\0'; _wifi_cfg.sta.ssid[ssid_len] = '\0';
// Copy password with null termination // Copy password with null termination
size_t pass_len = std::min(strlen(password), sizeof(_wifi_cfg.sta.password) - 1); size_t pass_len = std::min(strlen(password), sizeof(_wifi_cfg.sta.password) - 1);
memcpy(_wifi_cfg.sta.password, password, pass_len); memcpy(_wifi_cfg.sta.password, password, pass_len);
_wifi_cfg.sta.password[pass_len] = '\0'; _wifi_cfg.sta.password[pass_len] = '\0';
// Set other required fields // Set other required fields
// Use open auth if no password, otherwise allow any WPA variant // 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; _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN;
} else { }
else
{
// IMPORTANT: Set threshold to WEP to accept ANY security mode >= WEP // 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 // This allows WPA, WPA2, WPA3, etc. The driver will negotiate the highest common mode
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WEP; _wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WEP;
} }
// CRITICAL: Disable PMF completely - this often causes handshake timeouts // CRITICAL: Disable PMF completely - this often causes handshake timeouts
_wifi_cfg.sta.pmf_cfg.capable = false; _wifi_cfg.sta.pmf_cfg.capable = false;
_wifi_cfg.sta.pmf_cfg.required = false; _wifi_cfg.sta.pmf_cfg.required = false;
// IMPORTANT: Set scan method to ALL channels // IMPORTANT: Set scan method to ALL channels
_wifi_cfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN; _wifi_cfg.sta.scan_method = WIFI_ALL_CHANNEL_SCAN;
_wifi_cfg.sta.bssid_set = 0; // Don't use specific BSSID _wifi_cfg.sta.bssid_set = 0; // Don't use specific BSSID
_wifi_cfg.sta.channel = 0; // Scan all channels _wifi_cfg.sta.channel = 0; // Scan all channels
// Additional settings that might help with compatibility // Additional settings that might help with compatibility
_wifi_cfg.sta.listen_interval = 0; // Default listen interval _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.sort_method = WIFI_CONNECT_AP_BY_SIGNAL; // Connect to strongest signal
// IMPORTANT: For WPA/WPA2 Personal networks // IMPORTANT: For WPA/WPA2 Personal networks
_wifi_cfg.sta.threshold.rssi = -127; // Accept any signal strength _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.sae_pwe_h2e = WPA3_SAE_PWE_UNSPECIFIED; // Let driver decide SAE mode
// Log what we're trying to connect to with detailed debugging // 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, "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, "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); _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() void WiFiManager::ConnectWithHardcodedCredentials()
@@ -164,11 +160,11 @@ void WiFiManager::ConnectWithStoredCredentials()
// Stop WiFi once before the loop // Stop WiFi once before the loop
esp_wifi_stop(); esp_wifi_stop();
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
// Ensure we're in STA mode // Ensure we're in STA mode
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); 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 // Reset retry counter for each network attempt
s_retry_num = 0; s_retry_num = 0;
@@ -177,19 +173,14 @@ void WiFiManager::ConnectWithStoredCredentials()
// Update config without stopping WiFi again // Update config without stopping WiFi again
ESP_LOGI(WIFI_MANAGER_TAG, "Attempting to connect to SSID: '%s'", network.ssid.c_str()); 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)); ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg));
xQueueSend(this->eventQueue, &event, 10); xQueueSend(this->eventQueue, &event, 10);
// Start WiFi if not already started // Start WiFi if not already started
esp_err_t start_err = esp_wifi_start(); 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)); ESP_LOGE(WIFI_MANAGER_TAG, "Failed to start WiFi: %s", esp_err_to_name(start_err));
continue; continue;
} }
@@ -214,7 +205,7 @@ void WiFiManager::ConnectWithStoredCredentials()
} }
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to SSID:%s, trying next stored network", ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to SSID:%s, trying next stored network",
network.ssid.c_str()); network.ssid.c_str());
// Disconnect before trying next network // Disconnect before trying next network
esp_wifi_disconnect(); esp_wifi_disconnect();
vTaskDelay(pdMS_TO_TICKS(100)); vTaskDelay(pdMS_TO_TICKS(100));
@@ -249,107 +240,117 @@ void WiFiManager::SetupAccessPoint()
ESP_LOGI(WIFI_MANAGER_TAG, "AP started."); ESP_LOGI(WIFI_MANAGER_TAG, "AP started.");
} }
std::vector<WiFiNetwork> WiFiManager::ScanNetworks() { std::vector<WiFiNetwork> WiFiManager::ScanNetworks()
wifi_mode_t current_mode; {
esp_err_t err = esp_wifi_get_mode(&current_mode); wifi_mode_t current_mode;
esp_err_t err = esp_wifi_get_mode(&current_mode);
if (err == ESP_ERR_WIFI_NOT_INIT) {
ESP_LOGE(WIFI_MANAGER_TAG, "WiFi not initialized for scanning"); if (err == ESP_ERR_WIFI_NOT_INIT)
return std::vector<WiFiNetwork>(); {
ESP_LOGE(WIFI_MANAGER_TAG, "WiFi not initialized for scanning");
return std::vector<WiFiNetwork>();
}
// 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 ESP_LOGI(WIFI_MANAGER_TAG, "Switching to APSTA mode for scanning");
if (current_mode == WIFI_MODE_AP) { err = esp_wifi_set_mode(WIFI_MODE_APSTA);
ESP_LOGI(WIFI_MANAGER_TAG, "AP mode detected, checking for STA interface"); if (err != ESP_OK)
{
// Check if STA netif already exists ESP_LOGE(WIFI_MANAGER_TAG, "Failed to set APSTA mode: %s", esp_err_to_name(err));
esp_netif_t* sta_netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF"); if (!sta_netif_exists && sta_netif)
bool sta_netif_exists = (sta_netif != nullptr); {
esp_netif_destroy(sta_netif);
if (!sta_netif_exists) { }
ESP_LOGI(WIFI_MANAGER_TAG, "Creating STA interface for scanning"); return std::vector<WiFiNetwork>();
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<WiFiNetwork>();
}
// 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;
} }
// If already in STA or APSTA mode, scan directly // Configure STA with empty config to prevent auto-connect
return wifiScanner->scanNetworks(); 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() { WiFiState_e WiFiManager::GetCurrentWiFiState()
return this->stateManager->GetWifiState(); {
return this->stateManager->GetWifiState();
} }
void WiFiManager::TryConnectToStoredNetworks() { void WiFiManager::TryConnectToStoredNetworks()
ESP_LOGI(WIFI_MANAGER_TAG, "Manual WiFi connection attempt requested"); {
ESP_LOGI(WIFI_MANAGER_TAG, "Manual WiFi connection attempt requested");
// Check current WiFi mode
wifi_mode_t current_mode; // Check current WiFi mode
esp_err_t err = esp_wifi_get_mode(&current_mode); wifi_mode_t current_mode;
if (err != ESP_OK) { esp_err_t err = esp_wifi_get_mode(&current_mode);
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to get WiFi mode: %s", esp_err_to_name(err)); if (err != ESP_OK)
return; {
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 // Set to STA mode
if (current_mode == WIFI_MODE_AP || current_mode == WIFI_MODE_APSTA) { ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_LOGI(WIFI_MANAGER_TAG, "Currently in AP mode, transitioning to STA mode"); vTaskDelay(pdMS_TO_TICKS(100));
}
// Stop WiFi first
esp_wifi_stop(); // Reset retry counter and clear all event bits
vTaskDelay(pdMS_TO_TICKS(100)); s_retry_num = 0;
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT);
// Check if STA interface exists, create if needed this->ConnectWithStoredCredentials();
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();
} }
void WiFiManager::Begin() void WiFiManager::Begin()
@@ -363,18 +364,6 @@ void WiFiManager::Begin()
wifi_init_config_t esp_wifi_init_config = WIFI_INIT_CONFIG_DEFAULT(); wifi_init_config_t esp_wifi_init_config = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&esp_wifi_init_config)); 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_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID, ESP_EVENT_ANY_ID,
&WiFiManagerHelpers::event_handler, &WiFiManagerHelpers::event_handler,
@@ -387,8 +376,8 @@ void WiFiManager::Begin()
&instance_got_ip)); &instance_got_ip));
_wifi_cfg = {}; _wifi_cfg = {};
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN; // Start with open, will be set properly by SetCredentials _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.capable = false; // Disable PMF by default
_wifi_cfg.sta.pmf_cfg.required = false; _wifi_cfg.sta.pmf_cfg.required = false;
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

View File

@@ -19,9 +19,6 @@
#define WIFI_CONNECTED_BIT BIT0 #define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1 #define WIFI_FAIL_BIT BIT1
extern int s_retry_num;
extern EventGroupHandle_t s_wifi_event_group;
namespace WiFiManagerHelpers namespace WiFiManagerHelpers
{ {
void event_handler(void *arg, esp_event_base_t event_base, void event_handler(void *arg, esp_event_base_t event_base,

View File

@@ -30,10 +30,8 @@
#define CONFIG_LED_C_PIN_GPIO (gpio_num_t) CONFIG_LED_C_PIN #define CONFIG_LED_C_PIN_GPIO (gpio_num_t) CONFIG_LED_C_PIN
esp_timer_handle_t timerHandle; esp_timer_handle_t timerHandle;
esp_timer_handle_t startupTimerHandle;
QueueHandle_t eventQueue = xQueueCreate(10, sizeof(SystemEvent)); QueueHandle_t eventQueue = xQueueCreate(10, sizeof(SystemEvent));
QueueHandle_t ledStateQueue = xQueueCreate(10, sizeof(uint32_t)); QueueHandle_t ledStateQueue = xQueueCreate(10, sizeof(uint32_t));
bool startupCommandReceived = false;
auto *stateManager = new StateManager(eventQueue, ledStateQueue); auto *stateManager = new StateManager(eventQueue, ledStateQueue);
auto dependencyRegistry = std::make_shared<DependencyRegistry>(); auto dependencyRegistry = std::make_shared<DependencyRegistry>();
@@ -99,22 +97,16 @@ void start_video_streaming(void *arg)
{ {
#ifdef CONFIG_WIRED_MODE #ifdef CONFIG_WIRED_MODE
ESP_LOGI("[MAIN]", "Starting UVC streaming mode."); ESP_LOGI("[MAIN]", "Starting UVC streaming mode.");
// Initialize UVC if not already done ESP_LOGI("[MAIN]", "Initializing UVC hardware...");
static bool uvcInitialized = false; esp_err_t ret = uvcStream.setup();
if (!uvcInitialized) if (ret != ESP_OK)
{ {
ESP_LOGI("[MAIN]", "Initializing UVC hardware..."); ESP_LOGE("[MAIN]", "Failed to initialize UVC: %s", esp_err_to_name(ret));
esp_err_t ret = uvcStream.setup(); return;
if (ret != ESP_OK)
{
ESP_LOGE("[MAIN]", "Failed to initialize UVC: %s", esp_err_to_name(ret));
return;
}
uvcInitialized = true;
} }
uvcStream.start(); uvcStream.start();
#else #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"); ESP_LOGI("[MAIN]", "Falling back to WiFi mode if credentials available");
deviceMode = StreamingMode::WIFI; deviceMode = StreamingMode::WIFI;
#endif #endif
@@ -159,10 +151,10 @@ void activate_streaming(TaskHandle_t serialTaskHandle = nullptr)
void startup_timer_callback(void *arg) void startup_timer_callback(void *arg)
{ {
ESP_LOGI("[MAIN]", "Startup timer fired, startupCommandReceived=%s, startupPaused=%s", ESP_LOGI("[MAIN]", "Startup timer fired, startupCommandReceived=%s, startupPaused=%s",
startupCommandReceived ? "true" : "false", getStartupCommandReceived() ? "true" : "false",
startupPaused ? "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"); 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 else
{ {
if (startupPaused) if (getStartupPaused())
{ {
ESP_LOGI("[MAIN]", "Startup paused, staying in heartbeat mode"); 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 // Delete the timer after it fires
esp_timer_delete(startupTimerHandle); esp_timer_delete(timerHandle);
startupTimerHandle = nullptr; timerHandle = 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");
}
} }
extern "C" void app_main(void) extern "C" void app_main(void)
@@ -337,9 +314,6 @@ extern "C" void app_main(void)
restAPI->begin(); restAPI->begin();
cameraHandler->setupCamera(); cameraHandler->setupCamera();
// Don't initialize UVC here - wait for the timer to decide
// UVC will be initialized when streaming actually starts
xTaskCreate( xTaskCreate(
HandleRestAPIPollTask, HandleRestAPIPollTask,
"HandleRestAPIPollTask", "HandleRestAPIPollTask",
@@ -354,23 +328,6 @@ extern "C" void app_main(void)
ESP_LOGI("[MAIN]", "====================================="); ESP_LOGI("[MAIN]", "=====================================");
ESP_LOGI("[MAIN]", "Device will wait 20 seconds for commands..."); 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 // Create a one-shot timer for 20 seconds
const esp_timer_create_args_t startup_timer_args = { const esp_timer_create_args_t startup_timer_args = {
.callback = &startup_timer_callback, .callback = &startup_timer_callback,
@@ -379,13 +336,10 @@ extern "C" void app_main(void)
.name = "startup_timer", .name = "startup_timer",
.skip_unhandled_events = false}; .skip_unhandled_events = false};
ESP_ERROR_CHECK(esp_timer_create(&startup_timer_args, &startupTimerHandle)); ESP_ERROR_CHECK(esp_timer_create(&startup_timer_args, &timerHandle));
ESP_ERROR_CHECK(esp_timer_start_once(startupTimerHandle, 20000000)); // 20 seconds in microseconds ESP_ERROR_CHECK(esp_timer_start_once(timerHandle, 20000000)); // 20 seconds in microseconds
ESP_LOGI("[MAIN]", "Started 20-second startup timer"); ESP_LOGI("[MAIN]", "Started 20-second startup timer");
ESP_LOGI("[MAIN]", "Send any command within 20 seconds to enter heartbeat mode"); ESP_LOGI("[MAIN]", "Send any command within 20 seconds to enter heartbeat mode");
// Set global handles for component access
setStreamingTimerHandle(&timerHandle);
setSerialManagerHandle(&serialManagerHandle); setSerialManagerHandle(&serialManagerHandle);
} }

View File

@@ -6,11 +6,13 @@ This tool automatically discovers OpenIris devices via heartbeat,
allows WiFi configuration, and monitors the device logs. allows WiFi configuration, and monitors the device logs.
""" """
import re
import json import json
import time import time
import threading import threading
import argparse import argparse
import sys import sys
import string
from typing import Dict, List, Optional, Tuple from typing import Dict, List, Optional, Tuple
import serial import serial
import serial.tools.list_ports import serial.tools.list_ports
@@ -98,7 +100,6 @@ class OpenIrisDevice:
cmd_obj["commands"][0]["data"] = params cmd_obj["commands"][0]["data"] = params
cmd_json = json.dumps(cmd_obj) + '\n' cmd_json = json.dumps(cmd_obj) + '\n'
try: try:
# Clear any pending data # Clear any pending data
self.connection.reset_input_buffer() self.connection.reset_input_buffer()
@@ -130,9 +131,6 @@ class OpenIrisDevice:
print(f"📡 Raw: {repr(data)}") print(f"📡 Raw: {repr(data)}")
print(f"📝 Buffer: {repr(response_buffer[-200:])}") print(f"📝 Buffer: {repr(response_buffer[-200:])}")
# Clean buffer and look for JSON
import re
# Remove ANSI escape sequences # Remove ANSI escape sequences
clean_buffer = re.sub(r'\x1b\[[0-9;]*m', '', response_buffer) clean_buffer = re.sub(r'\x1b\[[0-9;]*m', '', response_buffer)
clean_buffer = clean_buffer.replace('\r', '') clean_buffer = clean_buffer.replace('\r', '')
@@ -303,6 +301,26 @@ class OpenIrisDevice:
print("✅ WiFi credentials set successfully") print("✅ WiFi credentials set successfully")
return True 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: def get_wifi_status(self) -> Dict:
"""Get current WiFi connection status""" """Get current WiFi connection status"""
response = self.send_command("get_wifi_status") response = self.send_command("get_wifi_status")
@@ -561,8 +579,89 @@ class OpenIrisDiscovery:
pass 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://<name>.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""" """Display available WiFi networks in a formatted table"""
networks = device.networks
if not networks: if not networks:
print("❌ No networks available") print("❌ No networks available")
return return
@@ -596,6 +695,65 @@ def display_networks(networks: List[WiFiNetwork]):
print() 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(): def main():
parser = argparse.ArgumentParser(description="OpenIris Setup CLI Tool") parser = argparse.ArgumentParser(description="OpenIris Setup CLI Tool")
parser.add_argument("--timeout", type=int, default=3, parser.add_argument("--timeout", type=int, default=3,
@@ -684,123 +842,25 @@ def main():
print("1. 🔍 Scan for WiFi networks") print("1. 🔍 Scan for WiFi networks")
print("2. 📡 Show available networks") print("2. 📡 Show available networks")
print("3. 🔐 Configure WiFi") print("3. 🔐 Configure WiFi")
print("4. 📶 Check WiFi status") print("4. 🌐 Configure MDNS")
print("5. 🔗 Connect to WiFi") print("5. 💻 Configure UVC Name")
print("6. 🚀 Start streaming mode") print("6. 📶 Check WiFi status")
print("7. 🔄 Switch device mode (WiFi/UVC/Auto)") print("7. 🔗 Connect to WiFi")
print("8. 📋 Monitor logs") print("8. 🚀 Start streaming mode")
print("9. 🚪 Exit") 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 == "exit":
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":
break break
else: command_to_run = COMMANDS_MAP.get(choice, None)
if not command_to_run:
print("❌ Invalid option") print("❌ Invalid option")
continue
command_to_run(device, args)
except KeyboardInterrupt: except KeyboardInterrupt:
print("\n🛑 Setup interrupted") print("\n🛑 Setup interrupted")

View File

@@ -28,7 +28,7 @@ class ESPWiFiScanner:
self.serial.reset_input_buffer() self.serial.reset_input_buffer()
command = '{"commands":[{"command":"scan_networks","data":{}}]}\n' command = '{"commands":[{"command":"scan_networks"}]}\n'
self.serial.write(command.encode()) self.serial.write(command.encode())
timeout_start = time.time() timeout_start = time.time()
@@ -92,7 +92,7 @@ def main():
import argparse import argparse
parser = argparse.ArgumentParser(description='ESP32 WiFi Scanner') 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, parser.add_argument('-t', '--timeout', type=int, default=30,
help='Scan timeout in seconds (default: 30)') help='Scan timeout in seconds (default: 30)')
args = parser.parse_args() args = parser.parse_args()