diff --git a/components/CommandManager/CMakeLists.txt b/components/CommandManager/CMakeLists.txt index 16e8afc..e4b61ca 100644 --- a/components/CommandManager/CMakeLists.txt +++ b/components/CommandManager/CMakeLists.txt @@ -11,5 +11,5 @@ idf_component_register( INCLUDE_DIRS "CommandManager" "CommandManager/commands" - REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks wifiManager Helpers + REQUIRES ProjectConfig cJSON CameraManager OpenIrisTasks wifiManager Helpers LEDManager ) \ No newline at end of file diff --git a/components/CommandManager/CommandManager/CommandManager.cpp b/components/CommandManager/CommandManager/CommandManager.cpp index 7932a1d..151b08e 100644 --- a/components/CommandManager/CommandManager/CommandManager.cpp +++ b/components/CommandManager/CommandManager/CommandManager.cpp @@ -23,53 +23,80 @@ std::unordered_map commandTypeMap = { {"connect_wifi", CommandType::CONNECT_WIFI}, {"switch_mode", CommandType::SWITCH_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 CommandManager::createCommand(const CommandType type, std::string_view json) const { +std::function CommandManager::createCommand(const CommandType type, std::string_view json) const +{ switch (type) { case CommandType::PING: - return { PingCommand }; + return {PingCommand}; case CommandType::PAUSE: - return [json] { return PauseCommand(json); }; + return [json] + { return PauseCommand(json); }; case CommandType::SET_STREAMING_MODE: - return [this, json] {return setDeviceModeCommand(this->registry, json); }; + return [this, json] + { return setDeviceModeCommand(this->registry, json); }; case CommandType::UPDATE_OTA_CREDENTIALS: - return [this, json] { return updateOTACredentialsCommand(this->registry, json); }; + return [this, json] + { return updateOTACredentialsCommand(this->registry, json); }; case CommandType::SET_WIFI: - return [this, json] { return setWiFiCommand(this->registry, json); }; + return [this, json] + { return setWiFiCommand(this->registry, json); }; case CommandType::UPDATE_WIFI: - return [this, json] { return updateWiFiCommand(this->registry, json); }; + return [this, json] + { return updateWiFiCommand(this->registry, json); }; case CommandType::UPDATE_AP_WIFI: - return [this, json] { return updateAPWiFiCommand(this->registry, json); }; + return [this, json] + { return updateAPWiFiCommand(this->registry, json); }; case CommandType::DELETE_NETWORK: - return [this, json] { return deleteWiFiCommand(this->registry, json); }; + return [this, json] + { return deleteWiFiCommand(this->registry, json); }; case CommandType::SET_MDNS: - return [this, json] { return setMDNSCommand(this->registry, json); }; + return [this, json] + { return setMDNSCommand(this->registry, json); }; case CommandType::UPDATE_CAMERA: - return [this, json] { return updateCameraCommand(this->registry, json); }; + return [this, json] + { return updateCameraCommand(this->registry, json); }; case CommandType::RESTART_CAMERA: - return [this, json] { return restartCameraCommand(this->registry, json); }; + return [this, json] + { return restartCameraCommand(this->registry, json); }; case CommandType::GET_CONFIG: - return [this] { return getConfigCommand(this->registry); }; + return [this] + { return getConfigCommand(this->registry); }; case CommandType::SAVE_CONFIG: - return [this] { return saveConfigCommand(this->registry); }; + return [this] + { return saveConfigCommand(this->registry); }; case CommandType::RESET_CONFIG: - return [this, json] { return resetConfigCommand(this->registry, json); }; + return [this, json] + { return resetConfigCommand(this->registry, json); }; case CommandType::RESTART_DEVICE: return restartDeviceCommand; case CommandType::SCAN_NETWORKS: - return [this] { return scanNetworksCommand(this->registry); }; + return [this] + { return scanNetworksCommand(this->registry); }; case CommandType::START_STREAMING: return startStreamingCommand; case CommandType::GET_WIFI_STATUS: - return [this] { return getWiFiStatusCommand(this->registry); }; + return [this] + { return getWiFiStatusCommand(this->registry); }; case CommandType::CONNECT_WIFI: - return [this] { return connectWiFiCommand(this->registry); }; + return [this] + { return connectWiFiCommand(this->registry); }; case CommandType::SWITCH_MODE: - return [this, json] { return switchModeCommand(this->registry, json); }; + return [this, json] + { return switchModeCommand(this->registry, json); }; case CommandType::GET_DEVICE_MODE: - return [this] { return getDeviceModeCommand(this->registry); }; + return [this] + { return getDeviceModeCommand(this->registry); }; + 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: return nullptr; } diff --git a/components/CommandManager/CommandManager/CommandManager.hpp b/components/CommandManager/CommandManager/CommandManager.hpp index 02231e3..ab9aa5b 100644 --- a/components/CommandManager/CommandManager/CommandManager.hpp +++ b/components/CommandManager/CommandManager/CommandManager.hpp @@ -44,6 +44,8 @@ enum class CommandType CONNECT_WIFI, SWITCH_MODE, GET_DEVICE_MODE, + SET_LED_DUTY_CYCLE, + GET_LED_DUTY_CYCLE, }; class CommandManager diff --git a/components/CommandManager/CommandManager/DependencyRegistry.hpp b/components/CommandManager/CommandManager/DependencyRegistry.hpp index 48b6ee8..b0758dd 100644 --- a/components/CommandManager/CommandManager/DependencyRegistry.hpp +++ b/components/CommandManager/CommandManager/DependencyRegistry.hpp @@ -8,7 +8,8 @@ enum class DependencyType { project_config, camera_manager, - wifi_manager + wifi_manager, + led_manager }; class DependencyRegistry diff --git a/components/CommandManager/CommandManager/commands/device_commands.cpp b/components/CommandManager/CommandManager/commands/device_commands.cpp index 2ffc4dd..8accbbd 100644 --- a/components/CommandManager/CommandManager/commands/device_commands.cpp +++ b/components/CommandManager/CommandManager/commands/device_commands.cpp @@ -1,4 +1,5 @@ #include "device_commands.hpp" +#include "LEDManager.hpp" // Implementation inspired by SummerSigh work, initial PR opened in openiris repo, adapted to this rewrite CommandResult setDeviceModeCommand(std::shared_ptr registry, std::string_view jsonPayload) @@ -25,6 +26,8 @@ CommandResult setDeviceModeCommand(std::shared_ptr registry, const auto projectConfig = registry->resolve(DependencyType::project_config); projectConfig->setDeviceMode(static_cast(mode)); + cJSON_Delete(parsedJson); + return CommandResult::getSuccessResult("Device mode set"); } @@ -64,16 +67,63 @@ CommandResult updateOTACredentialsCommand(std::shared_ptr re } } - projectConfig->setDeviceConfig(OTALogin, OTAPassword, OTAPort); + cJSON_Delete(parsedJson); + + projectConfig->setOTAConfig(OTALogin, OTAPassword, OTAPort); return CommandResult::getSuccessResult("OTA Config set"); } +CommandResult updateLEDDutyCycleCommand(std::shared_ptr 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(DependencyType::project_config); + projectConfig->setLEDDUtyCycleConfig(dutyCycle); + + // Try to apply the change live via LEDManager if available + auto ledMgr = registry->resolve(DependencyType::led_manager); + if (ledMgr) + { + ledMgr->setExternalLEDDutyCycle(static_cast(dutyCycle)); + } + + cJSON_Delete(parsedJson); + + return CommandResult::getSuccessResult("LED duty cycle set"); +} + CommandResult restartDeviceCommand() { OpenIrisTasks::ScheduleRestart(2000); return CommandResult::getSuccessResult("Device restarted"); } +CommandResult getLEDDutyCycleCommand(std::shared_ptr registry) +{ + const auto projectConfig = registry->resolve(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() { activateStreaming(false); // Don't disable setup interfaces by default diff --git a/components/CommandManager/CommandManager/commands/device_commands.hpp b/components/CommandManager/CommandManager/commands/device_commands.hpp index 4eb1b3d..9e89db4 100644 --- a/components/CommandManager/CommandManager/commands/device_commands.hpp +++ b/components/CommandManager/CommandManager/commands/device_commands.hpp @@ -13,6 +13,9 @@ CommandResult setDeviceModeCommand(std::shared_ptr registry, CommandResult updateOTACredentialsCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult updateLEDDutyCycleCommand(std::shared_ptr registry, std::string_view jsonPayload); +CommandResult getLEDDutyCycleCommand(std::shared_ptr registry); + CommandResult restartDeviceCommand(); CommandResult startStreamingCommand(); diff --git a/components/LEDManager/CMakeLists.txt b/components/LEDManager/CMakeLists.txt index b377b09..4466f41 100644 --- a/components/LEDManager/CMakeLists.txt +++ b/components/LEDManager/CMakeLists.txt @@ -1,4 +1,4 @@ idf_component_register(SRCS "LEDManager/LEDManager.cpp" INCLUDE_DIRS "LEDManager" - REQUIRES StateManager driver esp_driver_ledc Helpers + REQUIRES StateManager driver esp_driver_ledc Helpers ProjectConfig ) \ No newline at end of file diff --git a/components/LEDManager/LEDManager/LEDManager.cpp b/components/LEDManager/LEDManager/LEDManager.cpp index 97ad6e0..8ba2460 100644 --- a/components/LEDManager/LEDManager/LEDManager.cpp +++ b/components/LEDManager/LEDManager/LEDManager.cpp @@ -48,10 +48,7 @@ ledStateMap_t LEDManager::ledStateMap = { { 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, - QueueHandle_t ledStateQueue) : blink_led_pin(pin), - illumninator_led_pin(illumninator_led_pin), - ledStateQueue(ledStateQueue), - currentState(LEDStates_e::LedStateNone) { + QueueHandle_t ledStateQueue, std::shared_ptr deviceConfig) : blink_led_pin(pin), + illumninator_led_pin(illumninator_led_pin), + ledStateQueue(ledStateQueue), + currentState(LEDStates_e::LedStateNone), + deviceConfig(deviceConfig) +{ } -void LEDManager::setup() { - ESP_LOGD(LED_MANAGER_TAG, "Setting up status led."); +void LEDManager::setup() +{ + ESP_LOGI(LED_MANAGER_TAG, "Setting up status led."); gpio_reset_pin(blink_led_pin); /* Set the GPIO as a push/pull output */ gpio_set_direction(blink_led_pin, GPIO_MODE_OUTPUT); this->toggleLED(LED_OFF); #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 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 = { .speed_mode = LEDC_LOW_SPEED_MODE, .duty_resolution = resolution, .timer_num = LEDC_TIMER_0, .freq_hz = freq, - .clk_cfg = LEDC_AUTO_CLK - }; + .clk_cfg = LEDC_AUTO_CLK}; ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); @@ -101,8 +104,7 @@ void LEDManager::setup() { .intr_type = LEDC_INTR_DISABLE, .timer_sel = LEDC_TIMER_0, .duty = dutyCycle, - .hpoint = 0 - }; + .hpoint = 0}; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); #endif @@ -110,37 +112,46 @@ void LEDManager::setup() { ESP_LOGD(LED_MANAGER_TAG, "Done."); } -void LEDManager::handleLED() { - if (!this->finishedPattern) { +void LEDManager::handleLED() +{ + if (!this->finishedPattern) + { displayCurrentPattern(); return; } - if (xQueueReceive(this->ledStateQueue, &buffer, 10)) { + if (xQueueReceive(this->ledStateQueue, &buffer, 10)) + { this->updateState(buffer); - } else { + } + else + { // 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->finishedPattern = false; } } } -void LEDManager::displayCurrentPattern() { +void LEDManager::displayCurrentPattern() +{ auto [state, delayTime] = ledStateMap[this->currentState].patterns[this->currentPatternIndex]; this->toggleLED(state); this->timeToDelayFor = delayTime; if (this->currentPatternIndex < ledStateMap[this->currentState].patterns.size() - 1) this->currentPatternIndex++; - else { + else + { this->finishedPattern = true; this->toggleLED(LED_OFF); } } -void LEDManager::updateState(const LEDStates_e newState) { +void LEDManager::updateState(const LEDStates_e newState) +{ // we should change the displayed state // 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 @@ -153,21 +164,48 @@ void LEDManager::updateState(const LEDStates_e newState) { if (newState == this->currentState) return; - if (ledStateMap.contains(newState)) { + if (ledStateMap.contains(newState)) + { this->currentState = newState; this->currentPatternIndex = 0; this->finishedPattern = false; } } -void LEDManager::toggleLED(const bool state) const { +void LEDManager::toggleLED(const bool state) const +{ 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(100, dutyPercent); + const uint32_t dutyCycle = (static_cast(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(pvParameter); - while (true) { + while (true) + { ledManager->handleLED(); vTaskDelay(ledManager->getTimeToDelayFor()); } diff --git a/components/LEDManager/LEDManager/LEDManager.hpp b/components/LEDManager/LEDManager/LEDManager.hpp index af92387..8f0838f 100644 --- a/components/LEDManager/LEDManager/LEDManager.hpp +++ b/components/LEDManager/LEDManager/LEDManager.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include // it kinda looks like different boards have these states swapped @@ -41,12 +42,16 @@ typedef std::unordered_map class LEDManager { 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 deviceConfig); void setup(); void handleLED(); 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: void toggleLED(bool state) const; void displayCurrentPattern(); @@ -60,6 +65,7 @@ private: LEDStates_e buffer; LEDStates_e currentState; + std::shared_ptr deviceConfig; size_t currentPatternIndex = 0; size_t timeToDelayFor = 100; diff --git a/components/ProjectConfig/ProjectConfig/Models.hpp b/components/ProjectConfig/ProjectConfig/Models.hpp index 2cbf9df..4326344 100644 --- a/components/ProjectConfig/ProjectConfig/Models.hpp +++ b/components/ProjectConfig/ProjectConfig/Models.hpp @@ -21,35 +21,39 @@ struct BaseConfigModel Preferences *pref; }; -enum class StreamingMode { +enum class StreamingMode +{ AUTO, UVC, WIFI, }; -struct DeviceMode_t : BaseConfigModel { +struct DeviceMode_t : BaseConfigModel +{ StreamingMode mode; - explicit DeviceMode_t( Preferences *pref) : BaseConfigModel(pref), mode(StreamingMode::AUTO){} + explicit DeviceMode_t(Preferences *pref) : BaseConfigModel(pref), mode(StreamingMode::AUTO) {} - void load() { - int stored_mode = this->pref->getInt("mode", 1); + void load() + { + int stored_mode = this->pref->getInt("mode", 0); this->mode = static_cast(stored_mode); ESP_LOGI("DeviceMode", "Loaded device mode: %d", stored_mode); } - void save() const { + void save() const + { this->pref->putInt("mode", static_cast(this->mode)); ESP_LOGI("DeviceMode", "Saved device mode: %d", static_cast(this->mode)); } }; - struct DeviceConfig_t : BaseConfigModel { DeviceConfig_t(Preferences *pref) : BaseConfigModel(pref) {} std::string OTALogin; std::string OTAPassword; + int led_external_pwm_duty_cycle; int OTAPort; void load() @@ -57,20 +61,23 @@ struct DeviceConfig_t : BaseConfigModel this->OTALogin = this->pref->getString("OTALogin", "openiris"); this->OTAPassword = this->pref->getString("OTAPassword", "openiris"); 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("OTAPassword", this->OTAPassword.c_str()); this->pref->putInt("OTAPort", this->OTAPort); + this->pref->putInt("led_ext_pwm", this->led_external_pwm_duty_cycle); }; std::string toRepresentation() const { return Helpers::format_string( "\"device_config\": {\"OTALogin\": \"%s\", \"OTAPassword\": \"%s\", " - "\"OTAPort\": %u}", - this->OTALogin.c_str(), this->OTAPassword.c_str(), this->OTAPort); + "\"OTAPort\": %u, \"led_external_pwm_duty_cycle\": %u}", + 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); }; - void save() const { + void save() const + { this->pref->putString("hostname", this->hostname.c_str()); }; @@ -125,7 +133,8 @@ struct CameraConfig_t : BaseConfigModel this->brightness = this->pref->getInt("brightness", 2); }; - void save() const { + void save() const + { this->pref->putInt("vflip", this->vflip); this->pref->putInt("href", this->href); this->pref->putInt("framesize", this->framesize); @@ -186,12 +195,13 @@ 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", + + 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 { + void save() const + { char buffer[2]; auto const iter_str = std::string(Helpers::itoa(this->index, buffer, 10)); @@ -200,8 +210,8 @@ 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", + + ESP_LOGI("WiFiConfig", "Saved network %d: name=%s, ssid=%s, channel=%d", this->index, this->name.c_str(), this->ssid.c_str(), this->channel); }; @@ -228,7 +238,8 @@ struct AP_WiFiConfig_t : BaseConfigModel 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("apPass", this->password.c_str()); this->pref->putUInt("apChannel", this->channel); @@ -254,7 +265,8 @@ struct WiFiTxPower_t : BaseConfigModel this->power = this->pref->getUInt("txpower", 52); }; - void save() const { + void save() const + { this->pref->putUInt("txpower", this->power); }; diff --git a/components/ProjectConfig/ProjectConfig/ProjectConfig.cpp b/components/ProjectConfig/ProjectConfig/ProjectConfig.cpp index ae7b189..ca38622 100644 --- a/components/ProjectConfig/ProjectConfig/ProjectConfig.cpp +++ b/components/ProjectConfig/ProjectConfig/ProjectConfig.cpp @@ -24,7 +24,8 @@ ProjectConfig::ProjectConfig(Preferences *pref) : pref(pref), ProjectConfig::~ProjectConfig() = default; -void ProjectConfig::save() const { +void ProjectConfig::save() const +{ ESP_LOGD(CONFIGURATION_TAG, "Saving project config"); this->config.device.save(); this->config.device_mode.save(); @@ -92,14 +93,22 @@ bool ProjectConfig::reset() //! DeviceConfig //* //********************************************************************************************************************** -void ProjectConfig::setDeviceConfig(const std::string &OTALogin, - const std::string &OTAPassword, - const int OTAPort) +void ProjectConfig::setOTAConfig(const std::string &OTALogin, + const std::string &OTAPassword, + const int OTAPort) { ESP_LOGD(CONFIGURATION_TAG, "Updating device config"); this->config.device.OTALogin.assign(OTALogin); this->config.device.OTAPassword.assign(OTAPassword); 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) @@ -120,6 +129,7 @@ void ProjectConfig::setCameraConfig(const uint8_t vflip, this->config.camera.framesize = framesize; this->config.camera.quality = quality; this->config.camera.brightness = brightness; + this->config.camera.save(); ESP_LOGD(CONFIGURATION_TAG, "Updating Camera config"); } @@ -133,8 +143,8 @@ void ProjectConfig::setWifiConfig(const std::string &networkName, const auto size = this->config.networks.size(); const auto it = std::ranges::find_if(this->config.networks, - [&](const WiFiConfig_t &network) - { return network.name == networkName; }); + [&](const WiFiConfig_t &network) + { return network.name == networkName; }); if (it != this->config.networks.end()) { @@ -191,8 +201,8 @@ void ProjectConfig::deleteWifiConfig(const std::string &networkName) } const auto it = std::ranges::find_if(this->config.networks, - [&](const WiFiConfig_t &network) - { return network.name == networkName; }); + [&](const WiFiConfig_t &network) + { return network.name == networkName; }); if (it != this->config.networks.end()) { @@ -205,6 +215,7 @@ void ProjectConfig::deleteWifiConfig(const std::string &networkName) void ProjectConfig::setWiFiTxPower(uint8_t power) { this->config.txpower.power = power; + this->config.txpower.save(); ESP_LOGD(CONFIGURATION_TAG, "Updating wifi tx power"); } @@ -215,12 +226,14 @@ void ProjectConfig::setAPWifiConfig(const std::string &ssid, this->config.ap_network.ssid.assign(ssid); this->config.ap_network.password.assign(password); this->config.ap_network.channel = channel; + this->config.ap_network.save(); 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.save(); // Save immediately + this->config.device_mode.save(); // Save immediately } //********************************************************************************************************************** @@ -258,10 +271,12 @@ TrackerConfig_t &ProjectConfig::getTrackerConfig() return this->config; } -DeviceMode_t &ProjectConfig::getDeviceModeConfig() { +DeviceMode_t &ProjectConfig::getDeviceModeConfig() +{ return this->config.device_mode; } -StreamingMode ProjectConfig::getDeviceMode() { +StreamingMode ProjectConfig::getDeviceMode() +{ return this->config.device_mode.mode; } \ No newline at end of file diff --git a/components/ProjectConfig/ProjectConfig/ProjectConfig.hpp b/components/ProjectConfig/ProjectConfig/ProjectConfig.hpp index 045a8a6..07d04da 100644 --- a/components/ProjectConfig/ProjectConfig/ProjectConfig.hpp +++ b/components/ProjectConfig/ProjectConfig/ProjectConfig.hpp @@ -22,11 +22,6 @@ public: void load(); void save() const; - void wifiConfigSave(); - void cameraConfigSave(); - void deviceConfigSave(); - void mdnsConfigSave(); - void wifiTxPowerConfigSave(); bool reset(); DeviceConfig_t &getDeviceConfig(); @@ -38,9 +33,10 @@ public: WiFiTxPower_t &getWiFiTxPowerConfig(); TrackerConfig_t &getTrackerConfig(); - void setDeviceConfig(const std::string &OTALogin, - const std::string &OTAPassword, - int OTAPort); + void setOTAConfig(const std::string &OTALogin, + const std::string &OTAPassword, + int OTAPort); + void setLEDDUtyCycleConfig(int led_external_pwm_duty_cycle); void setMDNSConfig(const std::string &hostname); void setCameraConfig(uint8_t vflip, uint8_t framesize, diff --git a/main/openiris_main.cpp b/main/openiris_main.cpp index 8c908d6..33b171d 100644 --- a/main/openiris_main.cpp +++ b/main/openiris_main.cpp @@ -53,7 +53,7 @@ auto *restAPI = new RestAPI("http://0.0.0.0:81", commandManager); UVCStreamManager uvcStream; #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); static void initNVSStorage() @@ -228,6 +228,7 @@ extern "C" void app_main(void) dependencyRegistry->registerService(DependencyType::project_config, deviceConfig); dependencyRegistry->registerService(DependencyType::camera_manager, cameraHandler); dependencyRegistry->registerService(DependencyType::wifi_manager, wifiManager); + dependencyRegistry->registerService(DependencyType::led_manager, std::shared_ptr(ledManager, [](LEDManager*){})); // uvc plan // cleanup the logs - 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 // finish todos, overhaul stuff a bit + // esp_log_set_vprintf(&websocket_logger); Logo::printASCII(); initNVSStorage(); - - // esp_log_set_vprintf(&websocket_logger); + deviceConfig->load(); ledManager->setup(); xTaskCreate( @@ -297,7 +298,6 @@ extern "C" void app_main(void) 3, nullptr); - deviceConfig->load(); serialManager->setup(); static TaskHandle_t serialManagerHandle = nullptr; @@ -308,8 +308,7 @@ extern "C" void app_main(void) 1024 * 6, serialManager, 1, // we only rely on the serial manager during provisioning, we can run it slower - &serialManagerHandle - ); + &serialManagerHandle); wifiManager->Begin(); mdnsManager.start(); diff --git a/sdkconfig.board.project_babble b/sdkconfig.board.project_babble index 47b59a9..1ef150a 100644 --- a/sdkconfig.board.project_babble +++ b/sdkconfig.board.project_babble @@ -50,5 +50,5 @@ CONFIG_LED_EXTERNAL_CONTROL=y CONFIG_LED_EXTERNAL_PWM_FREQ=5000 CONFIG_LED_EXTERNAL_PWM_DUTY_CYCLE=100 CONFIG_LED_EXTERNAL_GPIO=1 -CONFIG_CAMERA_USB_XCLK_FREQ=23000000 # NOT TESTED +CONFIG_CAMERA_USB_XCLK_FREQ=23000000 CONFIG_GENERAL_WIRED_MODE=y \ No newline at end of file diff --git a/tools/openiris_setup.py b/tools/openiris_setup.py index d797918..8aa97f2 100644 --- a/tools/openiris_setup.py +++ b/tools/openiris_setup.py @@ -410,6 +410,35 @@ class OpenIrisDevice: print(f"❌ Failed to parse mode response: {e}") 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): """Monitor device logs until interrupted""" 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") +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): 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 = { "1": scan_networks, "2": display_networks, @@ -750,7 +803,9 @@ COMMANDS_MAP = { "7": attempt_wifi_connection, "8": start_streaming, "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("8. 🚀 Start streaming mode") 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") - choice = input("\nSelect option (1-10): ").strip() + choice = input("\nSelect option (1-12): ").strip() if choice == "exit": break