From d505b3b1aeab42e48feb583b5cc72b36db4d2ea8 Mon Sep 17 00:00:00 2001 From: Haibo Gu Date: Wed, 7 May 2025 02:33:38 +0800 Subject: [PATCH] =?UTF-8?q?=E3=80=90=E9=9C=80=E6=B1=82=E3=80=91=E5=8F=8C?= =?UTF-8?q?=E7=BD=91=E7=BB=9C=E7=B1=BB=E5=9E=8B=E9=80=9A=E8=BF=87=E9=95=BF?= =?UTF-8?q?=E6=8C=89boot=E5=88=87=E6=8D=A2=E7=BD=91=E7=BB=9C=20(#520)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 【需求】双网络类型通过长按boot切换网络 * Update Kconfig.projbuild --------- Co-authored-by: guhaibo Co-authored-by: Xiaoxia --- main/CMakeLists.txt | 2 + main/Kconfig.projbuild | 2 + main/boards/common/board.h | 2 +- main/boards/common/dual_network_board.cc | 103 ++++++++ main/boards/common/dual_network_board.h | 59 +++++ main/boards/common/ml307_board.h | 2 +- main/boards/common/wifi_board.h | 4 +- .../boards/xingzhi-cube-1.54tft-dual/config.h | 40 +++ .../xingzhi-cube-1.54tft-dual/config.json | 15 ++ .../xingzhi_cube_1_54tft_dual_board.cc | 232 ++++++++++++++++++ 10 files changed, 457 insertions(+), 4 deletions(-) create mode 100644 main/boards/common/dual_network_board.cc create mode 100644 main/boards/common/dual_network_board.h create mode 100644 main/boards/xingzhi-cube-1.54tft-dual/config.h create mode 100644 main/boards/xingzhi-cube-1.54tft-dual/config.json create mode 100644 main/boards/xingzhi-cube-1.54tft-dual/xingzhi_cube_1_54tft_dual_board.cc diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 8fe25ebb..f0c55b11 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -145,6 +145,8 @@ elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_WIFI) set(BOARD_TYPE "xingzhi-cube-1.54tft-wifi") elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_ML307) set(BOARD_TYPE "xingzhi-cube-1.54tft-ml307") +elseif(CONFIG_BOARD_TYPE_XINGZHI_Cube_1_54TFT_DUAL) + set(BOARD_TYPE "xingzhi-cube-1.54tft-dual") elseif(CONFIG_BOARD_TYPE_SENSECAP_WATCHER) set(BOARD_TYPE "sensecap-watcher") elseif(CONFIG_BOARD_TYPE_DOIT_S3_AIBOX) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 96567005..46e0f86a 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -140,6 +140,8 @@ choice BOARD_TYPE bool "无名科技星智1.54(WIFI)" config BOARD_TYPE_XINGZHI_Cube_1_54TFT_ML307 bool "无名科技星智1.54(ML307)" + config BOARD_TYPE_XINGZHI_Cube_1_54TFT_DUAL + bool "无名科技星智1.54(DUAL)" config BOARD_TYPE_SENSECAP_WATCHER bool "SenseCAP Watcher" config BOARD_TYPE_DOIT_S3_AIBOX diff --git a/main/boards/common/board.h b/main/boards/common/board.h index 9c6f5166..92cfc388 100644 --- a/main/boards/common/board.h +++ b/main/boards/common/board.h @@ -17,7 +17,6 @@ class Board { private: Board(const Board&) = delete; // 禁用拷贝构造函数 Board& operator=(const Board&) = delete; // 禁用赋值操作 - virtual std::string GetBoardJson() = 0; protected: Board(); @@ -48,6 +47,7 @@ public: virtual bool GetBatteryLevel(int &level, bool& charging, bool& discharging); virtual std::string GetJson(); virtual void SetPowerSaveMode(bool enabled) = 0; + virtual std::string GetBoardJson() = 0; }; #define DECLARE_BOARD(BOARD_CLASS_NAME) \ diff --git a/main/boards/common/dual_network_board.cc b/main/boards/common/dual_network_board.cc new file mode 100644 index 00000000..43f16809 --- /dev/null +++ b/main/boards/common/dual_network_board.cc @@ -0,0 +1,103 @@ +#include "dual_network_board.h" +#include "application.h" +#include "display.h" +#include "assets/lang_config.h" +#include "settings.h" +#include + +static const char *TAG = "DualNetworkBoard"; + +DualNetworkBoard::DualNetworkBoard(gpio_num_t ml307_tx_pin, gpio_num_t ml307_rx_pin, size_t ml307_rx_buffer_size) + : Board(), + ml307_tx_pin_(ml307_tx_pin), + ml307_rx_pin_(ml307_rx_pin), + ml307_rx_buffer_size_(ml307_rx_buffer_size) { + + // 从Settings加载网络类型 + network_type_ = LoadNetworkTypeFromSettings(); + + // 只初始化当前网络类型对应的板卡 + InitializeCurrentBoard(); +} + +NetworkType DualNetworkBoard::LoadNetworkTypeFromSettings() { + Settings settings("network", true); + int network_type = settings.GetInt("type", 1); // 默认使用ML307 (1) + + ESP_LOGI(TAG, "从Settings加载网络类型: %d", network_type); + + return network_type == 1 ? NetworkType::ML307 : NetworkType::WIFI; +} + +void DualNetworkBoard::SaveNetworkTypeToSettings(NetworkType type) { + Settings settings("network", true); + int network_type = (type == NetworkType::ML307) ? 1 : 0; + + ESP_LOGI(TAG, "保存网络类型到Settings: %d", network_type); + + settings.SetInt("type", network_type); +} + +void DualNetworkBoard::InitializeCurrentBoard() { + if (network_type_ == NetworkType::ML307) { + ESP_LOGI(TAG, "初始化ML307板卡"); + current_board_ = std::make_unique(ml307_tx_pin_, ml307_rx_pin_, ml307_rx_buffer_size_); + } else { + ESP_LOGI(TAG, "初始化WiFi板卡"); + current_board_ = std::make_unique(); + } +} + +void DualNetworkBoard::SwitchNetType() { + if (network_type_ == NetworkType::WIFI) { + ESP_LOGI(TAG, "切换到ML307模式"); + SaveNetworkTypeToSettings(NetworkType::ML307); + } else { + ESP_LOGI(TAG, "切换到WiFi模式"); + SaveNetworkTypeToSettings(NetworkType::WIFI); + } +} + + +std::string DualNetworkBoard::GetBoardType() { + return current_board_->GetBoardType(); +} + +void DualNetworkBoard::StartNetwork() { + auto display = Board::GetInstance().GetDisplay(); + + if (network_type_ == NetworkType::WIFI) { + display->SetStatus(Lang::Strings::CONNECTING); + } else { + display->SetStatus(Lang::Strings::DETECTING_MODULE); + } + current_board_->StartNetwork(); +} + +Http* DualNetworkBoard::CreateHttp() { + return current_board_->CreateHttp(); +} + +WebSocket* DualNetworkBoard::CreateWebSocket() { + return current_board_->CreateWebSocket(); +} + +Mqtt* DualNetworkBoard::CreateMqtt() { + return current_board_->CreateMqtt(); +} + +Udp* DualNetworkBoard::CreateUdp() { + return current_board_->CreateUdp(); +} + +const char* DualNetworkBoard::GetNetworkStateIcon() { + return current_board_->GetNetworkStateIcon(); +} + +void DualNetworkBoard::SetPowerSaveMode(bool enabled) { + current_board_->SetPowerSaveMode(enabled); +} + +std::string DualNetworkBoard::GetBoardJson() { + return current_board_->GetBoardJson(); +} \ No newline at end of file diff --git a/main/boards/common/dual_network_board.h b/main/boards/common/dual_network_board.h new file mode 100644 index 00000000..7450d9e5 --- /dev/null +++ b/main/boards/common/dual_network_board.h @@ -0,0 +1,59 @@ +#ifndef DUAL_NETWORK_BOARD_H +#define DUAL_NETWORK_BOARD_H + +#include "board.h" +#include "wifi_board.h" +#include "ml307_board.h" +#include + +//enum NetworkType +enum class NetworkType { + WIFI, + ML307 +}; + +// 双网络板卡类,可以在WiFi和ML307之间切换 +class DualNetworkBoard : public Board { +private: + // 使用基类指针存储当前活动的板卡 + std::unique_ptr current_board_; + NetworkType network_type_ = NetworkType::ML307; // Default to ML307 + + // ML307的引脚配置 + gpio_num_t ml307_tx_pin_; + gpio_num_t ml307_rx_pin_; + size_t ml307_rx_buffer_size_; + + // 从Settings加载网络类型 + NetworkType LoadNetworkTypeFromSettings(); + + // 保存网络类型到Settings + void SaveNetworkTypeToSettings(NetworkType type); + + // 初始化当前网络类型对应的板卡 + void InitializeCurrentBoard(); + +public: + DualNetworkBoard(gpio_num_t ml307_tx_pin, gpio_num_t ml307_rx_pin, size_t ml307_rx_buffer_size = 4096); + virtual ~DualNetworkBoard() = default; + + // 切换网络类型 + void SwitchNetType(); + + // 获取当前网络类型 + NetworkType GetNetworkType() const { return network_type_; } + + // 重写Board接口 + virtual std::string GetBoardType() override; + virtual void StartNetwork() override; + virtual Http* CreateHttp() override; + virtual WebSocket* CreateWebSocket() override; + virtual Mqtt* CreateMqtt() override; + virtual Udp* CreateUdp() override; + virtual const char* GetNetworkStateIcon() override; + virtual void SetPowerSaveMode(bool enabled) override; + virtual std::string GetBoardJson() override; + +}; + +#endif // DUAL_NETWORK_BOARD_H \ No newline at end of file diff --git a/main/boards/common/ml307_board.h b/main/boards/common/ml307_board.h index effaccec..4dd6cb09 100644 --- a/main/boards/common/ml307_board.h +++ b/main/boards/common/ml307_board.h @@ -7,7 +7,6 @@ class Ml307Board : public Board { protected: Ml307AtModem modem_; - virtual std::string GetBoardJson() override; void WaitForNetworkReady(); @@ -21,6 +20,7 @@ public: virtual Udp* CreateUdp() override; virtual const char* GetNetworkStateIcon() override; virtual void SetPowerSaveMode(bool enabled) override; + virtual AudioCodec* GetAudioCodec() override { return nullptr; } }; #endif // ML307_BOARD_H diff --git a/main/boards/common/wifi_board.h b/main/boards/common/wifi_board.h index 08da502e..a701b2b7 100644 --- a/main/boards/common/wifi_board.h +++ b/main/boards/common/wifi_board.h @@ -6,12 +6,11 @@ class WifiBoard : public Board { protected: bool wifi_config_mode_ = false; - - WifiBoard(); void EnterWifiConfigMode(); virtual std::string GetBoardJson() override; public: + WifiBoard(); virtual std::string GetBoardType() override; virtual void StartNetwork() override; virtual Http* CreateHttp() override; @@ -21,6 +20,7 @@ public: virtual const char* GetNetworkStateIcon() override; virtual void SetPowerSaveMode(bool enabled) override; virtual void ResetWifiConfiguration(); + virtual AudioCodec* GetAudioCodec() override { return nullptr; } }; #endif // WIFI_BOARD_H diff --git a/main/boards/xingzhi-cube-1.54tft-dual/config.h b/main/boards/xingzhi-cube-1.54tft-dual/config.h new file mode 100644 index 00000000..9167578a --- /dev/null +++ b/main/boards/xingzhi-cube-1.54tft-dual/config.h @@ -0,0 +1,40 @@ + +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include + +#define AUDIO_INPUT_SAMPLE_RATE 16000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_4 +#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_5 +#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_6 +#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_7 +#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_15 +#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_16 + +#define BOOT_BUTTON_GPIO GPIO_NUM_0 +#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_40 +#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_39 + +#define DISPLAY_SDA GPIO_NUM_10 +#define DISPLAY_SCL GPIO_NUM_9 +#define DISPLAY_DC GPIO_NUM_8 +#define DISPLAY_CS GPIO_NUM_14 +#define DISPLAY_RES GPIO_NUM_18 +#define DISPLAY_WIDTH 240 +#define DISPLAY_HEIGHT 240 +#define DISPLAY_SWAP_XY false +#define DISPLAY_MIRROR_X false +#define DISPLAY_MIRROR_Y false +#define BACKLIGHT_INVERT false +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 +#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_13 +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false + +#define ML307_RX_PIN GPIO_NUM_11 +#define ML307_TX_PIN GPIO_NUM_12 + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/xingzhi-cube-1.54tft-dual/config.json b/main/boards/xingzhi-cube-1.54tft-dual/config.json new file mode 100644 index 00000000..4822e606 --- /dev/null +++ b/main/boards/xingzhi-cube-1.54tft-dual/config.json @@ -0,0 +1,15 @@ +{ + "target": "esp32s3", + "builds": [ + { + "name": "xingzhi-cube-1.54tft-dual", + "sdkconfig_append": [] + }, + { + "name": "xingzhi-cube-1.54tft-dual-wechatui", + "sdkconfig_append": [ + "CONFIG_USE_WECHAT_MESSAGE_STYLE=y" + ] + } + ] +} \ No newline at end of file diff --git a/main/boards/xingzhi-cube-1.54tft-dual/xingzhi_cube_1_54tft_dual_board.cc b/main/boards/xingzhi-cube-1.54tft-dual/xingzhi_cube_1_54tft_dual_board.cc new file mode 100644 index 00000000..9c154162 --- /dev/null +++ b/main/boards/xingzhi-cube-1.54tft-dual/xingzhi_cube_1_54tft_dual_board.cc @@ -0,0 +1,232 @@ +#include "ml307_board.h" +#include "audio_codecs/no_audio_codec.h" +#include "display/lcd_display.h" +#include "system_reset.h" +#include "application.h" +#include "button.h" +#include "config.h" +#include "power_save_timer.h" +#include "iot/thing_manager.h" +#include "led/single_led.h" +#include "assets/lang_config.h" +#include "../xingzhi-cube-1.54tft-wifi/power_manager.h" +#include "boards/common/dual_network_board.h" + +#include +#include + +#include +#include + +#define TAG "XINGZHI_CUBE_1_54TFT_DUAL" + +LV_FONT_DECLARE(font_puhui_20_4); +LV_FONT_DECLARE(font_awesome_20_4); + + +class XINGZHI_CUBE_1_54TFT_DUAL : DualNetworkBoard { +private: + Button boot_button_; + Button volume_up_button_; + Button volume_down_button_; + SpiLcdDisplay* display_; + PowerSaveTimer* power_save_timer_; + PowerManager* power_manager_; + esp_lcd_panel_io_handle_t panel_io_ = nullptr; + esp_lcd_panel_handle_t panel_ = nullptr; + + void InitializePowerManager() { + power_manager_ = new PowerManager(GPIO_NUM_38); + power_manager_->OnChargingStatusChanged([this](bool is_charging) { + if (is_charging) { + power_save_timer_->SetEnabled(false); + } else { + power_save_timer_->SetEnabled(true); + } + }); + } + + void InitializePowerSaveTimer() { + rtc_gpio_init(GPIO_NUM_21); + rtc_gpio_set_direction(GPIO_NUM_21, RTC_GPIO_MODE_OUTPUT_ONLY); + rtc_gpio_set_level(GPIO_NUM_21, 1); + + power_save_timer_ = new PowerSaveTimer(-1, 60, 300); + power_save_timer_->OnEnterSleepMode([this]() { + ESP_LOGI(TAG, "Enabling sleep mode"); + display_->SetChatMessage("system", ""); + display_->SetEmotion("sleepy"); + GetBacklight()->SetBrightness(1); + }); + power_save_timer_->OnExitSleepMode([this]() { + display_->SetChatMessage("system", ""); + display_->SetEmotion("neutral"); + GetBacklight()->RestoreBrightness(); + }); + power_save_timer_->OnShutdownRequest([this]() { + ESP_LOGI(TAG, "Shutting down"); + rtc_gpio_set_level(GPIO_NUM_21, 0); + // 启用保持功能,确保睡眠期间电平不变 + rtc_gpio_hold_en(GPIO_NUM_21); + esp_lcd_panel_disp_on_off(panel_, false); //关闭显示 + esp_deep_sleep_start(); + }); + power_save_timer_->SetEnabled(true); + } + + void InitializeSpi() { + spi_bus_config_t buscfg = {}; + buscfg.mosi_io_num = DISPLAY_SDA; + buscfg.miso_io_num = GPIO_NUM_NC; + buscfg.sclk_io_num = DISPLAY_SCL; + buscfg.quadwp_io_num = GPIO_NUM_NC; + buscfg.quadhd_io_num = GPIO_NUM_NC; + buscfg.max_transfer_sz = DISPLAY_WIDTH * DISPLAY_HEIGHT * sizeof(uint16_t); + ESP_ERROR_CHECK(spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO)); + } + + void InitializeButtons() { + boot_button_.OnClick([this]() { + power_save_timer_->WakeUp(); + auto& app = Application::GetInstance(); + app.ToggleChatState(); + }); + + + boot_button_.OnLongPress([this]() { + SwitchNetType(); + auto& app = Application::GetInstance(); + app.Reboot(); + }); + + + volume_up_button_.OnClick([this]() { + power_save_timer_->WakeUp(); + auto codec = GetAudioCodec(); + auto volume = codec->output_volume() + 10; + if (volume > 100) { + volume = 100; + } + codec->SetOutputVolume(volume); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); + }); + + volume_up_button_.OnLongPress([this]() { + power_save_timer_->WakeUp(); + GetAudioCodec()->SetOutputVolume(100); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); + }); + + volume_down_button_.OnClick([this]() { + power_save_timer_->WakeUp(); + auto codec = GetAudioCodec(); + auto volume = codec->output_volume() - 10; + if (volume < 0) { + volume = 0; + } + codec->SetOutputVolume(volume); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); + }); + + volume_down_button_.OnLongPress([this]() { + power_save_timer_->WakeUp(); + GetAudioCodec()->SetOutputVolume(0); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); + }); + } + + void InitializeSt7789Display() { + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = {}; + io_config.cs_gpio_num = DISPLAY_CS; + io_config.dc_gpio_num = DISPLAY_DC; + io_config.spi_mode = 3; + io_config.pclk_hz = 80 * 1000 * 1000; + io_config.trans_queue_depth = 10; + io_config.lcd_cmd_bits = 8; + io_config.lcd_param_bits = 8; + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI3_HOST, &io_config, &panel_io_)); + + ESP_LOGD(TAG, "Install LCD driver"); + esp_lcd_panel_dev_config_t panel_config = {}; + panel_config.reset_gpio_num = DISPLAY_RES; + panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB; + panel_config.bits_per_pixel = 16; + ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io_, &panel_config, &panel_)); + ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_)); + ESP_ERROR_CHECK(esp_lcd_panel_init(panel_)); + ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel_, DISPLAY_SWAP_XY)); + ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel_, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y)); + ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_, true)); + + 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_20_4, + .icon_font = &font_awesome_20_4, +#if CONFIG_USE_WECHAT_MESSAGE_STYLE + .emoji_font = font_emoji_32_init(), +#else + .emoji_font = font_emoji_64_init(), +#endif + }); + } + + void InitializeIot() { + auto& thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + thing_manager.AddThing(iot::CreateThing("Screen")); + thing_manager.AddThing(iot::CreateThing("Battery")); + } + +public: + XINGZHI_CUBE_1_54TFT_DUAL() : + DualNetworkBoard(ML307_TX_PIN, ML307_RX_PIN, 4096), + boot_button_(BOOT_BUTTON_GPIO), + volume_up_button_(VOLUME_UP_BUTTON_GPIO), + volume_down_button_(VOLUME_DOWN_BUTTON_GPIO) { + InitializePowerManager(); + InitializePowerSaveTimer(); + InitializeSpi(); + InitializeButtons(); + InitializeSt7789Display(); + InitializeIot(); + GetBacklight()->RestoreBrightness(); + } + + virtual AudioCodec* GetAudioCodec() override { + static NoAudioCodecSimplex audio_codec(AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, + AUDIO_I2S_SPK_GPIO_BCLK, AUDIO_I2S_SPK_GPIO_LRCK, AUDIO_I2S_SPK_GPIO_DOUT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN); + return &audio_codec; + } + + virtual Display* GetDisplay() override { + return display_; + } + + virtual Backlight* GetBacklight() override { + static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT); + return &backlight; + } + + virtual bool GetBatteryLevel(int& level, bool& charging, bool& discharging) override { + static bool last_discharging = false; + charging = power_manager_->IsCharging(); + discharging = power_manager_->IsDischarging(); + if (discharging != last_discharging) { + power_save_timer_->SetEnabled(discharging); + last_discharging = discharging; + } + level = power_manager_->GetBatteryLevel(); + return true; + } + + virtual void SetPowerSaveMode(bool enabled) override { + if (!enabled) { + power_save_timer_->WakeUp(); + } + DualNetworkBoard::SetPowerSaveMode(enabled); + } +}; + +DECLARE_BOARD(XINGZHI_CUBE_1_54TFT_DUAL);