Merge branch 'feature/facefocus-duty-cycle-poc'

This commit is contained in:
PhosphorosVR
2025-08-22 01:14:53 +02:00
15 changed files with 308 additions and 102 deletions

View File

@@ -11,5 +11,5 @@ idf_component_register(
INCLUDE_DIRS INCLUDE_DIRS
"CommandManager" "CommandManager"
"CommandManager/commands" "CommandManager/commands"
REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks wifiManager Helpers REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks wifiManager Helpers LEDManager
) )

View File

@@ -23,53 +23,80 @@ std::unordered_map<std::string, CommandType> commandTypeMap = {
{"connect_wifi", CommandType::CONNECT_WIFI}, {"connect_wifi", CommandType::CONNECT_WIFI},
{"switch_mode", CommandType::SWITCH_MODE}, {"switch_mode", CommandType::SWITCH_MODE},
{"get_device_mode", CommandType::GET_DEVICE_MODE}, {"get_device_mode", CommandType::GET_DEVICE_MODE},
{"set_led_duty_cycle", CommandType::SET_LED_DUTY_CYCLE},
{"get_led_duty_cycle", CommandType::GET_LED_DUTY_CYCLE},
}; };
std::function<CommandResult()> CommandManager::createCommand(const CommandType type, std::string_view json) const { std::function<CommandResult()> CommandManager::createCommand(const CommandType type, std::string_view json) const
{
switch (type) switch (type)
{ {
case CommandType::PING: case CommandType::PING:
return { PingCommand }; return {PingCommand};
case CommandType::PAUSE: case CommandType::PAUSE:
return [json] { return PauseCommand(json); }; return [json]
{ return PauseCommand(json); };
case CommandType::SET_STREAMING_MODE: case CommandType::SET_STREAMING_MODE:
return [this, json] {return setDeviceModeCommand(this->registry, json); }; return [this, json]
{ return setDeviceModeCommand(this->registry, json); };
case CommandType::UPDATE_OTA_CREDENTIALS: case CommandType::UPDATE_OTA_CREDENTIALS:
return [this, json] { return updateOTACredentialsCommand(this->registry, json); }; return [this, json]
{ return updateOTACredentialsCommand(this->registry, json); };
case CommandType::SET_WIFI: case CommandType::SET_WIFI:
return [this, json] { return setWiFiCommand(this->registry, json); }; return [this, json]
{ return setWiFiCommand(this->registry, json); };
case CommandType::UPDATE_WIFI: case CommandType::UPDATE_WIFI:
return [this, json] { return updateWiFiCommand(this->registry, json); }; return [this, json]
{ return updateWiFiCommand(this->registry, json); };
case CommandType::UPDATE_AP_WIFI: case CommandType::UPDATE_AP_WIFI:
return [this, json] { return updateAPWiFiCommand(this->registry, json); }; return [this, json]
{ return updateAPWiFiCommand(this->registry, json); };
case CommandType::DELETE_NETWORK: case CommandType::DELETE_NETWORK:
return [this, json] { return deleteWiFiCommand(this->registry, json); }; return [this, json]
{ return deleteWiFiCommand(this->registry, json); };
case CommandType::SET_MDNS: case CommandType::SET_MDNS:
return [this, json] { return setMDNSCommand(this->registry, json); }; return [this, json]
{ return setMDNSCommand(this->registry, json); };
case CommandType::UPDATE_CAMERA: case CommandType::UPDATE_CAMERA:
return [this, json] { return updateCameraCommand(this->registry, json); }; return [this, json]
{ return updateCameraCommand(this->registry, json); };
case CommandType::RESTART_CAMERA: case CommandType::RESTART_CAMERA:
return [this, json] { return restartCameraCommand(this->registry, json); }; return [this, json]
{ return restartCameraCommand(this->registry, json); };
case CommandType::GET_CONFIG: case CommandType::GET_CONFIG:
return [this] { return getConfigCommand(this->registry); }; return [this]
{ return getConfigCommand(this->registry); };
case CommandType::SAVE_CONFIG: case CommandType::SAVE_CONFIG:
return [this] { return saveConfigCommand(this->registry); }; return [this]
{ return saveConfigCommand(this->registry); };
case CommandType::RESET_CONFIG: case CommandType::RESET_CONFIG:
return [this, json] { return resetConfigCommand(this->registry, json); }; return [this, json]
{ return resetConfigCommand(this->registry, json); };
case CommandType::RESTART_DEVICE: case CommandType::RESTART_DEVICE:
return restartDeviceCommand; return restartDeviceCommand;
case CommandType::SCAN_NETWORKS: case CommandType::SCAN_NETWORKS:
return [this] { return scanNetworksCommand(this->registry); }; return [this]
{ return scanNetworksCommand(this->registry); };
case CommandType::START_STREAMING: case CommandType::START_STREAMING:
return startStreamingCommand; return startStreamingCommand;
case CommandType::GET_WIFI_STATUS: case CommandType::GET_WIFI_STATUS:
return [this] { return getWiFiStatusCommand(this->registry); }; return [this]
{ return getWiFiStatusCommand(this->registry); };
case CommandType::CONNECT_WIFI: case CommandType::CONNECT_WIFI:
return [this] { return connectWiFiCommand(this->registry); }; return [this]
{ return connectWiFiCommand(this->registry); };
case CommandType::SWITCH_MODE: case CommandType::SWITCH_MODE:
return [this, json] { return switchModeCommand(this->registry, json); }; return [this, json]
{ return switchModeCommand(this->registry, json); };
case CommandType::GET_DEVICE_MODE: case CommandType::GET_DEVICE_MODE:
return [this] { return getDeviceModeCommand(this->registry); }; return [this]
{ return getDeviceModeCommand(this->registry); };
case CommandType::SET_LED_DUTY_CYCLE:
return [this, json]
{ return updateLEDDutyCycleCommand(this->registry, json); };
case CommandType::GET_LED_DUTY_CYCLE:
return [this]
{ return getLEDDutyCycleCommand(this->registry); };
default: default:
return nullptr; return nullptr;
} }

View File

@@ -44,6 +44,8 @@ enum class CommandType
CONNECT_WIFI, CONNECT_WIFI,
SWITCH_MODE, SWITCH_MODE,
GET_DEVICE_MODE, GET_DEVICE_MODE,
SET_LED_DUTY_CYCLE,
GET_LED_DUTY_CYCLE,
}; };
class CommandManager class CommandManager

View File

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

View File

@@ -1,4 +1,5 @@
#include "device_commands.hpp" #include "device_commands.hpp"
#include "LEDManager.hpp"
// Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite // Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite
CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload) CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload)
@@ -25,6 +26,8 @@ CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry,
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config); const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
projectConfig->setDeviceMode(static_cast<StreamingMode>(mode)); projectConfig->setDeviceMode(static_cast<StreamingMode>(mode));
cJSON_Delete(parsedJson);
return CommandResult::getSuccessResult("Device mode set"); return CommandResult::getSuccessResult("Device mode set");
} }
@@ -64,16 +67,63 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> re
} }
} }
projectConfig->setDeviceConfig(OTALogin, OTAPassword, OTAPort); cJSON_Delete(parsedJson);
projectConfig->setOTAConfig(OTALogin, OTAPassword, OTAPort);
return CommandResult::getSuccessResult("OTA Config set"); return CommandResult::getSuccessResult("OTA Config set");
} }
CommandResult updateLEDDutyCycleCommand(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 dutyCycleObject = cJSON_GetObjectItem(parsedJson, "dutyCycle");
if (dutyCycleObject == nullptr)
{
return CommandResult::getErrorResult("Invalid payload - missing dutyCycle");
}
const auto dutyCycle = dutyCycleObject->valueint;
if (dutyCycle < 0 || dutyCycle > 100)
{
return CommandResult::getErrorResult("Invalid payload - unsupported dutyCycle");
}
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
projectConfig->setLEDDUtyCycleConfig(dutyCycle);
// Try to apply the change live via LEDManager if available
auto ledMgr = registry->resolve<LEDManager>(DependencyType::led_manager);
if (ledMgr)
{
ledMgr->setExternalLEDDutyCycle(static_cast<uint8_t>(dutyCycle));
}
cJSON_Delete(parsedJson);
return CommandResult::getSuccessResult("LED duty cycle set");
}
CommandResult restartDeviceCommand() CommandResult restartDeviceCommand()
{ {
OpenIrisTasks::ScheduleRestart(2000); OpenIrisTasks::ScheduleRestart(2000);
return CommandResult::getSuccessResult("Device restarted"); return CommandResult::getSuccessResult("Device restarted");
} }
CommandResult getLEDDutyCycleCommand(std::shared_ptr<DependencyRegistry> registry)
{
const auto projectConfig = registry->resolve<ProjectConfig>(DependencyType::project_config);
const auto deviceCfg = projectConfig->getDeviceConfig();
int duty = deviceCfg.led_external_pwm_duty_cycle;
auto result = std::format("{{ \"led_external_pwm_duty_cycle\": {} }}", duty);
return CommandResult::getSuccessResult(result);
}
CommandResult startStreamingCommand() CommandResult startStreamingCommand()
{ {
activateStreaming(false); // Don't disable setup interfaces by default activateStreaming(false); // Don't disable setup interfaces by default

View File

@@ -13,6 +13,9 @@ CommandResult setDeviceModeCommand(std::shared_ptr<DependencyRegistry> registry,
CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload); CommandResult updateOTACredentialsCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);
CommandResult updateLEDDutyCycleCommand(std::shared_ptr<DependencyRegistry> registry, std::string_view jsonPayload);
CommandResult getLEDDutyCycleCommand(std::shared_ptr<DependencyRegistry> registry);
CommandResult restartDeviceCommand(); CommandResult restartDeviceCommand();
CommandResult startStreamingCommand(); CommandResult startStreamingCommand();

View File

@@ -1,4 +1,4 @@
idf_component_register(SRCS "LEDManager/LEDManager.cpp" idf_component_register(SRCS "LEDManager/LEDManager.cpp"
INCLUDE_DIRS "LEDManager" INCLUDE_DIRS "LEDManager"
REQUIRES StateManager driver esp_driver_ledc Helpers REQUIRES StateManager driver esp_driver_ledc Helpers ProjectConfig
) )

View File

@@ -48,10 +48,7 @@ ledStateMap_t LEDManager::ledStateMap = {
{ {
false, false,
false, false,
{ {{LED_ON, 200}, {LED_OFF, 200}, {LED_ON, 200}, {LED_OFF, 200}, {LED_ON, 200}, {LED_OFF, 200}, {LED_ON, 200}, {LED_OFF, 200}, {LED_ON, 200}, {LED_OFF, 200}},
{LED_ON, 200}, {LED_OFF, 200}, {LED_ON, 200}, {LED_OFF, 200}, {LED_ON, 200}, {LED_OFF, 200},
{LED_ON, 200}, {LED_OFF, 200}, {LED_ON, 200}, {LED_OFF, 200}
},
}, },
}, },
{ {
@@ -65,32 +62,38 @@ ledStateMap_t LEDManager::ledStateMap = {
}; };
LEDManager::LEDManager(gpio_num_t pin, gpio_num_t illumninator_led_pin, LEDManager::LEDManager(gpio_num_t pin, gpio_num_t illumninator_led_pin,
QueueHandle_t ledStateQueue) : blink_led_pin(pin), QueueHandle_t ledStateQueue, std::shared_ptr<ProjectConfig> deviceConfig) : blink_led_pin(pin),
illumninator_led_pin(illumninator_led_pin), illumninator_led_pin(illumninator_led_pin),
ledStateQueue(ledStateQueue), ledStateQueue(ledStateQueue),
currentState(LEDStates_e::LedStateNone) { currentState(LEDStates_e::LedStateNone),
deviceConfig(deviceConfig)
{
} }
void LEDManager::setup() { void LEDManager::setup()
ESP_LOGD(LED_MANAGER_TAG, "Setting up status led."); {
ESP_LOGI(LED_MANAGER_TAG, "Setting up status led.");
gpio_reset_pin(blink_led_pin); gpio_reset_pin(blink_led_pin);
/* Set the GPIO as a push/pull output */ /* Set the GPIO as a push/pull output */
gpio_set_direction(blink_led_pin, GPIO_MODE_OUTPUT); gpio_set_direction(blink_led_pin, GPIO_MODE_OUTPUT);
this->toggleLED(LED_OFF); this->toggleLED(LED_OFF);
#ifdef CONFIG_LED_EXTERNAL_CONTROL #ifdef CONFIG_LED_EXTERNAL_CONTROL
ESP_LOGD(LED_MANAGER_TAG, "Setting up illuminator led."); ESP_LOGI(LED_MANAGER_TAG, "Setting up illuminator led.");
const int freq = CONFIG_LED_EXTERNAL_PWM_FREQ; const int freq = CONFIG_LED_EXTERNAL_PWM_FREQ;
const auto resolution = LEDC_TIMER_8_BIT; const auto resolution = LEDC_TIMER_8_BIT;
const int dutyCycle = (CONFIG_LED_EXTERNAL_PWM_DUTY_CYCLE * 255) / 100; const auto deviceConfig = this->deviceConfig->getDeviceConfig();
const uint32_t dutyCycle = (deviceConfig.led_external_pwm_duty_cycle * 255) / 100;
ESP_LOGI(LED_MANAGER_TAG, "Setting dutyCycle to: %lu ", dutyCycle);
ledc_timer_config_t ledc_timer = { ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE, .speed_mode = LEDC_LOW_SPEED_MODE,
.duty_resolution = resolution, .duty_resolution = resolution,
.timer_num = LEDC_TIMER_0, .timer_num = LEDC_TIMER_0,
.freq_hz = freq, .freq_hz = freq,
.clk_cfg = LEDC_AUTO_CLK .clk_cfg = LEDC_AUTO_CLK};
};
ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer));
@@ -101,8 +104,7 @@ void LEDManager::setup() {
.intr_type = LEDC_INTR_DISABLE, .intr_type = LEDC_INTR_DISABLE,
.timer_sel = LEDC_TIMER_0, .timer_sel = LEDC_TIMER_0,
.duty = dutyCycle, .duty = dutyCycle,
.hpoint = 0 .hpoint = 0};
};
ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel));
#endif #endif
@@ -110,37 +112,46 @@ void LEDManager::setup() {
ESP_LOGD(LED_MANAGER_TAG, "Done."); ESP_LOGD(LED_MANAGER_TAG, "Done.");
} }
void LEDManager::handleLED() { void LEDManager::handleLED()
if (!this->finishedPattern) { {
if (!this->finishedPattern)
{
displayCurrentPattern(); displayCurrentPattern();
return; return;
} }
if (xQueueReceive(this->ledStateQueue, &buffer, 10)) { if (xQueueReceive(this->ledStateQueue, &buffer, 10))
{
this->updateState(buffer); this->updateState(buffer);
} else { }
else
{
// we've finished displaying the pattern, so let's check if it's repeatable and if so - reset it // we've finished displaying the pattern, so let's check if it's repeatable and if so - reset it
if (ledStateMap[this->currentState].isRepeatable || ledStateMap[this->currentState].isError) { if (ledStateMap[this->currentState].isRepeatable || ledStateMap[this->currentState].isError)
{
this->currentPatternIndex = 0; this->currentPatternIndex = 0;
this->finishedPattern = false; this->finishedPattern = false;
} }
} }
} }
void LEDManager::displayCurrentPattern() { void LEDManager::displayCurrentPattern()
{
auto [state, delayTime] = ledStateMap[this->currentState].patterns[this->currentPatternIndex]; auto [state, delayTime] = ledStateMap[this->currentState].patterns[this->currentPatternIndex];
this->toggleLED(state); this->toggleLED(state);
this->timeToDelayFor = delayTime; this->timeToDelayFor = delayTime;
if (this->currentPatternIndex < ledStateMap[this->currentState].patterns.size() - 1) if (this->currentPatternIndex < ledStateMap[this->currentState].patterns.size() - 1)
this->currentPatternIndex++; this->currentPatternIndex++;
else { else
{
this->finishedPattern = true; this->finishedPattern = true;
this->toggleLED(LED_OFF); this->toggleLED(LED_OFF);
} }
} }
void LEDManager::updateState(const LEDStates_e newState) { void LEDManager::updateState(const LEDStates_e newState)
{
// we should change the displayed state // we should change the displayed state
// only if we finished displaying the current one - which is handled by the task // only if we finished displaying the current one - which is handled by the task
// if the new state is not the same as the current one // if the new state is not the same as the current one
@@ -153,21 +164,48 @@ void LEDManager::updateState(const LEDStates_e newState) {
if (newState == this->currentState) if (newState == this->currentState)
return; return;
if (ledStateMap.contains(newState)) { if (ledStateMap.contains(newState))
{
this->currentState = newState; this->currentState = newState;
this->currentPatternIndex = 0; this->currentPatternIndex = 0;
this->finishedPattern = false; this->finishedPattern = false;
} }
} }
void LEDManager::toggleLED(const bool state) const { void LEDManager::toggleLED(const bool state) const
{
gpio_set_level(blink_led_pin, state); gpio_set_level(blink_led_pin, state);
} }
void HandleLEDDisplayTask(void *pvParameter) { void LEDManager::setExternalLEDDutyCycle(uint8_t dutyPercent)
{
#ifdef CONFIG_LED_EXTERNAL_CONTROL
dutyPercent = std::min<uint8_t>(100, dutyPercent);
const uint32_t dutyCycle = (static_cast<uint32_t>(dutyPercent) * 255) / 100;
ESP_LOGI(LED_MANAGER_TAG, "Updating external LED duty to %u%% (raw %lu)", dutyPercent, dutyCycle);
// Persist into config immediately so it survives reboot
if (this->deviceConfig)
{
this->deviceConfig->setLEDDUtyCycleConfig(dutyPercent);
}
// Apply to LEDC hardware live
// We configured channel 0 in setup with LEDC_LOW_SPEED_MODE
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, dutyCycle));
ESP_ERROR_CHECK_WITHOUT_ABORT(ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0));
#else
(void)dutyPercent; // unused
ESP_LOGW(LED_MANAGER_TAG, "CONFIG_LED_EXTERNAL_CONTROL not enabled; ignoring duty update");
#endif
}
void HandleLEDDisplayTask(void *pvParameter)
{
auto *ledManager = static_cast<LEDManager *>(pvParameter); auto *ledManager = static_cast<LEDManager *>(pvParameter);
while (true) { while (true)
{
ledManager->handleLED(); ledManager->handleLED();
vTaskDelay(ledManager->getTimeToDelayFor()); vTaskDelay(ledManager->getTimeToDelayFor());
} }

View File

@@ -16,6 +16,7 @@
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <StateManager.hpp> #include <StateManager.hpp>
#include <ProjectConfig.hpp>
#include <helpers.hpp> #include <helpers.hpp>
// it kinda looks like different boards have these states swapped // it kinda looks like different boards have these states swapped
@@ -41,12 +42,16 @@ typedef std::unordered_map<LEDStates_e, LEDStage>
class LEDManager class LEDManager
{ {
public: public:
LEDManager(gpio_num_t blink_led_pin, gpio_num_t illumninator_led_pin, QueueHandle_t ledStateQueue); LEDManager(gpio_num_t blink_led_pin, gpio_num_t illumninator_led_pin, QueueHandle_t ledStateQueue, std::shared_ptr<ProjectConfig> deviceConfig);
void setup(); void setup();
void handleLED(); void handleLED();
size_t getTimeToDelayFor() const { return timeToDelayFor; } size_t getTimeToDelayFor() const { return timeToDelayFor; }
// Apply new external LED PWM duty cycle immediately (0-100)
void setExternalLEDDutyCycle(uint8_t dutyPercent);
uint8_t getExternalLEDDutyCycle() const { return deviceConfig ? deviceConfig->getDeviceConfig().led_external_pwm_duty_cycle : 0; }
private: private:
void toggleLED(bool state) const; void toggleLED(bool state) const;
void displayCurrentPattern(); void displayCurrentPattern();
@@ -60,6 +65,7 @@ private:
LEDStates_e buffer; LEDStates_e buffer;
LEDStates_e currentState; LEDStates_e currentState;
std::shared_ptr<ProjectConfig> deviceConfig;
size_t currentPatternIndex = 0; size_t currentPatternIndex = 0;
size_t timeToDelayFor = 100; size_t timeToDelayFor = 100;

View File

@@ -21,35 +21,39 @@ struct BaseConfigModel
Preferences *pref; Preferences *pref;
}; };
enum class StreamingMode { enum class StreamingMode
{
AUTO, AUTO,
UVC, UVC,
WIFI, WIFI,
}; };
struct DeviceMode_t : BaseConfigModel { struct DeviceMode_t : BaseConfigModel
{
StreamingMode mode; StreamingMode mode;
explicit DeviceMode_t( Preferences *pref) : BaseConfigModel(pref), mode(StreamingMode::AUTO){} explicit DeviceMode_t(Preferences *pref) : BaseConfigModel(pref), mode(StreamingMode::AUTO) {}
void load() { void load()
int stored_mode = this->pref->getInt("mode", 1); {
int stored_mode = this->pref->getInt("mode", 0);
this->mode = static_cast<StreamingMode>(stored_mode); this->mode = static_cast<StreamingMode>(stored_mode);
ESP_LOGI("DeviceMode", "Loaded device mode: %d", stored_mode); ESP_LOGI("DeviceMode", "Loaded device mode: %d", stored_mode);
} }
void save() const { void save() const
{
this->pref->putInt("mode", static_cast<int>(this->mode)); this->pref->putInt("mode", static_cast<int>(this->mode));
ESP_LOGI("DeviceMode", "Saved device mode: %d", static_cast<int>(this->mode)); ESP_LOGI("DeviceMode", "Saved device mode: %d", static_cast<int>(this->mode));
} }
}; };
struct DeviceConfig_t : BaseConfigModel struct DeviceConfig_t : BaseConfigModel
{ {
DeviceConfig_t(Preferences *pref) : BaseConfigModel(pref) {} DeviceConfig_t(Preferences *pref) : BaseConfigModel(pref) {}
std::string OTALogin; std::string OTALogin;
std::string OTAPassword; std::string OTAPassword;
int led_external_pwm_duty_cycle;
int OTAPort; int OTAPort;
void load() void load()
@@ -57,20 +61,23 @@ struct DeviceConfig_t : BaseConfigModel
this->OTALogin = this->pref->getString("OTALogin", "openiris"); this->OTALogin = this->pref->getString("OTALogin", "openiris");
this->OTAPassword = this->pref->getString("OTAPassword", "openiris"); this->OTAPassword = this->pref->getString("OTAPassword", "openiris");
this->OTAPort = this->pref->getInt("OTAPort", 3232); this->OTAPort = this->pref->getInt("OTAPort", 3232);
this->led_external_pwm_duty_cycle = this->pref->getInt("led_ext_pwm", CONFIG_LED_EXTERNAL_PWM_DUTY_CYCLE);
}; };
void save() const { void save() const
{
this->pref->putString("OTALogin", this->OTALogin.c_str()); this->pref->putString("OTALogin", this->OTALogin.c_str());
this->pref->putString("OTAPassword", this->OTAPassword.c_str()); this->pref->putString("OTAPassword", this->OTAPassword.c_str());
this->pref->putInt("OTAPort", this->OTAPort); this->pref->putInt("OTAPort", this->OTAPort);
this->pref->putInt("led_ext_pwm", this->led_external_pwm_duty_cycle);
}; };
std::string toRepresentation() const std::string toRepresentation() const
{ {
return Helpers::format_string( return Helpers::format_string(
"\"device_config\": {\"OTALogin\": \"%s\", \"OTAPassword\": \"%s\", " "\"device_config\": {\"OTALogin\": \"%s\", \"OTAPassword\": \"%s\", "
"\"OTAPort\": %u}", "\"OTAPort\": %u, \"led_external_pwm_duty_cycle\": %u}",
this->OTALogin.c_str(), this->OTAPassword.c_str(), this->OTAPort); this->OTALogin.c_str(), this->OTAPassword.c_str(), this->OTAPort, this->led_external_pwm_duty_cycle);
}; };
}; };
@@ -94,7 +101,8 @@ struct MDNSConfig_t : BaseConfigModel
this->hostname = this->pref->getString("hostname", default_hostname); this->hostname = this->pref->getString("hostname", default_hostname);
}; };
void save() const { void save() const
{
this->pref->putString("hostname", this->hostname.c_str()); this->pref->putString("hostname", this->hostname.c_str());
}; };
@@ -125,7 +133,8 @@ struct CameraConfig_t : BaseConfigModel
this->brightness = this->pref->getInt("brightness", 2); this->brightness = this->pref->getInt("brightness", 2);
}; };
void save() const { void save() const
{
this->pref->putInt("vflip", this->vflip); this->pref->putInt("vflip", this->vflip);
this->pref->putInt("href", this->href); this->pref->putInt("href", this->href);
this->pref->putInt("framesize", this->framesize); this->pref->putInt("framesize", this->framesize);
@@ -191,7 +200,8 @@ struct WiFiConfig_t : BaseConfigModel
index, this->name.c_str(), this->ssid.c_str(), this->channel); index, this->name.c_str(), this->ssid.c_str(), this->channel);
}; };
void save() const { void save() const
{
char buffer[2]; char buffer[2];
auto const iter_str = std::string(Helpers::itoa(this->index, buffer, 10)); auto const iter_str = std::string(Helpers::itoa(this->index, buffer, 10));
@@ -228,7 +238,8 @@ struct AP_WiFiConfig_t : BaseConfigModel
this->password = this->pref->getString("apPassword", CONFIG_WIFI_AP_PASSWORD); this->password = this->pref->getString("apPassword", CONFIG_WIFI_AP_PASSWORD);
}; };
void save() const { void save() const
{
this->pref->putString("apSSID", this->ssid.c_str()); this->pref->putString("apSSID", this->ssid.c_str());
this->pref->putString("apPass", this->password.c_str()); this->pref->putString("apPass", this->password.c_str());
this->pref->putUInt("apChannel", this->channel); this->pref->putUInt("apChannel", this->channel);
@@ -254,7 +265,8 @@ struct WiFiTxPower_t : BaseConfigModel
this->power = this->pref->getUInt("txpower", 52); this->power = this->pref->getUInt("txpower", 52);
}; };
void save() const { void save() const
{
this->pref->putUInt("txpower", this->power); this->pref->putUInt("txpower", this->power);
}; };

View File

@@ -24,7 +24,8 @@ ProjectConfig::ProjectConfig(Preferences *pref) : pref(pref),
ProjectConfig::~ProjectConfig() = default; ProjectConfig::~ProjectConfig() = default;
void ProjectConfig::save() const { void ProjectConfig::save() const
{
ESP_LOGD(CONFIGURATION_TAG, "Saving project config"); ESP_LOGD(CONFIGURATION_TAG, "Saving project config");
this->config.device.save(); this->config.device.save();
this->config.device_mode.save(); this->config.device_mode.save();
@@ -92,7 +93,7 @@ bool ProjectConfig::reset()
//! DeviceConfig //! DeviceConfig
//* //*
//********************************************************************************************************************** //**********************************************************************************************************************
void ProjectConfig::setDeviceConfig(const std::string &OTALogin, void ProjectConfig::setOTAConfig(const std::string &OTALogin,
const std::string &OTAPassword, const std::string &OTAPassword,
const int OTAPort) const int OTAPort)
{ {
@@ -100,6 +101,14 @@ void ProjectConfig::setDeviceConfig(const std::string &OTALogin,
this->config.device.OTALogin.assign(OTALogin); this->config.device.OTALogin.assign(OTALogin);
this->config.device.OTAPassword.assign(OTAPassword); this->config.device.OTAPassword.assign(OTAPassword);
this->config.device.OTAPort = OTAPort; this->config.device.OTAPort = OTAPort;
this->config.device.save();
}
void ProjectConfig::setLEDDUtyCycleConfig(int led_external_pwm_duty_cycle)
{
this->config.device.led_external_pwm_duty_cycle = led_external_pwm_duty_cycle;
ESP_LOGI(CONFIGURATION_TAG, "Setting duty cycle to %d", led_external_pwm_duty_cycle);
this->config.device.save();
} }
void ProjectConfig::setMDNSConfig(const std::string &hostname) void ProjectConfig::setMDNSConfig(const std::string &hostname)
@@ -120,6 +129,7 @@ void ProjectConfig::setCameraConfig(const uint8_t vflip,
this->config.camera.framesize = framesize; this->config.camera.framesize = framesize;
this->config.camera.quality = quality; this->config.camera.quality = quality;
this->config.camera.brightness = brightness; this->config.camera.brightness = brightness;
this->config.camera.save();
ESP_LOGD(CONFIGURATION_TAG, "Updating Camera config"); ESP_LOGD(CONFIGURATION_TAG, "Updating Camera config");
} }
@@ -205,6 +215,7 @@ void ProjectConfig::deleteWifiConfig(const std::string &networkName)
void ProjectConfig::setWiFiTxPower(uint8_t power) void ProjectConfig::setWiFiTxPower(uint8_t power)
{ {
this->config.txpower.power = power; this->config.txpower.power = power;
this->config.txpower.save();
ESP_LOGD(CONFIGURATION_TAG, "Updating wifi tx power"); ESP_LOGD(CONFIGURATION_TAG, "Updating wifi tx power");
} }
@@ -215,10 +226,12 @@ void ProjectConfig::setAPWifiConfig(const std::string &ssid,
this->config.ap_network.ssid.assign(ssid); this->config.ap_network.ssid.assign(ssid);
this->config.ap_network.password.assign(password); this->config.ap_network.password.assign(password);
this->config.ap_network.channel = channel; this->config.ap_network.channel = channel;
this->config.ap_network.save();
ESP_LOGD(CONFIGURATION_TAG, "Updating access point config"); ESP_LOGD(CONFIGURATION_TAG, "Updating access point config");
} }
void ProjectConfig::setDeviceMode(const StreamingMode deviceMode) { void ProjectConfig::setDeviceMode(const StreamingMode deviceMode)
{
this->config.device_mode.mode = deviceMode; this->config.device_mode.mode = deviceMode;
this->config.device_mode.save(); // Save immediately this->config.device_mode.save(); // Save immediately
} }
@@ -258,10 +271,12 @@ TrackerConfig_t &ProjectConfig::getTrackerConfig()
return this->config; return this->config;
} }
DeviceMode_t &ProjectConfig::getDeviceModeConfig() { DeviceMode_t &ProjectConfig::getDeviceModeConfig()
{
return this->config.device_mode; return this->config.device_mode;
} }
StreamingMode ProjectConfig::getDeviceMode() { StreamingMode ProjectConfig::getDeviceMode()
{
return this->config.device_mode.mode; return this->config.device_mode.mode;
} }

View File

@@ -22,11 +22,6 @@ public:
void load(); void load();
void save() const; void save() const;
void wifiConfigSave();
void cameraConfigSave();
void deviceConfigSave();
void mdnsConfigSave();
void wifiTxPowerConfigSave();
bool reset(); bool reset();
DeviceConfig_t &getDeviceConfig(); DeviceConfig_t &getDeviceConfig();
@@ -38,9 +33,10 @@ public:
WiFiTxPower_t &getWiFiTxPowerConfig(); WiFiTxPower_t &getWiFiTxPowerConfig();
TrackerConfig_t &getTrackerConfig(); TrackerConfig_t &getTrackerConfig();
void setDeviceConfig(const std::string &OTALogin, void setOTAConfig(const std::string &OTALogin,
const std::string &OTAPassword, const std::string &OTAPassword,
int OTAPort); int OTAPort);
void setLEDDUtyCycleConfig(int led_external_pwm_duty_cycle);
void setMDNSConfig(const std::string &hostname); void setMDNSConfig(const std::string &hostname);
void setCameraConfig(uint8_t vflip, void setCameraConfig(uint8_t vflip,
uint8_t framesize, uint8_t framesize,

View File

@@ -53,7 +53,7 @@ auto *restAPI = new RestAPI("http://0.0.0.0:81", commandManager);
UVCStreamManager uvcStream; UVCStreamManager uvcStream;
#endif #endif
auto *ledManager = new LEDManager(BLINK_GPIO, CONFIG_LED_C_PIN_GPIO, ledStateQueue); auto *ledManager = new LEDManager(BLINK_GPIO, CONFIG_LED_C_PIN_GPIO, ledStateQueue, deviceConfig);
auto *serialManager = new SerialManager(commandManager, &timerHandle, deviceConfig); auto *serialManager = new SerialManager(commandManager, &timerHandle, deviceConfig);
static void initNVSStorage() static void initNVSStorage()
@@ -228,6 +228,7 @@ extern "C" void app_main(void)
dependencyRegistry->registerService<ProjectConfig>(DependencyType::project_config, deviceConfig); dependencyRegistry->registerService<ProjectConfig>(DependencyType::project_config, deviceConfig);
dependencyRegistry->registerService<CameraManager>(DependencyType::camera_manager, cameraHandler); dependencyRegistry->registerService<CameraManager>(DependencyType::camera_manager, cameraHandler);
dependencyRegistry->registerService<WiFiManager>(DependencyType::wifi_manager, wifiManager); dependencyRegistry->registerService<WiFiManager>(DependencyType::wifi_manager, wifiManager);
dependencyRegistry->registerService<LEDManager>(DependencyType::led_manager, std::shared_ptr<LEDManager>(ledManager, [](LEDManager*){}));
// uvc plan // uvc plan
// cleanup the logs - done // cleanup the logs - done
// prepare the camera to be initialized with UVC - done? // prepare the camera to be initialized with UVC - done?
@@ -274,10 +275,10 @@ extern "C" void app_main(void)
// setup CI and building for other boards // setup CI and building for other boards
// finish todos, overhaul stuff a bit // finish todos, overhaul stuff a bit
// esp_log_set_vprintf(&websocket_logger);
Logo::printASCII(); Logo::printASCII();
initNVSStorage(); initNVSStorage();
deviceConfig->load();
// esp_log_set_vprintf(&websocket_logger);
ledManager->setup(); ledManager->setup();
xTaskCreate( xTaskCreate(
@@ -297,7 +298,6 @@ extern "C" void app_main(void)
3, 3,
nullptr); nullptr);
deviceConfig->load();
serialManager->setup(); serialManager->setup();
static TaskHandle_t serialManagerHandle = nullptr; static TaskHandle_t serialManagerHandle = nullptr;
@@ -308,8 +308,7 @@ extern "C" void app_main(void)
1024 * 6, 1024 * 6,
serialManager, serialManager,
1, // we only rely on the serial manager during provisioning, we can run it slower 1, // we only rely on the serial manager during provisioning, we can run it slower
&serialManagerHandle &serialManagerHandle);
);
wifiManager->Begin(); wifiManager->Begin();
mdnsManager.start(); mdnsManager.start();

View File

@@ -50,5 +50,5 @@ CONFIG_LED_EXTERNAL_CONTROL=y
CONFIG_LED_EXTERNAL_PWM_FREQ=5000 CONFIG_LED_EXTERNAL_PWM_FREQ=5000
CONFIG_LED_EXTERNAL_PWM_DUTY_CYCLE=100 CONFIG_LED_EXTERNAL_PWM_DUTY_CYCLE=100
CONFIG_LED_EXTERNAL_GPIO=1 CONFIG_LED_EXTERNAL_GPIO=1
CONFIG_CAMERA_USB_XCLK_FREQ=23000000 # NOT TESTED CONFIG_CAMERA_USB_XCLK_FREQ=23000000
CONFIG_GENERAL_WIRED_MODE=y CONFIG_GENERAL_WIRED_MODE=y

View File

@@ -410,6 +410,35 @@ class OpenIrisDevice:
print(f"❌ Failed to parse mode response: {e}") print(f"❌ Failed to parse mode response: {e}")
return "unknown" return "unknown"
def set_led_duty_cycle(self, duty_cycle):
"""Sets the PWN duty cycle of the LED"""
print(f"🌟 Setting LED duty cycle to {duty_cycle}%...")
response = self.send_command("set_led_duty_cycle", {"dutyCycle": duty_cycle})
if "error" in response:
print(f"❌ Failed to set LED duty cycle: {response['error']}")
return False
print("✅ LED duty cycle set successfully")
return True
def get_led_duty_cycle(self) -> Optional[int]:
"""Get the current LED PWM duty cycle from the device"""
response = self.send_command("get_led_duty_cycle")
if "error" in response:
print(f"❌ Failed to get LED duty cycle: {response['error']}")
return None
try:
results = response.get("results", [])
if results:
result_data = json.loads(results[0])
payload = result_data["result"]
if isinstance(payload, str):
payload = json.loads(payload)
return int(payload.get("led_external_pwm_duty_cycle"))
except Exception as e:
print(f"❌ Failed to parse LED duty cycle: {e}")
return None
def monitor_logs(self): def monitor_logs(self):
"""Monitor device logs until interrupted""" """Monitor device logs until interrupted"""
print("📋 Monitoring device logs (Press Ctrl+C to exit)...") print("📋 Monitoring device logs (Press Ctrl+C to exit)...")
@@ -736,10 +765,34 @@ def switch_device_mode(device: OpenIrisDevice, args = None):
print("❌ Invalid mode selection") print("❌ Invalid mode selection")
def set_led_duty_cycle(device: OpenIrisDevice, args=None):
while True:
input_data = input("Enter LED external PWM duty cycle (0-100) or `back` to exit: \n")
if input_data.lower() == "back":
break
try:
duty_cycle = int(input_data)
except ValueError:
print("❌ Invalid input. Please enter a number between 0 and 100.")
if duty_cycle < 0 or duty_cycle > 100:
print("❌ Duty cycle must be between 0 and 100.")
else:
# Apply immediately; stay in loop for further tweaks
device.set_led_duty_cycle(duty_cycle)
def monitor_logs(device: OpenIrisDevice, args = None): def monitor_logs(device: OpenIrisDevice, args = None):
device.monitor_logs() device.monitor_logs()
def get_led_duty_cycle(device: OpenIrisDevice, args=None):
duty = device.get_led_duty_cycle()
if duty is not None:
print(f"💡 Current LED duty cycle: {duty}%")
COMMANDS_MAP = { COMMANDS_MAP = {
"1": scan_networks, "1": scan_networks,
"2": display_networks, "2": display_networks,
@@ -750,7 +803,9 @@ COMMANDS_MAP = {
"7": attempt_wifi_connection, "7": attempt_wifi_connection,
"8": start_streaming, "8": start_streaming,
"9": switch_device_mode, "9": switch_device_mode,
"10": monitor_logs, "10": set_led_duty_cycle,
"11": get_led_duty_cycle,
"12": monitor_logs,
} }
@@ -848,9 +903,11 @@ def main():
print("7. 🔗 Connect to WiFi") print("7. 🔗 Connect to WiFi")
print("8. 🚀 Start streaming mode") print("8. 🚀 Start streaming mode")
print("9. 🔄 Switch device mode (WiFi/UVC/Auto)") print("9. 🔄 Switch device mode (WiFi/UVC/Auto)")
print("10. 📋 Monitor logs") print("10. 💡 Update PWM Duty Cycle")
print("11. 💡Get PWM Duty Cycle")
print("12. 📖 Monitor logs")
print("exit. 🚪 Exit") print("exit. 🚪 Exit")
choice = input("\nSelect option (1-10): ").strip() choice = input("\nSelect option (1-12): ").strip()
if choice == "exit": if choice == "exit":
break break