diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 4134feb2..3b69b6ae 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -57,8 +57,12 @@ elseif(CONFIG_BOARD_TYPE_LICHUANG_C3_DEV) set(BOARD_TYPE "lichuang-c3-dev") elseif(CONFIG_BOARD_TYPE_MAGICLICK_2P4) set(BOARD_TYPE "magiclick-2p4") +elseif(CONFIG_BOARD_TYPE_MAGICLICK_2P5) + set(BOARD_TYPE "magiclick-2p5") elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3) set(BOARD_TYPE "magiclick-c3") +elseif(CONFIG_BOARD_TYPE_MAGICLICK_C3_V2) + set(BOARD_TYPE "magiclick-c3-v2") elseif(CONFIG_BOARD_TYPE_M5STACK_CORE_S3) set(BOARD_TYPE "m5stack-core-s3") elseif(CONFIG_BOARD_TYPE_ATOMS3_ECHO_BASE) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index f25a1f41..5bfa9cb4 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -73,9 +73,13 @@ choice BOARD_TYPE config BOARD_TYPE_LICHUANG_C3_DEV bool "立创·实战派ESP32-C3开发板" config BOARD_TYPE_MAGICLICK_2P4 - bool "神奇按钮 Magiclick_2.4" + bool "神奇按钮 Magiclick_2.4" + config BOARD_TYPE_MAGICLICK_2P5 + bool "神奇按钮 Magiclick_2.5" config BOARD_TYPE_MAGICLICK_C3 - bool "神奇按钮 Magiclick_C3" + bool "神奇按钮 Magiclick_C3" + config BOARD_TYPE_MAGICLICK_C3_V2 + bool "神奇按钮 Magiclick_C3_v2" config BOARD_TYPE_M5STACK_CORE_S3 bool "M5Stack CoreS3" config BOARD_TYPE_ATOMS3_ECHO_BASE diff --git a/main/boards/magiclick-2p5/config.h b/main/boards/magiclick-2p5/config.h new file mode 100644 index 00000000..46fc3fa5 --- /dev/null +++ b/main/boards/magiclick-2p5/config.h @@ -0,0 +1,50 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include + +#define AUDIO_INPUT_SAMPLE_RATE 24000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_8 +#define AUDIO_I2S_GPIO_WS GPIO_NUM_11 +#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_9 +#define AUDIO_I2S_GPIO_DIN GPIO_NUM_10 +#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_12 + +#define AUDIO_CODEC_PA_PIN GPIO_NUM_4 +#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_5 +#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_6 +#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR + +//led power +#define BUILTIN_LED_POWER GPIO_NUM_39 // 低电平有效 +#define BUILTIN_LED_POWER_OUTPUT_INVERT true + +#define BUILTIN_LED_NUM 2 +#define BUILTIN_LED_GPIO GPIO_NUM_38 + +#define MAIN_BUTTON_GPIO GPIO_NUM_21 +#define LEFT_BUTTON_GPIO GPIO_NUM_0 +#define RIGHT_BUTTON_GPIO GPIO_NUM_47 + +// display +#define DISPLAY_SDA_PIN GPIO_NUM_16 +#define DISPLAY_SCL_PIN GPIO_NUM_15 +#define DISPLAY_CS_PIN GPIO_NUM_14 +#define DISPLAY_DC_PIN GPIO_NUM_18 +#define DISPLAY_RST_PIN GPIO_NUM_17 + +#define DISPLAY_WIDTH 128 +#define DISPLAY_HEIGHT 128 +#define DISPLAY_MIRROR_X false +#define DISPLAY_MIRROR_Y false +#define DISPLAY_SWAP_XY false + +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 + +#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_13 +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/magiclick-2p5/config.json b/main/boards/magiclick-2p5/config.json new file mode 100644 index 00000000..05638f96 --- /dev/null +++ b/main/boards/magiclick-2p5/config.json @@ -0,0 +1,9 @@ +{ + "target": "esp32s3", + "builds": [ + { + "name": "magiclick-2p5", + "sdkconfig_append": [] + } + ] +} \ No newline at end of file diff --git a/main/boards/magiclick-2p5/magiclick_2p5_board.cc b/main/boards/magiclick-2p5/magiclick_2p5_board.cc new file mode 100644 index 00000000..caaa080a --- /dev/null +++ b/main/boards/magiclick-2p5/magiclick_2p5_board.cc @@ -0,0 +1,260 @@ +#include "wifi_board.h" +#include "display/lcd_display.h" +#include "audio_codecs/es8311_audio_codec.h" +#include "application.h" +#include "button.h" +#include "led/circular_strip.h" +#include "iot/thing_manager.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "font_awesome_symbols.h" + +#define TAG "magiclick_2p5" + +LV_FONT_DECLARE(font_puhui_16_4); +LV_FONT_DECLARE(font_awesome_16_4); + +class GC9107Display : public LcdDisplay { +public: + GC9107Display(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) + : LcdDisplay(panel_io, panel, backlight_pin, backlight_output_invert, + width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy, + { + .text_font = &font_puhui_16_4, + .icon_font = &font_awesome_16_4, + .emoji_font = font_emoji_32_init(), + }) { + + DisplayLockGuard lock(this); + // 只需要覆盖颜色相关的样式 + auto screen = lv_disp_get_scr_act(lv_disp_get_default()); + lv_obj_set_style_text_color(screen, lv_color_black(), 0); + + // 设置容器背景色 + lv_obj_set_style_bg_color(container_, lv_color_black(), 0); + + // 设置状态栏背景色和文本颜色 + lv_obj_set_style_bg_color(status_bar_, lv_color_white(), 0); + lv_obj_set_style_text_color(network_label_, lv_color_black(), 0); + lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0); + lv_obj_set_style_text_color(status_label_, lv_color_black(), 0); + lv_obj_set_style_text_color(mute_label_, lv_color_black(), 0); + lv_obj_set_style_text_color(battery_label_, lv_color_black(), 0); + + // 设置内容区背景色和文本颜色 + lv_obj_set_style_bg_color(content_, lv_color_black(), 0); + lv_obj_set_style_border_width(content_, 0, 0); + lv_obj_set_style_text_color(emotion_label_, lv_color_white(), 0); + lv_obj_set_style_text_color(chat_message_label_, lv_color_white(), 0); + } +}; + +static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = { + // {cmd, { data }, data_size, delay_ms} + {0xfe, (uint8_t[]){0x00}, 0, 0}, + {0xef, (uint8_t[]){0x00}, 0, 0}, + {0xb0, (uint8_t[]){0xc0}, 1, 0}, + {0xb1, (uint8_t[]){0x80}, 1, 0}, + {0xb2, (uint8_t[]){0x27}, 1, 0}, + {0xb3, (uint8_t[]){0x13}, 1, 0}, + {0xb6, (uint8_t[]){0x19}, 1, 0}, + {0xb7, (uint8_t[]){0x05}, 1, 0}, + {0xac, (uint8_t[]){0xc8}, 1, 0}, + {0xab, (uint8_t[]){0x0f}, 1, 0}, + {0x3a, (uint8_t[]){0x05}, 1, 0}, + {0xb4, (uint8_t[]){0x04}, 1, 0}, + {0xa8, (uint8_t[]){0x08}, 1, 0}, + {0xb8, (uint8_t[]){0x08}, 1, 0}, + {0xea, (uint8_t[]){0x02}, 1, 0}, + {0xe8, (uint8_t[]){0x2A}, 1, 0}, + {0xe9, (uint8_t[]){0x47}, 1, 0}, + {0xe7, (uint8_t[]){0x5f}, 1, 0}, + {0xc6, (uint8_t[]){0x21}, 1, 0}, + {0xc7, (uint8_t[]){0x15}, 1, 0}, + {0xf0, + (uint8_t[]){0x1D, 0x38, 0x09, 0x4D, 0x92, 0x2F, 0x35, 0x52, 0x1E, 0x0C, + 0x04, 0x12, 0x14, 0x1f}, + 14, 0}, + {0xf1, + (uint8_t[]){0x16, 0x40, 0x1C, 0x54, 0xA9, 0x2D, 0x2E, 0x56, 0x10, 0x0D, + 0x0C, 0x1A, 0x14, 0x1E}, + 14, 0}, + {0xf4, (uint8_t[]){0x00, 0x00, 0xFF}, 3, 0}, + {0xba, (uint8_t[]){0xFF, 0xFF}, 2, 0}, +}; + +class magiclick_2p5 : public WifiBoard { +private: + i2c_master_bus_handle_t codec_i2c_bus_; + Button main_button_; + Button left_button_; + Button right_button_; + GC9107Display* display_; + + void InitializeCodecI2c() { + // Initialize I2C peripheral + i2c_master_bus_config_t i2c_bus_cfg = { + .i2c_port = I2C_NUM_0, + .sda_io_num = AUDIO_CODEC_I2C_SDA_PIN, + .scl_io_num = AUDIO_CODEC_I2C_SCL_PIN, + .clk_source = I2C_CLK_SRC_DEFAULT, + .glitch_ignore_cnt = 7, + .intr_priority = 0, + .trans_queue_depth = 0, + .flags = { + .enable_internal_pullup = 1, + }, + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_)); + } + + void InitializeButtons() { + main_button_.OnPressDown([this]() { + Application::GetInstance().StartListening(); + }); + main_button_.OnPressUp([this]() { + Application::GetInstance().StopListening(); + }); + + left_button_.OnClick([this]() { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { + ResetWifiConfiguration(); + } + auto codec = GetAudioCodec(); + auto volume = codec->output_volume() - 10; + if (volume < 0) { + volume = 0; + } + codec->SetOutputVolume(volume); + GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + }); + + left_button_.OnLongPress([this]() { + GetAudioCodec()->SetOutputVolume(0); + GetDisplay()->ShowNotification("已静音"); + }); + + right_button_.OnClick([this]() { + auto codec = GetAudioCodec(); + auto volume = codec->output_volume() + 10; + if (volume > 100) { + volume = 100; + } + codec->SetOutputVolume(volume); + GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + }); + + right_button_.OnLongPress([this]() { + GetAudioCodec()->SetOutputVolume(100); + GetDisplay()->ShowNotification("最大音量"); + }); + + + } + + void InitializeLedPower() { + // 设置GPIO模式 + gpio_reset_pin(BUILTIN_LED_POWER); + 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; + buscfg.miso_io_num = GPIO_NUM_NC; + buscfg.sclk_io_num = DISPLAY_SCL_PIN; + 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 InitializeGc9107Display(){ + esp_lcd_panel_io_handle_t panel_io = nullptr; + esp_lcd_panel_handle_t panel = nullptr; + // 液晶屏控制IO初始化 + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = {}; + io_config.cs_gpio_num = DISPLAY_CS_PIN; + io_config.dc_gpio_num = DISPLAY_DC_PIN; + io_config.spi_mode = 0; + io_config.pclk_hz = 40 * 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)); + + // 初始化液晶屏驱动芯片NV3023 + ESP_LOGD(TAG, "Install LCD driver"); + gc9a01_vendor_config_t gc9107_vendor_config = { + .init_cmds = gc9107_lcd_init_cmds, + .init_cmds_size = sizeof(gc9107_lcd_init_cmds) / sizeof(gc9a01_lcd_init_cmd_t), + }; + esp_lcd_panel_dev_config_t panel_config = {}; + panel_config.reset_gpio_num = DISPLAY_RST_PIN; + panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB; + panel_config.bits_per_pixel = 16; + panel_config.vendor_config = &gc9107_vendor_config; + + esp_lcd_new_panel_gc9a01(panel_io, &panel_config, &panel); + + esp_lcd_panel_reset(panel); + + esp_lcd_panel_init(panel); + esp_lcd_panel_invert_color(panel, false); + 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 GC9107Display(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); + } + + // 物联网初始化,添加对 AI 可见设备 + void InitializeIot() { + auto& thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + thing_manager.AddThing(iot::CreateThing("Backlight")); + } + +public: + magiclick_2p5() : + main_button_(MAIN_BUTTON_GPIO), + left_button_(LEFT_BUTTON_GPIO), + right_button_(RIGHT_BUTTON_GPIO) { + InitializeCodecI2c(); + InitializeButtons(); + InitializeLedPower(); + InitializeSpi(); + InitializeGc9107Display(); + InitializeIot(); + } + + virtual Led* GetLed() override { + static CircularStrip led(BUILTIN_LED_GPIO, BUILTIN_LED_NUM); + return &led; + } + + virtual AudioCodec* GetAudioCodec() override { + static Es8311AudioCodec audio_codec(codec_i2c_bus_, I2C_NUM_0, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, + AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN, + AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR); + return &audio_codec; + } + + virtual Display* GetDisplay() override { + return display_; + } +}; + +DECLARE_BOARD(magiclick_2p5); diff --git a/main/boards/magiclick-c3-v2/config.h b/main/boards/magiclick-c3-v2/config.h new file mode 100644 index 00000000..5609bf64 --- /dev/null +++ b/main/boards/magiclick-c3-v2/config.h @@ -0,0 +1,47 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include + +#define AUDIO_INPUT_SAMPLE_RATE 24000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define AUDIO_I2S_GPIO_MCLK GPIO_NUM_5 +#define AUDIO_I2S_GPIO_WS GPIO_NUM_8 +#define AUDIO_I2S_GPIO_BCLK GPIO_NUM_6 +#define AUDIO_I2S_GPIO_DIN GPIO_NUM_7 +#define AUDIO_I2S_GPIO_DOUT GPIO_NUM_10 + +#define AUDIO_CODEC_PA_PIN GPIO_NUM_11 +#define AUDIO_CODEC_I2C_SDA_PIN GPIO_NUM_3 +#define AUDIO_CODEC_I2C_SCL_PIN GPIO_NUM_4 +#define AUDIO_CODEC_ES8311_ADDR ES8311_CODEC_DEFAULT_ADDR + +#define BUILTIN_LED_NUM 1 +#define BUILTIN_LED_GPIO GPIO_NUM_0 + +#define BOOT_BUTTON_GPIO GPIO_NUM_2 + +//battery +#define BUILTIN_BATTERY_GPIO GPIO_NUM_1 + +// display +#define DISPLAY_SDA_PIN GPIO_NUM_13 +#define DISPLAY_SCL_PIN GPIO_NUM_12 +#define DISPLAY_CS_PIN GPIO_NUM_20 +#define DISPLAY_DC_PIN GPIO_NUM_21 +#define DISPLAY_RST_PIN GPIO_NUM_NC + +#define DISPLAY_WIDTH 128 +#define DISPLAY_HEIGHT 128 +#define DISPLAY_MIRROR_X false +#define DISPLAY_MIRROR_Y false +#define DISPLAY_SWAP_XY false + +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 + +#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_9 +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT true + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/magiclick-c3-v2/config.json b/main/boards/magiclick-c3-v2/config.json new file mode 100644 index 00000000..2f221b55 --- /dev/null +++ b/main/boards/magiclick-c3-v2/config.json @@ -0,0 +1,9 @@ +{ + "target": "esp32c3", + "builds": [ + { + "name": "magiclick-c3-v2", + "sdkconfig_append": [] + } + ] +} \ No newline at end of file diff --git a/main/boards/magiclick-c3-v2/magiclick_c3_v2_board.cc b/main/boards/magiclick-c3-v2/magiclick_c3_v2_board.cc new file mode 100644 index 00000000..c1cb8321 --- /dev/null +++ b/main/boards/magiclick-c3-v2/magiclick_c3_v2_board.cc @@ -0,0 +1,220 @@ +#include "wifi_board.h" +#include "display/lcd_display.h" +#include "audio_codecs/es8311_audio_codec.h" +#include "application.h" +#include "button.h" +#include "led/single_led.h" +#include "iot/thing_manager.h" +#include "config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include "font_awesome_symbols.h" +#include + +#define TAG "magiclick_c3_v2" + +LV_FONT_DECLARE(font_puhui_16_4); +LV_FONT_DECLARE(font_awesome_16_4); + +class GC9107Display : public LcdDisplay { +public: + GC9107Display(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) + : LcdDisplay(panel_io, panel, backlight_pin, backlight_output_invert, + width, height, offset_x, offset_y, mirror_x, mirror_y, swap_xy, + { + .text_font = &font_puhui_16_4, + .icon_font = &font_awesome_16_4, + .emoji_font = font_emoji_32_init(), + }) { + + DisplayLockGuard lock(this); + // 只需要覆盖颜色相关的样式 + auto screen = lv_disp_get_scr_act(lv_disp_get_default()); + lv_obj_set_style_text_color(screen, lv_color_black(), 0); + + // 设置容器背景色 + lv_obj_set_style_bg_color(container_, lv_color_black(), 0); + + // 设置状态栏背景色和文本颜色 + lv_obj_set_style_bg_color(status_bar_, lv_color_make(0x1e, 0x90, 0xff), 0); + lv_obj_set_style_text_color(network_label_, lv_color_black(), 0); + lv_obj_set_style_text_color(notification_label_, lv_color_black(), 0); + lv_obj_set_style_text_color(status_label_, lv_color_black(), 0); + lv_obj_set_style_text_color(mute_label_, lv_color_black(), 0); + lv_obj_set_style_text_color(battery_label_, lv_color_black(), 0); + + // 设置内容区背景色和文本颜色 + lv_obj_set_style_bg_color(content_, lv_color_black(), 0); + lv_obj_set_style_border_width(content_, 0, 0); + lv_obj_set_style_text_color(emotion_label_, lv_color_white(), 0); + lv_obj_set_style_text_color(chat_message_label_, lv_color_white(), 0); + } +}; + +static const gc9a01_lcd_init_cmd_t gc9107_lcd_init_cmds[] = { + // {cmd, { data }, data_size, delay_ms} + {0xfe, (uint8_t[]){0x00}, 0, 0}, + {0xef, (uint8_t[]){0x00}, 0, 0}, + {0xb0, (uint8_t[]){0xc0}, 1, 0}, + {0xb1, (uint8_t[]){0x80}, 1, 0}, + {0xb2, (uint8_t[]){0x27}, 1, 0}, + {0xb3, (uint8_t[]){0x13}, 1, 0}, + {0xb6, (uint8_t[]){0x19}, 1, 0}, + {0xb7, (uint8_t[]){0x05}, 1, 0}, + {0xac, (uint8_t[]){0xc8}, 1, 0}, + {0xab, (uint8_t[]){0x0f}, 1, 0}, + {0x3a, (uint8_t[]){0x05}, 1, 0}, + {0xb4, (uint8_t[]){0x04}, 1, 0}, + {0xa8, (uint8_t[]){0x08}, 1, 0}, + {0xb8, (uint8_t[]){0x08}, 1, 0}, + {0xea, (uint8_t[]){0x02}, 1, 0}, + {0xe8, (uint8_t[]){0x2A}, 1, 0}, + {0xe9, (uint8_t[]){0x47}, 1, 0}, + {0xe7, (uint8_t[]){0x5f}, 1, 0}, + {0xc6, (uint8_t[]){0x21}, 1, 0}, + {0xc7, (uint8_t[]){0x15}, 1, 0}, + {0xf0, + (uint8_t[]){0x1D, 0x38, 0x09, 0x4D, 0x92, 0x2F, 0x35, 0x52, 0x1E, 0x0C, + 0x04, 0x12, 0x14, 0x1f}, + 14, 0}, + {0xf1, + (uint8_t[]){0x16, 0x40, 0x1C, 0x54, 0xA9, 0x2D, 0x2E, 0x56, 0x10, 0x0D, + 0x0C, 0x1A, 0x14, 0x1E}, + 14, 0}, + {0xf4, (uint8_t[]){0x00, 0x00, 0xFF}, 3, 0}, + {0xba, (uint8_t[]){0xFF, 0xFF}, 2, 0}, +}; + +class magiclick_c3_v2 : public WifiBoard { +private: + i2c_master_bus_handle_t codec_i2c_bus_; + Button boot_button_; + GC9107Display* display_; + + void InitializeCodecI2c() { + // Initialize I2C peripheral + i2c_master_bus_config_t i2c_bus_cfg = { + .i2c_port = I2C_NUM_0, + .sda_io_num = AUDIO_CODEC_I2C_SDA_PIN, + .scl_io_num = AUDIO_CODEC_I2C_SCL_PIN, + .clk_source = I2C_CLK_SRC_DEFAULT, + .glitch_ignore_cnt = 7, + .intr_priority = 0, + .trans_queue_depth = 0, + .flags = { + .enable_internal_pullup = 1, + }, + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &codec_i2c_bus_)); + } + + void InitializeButtons() { + boot_button_.OnClick([this]() { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { + ResetWifiConfiguration(); + } + }); + boot_button_.OnPressDown([this]() { + Application::GetInstance().StartListening(); + }); + boot_button_.OnPressUp([this]() { + Application::GetInstance().StopListening(); + }); + } + + void InitializeSpi() { + spi_bus_config_t buscfg = {}; + buscfg.mosi_io_num = DISPLAY_SDA_PIN; + buscfg.miso_io_num = GPIO_NUM_NC; + buscfg.sclk_io_num = DISPLAY_SCL_PIN; + 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(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO)); + } + + void InitializeGc9107Display(){ + esp_lcd_panel_io_handle_t panel_io = nullptr; + esp_lcd_panel_handle_t panel = nullptr; + // 液晶屏控制IO初始化 + ESP_LOGD(TAG, "Install panel IO"); + esp_lcd_panel_io_spi_config_t io_config = {}; + io_config.cs_gpio_num = DISPLAY_CS_PIN; + io_config.dc_gpio_num = DISPLAY_DC_PIN; + io_config.spi_mode = 0; + io_config.pclk_hz = 40 * 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(SPI2_HOST, &io_config, &panel_io)); + + // 初始化液晶屏驱动芯片GC9107 + ESP_LOGD(TAG, "Install LCD driver"); + gc9a01_vendor_config_t gc9107_vendor_config = { + .init_cmds = gc9107_lcd_init_cmds, + .init_cmds_size = sizeof(gc9107_lcd_init_cmds) / sizeof(gc9a01_lcd_init_cmd_t), + }; + esp_lcd_panel_dev_config_t panel_config = {}; + panel_config.reset_gpio_num = DISPLAY_RST_PIN; + panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB; + panel_config.bits_per_pixel = 16; + panel_config.vendor_config = &gc9107_vendor_config; + + esp_lcd_new_panel_gc9a01(panel_io, &panel_config, &panel); + + esp_lcd_panel_reset(panel); + esp_lcd_panel_init(panel); + esp_lcd_panel_invert_color(panel, false); + esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); + esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); + esp_lcd_panel_disp_on_off(panel, true); + display_ = new GC9107Display(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); + } + + // 物联网初始化,添加对 AI 可见设备 + void InitializeIot() { + auto& thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + thing_manager.AddThing(iot::CreateThing("Backlight")); + } + +public: + magiclick_c3_v2() : boot_button_(BOOT_BUTTON_GPIO) { + // 把 ESP32C3 的 VDD SPI 引脚作为普通 GPIO 口使用 + esp_efuse_write_field_bit(ESP_EFUSE_VDD_SPI_AS_GPIO); + + InitializeCodecI2c(); + InitializeButtons(); + InitializeSpi(); + InitializeGc9107Display(); + InitializeIot(); + } + + virtual Led* GetLed() override { + static SingleLed led(BUILTIN_LED_GPIO); + return &led; + } + + virtual AudioCodec* GetAudioCodec() override { + static Es8311AudioCodec audio_codec(codec_i2c_bus_, I2C_NUM_0, AUDIO_INPUT_SAMPLE_RATE, AUDIO_OUTPUT_SAMPLE_RATE, + AUDIO_I2S_GPIO_MCLK, AUDIO_I2S_GPIO_BCLK, AUDIO_I2S_GPIO_WS, AUDIO_I2S_GPIO_DOUT, AUDIO_I2S_GPIO_DIN, + AUDIO_CODEC_PA_PIN, AUDIO_CODEC_ES8311_ADDR); + return &audio_codec; + } + + virtual Display* GetDisplay() override { + return display_; + } +}; + +DECLARE_BOARD(magiclick_c3_v2);