From f20051dfb661427fb600bf0a302bb2020eaa0135 Mon Sep 17 00:00:00 2001 From: lichen <2667497307@qq.com> Date: Wed, 29 Jul 2026 17:26:24 +0800 Subject: [PATCH] =?UTF-8?q?feat(ai-c5):=20=E5=AE=8C=E5=96=84=E7=BD=91?= =?UTF-8?q?=E7=BB=9C=E5=88=87=E6=8D=A2=E3=80=81=E9=85=8D=E7=BD=91=E4=B8=8E?= =?UTF-8?q?=E7=94=B5=E6=B1=A0=E7=8A=B6=E6=80=81=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/boards/doit-ai-c5-kit-lcd/README.md | 68 +++++++++++++ main/boards/doit-ai-c5-kit-lcd/config.h | 3 + .../doit-ai-c5-kit-lcd/doit-ai-c5-kit-lcd.cc | 96 ++++++++++++++++--- 3 files changed, 152 insertions(+), 15 deletions(-) create mode 100644 main/boards/doit-ai-c5-kit-lcd/README.md diff --git a/main/boards/doit-ai-c5-kit-lcd/README.md b/main/boards/doit-ai-c5-kit-lcd/README.md new file mode 100644 index 0000000..c1d9826 --- /dev/null +++ b/main/boards/doit-ai-c5-kit-lcd/README.md @@ -0,0 +1,68 @@ +# DOIT AI C5 KIT LCD + +本目录包含 DOIT AI C5 LCD 板级适配代码,负责 C5 的供电保持、按键、LCD、RGB 指示灯、网络切换和电量监测。 + +## 相关文件 + +| 文件 | 作用 | +| --- | --- | +| `doit-ai-c5-kit-lcd.cc` | C5 板级初始化、BOOT 按键、状态栏位置和电量接口 | +| `config.h` | C5 的 GPIO、LCD 和电池分压参数 | +| `esp_lcd_panel_st7789_fix.c` | C5 ST7789 LCD 的初始化与驱动补丁 | + +## 启动流程 + +设备启动时,父类根据已保存的配置建立 4G 或 Wi-Fi 网络。随后 C5 板级代码执行以下初始化: + +1. 拉高 GPIO27,保持系统供电。 +2. 创建 RGB 指示灯初始化任务。 +3. 注册 BOOT 按键的单击、双击和多击事件。 +4. 初始化 LCD SPI 总线、ST7789 屏幕、LVGL 和背光。 +5. 初始化 GPIO5 电池 ADC 监测器。 +6. 恢复保存的背光亮度。 +7. 注册 VB6824 唤醒词和“开始配网”语音命令。 + +## BOOT 按键 + +- 单击:打印当前网络与设备状态。Wi-Fi 未连接且设备仍处于启动状态时,清除 Wi-Fi 配置;设备未处于监听状态时,触发语音唤醒。 +- 双击:仅限开机后 30 秒内。4G 模式切换到 Wi-Fi;Wi-Fi 配网模式切换到 4G。切换后设备重启。 +- 三击及以上:仅在 Wi-Fi 已连接时清除 Wi-Fi 配置,重启后重新进入配网模式。 +- 开机超过 30 秒后的双击:启用 VB6824 OTA 支持且 Wi-Fi 已连接时,按 OTA 逻辑处理。 +- 接电池情况下,长按开关机。 + +## 状态栏 + +- 时间由公共显示模块显示在顶部中间区域。 +- 网络图标固定在顶部左侧。 +- 电池图标固定在顶部右侧。 +- 网络状态每 10 秒更新一次,根据当前 Wi-Fi 或 4G 状态切换图标。 + +## 电量与低电量弹窗 + +- 检测为充电状态时,状态栏显示闪电电池图标。 +- 未充电时,状态栏按 `0%~19%`、`20%~39%`、`40%~59%`、`60%~79%`、`80%~100%` 显示空电池至满电图标。 +- 电量处于 `0%~19%` 且处于放电状态时,显示“电量低,请充电”弹窗并播放提示音。 +- 开始充电或电量高于低电量范围后,弹窗自动隐藏。 + +## Wi-Fi 配网超时 + +若设备在 Wi-Fi 配网时无法连接路由器,或网页在设备实际连上路由器前就显示失败,可将配网等待时间从 10 秒调整为 30 秒: + +```cpp +// managed_components/78__esp-wifi-connect/wifi_configuration_ap.cc +pdMS_TO_TICKS(10000) +``` + +修改为: + +```cpp +pdMS_TO_TICKS(30000) +``` + +修改后重新编译和烧录固件。调试时可关注以下日志: + +```text +Connecting to WiFi +Connected to WiFi +Got IP +``` diff --git a/main/boards/doit-ai-c5-kit-lcd/config.h b/main/boards/doit-ai-c5-kit-lcd/config.h index 998acd5..831097a 100644 --- a/main/boards/doit-ai-c5-kit-lcd/config.h +++ b/main/boards/doit-ai-c5-kit-lcd/config.h @@ -13,6 +13,9 @@ #define BATTERY_CHANG_GPIO GPIO_NUM_15 #define BATTERY_ADC_GPIO GPIO_NUM_5 +#define BATTERY_ADC_CHANNEL ADC_CHANNEL_4 +#define BATTERY_DIVIDER_UPPER_RESISTOR_OHMS 520000.0f +#define BATTERY_DIVIDER_LOWER_RESISTOR_OHMS 100000.0f #define RBG_DI_GPIO GPIO_NUM_6 diff --git a/main/boards/doit-ai-c5-kit-lcd/doit-ai-c5-kit-lcd.cc b/main/boards/doit-ai-c5-kit-lcd/doit-ai-c5-kit-lcd.cc index ec45aa0..8f6af65 100644 --- a/main/boards/doit-ai-c5-kit-lcd/doit-ai-c5-kit-lcd.cc +++ b/main/boards/doit-ai-c5-kit-lcd/doit-ai-c5-kit-lcd.cc @@ -1,9 +1,9 @@ - #include "dual_network_board.h" #include "audio/codecs/vb6824_audio_codec.h" #include "display/lcd_display.h" #include "application.h" #include "button.h" +#include "adc_battery_monitor.h" #include "config.h" #include @@ -19,7 +19,7 @@ #include "assets/lang_config.h" #include "led/circular_strip.h" -#include "power_save_timer.h" +#include "power_save_timer.h" #include "backlight.h" #include "esp_lcd_panel_st7789_fix.h" @@ -28,15 +28,38 @@ LV_FONT_DECLARE(font_puhui_16_4); LV_FONT_DECLARE(font_awesome_16_4); +class C5LcdDisplay final : public SpiLcdDisplay +{ +public: + C5LcdDisplay(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_handle_t panel, + int width, int height, int offset_x, int offset_y, + bool mirror_x, bool mirror_y, bool swap_xy, DisplayFonts fonts) + : SpiLcdDisplay(panel_io, panel, width, height, offset_x, offset_y, + mirror_x, mirror_y, swap_xy, fonts) + { + // Keep network and battery indicators independent from the status text's flex layout. + auto screen = lv_screen_active(); + lv_obj_set_parent(network_label_, screen); + lv_obj_align(network_label_, LV_ALIGN_TOP_LEFT, 65, 3); + lv_obj_move_foreground(network_label_); + + lv_obj_set_parent(battery_label_, screen); + lv_obj_align(battery_label_, LV_ALIGN_TOP_LEFT, 220, 3); + lv_obj_move_foreground(battery_label_); + + } +}; class CustomBoard : public DualNetworkBoard { private: + int64_t boot_time_us_ = 0; Button boot_button_; VbAduioCodec audio_codec_; LcdDisplay *display_ = nullptr; PwmBacklight *backlight_ = nullptr; PowerSaveTimer *power_save_timer_ = nullptr; + AdcBatteryMonitor *battery_monitor_ = nullptr; CircularStrip *led_ = nullptr; // PullUp4GPin pullup = PullUp4GPin(); @@ -45,6 +68,9 @@ class CustomBoard : public DualNetworkBoard boot_button_.OnClick([this]() { auto& app = Application::GetInstance(); + ESP_LOGI(TAG, "BOOT button single click, network=%s, state=%d", + GetNetworkType() == NetworkType::ML307 ? "ML307" : "WIFI", + static_cast(app.GetDeviceState())); if (GetNetworkType() == NetworkType::WIFI) { if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { // cast to WifiBoard @@ -60,10 +86,21 @@ class CustomBoard : public DualNetworkBoard boot_button_.OnDoubleClick([this]() { - auto &app = Application::GetInstance(); - if (app.GetDeviceState() == kDeviceStateStarting || app.GetDeviceState() == kDeviceStateWifiConfiguring) + auto& app = Application::GetInstance(); + const int64_t elapsed_us = esp_timer_get_time() - boot_time_us_; + ESP_LOGI(TAG, "BOOT button double click, network=%s, elapsed=%lld ms", + GetNetworkType() == NetworkType::ML307 ? "ML307" : "WIFI", + static_cast(elapsed_us / 1000)); + const bool is_wifi_configuring = + GetNetworkType() == NetworkType::WIFI && + app.GetDeviceState() == kDeviceStateWifiConfiguring; + if (elapsed_us <= 30LL * 1000LL * 1000LL && + (GetNetworkType() == NetworkType::ML307 || is_wifi_configuring)) { + ESP_LOGI(TAG, "Double click switches network to %s", + GetNetworkType() == NetworkType::ML307 ? "WIFI" : "ML307"); SwitchNetworkType(); + return; } #if (defined(CONFIG_VB6824_OTA_SUPPORT) && CONFIG_VB6824_OTA_SUPPORT == 1) if (esp_timer_get_time() > 20 * 1000 * 1000) @@ -81,11 +118,15 @@ class CustomBoard : public DualNetworkBoard boot_button_.OnPressRepeaDone([this](uint16_t count) { - if (count >= 3 && GetNetworkType() == NetworkType::WIFI) + ESP_LOGI(TAG, "BOOT button press sequence done, count=%u, network=%s", + static_cast(count), + GetNetworkType() == NetworkType::ML307 ? "ML307" : "WIFI"); + if (count >= 3 && GetNetworkType() == NetworkType::WIFI && + WifiStation::GetInstance().IsConnected()) { - auto& app = Application::GetInstance(); - auto& wifi_board = static_cast(GetCurrentBoard()); - wifi_board.ResetWifiConfiguration(); + ESP_LOGI(TAG, "Three or more presses reset WiFi configuration"); + auto& wifi_board = static_cast(GetCurrentBoard()); + wifi_board.ResetWifiConfiguration(); } }); } @@ -138,19 +179,29 @@ class CustomBoard : public DualNetworkBoard // AngleMap angle_map_ = {(angle) % 360, (angle + 90) % 360, // (angle + 180) % 360, (angle + 270) % 360}; - display_ = new SpiLcdDisplay(panel_io, panel, - DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, - { - .text_font = &font_puhui_16_4, - .icon_font = &font_awesome_16_4, - .emoji_font = font_emoji_64_init(), - }); + auto *display = new C5LcdDisplay(panel_io, panel, + DISPLAY_WIDTH, DISPLAY_HEIGHT, DISPLAY_OFFSET_X, DISPLAY_OFFSET_Y, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y, DISPLAY_SWAP_XY, + { + .text_font = &font_puhui_16_4, + .icon_font = &font_awesome_16_4, + .emoji_font = font_emoji_64_init(), + }); + display_ = display; } void InitializeBacklight() { backlight_ = new PwmBacklight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT); } + + void InitializeBatteryMonitor() + { + battery_monitor_ = new AdcBatteryMonitor( + ADC_UNIT_1, BATTERY_ADC_CHANNEL, + BATTERY_DIVIDER_UPPER_RESISTOR_OHMS, + BATTERY_DIVIDER_LOWER_RESISTOR_OHMS, + BATTERY_CHANG_GPIO); + } void InitializeLed() { xTaskCreate( @@ -171,6 +222,7 @@ class CustomBoard : public DualNetworkBoard boot_button_(BOOT_BUTTON_GPIO), audio_codec_(CODEC_TX_GPIO,CODEC_RX_GPIO) { + boot_time_us_ = esp_timer_get_time(); gpio_set_level(POWER_KRRP_GPIO, 1); // 保持高电平 InitializeLed(); @@ -181,6 +233,7 @@ class CustomBoard : public DualNetworkBoard InitializeLcdDisplay(); InitializeBacklight(); + InitializeBatteryMonitor(); GetBacklight()->RestoreBrightness(); // McpTools::GetInstance()->McpToolsInit(); @@ -214,6 +267,19 @@ class CustomBoard : public DualNetworkBoard return backlight_; } + virtual bool GetBatteryLevel(int &level, bool &charging, bool &discharging) override + { + if (battery_monitor_ == nullptr) + { + return false; + } + + charging = battery_monitor_->IsCharging(); + discharging = battery_monitor_->IsDischarging(); + level = battery_monitor_->GetBatteryLevel(); + return true; + } + // virtual void SetPowerSaveMode(bool enabled) override // { // if (!enabled && power_save_timer_)