#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 #include #include #include #include #include #include #include #include #include "power_save_timer.h" #include "power_manager.h" #include #include #include "esp_io_expander_tca95xx_16bit.h" #include "assets/lang_config.h" #include #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(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(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(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(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(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(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(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;