diff --git a/main/boards/waveshare-c6-lcd-1.69/README.md b/main/boards/waveshare-c6-lcd-1.69/README.md index 167e0c71..5a44c06f 100644 --- a/main/boards/waveshare-c6-lcd-1.69/README.md +++ b/main/boards/waveshare-c6-lcd-1.69/README.md @@ -46,4 +46,11 @@ idf.py build ```bash idf.py build flash monitor ``` +# 按键操作 +## BOOT 按键 +**未连接服务器前单击: 进入配网模式** +**连接服务器后单击: 唤醒、打断** +## PWR 按键 +**双击:息屏、亮屏** +**长按:开关机** \ No newline at end of file diff --git a/main/boards/waveshare-c6-lcd-1.69/config.h b/main/boards/waveshare-c6-lcd-1.69/config.h index 2da0fae4..0904631e 100644 --- a/main/boards/waveshare-c6-lcd-1.69/config.h +++ b/main/boards/waveshare-c6-lcd-1.69/config.h @@ -20,6 +20,7 @@ #define BUILTIN_LED_GPIO GPIO_NUM_NC #define BOOT_BUTTON_GPIO GPIO_NUM_9 +#define PWR_BUTTON_GPIO GPIO_NUM_18 #define VOLUME_UP_BUTTON_GPIO GPIO_NUM_NC #define VOLUME_DOWN_BUTTON_GPIO GPIO_NUM_NC @@ -31,6 +32,9 @@ #define DISPLAY_DC_PIN GPIO_NUM_3 #define DISPLAY_RST_PIN GPIO_NUM_4 +#define BATTERY_EN_PIN GPIO_NUM_15 +#define BATTERY_ADC_PIN GPIO_NUM_0 +#define BATTERY_CHARGING_PIN GPIO_NUM_NC #define DISPLAY_WIDTH 240 diff --git a/main/boards/waveshare-c6-lcd-1.69/esp32-c6-lcd-1.69.cc b/main/boards/waveshare-c6-lcd-1.69/esp32-c6-lcd-1.69.cc index 66867f30..f5eea48f 100644 --- a/main/boards/waveshare-c6-lcd-1.69/esp32-c6-lcd-1.69.cc +++ b/main/boards/waveshare-c6-lcd-1.69/esp32-c6-lcd-1.69.cc @@ -5,7 +5,6 @@ #include "application.h" #include "button.h" #include "config.h" - #include #include "i2c_device.h" #include @@ -14,14 +13,13 @@ #include #include #include - #include - - +#include "iot_button.h" +#include "power_manager.h" +#include "power_save_timer.h" #define TAG "waveshare_lcd_1_69" - LV_FONT_DECLARE(font_puhui_20_4); LV_FONT_DECLARE(font_awesome_20_4); @@ -55,11 +53,52 @@ public: }; +class CustomButton: public Button { +public: + void OnPressDownDel(void) { + if (button_handle_ == nullptr) { + return; + } + on_press_down_ = NULL; + iot_button_unregister_cb(button_handle_, BUTTON_PRESS_DOWN, nullptr); + } + void OnPressUpDel(void) { + if (button_handle_ == nullptr) { + return; + } + on_press_up_ = NULL; + iot_button_unregister_cb(button_handle_, BUTTON_PRESS_UP, nullptr); + } +}; + + class CustomBoard : public WifiBoard { private: - Button boot_button_; + CustomButton boot_button_; + CustomButton pwr_button_; i2c_master_bus_handle_t i2c_bus_; LcdDisplay* display_; + PowerManager* power_manager_ = nullptr; + PowerSaveTimer* power_save_timer_ = nullptr; + + void InitializePowerManager() { + power_manager_ = new PowerManager(BATTERY_CHARGING_PIN, BATTERY_ADC_PIN, BATTERY_EN_PIN); + power_manager_->PowerON(); + } + + void InitializePowerSaveTimer() { + power_save_timer_ = new PowerSaveTimer(-1, 60, 300); + power_save_timer_->OnEnterSleepMode([this]() { + GetDisplay()->SetPowerSaveMode(true); + }); + power_save_timer_->OnExitSleepMode([this]() { + GetDisplay()->SetPowerSaveMode(false); + }); + power_save_timer_->OnShutdownRequest([this]() { + power_manager_->PowerOff(); + }); + power_save_timer_->SetEnabled(true); + } void InitializeI2c() { // Initialize I2C peripheral @@ -93,6 +132,7 @@ private: 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 = {}; @@ -105,26 +145,19 @@ private: io_config.lcd_param_bits = 8; ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi(SPI2_HOST, &io_config, &panel_io)); - // 初始化液晶屏驱动芯片 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_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 CustomLcdDisplay( panel_io, panel, @@ -146,11 +179,40 @@ private: } app.ToggleChatState(); }); + + pwr_button_.OnPressUp([this]() { + pwr_button_.OnDoubleClick([this]() { + static uint8_t brightness_last = 0; + auto backlight = Board::GetInstance().GetBacklight(); + if (backlight->brightness() == 0) { + brightness_last = 0; + if (brightness_last == 0) { + backlight->SetBrightness(50, true); + } else { + backlight->SetBrightness(brightness_last, true); + } + } else { + brightness_last = backlight->brightness(); + backlight->SetBrightness(0); + } + }); + + pwr_button_.OnLongPress([this]() { + // printf("Power button long press\n"); + if (power_manager_ != nullptr){ + power_manager_->PowerOff(); + } + }); + + pwr_button_.OnPressUpDel(); + }); } public: CustomBoard() : - boot_button_(BOOT_BUTTON_GPIO) { + boot_button_(BOOT_BUTTON_GPIO), pwr_button_(PWR_BUTTON_GPIO) { + InitializePowerManager(); + InitializePowerSaveTimer(); InitializeI2c(); InitializeSpi(); InitializeLcdDisplay(); @@ -159,9 +221,19 @@ public: } 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); + 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; } @@ -173,6 +245,25 @@ public: 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 = 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 SetPowerSaveMode(bool enabled) override { + if (!enabled) { + power_save_timer_->WakeUp(); + } + WifiBoard::SetPowerSaveMode(enabled); + } }; DECLARE_BOARD(CustomBoard); diff --git a/main/boards/waveshare-c6-lcd-1.69/power_manager.h b/main/boards/waveshare-c6-lcd-1.69/power_manager.h new file mode 100644 index 00000000..97d6546f --- /dev/null +++ b/main/boards/waveshare-c6-lcd-1.69/power_manager.h @@ -0,0 +1,176 @@ +#pragma once +#include +#include +#include +#include +#include +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" +#include + +#define TAG "power_manager" + + +class PowerManager { +private: + gpio_num_t charging_pin_ = GPIO_NUM_NC; + gpio_num_t bat_adc_pin_ = GPIO_NUM_NC; + gpio_num_t bat_power_pin_ = GPIO_NUM_NC; + adc_oneshot_unit_handle_t adc_handle_ = NULL; + adc_cali_handle_t adc_cali_handle_ = NULL; + adc_channel_t adc_channel_; + bool do_calibration = false; + + bool adc_calibration_init(adc_unit_t unit, adc_channel_t channel, adc_atten_t atten, adc_cali_handle_t *out_handle) { + adc_cali_handle_t handle = NULL; + esp_err_t ret = ESP_FAIL; + bool calibrated = false; + + if (!calibrated) { + ESP_LOGI(TAG, "calibration scheme version is %s", "Curve Fitting"); + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = unit, + .chan = channel, + .atten = atten, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + ret = adc_cali_create_scheme_curve_fitting(&cali_config, &handle); + if (ret == ESP_OK) { + calibrated = true; + } + } + + *out_handle = handle; + if (ret == ESP_OK) { + ESP_LOGI(TAG, "Calibration Success"); + } + else if (ret == ESP_ERR_NOT_SUPPORTED || !calibrated) { + ESP_LOGW(TAG, "eFuse not burnt, skip software calibration"); + } + else { + ESP_LOGE(TAG, "Invalid arg or no memory"); + } + return calibrated; + } + +public: + PowerManager(gpio_num_t charging_pin, gpio_num_t bat_adc_pin, gpio_num_t bat_power_pin) + : charging_pin_(charging_pin), bat_adc_pin_(bat_adc_pin), bat_power_pin_(bat_power_pin) { + // 初始化充电引脚 + if (charging_pin_ != GPIO_NUM_NC) { + gpio_config_t io_conf = {}; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = 1ULL << charging_pin_; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_ENABLE; + gpio_config(&io_conf); + } + + // 初始化电池使能引脚 + if (bat_power_pin_ != GPIO_NUM_NC) { + gpio_config_t io_conf = {}; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = 1ULL << bat_power_pin_; + io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; + io_conf.pull_up_en = GPIO_PULLUP_DISABLE; + gpio_config(&io_conf); + } + + // 初始化adc + if (bat_adc_pin_ != GPIO_NUM_NC) { + adc_oneshot_unit_init_cfg_t init_config = {}; + init_config.ulp_mode = ADC_ULP_MODE_DISABLE; + init_config.unit_id = ADC_UNIT_1; + + if (bat_adc_pin_ >= GPIO_NUM_0 && bat_adc_pin_ <= GPIO_NUM_6) + adc_channel_ = (adc_channel_t)((int)bat_adc_pin_); + else + return; + + ESP_ERROR_CHECK(adc_oneshot_new_unit(&init_config, &adc_handle_)); + adc_oneshot_chan_cfg_t config = {}; + config.bitwidth = ADC_BITWIDTH_DEFAULT; + config.atten = ADC_ATTEN_DB_12; + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle_, adc_channel_, &config)); + do_calibration = adc_calibration_init(init_config.unit_id, adc_channel_, config.atten, &adc_cali_handle_); + } + } + + ~PowerManager() { + if (adc_handle_) { + ESP_ERROR_CHECK(adc_oneshot_del_unit(adc_handle_)); + } + } + + int GetBatteryLevel(void) { + int adc_raw = 0; + int voltage_int = 0; + const float voltage_float_threshold = 0.1f; + float voltage_float = 0.0f; + static float last_voltage_float = 0.0f; + static int last_battery_level = 0; + + if (adc_handle_ != nullptr) { + ESP_ERROR_CHECK(adc_oneshot_read(adc_handle_, adc_channel_, &adc_raw)); + if (do_calibration) { + ESP_ERROR_CHECK(adc_cali_raw_to_voltage(adc_cali_handle_, adc_raw, &voltage_int)); + voltage_float = (voltage_int / 1000.0f) * 3.0; + + if (fabs(voltage_float - last_voltage_float) >= voltage_float_threshold) { + last_voltage_float = voltage_float; + if (voltage_float < 3.52) { + last_battery_level = 1; + } else if (voltage_float < 3.64) { + last_battery_level = 20; + } else if (voltage_float < 3.76) { + last_battery_level = 40; + } else if (voltage_float < 3.88) { + last_battery_level = 60; + } else if (voltage_float < 4.0) { + last_battery_level = 80; + } else { + last_battery_level = 100; + } + } + return last_battery_level; + } + } + return 100; + } + + bool IsCharging(void) { + if (charging_pin_ != GPIO_NUM_NC) { + return gpio_get_level(charging_pin_) == 0 ? true : false; + } + return false; + } + + bool IsDischarging(void) { + if (charging_pin_ != GPIO_NUM_NC) { + return gpio_get_level(charging_pin_) == 1; + } + return true; + } + + bool IsChargingDone(void) { + if (GetBatteryLevel() == 100) { + return true; + } + return false; + } + + void PowerOff(void) { + if (bat_power_pin_ != GPIO_NUM_NC) { + gpio_set_level(bat_power_pin_, 0); + } + } + + void PowerON(void) { + if (bat_power_pin_ != GPIO_NUM_NC) { + gpio_set_level(bat_power_pin_, 1); + } + } +};