From 5bce5c3f709ab9ea986b151b115972bb28aa0e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=8C=E4=B9=9D?= <153150268+Sail-211010@users.noreply.github.com> Date: Fri, 14 Feb 2025 17:53:22 +0800 Subject: [PATCH] Added several boards for Waveshare (#159) * Added SPD2010 display adaptation Added SPD2010 display adaptation * Added other channel configurations Added functions for other channel applications * Add new boards Add new boards * Add new boards Add new boards * Update display compatibility Update display compatibility * The lcd display.cc changes are restored The lcd display.cc changes are restored * Modify the SPD2010 adaptation to the board file Modify the SPD2010 adaptation to the board file * The lcd display.cc changes are restored The lcd display.cc changes are restored * New backlight control New backlight control * New backlight control New backlight control * Add backlight controls Add backlight controls * Delete main/boards/esp32-s3-touch-lcd-1.85c/esp32-s3-touch-lcd-1.85c directory Add path error * Add backlight controls Add backlight controls * Update variable name Update variable name * Eliminate unnecessary programs Eliminate unnecessary programs * Update esp32-s3-touch-lcd-1.46.cc * Update esp32-s3-touch-lcd-1.85.cc Eliminate unnecessary programs * Update esp32-s3-touch-lcd-1.85c.cc Eliminate unnecessary programs * Update no_audio_codec.cc * Update esp32-s3-touch-lcd-1.46.cc * Update esp32-s3-touch-lcd-1.85.cc * Update esp32-s3-touch-lcd-1.85c.cc --------- Co-authored-by: Xiaoxia --- main/CMakeLists.txt | 6 + main/Kconfig.projbuild | 6 + main/audio_codecs/no_audio_codec.cc | 71 ++++++- main/audio_codecs/no_audio_codec.h | 1 + main/boards/esp32-s3-touch-lcd-1.46/config.h | 68 ++++++ .../esp32-s3-touch-lcd-1.46.cc | 199 ++++++++++++++++++ main/boards/esp32-s3-touch-lcd-1.85/config.h | 67 ++++++ .../esp32-s3-touch-lcd-1.85.cc | 163 ++++++++++++++ main/boards/esp32-s3-touch-lcd-1.85c/config.h | 67 ++++++ .../esp32-s3-touch-lcd-1.85c.cc | 162 ++++++++++++++ main/idf_component.yml | 2 + 11 files changed, 811 insertions(+), 1 deletion(-) create mode 100644 main/boards/esp32-s3-touch-lcd-1.46/config.h create mode 100644 main/boards/esp32-s3-touch-lcd-1.46/esp32-s3-touch-lcd-1.46.cc create mode 100644 main/boards/esp32-s3-touch-lcd-1.85/config.h create mode 100644 main/boards/esp32-s3-touch-lcd-1.85/esp32-s3-touch-lcd-1.85.cc create mode 100644 main/boards/esp32-s3-touch-lcd-1.85c/config.h create mode 100644 main/boards/esp32-s3-touch-lcd-1.85c/esp32-s3-touch-lcd-1.85c.cc diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 2f7d78b0..3873cbc5 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -77,6 +77,12 @@ elseif(CONFIG_BOARD_TYPE_ESP_SPARKBOT) set(BOARD_TYPE "esp-sparkbot") elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8) set(BOARD_TYPE "esp32-s3-touch-amoled-1.8") +elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85C) + set(BOARD_TYPE "esp32-s3-touch-lcd-1.85c") +elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_85) + set(BOARD_TYPE "esp32-s3-touch-lcd-1.85") +elseif(CONFIG_BOARD_TYPE_ESP32S3_Touch_LCD_1_46) + set(BOARD_TYPE "esp32-s3-touch-lcd-1.46") elseif(CONFIG_BOARD_TYPE_BREAD_COMPACT_WIFI_LCD) set(BOARD_TYPE "bread-compact-wifi-lcd") elseif(CONFIG_BOARD_TYPE_TUDOUZI) diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 34ec7963..8e591971 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -78,6 +78,12 @@ choice BOARD_TYPE bool "ESP-SparkBot开发板" config BOARD_TYPE_ESP32S3_Touch_AMOLED_1_8 bool "Waveshare ESP32-S3-Touch-AMOLED-1.8" + config BOARD_TYPE_ESP32S3_Touch_LCD_1_85C + bool "Waveshare ESP32-S3-Touch-LCD-1.85C" + config BOARD_TYPE_ESP32S3_Touch_LCD_1_85 + bool "Waveshare ESP32-S3-Touch-LCD-1.85" + config BOARD_TYPE_ESP32S3_Touch_LCD_1_46 + bool "Waveshare ESP32-S3-Touch-LCD-1.46" config BOARD_TYPE_TUDOUZI bool "土豆子" config BOARD_TYPE_LILYGO_T_CIRCLE_S3 diff --git a/main/audio_codecs/no_audio_codec.cc b/main/audio_codecs/no_audio_codec.cc index e38da64d..289686c9 100644 --- a/main/audio_codecs/no_audio_codec.cc +++ b/main/audio_codecs/no_audio_codec.cc @@ -14,7 +14,6 @@ NoAudioCodec::~NoAudioCodec() { } } - NoAudioCodecDuplex::NoAudioCodecDuplex(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din) { duplex_ = true; input_sample_rate_ = input_sample_rate; @@ -201,6 +200,76 @@ NoAudioCodecSimplex::NoAudioCodecSimplex(int input_sample_rate, int output_sampl ESP_LOGI(TAG, "Simplex channels created"); } +NoAudioCodecSimplex::NoAudioCodecSimplex(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, i2s_std_slot_mask_t spk_slot_mask, gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din, i2s_std_slot_mask_t mic_slot_mask){ + duplex_ = false; + input_sample_rate_ = input_sample_rate; + output_sample_rate_ = output_sample_rate; + + // Create a new channel for speaker + i2s_chan_config_t chan_cfg = { + .id = (i2s_port_t)0, + .role = I2S_ROLE_MASTER, + .dma_desc_num = 6, + .dma_frame_num = 240, + .auto_clear_after_cb = true, + .auto_clear_before_cb = false, + .intr_priority = 0, + }; + ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, nullptr)); + + i2s_std_config_t std_cfg = { + .clk_cfg = { + .sample_rate_hz = (uint32_t)output_sample_rate_, + .clk_src = I2S_CLK_SRC_DEFAULT, + .mclk_multiple = I2S_MCLK_MULTIPLE_256, + #ifdef I2S_HW_VERSION_2 + .ext_clk_freq_hz = 0, + #endif + + }, + .slot_cfg = { + .data_bit_width = I2S_DATA_BIT_WIDTH_32BIT, + .slot_bit_width = I2S_SLOT_BIT_WIDTH_AUTO, + .slot_mode = I2S_SLOT_MODE_MONO, + .slot_mask = spk_slot_mask, + .ws_width = I2S_DATA_BIT_WIDTH_32BIT, + .ws_pol = false, + .bit_shift = true, + #ifdef I2S_HW_VERSION_2 + .left_align = true, + .big_endian = false, + .bit_order_lsb = false + #endif + + }, + .gpio_cfg = { + .mclk = I2S_GPIO_UNUSED, + .bclk = spk_bclk, + .ws = spk_ws, + .dout = spk_dout, + .din = I2S_GPIO_UNUSED, + .invert_flags = { + .mclk_inv = false, + .bclk_inv = false, + .ws_inv = false + } + } + }; + ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &std_cfg)); + + // Create a new channel for MIC + chan_cfg.id = (i2s_port_t)1; + ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, nullptr, &rx_handle_)); + std_cfg.clk_cfg.sample_rate_hz = (uint32_t)input_sample_rate_; + std_cfg.slot_cfg.slot_mask = mic_slot_mask; + std_cfg.gpio_cfg.bclk = mic_sck; + std_cfg.gpio_cfg.ws = mic_ws; + std_cfg.gpio_cfg.dout = I2S_GPIO_UNUSED; + std_cfg.gpio_cfg.din = mic_din; + ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &std_cfg)); + ESP_LOGI(TAG, "Simplex channels created"); +} + NoAudioCodecSimplexPdm::NoAudioCodecSimplexPdm(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_din) { duplex_ = false; input_sample_rate_ = input_sample_rate; diff --git a/main/audio_codecs/no_audio_codec.h b/main/audio_codecs/no_audio_codec.h index 087b7af8..20c23ec7 100644 --- a/main/audio_codecs/no_audio_codec.h +++ b/main/audio_codecs/no_audio_codec.h @@ -28,6 +28,7 @@ public: class NoAudioCodecSimplex : public NoAudioCodec { public: NoAudioCodecSimplex(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din); + NoAudioCodecSimplex(int input_sample_rate, int output_sample_rate, gpio_num_t spk_bclk, gpio_num_t spk_ws, gpio_num_t spk_dout, i2s_std_slot_mask_t spk_slot_mask, gpio_num_t mic_sck, gpio_num_t mic_ws, gpio_num_t mic_din, i2s_std_slot_mask_t mic_slot_mask); }; class NoAudioCodecSimplexPdm : public NoAudioCodec { diff --git a/main/boards/esp32-s3-touch-lcd-1.46/config.h b/main/boards/esp32-s3-touch-lcd-1.46/config.h new file mode 100644 index 00000000..7ce9106e --- /dev/null +++ b/main/boards/esp32-s3-touch-lcd-1.46/config.h @@ -0,0 +1,68 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include +#include + +#define AUDIO_INPUT_SAMPLE_RATE 16000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define BOOT_BUTTON_GPIO GPIO_NUM_0 + + +#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_2 +#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_15 +#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_39 +#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_47 +#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_48 +#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_38 + +#define I2C_SCL_IO GPIO_NUM_10 +#define I2C_SDA_IO GPIO_NUM_11 + +#define I2C_ADDRESS ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000 + +#define DISPLAY_WIDTH 412 +#define DISPLAY_HEIGHT 412 +#define DISPLAY_MIRROR_X false +#define DISPLAY_MIRROR_Y false +#define DISPLAY_SWAP_XY false + +#define QSPI_LCD_H_RES (412) +#define QSPI_LCD_V_RES (412) +#define QSPI_LCD_BIT_PER_PIXEL (16) + +#define QSPI_LCD_HOST SPI2_HOST +#define QSPI_PIN_NUM_LCD_PCLK GPIO_NUM_40 +#define QSPI_PIN_NUM_LCD_CS GPIO_NUM_21 +#define QSPI_PIN_NUM_LCD_DATA0 GPIO_NUM_46 +#define QSPI_PIN_NUM_LCD_DATA1 GPIO_NUM_45 +#define QSPI_PIN_NUM_LCD_DATA2 GPIO_NUM_42 +#define QSPI_PIN_NUM_LCD_DATA3 GPIO_NUM_41 +#define QSPI_PIN_NUM_LCD_RST GPIO_NUM_NC +#define QSPI_PIN_NUM_LCD_BL GPIO_NUM_5 + +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 + +#define TP_PORT (I2C_NUM_1) +#define TP_PIN_NUM_SDA (I2C_SDA_IO) +#define TP_PIN_NUM_SCL (I2C_SCL_IO) +#define TP_PIN_NUM_RST (GPIO_NUM_NC) +#define TP_PIN_NUM_INT (GPIO_NUM_4) + +#define DISPLAY_BACKLIGHT_PIN QSPI_PIN_NUM_LCD_BL +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false + +#define TAIJIPI_SPD2010_PANEL_BUS_QSPI_CONFIG(sclk, d0, d1, d2, d3, max_trans_sz) \ + { \ + .data0_io_num = d0, \ + .data1_io_num = d1, \ + .sclk_io_num = sclk, \ + .data2_io_num = d2, \ + .data3_io_num = d3, \ + .max_transfer_sz = max_trans_sz, \ + } + + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/esp32-s3-touch-lcd-1.46/esp32-s3-touch-lcd-1.46.cc b/main/boards/esp32-s3-touch-lcd-1.46/esp32-s3-touch-lcd-1.46.cc new file mode 100644 index 00000000..d44c3627 --- /dev/null +++ b/main/boards/esp32-s3-touch-lcd-1.46/esp32-s3-touch-lcd-1.46.cc @@ -0,0 +1,199 @@ +#include "wifi_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 "iot/thing_manager.h" + +#include +#include "i2c_device.h" +#include +#include +#include +#include +#include +#include +#include +#include "esp_io_expander_tca9554.h" +#include "lcd_display.h" +#define TAG "waveshare_lcd_1_46" + +LV_FONT_DECLARE(font_puhui_16_4); +LV_FONT_DECLARE(font_awesome_16_4); + + +// 在waveshare_lcd_1_46类之前添加新的显示类 +class CustomLcdDisplay : public LcdDisplay { +public: + static void rounder_event_cb(lv_event_t * e) + { + lv_area_t * area = (lv_area_t *)lv_event_get_param(e); + uint16_t x1 = area->x1; + uint16_t x2 = area->x2; + + area->x1 = (x1 >> 2) << 2; // round the start of coordinate down to the nearest 4M number + area->x2 = ((x2 >> 2) << 2) + 3; // round the end of coordinate up to the nearest 4N+3 number + } + + CustomLcdDisplay(esp_lcd_panel_io_handle_t io_handle, + esp_lcd_panel_handle_t panel_handle, + 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(io_handle, panel_handle, 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_64_init(), + }) { + + DisplayLockGuard lock(this); + + lv_display_add_event_cb(display_, rounder_event_cb, LV_EVENT_INVALIDATE_AREA, NULL); + + + } +}; + +class CustomBoard : public WifiBoard { +private: + + Button boot_button_; + i2c_master_bus_handle_t i2c_bus_; + esp_io_expander_handle_t io_expander = NULL; + LcdDisplay* display_; + + void InitializeI2c() { + // Initialize I2C peripheral + i2c_master_bus_config_t i2c_bus_cfg = { + .i2c_port = (i2c_port_t)0, + .sda_io_num = I2C_SDA_IO, + .scl_io_num = I2C_SCL_IO, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_)); + } + + void i2c_dev_tca9554_init(void) + { + esp_err_t ret = esp_io_expander_new_i2c_tca9554(i2c_bus_, I2C_ADDRESS, &io_expander); + if(ret != ESP_OK) + ESP_LOGE(TAG, "TCA9554 create returned error"); + + // uint32_t input_level_mask = 0; + // ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_INPUT); // 设置引脚 EXIO0 和 EXIO1 模式为输入 + // ret = esp_io_expander_get_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, &input_level_mask); // 获取引脚 EXIO0 和 EXIO1 的电平状态,存放在 input_level_mask 中 + + // ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, IO_EXPANDER_OUTPUT); // 设置引脚 EXIO2 和 EXIO3 模式为输出 + // ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, 1); // 将引脚电平设置为 1 + // ret = esp_io_expander_print_state(io_expander); // 打印引脚状态 + + ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT); // 设置引脚 EXIO0 和 EXIO1 模式为输出 + ESP_ERROR_CHECK(ret); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + vTaskDelay(pdMS_TO_TICKS(300)); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + vTaskDelay(pdMS_TO_TICKS(300)); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + } + + void InitializeSpi() { + ESP_LOGI(TAG, "Initialize QSPI bus"); + + const spi_bus_config_t bus_config = TAIJIPI_SPD2010_PANEL_BUS_QSPI_CONFIG(QSPI_PIN_NUM_LCD_PCLK, + QSPI_PIN_NUM_LCD_DATA0, + QSPI_PIN_NUM_LCD_DATA1, + QSPI_PIN_NUM_LCD_DATA2, + QSPI_PIN_NUM_LCD_DATA3, + QSPI_LCD_H_RES * 80 * sizeof(uint16_t)); + ESP_ERROR_CHECK(spi_bus_initialize(QSPI_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO)); + } + + void Initializespd2010Display() { + esp_lcd_panel_io_handle_t panel_io = nullptr; + esp_lcd_panel_handle_t panel = nullptr; + + ESP_LOGI(TAG, "Install panel IO"); + + const esp_lcd_panel_io_spi_config_t io_config = SPD2010_PANEL_IO_QSPI_CONFIG(QSPI_PIN_NUM_LCD_CS, NULL, NULL); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)QSPI_LCD_HOST, &io_config, &panel_io)); + + ESP_LOGI(TAG, "Install SPD2010 panel driver"); + + spd2010_vendor_config_t vendor_config = { + .flags = { + .use_qspi_interface = 1, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = QSPI_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = QSPI_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_spd2010(panel_io, &panel_config, &panel)); + + esp_lcd_panel_reset(panel); + esp_lcd_panel_init(panel); + esp_lcd_panel_disp_on_off(panel, true); + esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); + esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); + display_ = new CustomLcdDisplay(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); + } + + void InitializeButtons() { + boot_button_.OnClick([this]() { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { + ResetWifiConfiguration(); + } + app.ToggleChatState(); + }); + } + + // 物联网初始化,添加对 AI 可见设备 + void InitializeIot() { + auto& thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + thing_manager.AddThing(iot::CreateThing("Backlight")); + } + +public: + CustomBoard() : + boot_button_(BOOT_BUTTON_GPIO) { + InitializeI2c(); + i2c_dev_tca9554_init(); + InitializeSpi(); + Initializespd2010Display(); + InitializeButtons(); + InitializeIot(); + } + + 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, I2S_STD_SLOT_LEFT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN, I2S_STD_SLOT_RIGHT); // I2S_STD_SLOT_LEFT / I2S_STD_SLOT_RIGHT / I2S_STD_SLOT_BOTH + + return &audio_codec; + } + + + virtual Display* GetDisplay() override { + return display_; + } + +}; + +DECLARE_BOARD(CustomBoard); diff --git a/main/boards/esp32-s3-touch-lcd-1.85/config.h b/main/boards/esp32-s3-touch-lcd-1.85/config.h new file mode 100644 index 00000000..7864c2ca --- /dev/null +++ b/main/boards/esp32-s3-touch-lcd-1.85/config.h @@ -0,0 +1,67 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include +#include + +#define AUDIO_INPUT_SAMPLE_RATE 16000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define BOOT_BUTTON_GPIO GPIO_NUM_0 + +#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_2 +#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_15 +#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_39 +#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_47 +#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_48 +#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_38 + +#define I2C_SCL_IO GPIO_NUM_10 +#define I2C_SDA_IO GPIO_NUM_11 + +#define I2C_ADDRESS ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000 + +#define DISPLAY_WIDTH 360 +#define DISPLAY_HEIGHT 360 +#define DISPLAY_MIRROR_X false +#define DISPLAY_MIRROR_Y false +#define DISPLAY_SWAP_XY false + +#define QSPI_LCD_H_RES (360) +#define QSPI_LCD_V_RES (360) +#define QSPI_LCD_BIT_PER_PIXEL (16) + +#define QSPI_LCD_HOST SPI2_HOST +#define QSPI_PIN_NUM_LCD_PCLK GPIO_NUM_40 +#define QSPI_PIN_NUM_LCD_CS GPIO_NUM_21 +#define QSPI_PIN_NUM_LCD_DATA0 GPIO_NUM_46 +#define QSPI_PIN_NUM_LCD_DATA1 GPIO_NUM_45 +#define QSPI_PIN_NUM_LCD_DATA2 GPIO_NUM_42 +#define QSPI_PIN_NUM_LCD_DATA3 GPIO_NUM_41 +#define QSPI_PIN_NUM_LCD_RST GPIO_NUM_NC +#define QSPI_PIN_NUM_LCD_BL GPIO_NUM_5 + +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 + +#define TP_PORT (I2C_NUM_1) +#define TP_PIN_NUM_SDA (GPIO_NUM_1) +#define TP_PIN_NUM_SCL (GPIO_NUM_3) +#define TP_PIN_NUM_RST (GPIO_NUM_NC) +#define TP_PIN_NUM_INT (GPIO_NUM_4) + +#define DISPLAY_BACKLIGHT_PIN QSPI_PIN_NUM_LCD_BL +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false + +#define TAIJIPI_ST77916_PANEL_BUS_QSPI_CONFIG(sclk, d0, d1, d2, d3, max_trans_sz) \ + { \ + .data0_io_num = d0, \ + .data1_io_num = d1, \ + .sclk_io_num = sclk, \ + .data2_io_num = d2, \ + .data3_io_num = d3, \ + .max_transfer_sz = max_trans_sz, \ + } + + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/esp32-s3-touch-lcd-1.85/esp32-s3-touch-lcd-1.85.cc b/main/boards/esp32-s3-touch-lcd-1.85/esp32-s3-touch-lcd-1.85.cc new file mode 100644 index 00000000..9f0d1d05 --- /dev/null +++ b/main/boards/esp32-s3-touch-lcd-1.85/esp32-s3-touch-lcd-1.85.cc @@ -0,0 +1,163 @@ +#include "wifi_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 "iot/thing_manager.h" + +#include +#include "i2c_device.h" +#include +#include +#include +#include +#include +#include +#include +#include "esp_io_expander_tca9554.h" + +#define TAG "waveshare_lcd_1_85" + +LV_FONT_DECLARE(font_puhui_16_4); +LV_FONT_DECLARE(font_awesome_16_4); + + +class CustomBoard : public WifiBoard { +private: + Button boot_button_; + i2c_master_bus_handle_t i2c_bus_; + esp_io_expander_handle_t io_expander = NULL; + LcdDisplay* display_; + + void InitializeI2c() { + // Initialize I2C peripheral + i2c_master_bus_config_t i2c_bus_cfg = { + .i2c_port = (i2c_port_t)0, + .sda_io_num = I2C_SDA_IO, + .scl_io_num = I2C_SCL_IO, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_)); + } + + void i2c_dev_tca9554_init(void) + { + esp_err_t ret = esp_io_expander_new_i2c_tca9554(i2c_bus_, I2C_ADDRESS, &io_expander); + if(ret != ESP_OK) + ESP_LOGE(TAG, "TCA9554 create returned error"); + + // uint32_t input_level_mask = 0; + // ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_INPUT); // 设置引脚 EXIO0 和 EXIO1 模式为输入 + // ret = esp_io_expander_get_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, &input_level_mask); // 获取引脚 EXIO0 和 EXIO1 的电平状态,存放在 input_level_mask 中 + + // ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, IO_EXPANDER_OUTPUT); // 设置引脚 EXIO2 和 EXIO3 模式为输出 + // ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, 1); // 将引脚电平设置为 1 + // ret = esp_io_expander_print_state(io_expander); // 打印引脚状态 + + ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT); // 设置引脚 EXIO0 和 EXIO1 模式为输出 + ESP_ERROR_CHECK(ret); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + vTaskDelay(pdMS_TO_TICKS(300)); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + vTaskDelay(pdMS_TO_TICKS(300)); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + } + + void InitializeSpi() { + ESP_LOGI(TAG, "Initialize QSPI bus"); + + const spi_bus_config_t bus_config = TAIJIPI_ST77916_PANEL_BUS_QSPI_CONFIG(QSPI_PIN_NUM_LCD_PCLK, + QSPI_PIN_NUM_LCD_DATA0, + QSPI_PIN_NUM_LCD_DATA1, + QSPI_PIN_NUM_LCD_DATA2, + QSPI_PIN_NUM_LCD_DATA3, + QSPI_LCD_H_RES * 80 * sizeof(uint16_t)); + ESP_ERROR_CHECK(spi_bus_initialize(QSPI_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO)); + } + + void Initializest77916Display() { + + esp_lcd_panel_io_handle_t panel_io = nullptr; + esp_lcd_panel_handle_t panel = nullptr; + + ESP_LOGI(TAG, "Install panel IO"); + + const esp_lcd_panel_io_spi_config_t io_config = ST77916_PANEL_IO_QSPI_CONFIG(QSPI_PIN_NUM_LCD_CS, NULL, NULL); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)QSPI_LCD_HOST, &io_config, &panel_io)); + + ESP_LOGI(TAG, "Install ST77916 panel driver"); + + st77916_vendor_config_t vendor_config = { + .flags = { + .use_qspi_interface = 1, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = QSPI_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = QSPI_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(panel_io, &panel_config, &panel)); + + esp_lcd_panel_reset(panel); + esp_lcd_panel_init(panel); + esp_lcd_panel_disp_on_off(panel, true); + esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); + esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); + + 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, + { + .text_font = &font_puhui_16_4, + .icon_font = &font_awesome_16_4, + .emoji_font = font_emoji_64_init(), + }); + } + + void InitializeButtons() { + boot_button_.OnClick([this]() { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { + ResetWifiConfiguration(); + } + app.ToggleChatState(); + }); + } + + // 物联网初始化,添加对 AI 可见设备 + void InitializeIot() { + auto& thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + thing_manager.AddThing(iot::CreateThing("Backlight")); + } + +public: + CustomBoard() : + boot_button_(BOOT_BUTTON_GPIO) { + InitializeI2c(); + i2c_dev_tca9554_init(); + InitializeSpi(); + Initializest77916Display(); + InitializeButtons(); + InitializeIot(); + } + + 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, I2S_STD_SLOT_BOTH, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN, I2S_STD_SLOT_RIGHT); // I2S_STD_SLOT_LEFT / I2S_STD_SLOT_RIGHT / I2S_STD_SLOT_BOTH + + return &audio_codec; + } + + virtual Display* GetDisplay() override { + return display_; + } +}; + +DECLARE_BOARD(CustomBoard); diff --git a/main/boards/esp32-s3-touch-lcd-1.85c/config.h b/main/boards/esp32-s3-touch-lcd-1.85c/config.h new file mode 100644 index 00000000..dfd5a891 --- /dev/null +++ b/main/boards/esp32-s3-touch-lcd-1.85c/config.h @@ -0,0 +1,67 @@ +#ifndef _BOARD_CONFIG_H_ +#define _BOARD_CONFIG_H_ + +#include +#include + +#define AUDIO_INPUT_SAMPLE_RATE 16000 +#define AUDIO_OUTPUT_SAMPLE_RATE 24000 + +#define BOOT_BUTTON_GPIO GPIO_NUM_0 + +#define AUDIO_I2S_MIC_GPIO_WS GPIO_NUM_2 +#define AUDIO_I2S_MIC_GPIO_SCK GPIO_NUM_15 +#define AUDIO_I2S_MIC_GPIO_DIN GPIO_NUM_39 +#define AUDIO_I2S_SPK_GPIO_DOUT GPIO_NUM_47 +#define AUDIO_I2S_SPK_GPIO_BCLK GPIO_NUM_48 +#define AUDIO_I2S_SPK_GPIO_LRCK GPIO_NUM_38 + +#define I2C_SCL_IO GPIO_NUM_10 +#define I2C_SDA_IO GPIO_NUM_11 + +#define I2C_ADDRESS ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000 + +#define DISPLAY_WIDTH 360 +#define DISPLAY_HEIGHT 360 +#define DISPLAY_MIRROR_X false +#define DISPLAY_MIRROR_Y false +#define DISPLAY_SWAP_XY false + +#define QSPI_LCD_H_RES (360) +#define QSPI_LCD_V_RES (360) +#define QSPI_LCD_BIT_PER_PIXEL (16) + +#define QSPI_LCD_HOST SPI2_HOST +#define QSPI_PIN_NUM_LCD_PCLK GPIO_NUM_40 +#define QSPI_PIN_NUM_LCD_CS GPIO_NUM_21 +#define QSPI_PIN_NUM_LCD_DATA0 GPIO_NUM_46 +#define QSPI_PIN_NUM_LCD_DATA1 GPIO_NUM_45 +#define QSPI_PIN_NUM_LCD_DATA2 GPIO_NUM_42 +#define QSPI_PIN_NUM_LCD_DATA3 GPIO_NUM_41 +#define QSPI_PIN_NUM_LCD_RST GPIO_NUM_NC +#define QSPI_PIN_NUM_LCD_BL GPIO_NUM_5 + +#define DISPLAY_OFFSET_X 0 +#define DISPLAY_OFFSET_Y 0 + +#define TP_PORT (I2C_NUM_1) +#define TP_PIN_NUM_SDA (I2C_SDA_IO) +#define TP_PIN_NUM_SCL (I2C_SCL_IO) +#define TP_PIN_NUM_RST (GPIO_NUM_NC) +#define TP_PIN_NUM_INT (GPIO_NUM_4) + +#define DISPLAY_BACKLIGHT_PIN QSPI_PIN_NUM_LCD_BL +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false + +#define TAIJIPI_ST77916_PANEL_BUS_QSPI_CONFIG(sclk, d0, d1, d2, d3, max_trans_sz) \ + { \ + .data0_io_num = d0, \ + .data1_io_num = d1, \ + .sclk_io_num = sclk, \ + .data2_io_num = d2, \ + .data3_io_num = d3, \ + .max_transfer_sz = max_trans_sz, \ + } + + +#endif // _BOARD_CONFIG_H_ diff --git a/main/boards/esp32-s3-touch-lcd-1.85c/esp32-s3-touch-lcd-1.85c.cc b/main/boards/esp32-s3-touch-lcd-1.85c/esp32-s3-touch-lcd-1.85c.cc new file mode 100644 index 00000000..02ff59fe --- /dev/null +++ b/main/boards/esp32-s3-touch-lcd-1.85c/esp32-s3-touch-lcd-1.85c.cc @@ -0,0 +1,162 @@ +#include "wifi_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 "iot/thing_manager.h" + +#include +#include "i2c_device.h" +#include +#include +#include +#include +#include +#include +#include +#include "esp_io_expander_tca9554.h" + +#define TAG "waveshare_lcd_1_85c" + +LV_FONT_DECLARE(font_puhui_16_4); +LV_FONT_DECLARE(font_awesome_16_4); + + +class CustomBoard : public WifiBoard { +private: + Button boot_button_; + i2c_master_bus_handle_t i2c_bus_; + esp_io_expander_handle_t io_expander = NULL; + LcdDisplay* display_; + + void InitializeI2c() { + // Initialize I2C peripheral + i2c_master_bus_config_t i2c_bus_cfg = { + .i2c_port = (i2c_port_t)0, + .sda_io_num = I2C_SDA_IO, + .scl_io_num = I2C_SCL_IO, + .clk_source = I2C_CLK_SRC_DEFAULT, + }; + ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_bus_cfg, &i2c_bus_)); + } + + void i2c_dev_tca9554_init(void) + { + esp_err_t ret = esp_io_expander_new_i2c_tca9554(i2c_bus_, I2C_ADDRESS, &io_expander); + if(ret != ESP_OK) + ESP_LOGE(TAG, "TCA9554 create returned error"); + + // uint32_t input_level_mask = 0; + // ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_INPUT); // 设置引脚 EXIO0 和 EXIO1 模式为输入 + // ret = esp_io_expander_get_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, &input_level_mask); // 获取引脚 EXIO0 和 EXIO1 的电平状态,存放在 input_level_mask 中 + + // ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, IO_EXPANDER_OUTPUT); // 设置引脚 EXIO2 和 EXIO3 模式为输出 + // ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_2 | IO_EXPANDER_PIN_NUM_3, 1); // 将引脚电平设置为 1 + // ret = esp_io_expander_print_state(io_expander); // 打印引脚状态 + + ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT); // 设置引脚 EXIO0 和 EXIO1 模式为输出 + ESP_ERROR_CHECK(ret); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + vTaskDelay(pdMS_TO_TICKS(300)); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + vTaskDelay(pdMS_TO_TICKS(300)); + ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 1); // 复位 LCD 与 TouchPad + ESP_ERROR_CHECK(ret); + } + + void InitializeSpi() { + ESP_LOGI(TAG, "Initialize QSPI bus"); + + const spi_bus_config_t bus_config = TAIJIPI_ST77916_PANEL_BUS_QSPI_CONFIG(QSPI_PIN_NUM_LCD_PCLK, + QSPI_PIN_NUM_LCD_DATA0, + QSPI_PIN_NUM_LCD_DATA1, + QSPI_PIN_NUM_LCD_DATA2, + QSPI_PIN_NUM_LCD_DATA3, + QSPI_LCD_H_RES * 80 * sizeof(uint16_t)); + ESP_ERROR_CHECK(spi_bus_initialize(QSPI_LCD_HOST, &bus_config, SPI_DMA_CH_AUTO)); + } + + void Initializest77916Display() { + esp_lcd_panel_io_handle_t panel_io = nullptr; + esp_lcd_panel_handle_t panel = nullptr; + + ESP_LOGI(TAG, "Install panel IO"); + + const esp_lcd_panel_io_spi_config_t io_config = ST77916_PANEL_IO_QSPI_CONFIG(QSPI_PIN_NUM_LCD_CS, NULL, NULL); + ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)QSPI_LCD_HOST, &io_config, &panel_io)); + + ESP_LOGI(TAG, "Install ST77916 panel driver"); + + st77916_vendor_config_t vendor_config = { + .flags = { + .use_qspi_interface = 1, + }, + }; + const esp_lcd_panel_dev_config_t panel_config = { + .reset_gpio_num = QSPI_PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, // Implemented by LCD command `36h` + .bits_per_pixel = QSPI_LCD_BIT_PER_PIXEL, // Implemented by LCD command `3Ah` (16/18) + .vendor_config = &vendor_config, + }; + ESP_ERROR_CHECK(esp_lcd_new_panel_st77916(panel_io, &panel_config, &panel)); + + esp_lcd_panel_reset(panel); + esp_lcd_panel_init(panel); + esp_lcd_panel_disp_on_off(panel, true); + esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY); + esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y); + + 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, + { + .text_font = &font_puhui_16_4, + .icon_font = &font_awesome_16_4, + .emoji_font = font_emoji_64_init(), + }); + } + + void InitializeButtons() { + boot_button_.OnClick([this]() { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) { + ResetWifiConfiguration(); + } + app.ToggleChatState(); + }); + } + + // 物联网初始化,添加对 AI 可见设备 + void InitializeIot() { + auto& thing_manager = iot::ThingManager::GetInstance(); + thing_manager.AddThing(iot::CreateThing("Speaker")); + thing_manager.AddThing(iot::CreateThing("Backlight")); + } + +public: + CustomBoard() : + boot_button_(BOOT_BUTTON_GPIO) { + InitializeI2c(); + i2c_dev_tca9554_init(); + InitializeSpi(); + Initializest77916Display(); + InitializeButtons(); + InitializeIot(); + } + + 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, I2S_STD_SLOT_LEFT, AUDIO_I2S_MIC_GPIO_SCK, AUDIO_I2S_MIC_GPIO_WS, AUDIO_I2S_MIC_GPIO_DIN, I2S_STD_SLOT_RIGHT); // I2S_STD_SLOT_LEFT / I2S_STD_SLOT_RIGHT / I2S_STD_SLOT_BOTH + + return &audio_codec; + } + + virtual Display* GetDisplay() override { + return display_; + } +}; + +DECLARE_BOARD(CustomBoard); diff --git a/main/idf_component.yml b/main/idf_component.yml index f083356d..a7c506a2 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -4,6 +4,8 @@ dependencies: espressif/esp_lcd_ili9341: "==1.2.0" espressif/esp_lcd_gc9a01: "^2.0.1" espressif/esp_lcd_st77916: "^1.0.1" + espressif/esp_lcd_spd2010: "==1.0.2" + espressif/esp_io_expander_tca9554: "==2.0.0" 78/esp_lcd_nv3023: "~1.0.0" 78/esp-wifi-connect: "==2.1.0" 78/esp-opus-encoder: "~2.1.0"