xiaozhi-esp32/main/boards/esp32-s3-touch-lcd-3.5/esp32-s3-touch-lcd-3.5.cc
Xiaoxia b7db68457c
Some checks failed
Build Boards / Determine variants to build (push) Has been cancelled
Build Boards / Build ${{ matrix.name }} (push) Has been cancelled
v2.1.0: Upgrade esp-wifi-connect to 3.0; New device state machine (#1528)
* Upgrade component version

* update fonts component version

* Handle OTA error code

* Update project version to 2.1.0 and add device state machine implementation

- Upgrade  esp-wifi-connect to 3.0.0, allowing reconfiguring wifi without rebooting
- Introduce device state machine with state change notification in new files
- Remove obsolete device state event files
- Update application logic to utilize new state machine
- Minor adjustments in various board implementations for state handling

* fix compile errors

* Refactor power saving mode implementation to use PowerSaveLevel enumeration

- Updated Application class to replace SetPowerSaveMode with SetPowerSaveLevel, allowing for LOW_POWER and PERFORMANCE settings.
- Modified various board implementations to align with the new power save level structure.
- Ensured consistent handling of power save levels across different board files, enhancing code maintainability and clarity.

* Refactor power save level checks across multiple board implementations

- Updated the condition for power save level checks in various board files to ensure that the power save timer only wakes up when the level is not set to LOW_POWER.
- Improved consistency in handling power save levels, enhancing code clarity and maintainability.

* Refactor EnterWifiConfigMode calls in board implementations

- Updated calls to EnterWifiConfigMode to use the appropriate instance reference (self or board) across multiple board files.
- Improved code consistency and clarity in handling device state during WiFi configuration mode entry.

* Add cellular modem event handling and improve network status updates

- Introduced new network events for cellular modem operations, including detecting, registration errors, and timeouts.
- Enhanced the Application class to handle different network states and update the display status accordingly.
- Refactored Ml307Board to implement a callback mechanism for network events, improving modularity and responsiveness.
- Updated dual_network_board and board headers to support new network event callbacks, ensuring consistent handling across board implementations.

* update esp-wifi-connect version

* Update WiFi configuration tool messages across multiple board implementations to clarify user actions
2025-12-09 09:24:56 +08:00

374 lines
13 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "wifi_board.h"
#include "codecs/es8311_audio_codec.h"
#include "display/lcd_display.h"
#include "system_reset.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "mcp_server.h"
#include <esp_log.h>
#include "i2c_device.h"
#include <driver/i2c_master.h>
#include <driver/ledc.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h>
#include <esp_timer.h>
#include "esp_io_expander_tca9554.h"
#include "axp2101.h"
#include "power_save_timer.h"
#include <esp_lcd_touch_ft5x06.h>
#include <esp_lvgl_port.h>
#include "esp32_camera.h"
#define TAG "waveshare_lcd_3_5"
class Pmic : public Axp2101 {
public:
Pmic(i2c_master_bus_handle_t i2c_bus, uint8_t addr) : Axp2101(i2c_bus, addr) {
WriteReg(0x22, 0b110); // PWRON > OFFLEVEL as POWEROFF Source enable
WriteReg(0x27, 0x10); // hold 4s to power off
// Disable All DCs but DC1
WriteReg(0x80, 0x01);
// Disable All LDOs
WriteReg(0x90, 0x00);
WriteReg(0x91, 0x00);
// Set DC1 to 3.3V
WriteReg(0x82, (3300 - 1500) / 100);
// Set ALDO1 to 3.3V
WriteReg(0x92, (3300 - 500) / 100);
WriteReg(0x96, (1500 - 500) / 100);
WriteReg(0x97, (2800 - 500) / 100);
// Enable ALDO1 BLDO1 BLDO2
WriteReg(0x90, 0x31);
WriteReg(0x64, 0x02); // CV charger voltage setting to 4.1V
WriteReg(0x61, 0x02); // set Main battery precharge current to 50mA
WriteReg(0x62, 0x08); // set Main battery charger current to 400mA ( 0x08-200mA, 0x09-300mA, 0x0A-400mA )
WriteReg(0x63, 0x01); // set Main battery term charge current to 25mA
}
};
typedef struct {
int cmd; /*<! The specific LCD command */
const void *data; /*<! Buffer that holds the command specific data */
size_t data_bytes; /*<! Size of `data` in memory, in bytes */
unsigned int delay_ms; /*<! Delay in milliseconds after this command */
} st7796_lcd_init_cmd_t;
typedef struct {
const st7796_lcd_init_cmd_t *init_cmds; /*!< Pointer to initialization commands array. Set to NULL if using default commands.
* The array should be declared as `static const` and positioned outside the function.
* Please refer to `vendor_specific_init_default` in source file.
*/
uint16_t init_cmds_size; /*<! Number of commands in above array */
} st7796_vendor_config_t;
st7796_lcd_init_cmd_t st7796_lcd_init_cmds[] = {
{0x11, (uint8_t []){ 0x00 }, 0, 120},
// {0x36, (uint8_t []){ 0x08 }, 1, 0},
{0x3A, (uint8_t []){ 0x05 }, 1, 0},
{0xF0, (uint8_t []){ 0xC3 }, 1, 0},
{0xF0, (uint8_t []){ 0x96 }, 1, 0},
{0xB4, (uint8_t []){ 0x01 }, 1, 0},
{0xB7, (uint8_t []){ 0xC6 }, 1, 0},
{0xC0, (uint8_t []){ 0x80, 0x45 }, 2, 0},
{0xC1, (uint8_t []){ 0x13 }, 1, 0},
{0xC2, (uint8_t []){ 0xA7 }, 1, 0},
{0xC5, (uint8_t []){ 0x0A }, 1, 0},
{0xE8, (uint8_t []){ 0x40, 0x8A, 0x00, 0x00, 0x29, 0x19, 0xA5, 0x33}, 8, 0},
{0xE0, (uint8_t []){ 0xD0, 0x08, 0x0F, 0x06, 0x06, 0x33, 0x30, 0x33, 0x47, 0x17, 0x13, 0x13, 0x2B, 0x31}, 14, 0},
{0xE1, (uint8_t []){ 0xD0, 0x0A, 0x11, 0x0B, 0x09, 0x07, 0x2F, 0x33, 0x47, 0x38, 0x15, 0x16, 0x2C, 0x32},14, 0},
{0xF0, (uint8_t []){ 0x3C }, 1, 0},
{0xF0, (uint8_t []){ 0x69 }, 1, 120},
{0x21, (uint8_t []){ 0x00 }, 0, 0},
{0x29, (uint8_t []){ 0x00 }, 0, 0},
};
class CustomBoard : public WifiBoard {
private:
Button boot_button_;
Pmic* pmic_ = nullptr;
i2c_master_bus_handle_t i2c_bus_;
esp_io_expander_handle_t io_expander = NULL;
LcdDisplay* display_;
PowerSaveTimer* power_save_timer_;
Esp32Camera* camera_;
void InitializePowerSaveTimer() {
power_save_timer_ = new PowerSaveTimer(-1, 60, 300);
power_save_timer_->OnEnterSleepMode([this]() {
GetDisplay()->SetPowerSaveMode(true);
GetBacklight()->SetBrightness(20);
});
power_save_timer_->OnExitSleepMode([this]() {
GetDisplay()->SetPowerSaveMode(false);
GetBacklight()->RestoreBrightness();
});
power_save_timer_->OnShutdownRequest([this]() {
pmic_->PowerOff();
});
power_save_timer_->SetEnabled(true);
}
void InitializeI2c() {
// Initialize I2C peripheral
i2c_master_bus_config_t i2c_bus_cfg = {
.i2c_port = (i2c_port_t)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, &i2c_bus_));
}
void InitializeTca9554(void)
{
esp_err_t ret = esp_io_expander_new_i2c_tca9554(i2c_bus_, ESP_IO_EXPANDER_I2C_TCA9554_ADDRESS_000, &io_expander);
if(ret != ESP_OK)
ESP_LOGE(TAG, "TCA9554 create returned error");
ret = esp_io_expander_set_dir(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, IO_EXPANDER_OUTPUT);
ESP_ERROR_CHECK(ret);
vTaskDelay(pdMS_TO_TICKS(100));
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_0 | IO_EXPANDER_PIN_NUM_1, 0);
ESP_ERROR_CHECK(ret);
vTaskDelay(pdMS_TO_TICKS(100));
ret = esp_io_expander_set_level(io_expander, IO_EXPANDER_PIN_NUM_1, 1);
ESP_ERROR_CHECK(ret);
}
void InitializeAxp2101() {
ESP_LOGI(TAG, "Init AXP2101");
pmic_ = new Pmic(i2c_bus_, 0x34);
}
void InitializeSpi() {
ESP_LOGI(TAG, "Initialize QSPI bus");
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = DISPLAY_MOSI_PIN;
buscfg.miso_io_num = DISPLAY_MISO_PIN;
buscfg.sclk_io_num = DISPLAY_CLK_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 InitializeCamera() {
static esp_cam_ctlr_dvp_pin_config_t dvp_pin_config = {
.data_width = CAM_CTLR_DATA_WIDTH_8,
.data_io = {
[0] = CAM_PIN_D0,
[1] = CAM_PIN_D1,
[2] = CAM_PIN_D2,
[3] = CAM_PIN_D3,
[4] = CAM_PIN_D4,
[5] = CAM_PIN_D5,
[6] = CAM_PIN_D6,
[7] = CAM_PIN_D7,
},
.vsync_io = CAM_PIN_VSYNC,
.de_io = CAM_PIN_HREF,
.pclk_io = CAM_PIN_PCLK,
.xclk_io = CAM_PIN_XCLK,
};
esp_video_init_sccb_config_t sccb_config = {
.init_sccb = false, // 不初始化新的 SCCB使用现有的 I2C 总线
.i2c_handle = i2c_bus_, // 使用现有的 I2C 总线句柄
.freq = 100000, // 100kHz
};
esp_video_init_dvp_config_t dvp_config = {
.sccb_config = sccb_config,
.reset_pin = CAM_PIN_RESET,
.pwdn_pin = CAM_PIN_PWDN,
.dvp_pin = dvp_pin_config,
.xclk_freq = 12000000,
};
esp_video_init_config_t video_config = {
.dvp = &dvp_config,
};
camera_ = new Esp32Camera(video_config);
}
void InitializeTouch()
{
esp_lcd_touch_handle_t tp;
esp_lcd_touch_config_t tp_cfg = {
.x_max = DISPLAY_WIDTH,
.y_max = DISPLAY_HEIGHT,
.rst_gpio_num = GPIO_NUM_NC,
.int_gpio_num = GPIO_NUM_NC,
.levels = {
.reset = 0,
.interrupt = 0,
},
.flags = {
.swap_xy = 1,
.mirror_x = 1,
.mirror_y = 1,
},
};
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_FT5x06_CONFIG();
tp_io_config.scl_speed_hz = 400 * 1000;
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus_, &tp_io_config, &tp_io_handle));
ESP_LOGI(TAG, "Initialize touch controller");
ESP_ERROR_CHECK(esp_lcd_touch_new_i2c_ft5x06(tp_io_handle, &tp_cfg, &tp));
const lvgl_port_touch_cfg_t touch_cfg = {
.disp = lv_display_get_default(),
.handle = tp,
};
lvgl_port_add_touch(&touch_cfg);
ESP_LOGI(TAG, "Touch panel initialized successfully");
}
void InitializeLcdDisplay() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
// 液晶屏控制IO初始化
ESP_LOGI(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 = DISPLAY_SPI_MODE;
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));
st7796_vendor_config_t st7796_vendor_config = {
.init_cmds = st7796_lcd_init_cmds,
.init_cmds_size = sizeof(st7796_lcd_init_cmds) / sizeof(st7796_lcd_init_cmd_t),
};
// 初始化液晶屏驱动芯片
ESP_LOGI(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = DISPLAY_RST_PIN;
panel_config.rgb_ele_order = DISPLAY_RGB_ORDER;
panel_config.bits_per_pixel = 16;
panel_config.vendor_config = &st7796_vendor_config;
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(panel_io, &panel_config, &panel));
esp_lcd_panel_reset(panel);
esp_lcd_panel_init(panel);
esp_lcd_panel_invert_color(panel, DISPLAY_INVERT_COLOR);
esp_lcd_panel_swap_xy(panel, DISPLAY_SWAP_XY);
esp_lcd_panel_mirror(panel, DISPLAY_MIRROR_X, DISPLAY_MIRROR_Y);
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);
}
void InitializeButtons() {
boot_button_.OnClick([this]() {
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting) {
EnterWifiConfigMode();
return;
}
app.ToggleChatState();
});
}
// 初始化工具
void InitializeTools() {
auto &mcp_server = McpServer::GetInstance();
mcp_server.AddTool("self.system.reconfigure_wifi",
"End this conversation and enter WiFi configuration mode.\n"
"**CAUTION** You must ask the user to confirm this action.",
PropertyList(), [this](const PropertyList& properties) {
EnterWifiConfigMode();
return true;
});
}
public:
CustomBoard() :
boot_button_(BOOT_BUTTON_GPIO) {
InitializePowerSaveTimer();
InitializeI2c();
InitializeTca9554();
InitializeAxp2101();
InitializeSpi();
InitializeLcdDisplay();
// 解决部分开机黑屏的问题
if (esp_reset_reason() == ESP_RST_POWERON) {
fflush(stdout);
esp_restart();
}
InitializeTouch();
InitializeButtons();
InitializeCamera();
InitializeTools();
GetBacklight()->RestoreBrightness();
}
virtual AudioCodec* GetAudioCodec() override {
static Es8311AudioCodec audio_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_;
}
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 = pmic_->IsCharging();
discharging = pmic_->IsDischarging();
if (discharging != last_discharging) {
power_save_timer_->SetEnabled(discharging);
last_discharging = discharging;
}
level = pmic_->GetBatteryLevel();
return true;
}
virtual void SetPowerSaveLevel(PowerSaveLevel level) override {
if (level != PowerSaveLevel::LOW_POWER) {
power_save_timer_->WakeUp();
}
WifiBoard::SetPowerSaveLevel(level);
}
virtual Camera* GetCamera() override {
return camera_;
}
};
DECLARE_BOARD(CustomBoard);