diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 3f32affd..70eeafe1 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -6,6 +6,7 @@ set(SOURCES "audio_codecs/audio_codec.cc" "audio_codecs/cores3_audio_codec.cc" "led/single_led.cc" "led/circular_strip.cc" + "led/gpio_led.cc" "display/display.cc" "display/no_display.cc" "display/lcd_display.cc" @@ -38,7 +39,7 @@ if(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI) elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ML307) set(BOARD_TYPE "bread-compact-ml307") elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_ESP32) - set(BOARD_TYPE "bread-compact-esp32") + set(BOARD_TYPE "bread-compact-esp32") elseif(CONFIG_BOARD_TYPE_ESP_BOX_3) set(BOARD_TYPE "esp-box-3") elseif(CONFIG_BOARD_TYPE_ESP_BOX) @@ -48,7 +49,7 @@ elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_1) elseif(CONFIG_BOARD_TYPE_KEVIN_BOX_2) set(BOARD_TYPE "kevin-box-2") elseif(CONFIG_BOARD_TYPE_KEVIN_C3) - set(BOARD_TYPE "kevin-c3") + set(BOARD_TYPE "kevin-c3") elseif(CONFIG_BOARD_TYPE_KEVIN_SP_V3_DEV) set(BOARD_TYPE "kevin-sp-v3-dev") elseif(CONFIG_BOARD_TYPE_KEVIN_YUYING_313LCD) @@ -72,13 +73,13 @@ elseif(CONFIG_BOARD_TYPE_ATOMS3_ECHO_BASE) elseif(CONFIG_BOARD_TYPE_ATOMS3R_ECHO_BASE) set(BOARD_TYPE "atoms3r-echo-base") elseif(CONFIG_BOARD_TYPE_ATOMMATRIX_ECHO_BASE) - set(BOARD_TYPE "atommatrix-echo-base") + set(BOARD_TYPE "atommatrix-echo-base") elseif(CONFIG_BOARD_TYPE_XMINI_C3) set(BOARD_TYPE "xmini-c3") elseif(CONFIG_BOARD_TYPE_ESP32S3_KORVO2_V3) set(BOARD_TYPE "esp32s3-korvo2-v3") elseif(CONFIG_BOARD_TYPE_ESP_SPARKBOT) - set(BOARD_TYPE "esp-sparkbot") + set(BOARD_TYPE "esp-sparkbot") elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8) set(BOARD_TYPE "esp32-s3-touch-amoled-1.8") elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85C) @@ -88,21 +89,21 @@ elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85) elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_46) set(BOARD_TYPE "esp32-s3-touch-lcd-1.46") elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_LCD) - set(BOARD_TYPE "bread-compact-wifi-lcd") + set(BOARD_TYPE "bread-compact-wifi-lcd") elseif(CONFIG_BOARD_TYPE_TUDOUZI) - set(BOARD_TYPE "tudouzi") + set(BOARD_TYPE "tudouzi") elseif(CONFIG_BOARD_TYPE_LILYGO_T_CIRCLE_S3) - set(BOARD_TYPE "lilygo-t-circle-s3") + set(BOARD_TYPE "lilygo-t-circle-s3") elseif(CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3) - set(BOARD_TYPE "lilygo-t-cameraplus-s3") + set(BOARD_TYPE "lilygo-t-cameraplus-s3") elseif(CONFIG_BOARD_TYPE_MOVECALL_MOJI_ESP32S3) - set(BOARD_TYPE "movecall-moji-esp32s3") + set(BOARD_TYPE "movecall-moji-esp32s3") elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3) set(BOARD_TYPE "atk-dnesp32s3") elseif(CONFIG_BOARD_TYPE_ATK_DNESP32S3_BOX) - set(BOARD_TYPE "atk-dnesp32s3-box") + set(BOARD_TYPE "atk-dnesp32s3-box") elseif(CONFIG_BOARD_TYPE_DU_CHATX) - set(BOARD_TYPE "du-chatx") + set(BOARD_TYPE "du-chatx") elseif(CONFIG_BOARD_TYPE_ESP32S3_Taiji_Pi) set(BOARD_TYPE "taiji-pi-s3") elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_0_96OLED_WIFI) @@ -116,7 +117,7 @@ elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_ML307) elseif(CONFIG_BOARD_TYPE_SENSECAP_WATCHER) set(BOARD_TYPE "sensecap-watcher") endif() -file(GLOB BOARD_SOURCES +file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.c ) @@ -173,7 +174,7 @@ add_custom_command( COMMAND python ${PROJECT_DIR}/scripts/gen_lang.py --input "${LANG_JSON}" --output "${LANG_HEADER}" - DEPENDS + DEPENDS ${LANG_JSON} ${PROJECT_DIR}/scripts/gen_lang.py COMMENT "Generating ${LANG_DIR} language config" diff --git a/main/led/gpio_led.cc b/main/led/gpio_led.cc new file mode 100644 index 00000000..0ba5c15e --- /dev/null +++ b/main/led/gpio_led.cc @@ -0,0 +1,235 @@ +#include "gpio_led.h" +#include "application.h" +#include + +#define TAG "GpioLed" + +#define DEFAULT_BRIGHTNESS 50 +#define HIGH_BRIGHTNESS 100 +#define LOW_BRIGHTNESS 10 + +#define IDLE_BRIGHTNESS 5 +#define SPEAKING_BRIGHTNESS 75 +#define UPGRADING_BRIGHTNESS 25 +#define ACTIVATING_BRIGHTNESS 35 + +#define BLINK_INFINITE -1 + +// GPIO_LED +#define LEDC_LS_TIMER LEDC_TIMER_1 +#define LEDC_LS_MODE LEDC_LOW_SPEED_MODE +#define LEDC_LS_CH0_CHANNEL LEDC_CHANNEL_0 + +#define LEDC_DUTY (4096) +#define LEDC_FADE_TIME (1000) +// GPIO_LED + +GpioLed::GpioLed(gpio_num_t gpio, int output_invert) { + // If the gpio is not connected, you should use NoLed class + assert(gpio != GPIO_NUM_NC); + + /* + * Prepare and set configuration of timers + * that will be used by LED Controller + */ + ledc_timer_config_t ledc_timer = {}; + ledc_timer.duty_resolution = LEDC_TIMER_13_BIT; // resolution of PWM duty + ledc_timer.freq_hz = 4000; // frequency of PWM signal + ledc_timer.speed_mode = LEDC_LS_MODE; // timer mode + ledc_timer.timer_num = LEDC_LS_TIMER; // timer index + ledc_timer.clk_cfg = LEDC_AUTO_CLK; // Auto select the source clock + + ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); + + ledc_channel_.channel = LEDC_LS_CH0_CHANNEL, + ledc_channel_.duty = 0, + ledc_channel_.gpio_num = gpio, + ledc_channel_.speed_mode = LEDC_LS_MODE, + ledc_channel_.hpoint = 0, + ledc_channel_.timer_sel = LEDC_LS_TIMER, + ledc_channel_.flags.output_invert = output_invert & 0x01, + + // Set LED Controller with previously prepared configuration + ledc_channel_config(&ledc_channel_); + + // Initialize fade service. + ledc_fade_func_install(0); + + // When the callback registered by ledc_cb_degister is called, run led ->OnFadeEnd() + ledc_cbs_t ledc_callbacks = { + .fade_cb = FadeCallback + }; + ledc_cb_register(ledc_channel_.speed_mode, ledc_channel_.channel, &ledc_callbacks, this); + + esp_timer_create_args_t blink_timer_args = { + .callback = [](void *arg) { + auto led = static_cast(arg); + led->OnBlinkTimer(); + }, + .arg = this, + .dispatch_method = ESP_TIMER_TASK, + .name = "Blink Timer", + .skip_unhandled_events = false, + }; + ESP_ERROR_CHECK(esp_timer_create(&blink_timer_args, &blink_timer_)); + + ledc_initialized_ = true; +} + +GpioLed::~GpioLed() { + esp_timer_stop(blink_timer_); + if (ledc_initialized_) { + ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel); + ledc_fade_func_uninstall(); + } +} + + +void GpioLed::SetBrightness(uint8_t brightness) { + duty_ = brightness * LEDC_DUTY / 100; +} + +void GpioLed::TurnOn() { + if (!ledc_initialized_) { + return; + } + + std::lock_guard lock(mutex_); + esp_timer_stop(blink_timer_); + ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel); + ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, duty_); + ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel); +} + +void GpioLed::TurnOff() { + if (!ledc_initialized_) { + return; + } + + std::lock_guard lock(mutex_); + esp_timer_stop(blink_timer_); + ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel); + ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, 0); + ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel); +} + +void GpioLed::BlinkOnce() { + Blink(1, 100); +} + +void GpioLed::Blink(int times, int interval_ms) { + StartBlinkTask(times, interval_ms); +} + +void GpioLed::StartContinuousBlink(int interval_ms) { + StartBlinkTask(BLINK_INFINITE, interval_ms); +} + +void GpioLed::StartBlinkTask(int times, int interval_ms) { + if (!ledc_initialized_) { + return; + } + + std::lock_guard lock(mutex_); + esp_timer_stop(blink_timer_); + ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel); + + blink_counter_ = times * 2; + blink_interval_ms_ = interval_ms; + esp_timer_start_periodic(blink_timer_, interval_ms * 1000); +} + +void GpioLed::OnBlinkTimer() { + std::lock_guard lock(mutex_); + blink_counter_--; + if (blink_counter_ & 1) { + ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, duty_); + } else { + ledc_set_duty(ledc_channel_.speed_mode, ledc_channel_.channel, 0); + + if (blink_counter_ == 0) { + esp_timer_stop(blink_timer_); + } + } + ledc_update_duty(ledc_channel_.speed_mode, ledc_channel_.channel); +} + +void GpioLed::StartFadeTask() { + if (!ledc_initialized_) { + return; + } + + std::lock_guard lock(mutex_); + esp_timer_stop(blink_timer_); + ledc_fade_stop(ledc_channel_.speed_mode, ledc_channel_.channel); + fade_up_ = true; + ledc_set_fade_with_time(ledc_channel_.speed_mode, + ledc_channel_.channel, LEDC_DUTY, LEDC_FADE_TIME); + ledc_fade_start(ledc_channel_.speed_mode, + ledc_channel_.channel, LEDC_FADE_NO_WAIT); +} + +void GpioLed::OnFadeEnd() { + std::lock_guard lock(mutex_); + fade_up_ = !fade_up_; + ledc_set_fade_with_time(ledc_channel_.speed_mode, + ledc_channel_.channel, fade_up_ ? LEDC_DUTY : 0, LEDC_FADE_TIME); + ledc_fade_start(ledc_channel_.speed_mode, + ledc_channel_.channel, LEDC_FADE_NO_WAIT); +} + +bool GpioLed::FadeCallback(const ledc_cb_param_t *param, void *user_arg) { + if (param->event == LEDC_FADE_END_EVT) { + auto led = static_cast(user_arg); + led->OnFadeEnd(); + } + return true; +} + +void GpioLed::OnStateChanged() { + auto& app = Application::GetInstance(); + auto device_state = app.GetDeviceState(); + switch (device_state) { + case kDeviceStateStarting: + SetBrightness(DEFAULT_BRIGHTNESS); + StartContinuousBlink(100); + break; + case kDeviceStateWifiConfiguring: + SetBrightness(DEFAULT_BRIGHTNESS); + StartContinuousBlink(500); + break; + case kDeviceStateIdle: + SetBrightness(IDLE_BRIGHTNESS); + TurnOn(); + // TurnOff(); + break; + case kDeviceStateConnecting: + SetBrightness(DEFAULT_BRIGHTNESS); + TurnOn(); + break; + case kDeviceStateListening: + if (app.IsVoiceDetected()) { + SetBrightness(HIGH_BRIGHTNESS); + } else { + SetBrightness(LOW_BRIGHTNESS); + } + // TurnOn(); + StartFadeTask(); + break; + case kDeviceStateSpeaking: + SetBrightness(SPEAKING_BRIGHTNESS); + TurnOn(); + break; + case kDeviceStateUpgrading: + SetBrightness(UPGRADING_BRIGHTNESS); + StartContinuousBlink(100); + break; + case kDeviceStateActivating: + SetBrightness(ACTIVATING_BRIGHTNESS); + StartContinuousBlink(500); + break; + default: + ESP_LOGE(TAG, "Unknown gpio led event: %d", device_state); + return; + } +} diff --git a/main/led/gpio_led.h b/main/led/gpio_led.h new file mode 100644 index 00000000..a3870539 --- /dev/null +++ b/main/led/gpio_led.h @@ -0,0 +1,45 @@ +#ifndef _GPIO_LED_H_ +#define _GPIO_LED_H_ + +#include +#include +#include "led.h" +#include +#include +#include +#include +#include + +class GpioLed : public Led { +public: + GpioLed(gpio_num_t gpio, int output_invert=0); + virtual ~GpioLed(); + + void OnStateChanged() override; + +private: + std::mutex mutex_; + TaskHandle_t blink_task_ = nullptr; + ledc_channel_config_t ledc_channel_ = {0}; + bool ledc_initialized_ = false; + uint32_t duty_ = 0; + int blink_counter_ = 0; + int blink_interval_ms_ = 0; + esp_timer_handle_t blink_timer_ = nullptr; + bool fade_up_ = true; + + void StartBlinkTask(int times, int interval_ms); + void OnBlinkTimer(); + + void BlinkOnce(); + void Blink(int times, int interval_ms); + void StartContinuousBlink(int interval_ms); + void TurnOn(); + void TurnOff(); + void SetBrightness(uint8_t brightness); + void StartFadeTask(); + void OnFadeEnd(); + static bool FadeCallback(const ledc_cb_param_t *param, void *user_arg); +}; + +#endif // _GPIO_LED_H_