From 2e6a278b0da1922188ad5ff0d15f2f85bdf618fd Mon Sep 17 00:00:00 2001 From: Andy <550896603@qq.com> Date: Tue, 17 Dec 2024 14:03:45 +0800 Subject: [PATCH 1/5] feat: add led strip class --- main/CMakeLists.txt | 3 + main/application.cc | 31 +-- .../compact_ml307_board.cc | 8 +- .../bread-compact-wifi/compact_wifi_board.cc | 8 +- main/boards/common/board.h | 4 +- main/boards/common/led.cc | 69 +----- main/boards/common/led.h | 21 +- main/boards/common/wifi_board.cc | 5 +- main/boards/esp-box-3/esp_box3_board.cc | 10 +- main/boards/kevin-box-1/kevin_box_board.cc | 10 +- main/boards/kevin-box-2/kevin_box_board.cc | 10 +- main/boards/kevin-c3/config.h | 2 +- main/boards/kevin-c3/kevin_box_board.cc | 10 +- .../boards/lichuang-dev/lichuang_dev_board.cc | 8 +- main/led_strip/led_strip_wrapper.cc | 219 ++++++++++++++++++ main/led_strip/led_strip_wrapper.h | 58 +++++ main/led_strip/multiple_led.cc | 39 ++++ main/led_strip/multiple_led.h | 14 ++ main/led_strip/single_led.cc | 39 ++++ main/led_strip/single_led.h | 14 ++ 20 files changed, 449 insertions(+), 133 deletions(-) create mode 100644 main/led_strip/led_strip_wrapper.cc create mode 100644 main/led_strip/led_strip_wrapper.h create mode 100644 main/led_strip/multiple_led.cc create mode 100644 main/led_strip/multiple_led.h create mode 100644 main/led_strip/single_led.cc create mode 100644 main/led_strip/single_led.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 901cad0..3745605 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -3,6 +3,9 @@ set(SOURCES "audio_codecs/audio_codec.cc" "audio_codecs/box_audio_codec.cc" "audio_codecs/es8311_audio_codec.cc" "audio_codecs/cores3_audio_codec.cc" + "led_strip/single_led.cc" + "led_strip/multiple_led.cc" + "led_strip/led_strip_wrapper.cc" "display/display.cc" "display/no_display.cc" "display/st7789_display.cc" diff --git a/main/application.cc b/main/application.cc index 3119e00..80e0dab 100644 --- a/main/application.cc +++ b/main/application.cc @@ -186,9 +186,8 @@ void Application::StopListening() { void Application::Start() { auto& board = Board::GetInstance(); - auto builtin_led = board.GetBuiltinLed(); - builtin_led->SetBlue(); - builtin_led->StartContinuousBlink(100); + auto led_strip = board.GetLedStrip(); + led_strip->LightOn(kStartup); /* Setup the display */ auto display = board.GetDisplay(); @@ -246,14 +245,13 @@ void Application::Start() { wake_word_detect_.Initialize(codec->input_channels(), codec->input_reference()); wake_word_detect_.OnVadStateChange([this](bool speaking) { Schedule([this, speaking]() { - auto builtin_led = Board::GetInstance().GetBuiltinLed(); + auto led_strip = Board::GetInstance().GetLedStrip(); if (chat_state_ == kChatStateListening) { if (speaking) { - builtin_led->SetRed(HIGH_BRIGHTNESS); + led_strip->LightOn(kListeningAndSpeaking); } else { - builtin_led->SetRed(LOW_BRIGHTNESS); + led_strip->LightOn(kListening); } - builtin_led->TurnOn(); } }); }); @@ -382,8 +380,7 @@ void Application::Start() { // Blink the LED to indicate the device is running display->SetStatus("待命"); - builtin_led->SetGreen(); - builtin_led->BlinkOnce(); + led_strip->LightOn(kStandby); SetChatState(kChatStateIdle); } @@ -545,11 +542,11 @@ void Application::SetChatState(ChatState state) { background_task_.WaitForCompletion(); auto display = Board::GetInstance().GetDisplay(); - auto builtin_led = Board::GetInstance().GetBuiltinLed(); + auto led_strip = Board::GetInstance().GetLedStrip(); switch (state) { case kChatStateUnknown: case kChatStateIdle: - builtin_led->TurnOff(); + led_strip->LightOff(); display->SetStatus("待命"); display->SetEmotion("neutral"); #ifdef CONFIG_IDF_TARGET_ESP32S3 @@ -557,13 +554,11 @@ void Application::SetChatState(ChatState state) { #endif break; case kChatStateConnecting: - builtin_led->SetBlue(); - builtin_led->TurnOn(); + led_strip->LightOn(kConnecting); display->SetStatus("连接中..."); break; case kChatStateListening: - builtin_led->SetRed(); - builtin_led->TurnOn(); + led_strip->LightOn(kListening); display->SetStatus("聆听中..."); display->SetEmotion("neutral"); ResetDecoder(); @@ -574,8 +569,7 @@ void Application::SetChatState(ChatState state) { UpdateIotStates(); break; case kChatStateSpeaking: - builtin_led->SetGreen(); - builtin_led->TurnOn(); + led_strip->LightOn(kSpeaking); display->SetStatus("说话中..."); ResetDecoder(); #if CONFIG_IDF_TARGET_ESP32S3 @@ -583,8 +577,7 @@ void Application::SetChatState(ChatState state) { #endif break; case kChatStateUpgrading: - builtin_led->SetGreen(); - builtin_led->StartContinuousBlink(100); + led_strip->LightOn(kUpgrading); break; default: ESP_LOGE(TAG, "Invalid chat state: %d", chat_state_); diff --git a/main/boards/bread-compact-ml307/compact_ml307_board.cc b/main/boards/bread-compact-ml307/compact_ml307_board.cc index bca70e8..604871b 100644 --- a/main/boards/bread-compact-ml307/compact_ml307_board.cc +++ b/main/boards/bread-compact-ml307/compact_ml307_board.cc @@ -4,9 +4,9 @@ #include "system_reset.h" #include "application.h" #include "button.h" -#include "led.h" #include "config.h" #include "iot/thing_manager.h" +#include "led_strip/single_led.h" #include #include @@ -102,9 +102,9 @@ public: InitializeIot(); } - virtual Led* GetBuiltinLed() override { - static Led led(BUILTIN_LED_GPIO); - return &led; + virtual LedStripWrapper* GetLedStrip() override { + static SingleLed led_strip(BUILTIN_LED_GPIO); + return &led_strip; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/bread-compact-wifi/compact_wifi_board.cc b/main/boards/bread-compact-wifi/compact_wifi_board.cc index 5d9ac8d..38b2fe1 100644 --- a/main/boards/bread-compact-wifi/compact_wifi_board.cc +++ b/main/boards/bread-compact-wifi/compact_wifi_board.cc @@ -4,9 +4,9 @@ #include "system_reset.h" #include "application.h" #include "button.h" -#include "led.h" #include "config.h" #include "iot/thing_manager.h" +#include "led_strip/single_led.h" #include #include @@ -107,9 +107,9 @@ public: InitializeIot(); } - virtual Led* GetBuiltinLed() override { - static Led led(BUILTIN_LED_GPIO); - return &led; + virtual LedStripWrapper* GetLedStrip() override { + static SingleLed led_strip(BUILTIN_LED_GPIO); + return &led_strip; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/common/board.h b/main/boards/common/board.h index 66de827..6a4bd43 100644 --- a/main/boards/common/board.h +++ b/main/boards/common/board.h @@ -7,7 +7,7 @@ #include #include -#include "led.h" +#include "led_strip/led_strip_wrapper.h" void* create_board(); class AudioCodec; @@ -32,7 +32,7 @@ public: virtual void StartNetwork() = 0; virtual ~Board() = default; - virtual Led* GetBuiltinLed() = 0; + virtual LedStripWrapper* GetLedStrip() = 0; virtual AudioCodec* GetAudioCodec() = 0; virtual Display* GetDisplay(); virtual Http* CreateHttp() = 0; diff --git a/main/boards/common/led.cc b/main/boards/common/led.cc index e3640af..61ac028 100644 --- a/main/boards/common/led.cc +++ b/main/boards/common/led.cc @@ -6,41 +6,29 @@ #define TAG "Led" -Led::Led(gpio_num_t gpio) { +Led::Led(gpio_num_t gpio, uint8_t max_leds) { if (gpio == GPIO_NUM_NC) { ESP_LOGI(TAG, "Builtin LED not connected"); return; } - + led_strip_config_t strip_config = {}; strip_config.strip_gpio_num = gpio; - strip_config.max_leds = 1; + strip_config.max_leds = max_leds; strip_config.led_pixel_format = LED_PIXEL_FORMAT_GRB; strip_config.led_model = LED_MODEL_WS2812; led_strip_rmt_config_t rmt_config = {}; rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz + max_leds_ = max_leds; ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_)); + led_strip_clear(led_strip_); - SetGrey(); - - 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_)); } Led::~Led() { - esp_timer_stop(blink_timer_); if (led_strip_ != nullptr) { led_strip_del(led_strip_); } @@ -58,8 +46,9 @@ void Led::TurnOn() { } std::lock_guard lock(mutex_); - esp_timer_stop(blink_timer_); - led_strip_set_pixel(led_strip_, 0, r_, g_, b_); + for (int i = 0; i < max_leds_; i++) { + led_strip_set_pixel(led_strip_, i, r_, g_, b_); + } led_strip_refresh(led_strip_); } @@ -69,47 +58,5 @@ void Led::TurnOff() { } std::lock_guard lock(mutex_); - esp_timer_stop(blink_timer_); led_strip_clear(led_strip_); } - -void Led::BlinkOnce() { - Blink(1, 100); -} - -void Led::Blink(int times, int interval_ms) { - StartBlinkTask(times, interval_ms); -} - -void Led::StartContinuousBlink(int interval_ms) { - StartBlinkTask(BLINK_INFINITE, interval_ms); -} - -void Led::StartBlinkTask(int times, int interval_ms) { - if (led_strip_ == nullptr) { - return; - } - - std::lock_guard lock(mutex_); - esp_timer_stop(blink_timer_); - - led_strip_clear(led_strip_); - blink_counter_ = times * 2; - blink_interval_ms_ = interval_ms; - esp_timer_start_periodic(blink_timer_, interval_ms * 1000); -} - -void Led::OnBlinkTimer() { - std::lock_guard lock(mutex_); - blink_counter_--; - if (blink_counter_ & 1) { - led_strip_set_pixel(led_strip_, 0, r_, g_, b_); - led_strip_refresh(led_strip_); - } else { - led_strip_clear(led_strip_); - - if (blink_counter_ == 0) { - esp_timer_stop(blink_timer_); - } - } -} diff --git a/main/boards/common/led.h b/main/boards/common/led.h index e0fe334..8cd1858 100644 --- a/main/boards/common/led.h +++ b/main/boards/common/led.h @@ -6,9 +6,6 @@ #include #include -#define BLINK_INFINITE -1 -#define BLINK_TASK_STOPPED_BIT BIT0 -#define BLINK_TASK_RUNNING_BIT BIT1 #define DEFAULT_BRIGHTNESS 4 #define HIGH_BRIGHTNESS 16 @@ -16,12 +13,12 @@ class Led { public: - Led(gpio_num_t gpio); + Led(gpio_num_t gpio, uint8_t max_leds); ~Led(); - void BlinkOnce(); - void Blink(int times, int interval_ms); - void StartContinuousBlink(int interval_ms); + led_strip_handle_t led_strip() { return led_strip_; } + uint8_t max_leds() { return max_leds_; } + void TurnOn(); void TurnOff(); void SetColor(uint8_t r, uint8_t g, uint8_t b); @@ -30,18 +27,12 @@ public: void SetRed(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(brightness, 0, 0); } void SetGreen(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(0, brightness, 0); } void SetBlue(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(0, 0, brightness); } - + private: std::mutex mutex_; - TaskHandle_t blink_task_ = nullptr; + uint8_t max_leds_ = -1; led_strip_handle_t led_strip_ = nullptr; uint8_t r_ = 0, g_ = 0, b_ = 0; - int blink_counter_ = 0; - int blink_interval_ms_ = 0; - esp_timer_handle_t blink_timer_ = nullptr; - - void StartBlinkTask(int times, int interval_ms); - void OnBlinkTimer(); }; #endif // _LED_H_ diff --git a/main/boards/common/wifi_board.cc b/main/boards/common/wifi_board.cc index 3198904..27deca2 100644 --- a/main/boards/common/wifi_board.cc +++ b/main/boards/common/wifi_board.cc @@ -38,15 +38,14 @@ static std::string rssi_to_string(int rssi) { void WifiBoard::StartNetwork() { auto& application = Application::GetInstance(); auto display = Board::GetInstance().GetDisplay(); - auto builtin_led = Board::GetInstance().GetBuiltinLed(); + auto led_strip = Board::GetInstance().GetLedStrip(); // Try to connect to WiFi, if failed, launch the WiFi configuration AP auto& wifi_station = WifiStation::GetInstance(); display->SetStatus(std::string("正在连接 ") + wifi_station.GetSsid()); wifi_station.Start(); if (!wifi_station.IsConnected()) { - builtin_led->SetBlue(); - builtin_led->Blink(1000, 500); + led_strip->LightOn(kConnecting); auto& wifi_ap = WifiConfigurationAp::GetInstance(); wifi_ap.SetSsidPrefix("Xiaozhi"); wifi_ap.Start(); diff --git a/main/boards/esp-box-3/esp_box3_board.cc b/main/boards/esp-box-3/esp_box3_board.cc index 7df36c1..c47c620 100644 --- a/main/boards/esp-box-3/esp_box3_board.cc +++ b/main/boards/esp-box-3/esp_box3_board.cc @@ -5,9 +5,9 @@ #include "font_awesome_symbols.h" #include "application.h" #include "button.h" -#include "led.h" #include "config.h" #include "iot/thing_manager.h" +#include "led_strip/single_led.h" #include #include @@ -214,10 +214,10 @@ public: InitializeButtons(); InitializeIot(); } - - virtual Led* GetBuiltinLed() override { - static Led led(GPIO_NUM_NC); - return &led; + + virtual LedStripWrapper* GetLedStrip() override { + static SingleLed led_strip(GPIO_NUM_NC); + return &led_strip; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/kevin-box-1/kevin_box_board.cc b/main/boards/kevin-box-1/kevin_box_board.cc index 86d9a44..ba79f4f 100644 --- a/main/boards/kevin-box-1/kevin_box_board.cc +++ b/main/boards/kevin-box-1/kevin_box_board.cc @@ -3,9 +3,9 @@ #include "display/ssd1306_display.h" #include "application.h" #include "button.h" -#include "led.h" #include "config.h" #include "iot/thing_manager.h" +#include "led_strip/single_led.h" #include #include @@ -135,10 +135,10 @@ public: InitializeButtons(); InitializeIot(); } - - virtual Led* GetBuiltinLed() override { - static Led led(BUILTIN_LED_GPIO); - return &led; + + virtual LedStripWrapper* GetLedStrip() override { + static SingleLed led_strip(BUILTIN_LED_GPIO); + return &led_strip; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/kevin-box-2/kevin_box_board.cc b/main/boards/kevin-box-2/kevin_box_board.cc index bddb0bf..e65be33 100644 --- a/main/boards/kevin-box-2/kevin_box_board.cc +++ b/main/boards/kevin-box-2/kevin_box_board.cc @@ -3,10 +3,10 @@ #include "display/ssd1306_display.h" #include "application.h" #include "button.h" -#include "led.h" #include "config.h" #include "axp2101.h" #include "iot/thing_manager.h" +#include "led_strip/single_led.h" #include #include @@ -179,10 +179,10 @@ public: InitializePowerSaveTimer(); InitializeIot(); } - - virtual Led* GetBuiltinLed() override { - static Led led(BUILTIN_LED_GPIO); - return &led; + + virtual LedStripWrapper* GetLedStrip() override { + static SingleLed led_strip(BUILTIN_LED_GPIO); + return &led_strip; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/kevin-c3/config.h b/main/boards/kevin-c3/config.h index a8034e8..0850d25 100644 --- a/main/boards/kevin-c3/config.h +++ b/main/boards/kevin-c3/config.h @@ -17,7 +17,7 @@ #define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_1 #define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR -#define BUILTIN_LED_GPIO GPIO_NUM_2 +#define BUILTIN_LED_GPIO GPIO_NUM_5 #define BOOT_BUTTON_GPIO GPIO_NUM_9 diff --git a/main/boards/kevin-c3/kevin_box_board.cc b/main/boards/kevin-c3/kevin_box_board.cc index c51b185..fac2805 100644 --- a/main/boards/kevin-c3/kevin_box_board.cc +++ b/main/boards/kevin-c3/kevin_box_board.cc @@ -2,9 +2,9 @@ #include "audio_codecs/es8311_audio_codec.h" #include "application.h" #include "button.h" -#include "led.h" #include "config.h" #include "iot/thing_manager.h" +#include "led_strip/multiple_led.h" #include #include @@ -60,15 +60,15 @@ public: KevinBoxBoard() : boot_button_(BOOT_BUTTON_GPIO) { // 把 ESP32C3 的 VDD SPI 引脚作为普通 GPIO 口使用 esp_efuse_write_field_bit(ESP_EFUSE_VDD_SPI_AS_GPIO); - + InitializeCodecI2c(); InitializeButtons(); InitializeIot(); } - virtual Led* GetBuiltinLed() override { - static Led led(BUILTIN_LED_GPIO); - return &led; + virtual LedStripWrapper* GetLedStrip() override { + static MultipleLed led_strip(BUILTIN_LED_GPIO, 8); + return &led_strip; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/lichuang-dev/lichuang_dev_board.cc b/main/boards/lichuang-dev/lichuang_dev_board.cc index e279903..257fee0 100644 --- a/main/boards/lichuang-dev/lichuang_dev_board.cc +++ b/main/boards/lichuang-dev/lichuang_dev_board.cc @@ -3,10 +3,10 @@ #include "display/st7789_display.h" #include "application.h" #include "button.h" -#include "led.h" #include "config.h" #include "i2c_device.h" #include "iot/thing_manager.h" +#include "led_strip/single_led.h" #include #include @@ -135,9 +135,9 @@ public: InitializeIot(); } - virtual Led* GetBuiltinLed() override { - static Led led(GPIO_NUM_NC); - return &led; + virtual LedStripWrapper* GetLedStrip() override { + static SingleLed led_strip(GPIO_NUM_NC); + return &led_strip; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/led_strip/led_strip_wrapper.cc b/main/led_strip/led_strip_wrapper.cc new file mode 100644 index 0000000..d631445 --- /dev/null +++ b/main/led_strip/led_strip_wrapper.cc @@ -0,0 +1,219 @@ +#include "led_strip_wrapper.h" +#include "board.h" + +#include +#include +#include + +#define TAG "LedStripWrapper" + +LedStripWrapper::LedStripWrapper(gpio_num_t gpio, uint8_t max_leds) { + if (gpio == GPIO_NUM_NC) { + ESP_LOGI(TAG, "Builtin LED not connected"); + return; + } + + led_ = new Led(gpio, max_leds); + + esp_timer_create_args_t led_strip_timer_args = { + .callback = [](void *arg) { + auto light = static_cast(arg); + light->OnBlinkTimer(); + }, + .arg = this, + .dispatch_method = ESP_TIMER_TASK, + .name = "Led Strip Timer", + .skip_unhandled_events = false, + }; + ESP_ERROR_CHECK(esp_timer_create(&led_strip_timer_args, &led_strip_timer_)); +} + +LedStripWrapper::~LedStripWrapper() { + if (led_strip_timer_ != nullptr) { + esp_timer_delete(led_strip_timer_); + } + if (led_ != nullptr) { + delete led_; + } +} + +void LedStripWrapper::OnBlinkTimer() { + std::lock_guard lock(mutex_); + counter_--; + timer_callback_(); +} + +void LedStripWrapper::SetLedBasicColor(LedBasicColor color, uint8_t brightness) { + if (led_ == nullptr) { + ESP_LOGE(TAG, "Builtin LED not connected"); + return; + } + + switch (color) { + case kLedColorWhite: + led_->SetWhite(brightness); + break; + case kLedColorGrey: + led_->SetGrey(brightness); + break; + case kLedColorRed: + led_->SetRed(brightness); + break; + case kLedColorGreen: + led_->SetGreen(brightness); + break; + case kLedColorBlue: + led_->SetBlue(brightness); + break; + } +} + +void LedStripWrapper::SetLedStripBasicColor(uint8_t index, LedBasicColor color, uint8_t brightness) { + if (led_ == nullptr) { + ESP_LOGE(TAG, "Builtin LED not connected"); + return; + } + + if (index >= led_->max_leds()) { + ESP_LOGE(TAG, "Invalid led index: %d", index); + return; + } + + switch (color) { + case kLedColorWhite: + led_strip_set_pixel(led_->led_strip(), index, brightness, brightness, brightness); + break; + case kLedColorGrey: + led_strip_set_pixel(led_->led_strip(), index, brightness, brightness, brightness); + break; + case kLedColorRed: + led_strip_set_pixel(led_->led_strip(), index, brightness, 0, 0); + break; + case kLedColorGreen: + led_strip_set_pixel(led_->led_strip(), index, 0, brightness, 0); + break; + case kLedColorBlue: + led_strip_set_pixel(led_->led_strip(), index, 0, 0, brightness); + break; + } +} + +void LedStripWrapper::StartBlinkTask(uint32_t times, uint32_t interval_ms) { + std::lock_guard lock(mutex_); + + if (led_ == nullptr) { + ESP_LOGE(TAG, "Builtin LED not connected"); + return; + } + + esp_timer_stop(led_strip_timer_); + counter_ = times * 2; + timer_callback_ = [this]() { + if (counter_ & 1) { + led_->TurnOn(); + } else { + led_->TurnOff(); + if (counter_ == 0) { + esp_timer_stop(led_strip_timer_); + } + } + }; + esp_timer_start_periodic(led_strip_timer_, interval_ms * 1000); +} + +void LedStripWrapper::BlinkOnce(LedBasicColor color, uint8_t brightness) { + Blink(color, brightness, 1, 100); +} + +void LedStripWrapper::Blink(LedBasicColor color, uint32_t times, uint32_t interval_ms, uint8_t brightness) { + SetLedBasicColor(color, brightness); + StartBlinkTask(times, interval_ms); +} + +void LedStripWrapper::ContinuousBlink(LedBasicColor color, uint32_t interval_ms, uint8_t brightness) { + SetLedBasicColor(color, brightness); + StartBlinkTask(COUNTER_INFINITE, interval_ms); +} + +void LedStripWrapper::StaticLight(LedBasicColor color, uint8_t brightness) { + std::lock_guard lock(mutex_); + + if (led_ == nullptr) { + ESP_LOGE(TAG, "Builtin LED not connected"); + return; + } + + SetLedBasicColor(color, brightness); + esp_timer_stop(led_strip_timer_); + led_->TurnOn(); +} + +void LedStripWrapper::ChasingLight(LedBasicColor base_color, LedBasicColor color, uint32_t interval_ms, uint8_t brightness) { + std::lock_guard lock(mutex_); + + if (led_ == nullptr) { + ESP_LOGE(TAG, "Builtin LED not connected"); + return; + } + + esp_timer_stop(led_strip_timer_); + counter_ = COUNTER_INFINITE; + timer_callback_ = [this, base_color, color, brightness]() { + auto index = counter_ % led_->max_leds(); + for (uint8_t i = 0; i < led_->max_leds(); i++) { + if (i == index || i == (index + 1) % led_->max_leds()) { + SetLedStripBasicColor(i, color, brightness); + } else { + SetLedStripBasicColor(i, base_color, LOW_BRIGHTNESS); + } + } + led_strip_refresh(led_->led_strip()); + }; + esp_timer_start_periodic(led_strip_timer_, interval_ms * 1000); +} + +void LedStripWrapper::BreathLight(LedBasicColor color, uint32_t interval_ms) { + std::lock_guard lock(mutex_); + + if (led_ == nullptr) { + ESP_LOGE(TAG, "Builtin LED not connected"); + return; + } + + esp_timer_stop(led_strip_timer_); + counter_ = COUNTER_INFINITE; + timer_callback_ = [this, color]() { + static bool increase = true; + static uint32_t brightness = LOW_BRIGHTNESS; + + for (uint8_t i = 0; i < led_->max_leds(); i++) { + SetLedStripBasicColor(i, color, brightness); + } + led_strip_refresh(led_->led_strip()); + + if (brightness == HIGH_BRIGHTNESS) { + increase = false; + } else if (brightness == LOW_BRIGHTNESS) { + increase = true; + } + + if (increase) { + brightness += 2; + } else { + brightness -= 2; + } + }; + esp_timer_start_periodic(led_strip_timer_, interval_ms * 1000); +} + +void LedStripWrapper::LightOff() { + std::lock_guard lock(mutex_); + + if (led_ == nullptr) { + ESP_LOGE(TAG, "Builtin LED not connected"); + return; + } + + esp_timer_stop(led_strip_timer_); + led_->TurnOff(); +} diff --git a/main/led_strip/led_strip_wrapper.h b/main/led_strip/led_strip_wrapper.h new file mode 100644 index 0000000..92ca93a --- /dev/null +++ b/main/led_strip/led_strip_wrapper.h @@ -0,0 +1,58 @@ +#ifndef _LED_STRIP_WRAPPER_H_ +#define _LED_STRIP_WRAPPER_H_ + +#include "led.h" + +#define COUNTER_INFINITE -1 + +enum LedStripEvent { + kStartup, + kListening, + kListeningAndSpeaking, + kSpeaking, + kStandby, + kConnecting, + kUpgrading, +}; + +enum LedBasicColor { + kLedColorWhite, + kLedColorGrey, + kLedColorRed, + kLedColorGreen, + kLedColorBlue, +}; + +typedef std::function TimerCallback; + +class LedStripWrapper { +private: + Led* led_ = nullptr; + std::mutex mutex_; + + uint32_t counter_ = 0; + esp_timer_handle_t led_strip_timer_ = nullptr; + TimerCallback timer_callback_; + + void SetLedBasicColor(LedBasicColor color, uint8_t brightness); + void SetLedStripBasicColor(uint8_t index, LedBasicColor color, uint8_t brightness = DEFAULT_BRIGHTNESS); + void StartBlinkTask(uint32_t times, uint32_t interval_ms); + void OnBlinkTimer(); + +public: + LedStripWrapper(gpio_num_t gpio, uint8_t max_leds); + virtual ~LedStripWrapper(); + + void LightOff(); + virtual void LightOn(LedStripEvent event) = 0; + +protected: + void BlinkOnce(LedBasicColor color, uint8_t brightness = DEFAULT_BRIGHTNESS); + void Blink(LedBasicColor color, uint32_t times, uint32_t interval_ms, uint8_t brightness = DEFAULT_BRIGHTNESS); + void ContinuousBlink(LedBasicColor color, uint32_t interval_ms, uint8_t brightness = DEFAULT_BRIGHTNESS); + void StaticLight(LedBasicColor color, uint8_t brightness = DEFAULT_BRIGHTNESS); + void ChasingLight(LedBasicColor base_color, LedBasicColor color, uint32_t interval_ms, uint8_t brightness = DEFAULT_BRIGHTNESS); + void BreathLight(LedBasicColor color, uint32_t interval_ms); +}; + +#endif // _LED_STRIP_WRAPPER_H_ diff --git a/main/led_strip/multiple_led.cc b/main/led_strip/multiple_led.cc new file mode 100644 index 0000000..8b261d4 --- /dev/null +++ b/main/led_strip/multiple_led.cc @@ -0,0 +1,39 @@ +#include "multiple_led.h" +#include + +#define TAG "MultipleLed" + +MultipleLed::MultipleLed(gpio_num_t gpio, uint8_t max_leds) : LedStripWrapper(gpio, max_leds) { +} + +MultipleLed::~MultipleLed() { +} + +void MultipleLed::LightOn(LedStripEvent event) { + switch (event) { + case kStartup: + ChasingLight(kLedColorWhite, kLedColorBlue, 100, HIGH_BRIGHTNESS); + break; + case kListeningAndSpeaking: + BreathLight(kLedColorRed, 100); + break; + case kListening: + BreathLight(kLedColorRed, 100); + break; + case kSpeaking: + StaticLight(kLedColorGreen, HIGH_BRIGHTNESS); + break; + case kStandby: + BlinkOnce(kLedColorGreen); + break; + case kConnecting: + Blink(kLedColorBlue, 1000, 500); + break; + case kUpgrading: + ContinuousBlink(kLedColorGreen, 100); + break; + default: + ESP_LOGE(TAG, "Invalid led strip event: %d", event); + return; + } +} diff --git a/main/led_strip/multiple_led.h b/main/led_strip/multiple_led.h new file mode 100644 index 0000000..14cacd4 --- /dev/null +++ b/main/led_strip/multiple_led.h @@ -0,0 +1,14 @@ +#ifndef _LED_STRIP_EFFECT_V2_H_ +#define _LED_STRIP_EFFECT_V2_H_ + +#include "led_strip_wrapper.h" + +class MultipleLed : public LedStripWrapper { +public: + MultipleLed(gpio_num_t gpio, uint8_t max_leds); + virtual ~MultipleLed(); + + void LightOn(LedStripEvent event) override; +}; + +#endif // _LED_STRIP_EFFECT_V2_H_ diff --git a/main/led_strip/single_led.cc b/main/led_strip/single_led.cc new file mode 100644 index 0000000..fd46791 --- /dev/null +++ b/main/led_strip/single_led.cc @@ -0,0 +1,39 @@ +#include "single_led.h" +#include + +#define TAG "SingleLed" + +SingleLed::SingleLed(gpio_num_t gpio) : LedStripWrapper(gpio, 1) { +} + +SingleLed::~SingleLed() { +} + +void SingleLed::LightOn(LedStripEvent event) { + switch (event) { + case kStartup: + ContinuousBlink(kLedColorBlue, 100); + break; + case kListeningAndSpeaking: + StaticLight(kLedColorRed, HIGH_BRIGHTNESS); + break; + case kListening: + StaticLight(kLedColorRed, LOW_BRIGHTNESS); + break; + case kSpeaking: + StaticLight(kLedColorGreen, HIGH_BRIGHTNESS); + break; + case kStandby: + BlinkOnce(kLedColorGreen); + break; + case kConnecting: + Blink(kLedColorBlue, 1000, 500); + break; + case kUpgrading: + ContinuousBlink(kLedColorGreen, 100); + break; + default: + ESP_LOGE(TAG, "Invalid led strip event: %d", event); + return; + } +} diff --git a/main/led_strip/single_led.h b/main/led_strip/single_led.h new file mode 100644 index 0000000..1d64ea3 --- /dev/null +++ b/main/led_strip/single_led.h @@ -0,0 +1,14 @@ +#ifndef _LED_STRIP_EFFECT_V1_H_ +#define _LED_STRIP_EFFECT_V1_H_ + +#include "led_strip_wrapper.h" + +class SingleLed : public LedStripWrapper { +public: + SingleLed(gpio_num_t gpio); + virtual ~SingleLed(); + + void LightOn(LedStripEvent event) override; +}; + +#endif // _LED_STRIP_EFFECT_V1_H_ From 495b949d7715e7bfd3b0e7d6ee6d002834238a8e Mon Sep 17 00:00:00 2001 From: Andy <550896603@qq.com> Date: Fri, 20 Dec 2024 19:10:17 +0800 Subject: [PATCH 2/5] fix boot button gpio --- main/boards/kevin-c3/config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main/boards/kevin-c3/config.h b/main/boards/kevin-c3/config.h index 0850d25..4241320 100644 --- a/main/boards/kevin-c3/config.h +++ b/main/boards/kevin-c3/config.h @@ -18,7 +18,7 @@ #define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR #define BUILTIN_LED_GPIO GPIO_NUM_5 -#define BOOT_BUTTON_GPIO GPIO_NUM_9 +#define BOOT_BUTTON_GPIO GPIO_NUM_6 #endif // _BOARD_CONFIG_H_ From c7c5b74d37cb7657d006efc8d529345b78866715 Mon Sep 17 00:00:00 2001 From: Terrence Date: Sun, 5 Jan 2025 19:34:28 +0800 Subject: [PATCH 3/5] reconstruct led control --- main/CMakeLists.txt | 5 +- main/application.cc | 117 +++++----- main/application.h | 25 +- .../compact_ml307_board.cc | 8 +- .../bread-compact-wifi/compact_wifi_board.cc | 10 +- main/boards/common/board.cc | 4 + main/boards/common/board.h | 4 +- main/boards/common/led.cc | 62 ----- main/boards/common/led.h | 38 --- main/boards/common/ml307_board.cc | 2 +- main/boards/common/wifi_board.cc | 4 +- main/boards/esp-box-3/esp_box3_board.cc | 6 - main/boards/kevin-box-1/kevin_box_board.cc | 6 +- main/boards/kevin-box-2/kevin_box_board.cc | 10 +- main/boards/kevin-c3/kevin_box_board.cc | 10 +- .../boards/lichuang-dev/lichuang_dev_board.cc | 8 +- main/display/display.cc | 4 +- main/led/circular_strip.cc | 218 ++++++++++++++++++ main/led/circular_strip.h | 44 ++++ .../led_strip_wrapper.cc => led/led.cc} | 10 +- main/led/led.h | 17 ++ main/led/single_led.cc | 158 +++++++++++++ main/led/single_led.h | 38 +++ main/led_strip/led_strip_wrapper.h | 58 ----- main/led_strip/multiple_led.cc | 39 ---- main/led_strip/multiple_led.h | 14 -- main/led_strip/single_led.cc | 39 ---- main/led_strip/single_led.h | 14 -- 28 files changed, 586 insertions(+), 386 deletions(-) delete mode 100644 main/boards/common/led.cc delete mode 100644 main/boards/common/led.h create mode 100644 main/led/circular_strip.cc create mode 100644 main/led/circular_strip.h rename main/{led_strip/led_strip_wrapper.cc => led/led.cc} (96%) create mode 100644 main/led/led.h create mode 100644 main/led/single_led.cc create mode 100644 main/led/single_led.h delete mode 100644 main/led_strip/led_strip_wrapper.h delete mode 100644 main/led_strip/multiple_led.cc delete mode 100644 main/led_strip/multiple_led.h delete mode 100644 main/led_strip/single_led.cc delete mode 100644 main/led_strip/single_led.h diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 3745605..6eb69dc 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -3,9 +3,8 @@ set(SOURCES "audio_codecs/audio_codec.cc" "audio_codecs/box_audio_codec.cc" "audio_codecs/es8311_audio_codec.cc" "audio_codecs/cores3_audio_codec.cc" - "led_strip/single_led.cc" - "led_strip/multiple_led.cc" - "led_strip/led_strip_wrapper.cc" + "led/single_led.cc" + "led/circular_strip.cc" "display/display.cc" "display/no_display.cc" "display/st7789_display.cc" diff --git a/main/application.cc b/main/application.cc index 80e0dab..d4d6b78 100644 --- a/main/application.cc +++ b/main/application.cc @@ -26,11 +26,14 @@ extern const char p3_err_wificonfig_end[] asm("_binary_err_wificonfig_p3_end"); static const char* const STATE_STRINGS[] = { "unknown", + "starting", + "configuring", "idle", "connecting", "listening", "speaking", "upgrading", + "fatal_error", "invalid_state" }; @@ -57,9 +60,9 @@ void Application::CheckNewVersion() { // Wait for the chat state to be idle do { vTaskDelay(pdMS_TO_TICKS(3000)); - } while (GetChatState() != kChatStateIdle); + } while (GetDeviceState() != kDeviceStateIdle); - SetChatState(kChatStateUpgrading); + SetDeviceState(kDeviceStateUpgrading); display->SetIcon(FONT_AWESOME_DOWNLOAD); display->SetStatus("新版本 " + ota_.GetFirmwareVersion()); @@ -75,7 +78,7 @@ void Application::CheckNewVersion() { // If upgrade success, the device will reboot and never reach here ESP_LOGI(TAG, "Firmware upgrade failed..."); - SetChatState(kChatStateIdle); + SetDeviceState(kDeviceStateIdle); } else { ota_.MarkCurrentVersionValid(); display->ShowNotification("版本 " + ota_.GetCurrentVersion()); @@ -127,20 +130,20 @@ void Application::ToggleChatState() { return; } - if (chat_state_ == kChatStateIdle) { - SetChatState(kChatStateConnecting); + if (device_state_ == kDeviceStateIdle) { + SetDeviceState(kDeviceStateConnecting); if (!protocol_->OpenAudioChannel()) { Alert("Error", "Failed to open audio channel"); - SetChatState(kChatStateIdle); + SetDeviceState(kDeviceStateIdle); return; } keep_listening_ = true; protocol_->SendStartListening(kListeningModeAutoStop); - SetChatState(kChatStateListening); - } else if (chat_state_ == kChatStateSpeaking) { + SetDeviceState(kDeviceStateListening); + } else if (device_state_ == kDeviceStateSpeaking) { AbortSpeaking(kAbortReasonNone); - } else if (chat_state_ == kChatStateListening) { + } else if (device_state_ == kDeviceStateListening) { protocol_->CloseAudioChannel(); } }); @@ -154,40 +157,39 @@ void Application::StartListening() { } keep_listening_ = false; - if (chat_state_ == kChatStateIdle) { + if (device_state_ == kDeviceStateIdle) { if (!protocol_->IsAudioChannelOpened()) { - SetChatState(kChatStateConnecting); + SetDeviceState(kDeviceStateConnecting); if (!protocol_->OpenAudioChannel()) { - SetChatState(kChatStateIdle); + SetDeviceState(kDeviceStateIdle); Alert("Error", "Failed to open audio channel"); return; } } protocol_->SendStartListening(kListeningModeManualStop); - SetChatState(kChatStateListening); - } else if (chat_state_ == kChatStateSpeaking) { + SetDeviceState(kDeviceStateListening); + } else if (device_state_ == kDeviceStateSpeaking) { AbortSpeaking(kAbortReasonNone); protocol_->SendStartListening(kListeningModeManualStop); // FIXME: Wait for the speaker to empty the buffer vTaskDelay(pdMS_TO_TICKS(120)); - SetChatState(kChatStateListening); + SetDeviceState(kDeviceStateListening); } }); } void Application::StopListening() { Schedule([this]() { - if (chat_state_ == kChatStateListening) { + if (device_state_ == kDeviceStateListening) { protocol_->SendStopListening(); - SetChatState(kChatStateIdle); + SetDeviceState(kDeviceStateIdle); } }); } void Application::Start() { auto& board = Board::GetInstance(); - auto led_strip = board.GetLedStrip(); - led_strip->LightOn(kStartup); + SetDeviceState(kDeviceStateStarting); /* Setup the display */ auto display = board.GetDisplay(); @@ -245,26 +247,27 @@ void Application::Start() { wake_word_detect_.Initialize(codec->input_channels(), codec->input_reference()); wake_word_detect_.OnVadStateChange([this](bool speaking) { Schedule([this, speaking]() { - auto led_strip = Board::GetInstance().GetLedStrip(); - if (chat_state_ == kChatStateListening) { + if (device_state_ == kDeviceStateListening) { if (speaking) { - led_strip->LightOn(kListeningAndSpeaking); + voice_detected_ = true; } else { - led_strip->LightOn(kListening); + voice_detected_ = false; } + auto led = Board::GetInstance().GetLed(); + led->OnStateChanged(); } }); }); wake_word_detect_.OnWakeWordDetected([this](const std::string& wake_word) { Schedule([this, &wake_word]() { - if (chat_state_ == kChatStateIdle) { - SetChatState(kChatStateConnecting); + if (device_state_ == kDeviceStateIdle) { + SetDeviceState(kDeviceStateConnecting); wake_word_detect_.EncodeWakeWordData(); if (!protocol_->OpenAudioChannel()) { ESP_LOGE(TAG, "Failed to open audio channel"); - SetChatState(kChatStateIdle); + SetDeviceState(kDeviceStateIdle); wake_word_detect_.StartDetection(); return; } @@ -278,8 +281,8 @@ void Application::Start() { protocol_->SendWakeWordDetected(wake_word); ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str()); keep_listening_ = true; - SetChatState(kChatStateListening); - } else if (chat_state_ == kChatStateSpeaking) { + SetDeviceState(kDeviceStateListening); + } else if (device_state_ == kDeviceStateSpeaking) { AbortSpeaking(kAbortReasonWakeWordDetected); } @@ -302,7 +305,7 @@ void Application::Start() { }); protocol_->OnIncomingAudio([this](std::vector&& data) { std::lock_guard lock(mutex_); - if (chat_state_ == kChatStateSpeaking) { + if (device_state_ == kDeviceStateSpeaking) { audio_decode_queue_.emplace_back(std::move(data)); } }); @@ -321,7 +324,7 @@ void Application::Start() { protocol_->OnAudioChannelClosed([this, &board]() { board.SetPowerSaveMode(true); Schedule([this]() { - SetChatState(kChatStateIdle); + SetDeviceState(kDeviceStateIdle); }); }); protocol_->OnIncomingJson([this, display](const cJSON* root) { @@ -332,19 +335,19 @@ void Application::Start() { if (strcmp(state->valuestring, "start") == 0) { Schedule([this]() { aborted_ = false; - if (chat_state_ == kChatStateIdle || chat_state_ == kChatStateListening) { - SetChatState(kChatStateSpeaking); + if (device_state_ == kDeviceStateIdle || device_state_ == kDeviceStateListening) { + SetDeviceState(kDeviceStateSpeaking); } }); } else if (strcmp(state->valuestring, "stop") == 0) { Schedule([this]() { - if (chat_state_ == kChatStateSpeaking) { + if (device_state_ == kDeviceStateSpeaking) { background_task_.WaitForCompletion(); if (keep_listening_) { protocol_->SendStartListening(kListeningModeAutoStop); - SetChatState(kChatStateListening); + SetDeviceState(kDeviceStateListening); } else { - SetChatState(kChatStateIdle); + SetDeviceState(kDeviceStateIdle); } } }); @@ -378,11 +381,7 @@ void Application::Start() { } }); - // Blink the LED to indicate the device is running - display->SetStatus("待命"); - led_strip->LightOn(kStandby); - - SetChatState(kChatStateIdle); + SetDeviceState(kDeviceStateIdle); } void Application::Schedule(std::function callback) { @@ -433,7 +432,7 @@ void Application::OutputAudio() { std::unique_lock lock(mutex_); if (audio_decode_queue_.empty()) { // Disable the output if there is no audio data for a long time - if (chat_state_ == kChatStateIdle) { + if (device_state_ == kDeviceStateIdle) { auto duration = std::chrono::duration_cast(now - last_output_time_).count(); if (duration > max_silence_seconds) { codec->EnableOutput(false); @@ -442,7 +441,7 @@ void Application::OutputAudio() { return; } - if (chat_state_ == kChatStateListening) { + if (device_state_ == kDeviceStateListening) { audio_decode_queue_.clear(); return; } @@ -513,7 +512,7 @@ void Application::InputAudio() { wake_word_detect_.Feed(data); } #else - if (chat_state_ == kChatStateListening) { + if (device_state_ == kDeviceStateListening) { background_task_.Schedule([this, data = std::move(data)]() mutable { opus_encoder_->Encode(std::move(data), [this](std::vector&& opus) { Schedule([this, opus = std::move(opus)]() { @@ -531,34 +530,32 @@ void Application::AbortSpeaking(AbortReason reason) { protocol_->SendAbortSpeaking(reason); } -void Application::SetChatState(ChatState state) { - if (chat_state_ == state) { +void Application::SetDeviceState(DeviceState state) { + if (device_state_ == state) { return; } - chat_state_ = state; - ESP_LOGI(TAG, "STATE: %s", STATE_STRINGS[chat_state_]); + device_state_ = state; + ESP_LOGI(TAG, "STATE: %s", STATE_STRINGS[device_state_]); // The state is changed, wait for all background tasks to finish background_task_.WaitForCompletion(); auto display = Board::GetInstance().GetDisplay(); - auto led_strip = Board::GetInstance().GetLedStrip(); + auto led = Board::GetInstance().GetLed(); + led->OnStateChanged(); switch (state) { - case kChatStateUnknown: - case kChatStateIdle: - led_strip->LightOff(); + case kDeviceStateUnknown: + case kDeviceStateIdle: display->SetStatus("待命"); display->SetEmotion("neutral"); #ifdef CONFIG_IDF_TARGET_ESP32S3 audio_processor_.Stop(); #endif break; - case kChatStateConnecting: - led_strip->LightOn(kConnecting); + case kDeviceStateConnecting: display->SetStatus("连接中..."); break; - case kChatStateListening: - led_strip->LightOn(kListening); + case kDeviceStateListening: display->SetStatus("聆听中..."); display->SetEmotion("neutral"); ResetDecoder(); @@ -568,20 +565,16 @@ void Application::SetChatState(ChatState state) { #endif UpdateIotStates(); break; - case kChatStateSpeaking: - led_strip->LightOn(kSpeaking); + case kDeviceStateSpeaking: display->SetStatus("说话中..."); ResetDecoder(); #if CONFIG_IDF_TARGET_ESP32S3 audio_processor_.Stop(); #endif break; - case kChatStateUpgrading: - led_strip->LightOn(kUpgrading); - break; default: - ESP_LOGE(TAG, "Invalid chat state: %d", chat_state_); - return; + // Do nothing + break; } } diff --git a/main/application.h b/main/application.h index c31f803..45bdaaf 100644 --- a/main/application.h +++ b/main/application.h @@ -26,13 +26,16 @@ #define AUDIO_INPUT_READY_EVENT (1 << 1) #define AUDIO_OUTPUT_READY_EVENT (1 << 2) -enum ChatState { - kChatStateUnknown, - kChatStateIdle, - kChatStateConnecting, - kChatStateListening, - kChatStateSpeaking, - kChatStateUpgrading +enum DeviceState { + kDeviceStateUnknown, + kDeviceStateStarting, + kDeviceStateWifiConfiguring, + kDeviceStateIdle, + kDeviceStateConnecting, + kDeviceStateListening, + kDeviceStateSpeaking, + kDeviceStateUpgrading, + kDeviceStateFatalError }; #define OPUS_FRAME_DURATION_MS 60 @@ -48,9 +51,10 @@ public: Application& operator=(const Application&) = delete; void Start(); - ChatState GetChatState() const { return chat_state_; } + DeviceState GetDeviceState() const { return device_state_; } + bool IsVoiceDetected() const { return voice_detected_; } void Schedule(std::function callback); - void SetChatState(ChatState state); + void SetDeviceState(DeviceState state); void Alert(const std::string& title, const std::string& message); void AbortSpeaking(AbortReason reason); void ToggleChatState(); @@ -71,9 +75,10 @@ private: std::list> main_tasks_; std::unique_ptr protocol_; EventGroupHandle_t event_group_; - volatile ChatState chat_state_ = kChatStateUnknown; + volatile DeviceState device_state_ = kDeviceStateUnknown; bool keep_listening_ = false; bool aborted_ = false; + bool voice_detected_ = false; std::string last_iot_states_; // Audio encode / decode diff --git a/main/boards/bread-compact-ml307/compact_ml307_board.cc b/main/boards/bread-compact-ml307/compact_ml307_board.cc index 604871b..37a53cf 100644 --- a/main/boards/bread-compact-ml307/compact_ml307_board.cc +++ b/main/boards/bread-compact-ml307/compact_ml307_board.cc @@ -6,7 +6,7 @@ #include "button.h" #include "config.h" #include "iot/thing_manager.h" -#include "led_strip/single_led.h" +#include "led/single_led.h" #include #include @@ -102,9 +102,9 @@ public: InitializeIot(); } - virtual LedStripWrapper* GetLedStrip() override { - static SingleLed led_strip(BUILTIN_LED_GPIO); - return &led_strip; + virtual Led* GetLed() override { + static SingleLed led(BUILTIN_LED_GPIO); + return &led; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/bread-compact-wifi/compact_wifi_board.cc b/main/boards/bread-compact-wifi/compact_wifi_board.cc index 38b2fe1..b1c7824 100644 --- a/main/boards/bread-compact-wifi/compact_wifi_board.cc +++ b/main/boards/bread-compact-wifi/compact_wifi_board.cc @@ -6,7 +6,7 @@ #include "button.h" #include "config.h" #include "iot/thing_manager.h" -#include "led_strip/single_led.h" +#include "led/single_led.h" #include #include @@ -42,7 +42,7 @@ private: void InitializeButtons() { boot_button_.OnClick([this]() { auto& app = Application::GetInstance(); - if (app.GetChatState() == kChatStateUnknown && !WifiStation::GetInstance().IsConnected()) { + if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { ResetWifiConfiguration(); } app.ToggleChatState(); @@ -107,9 +107,9 @@ public: InitializeIot(); } - virtual LedStripWrapper* GetLedStrip() override { - static SingleLed led_strip(BUILTIN_LED_GPIO); - return &led_strip; + virtual Led* GetLed() override { + static SingleLed led(BUILTIN_LED_GPIO); + return &led; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/common/board.cc b/main/boards/common/board.cc index e43b210..b0f6182 100644 --- a/main/boards/common/board.cc +++ b/main/boards/common/board.cc @@ -20,6 +20,10 @@ Display* Board::GetDisplay() { return &display; } +Led* Board::GetLed() { + static NoLed led; + return &led; +} std::string Board::GetJson() { /* diff --git a/main/boards/common/board.h b/main/boards/common/board.h index 6a4bd43..ce08e76 100644 --- a/main/boards/common/board.h +++ b/main/boards/common/board.h @@ -7,7 +7,7 @@ #include #include -#include "led_strip/led_strip_wrapper.h" +#include "led/led.h" void* create_board(); class AudioCodec; @@ -32,7 +32,7 @@ public: virtual void StartNetwork() = 0; virtual ~Board() = default; - virtual LedStripWrapper* GetLedStrip() = 0; + virtual Led* GetLed() = 0; virtual AudioCodec* GetAudioCodec() = 0; virtual Display* GetDisplay(); virtual Http* CreateHttp() = 0; diff --git a/main/boards/common/led.cc b/main/boards/common/led.cc deleted file mode 100644 index 61ac028..0000000 --- a/main/boards/common/led.cc +++ /dev/null @@ -1,62 +0,0 @@ -#include "led.h" -#include "board.h" - -#include -#include - -#define TAG "Led" - -Led::Led(gpio_num_t gpio, uint8_t max_leds) { - if (gpio == GPIO_NUM_NC) { - ESP_LOGI(TAG, "Builtin LED not connected"); - return; - } - - led_strip_config_t strip_config = {}; - strip_config.strip_gpio_num = gpio; - strip_config.max_leds = max_leds; - strip_config.led_pixel_format = LED_PIXEL_FORMAT_GRB; - strip_config.led_model = LED_MODEL_WS2812; - - led_strip_rmt_config_t rmt_config = {}; - rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz - - max_leds_ = max_leds; - ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_)); - - led_strip_clear(led_strip_); - SetGrey(); -} - -Led::~Led() { - if (led_strip_ != nullptr) { - led_strip_del(led_strip_); - } -} - -void Led::SetColor(uint8_t r, uint8_t g, uint8_t b) { - r_ = r; - g_ = g; - b_ = b; -} - -void Led::TurnOn() { - if (led_strip_ == nullptr) { - return; - } - - std::lock_guard lock(mutex_); - for (int i = 0; i < max_leds_; i++) { - led_strip_set_pixel(led_strip_, i, r_, g_, b_); - } - led_strip_refresh(led_strip_); -} - -void Led::TurnOff() { - if (led_strip_ == nullptr) { - return; - } - - std::lock_guard lock(mutex_); - led_strip_clear(led_strip_); -} diff --git a/main/boards/common/led.h b/main/boards/common/led.h deleted file mode 100644 index 8cd1858..0000000 --- a/main/boards/common/led.h +++ /dev/null @@ -1,38 +0,0 @@ -#ifndef _LED_H_ -#define _LED_H_ - -#include -#include -#include -#include - - -#define DEFAULT_BRIGHTNESS 4 -#define HIGH_BRIGHTNESS 16 -#define LOW_BRIGHTNESS 2 - -class Led { -public: - Led(gpio_num_t gpio, uint8_t max_leds); - ~Led(); - - led_strip_handle_t led_strip() { return led_strip_; } - uint8_t max_leds() { return max_leds_; } - - void TurnOn(); - void TurnOff(); - void SetColor(uint8_t r, uint8_t g, uint8_t b); - void SetWhite(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(brightness, brightness, brightness); } - void SetGrey(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(brightness, brightness, brightness); } - void SetRed(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(brightness, 0, 0); } - void SetGreen(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(0, brightness, 0); } - void SetBlue(uint8_t brightness = DEFAULT_BRIGHTNESS) { SetColor(0, 0, brightness); } - -private: - std::mutex mutex_; - uint8_t max_leds_ = -1; - led_strip_handle_t led_strip_ = nullptr; - uint8_t r_ = 0, g_ = 0, b_ = 0; -}; - -#endif // _LED_H_ diff --git a/main/boards/common/ml307_board.cc b/main/boards/common/ml307_board.cc index 3efd8bc..db3665d 100644 --- a/main/boards/common/ml307_board.cc +++ b/main/boards/common/ml307_board.cc @@ -46,7 +46,7 @@ void Ml307Board::StartNetwork() { modem_.OnMaterialReady([this, &application]() { ESP_LOGI(TAG, "ML307 material ready"); application.Schedule([this, &application]() { - application.SetChatState(kChatStateIdle); + application.SetDeviceState(kDeviceStateIdle); WaitForNetworkReady(); }); }); diff --git a/main/boards/common/wifi_board.cc b/main/boards/common/wifi_board.cc index 27deca2..cee969e 100644 --- a/main/boards/common/wifi_board.cc +++ b/main/boards/common/wifi_board.cc @@ -38,14 +38,14 @@ static std::string rssi_to_string(int rssi) { void WifiBoard::StartNetwork() { auto& application = Application::GetInstance(); auto display = Board::GetInstance().GetDisplay(); - auto led_strip = Board::GetInstance().GetLedStrip(); // Try to connect to WiFi, if failed, launch the WiFi configuration AP auto& wifi_station = WifiStation::GetInstance(); display->SetStatus(std::string("正在连接 ") + wifi_station.GetSsid()); wifi_station.Start(); if (!wifi_station.IsConnected()) { - led_strip->LightOn(kConnecting); + application.SetDeviceState(kDeviceStateWifiConfiguring); + auto& wifi_ap = WifiConfigurationAp::GetInstance(); wifi_ap.SetSsidPrefix("Xiaozhi"); wifi_ap.Start(); diff --git a/main/boards/esp-box-3/esp_box3_board.cc b/main/boards/esp-box-3/esp_box3_board.cc index c47c620..5ef418f 100644 --- a/main/boards/esp-box-3/esp_box3_board.cc +++ b/main/boards/esp-box-3/esp_box3_board.cc @@ -7,7 +7,6 @@ #include "button.h" #include "config.h" #include "iot/thing_manager.h" -#include "led_strip/single_led.h" #include #include @@ -214,11 +213,6 @@ public: InitializeButtons(); InitializeIot(); } - - virtual LedStripWrapper* GetLedStrip() override { - static SingleLed led_strip(GPIO_NUM_NC); - return &led_strip; - } virtual AudioCodec* GetAudioCodec() override { static BoxAudioCodec* audio_codec = nullptr; diff --git a/main/boards/kevin-box-1/kevin_box_board.cc b/main/boards/kevin-box-1/kevin_box_board.cc index ba79f4f..f6a9c55 100644 --- a/main/boards/kevin-box-1/kevin_box_board.cc +++ b/main/boards/kevin-box-1/kevin_box_board.cc @@ -136,9 +136,9 @@ public: InitializeIot(); } - virtual LedStripWrapper* GetLedStrip() override { - static SingleLed led_strip(BUILTIN_LED_GPIO); - return &led_strip; + virtual Led* GetLed() override { + static SingleLed led(BUILTIN_LED_GPIO); + return &led; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/kevin-box-2/kevin_box_board.cc b/main/boards/kevin-box-2/kevin_box_board.cc index e65be33..5ed8a77 100644 --- a/main/boards/kevin-box-2/kevin_box_board.cc +++ b/main/boards/kevin-box-2/kevin_box_board.cc @@ -6,7 +6,7 @@ #include "config.h" #include "axp2101.h" #include "iot/thing_manager.h" -#include "led_strip/single_led.h" +#include "led/single_led.h" #include #include @@ -46,7 +46,7 @@ private: // 电池放电模式下,如果待机超过一定时间,则自动关机 const int seconds_to_shutdown = 600; static int seconds = 0; - if (Application::GetInstance().GetChatState() != kChatStateIdle) { + if (Application::GetInstance().GetDeviceState() != kDeviceStateIdle) { seconds = 0; return; } @@ -180,9 +180,9 @@ public: InitializeIot(); } - virtual LedStripWrapper* GetLedStrip() override { - static SingleLed led_strip(BUILTIN_LED_GPIO); - return &led_strip; + virtual Led* GetLed() override { + static SingleLed led(BUILTIN_LED_GPIO); + return &led; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/kevin-c3/kevin_box_board.cc b/main/boards/kevin-c3/kevin_box_board.cc index fac2805..caf9cb8 100644 --- a/main/boards/kevin-c3/kevin_box_board.cc +++ b/main/boards/kevin-c3/kevin_box_board.cc @@ -4,7 +4,7 @@ #include "button.h" #include "config.h" #include "iot/thing_manager.h" -#include "led_strip/multiple_led.h" +#include "led/circular_strip.h" #include #include @@ -38,7 +38,7 @@ private: void InitializeButtons() { boot_button_.OnClick([this]() { auto& app = Application::GetInstance(); - if (app.GetChatState() == kChatStateUnknown && !WifiStation::GetInstance().IsConnected()) { + if (app.GetDeviceState() == kDeviceStateUnknown && !WifiStation::GetInstance().IsConnected()) { ResetWifiConfiguration(); } }); @@ -66,9 +66,9 @@ public: InitializeIot(); } - virtual LedStripWrapper* GetLedStrip() override { - static MultipleLed led_strip(BUILTIN_LED_GPIO, 8); - return &led_strip; + virtual Led* GetLed() override { + static CircularStrip led(BUILTIN_LED_GPIO, 8); + return &led; } virtual AudioCodec* GetAudioCodec() override { diff --git a/main/boards/lichuang-dev/lichuang_dev_board.cc b/main/boards/lichuang-dev/lichuang_dev_board.cc index 257fee0..996e43b 100644 --- a/main/boards/lichuang-dev/lichuang_dev_board.cc +++ b/main/boards/lichuang-dev/lichuang_dev_board.cc @@ -6,7 +6,6 @@ #include "config.h" #include "i2c_device.h" #include "iot/thing_manager.h" -#include "led_strip/single_led.h" #include #include @@ -74,7 +73,7 @@ private: void InitializeButtons() { boot_button_.OnClick([this]() { auto& app = Application::GetInstance(); - if (app.GetChatState() == kChatStateUnknown && !WifiStation::GetInstance().IsConnected()) { + if (app.GetDeviceState() == kDeviceStateUnknown && !WifiStation::GetInstance().IsConnected()) { ResetWifiConfiguration(); } }); @@ -135,11 +134,6 @@ public: InitializeIot(); } - virtual LedStripWrapper* GetLedStrip() override { - static SingleLed led_strip(GPIO_NUM_NC); - return &led_strip; - } - virtual AudioCodec* GetAudioCodec() override { static BoxAudioCodec* audio_codec = nullptr; if (audio_codec == nullptr) { diff --git a/main/display/display.cc b/main/display/display.cc index 5b21ced..50f5190 100644 --- a/main/display/display.cc +++ b/main/display/display.cc @@ -121,8 +121,8 @@ void Display::Update() { } // 仅在聊天状态为空闲时,读取网络状态(避免升级时占用 UART 资源) - auto chat_state = Application::GetInstance().GetChatState(); - if (chat_state == kChatStateIdle || chat_state == kChatStateUnknown) { + auto device_state = Application::GetInstance().GetDeviceState(); + if (device_state == kDeviceStateIdle || device_state == kDeviceStateStarting) { icon = board.GetNetworkStateIcon(); if (network_icon_ != icon) { network_icon_ = icon; diff --git a/main/led/circular_strip.cc b/main/led/circular_strip.cc new file mode 100644 index 0000000..6be6a08 --- /dev/null +++ b/main/led/circular_strip.cc @@ -0,0 +1,218 @@ +#include "circular_strip.h" +#include "application.h" +#include + +#define TAG "CircularStrip" + +#define DEFAULT_BRIGHTNESS 4 +#define HIGH_BRIGHTNESS 16 +#define LOW_BRIGHTNESS 1 + +#define BLINK_INFINITE -1 + +CircularStrip::CircularStrip(gpio_num_t gpio, uint8_t max_leds) : max_leds_(max_leds) { + // If the gpio is not connected, you should use NoLed class + assert(gpio != GPIO_NUM_NC); + + colors_.resize(max_leds_); + + led_strip_config_t strip_config = {}; + strip_config.strip_gpio_num = gpio; + strip_config.max_leds = max_leds_; + strip_config.led_pixel_format = LED_PIXEL_FORMAT_GRB; + strip_config.led_model = LED_MODEL_WS2812; + + led_strip_rmt_config_t rmt_config = {}; + rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz + + ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_)); + led_strip_clear(led_strip_); + + esp_timer_create_args_t strip_timer_args = { + .callback = [](void *arg) { + auto strip = static_cast(arg); + std::lock_guard lock(strip->mutex_); + if (strip->strip_callback_ != nullptr) { + strip->strip_callback_(); + } + }, + .arg = this, + .dispatch_method = ESP_TIMER_TASK, + .name = "Strip Timer", + .skip_unhandled_events = false, + }; + ESP_ERROR_CHECK(esp_timer_create(&strip_timer_args, &strip_timer_)); +} + +CircularStrip::~CircularStrip() { + esp_timer_stop(strip_timer_); + if (led_strip_ != nullptr) { + led_strip_del(led_strip_); + } +} + + +void CircularStrip::StaticColor(StripColor color) { + std::lock_guard lock(mutex_); + esp_timer_stop(strip_timer_); + for (int i = 0; i < max_leds_; i++) { + colors_[i] = color; + led_strip_set_pixel(led_strip_, i, color.red, color.green, color.blue); + } + led_strip_refresh(led_strip_); +} + +void CircularStrip::Blink(StripColor color, int interval_ms) { + for (int i = 0; i < max_leds_; i++) { + colors_[i] = color; + } + StartStripTask(interval_ms, [this]() { + static bool on = true; + if (on) { + for (int i = 0; i < max_leds_; i++) { + led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue); + } + led_strip_refresh(led_strip_); + } else { + led_strip_clear(led_strip_); + } + on = !on; + }); +} + +void CircularStrip::FadeOut(int interval_ms) { + StartStripTask(interval_ms, [this]() { + bool all_off = true; + for (int i = 0; i < max_leds_; i++) { + colors_[i].red /= 2; + colors_[i].green /= 2; + colors_[i].blue /= 2; + if (colors_[i].red != 0 || colors_[i].green != 0 || colors_[i].blue != 0) { + all_off = false; + } + led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue); + } + if (all_off) { + led_strip_clear(led_strip_); + esp_timer_stop(strip_timer_); + } else { + led_strip_refresh(led_strip_); + } + }); +} + +void CircularStrip::Breathe(StripColor low, StripColor high, int interval_ms) { + StartStripTask(interval_ms, [this, low, high]() { + static bool increase = true; + static StripColor color = low; + if (increase) { + if (color.red < high.red) { + color.red++; + } + if (color.green < high.green) { + color.green++; + } + if (color.blue < high.blue) { + color.blue++; + } + if (color.red == high.red && color.green == high.green && color.blue == high.blue) { + increase = false; + } + } else { + if (color.red > low.red) { + color.red--; + } + if (color.green > low.green) { + color.green--; + } + if (color.blue > low.blue) { + color.blue--; + } + if (color.red == low.red && color.green == low.green && color.blue == low.blue) { + increase = true; + } + } + for (int i = 0; i < max_leds_; i++) { + led_strip_set_pixel(led_strip_, i, color.red, color.green, color.blue); + } + led_strip_refresh(led_strip_); + }); +} + +void CircularStrip::Scroll(StripColor low, StripColor high, int length, int interval_ms) { + for (int i = 0; i < max_leds_; i++) { + colors_[i] = low; + } + StartStripTask(interval_ms, [this, low, high, length]() { + static int offset = 0; + for (int i = 0; i < max_leds_; i++) { + colors_[i] = low; + } + for (int j = 0; j < length; j++) { + int i = (offset + j) % max_leds_; + colors_[i] = high; + } + for (int i = 0; i < max_leds_; i++) { + led_strip_set_pixel(led_strip_, i, colors_[i].red, colors_[i].green, colors_[i].blue); + } + led_strip_refresh(led_strip_); + offset = (offset + 1) % max_leds_; + }); +} + +void CircularStrip::StartStripTask(int interval_ms, std::function cb) { + if (led_strip_ == nullptr) { + return; + } + + std::lock_guard lock(mutex_); + esp_timer_stop(strip_timer_); + + strip_callback_ = cb; + esp_timer_start_periodic(strip_timer_, interval_ms * 1000); +} + + +void CircularStrip::OnStateChanged() { + auto& app = Application::GetInstance(); + auto device_state = app.GetDeviceState(); + switch (device_state) { + case kDeviceStateStarting: { + StripColor low = { 0, 0, 0 }; + StripColor high = { LOW_BRIGHTNESS, LOW_BRIGHTNESS, DEFAULT_BRIGHTNESS }; + Scroll(low, high, 3, 100); + break; + } + case kDeviceStateWifiConfiguring: { + StripColor color = { LOW_BRIGHTNESS, LOW_BRIGHTNESS, DEFAULT_BRIGHTNESS }; + Blink(color, 500); + break; + } + case kDeviceStateIdle: + FadeOut(50); + break; + case kDeviceStateConnecting: { + StripColor color = { LOW_BRIGHTNESS, LOW_BRIGHTNESS, DEFAULT_BRIGHTNESS }; + StaticColor(color); + break; + } + case kDeviceStateListening: { + StripColor color = { DEFAULT_BRIGHTNESS, LOW_BRIGHTNESS, LOW_BRIGHTNESS }; + StaticColor(color); + break; + } + case kDeviceStateSpeaking: { + StripColor color = { LOW_BRIGHTNESS, DEFAULT_BRIGHTNESS, LOW_BRIGHTNESS }; + StaticColor(color); + break; + } + case kDeviceStateUpgrading: { + StripColor color = { LOW_BRIGHTNESS, DEFAULT_BRIGHTNESS, LOW_BRIGHTNESS }; + Blink(color, 100); + break; + } + default: + ESP_LOGE(TAG, "Invalid led strip event: %d", device_state); + return; + } +} diff --git a/main/led/circular_strip.h b/main/led/circular_strip.h new file mode 100644 index 0000000..5c684dd --- /dev/null +++ b/main/led/circular_strip.h @@ -0,0 +1,44 @@ +#ifndef _CIRCULAR_STRIP_H_ +#define _CIRCULAR_STRIP_H_ + +#include "led.h" +#include +#include +#include +#include +#include +#include + +struct StripColor { + uint8_t red = 0, green = 0, blue = 0; +}; + +class CircularStrip : public Led { +public: + CircularStrip(gpio_num_t gpio, uint8_t max_leds); + virtual ~CircularStrip(); + + void OnStateChanged() override; + +private: + std::mutex mutex_; + TaskHandle_t blink_task_ = nullptr; + led_strip_handle_t led_strip_ = nullptr; + int max_leds_ = 0; + std::vector colors_; + int blink_counter_ = 0; + int blink_interval_ms_ = 0; + esp_timer_handle_t strip_timer_ = nullptr; + std::function strip_callback_ = nullptr; + + void StartStripTask(int interval_ms, std::function cb); + + void StaticColor(StripColor color); + void Blink(StripColor color, int interval_ms); + void Breathe(StripColor low, StripColor high, int interval_ms); + void Rainbow(StripColor low, StripColor high, int interval_ms); + void Scroll(StripColor low, StripColor high, int length, int interval_ms); + void FadeOut(int interval_ms); +}; + +#endif // _CIRCULAR_STRIP_H_ diff --git a/main/led_strip/led_strip_wrapper.cc b/main/led/led.cc similarity index 96% rename from main/led_strip/led_strip_wrapper.cc rename to main/led/led.cc index d631445..bc86ab1 100644 --- a/main/led_strip/led_strip_wrapper.cc +++ b/main/led/led.cc @@ -1,13 +1,13 @@ -#include "led_strip_wrapper.h" +#include "led.h" #include "board.h" #include #include #include -#define TAG "LedStripWrapper" +#define TAG "Led" -LedStripWrapper::LedStripWrapper(gpio_num_t gpio, uint8_t max_leds) { +Led::Led(gpio_num_t gpio, uint8_t max_leds) { if (gpio == GPIO_NUM_NC) { ESP_LOGI(TAG, "Builtin LED not connected"); return; @@ -198,9 +198,9 @@ void LedStripWrapper::BreathLight(LedBasicColor color, uint32_t interval_ms) { } if (increase) { - brightness += 2; + brightness += 1; } else { - brightness -= 2; + brightness -= 1; } }; esp_timer_start_periodic(led_strip_timer_, interval_ms * 1000); diff --git a/main/led/led.h b/main/led/led.h new file mode 100644 index 0000000..251fd6a --- /dev/null +++ b/main/led/led.h @@ -0,0 +1,17 @@ +#ifndef _LED_H_ +#define _LED_H_ + +class Led { +public: + virtual ~Led() = default; + // Set the led state based on the device state + virtual void OnStateChanged() = 0; +}; + + +class NoLed : public Led { +public: + virtual void OnStateChanged() override {} +}; + +#endif // _LED_H_ diff --git a/main/led/single_led.cc b/main/led/single_led.cc new file mode 100644 index 0000000..d4df70b --- /dev/null +++ b/main/led/single_led.cc @@ -0,0 +1,158 @@ +#include "single_led.h" +#include "application.h" +#include + +#define TAG "SingleLed" + +#define DEFAULT_BRIGHTNESS 4 +#define HIGH_BRIGHTNESS 16 +#define LOW_BRIGHTNESS 2 + +#define BLINK_INFINITE -1 + + +SingleLed::SingleLed(gpio_num_t gpio) { + // If the gpio is not connected, you should use NoLed class + assert(gpio != GPIO_NUM_NC); + + led_strip_config_t strip_config = {}; + strip_config.strip_gpio_num = gpio; + strip_config.max_leds = 1; + strip_config.led_pixel_format = LED_PIXEL_FORMAT_GRB; + strip_config.led_model = LED_MODEL_WS2812; + + led_strip_rmt_config_t rmt_config = {}; + rmt_config.resolution_hz = 10 * 1000 * 1000; // 10MHz + + ESP_ERROR_CHECK(led_strip_new_rmt_device(&strip_config, &rmt_config, &led_strip_)); + led_strip_clear(led_strip_); + + 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_)); +} + +SingleLed::~SingleLed() { + esp_timer_stop(blink_timer_); + if (led_strip_ != nullptr) { + led_strip_del(led_strip_); + } +} + + +void SingleLed::SetColor(uint8_t r, uint8_t g, uint8_t b) { + r_ = r; + g_ = g; + b_ = b; +} + +void SingleLed::TurnOn() { + if (led_strip_ == nullptr) { + return; + } + + std::lock_guard lock(mutex_); + esp_timer_stop(blink_timer_); + led_strip_set_pixel(led_strip_, 0, r_, g_, b_); + led_strip_refresh(led_strip_); +} + +void SingleLed::TurnOff() { + if (led_strip_ == nullptr) { + return; + } + + std::lock_guard lock(mutex_); + esp_timer_stop(blink_timer_); + led_strip_clear(led_strip_); +} + +void SingleLed::BlinkOnce() { + Blink(1, 100); +} + +void SingleLed::Blink(int times, int interval_ms) { + StartBlinkTask(times, interval_ms); +} + +void SingleLed::StartContinuousBlink(int interval_ms) { + StartBlinkTask(BLINK_INFINITE, interval_ms); +} + +void SingleLed::StartBlinkTask(int times, int interval_ms) { + if (led_strip_ == nullptr) { + return; + } + + std::lock_guard lock(mutex_); + esp_timer_stop(blink_timer_); + + blink_counter_ = times * 2; + blink_interval_ms_ = interval_ms; + esp_timer_start_periodic(blink_timer_, interval_ms * 1000); +} + +void SingleLed::OnBlinkTimer() { + std::lock_guard lock(mutex_); + blink_counter_--; + if (blink_counter_ & 1) { + led_strip_set_pixel(led_strip_, 0, r_, g_, b_); + led_strip_refresh(led_strip_); + } else { + led_strip_clear(led_strip_); + + if (blink_counter_ == 0) { + esp_timer_stop(blink_timer_); + } + } +} + + +void SingleLed::OnStateChanged() { + auto& app = Application::GetInstance(); + auto device_state = app.GetDeviceState(); + switch (device_state) { + case kDeviceStateStarting: + SetColor(0, 0, DEFAULT_BRIGHTNESS); + StartContinuousBlink(100); + break; + case kDeviceStateWifiConfiguring: + SetColor(0, 0, DEFAULT_BRIGHTNESS); + StartContinuousBlink(500); + break; + case kDeviceStateIdle: + TurnOff(); + break; + case kDeviceStateConnecting: + SetColor(0, 0, DEFAULT_BRIGHTNESS); + TurnOn(); + break; + case kDeviceStateListening: + if (app.IsVoiceDetected()) { + SetColor(HIGH_BRIGHTNESS, 0, 0); + } else { + SetColor(LOW_BRIGHTNESS, 0, 0); + } + TurnOn(); + break; + case kDeviceStateSpeaking: + SetColor(0, DEFAULT_BRIGHTNESS, 0); + TurnOn(); + break; + case kDeviceStateUpgrading: + SetColor(0, DEFAULT_BRIGHTNESS, 0); + StartContinuousBlink(100); + break; + default: + ESP_LOGE(TAG, "Invalid led strip event: %d", device_state); + return; + } +} diff --git a/main/led/single_led.h b/main/led/single_led.h new file mode 100644 index 0000000..b949f74 --- /dev/null +++ b/main/led/single_led.h @@ -0,0 +1,38 @@ +#ifndef _SINGLE_LED_H_ +#define _SINGLE_LED_H_ + +#include "led.h" +#include +#include +#include +#include +#include + +class SingleLed : public Led { +public: + SingleLed(gpio_num_t gpio); + virtual ~SingleLed(); + + void OnStateChanged() override; + +private: + std::mutex mutex_; + TaskHandle_t blink_task_ = nullptr; + led_strip_handle_t led_strip_ = nullptr; + uint8_t r_ = 0, g_ = 0, b_ = 0; + int blink_counter_ = 0; + int blink_interval_ms_ = 0; + esp_timer_handle_t blink_timer_ = nullptr; + + 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 SetColor(uint8_t r, uint8_t g, uint8_t b); +}; + +#endif // _SINGLE_LED_H_ diff --git a/main/led_strip/led_strip_wrapper.h b/main/led_strip/led_strip_wrapper.h deleted file mode 100644 index 92ca93a..0000000 --- a/main/led_strip/led_strip_wrapper.h +++ /dev/null @@ -1,58 +0,0 @@ -#ifndef _LED_STRIP_WRAPPER_H_ -#define _LED_STRIP_WRAPPER_H_ - -#include "led.h" - -#define COUNTER_INFINITE -1 - -enum LedStripEvent { - kStartup, - kListening, - kListeningAndSpeaking, - kSpeaking, - kStandby, - kConnecting, - kUpgrading, -}; - -enum LedBasicColor { - kLedColorWhite, - kLedColorGrey, - kLedColorRed, - kLedColorGreen, - kLedColorBlue, -}; - -typedef std::function TimerCallback; - -class LedStripWrapper { -private: - Led* led_ = nullptr; - std::mutex mutex_; - - uint32_t counter_ = 0; - esp_timer_handle_t led_strip_timer_ = nullptr; - TimerCallback timer_callback_; - - void SetLedBasicColor(LedBasicColor color, uint8_t brightness); - void SetLedStripBasicColor(uint8_t index, LedBasicColor color, uint8_t brightness = DEFAULT_BRIGHTNESS); - void StartBlinkTask(uint32_t times, uint32_t interval_ms); - void OnBlinkTimer(); - -public: - LedStripWrapper(gpio_num_t gpio, uint8_t max_leds); - virtual ~LedStripWrapper(); - - void LightOff(); - virtual void LightOn(LedStripEvent event) = 0; - -protected: - void BlinkOnce(LedBasicColor color, uint8_t brightness = DEFAULT_BRIGHTNESS); - void Blink(LedBasicColor color, uint32_t times, uint32_t interval_ms, uint8_t brightness = DEFAULT_BRIGHTNESS); - void ContinuousBlink(LedBasicColor color, uint32_t interval_ms, uint8_t brightness = DEFAULT_BRIGHTNESS); - void StaticLight(LedBasicColor color, uint8_t brightness = DEFAULT_BRIGHTNESS); - void ChasingLight(LedBasicColor base_color, LedBasicColor color, uint32_t interval_ms, uint8_t brightness = DEFAULT_BRIGHTNESS); - void BreathLight(LedBasicColor color, uint32_t interval_ms); -}; - -#endif // _LED_STRIP_WRAPPER_H_ diff --git a/main/led_strip/multiple_led.cc b/main/led_strip/multiple_led.cc deleted file mode 100644 index 8b261d4..0000000 --- a/main/led_strip/multiple_led.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include "multiple_led.h" -#include - -#define TAG "MultipleLed" - -MultipleLed::MultipleLed(gpio_num_t gpio, uint8_t max_leds) : LedStripWrapper(gpio, max_leds) { -} - -MultipleLed::~MultipleLed() { -} - -void MultipleLed::LightOn(LedStripEvent event) { - switch (event) { - case kStartup: - ChasingLight(kLedColorWhite, kLedColorBlue, 100, HIGH_BRIGHTNESS); - break; - case kListeningAndSpeaking: - BreathLight(kLedColorRed, 100); - break; - case kListening: - BreathLight(kLedColorRed, 100); - break; - case kSpeaking: - StaticLight(kLedColorGreen, HIGH_BRIGHTNESS); - break; - case kStandby: - BlinkOnce(kLedColorGreen); - break; - case kConnecting: - Blink(kLedColorBlue, 1000, 500); - break; - case kUpgrading: - ContinuousBlink(kLedColorGreen, 100); - break; - default: - ESP_LOGE(TAG, "Invalid led strip event: %d", event); - return; - } -} diff --git a/main/led_strip/multiple_led.h b/main/led_strip/multiple_led.h deleted file mode 100644 index 14cacd4..0000000 --- a/main/led_strip/multiple_led.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _LED_STRIP_EFFECT_V2_H_ -#define _LED_STRIP_EFFECT_V2_H_ - -#include "led_strip_wrapper.h" - -class MultipleLed : public LedStripWrapper { -public: - MultipleLed(gpio_num_t gpio, uint8_t max_leds); - virtual ~MultipleLed(); - - void LightOn(LedStripEvent event) override; -}; - -#endif // _LED_STRIP_EFFECT_V2_H_ diff --git a/main/led_strip/single_led.cc b/main/led_strip/single_led.cc deleted file mode 100644 index fd46791..0000000 --- a/main/led_strip/single_led.cc +++ /dev/null @@ -1,39 +0,0 @@ -#include "single_led.h" -#include - -#define TAG "SingleLed" - -SingleLed::SingleLed(gpio_num_t gpio) : LedStripWrapper(gpio, 1) { -} - -SingleLed::~SingleLed() { -} - -void SingleLed::LightOn(LedStripEvent event) { - switch (event) { - case kStartup: - ContinuousBlink(kLedColorBlue, 100); - break; - case kListeningAndSpeaking: - StaticLight(kLedColorRed, HIGH_BRIGHTNESS); - break; - case kListening: - StaticLight(kLedColorRed, LOW_BRIGHTNESS); - break; - case kSpeaking: - StaticLight(kLedColorGreen, HIGH_BRIGHTNESS); - break; - case kStandby: - BlinkOnce(kLedColorGreen); - break; - case kConnecting: - Blink(kLedColorBlue, 1000, 500); - break; - case kUpgrading: - ContinuousBlink(kLedColorGreen, 100); - break; - default: - ESP_LOGE(TAG, "Invalid led strip event: %d", event); - return; - } -} diff --git a/main/led_strip/single_led.h b/main/led_strip/single_led.h deleted file mode 100644 index 1d64ea3..0000000 --- a/main/led_strip/single_led.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _LED_STRIP_EFFECT_V1_H_ -#define _LED_STRIP_EFFECT_V1_H_ - -#include "led_strip_wrapper.h" - -class SingleLed : public LedStripWrapper { -public: - SingleLed(gpio_num_t gpio); - virtual ~SingleLed(); - - void LightOn(LedStripEvent event) override; -}; - -#endif // _LED_STRIP_EFFECT_V1_H_ From b94c8a6e8b462fa47447ef73a46f31c06700420c Mon Sep 17 00:00:00 2001 From: Terrence Date: Sun, 5 Jan 2025 21:20:30 +0800 Subject: [PATCH 4/5] rename St7789 to LCD --- CMakeLists.txt | 2 +- main/CMakeLists.txt | 2 +- main/application.cc | 1 + main/audio_codecs/no_audio_codec.cc | 8 +- main/boards/common/board.h | 2 +- main/boards/esp-box-3/esp_box3_board.cc | 62 +++++---------- .../boards/lichuang-dev/lichuang_dev_board.cc | 6 +- .../boards/m5stack-core-s3/m5stack_core_s3.cc | 37 ++++++--- .../magiclick-2p4/magiclick_2p4_board.cc | 23 +++--- .../{st7789_display.cc => lcd_display.cc} | 78 +++++++------------ .../{st7789_display.h => lcd_display.h} | 12 +-- main/display/ssd1306_display.cc | 2 +- 12 files changed, 105 insertions(+), 130 deletions(-) rename main/display/{st7789_display.cc => lcd_display.cc} (81%) rename main/display/{st7789_display.h => lcd_display.h} (85%) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd1762e..5536104 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) -set(PROJECT_VER "0.9.7") +set(PROJECT_VER "0.9.8") include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(xiaozhi) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 6eb69dc..2e4d72a 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -7,7 +7,7 @@ set(SOURCES "audio_codecs/audio_codec.cc" "led/circular_strip.cc" "display/display.cc" "display/no_display.cc" - "display/st7789_display.cc" + "display/lcd_display.cc" "display/ssd1306_display.cc" "protocols/protocol.cc" "protocols/mqtt_protocol.cc" diff --git a/main/application.cc b/main/application.cc index d4d6b78..876d574 100644 --- a/main/application.cc +++ b/main/application.cc @@ -548,6 +548,7 @@ void Application::SetDeviceState(DeviceState state) { case kDeviceStateIdle: display->SetStatus("待命"); display->SetEmotion("neutral"); + display->SetChatMessage("", ""); #ifdef CONFIG_IDF_TARGET_ESP32S3 audio_processor_.Stop(); #endif diff --git a/main/audio_codecs/no_audio_codec.cc b/main/audio_codecs/no_audio_codec.cc index 434232e..d8f6452 100644 --- a/main/audio_codecs/no_audio_codec.cc +++ b/main/audio_codecs/no_audio_codec.cc @@ -77,8 +77,8 @@ NoAudioCodec::NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_n i2s_chan_config_t chan_cfg = { .id = (i2s_port_t)0, .role = I2S_ROLE_MASTER, - .dma_desc_num = 6, - .dma_frame_num = 240, + .dma_desc_num = 2, + .dma_frame_num = 240 * 3, .auto_clear_after_cb = true, .auto_clear_before_cb = false, .intr_priority = 0, @@ -138,8 +138,8 @@ NoAudioCodec::NoAudioCodec(int input_sample_rate, int output_sample_rate, gpio_n // Create a new channel for speaker i2s_chan_config_t tx_chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG((i2s_port_t)1, I2S_ROLE_MASTER); - tx_chan_cfg.dma_desc_num = 6; - tx_chan_cfg.dma_frame_num = 240; + tx_chan_cfg.dma_desc_num = 2; + tx_chan_cfg.dma_frame_num = 240 * 3; tx_chan_cfg.auto_clear_after_cb = true; tx_chan_cfg.auto_clear_before_cb = false; tx_chan_cfg.intr_priority = 0; diff --git a/main/boards/common/board.h b/main/boards/common/board.h index ce08e76..bf10a78 100644 --- a/main/boards/common/board.h +++ b/main/boards/common/board.h @@ -32,7 +32,7 @@ public: virtual void StartNetwork() = 0; virtual ~Board() = default; - virtual Led* GetLed() = 0; + virtual Led* GetLed(); virtual AudioCodec* GetAudioCodec() = 0; virtual Display* GetDisplay(); virtual Http* CreateHttp() = 0; diff --git a/main/boards/esp-box-3/esp_box3_board.cc b/main/boards/esp-box-3/esp_box3_board.cc index 5ef418f..1b0e274 100644 --- a/main/boards/esp-box-3/esp_box3_board.cc +++ b/main/boards/esp-box-3/esp_box3_board.cc @@ -1,6 +1,6 @@ #include "wifi_board.h" #include "audio_codecs/box_audio_codec.h" -#include "display/st7789_display.h" +#include "display/lcd_display.h" #include "esp_lcd_ili9341.h" #include "font_awesome_symbols.h" #include "application.h" @@ -42,7 +42,7 @@ static const ili9341_lcd_init_cmd_t vendor_specific_init[] = { }; // Example Display and UI overwrite in different board -class Ili9341Display : public St7789Display { +class Ili9341Display : public LcdDisplay { private: lv_obj_t *user_messge_label_ = nullptr; lv_obj_t *ai_messge_label_ = nullptr; @@ -50,44 +50,27 @@ public: Ili9341Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, gpio_num_t backlight_pin, bool backlight_output_invert, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy) - : St7789Display(panel_io, panel, backlight_pin, backlight_output_invert, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {} - - void SetStatus(const std::string &status) override - { - if (status_label_ == nullptr) { - return; - } - if(status=="待命") - { - SetChatMessage(" "," "); - } - DisplayLockGuard lock(this); - lv_label_set_text(status_label_, status.c_str()); - } + : LcdDisplay(panel_io, panel, backlight_pin, backlight_output_invert, width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy) {} void SetChatMessage(const std::string &role, const std::string &content) override { if (ai_messge_label_== nullptr || user_messge_label_== nullptr) { return; } + DisplayLockGuard lock(this); - ESP_LOGI(TAG,"role:%s",role.c_str()); - if(role=="assistant") - { - std::string new_content = "AI:" + content; + ESP_LOGI(TAG, "role:%s", role.c_str()); + if(role == "assistant") { + std::string new_content = "AI: " + content; lv_label_set_text(ai_messge_label_, new_content.c_str()); - } - else if(role=="user") - { - std::string new_content = "User:" + content; - lv_label_set_text(user_messge_label_, new_content.c_str()); - } - else{ - std::string new_content = "AI:"; - lv_label_set_text(ai_messge_label_, new_content.c_str()); - new_content="User:"; + } else if(role == "user") { + std::string new_content = "User: " + content; lv_label_set_text(user_messge_label_, new_content.c_str()); + } else{ + lv_label_set_text(ai_messge_label_, "AI: "); + lv_label_set_text(user_messge_label_, "User: "); } } + void SetupUI() override { DisplayLockGuard lock(this); @@ -97,23 +80,23 @@ public: lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_1, 0); lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP); - lv_obj_align(emotion_label_,LV_ALIGN_TOP_MID, 0, -10); // 左侧居中,向右偏移10个单位 + lv_obj_align(emotion_label_, LV_ALIGN_TOP_MID, 0, -10); // 左侧居中,向右偏移10个单位 static lv_style_t style_msg; lv_style_init(&style_msg); - lv_style_set_width(&style_msg, LV_HOR_RES-25); + lv_style_set_width(&style_msg, LV_HOR_RES - 25); user_messge_label_ = lv_label_create(content_); lv_obj_set_style_text_font(user_messge_label_, &font_puhui_14_1, 0); - lv_label_set_text(user_messge_label_, "User:"); + lv_label_set_text(user_messge_label_, "User: "); lv_obj_add_style(user_messge_label_, &style_msg, 0); - lv_obj_align(user_messge_label_,LV_ALIGN_TOP_LEFT, 2, 25); + lv_obj_align(user_messge_label_, LV_ALIGN_TOP_LEFT, 2, 25); ai_messge_label_ = lv_label_create(content_); lv_obj_set_style_text_font(ai_messge_label_, &font_puhui_14_1, 0); - lv_label_set_text(ai_messge_label_, "AI:"); + lv_label_set_text(ai_messge_label_, "AI: "); lv_obj_add_style(ai_messge_label_, &style_msg, 0); - lv_obj_align(ai_messge_label_,LV_ALIGN_TOP_LEFT, 2, 77); + lv_obj_align(ai_messge_label_, LV_ALIGN_TOP_LEFT, 2, 77); } }; @@ -228,13 +211,6 @@ public: virtual Display* GetDisplay() override { return display_; } - - virtual bool GetBatteryLevel(int &level, bool& charging) override - { - charging = false; - level = 60; - return true; - }; }; DECLARE_BOARD(EspBox3Board); diff --git a/main/boards/lichuang-dev/lichuang_dev_board.cc b/main/boards/lichuang-dev/lichuang_dev_board.cc index 996e43b..38e6e4e 100644 --- a/main/boards/lichuang-dev/lichuang_dev_board.cc +++ b/main/boards/lichuang-dev/lichuang_dev_board.cc @@ -1,6 +1,6 @@ #include "wifi_board.h" #include "audio_codecs/box_audio_codec.h" -#include "display/st7789_display.h" +#include "display/lcd_display.h" #include "application.h" #include "button.h" #include "config.h" @@ -36,7 +36,7 @@ private: i2c_master_bus_handle_t i2c_bus_; i2c_master_dev_handle_t pca9557_handle_; Button boot_button_; - St7789Display* display_; + LcdDisplay* display_; Pca9557* pca9557_; void InitializeI2c() { @@ -115,7 +115,7 @@ private: esp_lcd_panel_invert_color(panel, true); esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); - display_ = new St7789Display(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT, + display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY); } diff --git a/main/boards/m5stack-core-s3/m5stack_core_s3.cc b/main/boards/m5stack-core-s3/m5stack_core_s3.cc index fbcad42..6226f4a 100644 --- a/main/boards/m5stack-core-s3/m5stack_core_s3.cc +++ b/main/boards/m5stack-core-s3/m5stack_core_s3.cc @@ -1,9 +1,8 @@ #include "wifi_board.h" #include "audio_codecs/cores3_audio_codec.h" -#include "display/st7789_display.h" +#include "display/lcd_display.h" #include "application.h" #include "button.h" -#include "led.h" #include "config.h" #include "i2c_device.h" #include "iot/thing_manager.h" @@ -31,6 +30,18 @@ public: WriteReg(0x94, 33 - 5); WriteReg(0x95, 33 - 5); } + + int GetBatteryCurrentDirection() { + return (ReadReg(0x01) & 0b01100000) >> 5; + } + + bool IsCharging() { + return GetBatteryCurrentDirection() == 1; + } + + int GetBatteryLevel() { + return ReadReg(0xA4); + } }; class Aw9523 : public I2cDevice { @@ -103,7 +114,7 @@ private: Axp2101* axp2101_; Aw9523* aw9523_; Ft6336* ft6336_; - St7789Display* display_; + LcdDisplay* display_; Button boot_button_; void InitializeI2c() { @@ -227,7 +238,7 @@ private: esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); - display_ = new St7789Display(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT, + display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY); } @@ -256,11 +267,6 @@ public: InitializeIot(); } - virtual Led* GetBuiltinLed() override { - static Led led(GPIO_NUM_NC); - return &led; - } - virtual AudioCodec* GetAudioCodec() override { static CoreS3AudioCodec* audio_codec = nullptr; if (audio_codec == nullptr) { @@ -276,6 +282,19 @@ public: return display_; } + virtual bool GetBatteryLevel(int &level, bool& charging) override { + static int last_level = 0; + static bool last_charging = false; + level = axp2101_->GetBatteryLevel(); + charging = axp2101_->IsCharging(); + if (level != last_level || charging != last_charging) { + last_level = level; + last_charging = charging; + ESP_LOGI(TAG, "Battery level: %d, charging: %d", level, charging); + } + return true; + } + Ft6336* GetTouchpad() { return ft6336_; } diff --git a/main/boards/magiclick-2p4/magiclick_2p4_board.cc b/main/boards/magiclick-2p4/magiclick_2p4_board.cc index 696738e..ada53db 100644 --- a/main/boards/magiclick-2p4/magiclick_2p4_board.cc +++ b/main/boards/magiclick-2p4/magiclick_2p4_board.cc @@ -1,9 +1,9 @@ #include "wifi_board.h" -#include "display/st7789_display.h" +#include "display/lcd_display.h" #include "audio_codecs/es8311_audio_codec.h" #include "application.h" #include "button.h" -#include "led.h" +#include "led/single_led.h" #include "config.h" #include #include @@ -17,7 +17,7 @@ class magiclick_2p4 : public WifiBoard { private: i2c_master_bus_handle_t codec_i2c_bus_; Button boot_button_; - St7789Display* display_; + LcdDisplay* display_; void InitializeCodecI2c() { // Initialize I2C peripheral @@ -39,7 +39,7 @@ private: void InitializeButtons() { boot_button_.OnClick([this]() { auto& app = Application::GetInstance(); - if (app.GetChatState() == kChatStateUnknown && !WifiStation::GetInstance().IsConnected()) { + if (app.GetDeviceState() == kDeviceStateUnknown && !WifiStation::GetInstance().IsConnected()) { ResetWifiConfiguration(); } }); @@ -57,6 +57,7 @@ private: gpio_set_direction(BUILTIN_LED_POWER, GPIO_MODE_OUTPUT); gpio_set_level(BUILTIN_LED_POWER, BUILTIN_LED_POWER_OUTPUT_INVERT ? 0 : 1); } + void InitializeSpi() { spi_bus_config_t buscfg = {}; buscfg.mosi_io_num = DISPLAY_SDA_PIN; @@ -68,7 +69,7 @@ private: ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO)); } - void InitializeSt7789Display(){ + void InitializeNv3023Display(){ esp_lcd_panel_io_handle_t panel_io = nullptr; esp_lcd_panel_handle_t panel = nullptr; // 液晶屏控制IO初始化 @@ -83,7 +84,7 @@ private: io_config.lcd_param_bits = 8; ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io)); - // 初始化液晶屏驱动芯片ST7789 + // 初始化液晶屏驱动芯片NV3023 ESP_LOGD(TAG, "Install LCD driver"); esp_lcd_panel_dev_config_t panel_config = {}; panel_config.reset_gpio_num = DISPLAY_RST_PIN; @@ -99,7 +100,7 @@ private: esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel, true)); - display_ = new St7789Display(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT, + display_ = new LcdDisplay(panel_io, panel, DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT, DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY); } @@ -110,13 +111,11 @@ public: InitializeButtons(); InitializeLedPower(); InitializeSpi(); - InitializeSt7789Display(); + InitializeNv3023Display(); } - virtual Led* GetBuiltinLed() override { - // static Led led(BUILTIN_LED_GPIO,BUILTIN_LED_NUM); - static Led led(BUILTIN_LED_GPIO); - + virtual Led* GetLed() override { + static SingleLed led(BUILTIN_LED_GPIO); return &led; } diff --git a/main/display/st7789_display.cc b/main/display/lcd_display.cc similarity index 81% rename from main/display/st7789_display.cc rename to main/display/lcd_display.cc index 1daa67f..a269cd4 100644 --- a/main/display/st7789_display.cc +++ b/main/display/lcd_display.cc @@ -1,4 +1,4 @@ -#include "st7789_display.h" +#include "lcd_display.h" #include "font_awesome_symbols.h" #include @@ -6,21 +6,21 @@ #include #include -#define TAG "St7789Display" +#define TAG "LcdDisplay" #define LCD_LEDC_CH LEDC_CHANNEL_0 -#define ST7789_LVGL_TICK_PERIOD_MS 2 -#define ST7789_LVGL_TASK_MAX_DELAY_MS 20 -#define ST7789_LVGL_TASK_MIN_DELAY_MS 1 -#define ST7789_LVGL_TASK_STACK_SIZE (4 * 1024) -#define ST7789_LVGL_TASK_PRIORITY 10 +#define LCD_LVGL_TICK_PERIOD_MS 2 +#define LCD_LVGL_TASK_MAX_DELAY_MS 20 +#define LCD_LVGL_TASK_MIN_DELAY_MS 1 +#define LCD_LVGL_TASK_STACK_SIZE (4 * 1024) +#define LCD_LVGL_TASK_PRIORITY 10 LV_FONT_DECLARE(font_puhui_14_1); LV_FONT_DECLARE(font_awesome_30_1); LV_FONT_DECLARE(font_awesome_14_1); static lv_disp_drv_t disp_drv; -static void st7789_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) +static void lcd_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map) { esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data; int offsetx1 = area->x1; @@ -33,7 +33,7 @@ static void st7789_lvgl_flush_cb(lv_disp_drv_t *drv, const lv_area_t *area, lv_c } /* Rotate display and touch, when rotated screen in LVGL. Called when driver parameters are updated. */ -static void st7789_lvgl_port_update_callback(lv_disp_drv_t *drv) +static void lcd_lvgl_port_update_callback(lv_disp_drv_t *drv) { esp_lcd_panel_handle_t panel_handle = (esp_lcd_panel_handle_t)drv->user_data; @@ -43,48 +43,28 @@ static void st7789_lvgl_port_update_callback(lv_disp_drv_t *drv) // Rotate LCD display esp_lcd_panel_swap_xy(panel_handle, false); esp_lcd_panel_mirror(panel_handle, true, false); -#if CONFIG_ST7789_LCD_TOUCH_ENABLED - // Rotate LCD touch - esp_lcd_touch_set_mirror_y(tp, false); - esp_lcd_touch_set_mirror_x(tp, false); -#endif break; case LV_DISP_ROT_90: // Rotate LCD display esp_lcd_panel_swap_xy(panel_handle, true); esp_lcd_panel_mirror(panel_handle, true, true); -#if CONFIG_ST7789_LCD_TOUCH_ENABLED - // Rotate LCD touch - esp_lcd_touch_set_mirror_y(tp, false); - esp_lcd_touch_set_mirror_x(tp, false); -#endif break; case LV_DISP_ROT_180: // Rotate LCD display esp_lcd_panel_swap_xy(panel_handle, false); esp_lcd_panel_mirror(panel_handle, false, true); -#if CONFIG_ST7789_LCD_TOUCH_ENABLED - // Rotate LCD touch - esp_lcd_touch_set_mirror_y(tp, false); - esp_lcd_touch_set_mirror_x(tp, false); -#endif break; case LV_DISP_ROT_270: // Rotate LCD display esp_lcd_panel_swap_xy(panel_handle, true); esp_lcd_panel_mirror(panel_handle, false, false); -#if CONFIG_ST7789_LCD_TOUCH_ENABLED - // Rotate LCD touch - esp_lcd_touch_set_mirror_y(tp, false); - esp_lcd_touch_set_mirror_x(tp, false); -#endif break; } } -void St7789Display::LvglTask() { +void LcdDisplay::LvglTask() { ESP_LOGI(TAG, "Starting LVGL task"); - uint32_t task_delay_ms = ST7789_LVGL_TASK_MAX_DELAY_MS; + uint32_t task_delay_ms = LCD_LVGL_TASK_MAX_DELAY_MS; while (1) { // Lock the mutex due to the LVGL APIs are not thread-safe @@ -93,20 +73,20 @@ void St7789Display::LvglTask() { task_delay_ms = lv_timer_handler(); Unlock(); } - if (task_delay_ms > ST7789_LVGL_TASK_MAX_DELAY_MS) + if (task_delay_ms > LCD_LVGL_TASK_MAX_DELAY_MS) { - task_delay_ms = ST7789_LVGL_TASK_MAX_DELAY_MS; + task_delay_ms = LCD_LVGL_TASK_MAX_DELAY_MS; } - else if (task_delay_ms < ST7789_LVGL_TASK_MIN_DELAY_MS) + else if (task_delay_ms < LCD_LVGL_TASK_MIN_DELAY_MS) { - task_delay_ms = ST7789_LVGL_TASK_MIN_DELAY_MS; + task_delay_ms = LCD_LVGL_TASK_MIN_DELAY_MS; } vTaskDelay(pdMS_TO_TICKS(task_delay_ms)); } } -St7789Display::St7789Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, +LcdDisplay::LcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, gpio_num_t backlight_pin, bool backlight_output_invert, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy) : panel_io_(panel_io), panel_(panel), backlight_pin_(backlight_pin), backlight_output_invert_(backlight_output_invert), @@ -147,8 +127,8 @@ St7789Display::St7789Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_h disp_drv.ver_res = height_; disp_drv.offset_x = offset_x_; disp_drv.offset_y = offset_y_; - disp_drv.flush_cb = st7789_lvgl_flush_cb; - disp_drv.drv_update_cb = st7789_lvgl_port_update_callback; + disp_drv.flush_cb = lcd_lvgl_flush_cb; + disp_drv.drv_update_cb = lcd_lvgl_port_update_callback; disp_drv.draw_buf = &disp_buf; disp_drv.user_data = panel_; @@ -158,7 +138,7 @@ St7789Display::St7789Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_h // Tick interface for LVGL (using esp_timer to generate 2ms periodic event) const esp_timer_create_args_t lvgl_tick_timer_args = { .callback = [](void* arg) { - lv_tick_inc(ST7789_LVGL_TICK_PERIOD_MS); + lv_tick_inc(LCD_LVGL_TICK_PERIOD_MS); }, .arg = NULL, .dispatch_method = ESP_TIMER_TASK, @@ -166,22 +146,22 @@ St7789Display::St7789Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_h .skip_unhandled_events = false }; ESP_ERROR_CHECK(esp_timer_create(&lvgl_tick_timer_args, &lvgl_tick_timer_)); - ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer_, ST7789_LVGL_TICK_PERIOD_MS * 1000)); + ESP_ERROR_CHECK(esp_timer_start_periodic(lvgl_tick_timer_, LCD_LVGL_TICK_PERIOD_MS * 1000)); lvgl_mutex_ = xSemaphoreCreateRecursiveMutex(); assert(lvgl_mutex_ != nullptr); ESP_LOGI(TAG, "Create LVGL task"); xTaskCreate([](void *arg) { - static_cast(arg)->LvglTask(); + static_cast(arg)->LvglTask(); vTaskDelete(NULL); - }, "LVGL", ST7789_LVGL_TASK_STACK_SIZE, this, ST7789_LVGL_TASK_PRIORITY, NULL); + }, "LVGL", LCD_LVGL_TASK_STACK_SIZE, this, LCD_LVGL_TASK_PRIORITY, NULL); SetBacklight(100); SetupUI(); } -St7789Display::~St7789Display() { +LcdDisplay::~LcdDisplay() { ESP_ERROR_CHECK(esp_timer_stop(lvgl_tick_timer_)); ESP_ERROR_CHECK(esp_timer_delete(lvgl_tick_timer_)); @@ -207,7 +187,7 @@ St7789Display::~St7789Display() { vSemaphoreDelete(lvgl_mutex_); } -void St7789Display::InitializeBacklight(gpio_num_t backlight_pin) { +void LcdDisplay::InitializeBacklight(gpio_num_t backlight_pin) { if (backlight_pin == GPIO_NUM_NC) { return; } @@ -238,7 +218,7 @@ void St7789Display::InitializeBacklight(gpio_num_t backlight_pin) { ESP_ERROR_CHECK(ledc_channel_config(&backlight_channel)); } -void St7789Display::SetBacklight(uint8_t brightness) { +void LcdDisplay::SetBacklight(uint8_t brightness) { if (backlight_pin_ == GPIO_NUM_NC) { return; } @@ -254,18 +234,18 @@ void St7789Display::SetBacklight(uint8_t brightness) { ESP_ERROR_CHECK(ledc_update_duty(LEDC_LOW_SPEED_MODE, LCD_LEDC_CH)); } -bool St7789Display::Lock(int timeout_ms) { +bool LcdDisplay::Lock(int timeout_ms) { // Convert timeout in milliseconds to FreeRTOS ticks // If `timeout_ms` is set to 0, the program will block until the condition is met const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms); return xSemaphoreTakeRecursive(lvgl_mutex_, timeout_ticks) == pdTRUE; } -void St7789Display::Unlock() { +void LcdDisplay::Unlock() { xSemaphoreGiveRecursive(lvgl_mutex_); } -void St7789Display::SetupUI() { +void LcdDisplay::SetupUI() { DisplayLockGuard lock(this); auto screen = lv_disp_get_scr_act(lv_disp_get_default()); @@ -336,7 +316,7 @@ void St7789Display::SetupUI() { lv_obj_set_style_text_font(battery_label_, &font_awesome_14_1, 0); } -void St7789Display::SetChatMessage(const std::string &role, const std::string &content) { +void LcdDisplay::SetChatMessage(const std::string &role, const std::string &content) { if (chat_message_label_ == nullptr) { return; } diff --git a/main/display/st7789_display.h b/main/display/lcd_display.h similarity index 85% rename from main/display/st7789_display.h rename to main/display/lcd_display.h index e053e34..67f6ad9 100644 --- a/main/display/st7789_display.h +++ b/main/display/lcd_display.h @@ -1,5 +1,5 @@ -#ifndef ST7789_DISPLAY_H -#define ST7789_DISPLAY_H +#ifndef LCD_DISPLAY_H +#define LCD_DISPLAY_H #include "display.h" @@ -10,7 +10,7 @@ #include #include -class St7789Display : public Display { +class LcdDisplay : public Display { protected: esp_lcd_panel_io_handle_t panel_io_ = nullptr; esp_lcd_panel_handle_t panel_ = nullptr; @@ -39,12 +39,12 @@ protected: virtual void Unlock() override; public: - St7789Display(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, + LcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, gpio_num_t backlight_pin, bool backlight_output_invert, int width, int height, int offset_x, int offset_y, bool mirror_x, bool mirror_y, bool swap_xy); - ~St7789Display(); + ~LcdDisplay(); void SetChatMessage(const std::string &role, const std::string &content) override; }; -#endif // ST7789_DISPLAY_H +#endif // LCD_DISPLAY_H diff --git a/main/display/ssd1306_display.cc b/main/display/ssd1306_display.cc index 645a9f4..b2cc2ed 100644 --- a/main/display/ssd1306_display.cc +++ b/main/display/ssd1306_display.cc @@ -35,7 +35,7 @@ Ssd1306Display::Ssd1306Display(void* i2c_master_handle, int width, int height, b .dc_low_on_data = 0, .disable_control_phase = 0, }, - .scl_speed_hz = 400 * 1000, + .scl_speed_hz = 100 * 1000, }; ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c_v2((i2c_master_bus_t*)i2c_master_handle, &io_config, &panel_io_)); From b3cef41d77360545284453d2b6892ac4b5e2255e Mon Sep 17 00:00:00 2001 From: Terrence Date: Mon, 6 Jan 2025 01:26:39 +0800 Subject: [PATCH 5/5] fix display for magicclick --- main/boards/magiclick-2p4/magiclick_2p4_board.cc | 12 ++++++++++++ main/display/lcd_display.cc | 1 + 2 files changed, 13 insertions(+) diff --git a/main/boards/magiclick-2p4/magiclick_2p4_board.cc b/main/boards/magiclick-2p4/magiclick_2p4_board.cc index ada53db..887391e 100644 --- a/main/boards/magiclick-2p4/magiclick_2p4_board.cc +++ b/main/boards/magiclick-2p4/magiclick_2p4_board.cc @@ -4,6 +4,7 @@ #include "application.h" #include "button.h" #include "led/single_led.h" +#include "iot/thing_manager.h" #include "config.h" #include #include @@ -104,6 +105,12 @@ private: DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY); } + // 物联网初始化,添加对 AI 可见设备 + void InitializeIot() { + auto& thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + } + public: magiclick_2p4() : boot_button_(BOOT_BUTTON_GPIO) { @@ -112,6 +119,7 @@ public: InitializeLedPower(); InitializeSpi(); InitializeNv3023Display(); + InitializeIot(); } virtual Led* GetLed() override { @@ -125,6 +133,10 @@ public: AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR); return &audio_codec; } + + virtual Display* GetDisplay() override { + return display_; + } }; DECLARE_BOARD(magiclick_2p4); diff --git a/main/display/lcd_display.cc b/main/display/lcd_display.cc index a269cd4..ad724b1 100644 --- a/main/display/lcd_display.cc +++ b/main/display/lcd_display.cc @@ -304,6 +304,7 @@ void LcdDisplay::SetupUI() { status_label_ = lv_label_create(status_bar_); lv_obj_set_flex_grow(status_label_, 1); + lv_label_set_long_mode(status_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); lv_label_set_text(status_label_, "正在初始化"); lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0);