diff --git a/components/Helpers/CMakeLists.txt b/components/Helpers/CMakeLists.txt index 7bee02d..80ffdf3 100644 --- a/components/Helpers/CMakeLists.txt +++ b/components/Helpers/CMakeLists.txt @@ -1,3 +1,4 @@ idf_component_register(SRCS "Helpers/helpers.cpp" INCLUDE_DIRS "helpers" + REQUIRES esp_timer ) \ No newline at end of file diff --git a/components/Helpers/Helpers/helpers.cpp b/components/Helpers/Helpers/helpers.cpp index 5f6fe0a..264e1c6 100644 --- a/components/Helpers/Helpers/helpers.cpp +++ b/components/Helpers/Helpers/helpers.cpp @@ -77,4 +77,9 @@ std::vector Helpers::split(const std::string &s, char delimiter) parts.push_back(part); } return parts; +} + +int64_t Helpers::getTimeInMillis() +{ + return (esp_timer_get_time() / 1000); } \ No newline at end of file diff --git a/components/Helpers/Helpers/helpers.hpp b/components/Helpers/Helpers/helpers.hpp index 3f86007..28082c8 100644 --- a/components/Helpers/Helpers/helpers.hpp +++ b/components/Helpers/Helpers/helpers.hpp @@ -1,5 +1,7 @@ +#pragma once #ifndef HELPERS_HPP #define HELPERS_HPP +#include "esp_timer.h" #include #include #include @@ -31,6 +33,8 @@ namespace Helpers std::snprintf(buf.get(), size, format.c_str(), args...); return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside } + + int64_t getTimeInMillis(); } #endif // HELPERS_HPP diff --git a/components/LEDManager/CMakeLists.txt b/components/LEDManager/CMakeLists.txt new file mode 100644 index 0000000..b377b09 --- /dev/null +++ b/components/LEDManager/CMakeLists.txt @@ -0,0 +1,4 @@ +idf_component_register(SRCS "LEDManager/LEDManager.cpp" + INCLUDE_DIRS "LEDManager" + REQUIRES StateManager driver esp_driver_ledc Helpers +) \ No newline at end of file diff --git a/components/LEDManager/LEDManager/LEDManager.cpp b/components/LEDManager/LEDManager/LEDManager.cpp new file mode 100644 index 0000000..2087d7f --- /dev/null +++ b/components/LEDManager/LEDManager/LEDManager.cpp @@ -0,0 +1,161 @@ +#include "LEDManager.hpp" + +const char *LED_MANAGER_TAG = "[LED_MANAGER]"; + +LEDManager::ledStateMap_t LEDManager::ledStateMap = { + {LEDStates_e::_LedStateNone, {{0, 500}}}, + {LEDStates_e::_Improv_Error, + {{1, 1000}, {0, 500}, {0, 1000}, {0, 500}, {1, 1000}}}, + {LEDStates_e::_Improv_Start, + {{1, 500}, {0, 300}, {0, 300}, {0, 300}, {1, 500}}}, + {LEDStates_e::_Improv_Stop, + {{1, 300}, {0, 500}, {0, 500}, {0, 500}, {1, 300}}}, + {LEDStates_e::_Improv_Processing, + {{1, 200}, {0, 100}, {0, 500}, {0, 100}, {1, 200}}}, + {LEDStates_e::_WebServerState_Error, + {{1, 200}, {0, 100}, {0, 500}, {0, 100}, {1, 200}}}, + {LEDStates_e::_WiFiState_Error, + {{1, 200}, {0, 100}, {0, 500}, {0, 100}, {1, 200}}}, + {LEDStates_e::_MDNSState_Error, + {{1, 200}, + {0, 100}, + {1, 200}, + {0, 100}, + {0, 500}, + {0, 100}, + {1, 200}, + {0, 100}, + {1, 200}}}, + {LEDStates_e::_Camera_Error, + {{1, 5000}}}, // this also works as a more general error - something went + // critically wrong? We go here + {LEDStates_e::_WiFiState_Connecting, {{1, 100}, {0, 100}}}, + {LEDStates_e::_WiFiState_Connected, + {{1, 100}, + {0, 100}, + {1, 100}, + {0, 100}, + {1, 100}, + {0, 100}, + {1, 100}, + {0, 100}, + {1, 100}, + {0, 100}}}}; + +std::vector LEDManager::keepAliveStates = { + LEDStates_e::_WebServerState_Error, LEDStates_e::_Camera_Error}; + +LEDManager::LEDManager(gpio_num_t pin) : blink_led_pin(pin), state(false) {} + +#ifdef USE_ILLUMNATIOR_PIN +LEDManager::LEDManager(gpio_num_t pin, gpio_num_t illumninator_led_pin) : blink_led_pin(pin), illumninator_led_pin(illumninator_led_pin) state(false) {} +#endif + +void LEDManager::setup() +{ + ESP_LOGD(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); + // the defualt state is _LedStateNone so we're fine + this->currentState = ledStateManager.getCurrentState(); + this->currentPatternIndex = 0; + BlinkPatterns_t pattern = + this->ledStateMap[this->currentState][this->currentPatternIndex]; + this->toggleLED(pattern.state); + this->nextStateChangeMillis = pattern.delayTime; + +#ifdef USE_ILLUMNATIOR_PIN + ESP_LOGD(LED_MANAGER_TAG, "Setting up illuminator led."); + const int ledPin = 1; // Replace this with a command endpoint eventually. + const int freq = 5000; + const auto resolution = LEDC_TIMER_8_BIT; + const int dutyCycle = 255; + + 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}; + + ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); + + ledc_channel_config_t ledc_channel = { + .gpio_num = this->illumninator_led_pin, + .speed_mode = LEDC_LOW_SPEED_MODE, + .channel = LEDC_CHANNEL_0, + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = LEDC_TIMER_0, + .duty = dutyCycle, + .hpoint = 0}; + + ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); +#endif + ESP_LOGD(LED_MANAGER_TAG, "Done."); +} + +/** + * @brief Display the current state of the LED manager as a pattern of blinking + * LED + * @details This function must be called in the main loop + */ +void LEDManager::handleLED() +{ + if (Helpers::getTimeInMillis() <= this->nextStateChangeMillis) + { + return; + } + + // !TODO what if we want a looping state? Or a state that needs to stay + // bright? Am overthinking this, aren't I? + + // we've reached the timeout on that state, check if we can grab next one and + // start displaying it, or if we need to keep displaying the current one + if (this->currentPatternIndex > + this->ledStateMap[this->currentState].size() - 1) + { + auto nextState = ledStateManager.getCurrentState(); + // we want to keep displaying the same state only if its an keepAlive one, + // but we should change if the incoming one is also an errours state, maybe + // more serious one this time <- this may be a bad idea + if ((std::find(this->keepAliveStates.begin(), this->keepAliveStates.end(), + this->currentState) != this->keepAliveStates.end() || + std::find(this->keepAliveStates.begin(), this->keepAliveStates.end(), + nextState) != this->keepAliveStates.end()) || + (this->currentState != nextState && + this->ledStateMap.find(nextState) != this->ledStateMap.end())) + { + ESP_LOGD(LED_MANAGER_TAG, "Updating the state and reseting"); + this->toggleLED(false); + this->currentState = nextState; + this->currentPatternIndex = 0; + BlinkPatterns_t pattern = + this->ledStateMap[this->currentState][this->currentPatternIndex]; + this->nextStateChangeMillis = Helpers::getTimeInMillis() + pattern.delayTime; + return; + } + // it wasn't a keepAlive state, nor did we have another one ready, + // we're done for now + this->toggleLED(false); + return; + } + // we can safely advance it and display the next stage + BlinkPatterns_t pattern = + this->ledStateMap[this->currentState][this->currentPatternIndex]; + this->toggleLED(pattern.state); + this->nextStateChangeMillis = Helpers::getTimeInMillis() + pattern.delayTime; + ESP_LOGD(LED_MANAGER_TAG, "before updating stage %d", this->currentPatternIndex); + this->currentPatternIndex++; + ESP_LOGD(LED_MANAGER_TAG, "updated stage %d", this->currentPatternIndex); +} + +/** + * @brief Turn the LED on or off + * + * @param state + */ +void LEDManager::toggleLED(bool state) const +{ + gpio_set_level(blink_led_pin, state); +} diff --git a/components/LEDManager/LEDManager/LEDManager.hpp b/components/LEDManager/LEDManager/LEDManager.hpp new file mode 100644 index 0000000..d7a833b --- /dev/null +++ b/components/LEDManager/LEDManager/LEDManager.hpp @@ -0,0 +1,52 @@ +#pragma once +#ifndef _LEDMANAGER_HPP_ +#define _LEDMANAGER_HPP_ + +#include "driver/gpio.h" + +#ifdef USE_ILLUMNATIOR_PIN +#include "driver/ledc.h" +#endif + +#include +#include +#include +#include +#include +#include +#include + +class LEDManager +{ +public: + LEDManager(gpio_num_t blink_led_pin); + +#ifdef USE_ILLUMNATIOR_PIN + gpio_num_t illumninator_led_pin; + LEDManager(gpio_num_t blink_led_pin, gpio_num_t illumninator_led_pin); +#endif + + void setup(); + void handleLED(); + +private: + gpio_num_t blink_led_pin; + unsigned long nextStateChangeMillis = 0; + bool state = false; + + struct BlinkPatterns_t + { + int state; + int delayTime; + }; + + typedef std::unordered_map> ledStateMap_t; + static ledStateMap_t ledStateMap; + static std::vector keepAliveStates; + LEDStates_e currentState; + unsigned int currentPatternIndex = 0; + + void toggleLED(bool state) const; +}; + +#endif \ No newline at end of file diff --git a/main/openiris_main.cpp b/main/openiris_main.cpp index d93dbbc..9e47eb4 100644 --- a/main/openiris_main.cpp +++ b/main/openiris_main.cpp @@ -12,7 +12,6 @@ #include "freertos/task.h" #include "driver/gpio.h" #include "esp_log.h" -#include "led_strip.h" #include "sdkconfig.h" #include "usb_device_uvc.h" #include "esp_camera.h" @@ -22,29 +21,11 @@ #include #include #include +#include -static const char *TAG = "[MAIN]"; - -/* Use project configuration menu (idf.py menuconfig) to choose the GPIO to blink, - or you can edit the following line and set a number here. -*/ #define BLINK_GPIO (gpio_num_t) CONFIG_BLINK_GPIO -static uint8_t s_led_state = 0; - -static void blink_led(void) -{ - /* Set the GPIO level according to the state (LOW or HIGH)*/ - gpio_set_level(BLINK_GPIO, s_led_state); -} - -static void configure_led(void) -{ - ESP_LOGI(TAG, "Example configured to blink GPIO LED!"); - gpio_reset_pin(BLINK_GPIO); - /* Set the GPIO as a push/pull output */ - gpio_set_direction(BLINK_GPIO, GPIO_MODE_OUTPUT); -} +static const char *TAG = "[MAIN]"; static void initNVSStorage() { @@ -80,21 +61,23 @@ extern "C" void app_main(void) ProjectConfig deviceConfig("openiris", "openiristracker"); WiFiManager wifiManager; +#ifdef USE_ILLUMNATIOR_PIN + // LEDManager ledManager(BLINK_GPIO, ILLUMINATOR_PIN); + LEDManager ledManager(BLINK_GPIO, 1); +#else + LEDManager ledManager(BLINK_GPIO); +#endif + Logo::printASCII(); initNVSStorage(); + ledManager.setup(); deviceConfig.load(); wifiManager.Begin(); - /* Configure the peripheral according to the LED type */ - configure_led(); - while (1) { - ESP_LOGI(TAG, "Turning the LED on pin %d %s!", BLINK_GPIO, s_led_state == true ? "ON" : "OFF"); - blink_led(); - /* Toggle the LED state */ - s_led_state = !s_led_state; + ledManager.handleLED(); vTaskDelay(CONFIG_BLINK_PERIOD / portTICK_PERIOD_MS); } }