feat(power): add controlled low-battery shutdown

This commit is contained in:
chenjintao
2026-08-24 15:06:02 +08:00
parent 96e975d84c
commit 23f4969404
13 changed files with 467 additions and 19 deletions
@@ -79,6 +79,14 @@ static esp_err_t app_composition_run_plan_a_boot_sequence(void)
return err;
}
esp_err_t battery_guard_err = control_plane_battery_apply_boot_guard();
if (battery_guard_err != ESP_OK &&
battery_guard_err != ESP_ERR_NOT_SUPPORTED) {
ESP_LOGW(TAG,
"Battery boot guard failed, continue boot: %s",
esp_err_to_name(battery_guard_err));
}
app_composition_log_stage(APP_COMPOSITION_BOOT_STAGE_AUDIO_INIT,
"audio_init",
"boot_sound_policy_begin");
@@ -130,6 +130,12 @@ esp_err_t control_plane_init(void);
*/
esp_err_t control_plane_battery_service_start(void);
/**
* Re-enter deep sleep before normal boot when a low-battery wake has not
* recovered and no charger is active.
*/
esp_err_t control_plane_battery_apply_boot_guard(void);
/**
* @brief Prepare platform-backed persistent storage before runtime workers start.
*
@@ -147,6 +147,7 @@ bool control_plane_voice_turn_is_idle(void);
bool control_plane_ota_voice_services_allowed(void);
bool control_plane_ota_business_interactions_allowed(void);
bool control_plane_runtime_is_stopping(void);
void control_plane_set_runtime_stop_requested(bool stop_requested);
esp_err_t control_plane_battery_service_stop(int timeout_ms);
void control_plane_battery_service_set_light_standby(bool standby);
@@ -25,9 +25,31 @@
#define BATTERY_FULL_CONFIRM_TIME_MS 5000
#define BATTERY_FULL_CONFIRM_SAMPLES 3U
#define BATTERY_FULL_MIN_VOLTAGE_MV 4050
#define BATTERY_SHUTDOWN_CONFIRM_SAMPLES 3U
#define BATTERY_EMERGENCY_CONFIRM_SAMPLES 2U
#define BATTERY_SHUTDOWN_RETRY_MS 1000
#define BATTERY_SHUTDOWN_TASK_STACK_BYTES 6144
#define BATTERY_SHUTDOWN_TASK_PRIORITY 5
#define BATTERY_SHUTDOWN_MOTOR_STOP_MS 2000
#define BATTERY_LOW_PROMPT_FILE "/littlefs/audio_prompts/battery_low_20.opus"
#define BATTERY_FULL_PROMPT_FILE "/littlefs/audio_prompts/charge_complete.opus"
#ifndef CONFIG_TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
#define CONFIG_TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE 0
#endif
#ifndef CONFIG_TQ_BATTERY_SHUTDOWN_PROMPT_FILE
#define CONFIG_TQ_BATTERY_SHUTDOWN_PROMPT_FILE "/littlefs/audio_prompts/shutdown_power_empty.opus"
#endif
#if CONFIG_TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
#if CONFIG_TQ_BATTERY_EMERGENCY_VOLTAGE_MV >= CONFIG_TQ_BATTERY_SHUTDOWN_VOLTAGE_MV
#error "Emergency battery voltage must be below the controlled shutdown threshold"
#endif
#if CONFIG_TQ_BATTERY_WAKE_RECOVERY_VOLTAGE_MV <= CONFIG_TQ_BATTERY_SHUTDOWN_VOLTAGE_MV
#error "Battery wake recovery voltage must exceed the shutdown threshold"
#endif
#endif
#ifndef CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC
#define CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC 300
#endif
@@ -38,6 +60,17 @@ static bool s_stop_requested;
static bool s_starting;
static bool s_charge_gpio_isr_registered;
static bool s_light_standby;
static TaskHandle_t s_shutdown_task;
static uint8_t s_shutdown_confirmations;
static uint8_t s_emergency_confirmations;
typedef enum {
BATTERY_POWER_STATE_RUNNING = 0,
BATTERY_POWER_STATE_SHUTDOWN_REQUESTED,
BATTERY_POWER_STATE_SHUTTING_DOWN,
} battery_power_state_t;
static battery_power_state_t s_power_state = BATTERY_POWER_STATE_RUNNING;
static portMUX_TYPE s_battery_lock = portMUX_INITIALIZER_UNLOCKED;
static void IRAM_ATTR battery_charge_gpio_isr(void *arg)
@@ -145,14 +178,137 @@ static void battery_worker_finish(void)
portEXIT_CRITICAL(&s_battery_lock);
}
static void control_plane_battery_critical_hook(
static void battery_shutdown_task(void *arg)
{
(void)arg;
portENTER_CRITICAL(&s_battery_lock);
s_power_state = BATTERY_POWER_STATE_SHUTTING_DOWN;
portEXIT_CRITICAL(&s_battery_lock);
ESP_LOGW(TAG, "Controlled low-battery shutdown started");
control_plane_set_runtime_stop_requested(true);
(void)control_plane_local_wake_stop(BATTERY_SHUTDOWN_MOTOR_STOP_MS);
(void)control_plane_stop_ota_service(BATTERY_SHUTDOWN_MOTOR_STOP_MS);
(void)control_plane_stop_multimodal_client();
control_plane_stop_key_input_task();
control_plane_stop_system_event_task();
esp_err_t motor_err = domain_direction_release_motor_hold(
BATTERY_SHUTDOWN_MOTOR_STOP_MS);
if (motor_err != ESP_OK) {
ESP_LOGW(TAG, "Release stepper before shutdown failed: %s",
esp_err_to_name(motor_err));
}
(void)domain_runtime_multimodal_audio_stop_downstream_playback();
for (;;) {
esp_err_t audio_init_err = domain_runtime_multimodal_audio_init();
esp_err_t prompt_err = audio_init_err == ESP_OK
? domain_runtime_behavior_play_prompt_file(
CONFIG_TQ_BATTERY_SHUTDOWN_PROMPT_FILE)
: audio_init_err;
if (prompt_err == ESP_OK) {
break;
}
/* The product requires the complete prompt before entering deep sleep. */
ESP_LOGE(TAG,
"Shutdown prompt did not complete; retrying: %s",
esp_err_to_name(prompt_err));
vTaskDelay(pdMS_TO_TICKS(BATTERY_SHUTDOWN_RETRY_MS));
}
(void)domain_runtime_multimodal_audio_set_speaker_output_enabled(false);
domain_runtime_multimodal_audio_deinit();
(void)domain_runtime_set_lcd_backlight_brightness(0U);
(void)domain_runtime_wifi_portal_stop_sta();
ESP_LOGW(TAG, "Shutdown prompt completed; entering deep sleep");
esp_err_t sleep_err = domain_battery_prepare_shutdown();
ESP_LOGE(TAG, "Enter deep sleep failed: %s", esp_err_to_name(sleep_err));
portENTER_CRITICAL(&s_battery_lock);
s_power_state = BATTERY_POWER_STATE_RUNNING;
s_shutdown_task = NULL;
portEXIT_CRITICAL(&s_battery_lock);
vTaskDelete(NULL);
}
static void battery_consider_controlled_shutdown(
const domain_battery_status_t *status)
{
#if CONFIG_TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
if (status == NULL) {
return;
}
portENTER_CRITICAL(&s_battery_lock);
bool already_requested = s_power_state != BATTERY_POWER_STATE_RUNNING;
portEXIT_CRITICAL(&s_battery_lock);
if (already_requested) {
return;
}
if (status->charge_state == DOMAIN_BATTERY_CHARGE_CHARGING) {
s_shutdown_confirmations = 0U;
s_emergency_confirmations = 0U;
return;
}
bool normal_sample = status->valid && !status->measurement_suppressed &&
status->percentage <= CONFIG_TQ_BATTERY_CRITICAL_ENTER_PERCENT &&
status->voltage_mv <= CONFIG_TQ_BATTERY_SHUTDOWN_VOLTAGE_MV;
bool emergency_sample = status->latest_sample_valid &&
status->latest_sample_mv <= CONFIG_TQ_BATTERY_EMERGENCY_VOLTAGE_MV;
if (normal_sample) {
if (s_shutdown_confirmations < UINT8_MAX) {
++s_shutdown_confirmations;
}
} else {
s_shutdown_confirmations = 0U;
}
if (emergency_sample) {
if (s_emergency_confirmations < UINT8_MAX) {
++s_emergency_confirmations;
}
} else {
s_emergency_confirmations = 0U;
}
if (s_shutdown_confirmations < BATTERY_SHUTDOWN_CONFIRM_SAMPLES &&
s_emergency_confirmations < BATTERY_EMERGENCY_CONFIRM_SAMPLES) {
return;
}
portENTER_CRITICAL(&s_battery_lock);
if (s_power_state != BATTERY_POWER_STATE_RUNNING || s_shutdown_task != NULL) {
portEXIT_CRITICAL(&s_battery_lock);
return;
}
s_power_state = BATTERY_POWER_STATE_SHUTDOWN_REQUESTED;
portEXIT_CRITICAL(&s_battery_lock);
TaskHandle_t task = NULL;
BaseType_t created = xTaskCreate(battery_shutdown_task,
"cp_bat_shutdown",
BATTERY_SHUTDOWN_TASK_STACK_BYTES,
NULL,
BATTERY_SHUTDOWN_TASK_PRIORITY,
&task);
portENTER_CRITICAL(&s_battery_lock);
if (created == pdPASS && task != NULL) {
s_shutdown_task = task;
} else {
s_power_state = BATTERY_POWER_STATE_RUNNING;
}
portEXIT_CRITICAL(&s_battery_lock);
if (created != pdPASS || task == NULL) {
ESP_LOGE(TAG, "Create controlled-shutdown task failed");
}
#else
(void)status;
/*
* Reserved for a future product decision. Do not stop actuators, enter
* sleep, or cut board power in this release.
*/
#endif
}
static void control_plane_battery_low_hook(
@@ -268,9 +424,6 @@ static void battery_worker(void *arg)
previous_level == DOMAIN_BATTERY_LEVEL_UNKNOWN)) {
control_plane_battery_low_hook(&status);
}
if (status.level_state == DOMAIN_BATTERY_LEVEL_CRITICAL) {
control_plane_battery_critical_hook(&status);
}
previous_level = status.level_state;
}
if (status.charge_state != previous_charge) {
@@ -321,12 +474,15 @@ static void battery_worker(void *arg)
}
}
}
battery_consider_controlled_shutdown(&status);
}
uint32_t delay_ms;
if (status_err == ESP_OK && status.measurement_suppressed) {
delay_ms = BATTERY_SUPPRESSED_RETRY_MS;
} else if (full_hint_candidate) {
} else if (full_hint_candidate || s_shutdown_confirmations > 0U ||
s_emergency_confirmations > 0U) {
delay_ms = BATTERY_FULL_CONFIRM_RETRY_MS;
} else if (battery_light_standby()) {
delay_ms = (uint32_t)CONFIG_TQ_LIGHT_STANDBY_BATTERY_INTERVAL_SEC * 1000U;
@@ -347,6 +503,42 @@ static void battery_worker(void *arg)
vTaskDelete(NULL);
}
esp_err_t control_plane_battery_apply_boot_guard(void)
{
#if CONFIG_TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
if (!domain_battery_woke_from_deep_sleep()) {
return ESP_OK;
}
esp_err_t sample_err = domain_battery_sample();
if (sample_err != ESP_OK) {
return sample_err;
}
domain_battery_status_t status = {0};
esp_err_t status_err = domain_battery_get_status(&status);
if (status_err != ESP_OK) {
return status_err;
}
ESP_LOGW(TAG,
"Deep-sleep wake battery check voltage=%dmV charge=%s valid=%d",
status.voltage_mv,
charge_state_name(status.charge_state),
status.valid);
if (status.charge_state == DOMAIN_BATTERY_CHARGE_CHARGING ||
(status.valid &&
status.voltage_mv >= CONFIG_TQ_BATTERY_WAKE_RECOVERY_VOLTAGE_MV)) {
domain_battery_complete_wakeup();
return ESP_OK;
}
ESP_LOGW(TAG, "Battery has not recovered; returning to deep sleep");
return domain_battery_prepare_shutdown();
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
esp_err_t control_plane_battery_service_start(void)
{
portENTER_CRITICAL(&s_battery_lock);
@@ -442,6 +634,11 @@ esp_err_t control_plane_battery_service_start(void)
return ESP_ERR_NOT_SUPPORTED;
}
esp_err_t control_plane_battery_apply_boot_guard(void)
{
return ESP_ERR_NOT_SUPPORTED;
}
esp_err_t control_plane_battery_service_stop(int timeout_ms)
{
return timeout_ms < 0 ? ESP_ERR_INVALID_ARG : ESP_OK;
@@ -490,7 +490,7 @@ static bool control_plane_runtime_stop_requested(void)
return control_plane_runtime_is_stopping();
}
static void control_plane_set_runtime_stop_requested(bool stop_requested)
void control_plane_set_runtime_stop_requested(bool stop_requested)
{
portENTER_CRITICAL(&s_runtime_lock);
s_runtime_stop_requested = stop_requested;
+7 -1
View File
@@ -67,9 +67,15 @@ esp_err_t domain_battery_sample(void);
/** Copy the latest battery SSOT snapshot into `out_status`. */
esp_err_t domain_battery_get_status(domain_battery_status_t *out_status);
/** Reserved policy hook; this release intentionally performs no shutdown. */
/** Enter the configured board deep-sleep state; a successful call does not return. */
esp_err_t domain_battery_prepare_shutdown(void);
/** Return true when the current boot resumed through a deep-sleep wake source. */
bool domain_battery_woke_from_deep_sleep(void);
/** Release low-power GPIO holds after accepting a deep-sleep wake. */
void domain_battery_complete_wakeup(void);
typedef enum {
DOMAIN_WIFI_PROMPT_WIFI_SUCCESS = 0,
DOMAIN_WIFI_PROMPT_AFTER_WIFI_SUCCESS,
@@ -40,6 +40,14 @@ typedef struct {
uint8_t percentage;
} battery_curve_point_t;
typedef struct {
uint8_t version;
uint8_t reason;
uint8_t percentage;
uint8_t reserved;
int32_t voltage_mv;
} battery_shutdown_record_t;
static const battery_curve_point_t s_discharge_curve[] = {
{3300, 0},
{3400, 2},
@@ -329,8 +337,37 @@ esp_err_t domain_battery_get_status(domain_battery_status_t *out_status)
esp_err_t domain_battery_prepare_shutdown(void)
{
/* Reserved for a future board-specific sleep or power-cut sequence. */
#if CONFIG_TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
battery_shutdown_record_t record = {
.version = 1U,
.reason = 1U,
};
if (lock_state()) {
record.percentage = s_status.percentage;
record.voltage_mv = s_status.voltage_mv;
unlock_state();
}
/* Best-effort bounded record; a storage failure must not block deep sleep. */
(void)platform_storage_set_blob("battery",
"last_shutdown",
&record,
sizeof(record));
return platform_power_enter_deep_sleep(
CONFIG_TQ_BATTERY_CHARGE_GPIO,
CONFIG_TQ_BATTERY_SHUTDOWN_WAKE_BUTTON_GPIO);
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
bool domain_battery_woke_from_deep_sleep(void)
{
return platform_power_woke_from_deep_sleep();
}
void domain_battery_complete_wakeup(void)
{
platform_power_release_deep_sleep_holds();
}
#else
@@ -358,4 +395,13 @@ esp_err_t domain_battery_prepare_shutdown(void)
return ESP_ERR_NOT_SUPPORTED;
}
bool domain_battery_woke_from_deep_sleep(void)
{
return false;
}
void domain_battery_complete_wakeup(void)
{
}
#endif
+1
View File
@@ -44,6 +44,7 @@ set(platform_priv_requires
app_update
esp_http_client
esp_http_server
esp_hw_support
esp_lcd
esp_netif
esp_psram
+12
View File
@@ -310,6 +310,18 @@ esp_err_t platform_stepper_rotate_angle_with_accel_timeout(float angle,
esp_err_t platform_stepper_release_hold(int timeout_ms);
void platform_stepper_power_off(void);
/** Return true when this boot was caused by a configured deep-sleep wake source. */
bool platform_power_woke_from_deep_sleep(void);
/** Release board-output holds after a deep-sleep wake is accepted. */
void platform_power_release_deep_sleep_holds(void);
/**
* Enter deep sleep after configuring active-low charger and optional button
* wake GPIOs. A successful call does not return.
*/
esp_err_t platform_power_enter_deep_sleep(int charger_gpio, int button_gpio);
typedef enum {
PLATFORM_BATTERY_CHARGE_UNKNOWN = 0,
PLATFORM_BATTERY_CHARGE_NOT_CHARGING,
@@ -11,6 +11,7 @@
#include "driver/gpio.h"
#include "esp_app_desc.h"
#include "esp_log.h"
#include "esp_sleep.h"
#include "nvs.h"
#include "nvs_flash.h"
#include "platform_internal.h"
@@ -20,6 +21,18 @@
#ifndef CONFIG_TQ_STEPPER_ENABLE
#define CONFIG_TQ_STEPPER_ENABLE 1
#endif
#ifndef CONFIG_MULTIMODAL_AUDIO_PA_GPIO
#define CONFIG_MULTIMODAL_AUDIO_PA_GPIO -1
#endif
#ifndef CONFIG_TQ_SCREEN_BACKLIGHT_GPIO
#define CONFIG_TQ_SCREEN_BACKLIGHT_GPIO -1
#endif
#ifndef CONFIG_TQ_STEPPER_IN1_GPIO
#define CONFIG_TQ_STEPPER_IN1_GPIO -1
#define CONFIG_TQ_STEPPER_IN2_GPIO -1
#define CONFIG_TQ_STEPPER_IN3_GPIO -1
#define CONFIG_TQ_STEPPER_IN4_GPIO -1
#endif
static const char *TAG = "platform_core";
static bool s_initialized;
@@ -35,6 +48,106 @@ static const char *const s_known_nvs_namespaces[] = {
"fortune",
};
bool platform_power_woke_from_deep_sleep(void)
{
return esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_UNDEFINED;
}
static void platform_power_set_output_low_and_hold(int gpio)
{
if (gpio < 0 || gpio >= GPIO_NUM_MAX) {
return;
}
(void)gpio_hold_dis((gpio_num_t)gpio);
(void)gpio_set_direction((gpio_num_t)gpio, GPIO_MODE_OUTPUT);
(void)gpio_set_level((gpio_num_t)gpio, 0);
(void)gpio_hold_en((gpio_num_t)gpio);
}
static void platform_power_set_board_outputs_held(bool held)
{
const int output_gpios[] = {
CONFIG_MULTIMODAL_AUDIO_PA_GPIO,
CONFIG_TQ_SCREEN_BACKLIGHT_GPIO,
CONFIG_TQ_STEPPER_IN1_GPIO,
CONFIG_TQ_STEPPER_IN2_GPIO,
CONFIG_TQ_STEPPER_IN3_GPIO,
CONFIG_TQ_STEPPER_IN4_GPIO,
};
for (size_t i = 0; i < sizeof(output_gpios) / sizeof(output_gpios[0]); ++i) {
int gpio = output_gpios[i];
if (gpio < 0 || gpio >= GPIO_NUM_MAX) {
continue;
}
if (held) {
platform_power_set_output_low_and_hold(gpio);
} else {
(void)gpio_hold_dis((gpio_num_t)gpio);
}
}
if (held) {
gpio_deep_sleep_hold_en();
} else {
gpio_deep_sleep_hold_dis();
}
}
void platform_power_release_deep_sleep_holds(void)
{
platform_power_set_board_outputs_held(false);
}
esp_err_t platform_power_enter_deep_sleep(int charger_gpio, int button_gpio)
{
uint64_t wake_mask = 0U;
const int wake_gpios[] = {charger_gpio, button_gpio};
for (size_t i = 0; i < sizeof(wake_gpios) / sizeof(wake_gpios[0]); ++i) {
int gpio = wake_gpios[i];
if (gpio < 0) {
continue;
}
if (gpio >= GPIO_NUM_MAX ||
!esp_sleep_is_valid_wakeup_gpio((gpio_num_t)gpio)) {
ESP_LOGE(TAG, "Invalid deep-sleep wake GPIO: %d", gpio);
return ESP_ERR_INVALID_ARG;
}
gpio_config_t input_config = {
.pin_bit_mask = 1ULL << (uint32_t)gpio,
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE,
};
esp_err_t gpio_err = gpio_config(&input_config);
if (gpio_err != ESP_OK) {
return gpio_err;
}
wake_mask |= 1ULL << (uint32_t)gpio;
}
if (wake_mask == 0U) {
ESP_LOGE(TAG, "Deep sleep requires at least one wake GPIO");
return ESP_ERR_INVALID_STATE;
}
esp_err_t wake_err = esp_sleep_enable_ext1_wakeup_io(
wake_mask,
ESP_EXT1_WAKEUP_ANY_LOW);
if (wake_err != ESP_OK) {
return wake_err;
}
platform_power_set_board_outputs_held(true);
ESP_LOGW(TAG,
"Entering deep sleep wake_mask=0x%llx",
(unsigned long long)wake_mask);
esp_deep_sleep_start();
return ESP_ERR_INVALID_STATE;
}
static esp_err_t platform_gpio_isr_service_init(void)
{
if (s_gpio_isr_service_installed) {
+25 -7
View File
@@ -50,15 +50,33 @@ The direct LCD renderer composites the transparent `charging.png` asset in the
top-right corner whenever GPIO10 reports active-low charging. It composites
`lowbat.png` at the bottom center after the domain level enters low or critical
state. Both overlays may be visible together, and QR-code presentations omit
them to preserve scan reliability. Overlay state changes do not trigger any
shutdown, sleep, prompt, or actuator policy.
them to preserve scan reliability.
## Deferred Policy
## Controlled Critical-Battery Shutdown
Critical status currently produces a state-transition log only. The reserved
shutdown hook returns `ESP_ERR_NOT_SUPPORTED`; it does not stop audio or the
stepper, enter deep sleep, or release board power. Implement wake sources and
the product shutdown sequence before enabling any automatic critical action.
When controlled shutdown is enabled, an unplugged battery at or below both the
configured critical percentage and 3450 mV must produce three valid samples.
An emergency raw voltage at or below 3350 mV requires two samples and is not
blocked by the high-load suppression window. Either path latches a single
shutdown sequence.
The sequence stops interaction sources, OTA, multimodal transport, active
playback, and the stepper before synchronously playing
`shutdown_power_empty.opus`. The prompt is mandatory: a failed or incomplete
playback is retried and deep sleep is entered only after playback completion.
The codec/PA, LCD backlight, Wi-Fi, and stepper outputs are then disabled. PA,
backlight, and stepper GPIOs are held low through deep sleep. A small NVS record
stores the shutdown reason, voltage, and percentage on a best-effort basis.
GPIO10 is configured as the default active-low wake source. The optional
active-low button wake GPIO defaults to `-1` (disabled). After deep-sleep wake,
normal boot continues only while charging or when battery voltage is at least
3550 mV. Otherwise the device immediately returns to deep sleep before audio,
LCD, Wi-Fi, or server startup.
This is a low-power software shutdown, not physical board power removal,
because the board power-hold GPIO is disabled. The fixed battery divider and
any board-level always-on loads continue to draw current.
## Bench Calibration
+34
View File
@@ -1039,6 +1039,40 @@ menu "Example Configuration"
range 1 100
depends on TQ_BATTERY_ENABLE
config TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
bool "Enable controlled shutdown at critically low battery"
default y
depends on TQ_BATTERY_ENABLE
config TQ_BATTERY_SHUTDOWN_VOLTAGE_MV
int "Controlled shutdown voltage threshold (mV)"
default 3450
range 3000 4200
depends on TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
config TQ_BATTERY_EMERGENCY_VOLTAGE_MV
int "Emergency shutdown voltage threshold (mV)"
default 3350
range 3000 4200
depends on TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
config TQ_BATTERY_WAKE_RECOVERY_VOLTAGE_MV
int "Minimum voltage after deep-sleep wake (mV)"
default 3550
range 3000 4200
depends on TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
config TQ_BATTERY_SHUTDOWN_WAKE_BUTTON_GPIO
int "Optional active-low deep-sleep wake button GPIO (-1 to disable)"
default -1
range -1 48
depends on TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
config TQ_BATTERY_SHUTDOWN_PROMPT_FILE
string "Critical-battery shutdown prompt file"
default "/littlefs/audio_prompts/shutdown_power_empty.opus"
depends on TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE
endmenu
menu "Wi-Fi Provisioning"
+6
View File
@@ -341,4 +341,10 @@ CONFIG_TQ_BATTERY_LOW_ENTER_PERCENT=20
CONFIG_TQ_BATTERY_LOW_EXIT_PERCENT=25
CONFIG_TQ_BATTERY_CRITICAL_ENTER_PERCENT=5
CONFIG_TQ_BATTERY_CRITICAL_EXIT_PERCENT=10
CONFIG_TQ_BATTERY_CONTROLLED_SHUTDOWN_ENABLE=y
CONFIG_TQ_BATTERY_SHUTDOWN_VOLTAGE_MV=3450
CONFIG_TQ_BATTERY_EMERGENCY_VOLTAGE_MV=3350
CONFIG_TQ_BATTERY_WAKE_RECOVERY_VOLTAGE_MV=3550
CONFIG_TQ_BATTERY_SHUTDOWN_WAKE_BUTTON_GPIO=-1
CONFIG_TQ_BATTERY_SHUTDOWN_PROMPT_FILE="/littlefs/audio_prompts/shutdown_power_empty.opus"
CONFIG_MULTIMODAL_AUDIO_PA_GPIO=11