upload mutimodal

This commit is contained in:
Summer
2025-06-11 04:55:38 -07:00
committed by Lorow
parent b30a00900f
commit d9ace4bc05
41 changed files with 2484 additions and 219 deletions

View File

@@ -80,10 +80,10 @@ void CameraManager::setupCameraPinout()
.pixel_format = PIXFORMAT_JPEG, // YUV422,GRAYSCALE,RGB565,JPEG
.frame_size = FRAMESIZE_240X240, // QQVGA-UXGA, For ESP32, do not use sizes above QVGA when not JPEG. The performance of the ESP32-S series has improved a lot, but JPEG mode always gives better frame rates.
.jpeg_quality = 7, // 0-63, for OV series camera sensors, lower number means higher quality
.fb_count = 2, // 3 // When jpeg mode is used, if fb_count more than one, the driver will work in continuous mode.
.fb_location = CAMERA_FB_IN_PSRAM, // maybe it cannot put them fully in psram?
.grab_mode = CAMERA_GRAB_WHEN_EMPTY, // CAMERA_GRAB_LATEST
.jpeg_quality = 10, // 0-63, for OV series camera sensors, lower number means higher quality
.fb_count = 2, // Use 2 for streaming to balance memory and performance
.fb_location = CAMERA_FB_IN_PSRAM, // Use PSRAM for frame buffers
.grab_mode = CAMERA_GRAB_LATEST, // Always grab the latest frame for streaming
};
}

View File

@@ -7,8 +7,9 @@ idf_component_register(
"CommandManager/commands/config_commands.cpp"
"CommandManager/commands/mdns_commands.cpp"
"CommandManager/commands/device_commands.cpp"
"CommandManager/commands/scan_commands.cpp"
INCLUDE_DIRS
"CommandManager"
"CommandManager/commands"
REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks
REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks wifiManager Helpers
)

View File

@@ -1,7 +1,9 @@
#include "CommandManager.hpp"
#include <cstdlib>
std::unordered_map<std::string, CommandType> commandTypeMap = {
{"ping", CommandType::PING},
{"pause", CommandType::PAUSE},
{"set_wifi", CommandType::SET_WIFI},
{"update_wifi", CommandType::UPDATE_WIFI},
{"set_streaming_mode", CommandType::SET_STREAMING_MODE},
@@ -15,6 +17,12 @@ std::unordered_map<std::string, CommandType> commandTypeMap = {
{"get_config", CommandType::GET_CONFIG},
{"reset_config", CommandType::RESET_CONFIG},
{"restart_device", CommandType::RESTART_DEVICE},
{"scan_networks", CommandType::SCAN_NETWORKS},
{"start_streaming", CommandType::START_STREAMING},
{"get_wifi_status", CommandType::GET_WIFI_STATUS},
{"connect_wifi", CommandType::CONNECT_WIFI},
{"switch_mode", CommandType::SWITCH_MODE},
{"get_device_mode", CommandType::GET_DEVICE_MODE},
};
std::function<CommandResult()> CommandManager::createCommand(const CommandType type, std::string_view json) const {
@@ -22,6 +30,23 @@ std::function<CommandResult()> CommandManager::createCommand(const CommandType t
{
case CommandType::PING:
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);
};
case CommandType::SET_STREAMING_MODE:
return [this, json] {return setDeviceModeCommand(this->registry, json); };
case CommandType::UPDATE_OTA_CREDENTIALS:
@@ -48,6 +73,18 @@ std::function<CommandResult()> CommandManager::createCommand(const CommandType t
return [this, json] { return resetConfigCommand(this->registry, json); };
case CommandType::RESTART_DEVICE:
return restartDeviceCommand;
case CommandType::SCAN_NETWORKS:
return [this] { return scanNetworksCommand(this->registry); };
case CommandType::START_STREAMING:
return startStreamingCommand;
case CommandType::GET_WIFI_STATUS:
return [this] { return getWiFiStatusCommand(this->registry); };
case CommandType::CONNECT_WIFI:
return [this] { return connectWiFiCommand(this->registry); };
case CommandType::SWITCH_MODE:
return [this, json] { return switchModeCommand(this->registry, json); };
case CommandType::GET_DEVICE_MODE:
return [this] { return getDeviceModeCommand(this->registry); };
default:
return nullptr;
}
@@ -98,11 +135,15 @@ CommandResult CommandManager::executeFromJson(const std::string_view json) const
cJSON_AddItemToArray(responses, response);
}
const auto jsonString = cJSON_Print(responseDocument);
char* jsonString = cJSON_Print(responseDocument);
cJSON_Delete(responseDocument);
cJSON_Delete(parsedJson);
return CommandResult::getSuccessResult(jsonString);
// Return the JSON response directly without wrapping it
// The responseDocument already contains the proper format: {"results": [...]}
CommandResult result = CommandResult::getRawJsonResult(jsonString);
free(jsonString);
return result;
}
CommandResult CommandManager::executeFromType(const CommandType type, const std::string_view json) const

View File

@@ -17,12 +17,14 @@
#include "commands/mdns_commands.hpp"
#include "commands/wifi_commands.hpp"
#include "commands/device_commands.hpp"
#include "commands/scan_commands.hpp"
#include <cJSON.h>
enum class CommandType
{
None,
PING,
PAUSE,
SET_WIFI,
UPDATE_OTA_CREDENTIALS,
SET_STREAMING_MODE,
@@ -36,6 +38,12 @@ enum class CommandType
GET_CONFIG,
RESET_CONFIG,
RESTART_DEVICE,
SCAN_NETWORKS,
START_STREAMING,
GET_WIFI_STATUS,
CONNECT_WIFI,
SWITCH_MODE,
GET_DEVICE_MODE,
};
class CommandManager

View File

@@ -3,34 +3,52 @@
#include <format>
#include <string>
#include <algorithm>
class CommandResult
{
private:
public:
enum class Status
{
SUCCESS,
FAILURE,
};
private:
Status status;
std::string message;
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) {
escapedMessage.replace(pos, 1, "\\\\");
pos += 2;
}
// Then escape quotes
pos = 0;
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\":\" {} \"{}", "{", message, "}");
this->message = std::format("{}\"result\":\"{}\"{}", "{", escapedMessage, "}");
}
else
{
this->message = std::format("{}\"error\":\" {} \"{}", "{", message, "}");
this->message = std::format("{}\"error\":\"{}\"{}", "{", escapedMessage, "}");
}
}
public:
bool isSuccess() const { return status == Status::SUCCESS; }
static CommandResult getSuccessResult(const std::string &message)
@@ -42,8 +60,17 @@ public:
{
return CommandResult(message, Status::FAILURE);
}
// Create a result that returns raw JSON without wrapper
static CommandResult getRawJsonResult(const std::string &jsonMessage)
{
CommandResult result("", Status::SUCCESS);
result.message = jsonMessage;
return result;
}
std::string getResult() const { return this->message; }
};
#endif

View File

@@ -57,4 +57,9 @@ struct RestartCameraPayload : BasePayload
{
bool mode;
};
struct PausePayload : BasePayload
{
bool pause;
};
#endif

View File

@@ -7,7 +7,8 @@
enum class DependencyType
{
project_config,
camera_manager
camera_manager,
wifi_manager
};
class DependencyRegistry

View File

@@ -2,6 +2,8 @@
#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
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) {
@@ -64,3 +66,66 @@ CommandResult restartDeviceCommand() {
OpenIrisTasks::ScheduleRestart(2000);
return CommandResult::getSuccessResult("Device restarted");
}
CommandResult startStreamingCommand() {
activateStreaming(false); // Don't disable setup interfaces by default
return CommandResult::getSuccessResult("Streaming started");
}
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) {
const auto parsedJson = cJSON_Parse(jsonPayload.data());
if (parsedJson == nullptr) {
return CommandResult::getErrorResult("Invalid payload");
}
const auto modeObject = cJSON_GetObjectItem(parsedJson, "mode");
if (modeObject == nullptr) {
return CommandResult::getErrorResult("Invalid payload - missing mode");
}
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) {
newMode = StreamingMode::UVC;
} else if (strcmp(modeStr, "wifi") == 0 || strcmp(modeStr, "WiFi") == 0 || strcmp(modeStr, "WIFI") == 0) {
newMode = StreamingMode::WIFI;
} else if (strcmp(modeStr, "auto") == 0 || strcmp(modeStr, "AUTO") == 0) {
newMode = StreamingMode::AUTO;
} else {
return CommandResult::getErrorResult("Invalid mode - use 'uvc', 'wifi', or 'auto'");
}
const auto projectConfig = registry->resolve<ProjectConfig>(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<DependencyRegistry> registry) {
const auto projectConfig = registry->resolve<ProjectConfig>(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;
}
char result[100];
sprintf(result, "{\"mode\":\"%s\",\"value\":%d}", modeStr, (int)currentMode);
return CommandResult::getSuccessResult(result);
}

View File

@@ -6,4 +6,10 @@ CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry,
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);
CommandResult restartDeviceCommand();
CommandResult restartDeviceCommand();
CommandResult startStreamingCommand();
CommandResult switchModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);
CommandResult getDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry);

View File

@@ -0,0 +1,38 @@
#include "scan_commands.hpp"
#include "cJSON.h"
#include "esp_log.h"
#include <string>
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry) {
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
if (!wifiManager) {
return CommandResult::getErrorResult("WiFiManager not available");
}
auto networks = wifiManager->ScanNetworks();
cJSON *root = cJSON_CreateObject();
cJSON *networksArray = cJSON_CreateArray();
cJSON_AddItemToObject(root, "networks", networksArray);
for (const auto& network : networks) {
cJSON *networkObject = cJSON_CreateObject();
cJSON_AddStringToObject(networkObject, "ssid", network.ssid.c_str());
cJSON_AddNumberToObject(networkObject, "channel", network.channel);
cJSON_AddNumberToObject(networkObject, "rssi", network.rssi);
char mac_str[18];
sprintf(mac_str, "%02x:%02x:%02x:%02x:%02x:%02x",
network.mac[0], network.mac[1], network.mac[2],
network.mac[3], network.mac[4], network.mac[5]);
cJSON_AddStringToObject(networkObject, "mac_address", mac_str);
cJSON_AddNumberToObject(networkObject, "auth_mode", network.auth_mode);
cJSON_AddItemToArray(networksArray, networkObject);
}
char *json_string = cJSON_PrintUnformatted(root);
printf("%s\n", json_string);
cJSON_Delete(root);
free(json_string);
return CommandResult::getSuccessResult("Networks scanned");
}

View File

@@ -0,0 +1,10 @@
#ifndef SCAN_COMMANDS_HPP
#define SCAN_COMMANDS_HPP
#include "../CommandResult.hpp"
#include "../DependencyRegistry.hpp"
#include <wifiManager.hpp>
CommandResult scanNetworksCommand(std::shared_ptr<DependencyRegistry> registry);
#endif

View File

@@ -1,6 +1,25 @@
#include "simple_commands.hpp"
#include "main_globals.hpp"
#include "esp_log.h"
static const char* TAG = "SimpleCommands";
CommandResult PingCommand()
{
return CommandResult::getSuccessResult("pong");
};
CommandResult PauseCommand(const PausePayload& payload)
{
ESP_LOGI(TAG, "Pause command received: %s", payload.pause ? "true" : "false");
startupPaused = payload.pause;
if (payload.pause) {
ESP_LOGI(TAG, "Startup paused - device will remain in configuration mode");
return CommandResult::getSuccessResult("Startup paused");
} else {
ESP_LOGI(TAG, "Startup resumed");
return CommandResult::getSuccessResult("Startup resumed");
}
};

View File

@@ -3,7 +3,9 @@
#include <string>
#include "CommandResult.hpp"
#include "CommandSchema.hpp"
CommandResult PingCommand();
CommandResult PauseCommand(const PausePayload& payload);
#endif

View File

@@ -1,4 +1,5 @@
#include "wifi_commands.hpp"
#include "esp_netif.h"
std::optional<WifiPayload> parseSetWiFiCommandPayload(std::string_view jsonPayload)
{
@@ -223,3 +224,79 @@ CommandResult updateAPWiFiCommand(std::shared_ptr<DependencyRegistry> registry,
return CommandResult::getSuccessResult("Config updated");
}
CommandResult getWiFiStatusCommand(std::shared_ptr<DependencyRegistry> registry) {
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
// Get current WiFi state
auto wifiState = wifiManager->GetCurrentWiFiState();
auto networks = projectConfig->getWifiConfigs();
cJSON* statusJson = cJSON_CreateObject();
// Add WiFi state
const char* stateStr = "unknown";
switch(wifiState) {
case WiFiState_e::WiFiState_NotInitialized:
stateStr = "not_initialized";
break;
case WiFiState_e::WiFiState_Initialized:
stateStr = "initialized";
break;
case WiFiState_e::WiFiState_ReadyToConnect:
stateStr = "ready";
break;
case WiFiState_e::WiFiState_Connecting:
stateStr = "connecting";
break;
case WiFiState_e::WiFiState_WaitingForIp:
stateStr = "waiting_for_ip";
break;
case WiFiState_e::WiFiState_Connected:
stateStr = "connected";
break;
case WiFiState_e::WiFiState_Disconnected:
stateStr = "disconnected";
break;
case WiFiState_e::WiFiState_Error:
stateStr = "error";
break;
}
cJSON_AddStringToObject(statusJson, "status", stateStr);
cJSON_AddNumberToObject(statusJson, "networks_configured", networks.size());
// Add IP info if connected
if (wifiState == WiFiState_e::WiFiState_Connected) {
// Get IP address from ESP32
esp_netif_ip_info_t ip_info;
esp_netif_t* netif = esp_netif_get_handle_from_ifkey("WIFI_STA_DEF");
if (netif && esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) {
char ip_str[16];
sprintf(ip_str, IPSTR, IP2STR(&ip_info.ip));
cJSON_AddStringToObject(statusJson, "ip_address", ip_str);
}
}
char* statusString = cJSON_PrintUnformatted(statusJson);
std::string result(statusString);
free(statusString);
cJSON_Delete(statusJson);
return CommandResult::getSuccessResult(result);
}
CommandResult connectWiFiCommand(std::shared_ptr<DependencyRegistry> registry) {
auto wifiManager = registry->resolve<WiFiManager>(DependencyType::wifi_manager);
auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
auto networks = projectConfig->getWifiConfigs();
if (networks.empty()) {
return CommandResult::getErrorResult("No WiFi networks configured");
}
// Trigger WiFi connection attempt
wifiManager->TryConnectToStoredNetworks();
return CommandResult::getSuccessResult("WiFi connection attempt started");
}

View File

@@ -1,4 +1,6 @@
#include <ProjectConfig.hpp>
#include <wifiManager.hpp>
#include <StateManager.hpp>
#include <memory>
#include <string>
#include <optional>
@@ -18,3 +20,6 @@ CommandResult updateWiFiCommand(std::shared_ptr<DependencyRegistry> registry, st
std::optional<UpdateAPWiFiPayload> parseUpdateAPWiFiCommandPayload(std::string_view jsonPayload);
CommandResult updateAPWiFiCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);
CommandResult getWiFiStatusCommand(std::shared_ptr<DependencyRegistry> registry);
CommandResult connectWiFiCommand(std::shared_ptr<DependencyRegistry> registry);

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "Helpers/helpers.cpp"
idf_component_register(SRCS "Helpers/helpers.cpp" "Helpers/main_globals.cpp"
INCLUDE_DIRS "Helpers"
REQUIRES esp_timer
)

View File

@@ -0,0 +1,40 @@
#include "main_globals.hpp"
#include "esp_log.h"
// 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;
}
void setSerialManagerHandle(TaskHandle_t* handle) {
g_serial_manager_handle = handle;
}
// Functions for components to access the handles
esp_timer_handle_t* getStreamingTimerHandle() {
return g_streaming_timer_handle;
}
TaskHandle_t* getSerialManagerHandle() {
return g_serial_manager_handle;
}
// Global pause state
bool startupPaused = false;
// Function to manually activate streaming
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;
start_video_streaming(serialTaskHandle);
}

View File

@@ -0,0 +1,28 @@
#pragma once
#ifndef MAIN_GLOBALS_HPP
#define MAIN_GLOBALS_HPP
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#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();
// 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();
// Global variables for startup state
extern bool startupCommandReceived;
extern esp_timer_handle_t startupTimerHandle;
extern bool startupPaused;
#endif

View File

@@ -8,6 +8,7 @@
#include <helpers.hpp>
#include "sdkconfig.h"
#include <Preferences.hpp>
#include "esp_log.h"
struct BaseConfigModel
{
@@ -31,11 +32,14 @@ struct DeviceMode_t : BaseConfigModel {
explicit DeviceMode_t( Preferences *pref) : BaseConfigModel(pref), mode(StreamingMode::AUTO){}
void load() {
this->mode = static_cast<StreamingMode>(this->pref->getInt("mode", 0));
int stored_mode = this->pref->getInt("mode", 0);
this->mode = static_cast<StreamingMode>(stored_mode);
ESP_LOGI("DeviceMode", "Loaded device mode: %d", stored_mode);
}
void save() const {
this->pref->putInt("mode", static_cast<int>(this->mode));
ESP_LOGI("DeviceMode", "Saved device mode: %d", static_cast<int>(this->mode));
}
};
@@ -182,6 +186,9 @@ struct WiFiConfig_t : BaseConfigModel
this->password = this->pref->getString(("password" + iter_str).c_str(), "");
this->channel = this->pref->getUInt(("channel" + iter_str).c_str());
this->power = this->pref->getUInt(("power" + iter_str).c_str());
ESP_LOGI("WiFiConfig", "Loaded network %d: name=%s, ssid=%s, channel=%d",
index, this->name.c_str(), this->ssid.c_str(), this->channel);
};
void save() const {
@@ -193,6 +200,9 @@ struct WiFiConfig_t : BaseConfigModel
this->pref->putString(("password" + iter_str).c_str(), this->password.c_str());
this->pref->putUInt(("channel" + iter_str).c_str(), this->channel);
this->pref->putUInt(("power" + iter_str).c_str(), this->power);
ESP_LOGI("WiFiConfig", "Saved network %d: name=%s, ssid=%s, channel=%d",
this->index, this->name.c_str(), this->ssid.c_str(), this->channel);
};
std::string toRepresentation()

View File

@@ -147,6 +147,8 @@ void ProjectConfig::setWifiConfig(const std::string &networkName,
it->password = password;
it->channel = channel;
it->power = power;
// Save the updated network immediately
it->save();
return;
}
@@ -155,6 +157,9 @@ void ProjectConfig::setWifiConfig(const std::string &networkName,
ESP_LOGI(CONFIGURATION_TAG, "No networks, We're adding a new network");
this->config.networks.emplace_back(this->pref, static_cast<uint8_t>(0), networkName, ssid, password, channel,
power);
// Save the new network immediately
this->config.networks.back().save();
saveNetworkCount(this->pref, 1);
return;
}
@@ -168,6 +173,9 @@ void ProjectConfig::setWifiConfig(const std::string &networkName,
uint8_t last_index = getNetworkCount(this->pref);
this->config.networks.emplace_back(this->pref, last_index, networkName, ssid, password, channel,
power);
// Save the new network immediately
this->config.networks.back().save();
saveNetworkCount(this->pref, static_cast<int>(this->config.networks.size()));
}
else
{
@@ -212,6 +220,7 @@ void ProjectConfig::setAPWifiConfig(const std::string &ssid,
void ProjectConfig::setDeviceMode(const StreamingMode deviceMode) {
this->config.device_mode.mode = deviceMode;
this->config.device_mode.save(); // Save immediately
}
//**********************************************************************************************************************
@@ -249,6 +258,10 @@ TrackerConfig_t &ProjectConfig::getTrackerConfig()
return this->config;
}
DeviceMode_t &ProjectConfig::getDeviceMode() {
DeviceMode_t &ProjectConfig::getDeviceModeConfig() {
return this->config.device_mode;
}
StreamingMode ProjectConfig::getDeviceMode() {
return this->config.device_mode.mode;
}

View File

@@ -31,7 +31,7 @@ public:
bool reset();
DeviceConfig_t &getDeviceConfig();
DeviceMode_t &getDeviceMode();
DeviceMode_t &getDeviceModeConfig();
CameraConfig_t &getCameraConfig();
std::vector<WiFiConfig_t> &getWifiConfigs();
AP_WiFiConfig_t &getAPWifiConfig();
@@ -61,6 +61,7 @@ public:
uint8_t channel);
void setWiFiTxPower(uint8_t power);
void setDeviceMode(StreamingMode deviceMode);
StreamingMode getDeviceMode();
private:
Preferences *pref;

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "SerialManager/SerialManager.cpp"
INCLUDE_DIRS "SerialManager"
REQUIRES esp_driver_uart CommandManager
REQUIRES esp_driver_uart CommandManager ProjectConfig
)

View File

@@ -1,8 +1,11 @@
#include "SerialManager.hpp"
#include "esp_log.h"
#include "main_globals.hpp"
#define BUF_SIZE (1024)
SerialManager::SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t *timerHandle) : commandManager(commandManager), timerHandle(timerHandle) {
SerialManager::SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t *timerHandle, std::shared_ptr<ProjectConfig> deviceConfig)
: commandManager(commandManager), timerHandle(timerHandle), deviceConfig(deviceConfig) {
this->data = static_cast<uint8_t *>(malloc(BUF_SIZE));
this->temp_data = static_cast<uint8_t *>(malloc(256));
}
@@ -20,9 +23,6 @@ void SerialManager::try_receive()
int current_position = 0;
int len = usb_serial_jtag_read_bytes(this->temp_data, 256, 1000 / 20);
if (len) {
esp_timer_stop(*timerHandle);
}
// since we've got something on the serial port
// we gotta keep reading until we've got the whole message
while (len)
@@ -38,19 +38,73 @@ void SerialManager::try_receive()
data[current_position] = '\0';
current_position = 0;
// Notify main that a command was received during startup
notify_startup_command_received();
const auto result = this->commandManager->executeFromJson(std::string_view(reinterpret_cast<const char *>(this->data)));
const auto resultMessage = result.getResult();
usb_serial_jtag_write_bytes(resultMessage.c_str(), resultMessage.length(), 1000 / 20);
esp_timer_start_once(*timerHandle, 30000000); // 30s
}
}
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",
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) {
return true;
}
// If in heartbeat mode after startup, continue sending
if (startupCommandReceived) {
return true;
}
// Otherwise, only send if no WiFi credentials configured
const auto wifiConfigs = deviceConfig->getWifiConfigs();
return wifiConfigs.empty();
}
void HandleSerialManagerTask(void *pvParameters)
{
auto const serialManager = static_cast<SerialManager *>(pvParameters);
TickType_t lastHeartbeat = xTaskGetTickCount();
const TickType_t heartbeatInterval = pdMS_TO_TICKS(2000); // 2 second heartbeat
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()) {
serialManager->send_heartbeat();
}
lastHeartbeat = currentTime;
}
vTaskDelay(pdMS_TO_TICKS(50)); // Small delay to prevent busy waiting
}
}

View File

@@ -6,6 +6,7 @@
#include <string>
#include <memory>
#include <CommandManager.hpp>
#include <ProjectConfig.hpp>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
@@ -15,17 +16,21 @@
#include "driver/usb_serial_jtag.h"
#include "esp_vfs_usb_serial_jtag.h"
#include "esp_vfs_dev.h"
#include "esp_mac.h"
class SerialManager
{
public:
explicit SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t *timerHandle);
explicit SerialManager(std::shared_ptr<CommandManager> commandManager, esp_timer_handle_t *timerHandle, std::shared_ptr<ProjectConfig> deviceConfig);
void setup();
void try_receive();
void send_heartbeat();
bool should_send_heartbeat();
private:
std::shared_ptr<CommandManager> commandManager;
esp_timer_handle_t *timerHandle;
std::shared_ptr<ProjectConfig> deviceConfig;
uint8_t *data;
uint8_t *temp_data;
};

View File

@@ -38,8 +38,11 @@ esp_err_t StreamHelpers::stream(httpd_req_t *req)
if (!fb)
{
ESP_LOGE(STREAM_SERVER_TAG, "Camera capture failed with resposne %s", esp_err_to_name(response));
ESP_LOGE(STREAM_SERVER_TAG, "Camera capture failed");
response = ESP_FAIL;
// Don't break immediately, try to recover
vTaskDelay(pdMS_TO_TICKS(10));
continue;
}
else
{
@@ -70,10 +73,15 @@ esp_err_t StreamHelpers::stream(httpd_req_t *req)
}
if (response != ESP_OK)
break;
long request_end = Helpers::getTimeInMillis();
long latency = (request_end - last_request_time);
last_request_time = request_end;
ESP_LOGI(STREAM_SERVER_TAG, "Size: %uKB, Time: %lims (%lifps)\n", _jpg_buf_len / 1024, latency, 1000 / latency);
// Only log every 100 frames to reduce overhead
static int frame_count = 0;
if (++frame_count % 100 == 0) {
long request_end = Helpers::getTimeInMillis();
long latency = (request_end - last_request_time);
last_request_time = request_end;
ESP_LOGI(STREAM_SERVER_TAG, "Size: %uKB, Time: %lims (%lifps)", _jpg_buf_len / 1024, latency, 1000 / latency);
}
}
last_frame = 0;
return response;
@@ -93,8 +101,9 @@ esp_err_t StreamServer::startStreamServer()
config.max_uri_handlers = 10;
config.server_port = STREAM_SERVER_PORT;
config.ctrl_port = STREAM_SERVER_PORT;
config.recv_wait_timeout = 100;
config.send_wait_timeout = 100;
config.recv_wait_timeout = 5; // 5 seconds for receiving
config.send_wait_timeout = 5; // 5 seconds for sending
config.lru_purge_enable = true; // Enable LRU purge for better connection handling
httpd_uri_t stream_page = {
.uri = "/",

View File

@@ -149,5 +149,12 @@ esp_err_t UVCStreamManager::setup()
}
ESP_LOGI(UVC_STREAM_TAG, "Initialized UVC Device");
return ESP_OK;
}
esp_err_t UVCStreamManager::start()
{
ESP_LOGI(UVC_STREAM_TAG, "Starting UVC streaming");
// UVC device is already initialized in setup(), just log that we're starting
return ESP_OK;
}

View File

@@ -54,6 +54,7 @@ class UVCStreamManager
public:
esp_err_t setup();
esp_err_t start();
};
#endif // UVCSTREAM_HPP

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "wifiManager/wifiManager.cpp"
idf_component_register(SRCS "wifiManager/wifiManager.cpp" "wifiManager/WiFiScanner.cpp"
INCLUDE_DIRS "wifiManager"
REQUIRES esp_wifi nvs_flash esp_event esp_netif lwip StateManager ProjectConfig
)

View File

@@ -0,0 +1,205 @@
#include "WiFiScanner.hpp"
#include <cstring>
WiFiScanner::WiFiScanner() {}
void WiFiScanner::scanResultCallback(void* arg, esp_event_base_t event_base,
int32_t event_id, void* event_data) {
auto* scanner = static_cast<WiFiScanner*>(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) {
ESP_LOGI(TAG, "No access points found");
return;
}
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++) {
WiFiNetwork network;
network.ssid = std::string(reinterpret_cast<char*>(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);
}
delete[] ap_records;
ESP_LOGI(TAG, "Found %d access points", ap_count);
}
}
std::vector<WiFiNetwork> WiFiScanner::scanNetworks() {
std::vector<WiFiNetwork> 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) {
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) {
// Normal all-channel scan
wifi_scan_config_t scan_config = {
.ssid = nullptr,
.bssid = nullptr,
.channel = 0, // 0 means scan all channels
.show_hidden = true,
.scan_type = WIFI_SCAN_TYPE_ACTIVE, // Active scan
.scan_time = {
.active = {
.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
};
err = esp_wifi_scan_start(&scan_config, false);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to start scan: %s", esp_err_to_name(err));
return scan_results;
}
} else {
// Sequential channel scan - scan each channel individually
std::vector<wifi_ap_record_t> all_records;
for (uint8_t ch = 1; ch <= 13; ch++) {
wifi_scan_config_t scan_config = {
.ssid = nullptr,
.bssid = nullptr,
.channel = ch, // Specific channel
.show_hidden = true,
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
.scan_time = {
.active = {
.min = 100,
.max = 200
},
.passive = 300
},
.home_chan_dwell_time = 0,
.channel_bitmap = 0
};
err = esp_wifi_scan_start(&scan_config, true); // Blocking scan
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++) {
all_records.push_back(ch_records[i]);
}
}
delete[] ch_records;
}
}
vTaskDelay(pdMS_TO_TICKS(50));
}
// Process all collected records
for (const auto& record : all_records) {
WiFiNetwork network;
network.ssid = std::string(reinterpret_cast<const char*>(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) {
uint16_t temp_count = 0;
esp_err_t count_err = esp_wifi_scan_get_ap_num(&temp_count);
if (count_err == ESP_OK) {
// Wait a bit longer after finding networks to ensure scan is complete
if (temp_count > 0 && elapsed_ms > 5000) {
break;
}
}
vTaskDelay(pdMS_TO_TICKS(200));
elapsed_ms += 200;
}
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) {
ESP_LOGI(TAG, "No access points found");
return scan_results;
}
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) {
ESP_LOGE(TAG, "Failed to get scan records: %s", esp_err_to_name(err));
delete[] ap_records;
return scan_results;
}
// 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++) {
WiFiNetwork network;
network.ssid = std::string(reinterpret_cast<char*>(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) {
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;
}

View File

@@ -0,0 +1,29 @@
#pragma once
#ifndef WIFI_SCANNER_HPP
#define WIFI_SCANNER_HPP
#include <vector>
#include <string>
#include "esp_wifi.h"
#include "esp_log.h"
struct WiFiNetwork {
std::string ssid;
uint8_t channel;
int8_t rssi;
uint8_t mac[6];
wifi_auth_mode_t auth_mode;
};
class WiFiScanner {
public:
WiFiScanner();
std::vector<WiFiNetwork> scanNetworks();
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<WiFiNetwork> networks;
};
#endif

View File

@@ -2,6 +2,10 @@
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;
void WiFiManagerHelpers::event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
@@ -15,6 +19,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;
ESP_LOGI(WIFI_MANAGER_TAG, "Disconnect reason: %d", disconnected->reason);
if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY)
{
esp_wifi_connect();
@@ -37,19 +44,70 @@ void WiFiManagerHelpers::event_handler(void *arg, esp_event_base_t event_base,
}
}
WiFiManager::WiFiManager(std::shared_ptr<ProjectConfig> deviceConfig, QueueHandle_t eventQueue, StateManager *stateManager) : deviceConfig(deviceConfig), eventQueue(eventQueue), stateManager(stateManager) {}
WiFiManager::WiFiManager(std::shared_ptr<ProjectConfig> deviceConfig, QueueHandle_t eventQueue, StateManager *stateManager)
: deviceConfig(deviceConfig), eventQueue(eventQueue), stateManager(stateManager), wifiScanner(std::make_unique<WiFiScanner>()) {}
void WiFiManager::SetCredentials(const char *ssid, const char *password)
{
memcpy(_wifi_cfg.sta.ssid, ssid, std::min(strlen(ssid), sizeof(_wifi_cfg.sta.ssid)));
memcpy(_wifi_cfg.sta.password, password, std::min(strlen(password), sizeof(_wifi_cfg.sta.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) {
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_OPEN;
} 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
// 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
// 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
// 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",
_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()
{
SystemEvent event = {EventSource::WIFI, WiFiState_e::WiFiState_ReadyToConnect};
this->SetCredentials(CONFIG_WIFI_SSID, CONFIG_WIFI_PASSWORD);
esp_wifi_stop();
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &_wifi_cfg));
xQueueSend(this->eventQueue, &event, 10);
@@ -103,16 +161,38 @@ void WiFiManager::ConnectWithStoredCredentials()
return;
}
// 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)
{
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT);
// Reset retry counter for each network attempt
s_retry_num = 0;
xEventGroupClearBits(s_wifi_event_group, WIFI_FAIL_BIT | WIFI_CONNECTED_BIT);
this->SetCredentials(network.ssid.c_str(), network.password.c_str());
// we need to update the config after every credential change
// 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);
esp_wifi_start();
// 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) {
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to start WiFi: %s", esp_err_to_name(start_err));
continue;
}
event.value = WiFiState_e::WiFiState_Connecting;
xQueueSend(this->eventQueue, &event, 10);
@@ -121,19 +201,23 @@ void WiFiManager::ConnectWithStoredCredentials()
WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
pdFALSE,
pdFALSE,
portMAX_DELAY);
pdMS_TO_TICKS(30000)); // 30 second timeout instead of portMAX_DELAY
if (bits & WIFI_CONNECTED_BIT)
{
ESP_LOGI(WIFI_MANAGER_TAG, "connected to ap SSID:%p password:%p",
_wifi_cfg.sta.ssid, _wifi_cfg.sta.password);
ESP_LOGI(WIFI_MANAGER_TAG, "connected to ap SSID:%s",
network.ssid.c_str());
event.value = WiFiState_e::WiFiState_Connected;
xQueueSend(this->eventQueue, &event, 10);
return;
}
ESP_LOGE(WIFI_MANAGER_TAG, "Failed to connect to SSID:%p, password:%p, trying next stored network",
_wifi_cfg.sta.ssid, _wifi_cfg.sta.password);
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));
}
event.value = WiFiState_e::WiFiState_Error;
@@ -165,6 +249,109 @@ void WiFiManager::SetupAccessPoint()
ESP_LOGI(WIFI_MANAGER_TAG, "AP started.");
}
std::vector<WiFiNetwork> WiFiManager::ScanNetworks() {
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");
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();
}
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
return wifiScanner->scanNetworks();
}
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(&current_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();
}
// 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()
{
s_wifi_event_group = xEventGroupCreate();
@@ -176,6 +363,18 @@ 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,
@@ -188,8 +387,8 @@ void WiFiManager::Begin()
&instance_got_ip));
_wifi_cfg = {};
_wifi_cfg.sta.threshold.authmode = WIFI_AUTH_WEP;
_wifi_cfg.sta.pmf_cfg.capable = true;
_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));

View File

@@ -7,17 +7,20 @@
#include <algorithm>
#include <StateManager.hpp>
#include <ProjectConfig.hpp>
#include "WiFiScanner.hpp"
#include "esp_event.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define EXAMPLE_ESP_MAXIMUM_RETRY 3
#define WIFI_CONNECTED_BIT BIT0
#define WIFI_FAIL_BIT BIT1
static int s_retry_num = 0;
static EventGroupHandle_t s_wifi_event_group;
extern int s_retry_num;
extern EventGroupHandle_t s_wifi_event_group;
namespace WiFiManagerHelpers
{
@@ -34,6 +37,7 @@ private:
StateManager *stateManager;
wifi_init_config_t _wifi_init_cfg = WIFI_INIT_CONFIG_DEFAULT();
wifi_config_t _wifi_cfg = {};
std::unique_ptr<WiFiScanner> wifiScanner;
esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
@@ -48,6 +52,9 @@ private:
public:
WiFiManager(std::shared_ptr<ProjectConfig> deviceConfig, QueueHandle_t eventQueue, StateManager *stateManager);
void Begin();
std::vector<WiFiNetwork> ScanNetworks();
WiFiState_e GetCurrentWiFiState();
void TryConnectToStoredNetworks();
};
#endif

View File

@@ -53,7 +53,7 @@ dependencies:
idf:
source:
type: idf
version: 5.3.2
version: 5.3.3
direct_dependencies:
- espressif/cmake_utilities
- espressif/esp32-camera

View File

@@ -20,6 +20,7 @@
#include <CommandManager.hpp>
#include <SerialManager.hpp>
#include <RestAPI.hpp>
#include <main_globals.hpp>
#ifdef CONFIG_WIRED_MODE
#include <UVCStream.hpp>
@@ -29,8 +30,10 @@
#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<DependencyRegistry>();
@@ -39,8 +42,8 @@ auto commandManager = std::make_shared<CommandManager>(dependencyRegistry);
WebSocketLogger webSocketLogger;
Preferences preferences;
std::shared_ptr<ProjectConfig> deviceConfig = std::make_shared<ProjectConfig>(&preferences);
WiFiManager wifiManager(deviceConfig, eventQueue, stateManager);
auto deviceConfig = std::make_shared<ProjectConfig>(&preferences);
auto wifiManager = std::make_shared<WiFiManager>(deviceConfig, eventQueue, stateManager);
MDNSManager mdnsManager(deviceConfig, eventQueue);
std::shared_ptr<CameraManager> cameraHandler = std::make_shared<CameraManager>(deviceConfig, eventQueue);
@@ -53,7 +56,7 @@ UVCStreamManager uvcStream;
#endif
auto *ledManager = new LEDManager(BLINK_GPIO, CONFIG_LED_C_PIN_GPIO, ledStateQueue);
auto *serialManager = new SerialManager(commandManager, &timerHandle);
auto *serialManager = new SerialManager(commandManager, &timerHandle, deviceConfig);
static void initNVSStorage() {
esp_err_t ret = nvs_flash_init();
@@ -73,58 +76,151 @@ void disable_serial_manager_task(TaskHandle_t serialManagerHandle) {
vTaskDelete(serialManagerHandle);
}
// the idea here is pretty simple.
// After setting everything up, we start a 30s timer with this as a callback
// if we get anything on the serial, we stop the timer and reset it after the commands are done
// this is done to ensure the user has enough time to configure the board if need be
void start_video_streaming(void *arg) {
// if we're in auto-mode, we can decide which streaming helper to start based on the
// presence of Wi-Fi credentials
ESP_LOGI("[MAIN]", "Setup window expired, starting streaming services, quitting serial manager.");
switch (deviceConfig->getDeviceMode().mode) {
case StreamingMode::AUTO:
if (!deviceConfig->getWifiConfigs().empty() || strcmp(CONFIG_WIFI_SSID, "") != 0) {
// todo make sure the server runs on a separate core
ESP_LOGI("[MAIN]", "WiFi setup detected, starting WiFi streaming.");
streamServer.startStreamServer();
} else {
ESP_LOGI("[MAIN]", "UVC setup detected, starting UVC streaming.");
uvcStream.setup();
// New setup flow:
// 1. Device starts in setup mode (AP + Serial active)
// 2. User configures WiFi via serial commands
// 3. Device attempts WiFi connection while maintaining setup interfaces
// 4. Device reports connection status via serial
// 5. User explicitly starts streaming after verifying connectivity
void start_video_streaming(void *arg)
{
// Get the stored device mode
StreamingMode deviceMode = deviceConfig->getDeviceMode();
// Check if WiFi is actually connected, not just configured
bool hasWifiCredentials = !deviceConfig->getWifiConfigs().empty() || strcmp(CONFIG_WIFI_SSID, "") != 0;
bool wifiConnected = (wifiManager->GetCurrentWiFiState() == WiFiState_e::WiFiState_Connected);
if (deviceMode == StreamingMode::UVC) {
#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_LOGE("[MAIN]", "Failed to initialize UVC: %s", esp_err_to_name(ret));
return;
}
break;
case StreamingMode::UVC:
ESP_LOGI("[MAIN]", "Device set to UVC Mode, starting UVC streaming.");
uvcStream.setup();
break;
case StreamingMode::WIFI:
ESP_LOGI("[MAIN]", "Device set to Wi-Fi mode, starting WiFi streaming.");
streamServer.startStreamServer();
break;
uvcInitialized = true;
}
uvcStream.start();
#else
ESP_LOGE("[MAIN]", "UVC mode selected but CONFIG_WIRED_MODE not enabled in build!");
ESP_LOGI("[MAIN]", "Falling back to WiFi mode if credentials available");
deviceMode = StreamingMode::WIFI;
#endif
}
const auto serialTaskHandle = static_cast<TaskHandle_t>(arg);
disable_serial_manager_task(serialTaskHandle);
if ((deviceMode == StreamingMode::WIFI || deviceMode == StreamingMode::AUTO) && hasWifiCredentials && wifiConnected) {
ESP_LOGI("[MAIN]", "Starting WiFi streaming mode.");
streamServer.startStreamServer();
} else {
if (hasWifiCredentials && !wifiConnected) {
ESP_LOGE("[MAIN]", "WiFi credentials configured but not connected. Try connecting first.");
} else {
ESP_LOGE("[MAIN]", "No streaming mode available. Please configure WiFi.");
}
return;
}
ESP_LOGI("[MAIN]", "Streaming started successfully.");
// Optionally disable serial manager after explicit streaming start
if (arg != nullptr) {
ESP_LOGI("[MAIN]", "Disabling setup interfaces after streaming start.");
const auto serialTaskHandle = static_cast<TaskHandle_t>(arg);
disable_serial_manager_task(serialTaskHandle);
}
}
esp_timer_handle_t createStartVideoStreamingTimer(void *pvParameter) {
esp_timer_handle_t handle;
const esp_timer_create_args_t args = {
.callback = &start_video_streaming,
.arg = pvParameter,
.name = "startVideoStreaming"
};
// Manual streaming activation - no timer needed
void activate_streaming(TaskHandle_t serialTaskHandle = nullptr)
{
start_video_streaming(serialTaskHandle);
}
if (const auto result = esp_timer_create(&args, &handle); result != ESP_OK) {
ESP_LOGE("[MAIN]", "Failed to create timer: %s", esp_err_to_name(result));
// Callback for automatic startup after delay
void startup_timer_callback(void* arg)
{
ESP_LOGI("[MAIN]", "Startup timer fired, startupCommandReceived=%s, startupPaused=%s",
startupCommandReceived ? "true" : "false",
startupPaused ? "true" : "false");
if (!startupCommandReceived && !startupPaused) {
ESP_LOGI("[MAIN]", "No command received during startup delay, proceeding with automatic mode startup");
// Get the stored device mode
StreamingMode deviceMode = deviceConfig->getDeviceMode();
ESP_LOGI("[MAIN]", "Stored device mode: %d", (int)deviceMode);
// Get the serial manager handle to disable it after streaming starts
TaskHandle_t* serialHandle = getSerialManagerHandle();
TaskHandle_t serialTaskHandle = (serialHandle && *serialHandle) ? *serialHandle : nullptr;
if (deviceMode == StreamingMode::WIFI || deviceMode == StreamingMode::AUTO) {
// For WiFi mode, check if we have credentials and are connected
bool hasWifiCredentials = !deviceConfig->getWifiConfigs().empty() || strcmp(CONFIG_WIFI_SSID, "") != 0;
bool wifiConnected = (wifiManager->GetCurrentWiFiState() == WiFiState_e::WiFiState_Connected);
ESP_LOGI("[MAIN]", "WiFi check - hasCredentials: %s, connected: %s",
hasWifiCredentials ? "true" : "false",
wifiConnected ? "true" : "false");
if (hasWifiCredentials && wifiConnected) {
ESP_LOGI("[MAIN]", "Starting WiFi streaming automatically");
activate_streaming(serialTaskHandle);
} else if (hasWifiCredentials && !wifiConnected) {
ESP_LOGI("[MAIN]", "WiFi credentials exist but not connected, waiting...");
// Could retry connection here
} else {
ESP_LOGI("[MAIN]", "No WiFi credentials, staying in setup mode");
}
}
else if (deviceMode == StreamingMode::UVC) {
#ifdef CONFIG_WIRED_MODE
ESP_LOGI("[MAIN]", "Starting UVC streaming automatically");
activate_streaming(serialTaskHandle);
#else
ESP_LOGE("[MAIN]", "UVC mode selected but CONFIG_WIRED_MODE not enabled in build!");
ESP_LOGI("[MAIN]", "Device will stay in setup mode. Enable CONFIG_WIRED_MODE and rebuild.");
#endif
}
else {
ESP_LOGI("[MAIN]", "Unknown device mode: %d", (int)deviceMode);
}
} else {
if (startupPaused) {
ESP_LOGI("[MAIN]", "Startup paused, staying in heartbeat mode");
} else {
ESP_LOGI("[MAIN]", "Command received during startup, staying in heartbeat mode");
}
}
return handle;
// 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");
}
}
extern "C" void app_main(void) {
TaskHandle_t *serialManagerHandle = nullptr;
dependencyRegistry->registerService<ProjectConfig>(DependencyType::project_config, deviceConfig);
dependencyRegistry->registerService<CameraManager>(DependencyType::camera_manager, cameraHandler);
dependencyRegistry->registerService<WiFiManager>(DependencyType::wifi_manager, wifiManager);
// uvc plan
// cleanup the logs - done
// prepare the camera to be initialized with UVC - done?
@@ -197,19 +293,23 @@ extern "C" void app_main(void) {
deviceConfig->load();
serialManager->setup();
static TaskHandle_t serialManagerHandle = nullptr;
xTaskCreate(
HandleSerialManagerTask,
"HandleSerialManagerTask",
1024 * 6,
serialManager,
1, // we only rely on the serial manager during provisioning, we can run it slower
serialManagerHandle);
&serialManagerHandle);
wifiManager.Begin();
wifiManager->Begin();
mdnsManager.start();
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",
@@ -218,8 +318,44 @@ extern "C" void app_main(void) {
1, // it's the rest API, we only serve commands over it so we don't really need a higher priority
nullptr);
timerHandle = createStartVideoStreamingTimer(serialManagerHandle);
if (timerHandle != nullptr) {
esp_timer_start_once(timerHandle, 30000000); // 30s
// New flow: Device starts with a 20-second delay before automatic mode startup
ESP_LOGI("[MAIN]", "=====================================");
ESP_LOGI("[MAIN]", "STARTUP: 20-SECOND DELAY MODE ACTIVE");
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,
.arg = nullptr,
.dispatch_method = ESP_TIMER_TASK,
.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_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);
}

201
sdkconfig
View File

@@ -1,6 +1,6 @@
#
# Automatically generated file. DO NOT EDIT.
# Espressif IoT Development Framework (ESP-IDF) 5.3.2 Project Configuration
# Espressif IoT Development Framework (ESP-IDF) 5.3.3 Project Configuration
#
CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000
CONFIG_SOC_MPU_REGIONS_MAX_NUM=8
@@ -64,6 +64,7 @@ CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y
CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y
CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y
CONFIG_SOC_PM_SUPPORTED=y
CONFIG_SOC_SIMD_INSTRUCTION_SUPPORTED=y
CONFIG_SOC_XTAL_SUPPORT_40M=y
CONFIG_SOC_APPCPU_HAS_CLOCK_GATING_BUG=y
CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y
@@ -101,6 +102,7 @@ CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y
CONFIG_SOC_CPU_BREAKPOINTS_NUM=2
CONFIG_SOC_CPU_WATCHPOINTS_NUM=2
CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=64
CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16
CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096
CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16
CONFIG_SOC_DS_KEY_CHECK_MAX_WAIT_US=1100
@@ -203,6 +205,7 @@ CONFIG_SOC_RTCIO_PIN_COUNT=22
CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y
CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y
CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y
CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y
CONFIG_SOC_SDM_GROUPS=y
CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8
CONFIG_SOC_SDM_CLK_SUPPORT_APB=y
@@ -243,6 +246,8 @@ CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=54
CONFIG_SOC_TIMER_GROUP_SUPPORT_XTAL=y
CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y
CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4
CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32
CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16
CONFIG_SOC_TOUCH_SENSOR_VERSION=2
CONFIG_SOC_TOUCH_SENSOR_NUM=15
CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y
@@ -366,7 +371,7 @@ CONFIG_IDF_TOOLCHAIN="gcc"
CONFIG_IDF_TARGET_ARCH_XTENSA=y
CONFIG_IDF_TARGET_ARCH="xtensa"
CONFIG_IDF_TARGET="esp32s3"
CONFIG_IDF_INIT_VERSION="5.3.2"
CONFIG_IDF_INIT_VERSION="5.3.3"
CONFIG_IDF_TARGET_ESP32S3=y
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009
@@ -628,7 +633,12 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
# Bluetooth
#
# CONFIG_BT_ENABLED is not set
CONFIG_BT_ALARM_MAX_NUM=50
#
# Common Options
#
# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set
# end of Common Options
# end of Bluetooth
#
@@ -652,6 +662,7 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
# Legacy ADC Driver Configuration
#
# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set
#
# Legacy ADC Calibration Configuration
@@ -664,42 +675,49 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
# Legacy MCPWM Driver Configurations
#
# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy MCPWM Driver Configurations
#
# Legacy Timer Group Driver Configurations
#
# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Timer Group Driver Configurations
#
# Legacy RMT Driver Configurations
#
# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy RMT Driver Configurations
#
# Legacy I2S Driver Configurations
#
# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy I2S Driver Configurations
#
# Legacy PCNT Driver Configurations
#
# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy PCNT Driver Configurations
#
# Legacy SDM Driver Configurations
#
# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy SDM Driver Configurations
#
# Legacy Temperature Sensor Driver Configurations
#
# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Temperature Sensor Driver Configurations
# end of Driver Configurations
@@ -759,6 +777,7 @@ CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set
# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
CONFIG_GPTIMER_OBJ_CACHE_SAFE=y
# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:GPTimer Configurations
@@ -877,6 +896,13 @@ CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y
CONFIG_ESP_GDBSTUB_MAX_TASKS=32
# end of GDB Stub
#
# ESP HID
#
CONFIG_ESPHID_TASK_SIZE_BT=2048
CONFIG_ESPHID_TASK_SIZE_BLE=4096
# end of ESP HID
#
# ESP HTTP client
#
@@ -1051,6 +1077,7 @@ CONFIG_ESP_PHY_RF_CAL_PARTIAL=y
# CONFIG_ESP_PHY_RF_CAL_FULL is not set
CONFIG_ESP_PHY_CALIBRATION_MODE=0
# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set
# CONFIG_ESP_PHY_RECORD_USED_TIME is not set
# end of PHY
#
@@ -1251,9 +1278,9 @@ CONFIG_ESP_WIFI_ENABLED=y
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y
# CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER is not set
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=16
CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32
CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y
# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
@@ -1263,7 +1290,6 @@ CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP_WIFI_TX_BA_WIN=6
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP_WIFI_RX_BA_WIN=6
# CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set
CONFIG_ESP_WIFI_NVS_ENABLED=y
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y
# CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1 is not set
@@ -1434,7 +1460,6 @@ CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2
CONFIG_HAL_WDT_USE_ROM_IMPL=y
CONFIG_HAL_SPI_MASTER_FUNC_IN_IRAM=y
CONFIG_HAL_SPI_SLAVE_FUNC_IN_IRAM=y
# CONFIG_HAL_ECDSA_GEN_SIG_CM is not set
# end of Hardware Abstraction Layer (HAL) and Low Level (LL)
#
@@ -1836,25 +1861,10 @@ CONFIG_STDATOMIC_S32C1I_SPIRAM_WORKAROUND=y
# CONFIG_OPENTHREAD_ENABLED is not set
#
# Thread Operational Dataset
# OpenThread Spinel
#
CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread-ESP"
CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX="fd00:db8:a0:0::/64"
CONFIG_OPENTHREAD_NETWORK_CHANNEL=15
CONFIG_OPENTHREAD_NETWORK_PANID=0x1234
CONFIG_OPENTHREAD_NETWORK_EXTPANID="dead00beef00cafe"
CONFIG_OPENTHREAD_NETWORK_MASTERKEY="00112233445566778899aabbccddeeff"
CONFIG_OPENTHREAD_NETWORK_PSKC="104810e2315100afd6bc9215a6bfac53"
# end of Thread Operational Dataset
CONFIG_OPENTHREAD_XTAL_ACCURACY=130
# CONFIG_OPENTHREAD_SPINEL_ONLY is not set
CONFIG_OPENTHREAD_RX_ON_WHEN_IDLE=y
#
# Thread Address Query Config
#
# end of Thread Address Query Config
# end of OpenThread Spinel
# end of OpenThread
#
@@ -1863,6 +1873,7 @@ CONFIG_OPENTHREAD_RX_ON_WHEN_IDLE=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_PATCH_VERSION=y
# end of Protocomm
#
@@ -2093,75 +2104,6 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
# CONFIG_WIFI_PROV_STA_FAST_SCAN is not set
# end of Wi-Fi Provisioning Manager
#
# USB Device UVC
#
CONFIG_TUSB_VID=0x303A
CONFIG_TUSB_PID=0x8000
CONFIG_TUSB_MANUFACTURER="ETVR"
CONFIG_TUSB_PRODUCT="OpenIris Camera"
CONFIG_TUSB_SERIAL_NUM="12345678"
# CONFIG_UVC_SUPPORT_TWO_CAM is not set
#
# USB Cam1 Config
#
CONFIG_FORMAT_MJPEG_CAM1=y
# CONFIG_FORMAT_H264_CAM1 is not set
# CONFIG_FORMAT_UNCOMPR_CAM1 is not set
# CONFIG_UVC_MODE_ISOC_CAM1 is not set
CONFIG_UVC_MODE_BULK_CAM1=y
CONFIG_FRAMESIZE_QVGA=y
# CONFIG_FRAMESIZE_HVGA is not set
# CONFIG_FRAMESIZE_VGA is not set
# CONFIG_FRAMESIZE_SVGA is not set
# CONFIG_FRAMESIZE_HD is not set
# CONFIG_FRAMESIZE_FHD is not set
CONFIG_UVC_CAM1_FRAMERATE=90
CONFIG_UVC_CAM1_FRAMESIZE_WIDTH=240
CONFIG_UVC_CAM1_FRAMESIZE_HEIGT=240
CONFIG_UVC_CAM1_MULTI_FRAMESIZE=y
# end of USB Cam1 Config
#
# UVC_MULTI_FRAME_CONFIG
#
#
# FRAME_SIZE_1
#
CONFIG_UVC_MULTI_FRAME_WIDTH_1=240
CONFIG_UVC_MULTI_FRAME_HEIGHT_1=240
CONFIG_UVC_MULTI_FRAME_FPS_1=90
# end of FRAME_SIZE_1
#
# FRAME_SIZE_2
#
CONFIG_UVC_MULTI_FRAME_WIDTH_2=240
CONFIG_UVC_MULTI_FRAME_HEIGHT_2=240
CONFIG_UVC_MULTI_FRAME_FPS_2=90
# end of FRAME_SIZE_2
#
# FRAME_SIZE_3
#
CONFIG_UVC_MULTI_FRAME_WIDTH_3=240
CONFIG_UVC_MULTI_FRAME_HEIGHT_3=240
CONFIG_UVC_MULTI_FRAME_FPS_3=90
# end of FRAME_SIZE_3
# end of UVC_MULTI_FRAME_CONFIG
#
# UVC Task Config
#
CONFIG_UVC_TINYUSB_TASK_PRIORITY=3
CONFIG_UVC_TINYUSB_TASK_CORE=-1
CONFIG_UVC_CAM1_TASK_PRIORITY=2
CONFIG_UVC_CAM1_TASK_CORE=-1
# end of UVC Task Config
# end of USB Device UVC
#
# CMake Utilities
#
@@ -2233,6 +2175,75 @@ CONFIG_MDNS_PREDEF_NETIF_AP=y
CONFIG_MDNS_PREDEF_NETIF_ETH=y
# end of MDNS Predefined interfaces
# end of mDNS
#
# USB Device UVC
#
CONFIG_TUSB_VID=0x303A
CONFIG_TUSB_PID=0x8000
CONFIG_TUSB_MANUFACTURER="ETVR"
CONFIG_TUSB_PRODUCT="OpenIris Camera"
CONFIG_TUSB_SERIAL_NUM="12345678"
# CONFIG_UVC_SUPPORT_TWO_CAM is not set
#
# USB Cam1 Config
#
CONFIG_FORMAT_MJPEG_CAM1=y
# CONFIG_FORMAT_H264_CAM1 is not set
# CONFIG_FORMAT_UNCOMPR_CAM1 is not set
# CONFIG_UVC_MODE_ISOC_CAM1 is not set
CONFIG_UVC_MODE_BULK_CAM1=y
CONFIG_FRAMESIZE_QVGA=y
# CONFIG_FRAMESIZE_HVGA is not set
# CONFIG_FRAMESIZE_VGA is not set
# CONFIG_FRAMESIZE_SVGA is not set
# CONFIG_FRAMESIZE_HD is not set
# CONFIG_FRAMESIZE_FHD is not set
CONFIG_UVC_CAM1_FRAMERATE=90
CONFIG_UVC_CAM1_FRAMESIZE_WIDTH=240
CONFIG_UVC_CAM1_FRAMESIZE_HEIGT=240
CONFIG_UVC_CAM1_MULTI_FRAMESIZE=y
# end of USB Cam1 Config
#
# UVC_MULTI_FRAME_CONFIG
#
#
# FRAME_SIZE_1
#
CONFIG_UVC_MULTI_FRAME_WIDTH_1=240
CONFIG_UVC_MULTI_FRAME_HEIGHT_1=240
CONFIG_UVC_MULTI_FRAME_FPS_1=60
# end of FRAME_SIZE_1
#
# FRAME_SIZE_2
#
CONFIG_UVC_MULTI_FRAME_WIDTH_2=240
CONFIG_UVC_MULTI_FRAME_HEIGHT_2=240
CONFIG_UVC_MULTI_FRAME_FPS_2=60
# end of FRAME_SIZE_2
#
# FRAME_SIZE_3
#
CONFIG_UVC_MULTI_FRAME_WIDTH_3=240
CONFIG_UVC_MULTI_FRAME_HEIGHT_3=240
CONFIG_UVC_MULTI_FRAME_FPS_3=60
# end of FRAME_SIZE_3
# end of UVC_MULTI_FRAME_CONFIG
#
# UVC Task Config
#
CONFIG_UVC_TINYUSB_TASK_PRIORITY=4
CONFIG_UVC_TINYUSB_TASK_CORE=-1
CONFIG_UVC_CAM1_TASK_PRIORITY=3
CONFIG_UVC_CAM1_TASK_CORE=-1
# end of UVC Task Config
# end of USB Device UVC
# end of Component config
# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set
@@ -2326,7 +2337,6 @@ CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
CONFIG_ESP32S3_DEBUG_OCDAWARE=y
CONFIG_BROWNOUT_DET=y
CONFIG_ESP32S3_BROWNOUT_DET=y
CONFIG_ESP32S3_BROWNOUT_DET=y
CONFIG_BROWNOUT_DET_LVL_SEL_7=y
CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y
# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
@@ -2349,17 +2359,14 @@ CONFIG_ESP32_WIFI_ENABLED=y
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y
# CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER is not set
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16
CONFIG_ESP32_WIFI_CACHE_TX_BUFFER_NUM=32
# CONFIG_ESP32_WIFI_CSI_ENABLED is not set
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP32_WIFI_TX_BA_WIN=6
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_RX_BA_WIN=6
CONFIG_ESP32_WIFI_RX_BA_WIN=6
# CONFIG_ESP32_WIFI_AMSDU_TX_ENABLED is not set
CONFIG_ESP32_WIFI_NVS_ENABLED=y
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set

View File

@@ -1,6 +1,6 @@
#
# Automatically generated file. DO NOT EDIT.
# Espressif IoT Development Framework (ESP-IDF) 5.3.2 Project Configuration
# Espressif IoT Development Framework (ESP-IDF) 5.3.3 Project Configuration
#
CONFIG_SOC_MPU_MIN_REGION_SIZE=0x20000000
CONFIG_SOC_MPU_REGIONS_MAX_NUM=8
@@ -64,6 +64,7 @@ CONFIG_SOC_LIGHT_SLEEP_SUPPORTED=y
CONFIG_SOC_DEEP_SLEEP_SUPPORTED=y
CONFIG_SOC_LP_PERIPH_SHARE_INTERRUPT=y
CONFIG_SOC_PM_SUPPORTED=y
CONFIG_SOC_SIMD_INSTRUCTION_SUPPORTED=y
CONFIG_SOC_XTAL_SUPPORT_40M=y
CONFIG_SOC_APPCPU_HAS_CLOCK_GATING_BUG=y
CONFIG_SOC_ADC_RTC_CTRL_SUPPORTED=y
@@ -101,6 +102,7 @@ CONFIG_SOC_HP_CPU_HAS_MULTIPLE_CORES=y
CONFIG_SOC_CPU_BREAKPOINTS_NUM=2
CONFIG_SOC_CPU_WATCHPOINTS_NUM=2
CONFIG_SOC_CPU_WATCHPOINT_MAX_REGION_SIZE=64
CONFIG_SOC_SIMD_PREFERRED_DATA_ALIGNMENT=16
CONFIG_SOC_DS_SIGNATURE_MAX_BIT_LEN=4096
CONFIG_SOC_DS_KEY_PARAM_MD_IV_LENGTH=16
CONFIG_SOC_DS_KEY_CHECK_MAX_WAIT_US=1100
@@ -203,6 +205,7 @@ CONFIG_SOC_RTCIO_PIN_COUNT=22
CONFIG_SOC_RTCIO_INPUT_OUTPUT_SUPPORTED=y
CONFIG_SOC_RTCIO_HOLD_SUPPORTED=y
CONFIG_SOC_RTCIO_WAKE_SUPPORTED=y
CONFIG_SOC_LP_IO_CLOCK_IS_INDEPENDENT=y
CONFIG_SOC_SDM_GROUPS=y
CONFIG_SOC_SDM_CHANNELS_PER_GROUP=8
CONFIG_SOC_SDM_CLK_SUPPORT_APB=y
@@ -243,6 +246,8 @@ CONFIG_SOC_TIMER_GROUP_COUNTER_BIT_WIDTH=54
CONFIG_SOC_TIMER_GROUP_SUPPORT_XTAL=y
CONFIG_SOC_TIMER_GROUP_SUPPORT_APB=y
CONFIG_SOC_TIMER_GROUP_TOTAL_TIMERS=4
CONFIG_SOC_LP_TIMER_BIT_WIDTH_LO=32
CONFIG_SOC_LP_TIMER_BIT_WIDTH_HI=16
CONFIG_SOC_TOUCH_SENSOR_VERSION=2
CONFIG_SOC_TOUCH_SENSOR_NUM=15
CONFIG_SOC_TOUCH_SUPPORT_SLEEP_WAKEUP=y
@@ -366,7 +371,7 @@ CONFIG_IDF_TOOLCHAIN="gcc"
CONFIG_IDF_TARGET_ARCH_XTENSA=y
CONFIG_IDF_TARGET_ARCH="xtensa"
CONFIG_IDF_TARGET="esp32s3"
CONFIG_IDF_INIT_VERSION="5.3.2"
CONFIG_IDF_INIT_VERSION="5.3.3"
CONFIG_IDF_TARGET_ESP32S3=y
CONFIG_IDF_FIRMWARE_CHIP_ID=0x0009
@@ -544,11 +549,10 @@ CONFIG_ENV_GPIO_RANGE_MAX=48
CONFIG_ENV_GPIO_IN_RANGE_MAX=48
CONFIG_ENV_GPIO_OUT_RANGE_MAX=48
CONFIG_BLINK_GPIO=38
# CONFIG_SUPPORTS_EXTERNAL_LED_CONTROL is not set
CONFIG_SUPPORTS_EXTERNAL_LED_CONTROL=y
CONFIG_LED_C_PIN=1
CONFIG_BLINK_PERIOD=1000
CONFIG_ILLUMINATOR_PIN=1
CONFIG_WIRED_MODE=y
# CONFIG_WIRED_MODE is not set
CONFIG_MDNS_HOSTNAME="openiristracker"
CONFIG_WIFI_SSID=""
CONFIG_WIFI_PASSWORD=""
@@ -629,7 +633,12 @@ CONFIG_APPTRACE_LOCK_ENABLE=y
# Bluetooth
#
# CONFIG_BT_ENABLED is not set
CONFIG_BT_ALARM_MAX_NUM=50
#
# Common Options
#
# CONFIG_BT_BLE_LOG_SPI_OUT_ENABLED is not set
# end of Common Options
# end of Bluetooth
#
@@ -653,6 +662,7 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
# Legacy ADC Driver Configuration
#
# CONFIG_ADC_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_ADC_SKIP_LEGACY_CONFLICT_CHECK is not set
#
# Legacy ADC Calibration Configuration
@@ -665,42 +675,49 @@ CONFIG_TWAI_ERRATA_FIX_LISTEN_ONLY_DOM=y
# Legacy MCPWM Driver Configurations
#
# CONFIG_MCPWM_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_MCPWM_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy MCPWM Driver Configurations
#
# Legacy Timer Group Driver Configurations
#
# CONFIG_GPTIMER_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_GPTIMER_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Timer Group Driver Configurations
#
# Legacy RMT Driver Configurations
#
# CONFIG_RMT_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_RMT_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy RMT Driver Configurations
#
# Legacy I2S Driver Configurations
#
# CONFIG_I2S_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_I2S_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy I2S Driver Configurations
#
# Legacy PCNT Driver Configurations
#
# CONFIG_PCNT_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_PCNT_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy PCNT Driver Configurations
#
# Legacy SDM Driver Configurations
#
# CONFIG_SDM_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_SDM_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy SDM Driver Configurations
#
# Legacy Temperature Sensor Driver Configurations
#
# CONFIG_TEMP_SENSOR_SUPPRESS_DEPRECATE_WARN is not set
# CONFIG_TEMP_SENSOR_SKIP_LEGACY_CONFLICT_CHECK is not set
# end of Legacy Temperature Sensor Driver Configurations
# end of Driver Configurations
@@ -760,6 +777,7 @@ CONFIG_ESP_ERR_TO_NAME_LOOKUP=y
CONFIG_GPTIMER_ISR_HANDLER_IN_IRAM=y
# CONFIG_GPTIMER_CTRL_FUNC_IN_IRAM is not set
# CONFIG_GPTIMER_ISR_IRAM_SAFE is not set
CONFIG_GPTIMER_OBJ_CACHE_SAFE=y
# CONFIG_GPTIMER_ENABLE_DEBUG_LOG is not set
# end of ESP-Driver:GPTimer Configurations
@@ -878,6 +896,13 @@ CONFIG_ESP_GDBSTUB_SUPPORT_TASKS=y
CONFIG_ESP_GDBSTUB_MAX_TASKS=32
# end of GDB Stub
#
# ESP HID
#
CONFIG_ESPHID_TASK_SIZE_BT=2048
CONFIG_ESPHID_TASK_SIZE_BLE=4096
# end of ESP HID
#
# ESP HTTP client
#
@@ -1052,6 +1077,7 @@ CONFIG_ESP_PHY_RF_CAL_PARTIAL=y
# CONFIG_ESP_PHY_RF_CAL_FULL is not set
CONFIG_ESP_PHY_CALIBRATION_MODE=0
# CONFIG_ESP_PHY_PLL_TRACK_DEBUG is not set
# CONFIG_ESP_PHY_RECORD_USED_TIME is not set
# end of PHY
#
@@ -1252,9 +1278,9 @@ CONFIG_ESP_WIFI_ENABLED=y
CONFIG_ESP_WIFI_STATIC_RX_BUFFER_NUM=10
CONFIG_ESP_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP_WIFI_STATIC_TX_BUFFER=y
# CONFIG_ESP_WIFI_DYNAMIC_TX_BUFFER is not set
CONFIG_ESP_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP_WIFI_STATIC_TX_BUFFER_NUM=16
CONFIG_ESP_WIFI_CACHE_TX_BUFFER_NUM=32
CONFIG_ESP_WIFI_STATIC_RX_MGMT_BUFFER=y
# CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUFFER is not set
CONFIG_ESP_WIFI_DYNAMIC_RX_MGMT_BUF=0
@@ -1264,7 +1290,6 @@ CONFIG_ESP_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP_WIFI_TX_BA_WIN=6
CONFIG_ESP_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP_WIFI_RX_BA_WIN=6
# CONFIG_ESP_WIFI_AMSDU_TX_ENABLED is not set
CONFIG_ESP_WIFI_NVS_ENABLED=y
CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_0=y
# CONFIG_ESP_WIFI_TASK_PINNED_TO_CORE_1 is not set
@@ -1435,7 +1460,6 @@ CONFIG_HAL_DEFAULT_ASSERTION_LEVEL=2
CONFIG_HAL_WDT_USE_ROM_IMPL=y
CONFIG_HAL_SPI_MASTER_FUNC_IN_IRAM=y
CONFIG_HAL_SPI_SLAVE_FUNC_IN_IRAM=y
# CONFIG_HAL_ECDSA_GEN_SIG_CM is not set
# end of Hardware Abstraction Layer (HAL) and Low Level (LL)
#
@@ -1837,25 +1861,10 @@ CONFIG_STDATOMIC_S32C1I_SPIRAM_WORKAROUND=y
# CONFIG_OPENTHREAD_ENABLED is not set
#
# Thread Operational Dataset
# OpenThread Spinel
#
CONFIG_OPENTHREAD_NETWORK_NAME="OpenThread-ESP"
CONFIG_OPENTHREAD_MESH_LOCAL_PREFIX="fd00:db8:a0:0::/64"
CONFIG_OPENTHREAD_NETWORK_CHANNEL=15
CONFIG_OPENTHREAD_NETWORK_PANID=0x1234
CONFIG_OPENTHREAD_NETWORK_EXTPANID="dead00beef00cafe"
CONFIG_OPENTHREAD_NETWORK_MASTERKEY="00112233445566778899aabbccddeeff"
CONFIG_OPENTHREAD_NETWORK_PSKC="104810e2315100afd6bc9215a6bfac53"
# end of Thread Operational Dataset
CONFIG_OPENTHREAD_XTAL_ACCURACY=130
# CONFIG_OPENTHREAD_SPINEL_ONLY is not set
CONFIG_OPENTHREAD_RX_ON_WHEN_IDLE=y
#
# Thread Address Query Config
#
# end of Thread Address Query Config
# end of OpenThread Spinel
# end of OpenThread
#
@@ -1864,6 +1873,7 @@ CONFIG_OPENTHREAD_RX_ON_WHEN_IDLE=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_0=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_1=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_VERSION_2=y
CONFIG_ESP_PROTOCOMM_SUPPORT_SECURITY_PATCH_VERSION=y
# end of Protocomm
#
@@ -2237,3 +2247,195 @@ CONFIG_UVC_CAM1_TASK_CORE=-1
# end of Component config
# CONFIG_IDF_EXPERIMENTAL_FEATURES is not set
# Deprecated options for backward compatibility
# CONFIG_APP_BUILD_TYPE_ELF_RAM is not set
# CONFIG_NO_BLOBS is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_WARN is not set
CONFIG_LOG_BOOTLOADER_LEVEL_INFO=y
# CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set
# CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set
CONFIG_LOG_BOOTLOADER_LEVEL=3
# CONFIG_APP_ROLLBACK_ENABLE is not set
# CONFIG_FLASH_ENCRYPTION_ENABLED is not set
CONFIG_FLASHMODE_QIO=y
# CONFIG_FLASHMODE_QOUT is not set
# CONFIG_FLASHMODE_DIO is not set
# CONFIG_FLASHMODE_DOUT is not set
CONFIG_MONITOR_BAUD=115200
# CONFIG_OPTIMIZATION_LEVEL_DEBUG is not set
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_DEBUG is not set
# CONFIG_COMPILER_OPTIMIZATION_DEFAULT is not set
# CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set
# CONFIG_COMPILER_OPTIMIZATION_LEVEL_RELEASE is not set
CONFIG_OPTIMIZATION_ASSERTIONS_ENABLED=y
# CONFIG_OPTIMIZATION_ASSERTIONS_SILENT is not set
# CONFIG_OPTIMIZATION_ASSERTIONS_DISABLED is not set
CONFIG_OPTIMIZATION_ASSERTION_LEVEL=2
# CONFIG_CXX_EXCEPTIONS is not set
CONFIG_STACK_CHECK_NONE=y
# CONFIG_STACK_CHECK_NORM is not set
# CONFIG_STACK_CHECK_STRONG is not set
# CONFIG_STACK_CHECK_ALL is not set
# CONFIG_WARN_WRITE_STRINGS is not set
# CONFIG_ESP32_APPTRACE_DEST_TRAX is not set
CONFIG_ESP32_APPTRACE_DEST_NONE=y
CONFIG_ESP32_APPTRACE_LOCK_ENABLE=y
# CONFIG_EXTERNAL_COEX_ENABLE is not set
# CONFIG_ESP_WIFI_EXTERNAL_COEXIST_ENABLE is not set
# CONFIG_MCPWM_ISR_IN_IRAM is not set
# CONFIG_EVENT_LOOP_PROFILING is not set
CONFIG_POST_EVENTS_FROM_ISR=y
CONFIG_POST_EVENTS_FROM_IRAM_ISR=y
CONFIG_GDBSTUB_SUPPORT_TASKS=y
CONFIG_GDBSTUB_MAX_TASKS=32
# CONFIG_OTA_ALLOW_HTTP is not set
CONFIG_ESP32S3_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP_SLEEP_DEEP_SLEEP_WAKEUP_DELAY=2000
CONFIG_ESP32S3_RTC_CLK_SRC_INT_RC=y
# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_CRYS is not set
# CONFIG_ESP32S3_RTC_CLK_SRC_EXT_OSC is not set
# CONFIG_ESP32S3_RTC_CLK_SRC_INT_8MD256 is not set
CONFIG_ESP32S3_RTC_CLK_CAL_CYCLES=1024
CONFIG_ESP32_PHY_CALIBRATION_AND_DATA_STORAGE=y
# CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set
CONFIG_ESP32_PHY_MAX_WIFI_TX_POWER=20
CONFIG_ESP32_PHY_MAX_TX_POWER=20
# CONFIG_REDUCE_PHY_TX_POWER is not set
# CONFIG_ESP32_REDUCE_PHY_TX_POWER is not set
CONFIG_ESP_SYSTEM_PM_POWER_DOWN_CPU=y
CONFIG_PM_POWER_DOWN_TAGMEM_IN_LIGHT_SLEEP=y
CONFIG_ESP32S3_SPIRAM_SUPPORT=y
CONFIG_DEFAULT_PSRAM_CLK_IO=30
CONFIG_DEFAULT_PSRAM_CS_IO=26
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_80 is not set
# CONFIG_ESP32S3_DEFAULT_CPU_FREQ_160 is not set
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32S3_DEFAULT_CPU_FREQ_MHZ=240
CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2304
CONFIG_MAIN_TASK_STACK_SIZE=3584
CONFIG_CONSOLE_UART_DEFAULT=y
# CONFIG_CONSOLE_UART_CUSTOM is not set
# CONFIG_CONSOLE_UART_NONE is not set
# CONFIG_ESP_CONSOLE_UART_NONE is not set
CONFIG_CONSOLE_UART=y
CONFIG_CONSOLE_UART_NUM=0
CONFIG_CONSOLE_UART_BAUDRATE=115200
CONFIG_INT_WDT=y
CONFIG_INT_WDT_TIMEOUT_MS=300
CONFIG_INT_WDT_CHECK_CPU1=y
CONFIG_TASK_WDT=y
CONFIG_ESP_TASK_WDT=y
# CONFIG_TASK_WDT_PANIC is not set
CONFIG_TASK_WDT_TIMEOUT_S=5
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU0=y
CONFIG_TASK_WDT_CHECK_IDLE_TASK_CPU1=y
# CONFIG_ESP32_DEBUG_STUBS_ENABLE is not set
CONFIG_ESP32S3_DEBUG_OCDAWARE=y
CONFIG_BROWNOUT_DET=y
CONFIG_ESP32S3_BROWNOUT_DET=y
CONFIG_BROWNOUT_DET_LVL_SEL_7=y
CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_7=y
# CONFIG_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_6 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_5 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_4 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_3 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_2 is not set
# CONFIG_BROWNOUT_DET_LVL_SEL_1 is not set
# CONFIG_ESP32S3_BROWNOUT_DET_LVL_SEL_1 is not set
CONFIG_BROWNOUT_DET_LVL=7
CONFIG_ESP32S3_BROWNOUT_DET_LVL=7
CONFIG_IPC_TASK_STACK_SIZE=1280
CONFIG_TIMER_TASK_STACK_SIZE=3584
CONFIG_ESP32_WIFI_ENABLED=y
CONFIG_ESP32_WIFI_STATIC_RX_BUFFER_NUM=10
CONFIG_ESP32_WIFI_DYNAMIC_RX_BUFFER_NUM=32
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER=y
# CONFIG_ESP32_WIFI_DYNAMIC_TX_BUFFER is not set
CONFIG_ESP32_WIFI_TX_BUFFER_TYPE=0
CONFIG_ESP32_WIFI_STATIC_TX_BUFFER_NUM=16
# CONFIG_ESP32_WIFI_CSI_ENABLED is not set
CONFIG_ESP32_WIFI_AMPDU_TX_ENABLED=y
CONFIG_ESP32_WIFI_TX_BA_WIN=6
CONFIG_ESP32_WIFI_AMPDU_RX_ENABLED=y
CONFIG_ESP32_WIFI_RX_BA_WIN=6
CONFIG_ESP32_WIFI_NVS_ENABLED=y
CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_0=y
# CONFIG_ESP32_WIFI_TASK_PINNED_TO_CORE_1 is not set
CONFIG_ESP32_WIFI_SOFTAP_BEACON_MAX_LEN=752
CONFIG_ESP32_WIFI_MGMT_SBUF_NUM=32
CONFIG_ESP32_WIFI_IRAM_OPT=y
CONFIG_ESP32_WIFI_RX_IRAM_OPT=y
CONFIG_ESP32_WIFI_ENABLE_WPA3_SAE=y
CONFIG_ESP32_WIFI_ENABLE_WPA3_OWE_STA=y
CONFIG_WPA_MBEDTLS_CRYPTO=y
CONFIG_WPA_MBEDTLS_TLS_CLIENT=y
# CONFIG_WPA_WAPI_PSK is not set
# CONFIG_WPA_SUITE_B_192 is not set
# CONFIG_WPA_11KV_SUPPORT is not set
# CONFIG_WPA_MBO_SUPPORT is not set
# CONFIG_WPA_DPP_SUPPORT is not set
# CONFIG_WPA_11R_SUPPORT is not set
# CONFIG_WPA_WPS_SOFTAP_REGISTRAR is not set
# CONFIG_WPA_WPS_STRICT is not set
# CONFIG_WPA_DEBUG_PRINT is not set
# CONFIG_WPA_TESTING_OPTIONS is not set
# CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set
# CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set
CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y
CONFIG_TIMER_TASK_PRIORITY=1
CONFIG_TIMER_TASK_STACK_DEPTH=2048
CONFIG_TIMER_QUEUE_LENGTH=10
# CONFIG_ENABLE_STATIC_TASK_CLEAN_UP_HOOK is not set
# CONFIG_HAL_ASSERTION_SILIENT is not set
# CONFIG_L2_TO_L3_COPY is not set
CONFIG_ESP_GRATUITOUS_ARP=y
CONFIG_GARP_TMR_INTERVAL=60
CONFIG_TCPIP_RECVMBOX_SIZE=32
CONFIG_TCP_MAXRTX=12
CONFIG_TCP_SYNMAXRTX=12
CONFIG_TCP_MSS=1440
CONFIG_TCP_MSL=60000
CONFIG_TCP_SND_BUF_DEFAULT=5760
CONFIG_TCP_WND_DEFAULT=5760
CONFIG_TCP_RECVMBOX_SIZE=6
CONFIG_TCP_QUEUE_OOSEQ=y
CONFIG_TCP_OVERSIZE_MSS=y
# CONFIG_TCP_OVERSIZE_QUARTER_MSS is not set
# CONFIG_TCP_OVERSIZE_DISABLE is not set
CONFIG_UDP_RECVMBOX_SIZE=6
CONFIG_TCPIP_TASK_STACK_SIZE=3072
CONFIG_TCPIP_TASK_AFFINITY_NO_AFFINITY=y
# CONFIG_TCPIP_TASK_AFFINITY_CPU0 is not set
# CONFIG_TCPIP_TASK_AFFINITY_CPU1 is not set
CONFIG_TCPIP_TASK_AFFINITY=0x7FFFFFFF
# CONFIG_PPP_SUPPORT is not set
CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_SYSTIMER=y
CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC_FRC1=y
# CONFIG_ESP32S3_TIME_SYSCALL_USE_RTC is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_SYSTIMER is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_FRC1 is not set
# CONFIG_ESP32S3_TIME_SYSCALL_USE_NONE is not set
CONFIG_ESP32_PTHREAD_TASK_PRIO_DEFAULT=5
CONFIG_ESP32_PTHREAD_TASK_STACK_SIZE_DEFAULT=3072
CONFIG_ESP32_PTHREAD_STACK_MIN=768
CONFIG_ESP32_DEFAULT_PTHREAD_CORE_NO_AFFINITY=y
# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_0 is not set
# CONFIG_ESP32_DEFAULT_PTHREAD_CORE_1 is not set
CONFIG_ESP32_PTHREAD_TASK_CORE_DEFAULT=-1
CONFIG_ESP32_PTHREAD_TASK_NAME_DEFAULT="pthread"
CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ABORTS=y
# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_FAILS is not set
# CONFIG_SPI_FLASH_WRITING_DANGEROUS_REGIONS_ALLOWED is not set
CONFIG_SUPPRESS_SELECT_DEBUG_OUTPUT=y
CONFIG_SUPPORT_TERMIOS=y
CONFIG_SEMIHOSTFS_MAX_MOUNT_POINTS=1
# End of deprecated options

816
tools/openiris_setup.py Normal file
View File

@@ -0,0 +1,816 @@
#!/usr/bin/env python3
"""
OpenIris Setup CLI Tool
This tool automatically discovers OpenIris devices via heartbeat,
allows WiFi configuration, and monitors the device logs.
"""
import json
import time
import threading
import argparse
import sys
from typing import Dict, List, Optional, Tuple
import serial
import serial.tools.list_ports
from dataclasses import dataclass
@dataclass
class WiFiNetwork:
ssid: str
channel: int
rssi: int
mac_address: str
auth_mode: int
@property
def security_type(self) -> str:
"""Convert auth_mode to human readable string"""
auth_modes = {
0: "Open",
1: "WEP",
2: "WPA PSK",
3: "WPA2 PSK",
4: "WPA WPA2 PSK",
5: "WPA2 Enterprise",
6: "WPA3 PSK",
7: "WPA2 WPA3 PSK"
}
return auth_modes.get(self.auth_mode, f"Unknown ({self.auth_mode})")
class OpenIrisDevice:
def __init__(self, port: str, serial_number: str, debug: bool = False):
self.port = port
self.serial_number = serial_number
self.connection: Optional[serial.Serial] = None
self.networks: List[WiFiNetwork] = []
self.debug = debug
def connect(self) -> bool:
"""Connect to the device"""
try:
self.connection = serial.Serial(
port=self.port,
baudrate=115200,
timeout=1,
write_timeout=1
)
print(f"✅ Connected to device {self.serial_number} on {self.port}")
# Immediately send pause command to keep device in setup mode
print("⏸️ Pausing device startup...")
# Use shorter timeout for pause command since device is just starting up
pause_response = self.send_command("pause", {"pause": True}, timeout=5)
if "error" not in pause_response and pause_response.get("results"):
print("✅ Device paused in setup mode")
elif "error" in pause_response and pause_response["error"] == "Command timeout":
# Even if we timeout, the command likely worked (as seen in logs)
print("✅ Device pause command sent (startup logs may have obscured response)")
else:
print(f"⚠️ Pause status uncertain: {pause_response}")
return True
except Exception as e:
print(f"❌ Failed to connect to {self.port}: {e}")
return False
def disconnect(self):
"""Disconnect from the device"""
if self.connection and self.connection.is_open:
# Optionally unpause the device before disconnecting
print("Resuming device startup...")
self.send_command("pause", {"pause": False})
self.connection.close()
print(f"🔌 Disconnected from {self.port}")
def send_command(self, command: str, params: Dict = None, timeout: int = None) -> Dict:
"""Send a command to the device and wait for response"""
if not self.connection or not self.connection.is_open:
return {"error": "Not connected"}
cmd_obj = {"commands": [{"command": command}]}
if params:
cmd_obj["commands"][0]["data"] = params
cmd_json = json.dumps(cmd_obj) + '\n'
try:
# Clear any pending data
self.connection.reset_input_buffer()
# Send command
print(f"📤 Sending: {cmd_json.strip()}")
self.connection.write(cmd_json.encode())
# For scan_networks command, handle special case
if command == "scan_networks":
# Use provided timeout or default to 30 seconds for scan
scan_timeout = timeout if timeout is not None else 30
return self._handle_scan_response(scan_timeout)
# Wait for response (skip heartbeats and logs)
start_time = time.time()
response_buffer = ""
# Use provided timeout or default to 15 seconds
cmd_timeout = timeout if timeout is not None else 15
while time.time() - start_time < cmd_timeout:
try:
if self.connection.in_waiting:
data = self.connection.read(self.connection.in_waiting).decode('utf-8', errors='ignore')
response_buffer += data
# Show raw data for debugging
if self.debug and data.strip():
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', '')
# Look for JSON objects - handle both single-line and multi-line
# Try to find complete JSON objects
start_idx = clean_buffer.find('{')
while start_idx >= 0:
# Count braces to find complete JSON
brace_count = 0
end_idx = -1
for i in range(start_idx, len(clean_buffer)):
if clean_buffer[i] == '{':
brace_count += 1
elif clean_buffer[i] == '}':
brace_count -= 1
if brace_count == 0:
end_idx = i + 1
break
if end_idx > start_idx:
json_str = clean_buffer[start_idx:end_idx]
# Try to parse any complete JSON object
try:
# Clean up the JSON
clean_json = json_str.replace('\t', ' ').replace('\n', ' ').replace('\r', '')
clean_json = re.sub(r'\s+', ' ', clean_json)
response = json.loads(clean_json)
# Return if this is a command response with results
if "results" in response:
return response
except json.JSONDecodeError:
pass
# Look for next JSON object
start_idx = clean_buffer.find('{', end_idx)
else:
# No complete JSON found yet
break
else:
time.sleep(0.1)
except Exception as e:
print(f"⚠️ Exception: {e}")
continue
return {"error": "Command timeout"}
except Exception as e:
return {"error": f"Communication error: {e}"}
def _handle_scan_response(self, timeout: int = 30) -> Dict:
"""Handle scan_networks command response which outputs raw JSON first"""
start_time = time.time()
response_buffer = ""
while time.time() - start_time < timeout: # Configurable timeout for scan
if self.connection.in_waiting:
data = self.connection.read(self.connection.in_waiting).decode('utf-8', errors='ignore')
response_buffer += data
# Look for WiFi networks JSON directly (new format)
# The scan command now outputs JSON directly followed by command result
if '{"networks":[' in response_buffer:
import re
# Look for the networks JSON pattern that appears first
networks_pattern = r'\{"networks":\[.*?\]\}'
matches = re.findall(networks_pattern, response_buffer, re.DOTALL)
for match in matches:
try:
# Parse the networks JSON directly
networks_data = json.loads(match)
if "networks" in networks_data:
# Return in the expected format for compatibility
return {"results": [json.dumps({"result": match})]}
except json.JSONDecodeError:
continue
# Also check if we have the command result indicating completion
if '{"results":' in response_buffer and '"Networks scanned"' in response_buffer:
# We've received the completion message, parse any networks found
import re
networks_pattern = r'\{"networks":\[.*?\]\}'
matches = re.findall(networks_pattern, response_buffer, re.DOTALL)
for match in matches:
try:
networks_data = json.loads(match)
if "networks" in networks_data:
return {"results": [json.dumps({"result": match})]}
except json.JSONDecodeError:
continue
# If we get here, scan completed but no networks found
return {"results": [json.dumps({"result": '{"networks":[]}'})]}
else:
time.sleep(0.1)
return {"error": "Scan timeout"}
def scan_networks(self, timeout: int = 30) -> bool:
"""Scan for WiFi networks"""
print(f"🔍 Scanning for WiFi networks (this may take up to {timeout} seconds)...")
response = self.send_command("scan_networks", timeout=timeout)
if "error" in response:
print(f"❌ Scan failed: {response['error']}")
return False
try:
# Parse the nested JSON response
results = response.get("results", [])
if not results:
print("❌ No scan results received")
return False
# The result is JSON-encoded string inside the response
result_data = json.loads(results[0])
networks_data = json.loads(result_data["result"])
self.networks = []
channels_found = set()
for net in networks_data.get("networks", []):
network = WiFiNetwork(
ssid=net["ssid"],
channel=net["channel"],
rssi=net["rssi"],
mac_address=net["mac_address"],
auth_mode=net["auth_mode"]
)
self.networks.append(network)
channels_found.add(net["channel"])
# Sort networks by RSSI (strongest first)
self.networks.sort(key=lambda x: x.rssi, reverse=True)
print(f"✅ Found {len(self.networks)} networks on channels: {sorted(channels_found)}")
return True
except Exception as e:
print(f"❌ Failed to parse scan results: {e}")
return False
def set_wifi(self, ssid: str, password: str) -> bool:
"""Configure WiFi credentials"""
print(f"🔧 Setting WiFi credentials for '{ssid}'...")
params = {
"name": "main",
"ssid": ssid,
"password": password,
"channel": 0,
"power": 0
}
response = self.send_command("set_wifi", params)
if "error" in response:
print(f"❌ WiFi setup failed: {response['error']}")
return False
print("✅ WiFi credentials set successfully")
return True
def get_wifi_status(self) -> Dict:
"""Get current WiFi connection status"""
response = self.send_command("get_wifi_status")
if "error" in response:
print(f"❌ Failed to get WiFi status: {response['error']}")
return {}
try:
# Parse the nested JSON response
results = response.get("results", [])
if results:
result_data = json.loads(results[0])
# The result is a JSON-encoded string, need to decode it
status_json = result_data["result"]
# First, unescape the JSON string properly
# Replace escaped backslashes and quotes
status_json = status_json.replace('\\\\', '\\')
status_json = status_json.replace('\\"', '"')
# Now parse the cleaned JSON
status_data = json.loads(status_json)
return status_data
except Exception as e:
print(f"❌ Failed to parse WiFi status: {e}")
# Try to show raw response for debugging
if "results" in response and response["results"]:
print(f"📝 Raw result: {response['results'][0]}")
return {}
def connect_wifi(self) -> bool:
"""Attempt to connect to configured WiFi"""
print("🔗 Attempting WiFi connection...")
response = self.send_command("connect_wifi")
if "error" in response:
print(f"❌ WiFi connection failed: {response['error']}")
return False
print("✅ WiFi connection attempt started")
return True
def start_streaming(self) -> bool:
"""Start streaming mode"""
print("🚀 Starting streaming mode...")
response = self.send_command("start_streaming")
if "error" in response:
print(f"❌ Failed to start streaming: {response['error']}")
return False
print("✅ Streaming mode started")
return True
def switch_mode(self, mode: str) -> bool:
"""Switch device mode between WiFi, UVC, and Auto"""
print(f"🔄 Switching device mode to '{mode}'...")
params = {"mode": mode}
response = self.send_command("switch_mode", params)
if "error" in response:
print(f"❌ Failed to switch mode: {response['error']}")
return False
print(f"✅ Device mode switched to '{mode}' successfully!")
print("🔄 Please restart the device for changes to take effect")
return True
def get_device_mode(self) -> str:
"""Get current device mode"""
response = self.send_command("get_device_mode")
if "error" in response:
print(f"❌ Failed to get device mode: {response['error']}")
return "unknown"
try:
results = response.get("results", [])
if results:
result_data = json.loads(results[0])
mode_data = json.loads(result_data["result"])
return mode_data.get("mode", "unknown")
except Exception as e:
print(f"❌ Failed to parse mode response: {e}")
return "unknown"
def monitor_logs(self):
"""Monitor device logs until interrupted"""
print("📋 Monitoring device logs (Press Ctrl+C to exit)...")
print("-" * 60)
if not self.connection or not self.connection.is_open:
print("❌ Not connected to device")
return
try:
while True:
try:
if self.connection.in_waiting > 0:
line = self.connection.readline().decode().strip()
if line:
# Skip JSON command responses, show raw logs
if not (line.startswith('{') and line.endswith('}')):
print(line)
elif "heartbeat" not in line:
# Show non-heartbeat JSON responses
print(f"📡 {line}")
else:
time.sleep(0.1) # Small delay to prevent busy waiting
except Exception:
continue
except KeyboardInterrupt:
print("\n🛑 Log monitoring stopped")
class OpenIrisDiscovery:
def __init__(self):
self.devices: Dict[str, OpenIrisDevice] = {}
self.discovery_active = False
def discover_devices(self, timeout: int = 3) -> List[OpenIrisDevice]:
"""Discover OpenIris devices via heartbeat - ultra-fast concurrent scanning"""
print(f"⚡ Fast-scanning for OpenIris devices...")
# Get all serial ports
ports = list(serial.tools.list_ports.comports())
if not ports:
print("❌ No serial ports found")
return []
# Prioritize likely ESP32 USB ports for faster detection
priority_ports = []
other_ports = []
for port in ports:
# Common ESP32 USB-to-serial descriptions
desc_lower = (port.description or "").lower()
# Include generic "USB Serial Device" which is common on Windows
if any(keyword in desc_lower for keyword in
["cp210", "ch340", "ftdi", "esp32", "silicon labs", "usb-serial", "usb serial", "usb serial device"]):
priority_ports.append(port)
else:
other_ports.append(port)
# Check priority ports first, then others
sorted_ports = priority_ports + other_ports
if priority_ports:
print(f"📡 Checking {len(sorted_ports)} ports ({len(priority_ports)} prioritized USB serial ports)...")
else:
print(f"📡 Checking {len(sorted_ports)} serial ports...")
discovered = {}
lock = threading.Lock()
threads = []
def check_port_fast(port_info):
"""Check a single port for OpenIris heartbeat - optimized for speed"""
try:
# Initial connection timeout - 500ms
ser = serial.Serial(port_info.device, 115200, timeout=0.5)
ser.reset_input_buffer()
# Wait up to 2 seconds for heartbeat
start_time = time.time()
while time.time() - start_time < 2.0:
try:
# Read timeout - 200ms
ser.timeout = 10
if ser.in_waiting > 0:
line = ser.readline()
if line:
try:
data = json.loads(line.decode().strip())
if (data.get("heartbeat") == "openiris_setup_mode" and
"serial" in data):
serial_num = data["serial"]
with lock:
if serial_num not in discovered:
device = OpenIrisDevice(port_info.device, serial_num, debug=False)
discovered[serial_num] = device
print(f"💓 Found {serial_num} on {port_info.device}")
# Return immediately to stop checking this port
ser.close()
return True
except (json.JSONDecodeError, UnicodeDecodeError):
pass
else:
time.sleep(0.05) # Very short sleep
except Exception:
pass
ser.close()
except Exception:
# Port not available or not the right device
pass
return False
# Start concurrent port checking
for port in sorted_ports:
thread = threading.Thread(target=check_port_fast, args=(port,))
thread.daemon = True
thread.start()
threads.append(thread)
# Wait for threads to complete or timeout
timeout_time = time.time() + timeout
for thread in threads:
remaining = timeout_time - time.time()
if remaining > 0:
thread.join(timeout=remaining)
# If we found at least one device, return immediately
if discovered:
break
devices = list(discovered.values())
if devices:
print(f"✅ Found {len(devices)} OpenIris device(s)")
else:
print("❌ No OpenIris devices found in {:.1f} seconds".format(time.time() - (timeout_time - timeout)))
print("💡 Device has 20-second setup window after power on")
return devices
def _check_port(self, port: str, discovered: Dict, timeout: int):
"""Check a single port for OpenIris heartbeat"""
try:
with serial.Serial(port, 115200, timeout=1) as ser:
start_time = time.time()
while time.time() - start_time < timeout:
try:
line = ser.readline().decode().strip()
if line:
try:
data = json.loads(line)
if (data.get("heartbeat") == "openiris_setup_mode" and
"serial" in data):
serial_num = data["serial"]
if serial_num not in discovered:
discovered[serial_num] = OpenIrisDevice(port, serial_num, debug=False)
print(f"💓 Found device {serial_num} on {port}")
return
except json.JSONDecodeError:
continue
except Exception:
continue
except Exception:
# Port not available or not a serial device
pass
def display_networks(networks: List[WiFiNetwork]):
"""Display available WiFi networks in a formatted table"""
if not networks:
print("❌ No networks available")
return
print("\n📡 Available WiFi Networks:")
print("-" * 85)
print(f"{'#':<3} {'SSID':<32} {'Channel':<8} {'Signal':<20} {'Security':<15}")
print("-" * 85)
# Networks are already sorted by signal strength from scan_networks
for i, network in enumerate(networks, 1):
# Create signal strength visualization
signal_bars = "" * min(5, max(0, (network.rssi + 100) // 10))
signal_str = f"{signal_bars:<5} ({network.rssi} dBm)"
# Format SSID (show hidden networks as <hidden>)
ssid_display = network.ssid if network.ssid else "<hidden>"
print(f"{i:<3} {ssid_display:<32} {network.channel:<8} {signal_str:<20} {network.security_type:<15}")
print("-" * 85)
# Show channel distribution
channels = {}
for net in networks:
channels[net.channel] = channels.get(net.channel, 0) + 1
print(f"\n📊 Channel distribution: ", end="")
for ch in sorted(channels.keys()):
print(f"Ch{ch}: {channels[ch]} networks ", end="")
print()
def main():
parser = argparse.ArgumentParser(description="OpenIris Setup CLI Tool")
parser.add_argument("--timeout", type=int, default=3,
help="Discovery timeout in seconds (default: 3)")
parser.add_argument("--port", type=str,
help="Skip discovery and connect directly to specified port")
parser.add_argument("--scan-timeout", type=int, default=30,
help="WiFi scan timeout in seconds (default: 30)")
parser.add_argument("--no-auto", action="store_true",
help="Don't auto-connect to first device found")
parser.add_argument("--debug", action="store_true",
help="Show debug output including raw serial data")
args = parser.parse_args()
print("🔧 OpenIris Setup Tool")
print("=" * 50)
device = None
try:
if args.port:
# Connect directly to specified port
print(f"📡 Connecting directly to {args.port}...")
device = OpenIrisDevice(args.port, "direct", debug=args.debug)
if not device.connect():
return 1
else:
# Fast device discovery
discovery = OpenIrisDiscovery()
devices = discovery.discover_devices(args.timeout)
if not devices:
print("\n❌ No OpenIris devices found automatically")
print("\n💡 Troubleshooting:")
print(" - Make sure device is connected via USB")
print(" - Device must be powered on within last 20 seconds")
print(" - Try specifying port manually with --port")
# Show available ports to help user
print("\n📋 Available serial ports:")
all_ports = list(serial.tools.list_ports.comports())
for p in all_ports:
print(f" - {p.device}: {p.description}")
# Offer manual port entry
manual_port = input("\n🔌 Enter serial port manually (e.g. COM15, /dev/ttyUSB0) or press Enter to exit: ").strip()
if manual_port:
device = OpenIrisDevice(manual_port, "manual", debug=args.debug)
if not device.connect():
return 1
else:
return 1
else:
# Auto-connect to first device found (unless disabled)
if len(devices) == 1 or not args.no_auto:
device = devices[0]
device.debug = args.debug # Set debug mode
print(f"\n🎯 Auto-connecting to {device.serial_number}...")
if not device.connect():
return 1
else:
# Multiple devices found with no-auto flag
print("\n🔢 Multiple devices found. Select one:")
for i, dev in enumerate(devices, 1):
print(f" {i}. {dev.serial_number} on {dev.port}")
while True:
try:
choice = int(input("\nEnter device number: ")) - 1
if 0 <= choice < len(devices):
device = devices[choice]
break
else:
print("❌ Invalid selection")
except ValueError:
print("❌ Please enter a number")
# Connect to selected device
device.debug = args.debug # Set debug mode
if not device.connect():
return 1
# Main interaction loop
while True:
print("\n🔧 Setup Options:")
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")
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":
break
else:
print("❌ Invalid option")
except KeyboardInterrupt:
print("\n🛑 Setup interrupted")
finally:
if device:
device.disconnect()
return 0
if __name__ == "__main__":
sys.exit(main())

1
tools/requirements.txt Normal file
View File

@@ -0,0 +1 @@
pyserial>=3.5

7
tools/setup.bat Normal file
View File

@@ -0,0 +1,7 @@
@echo off
echo Installing OpenIris Setup Tool dependencies...
pip install -r requirements.txt
echo.
echo Setup complete! Run the tool with:
echo python openiris_setup.py
pause

6
tools/setup.sh Normal file
View File

@@ -0,0 +1,6 @@
#!/bin/bash
echo "Installing OpenIris Setup Tool dependencies..."
pip3 install -r requirements.txt
echo ""
echo "Setup complete! Run the tool with:"
echo "python3 openiris_setup.py"

177
tools/wifi_scanner.py Normal file
View File

@@ -0,0 +1,177 @@
import serial
import time
import json
from typing import List, Dict
class ESPWiFiScanner:
def __init__(self, port: str, baudrate: int = 115200):
self.port = port
self.baudrate = baudrate
self.serial = None
def connect(self) -> bool:
try:
self.serial = serial.Serial(
port=self.port,
baudrate=self.baudrate,
timeout=1
)
return True
except serial.SerialException as e:
print(f"Error connecting to ESP32: {e}")
return False
def scan_networks(self, timeout_seconds: int = 30) -> List[Dict]:
if not self.serial:
print("Not connected to ESP32")
return []
self.serial.reset_input_buffer()
command = '{"commands":[{"command":"scan_networks","data":{}}]}\n'
self.serial.write(command.encode())
timeout_start = time.time()
response_buffer = ""
while time.time() - timeout_start < timeout_seconds:
if self.serial.in_waiting:
data = self.serial.read(self.serial.in_waiting).decode('utf-8', errors='ignore')
response_buffer += data
# Look for WiFi networks JSON directly (new format)
# The scan command now outputs JSON directly followed by command result
if '{"networks":[' in response_buffer:
import re
# Look for the networks JSON pattern that appears first
networks_pattern = r'\{"networks":\[.*?\]\}'
matches = re.findall(networks_pattern, response_buffer, re.DOTALL)
for match in matches:
try:
# Parse the networks JSON directly
networks_data = json.loads(match)
if "networks" in networks_data:
return networks_data["networks"]
except json.JSONDecodeError:
continue
# Also check if we have the command result indicating completion
if '{"results":' in response_buffer and '"Networks scanned"' in response_buffer:
# We've received the completion message, parse any networks found
import re
networks_pattern = r'\{"networks":\[.*?\]\}'
matches = re.findall(networks_pattern, response_buffer, re.DOTALL)
for match in matches:
try:
networks_data = json.loads(match)
if "networks" in networks_data:
return networks_data["networks"]
except json.JSONDecodeError:
continue
# If we get here, scan completed but no networks found
return []
else:
time.sleep(0.1)
print("Failed to receive clean JSON response. Raw data:")
print("=" * 50)
print(response_buffer)
print("=" * 50)
return []
def close(self):
if self.serial:
self.serial.close()
def main():
import sys
import argparse
parser = argparse.ArgumentParser(description='ESP32 WiFi Scanner')
parser.add_argument('port', nargs='?', default='COM15', help='Serial port (default: COM9)')
parser.add_argument('-t', '--timeout', type=int, default=30,
help='Scan timeout in seconds (default: 30)')
args = parser.parse_args()
scanner = ESPWiFiScanner(args.port)
if scanner.connect():
print(f"Connected to ESP32 on {args.port}")
print(f"Scanning for WiFi networks (timeout: {args.timeout} seconds)...")
start_time = time.time()
networks = scanner.scan_networks(args.timeout)
scan_time = time.time() - start_time
if networks:
# Sort by RSSI (strongest first)
networks.sort(key=lambda x: x.get('rssi', -100), reverse=True)
print(f"\n✅ Found {len(networks)} WiFi Networks in {scan_time:.1f} seconds:")
print("{:<32} | {:<7} | {:<15} | {:<17} | {:<9}".format(
"SSID", "Channel", "Signal", "MAC Address", "Security"
))
print("-" * 85)
# Track channels found
channels_found = set()
auth_modes = {
0: "Open",
1: "WEP",
2: "WPA-PSK",
3: "WPA2-PSK",
4: "WPA/WPA2",
5: "WPA2-Enterprise",
6: "WPA3-PSK",
7: "WPA2/WPA3",
8: "WAPI-PSK"
}
for network in networks:
ssid = network.get('ssid', '')
if not ssid:
ssid = "<hidden>"
channel = network.get('channel', 0)
channels_found.add(channel)
# Create signal strength visualization
rssi = network.get('rssi', -100)
signal_bars = "" * min(5, max(0, (rssi + 100) // 10))
signal_str = f"{signal_bars:<5} ({rssi} dBm)"
auth_mode = network.get('auth_mode', 0)
security = auth_modes.get(auth_mode, f"Type {auth_mode}")
print("{:<32} | {:<7} | {:<15} | {:<17} | {:<9}".format(
ssid[:32], # Truncate long SSIDs
channel,
signal_str,
network.get('mac_address', '?'),
security
))
# Show channel distribution
print(f"\n📊 Channels detected: {sorted(channels_found)}")
channel_counts = {}
for net in networks:
ch = net.get('channel', 0)
channel_counts[ch] = channel_counts.get(ch, 0) + 1
print("📊 Channel distribution: ", end="")
for ch in sorted(channel_counts.keys()):
print(f"Ch{ch}: {channel_counts[ch]} networks ", end="")
print()
else:
print("❌ No networks found or scan failed")
scanner.close()
else:
print(f"Failed to connect to ESP32 on {args.port}")
if __name__ == "__main__":
main()