From 8d171a749c273ffe95a6733077c47d46d6141e9d Mon Sep 17 00:00:00 2001 From: Y1hsiaochunnn Date: Tue, 30 Jun 2026 15:30:22 +0800 Subject: [PATCH 1/2] Add Waveshare ESP32-P4-WIFI6-Touch-LCD-5 board --- main/CMakeLists.txt | 6 +++ main/Kconfig.projbuild | 5 ++- .../esp32-p4-wifi6-touch-lcd/README.md | 1 + .../esp32-p4-wifi6-touch-lcd/config.h | 7 ++++ .../esp32-p4-wifi6-touch-lcd/config.json | 30 ++++++++++++++ .../esp32-p4-wifi6-touch-lcd.cc | 40 +++++++++++++++++++ main/idf_component.yml | 4 ++ 7 files changed, 92 insertions(+), 1 deletion(-) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 170cb18f..80d98fe8 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -531,6 +531,12 @@ elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3) set(BUILTIN_TEXT_FONT font_puhui_basic_30_4) set(BUILTIN_ICON_FONT font_awesome_30_4) set(DEFAULT_EMOJI_COLLECTION twemoji_64) +elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5) + set(MANUFACTURER "waveshare") + set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd") + set(BUILTIN_TEXT_FONT font_puhui_basic_30_4) + set(BUILTIN_ICON_FONT font_awesome_30_4) + set(DEFAULT_EMOJI_COLLECTION twemoji_64) elseif(CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B) set(MANUFACTURER "waveshare") set(BOARD_TYPE "esp32-p4-wifi6-touch-lcd") diff --git a/main/Kconfig.projbuild b/main/Kconfig.projbuild index 030b7141..53d67a7e 100644 --- a/main/Kconfig.projbuild +++ b/main/Kconfig.projbuild @@ -426,6 +426,9 @@ choice BOARD_TYPE config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3 bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3" depends on IDF_TARGET_ESP32P4 + config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5 + bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-5" + depends on IDF_TARGET_ESP32P4 config BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B bool "Waveshare ESP32-P4-WIFI6-Touch-LCD-7B" depends on IDF_TARGET_ESP32P4 @@ -920,7 +923,7 @@ config USE_DEVICE_AEC default n depends on USE_AUDIO_PROCESSOR && (BOARD_TYPE_ESP_BOX_3 || BOARD_TYPE_ESP_BOX || BOARD_TYPE_ESP_BOX_LITE \ || BOARD_TYPE_LICHUANG_DEV_S3 || BOARD_TYPE_RYMCU_BIGSMART || BOARD_TYPE_ESP_KORVO2_V3 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75|| BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_1_75C || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_1_83\ - || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3 \ + || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_AMOLED_2_06 || BOARD_TYPE_WAVESHARE_ESP32_S3_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4B || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5 \ || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B \ || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C || BOARD_TYPE_ESP_S3_LCD_EV_Board_2 || BOARD_TYPE_YUNLIAO_S3 \ || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_8 || BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_10_1 \ diff --git a/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/README.md b/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/README.md index 3f5f1d53..fa1690c5 100644 --- a/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/README.md +++ b/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/README.md @@ -10,6 +10,7 @@ Configuration in `menuconfig`. Selection Board Type `Xiaozhi Assistant --> Board Type` - Waveshare ESP32-P4-WIFI6-Touch-LCD-4B - Waveshare ESP32-P4-WIFI6-Touch-LCD-4.3 +- Waveshare ESP32-P4-WIFI6-Touch-LCD-5 - Waveshare ESP32-P4-WIFI6-Touch-LCD-7B - Waveshare ESP32-P4-WIFI6-Touch-LCD-3.4C - Waveshare ESP32-P4-WIFI6-Touch-LCD-4C diff --git a/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/config.h b/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/config.h index 057f3d50..9b4c30dd 100644 --- a/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/config.h +++ b/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/config.h @@ -36,6 +36,13 @@ #define DISPLAY_BACKLIGHT_PIN GPIO_NUM_26 #define DISPLAY_BACKLIGHT_OUTPUT_INVERT true #define LCD_MIPI_DSI_LANE_BITRATE_MBPS (500) +#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5 +#define DISPLAY_WIDTH (720) +#define DISPLAY_HEIGHT (1280) +#define PIN_NUM_LCD_RST GPIO_NUM_27 +#define DISPLAY_BACKLIGHT_PIN GPIO_NUM_26 +#define DISPLAY_BACKLIGHT_OUTPUT_INVERT false +#define LCD_MIPI_DSI_LANE_BITRATE_MBPS (700) #elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B #define DISPLAY_WIDTH (1024) #define DISPLAY_HEIGHT (600) diff --git a/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/config.json b/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/config.json index a434efe6..cc8c21dc 100644 --- a/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/config.json +++ b/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/config.json @@ -62,6 +62,36 @@ "CONFIG_XIAOZHI_ENABLE_CAMERA_ENDIANNESS_SWAP=y" ] }, + { + "name": "esp32-p4-wifi6-touch-lcd-5", + "sdkconfig_append": [ + "CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0", + "CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH=y", + "CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5=y", + "CONFIG_USE_WECHAT_MESSAGE_STYLE=n", + "CONFIG_USE_DEVICE_AEC=y", + "CONFIG_CAMERA_OV5647=y", + "CONFIG_CAMERA_OV5647_AUTO_DETECT_MIPI_INTERFACE_SENSOR=y", + "CONFIG_CAMERA_OV5647_MIPI_RAW8_800X800_50FPS=y", + "CONFIG_XIAOZHI_ENABLE_CAMERA_ENDIANNESS_SWAP=y", + "CONFIG_ESP32P4_SELECTS_REV_LESS_V3=y", + "CONFIG_ESP32P4_REV_MIN_100=y" + ] + }, + { + "name": "esp32-p4-wifi6-touch-lcd-5-p4x", + "sdkconfig_append": [ + "CONFIG_SPIRAM_MALLOC_ALWAYSINTERNAL=0", + "CONFIG_BOOTLOADER_CACHE_32BIT_ADDR_QUAD_FLASH=y", + "CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5=y", + "CONFIG_USE_WECHAT_MESSAGE_STYLE=n", + "CONFIG_USE_DEVICE_AEC=y", + "CONFIG_CAMERA_OV5647=y", + "CONFIG_CAMERA_OV5647_AUTO_DETECT_MIPI_INTERFACE_SENSOR=y", + "CONFIG_CAMERA_OV5647_MIPI_RAW8_800X800_50FPS=y", + "CONFIG_XIAOZHI_ENABLE_CAMERA_ENDIANNESS_SWAP=y" + ] + }, { "name": "esp32-p4-wifi6-touch-lcd-7b", "sdkconfig_append": [ diff --git a/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/esp32-p4-wifi6-touch-lcd.cc b/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/esp32-p4-wifi6-touch-lcd.cc index b493aac1..5a7f76ea 100644 --- a/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/esp32-p4-wifi6-touch-lcd.cc +++ b/main/boards/waveshare/esp32-p4-wifi6-touch-lcd/esp32-p4-wifi6-touch-lcd.cc @@ -17,6 +17,8 @@ #include "esp_lcd_st7703.h" #elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4_3 #include "esp_lcd_st7701.h" +#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5 +#include "esp_lcd_hx8394.h" #elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B #include "esp_lcd_ek79007.h" #elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_3_4C || CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_4C \ @@ -178,6 +180,44 @@ private: .vendor_config = &vendor_config, }; esp_lcd_new_panel_st7701(io, &lcd_dev_config, &disp_panel); +#elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_5 + esp_lcd_dpi_panel_config_t dpi_config = { + .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, + .dpi_clock_freq_mhz = 58, + .pixel_format = LCD_COLOR_PIXEL_FORMAT_RGB565, + .num_fbs = 1, + .video_timing = { + .h_size = DISPLAY_WIDTH, + .v_size = DISPLAY_HEIGHT, + .hsync_pulse_width = 20, + .hsync_back_porch = 20, + .hsync_front_porch = 40, + .vsync_pulse_width = 4, + .vsync_back_porch = 10, + .vsync_front_porch = 24, + }, + .flags = { + .use_dma2d = true, + }, + }; + hx8394_vendor_config_t vendor_config = { + .mipi_config = { + .dsi_bus = mipi_dsi_bus, + .dpi_config = &dpi_config, + .lane_num = 2, + }, + }; + + const esp_lcd_panel_dev_config_t lcd_dev_config = { + .reset_gpio_num = PIN_NUM_LCD_RST, + .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB, + .bits_per_pixel = 16, + .flags = { + .reset_active_high = true, + }, + .vendor_config = &vendor_config, + }; + esp_lcd_new_panel_hx8394(io, &lcd_dev_config, &disp_panel); #elif CONFIG_BOARD_TYPE_WAVESHARE_ESP32_P4_WIFI6_TOUCH_LCD_7B esp_lcd_dpi_panel_config_t dpi_config = { .dpi_clk_src = MIPI_DSI_DPI_CLK_SRC_DEFAULT, diff --git a/main/idf_component.yml b/main/idf_component.yml index acf2de73..f87e882f 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -80,6 +80,10 @@ dependencies: version: '*' rules: - if: target in [esp32p4] + waveshare/esp_lcd_hx8394: + version: '*' + rules: + - if: target in [esp32p4] waveshare/esp_lcd_st7703: version: '*' rules: From 71051062a3e4fd9426f3bbc19382ea2af7d5432a Mon Sep 17 00:00:00 2001 From: almirus Date: Wed, 1 Jul 2026 02:29:52 +0300 Subject: [PATCH 2/2] Add ESP-VoCat battery emotes (#2090) --- main/CMakeLists.txt | 37 +++- .../esp-vocat/assets/360_360/emote.json | 24 +++ .../esp-vocat/emoji/battery_connected.eaf | Bin 0 -> 98955 bytes main/boards/esp-vocat/emoji/low_battery.eaf | Bin 0 -> 129314 bytes main/boards/esp-vocat/esp_vocat.cc | 172 ++++++++++++++++-- 5 files changed, 213 insertions(+), 20 deletions(-) create mode 100644 main/boards/esp-vocat/assets/360_360/emote.json create mode 100644 main/boards/esp-vocat/emoji/battery_connected.eaf create mode 100644 main/boards/esp-vocat/emoji/low_battery.eaf diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 80d98fe8..4492a358 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -291,7 +291,8 @@ elseif(CONFIG_BOARD_TYPE_ESP_VOCAT) set(BUILTIN_ICON_FONT font_awesome_20_4) set(DEFAULT_EMOJI_COLLECTION twemoji_64) set(EMOTE_RESOLUTION "360_360") - # set(EMOTE_EXTERNAL_PATH "${CMAKE_CURRENT_SOURCE_DIR}/boards/esp-vocat/assets") + set(ESP_VOCAT_ASSETS_STAGING "${CMAKE_BINARY_DIR}/esp-vocat-assets") + set(EMOTE_EXTERNAL_PATH "${ESP_VOCAT_ASSETS_STAGING}") elseif(CONFIG_BOARD_TYPE_ESP_SENSAIRSHUTTLE) set(BOARD_TYPE "esp-sensairshuttle") set(BUILTIN_TEXT_FONT font_puhui_basic_16_4) @@ -1102,6 +1103,34 @@ if(XIAOZHI_FONTS_COMPONENT_PATH) set(XIAOZHI_FONTS_PATH "${XIAOZHI_FONTS_COMPONENT_PATH}") endif() +if(CONFIG_BOARD_TYPE_ESP_VOCAT) + find_component_by_pattern("esp_emote_assets" EMOTE_ASSETS_COMPONENT EMOTE_ASSETS_PATH) + if(EMOTE_ASSETS_PATH) + file(REMOVE_RECURSE "${ESP_VOCAT_ASSETS_STAGING}") + file(REMOVE + "${CMAKE_BINARY_DIR}/expression_assets.bin" + "${CMAKE_BINARY_DIR}/expression_assets.bin.validated" + ) + file(MAKE_DIRECTORY "${ESP_VOCAT_ASSETS_STAGING}/360_360") + file(MAKE_DIRECTORY "${ESP_VOCAT_ASSETS_STAGING}/emoji_large") + file(COPY "${EMOTE_ASSETS_PATH}/emoji_large/" DESTINATION "${ESP_VOCAT_ASSETS_STAGING}/emoji_large") + file(COPY "${EMOTE_ASSETS_PATH}/360_360/layout.json" DESTINATION "${ESP_VOCAT_ASSETS_STAGING}/360_360") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/boards/esp-vocat/assets/360_360/emote.json" + DESTINATION "${ESP_VOCAT_ASSETS_STAGING}/360_360") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/boards/esp-vocat/emoji/low_battery.eaf" + DESTINATION "${ESP_VOCAT_ASSETS_STAGING}/emoji_large") + file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/boards/esp-vocat/emoji/battery_connected.eaf" + DESTINATION "${ESP_VOCAT_ASSETS_STAGING}/emoji_large") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/boards/esp-vocat/assets/360_360/emote.json" + "${CMAKE_CURRENT_SOURCE_DIR}/boards/esp-vocat/emoji/low_battery.eaf" + "${CMAKE_CURRENT_SOURCE_DIR}/boards/esp-vocat/emoji/battery_connected.eaf" + ) + else() + message(WARNING "esp_emote_assets component not found; ESP-VoCat custom emotes will be skipped") + endif() +endif() + if(CONFIG_BOARD_TYPE_ESP_HI) set(URL "https://github.com/espressif2022/image_player/raw/main/test_apps/test_8bit") set(EMOJI_DIR "${CMAKE_BINARY_DIR}/emoji") @@ -1267,7 +1296,11 @@ if ("${size}" AND "${offset}") set(ASSETS_PARTITION "assets") set(ASSETS_FILE "${CMAKE_BINARY_DIR}/${ASSETS_NAME}.bin") - build_speaker_assets_bin("${ASSETS_PARTITION}" ${EMOTE_RESOLUTION} ${ASSETS_FILE} ${CONFIG_MMAP_FILE_NAME_LENGTH}) + if(EMOTE_EXTERNAL_PATH) + build_speaker_assets_bin("${ASSETS_PARTITION}" ${EMOTE_RESOLUTION} ${ASSETS_FILE} ${CONFIG_MMAP_FILE_NAME_LENGTH} "${EMOTE_EXTERNAL_PATH}") + else() + build_speaker_assets_bin("${ASSETS_PARTITION}" ${EMOTE_RESOLUTION} ${ASSETS_FILE} ${CONFIG_MMAP_FILE_NAME_LENGTH}) + endif() message(STATUS "Generated emote assets: ${ASSETS_FILE} -> ${ASSETS_PARTITION} partition") esptool_py_flash_to_partition(flash "${ASSETS_PARTITION}" "${ASSETS_FILE}") elseif(CONFIG_FLASH_NONE_ASSETS) diff --git a/main/boards/esp-vocat/assets/360_360/emote.json b/main/boards/esp-vocat/assets/360_360/emote.json new file mode 100644 index 00000000..8310ad10 --- /dev/null +++ b/main/boards/esp-vocat/assets/360_360/emote.json @@ -0,0 +1,24 @@ +[ + {"emote": "happy", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "laughing", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "funny", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "loving", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "embarrassed", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "confident", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "delicious", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "sad", "src": "Sad.eaf", "loop": true, "fps": 20}, + {"emote": "crying", "src": "cry.eaf", "loop": true, "fps": 20}, + {"emote": "sleepy", "src": "sleep.eaf", "loop": true, "fps": 20}, + {"emote": "silly", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "angry", "src": "angry.eaf", "loop": true, "fps": 20}, + {"emote": "surprised", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "shocked", "src": "shocked.eaf", "loop": true, "fps": 20}, + {"emote": "thinking", "src": "confused.eaf", "loop": true, "fps": 20}, + {"emote": "winking", "src": "neutral.eaf", "loop": true, "fps": 20}, + {"emote": "relaxed", "src": "Happy.eaf", "loop": true, "fps": 20}, + {"emote": "confused", "src": "confused.eaf", "loop": true, "fps": 20}, + {"emote": "neutral", "src": "winking.eaf", "loop": false, "fps": 20}, + {"emote": "idle", "src": "neutral.eaf", "loop": false, "fps": 20}, + {"emote": "low_battery", "src": "low_battery.eaf", "loop": false, "fps": 20}, + {"emote": "battery_connected", "src": "battery_connected.eaf", "loop": false, "fps": 20} +] diff --git a/main/boards/esp-vocat/emoji/battery_connected.eaf b/main/boards/esp-vocat/emoji/battery_connected.eaf new file mode 100644 index 0000000000000000000000000000000000000000..d75df3f5223446111e220808cc9abcb96f6b5494 GIT binary patch literal 98955 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 O#R1^CU0yfv{(k^myCnqx literal 0 HcmV?d00001 diff --git a/main/boards/esp-vocat/emoji/low_battery.eaf b/main/boards/esp-vocat/emoji/low_battery.eaf new file mode 100644 index 0000000000000000000000000000000000000000..ed188a9a96bac9726acac7e0aaae8946b07385ab GIT binary patch literal 129314 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[1] << 8 | read_buffer_[0]); - int16_t current = static_cast(read_buffer_[3] << 8 | read_buffer_[2]); - - // Use the variables to avoid warnings (can be removed if actual implementation uses them) + int16_t ReadWord(uint8_t reg) + { + uint8_t data[2] = {0}; + ReadRegs(reg, data, 2); + return static_cast(static_cast(data[0]) | (static_cast(data[1]) << 8)); + } + + int GetBatteryLevel() + { + int level = ReadWord(kRegStateOfCharge); + if (level < 0) { + return 0; + } + if (level > 100) { + return 100; + } + return level; + } + + bool IsCharging() + { + const uint16_t status = static_cast(ReadWord(kRegBatteryStatus)); + if ((status & kBatteryStatusDsg) == 0) { + return true; + } + + const int16_t avg_current_ma = ReadWord(kRegAverageCurrent); + const int16_t current_ma = ReadWord(kRegCurrent); + // Current is a fallback only: AverageCurrent can lag charger insertion noticeably. + return avg_current_ma > kChargingCurrentMa || current_ma > kChargingCurrentMa; + } + + bool IsDischarging() + { + return (static_cast(ReadWord(kRegBatteryStatus)) & kBatteryStatusDsg) != 0; + } + + void Update() + { + const int16_t voltage = ReadWord(kRegVoltage); + const int16_t current = ReadWord(kRegCurrent); + ESP_ERROR_CHECK(temperature_sensor_get_celsius(temp_sensor, &tsens_value)); (void)voltage; (void)current; } - static void TaskFunction(void *pvParameters) - { - Charge* charge = static_cast(pvParameters); - while (true) { - charge->Printcharge(); - vTaskDelay(pdMS_TO_TICKS(300)); - } - } private: uint8_t* read_buffer_ = nullptr; @@ -448,6 +483,10 @@ private: TaskHandle_t touch_slider_task_handle_ = nullptr; esp_timer_handle_t emotion_reset_timer_ = nullptr; bool bmi270_ready_ = false; + bool was_charging_ = false; + uint8_t low_battery_alert_mask_ = 0; + int low_battery_plays_left_ = 0; + int64_t next_low_battery_play_ms_ = 0; touch_slider_handle_t touch_slider_handle_ = nullptr; touch_button_handle_t touch_button_handle_ = nullptr; @@ -483,6 +522,92 @@ private: ShowTemporaryEmotion("happy", 2000); } + void PlayBatteryEmotion(const char* emotion, uint32_t duration_ms) + { +#if CONFIG_USE_EMOTE_MESSAGE_STYLE + if (display_ == nullptr || emotion == nullptr) { + return; + } + auto* emote_display = dynamic_cast(display_); + if (emote_display != nullptr) { + emote_display->InsertAnimDialog(emotion, duration_ms); + return; + } +#endif + ShowTemporaryEmotion(emotion, duration_ms); + } + + void StartLowBatteryAlertSequence() + { + constexpr int kMaxPlays = 3; + constexpr int64_t kIntervalMs = 5 * 60 * 1000; + + low_battery_plays_left_ = kMaxPlays - 1; + next_low_battery_play_ms_ = (esp_timer_get_time() / 1000) + kIntervalMs; + PlayBatteryEmotion("low_battery", 4000); + } + + void HandleBatteryEmotions() + { + if (charge_ == nullptr) { + return; + } + + const int level = charge_->GetBatteryLevel(); + const bool charging = charge_->IsCharging(); + + if (charging && !was_charging_) { + PlayBatteryEmotion("battery_connected", 4000); + low_battery_alert_mask_ = 0; + low_battery_plays_left_ = 0; + next_low_battery_play_ms_ = 0; + } + was_charging_ = charging; + + if (charging) { + return; + } + + if (level > 12) { + low_battery_alert_mask_ = 0; + low_battery_plays_left_ = 0; + next_low_battery_play_ms_ = 0; + return; + } + + const int64_t now_ms = esp_timer_get_time() / 1000; + if (low_battery_plays_left_ > 0 && now_ms >= next_low_battery_play_ms_) { + PlayBatteryEmotion("low_battery", 4000); + low_battery_plays_left_--; + if (low_battery_plays_left_ > 0) { + next_low_battery_play_ms_ = now_ms + 5 * 60 * 1000; + } + } + + if (level <= 5 && (low_battery_alert_mask_ & 0x02) == 0) { + low_battery_alert_mask_ |= 0x02; + StartLowBatteryAlertSequence(); + return; + } + + if (level <= 10 && (low_battery_alert_mask_ & 0x01) == 0) { + low_battery_alert_mask_ |= 0x01; + StartLowBatteryAlertSequence(); + } + } + + static void battery_task(void* arg) + { + auto* self = static_cast(arg); + while (true) { + if (self != nullptr && self->charge_ != nullptr) { + self->charge_->Update(); + self->HandleBatteryEmotions(); + } + vTaskDelay(pdMS_TO_TICKS(300)); + } + } + static void imu_event_task(void* arg) { auto* self = static_cast(arg); @@ -630,7 +755,8 @@ private: void InitializeCharge() { charge_ = new Charge(i2c_bus_, 0x55); - xTaskCreatePinnedToCore(Charge::TaskFunction, "batterydecTask", 3 * 1024, charge_, 6, &charge_task_handle_, 0); + was_charging_ = charge_->IsCharging(); + xTaskCreatePinnedToCore(battery_task, "batteryTask", 3 * 1024, this, 6, &charge_task_handle_, 0); } void InitializeCst816sTouchPad() @@ -1012,6 +1138,16 @@ public: virtual Camera* GetCamera() override { return camera_; } + + virtual bool GetBatteryLevel(int& level, bool& charging, bool& discharging) override { + if (charge_ == nullptr) { + return false; + } + level = charge_->GetBatteryLevel(); + charging = charge_->IsCharging(); + discharging = charge_->IsDischarging(); + return true; + } }; DECLARE_BOARD(EspVocat);