Files
xiaozhi-esp32/main/boards/atk-dnesp32s3-box3/atk_dnesp32s3_box3.cc

472 lines
18 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 "audio_codec.h"
#include "codecs/es8311_audio_codec.h"
#include "codecs/box_audio_codec.h"
#include "codecs/no_audio_codec.h"
#include "display/lcd_display.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "led/single_led.h"
#include "i2c_device.h"
#include "esp_video.h"
#include <wifi_station.h>
#include <esp_log.h>
#include <driver/i2c_master.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/timers.h>
#include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_vendor.h>
#include <esp_lcd_panel_ops.h>
#include "power_save_timer.h"
#include "power_manager.h"
#include <driver/rtc_io.h>
#include <esp_sleep.h>
#include "esp_io_expander_tca95xx_16bit.h"
#include "assets/lang_config.h"
#include <driver/spi_common.h>
#define TAG "atk_dnesp32s3_box3"
LV_FONT_DECLARE(font_puhui_20_4);
LV_FONT_DECLARE(font_awesome_20_4);
class atk_dnesp32s3_box3 : public WifiBoard {
private:
i2c_master_bus_handle_t i2c_bus_;
LcdDisplay* display_;
EspVideo* camera_;
static atk_dnesp32s3_box3* instance_;
esp_io_expander_handle_t io_exp_handle;
button_handle_t btns;
button_driver_t* btn_driver_ = nullptr;
PowerSaveTimer* power_save_timer_;
PowerManager* power_manager_;
PowerSupply power_status_;
esp_timer_handle_t wake_timer_handle_;
int ticks_ = 0;
const int kChgCtrlInterval = 5;
void InitializeBoardPowerManager() {
instance_ = this;
if (IoExpanderGetLevel(XIO_BAT_CHRG) == 0) {
power_status_ = kDeviceTypecSupply;
} else {
power_status_ = kDeviceBatterySupply;
}
esp_timer_create_args_t wake_display_timer_args = {
.callback = [](void *arg) {
atk_dnesp32s3_box3* self = static_cast<atk_dnesp32s3_box3*>(arg);
self->ticks_ ++;
if (self->ticks_ % self->kChgCtrlInterval == 0) {
if (self->IoExpanderGetLevel(XIO_BAT_CHRG) == 0) {
self->power_status_ = kDeviceTypecSupply;
} else {
self->power_status_ = kDeviceBatterySupply;
}
/* 低于某个电量,会自动关机 */
if (self->power_manager_->low_voltage_ < 2630 && self->power_status_ == kDeviceBatterySupply) {
esp_timer_stop(self->power_manager_->timer_handle_);
esp_io_expander_set_dir(self->io_exp_handle, XIO_BAT_CHRG_EN, IO_EXPANDER_OUTPUT);
esp_io_expander_set_level(self->io_exp_handle, XIO_BAT_CHRG_EN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
esp_io_expander_set_dir(self->io_exp_handle, XIO_BAT_CHRG_EN, IO_EXPANDER_INPUT);
esp_io_expander_set_level(self->io_exp_handle, XIO_BAT_CHRG_EN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
}
}
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "wake_update_timer",
.skip_unhandled_events = true,
};
ESP_ERROR_CHECK(esp_timer_create(&wake_display_timer_args, &wake_timer_handle_));
ESP_ERROR_CHECK(esp_timer_start_periodic(wake_timer_handle_, 100000));
}
void InitializePowerManager() {
power_manager_ = new PowerManager(io_exp_handle);
power_manager_->OnChargingStatusChanged([this](bool is_charging) {
if (is_charging) {
power_save_timer_->SetEnabled(false);
} else {
power_save_timer_->SetEnabled(true);
}
});
}
void InitializePowerSaveTimer() {
power_save_timer_ = new PowerSaveTimer(-1, -1, -1);
power_save_timer_->OnEnterSleepMode([this]() {
GetDisplay()->SetPowerSaveMode(true);
GetBacklight()->SetBrightness(1);
});
power_save_timer_->OnExitSleepMode([this]() {
GetDisplay()->SetPowerSaveMode(false);
GetBacklight()->RestoreBrightness();
});
power_save_timer_->OnShutdownRequest([this]() {
if (power_status_ == kDeviceBatterySupply) {
GetBacklight()->SetBrightness(0);
esp_timer_stop(power_manager_ ->timer_handle_);
esp_io_expander_set_dir(io_exp_handle, XIO_BAT_CHRG_EN, IO_EXPANDER_OUTPUT);
esp_io_expander_set_level(io_exp_handle, XIO_BAT_CHRG_EN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
esp_io_expander_set_level(io_exp_handle, XIO_VDD_3V3_EN, 0);
}
});
power_save_timer_->SetEnabled(true);
}
void audio_volume_change(bool direction) {
auto codec = GetAudioCodec();
auto volume = codec->output_volume();
if (direction) {
volume += 10;
if (volume > 100) {
volume = 100;
}
codec->SetOutputVolume(volume);
} else {
volume -= 10;
if (volume < 0) {
volume = 0;
}
codec->SetOutputVolume(volume);
}
GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}
void audio_volume_minimum(){
GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}
void audio_volume_maxmum(){
GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}
esp_err_t IoExpanderSetLevel(uint16_t pin_mask, uint8_t level) {
return esp_io_expander_set_level(io_exp_handle, pin_mask, level);
}
uint8_t IoExpanderGetLevel(uint16_t pin_mask) {
uint32_t pin_val = 0;
esp_io_expander_get_level(io_exp_handle, DRV_IO_EXP_INPUT_MASK, &pin_val);
pin_mask &= DRV_IO_EXP_INPUT_MASK;
return (uint8_t)((pin_val & pin_mask) ? 1 : 0);
}
void InitializeIoExpander() {
esp_err_t ret = ESP_OK;
esp_io_expander_new_i2c_tca95xx_16bit(i2c_bus_, AW9523B_ADDR, &io_exp_handle);
// ret |= esp_io_expander_set_pullupdown(io_exp_handle, DRV_IO_EXP_INPUT_MASK, IO_EXPANDER_PULL_NONE);
ret |= esp_io_expander_set_dir(io_exp_handle, DRV_IO_EXP_OUTPUT_MASK, IO_EXPANDER_OUTPUT);
ret |= esp_io_expander_set_dir(io_exp_handle, DRV_IO_EXP_INPUT_MASK, IO_EXPANDER_INPUT);
ret |= esp_io_expander_set_level(io_exp_handle, XIO_VDD_2V8_EN, 1); /* 0308 */
ret |= esp_io_expander_set_level(io_exp_handle, XIO_VDD_3V3_EN, 1); /* 电源 */
ret |= esp_io_expander_set_level(io_exp_handle, XIO_ESP_ADC_SEL, 1); /* ADC */
ret |= esp_io_expander_set_level(io_exp_handle, XIO_VDDA_3V3_EN, 1);/* 音频电源 */
ret |= esp_io_expander_set_level(io_exp_handle, XIO_VBAT_EN, 1); /* 音频 */
ret |= esp_io_expander_set_level(io_exp_handle, XIO_PA_CTRL, 1); /* 音频功放 */
ret |= esp_io_expander_set_level(io_exp_handle, XIO_LCD_BL, 0); /* LCD背光 */
assert(ret == ESP_OK);
}
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_));
}
// Initialize spi peripheral
void InitializeSpi() {
spi_bus_config_t buscfg = {};
buscfg.mosi_io_num = LCD_MOSI_PIN;
buscfg.miso_io_num = LCD_MISO_PIN;
buscfg.sclk_io_num = LCD_SCLK_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 InitializeSt7789Display() {
esp_lcd_panel_io_handle_t panel_io = nullptr;
esp_lcd_panel_handle_t panel = nullptr;
ESP_LOGD(TAG, "Install panel IO");
// 液晶屏控制IO初始化
esp_lcd_panel_io_spi_config_t io_config = {};
io_config.cs_gpio_num = LCD_CS_PIN;
io_config.dc_gpio_num = LCD_DC_PIN;
io_config.spi_mode = 0;
io_config.pclk_hz = 60 * 1000 * 1000;
io_config.trans_queue_depth = 7;
io_config.lcd_cmd_bits = 8;
io_config.lcd_param_bits = 8;
esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io);
// 初始化液晶屏驱动芯片ST7789
ESP_LOGD(TAG, "Install LCD driver");
esp_lcd_panel_dev_config_t panel_config = {};
panel_config.reset_gpio_num = GPIO_NUM_NC;
panel_config.rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB;
panel_config.bits_per_pixel = 16;
panel_config.data_endian = LCD_RGB_DATA_ENDIAN_BIG,
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_BACKLIGHT_OUTPUT_INVERT);
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() {
instance_ = this;
button_config_t bo_btn_cfg = {
.long_press_time = 800,
.short_press_time = 500
};
button_config_t k1_btn_cfg = {
.long_press_time = 800,
.short_press_time = 500
};
button_config_t k2_btn_cfg = {
.long_press_time = 800,
.short_press_time = 500
};
button_driver_t* xio_k1_btn_driver_ = nullptr;
button_driver_t* xio_k2_btn_driver_ = nullptr;
button_handle_t bo_btn_handle = NULL;
button_handle_t k1_btn_handle = NULL;
button_handle_t k2_btn_handle = NULL;
xio_k1_btn_driver_ = (button_driver_t*)calloc(1, sizeof(button_driver_t));
xio_k1_btn_driver_->enable_power_save = false;
xio_k1_btn_driver_->get_key_level = [](button_driver_t *button_driver) -> uint8_t {
return !instance_->IoExpanderGetLevel(XIO_KEY_K1);
};
ESP_ERROR_CHECK(iot_button_create(&k1_btn_cfg, xio_k1_btn_driver_, &k1_btn_handle));
xio_k2_btn_driver_ = (button_driver_t*)calloc(1, sizeof(button_driver_t));
xio_k2_btn_driver_->enable_power_save = false;
xio_k2_btn_driver_->get_key_level = [](button_driver_t *button_driver) -> uint8_t {
return instance_->IoExpanderGetLevel(XIO_KEY_K2);
};
ESP_ERROR_CHECK(iot_button_create(&k2_btn_cfg, xio_k2_btn_driver_, &k2_btn_handle));
button_gpio_config_t bo_cfg = {
.gpio_num = BOOT_BUTTON_GPIO,
.active_level = BUTTON_INACTIVE,
.enable_power_save = false,
.disable_pull = false
};
ESP_ERROR_CHECK(iot_button_new_gpio_device(&bo_btn_cfg, &bo_cfg, &bo_btn_handle));
iot_button_register_cb(k1_btn_handle, BUTTON_PRESS_DOWN, nullptr, [](void* button_handle, void* usr_data) {
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
self->power_save_timer_->WakeUp();
self->audio_volume_change(false);
}, this);
iot_button_register_cb(k1_btn_handle, BUTTON_LONG_PRESS_START, nullptr, [](void* button_handle, void* usr_data) {
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
self->power_save_timer_->WakeUp();
self->audio_volume_minimum();
}, this);
iot_button_register_cb(k2_btn_handle, BUTTON_PRESS_DOWN, nullptr, [](void* button_handle, void* usr_data) {
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
self->power_save_timer_->WakeUp();
auto& app = Application::GetInstance();
app.ToggleChatState();
}, this);
iot_button_register_cb(k2_btn_handle, BUTTON_LONG_PRESS_START, nullptr, [](void* button_handle, void* usr_data) {
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
auto& app = Application::GetInstance();
if (app.GetDeviceState() == kDeviceStateStarting) {
self->EnterWifiConfigMode();
return;
}
if (self->power_status_ == kDeviceBatterySupply) {
auto backlight = Board::GetInstance().GetBacklight();
backlight->SetBrightness(0);
esp_timer_stop(self->power_manager_->timer_handle_);
esp_io_expander_set_dir(self->io_exp_handle, XIO_BAT_CHRG_EN, IO_EXPANDER_OUTPUT);
esp_io_expander_set_level(self->io_exp_handle, XIO_BAT_CHRG_EN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
esp_io_expander_set_level(self->io_exp_handle, XIO_VDD_3V3_EN, 0);
vTaskDelay(pdMS_TO_TICKS(100));
}
}, this);
iot_button_register_cb(bo_btn_handle, BUTTON_PRESS_DOWN, nullptr, [](void* button_handle, void* usr_data) {
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
self->power_save_timer_->WakeUp();
self->audio_volume_change(true);
}, this);
iot_button_register_cb(bo_btn_handle, BUTTON_LONG_PRESS_START, nullptr, [](void* button_handle, void* usr_data) {
auto self = static_cast<atk_dnesp32s3_box3*>(usr_data);
self->power_save_timer_->WakeUp();
self->audio_volume_maxmum();
}, this);
}
/* 初始化摄像头GC0308 */
/* 根据正点原子官方示例参数 */
void InitializeCamera() {
esp_io_expander_set_level(io_exp_handle, XIO_TP_CAM_RESET, 0); /* 确保复位 */
vTaskDelay(pdMS_TO_TICKS(50)); /* 延长复位保持时间 */
esp_io_expander_set_level(io_exp_handle, XIO_TP_CAM_RESET, 1); /* 释放复位 */
vTaskDelay(pdMS_TO_TICKS(50)); /* 延长 50ms */
/* DVP pin configuration */
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_LREF,
.pclk_io = CAM_PIN_PCLK,
.xclk_io = CAM_PIN_XCLK,
};
/* 复用 I2C 总线 */
esp_video_init_sccb_config_t sccb_config = {
.init_sccb = false, /* 不初始化新的 SCCB使用现有的 I2C 总线 */
.i2c_handle = i2c_bus_, /* 使用现有的 I2C 总线句柄 */
.freq = 100000, /* SCCB 通信频率,通常为 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 = 24000000,
};
esp_video_init_config_t video_config = {
.dvp = &dvp_config,
};
camera_ = new EspVideo(video_config);
}
public:
atk_dnesp32s3_box3(){
InitializeI2c();
InitializeSpi();
InitializeIoExpander();
InitializePowerSaveTimer();
//InitializePowerManager();
InitializeSt7789Display();
InitializeButtons();
//GetBacklight()->RestoreBrightness();
//InitializeBoardPowerManager();
InitializeCamera();
}
virtual AudioCodec* GetAudioCodec() override {
static BoxAudioCodec audio_codec(
i2c_bus_,
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,
AUDIO_CODEC_ES7210_ADDR,
AUDIO_INPUT_REFERENCE);
return &audio_codec;
}
virtual Display* GetDisplay() override {
return display_;
}
// virtual Backlight* GetBacklight() override {
// static PwmBacklight backlight(GPIO_NUM_0, 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 SetPowerSaveLevel(PowerSaveLevel level) override {
if (level != PowerSaveLevel::LOW_POWER) {
power_save_timer_->WakeUp();
}
WifiBoard::SetPowerSaveLevel(level);
}
virtual Camera* GetCamera() override {
return camera_;
}
};
DECLARE_BOARD(atk_dnesp32s3_box3);
// 定义静态成员变量
atk_dnesp32s3_box3* atk_dnesp32s3_box3::instance_ = nullptr;