diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt
index 2e93df7425..f8dd2db18d 100644
--- a/main/CMakeLists.txt
+++ b/main/CMakeLists.txt
@@ -111,6 +111,10 @@ elseif(CONFIG_BOARD_TYPE_LILYGO_T_CIRCLE_S3)
set(BOARD_TYPE "lilygo-t-circle-s3")
elseif(CONFIG_BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3)
set(BOARD_TYPE "lilygo-t-cameraplus-s3")
+elseif(CONFIG_BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA)
+ set(BOARD_TYPE "lilygo-t-display-s3-pro-mvsrlora")
+elseif(CONFIG_BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA_NO_BATTERY)
+ set(BOARD_TYPE "lilygo-t-display-s3-pro-mvsrlora")
elseif(CONFIG_BOARD_TYPE_MOVECALL_MOJI_ESP32S3)
set(BOARD_TYPE "movecall-moji-esp32s3")
elseif(CONFIG_BOARD_TYPE_MOVECALL_CUICAN_ESP32S3)
diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild
index 67d2a12df0..d27174da43 100644
--- a/main/Kconfig.projbuild
+++ b/main/Kconfig.projbuild
@@ -106,6 +106,10 @@ choice BOARD_TYPE
bool "LILYGO T-Circle-S3"
config BOARD_TYPE_LILYGO_T_CAMERAPLUS_S3
bool "LILYGO T-CameraPlus-S3"
+ config BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA
+ bool "LILYGO T-Display-S3-Pro-MVSRLora"
+ config BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA_NO_BATTERY
+ bool "LILYGO T-Display-S3-Pro-MVSRLora_No_Battery"
config BOARD_TYPE_MOVECALL_MOJI_ESP32S3
bool "Movecall Moji 小智AI衍生版"
config BOARD_TYPE_MOVECALL_CUICAN_ESP32S3
diff --git a/main/boards/lilygo-t-display-s3-pro-mvsrlora/README.md b/main/boards/lilygo-t-display-s3-pro-mvsrlora/README.md
new file mode 100644
index 0000000000..c3b8d0eb53
--- /dev/null
+++ b/main/boards/lilygo-t-display-s3-pro-mvsrlora/README.md
@@ -0,0 +1,32 @@
+# 编译配置命令
+
+**配置编译目标为 ESP32S3:**
+
+```bash
+idf.py set-target esp32s3
+```
+
+**打开 menuconfig:**
+
+```bash
+idf.py menuconfig
+```
+
+**选择板子:**
+
+```
+Xiaozhi Assistant -> Board Type -> LILYGO T-Display-S3-Pro-MVSRLora
+Or
+Xiaozhi Assistant -> Board Type -> LILYGO T-Display-S3-Pro-MVSRLora_NO_BATTERY
+```
+
+
+**编译:**
+
+```bash
+idf.py build
+```
+
+LILYGO T-Display-S3-Pro
+
+LILYGO T-Display-S3-Pro-MVSRLora
\ No newline at end of file
diff --git a/main/boards/lilygo-t-display-s3-pro-mvsrlora/config.h b/main/boards/lilygo-t-display-s3-pro-mvsrlora/config.h
new file mode 100644
index 0000000000..44c0a03f0a
--- /dev/null
+++ b/main/boards/lilygo-t-display-s3-pro-mvsrlora/config.h
@@ -0,0 +1,47 @@
+#ifndef _BOARD_CONFIG_H_
+#define _BOARD_CONFIG_H_
+
+#include
+#include "pin_config.h"
+
+#define AUDIO_INPUT_REFERENCE true
+#define AUDIO_INPUT_SAMPLE_RATE 24000
+#define AUDIO_OUTPUT_SAMPLE_RATE 24000
+
+#define AUDIO_MIC_I2S_GPIO_BCLK GPIO_NUM_NC
+#define AUDIO_MIC_I2S_GPIO_WS static_cast(MP34DT05TR_LRCLK)
+#define AUDIO_MIC_I2S_GPIO_DATA static_cast(MP34DT05TR_DATA)
+#define AUDIO_MIC_ENABLE static_cast(MP34DT05TR_EN)
+
+#define AUDIO_SPKR_I2S_GPIO_BCLK static_cast(MAX98357A_BCLK)
+#define AUDIO_SPKR_I2S_GPIO_LRCLK static_cast(MAX98357A_LRCLK)
+#define AUDIO_SPKR_I2S_GPIO_DATA static_cast(MAX98357A_DATA)
+#define AUDIO_SPKR_ENABLE static_cast(MAX98357A_EN)
+
+#define TOUCH_I2C_SDA_PIN static_cast(TOUCH_IIC_SDA)
+#define TOUCH_I2C_SCL_PIN static_cast(TOUCH_IIC_SCL)
+
+#define BUILTIN_LED_GPIO GPIO_NUM_NC
+#define BOOT_BUTTON_GPIO GPIO_NUM_0
+#define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC
+#define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC
+
+#define DISPLAY_WIDTH LCD_WIDTH
+#define DISPLAY_HEIGHT LCD_HEIGHT
+#define DISPLAY_MOSI LCD_MOSI
+#define DISPLAY_SCLK LCD_SCLK
+#define DISPLAY_DC LCD_DC
+#define DISPLAY_RST LCD_RST
+#define DISPLAY_CS LCD_CS
+#define DISPLAY_BL static_cast(LCD_BL)
+#define DISPLAY_MIRROR_X true
+#define DISPLAY_MIRROR_Y false
+#define DISPLAY_SWAP_XY false
+
+#define DISPLAY_OFFSET_X 0
+#define DISPLAY_OFFSET_Y 0
+
+#define DISPLAY_BACKLIGHT_PIN DISPLAY_BL
+#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false
+
+#endif // _BOARD_CONFIG_H_
diff --git a/main/boards/lilygo-t-display-s3-pro-mvsrlora/config.json b/main/boards/lilygo-t-display-s3-pro-mvsrlora/config.json
new file mode 100644
index 0000000000..f7e4505ef2
--- /dev/null
+++ b/main/boards/lilygo-t-display-s3-pro-mvsrlora/config.json
@@ -0,0 +1,9 @@
+{
+ "target": "esp32s3",
+ "builds": [
+ {
+ "name": "lilygo-t-display-s3-pro-mvsrlora",
+ "sdkconfig_append": []
+ }
+ ]
+}
\ No newline at end of file
diff --git a/main/boards/lilygo-t-display-s3-pro-mvsrlora/lilygo-t-display-s3-pro-mvsrlora.cc b/main/boards/lilygo-t-display-s3-pro-mvsrlora/lilygo-t-display-s3-pro-mvsrlora.cc
new file mode 100644
index 0000000000..b79c35c413
--- /dev/null
+++ b/main/boards/lilygo-t-display-s3-pro-mvsrlora/lilygo-t-display-s3-pro-mvsrlora.cc
@@ -0,0 +1,305 @@
+#include "wifi_board.h"
+#include "tdisplays3promvsrlora_audio_codec.h"
+#include "display/lcd_display.h"
+#include "application.h"
+#include "button.h"
+#include "config.h"
+#include "power_save_timer.h"
+#include "i2c_device.h"
+#include "iot/thing_manager.h"
+
+#include
+#include
+#include
+#include
+#include
+#include "esp_lcd_st7796.h"
+
+#define TAG "LilygoTDisplays3ProMVSRLoraBoard"
+
+LV_FONT_DECLARE(font_puhui_16_4);
+LV_FONT_DECLARE(font_awesome_16_4);
+
+class Cst2xxse : public I2cDevice {
+public:
+ struct TouchPoint_t {
+ int num = 0;
+ int x = -1;
+ int y = -1;
+ };
+
+ Cst2xxse(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
+ uint8_t chip_id = ReadReg(0x06);
+ ESP_LOGI(TAG, "Get cst2xxse chip ID: 0x%02X", chip_id);
+ read_buffer_ = new uint8_t[6];
+ }
+
+ ~Cst2xxse() {
+ delete[] read_buffer_;
+ }
+
+ void UpdateTouchPoint() {
+ ReadRegs(0x00, read_buffer_, 6);
+ tp_.num = read_buffer_[5] & 0x0F;
+ tp_.x = (static_cast(read_buffer_[1]) << 4) | (read_buffer_[3] & 0xF0);
+ tp_.y = (static_cast(read_buffer_[2]) << 4) | (read_buffer_[3] & 0x0F);
+ // ESP_LOGI(TAG, "Touch num: %d x: %d y: %d", tp_.num,tp_.x,tp_.y);
+ }
+
+ const TouchPoint_t &GetTouchPoint() {
+ return tp_;
+ }
+
+private:
+ uint8_t *read_buffer_ = nullptr;
+ TouchPoint_t tp_;
+};
+
+class Sy6970 : public I2cDevice {
+public:
+
+ Sy6970(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : I2cDevice(i2c_bus, addr) {
+ uint8_t chip_id = ReadReg(0x14);
+ ESP_LOGI(TAG, "Get sy6970 chip ID: 0x%02X", (chip_id & 0B00111000));
+
+ WriteReg(0x00,0B00001000); // Disable ILIM pin
+ WriteReg(0x02,0B11011101); // Enable ADC measurement function
+ WriteReg(0x07,0B10001101); // Disable watchdog timer feeding function
+
+ #ifdef CONFIG_BOARD_TYPE_LILYGO_T_DISPLAY_S3_PRO_MVSRLORA_NO_BATTERY
+ WriteReg(0x09,0B01100100); // Disable BATFET when battery is not needed
+ #endif
+ }
+};
+
+class LilygoTDisplays3ProMVSRLoraBoard : public WifiBoard {
+private:
+ i2c_master_bus_handle_t i2c_bus_;
+ Cst2xxse *cst226se_;
+ Sy6970 *sy6970_;
+ LcdDisplay *display_;
+ Button boot_button_;
+ PowerSaveTimer* power_save_timer_;
+
+ void InitializePowerSaveTimer() {
+ power_save_timer_ = new PowerSaveTimer(-1, 60, 300);
+ power_save_timer_->OnEnterSleepMode([this]() {
+ ESP_LOGI(TAG, "Enabling sleep mode");
+ auto display = GetDisplay();
+ display->SetChatMessage("system", "");
+ display->SetEmotion("sleepy");
+ GetBacklight()->SetBrightness(10);
+ });
+ power_save_timer_->OnExitSleepMode([this]() {
+ auto display = GetDisplay();
+ display->SetChatMessage("system", "");
+ display->SetEmotion("neutral");
+ GetBacklight()->RestoreBrightness();
+ });
+ power_save_timer_->SetEnabled(true);
+ }
+
+ void InitI2c(){
+ // Initialize I2C peripheral
+ i2c_master_bus_config_t i2c_bus_config = {
+ .i2c_port = I2C_NUM_0,
+ .sda_io_num = TOUCH_I2C_SDA_PIN,
+ .scl_io_num = TOUCH_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_config, &i2c_bus_));
+ }
+
+ void I2cDetect() {
+ uint8_t address;
+ printf(" 0 1 2 3 4 5 6 7 8 9 a b c d e f\r\n");
+ for (int i = 0; i < 128; i += 16) {
+ printf("%02x: ", i);
+ for (int j = 0; j < 16; j++) {
+ fflush(stdout);
+ address = i + j;
+ esp_err_t ret = i2c_master_probe(i2c_bus_, address, pdMS_TO_TICKS(200));
+ if (ret == ESP_OK) {
+ printf("%02x ", address);
+ } else if (ret == ESP_ERR_TIMEOUT) {
+ printf("UU ");
+ } else {
+ printf("-- ");
+ }
+ }
+ printf("\r\n");
+ }
+ }
+
+ static void touchpad_daemon(void *param) {
+ vTaskDelay(pdMS_TO_TICKS(2000));
+ auto &board = (LilygoTDisplays3ProMVSRLoraBoard&)Board::GetInstance();
+ auto touchpad = board.GetTouchpad();
+ bool was_touched = false;
+ while (1) {
+ touchpad->UpdateTouchPoint();
+ if (touchpad->GetTouchPoint().num > 0){
+ // On press
+ if (!was_touched) {
+ was_touched = true;
+ Application::GetInstance().ToggleChatState();
+ }
+ }
+ // On release
+ else if (was_touched) {
+ was_touched = false;
+ }
+ vTaskDelay(pdMS_TO_TICKS(50));
+ }
+ vTaskDelete(NULL);
+ }
+
+ void InitCst226se() {
+ ESP_LOGI(TAG, "Init Cst2xxse");
+ cst226se_ = new Cst2xxse(i2c_bus_, 0x5A);
+ xTaskCreate(touchpad_daemon, "tp", 4096, NULL, 5, NULL);
+ }
+
+ void InitSy6970() {
+ ESP_LOGI(TAG, "Init Sy6970");
+ sy6970_ = new Sy6970(i2c_bus_, 0x6A);
+ }
+
+ void InitSpi() {
+ spi_bus_config_t buscfg = {};
+ buscfg.mosi_io_num = DISPLAY_MOSI;
+ buscfg.miso_io_num = GPIO_NUM_NC;
+ buscfg.sclk_io_num = DISPLAY_SCLK;
+ 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 InitSt7796Display() {
+ ESP_LOGI(TAG, "Init St7796");
+
+ esp_lcd_panel_io_handle_t panel_io = nullptr;
+ esp_lcd_panel_handle_t panel = nullptr;
+
+ 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 = 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));
+
+ ESP_LOGD(TAG, "Install LCD driver");
+ esp_lcd_panel_dev_config_t panel_config = {};
+ panel_config.reset_gpio_num = DISPLAY_RST;
+ panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR;
+ panel_config.bits_per_pixel = 16;
+ ESP_ERROR_CHECK(esp_lcd_new_panel_st7796(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_invert_color(panel, true));
+ ESP_ERROR_CHECK(esp_lcd_panel_swap_xy(panel, false));
+ ESP_ERROR_CHECK(esp_lcd_panel_mirror(panel, true, false));
+ ESP_ERROR_CHECK(esp_lcd_panel_set_gap(panel, 49, 0));
+ ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(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_16_4,
+ .icon_font = &font_awesome_16_4,
+ .emoji_font = font_emoji_32_init(),
+ });
+
+ gpio_config_t config;
+ config.pin_bit_mask = BIT64(DISPLAY_BL);
+ config.mode = GPIO_MODE_OUTPUT;
+ config.pull_up_en = GPIO_PULLUP_DISABLE;
+ config.pull_down_en = GPIO_PULLDOWN_ENABLE;
+ config.intr_type = GPIO_INTR_DISABLE;
+#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
+ config.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE;
+#endif
+ gpio_config(&config);
+ gpio_set_level(DISPLAY_BL, 0);
+ }
+
+ void InitializeButtons() {
+ boot_button_.OnClick([this]() {
+ auto& app = Application::GetInstance();
+ if (app.GetDeviceState() == kDeviceStateStarting && !WifiStation::GetInstance().IsConnected()) {
+ ResetWifiConfiguration();
+ }
+ power_save_timer_->WakeUp();
+ app.ToggleChatState();
+ });
+ }
+
+ // 物联网初始化,添加对 AI 可见设备
+ void InitializeIot() {
+ auto &thing_manager = iot::ThingManager::GetInstance();
+ thing_manager.AddThing(iot::CreateThing("Speaker"));
+ thing_manager.AddThing(iot::CreateThing("Screen"));
+ }
+
+public:
+ LilygoTDisplays3ProMVSRLoraBoard() : boot_button_(BOOT_BUTTON_GPIO) {
+ InitializePowerSaveTimer();
+ InitI2c();
+ I2cDetect();
+ InitCst226se();
+ InitSy6970();
+ InitSpi();
+ InitSt7796Display();
+ InitializeButtons();
+ InitializeIot();
+ GetBacklight()->RestoreBrightness();
+ }
+
+ virtual AudioCodec *GetAudioCodec() override {
+ static Tdisplays3promvsrloraAudioCodec audio_codec(
+ AUDIO_INPUT_SAMPLE_RATE,
+ AUDIO_OUTPUT_SAMPLE_RATE,
+ AUDIO_MIC_I2S_GPIO_BCLK,
+ AUDIO_MIC_I2S_GPIO_WS,
+ AUDIO_MIC_I2S_GPIO_DATA,
+ AUDIO_SPKR_I2S_GPIO_BCLK,
+ AUDIO_SPKR_I2S_GPIO_LRCLK,
+ AUDIO_SPKR_I2S_GPIO_DATA,
+ AUDIO_INPUT_REFERENCE);
+ return &audio_codec;
+ }
+
+ virtual Display *GetDisplay() override{
+ return display_;
+ }
+
+ virtual void SetPowerSaveMode(bool enabled) override {
+ if (!enabled) {
+ power_save_timer_->WakeUp();
+ }
+ WifiBoard::SetPowerSaveMode(enabled);
+ }
+
+ virtual Backlight* GetBacklight() override {
+ static PwmBacklight backlight(DISPLAY_BACKLIGHT_PIN, DISPLAY_BACKLIGHT_OUTPUT_INVERT);
+ return &backlight;
+ }
+
+ Cst2xxse *GetTouchpad() {
+ return cst226se_;
+ }
+};
+
+DECLARE_BOARD(LilygoTDisplays3ProMVSRLoraBoard);
diff --git a/main/boards/lilygo-t-display-s3-pro-mvsrlora/pin_config.h b/main/boards/lilygo-t-display-s3-pro-mvsrlora/pin_config.h
new file mode 100644
index 0000000000..0b952d547a
--- /dev/null
+++ b/main/boards/lilygo-t-display-s3-pro-mvsrlora/pin_config.h
@@ -0,0 +1,80 @@
+/*
+ * @Description: None
+ * @Author: None
+ * @Date: 2023-08-16 14:24:03
+ * @LastEditTime: 2025-04-23 11:25:00
+ * @License: GPL 3.0
+ */
+#pragma once
+
+// ST7796
+#define LCD_WIDTH 222
+#define LCD_HEIGHT 480
+#define LCD_BL 48
+#define LCD_MOSI 17
+#define LCD_MISO 8
+#define LCD_SCLK 18
+#define LCD_CS 39
+#define LCD_DC 9
+#define LCD_RST 47
+
+// IIC
+#define IIC_SDA 5
+#define IIC_SCL 6
+
+// CST226SE
+#define CST226SE_IIC_ADDRESS 0x5A
+#define TOUCH_RST 13
+#define TOUCH_INT 21
+#define TOUCH_IIC_SDA IIC_SDA
+#define TOUCH_IIC_SCL IIC_SCL
+
+// SY6970
+#define SY6970_SDA 5
+#define SY6970_SCL 6
+#define SY6970_Address 0x6A
+#define SY6970_INT 21
+
+// SD
+#define SD_CS 14
+#define SD_MISO 8
+#define SD_MOSI 17
+#define SD_SCLK 18
+
+// RT9080
+#define RT9080_EN 42
+
+// MAX98357A
+#define MAX98357A_BCLK 4
+#define MAX98357A_LRCLK 15
+#define MAX98357A_DATA 11
+#define MAX98357A_EN 41
+
+// Vibration Motor
+#define VIBRATINO_MOTOR_PWM 45
+
+// PCF85063
+#define PCF85063_IIC_SDA 5
+#define PCF85063_IIC_SCL 6
+#define PCF85063_INT 21
+
+// LR1121
+#define LR1121_BUSY 46
+#define LR1121_INT 40
+#define LR1121_SCLK 18
+#define LR1121_MOSI 17
+#define LR1121_MISO 8
+#define LR1121_CS 7
+#define LR1121_RST 10
+
+// ICM20948
+#define ICM20948_ADDRESS 0x28
+#define ICM20948_SDA 5
+#define ICM20948_SCL 6
+#define ICM20948_INT 21
+
+// MP34DT05TRF
+#define MP34DT05TR_LRCLK 1
+#define MP34DT05TR_DATA 2
+#define MP34DT05TR_EN 3
+
diff --git a/main/boards/lilygo-t-display-s3-pro-mvsrlora/tdisplays3promvsrlora_audio_codec.cc b/main/boards/lilygo-t-display-s3-pro-mvsrlora/tdisplays3promvsrlora_audio_codec.cc
new file mode 100644
index 0000000000..0beec452dc
--- /dev/null
+++ b/main/boards/lilygo-t-display-s3-pro-mvsrlora/tdisplays3promvsrlora_audio_codec.cc
@@ -0,0 +1,150 @@
+#include "tdisplays3promvsrlora_audio_codec.h"
+
+#include
+#include
+#include
+#include
+
+#include "config.h"
+
+static const char TAG[] = "Tdisplays3promvsrloraAudioCodec";
+
+Tdisplays3promvsrloraAudioCodec::Tdisplays3promvsrloraAudioCodec(int input_sample_rate, int output_sample_rate,
+ gpio_num_t mic_bclk, gpio_num_t mic_ws, gpio_num_t mic_data,
+ gpio_num_t spkr_bclk, gpio_num_t spkr_lrclk, gpio_num_t spkr_data,
+ bool input_reference) {
+ duplex_ = true; // 是否双工
+ input_reference_ = input_reference; // 是否使用参考输入,实现回声消除
+ input_channels_ = input_reference_ ? 2 : 1; // 输入通道数
+ input_sample_rate_ = input_sample_rate;
+ output_sample_rate_ = output_sample_rate;
+
+ CreateVoiceHardware(mic_bclk, mic_ws, mic_data, spkr_bclk, spkr_lrclk, spkr_data);
+
+ gpio_config_t config_mic_en;
+ config_mic_en.pin_bit_mask = BIT64(AUDIO_MIC_ENABLE);
+ config_mic_en.mode = GPIO_MODE_OUTPUT;
+ config_mic_en.pull_up_en = GPIO_PULLUP_ENABLE;
+ config_mic_en.pull_down_en = GPIO_PULLDOWN_DISABLE;
+ config_mic_en.intr_type = GPIO_INTR_DISABLE;
+#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
+config_mic_en.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE;
+#endif
+ gpio_config(&config_mic_en);
+ gpio_set_level(AUDIO_MIC_ENABLE, 1);
+
+ gpio_config_t config_spkr_en;
+ config_spkr_en.pin_bit_mask = BIT64(AUDIO_SPKR_ENABLE);
+ config_spkr_en.mode = GPIO_MODE_OUTPUT;
+ config_spkr_en.pull_up_en = GPIO_PULLUP_DISABLE;
+ config_spkr_en.pull_down_en = GPIO_PULLDOWN_ENABLE;
+ config_spkr_en.intr_type = GPIO_INTR_DISABLE;
+#if SOC_GPIO_SUPPORT_PIN_HYS_FILTER
+config_spkr_en.hys_ctrl_mode = GPIO_HYS_SOFT_ENABLE;
+#endif
+ gpio_config(&config_spkr_en);
+ gpio_set_level(AUDIO_SPKR_ENABLE, 0);
+
+ ESP_LOGI(TAG, "Tdisplays3promvsrloraAudioCodec initialized");
+}
+
+Tdisplays3promvsrloraAudioCodec::~Tdisplays3promvsrloraAudioCodec() {
+ audio_codec_delete_codec_if(in_codec_if_);
+ audio_codec_delete_ctrl_if(in_ctrl_if_);
+ audio_codec_delete_codec_if(out_codec_if_);
+ audio_codec_delete_ctrl_if(out_ctrl_if_);
+ audio_codec_delete_gpio_if(gpio_if_);
+ audio_codec_delete_data_if(data_if_);
+}
+
+void Tdisplays3promvsrloraAudioCodec::CreateVoiceHardware(gpio_num_t mic_bclk, gpio_num_t mic_ws, gpio_num_t mic_data,
+ gpio_num_t spkr_bclk, gpio_num_t spkr_lrclk, gpio_num_t spkr_data) {
+
+ i2s_chan_config_t mic_chan_config = I2S_CHANNEL_DEFAULT_CONFIG(i2s_port_t(0), I2S_ROLE_MASTER);
+ mic_chan_config.auto_clear = true; // Auto clear the legacy data in the DMA buffer
+ i2s_chan_config_t spkr_chan_config = I2S_CHANNEL_DEFAULT_CONFIG(i2s_port_t(1), I2S_ROLE_MASTER);
+ spkr_chan_config.auto_clear = true; // Auto clear the legacy data in the DMA buffer
+
+ ESP_ERROR_CHECK(i2s_new_channel(&mic_chan_config, NULL, &rx_handle_));
+ ESP_ERROR_CHECK(i2s_new_channel(&spkr_chan_config, &tx_handle_, NULL));
+
+ i2s_pdm_rx_config_t mic_config = {
+ .clk_cfg = I2S_PDM_RX_CLK_DEFAULT_CONFIG(static_cast(input_sample_rate_)),
+ /* The data bit-width of PDM mode is fixed to 16 */
+ .slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
+ .gpio_cfg = {
+ .clk = mic_ws,
+ .din = mic_data,
+ .invert_flags = {
+ .clk_inv = false,
+ },
+ },
+ };
+
+ i2s_std_config_t spkr_config = {
+ .clk_cfg ={
+ .sample_rate_hz = static_cast(11025),
+ .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 = I2S_STD_PHILIPS_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
+ .gpio_cfg ={
+ .mclk = I2S_GPIO_UNUSED,
+ .bclk = spkr_bclk,
+ .ws = spkr_lrclk,
+ .dout = spkr_data,
+ .din = I2S_GPIO_UNUSED,
+ .invert_flags = {
+ .mclk_inv = false,
+ .bclk_inv = false,
+ .ws_inv = false
+ }
+ }
+ };
+
+ ESP_ERROR_CHECK(i2s_channel_init_pdm_rx_mode(rx_handle_, &mic_config));
+ ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &spkr_config));
+ ESP_LOGI(TAG, "Voice hardware created");
+}
+
+void Tdisplays3promvsrloraAudioCodec::SetOutputVolume(int volume) {
+ volume_ = volume;
+ AudioCodec::SetOutputVolume(volume);
+}
+
+void Tdisplays3promvsrloraAudioCodec::EnableInput(bool enable) {
+ gpio_set_level(AUDIO_MIC_ENABLE, !enable);
+ AudioCodec::EnableInput(enable);
+}
+
+void Tdisplays3promvsrloraAudioCodec::EnableOutput(bool enable) {
+ gpio_set_level(AUDIO_SPKR_ENABLE, enable);
+ AudioCodec::EnableOutput(enable);
+}
+
+int Tdisplays3promvsrloraAudioCodec::Read(int16_t *dest, int samples){
+ if (input_enabled_){
+ size_t bytes_read;
+ i2s_channel_read(rx_handle_, dest, samples * sizeof(int16_t), &bytes_read, portMAX_DELAY);
+
+ // ESP_LOGI(TAG, "Left: %d\n", dest[0]);
+ // ESP_LOGI(TAG,"Right: %d\n", dest[1]);
+ }
+ return samples;
+}
+
+int Tdisplays3promvsrloraAudioCodec::Write(const int16_t *data, int samples){
+ if (output_enabled_){
+ size_t bytes_read;
+ auto output_data = (int16_t *)malloc(samples * sizeof(int16_t));
+ for (size_t i = 0; i < samples; i++){
+ output_data[i] = (float)data[i] * (float)(volume_ / 100.0);
+ }
+ i2s_channel_write(tx_handle_, output_data, samples * sizeof(int16_t), &bytes_read, portMAX_DELAY);
+ free(output_data);
+ }
+ return samples;
+}
diff --git a/main/boards/lilygo-t-display-s3-pro-mvsrlora/tdisplays3promvsrlora_audio_codec.h b/main/boards/lilygo-t-display-s3-pro-mvsrlora/tdisplays3promvsrlora_audio_codec.h
new file mode 100644
index 0000000000..ac5b42ab87
--- /dev/null
+++ b/main/boards/lilygo-t-display-s3-pro-mvsrlora/tdisplays3promvsrlora_audio_codec.h
@@ -0,0 +1,37 @@
+#ifndef _TDISPLAYS3PROMVSRLORA_AUDIO_CODEC_H
+#define _TDISPLAYS3PROMVSRLORA_AUDIO_CODEC_H
+
+#include "audio_codecs/audio_codec.h"
+
+#include
+#include
+
+class Tdisplays3promvsrloraAudioCodec : public AudioCodec {
+private:
+ const audio_codec_data_if_t *data_if_ = nullptr;
+ const audio_codec_ctrl_if_t *out_ctrl_if_ = nullptr;
+ const audio_codec_if_t *out_codec_if_ = nullptr;
+ const audio_codec_ctrl_if_t *in_ctrl_if_ = nullptr;
+ const audio_codec_if_t *in_codec_if_ = nullptr;
+ const audio_codec_gpio_if_t *gpio_if_ = nullptr;
+
+ uint32_t volume_ = 70;
+
+ void CreateVoiceHardware(gpio_num_t mic_bclk, gpio_num_t mic_ws, gpio_num_t mic_data,gpio_num_t spkr_bclk, gpio_num_t spkr_lrclk, gpio_num_t spkr_data);
+
+ virtual int Read(int16_t *dest, int samples) override;
+ virtual int Write(const int16_t *data, int samples) override;
+
+public:
+Tdisplays3promvsrloraAudioCodec(int input_sample_rate, int output_sample_rate,
+ gpio_num_t mic_bclk, gpio_num_t mic_ws, gpio_num_t mic_data,
+ gpio_num_t spkr_bclk, gpio_num_t spkr_lrclk, gpio_num_t spkr_data,
+ bool input_reference);
+ virtual ~Tdisplays3promvsrloraAudioCodec();
+
+ virtual void SetOutputVolume(int volume) override;
+ virtual void EnableInput(bool enable) override;
+ virtual void EnableOutput(bool enable) override;
+};
+
+#endif // _BOX_AUDIO_CODEC_H
diff --git a/main/idf_component.yml b/main/idf_component.yml
index efac5b8e6c..ba5103beff 100644
--- a/main/idf_component.yml
+++ b/main/idf_component.yml
@@ -4,6 +4,7 @@ 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_st7796: "==1.3.2"
espressif/esp_lcd_spd2010: "==1.0.2"
espressif/esp_io_expander_tca9554: "==2.0.0"
espressif/esp_lcd_panel_io_additions: "^1.0.1"