feat(control_plane): apply light standby hardware policy
This commit is contained in:
@@ -142,9 +142,9 @@ esp_err_t control_plane_prepare_runtime_storage(void);
|
||||
/**
|
||||
* @brief Start the control-plane owned light-standby coordinator.
|
||||
*
|
||||
* The coordinator tracks one inactivity deadline for the full runtime. Phase
|
||||
* one records state transitions only; hardware power policies are attached in
|
||||
* later phases. Repeated starts are idempotent.
|
||||
* The coordinator tracks one inactivity deadline for the full runtime and
|
||||
* applies configured local hardware power policies. Repeated starts are
|
||||
* idempotent.
|
||||
*/
|
||||
esp_err_t control_plane_light_standby_start(void);
|
||||
|
||||
@@ -159,8 +159,9 @@ esp_err_t control_plane_light_standby_stop(int timeout_ms);
|
||||
/**
|
||||
* @brief Record user activity and return the coordinator to ACTIVE state.
|
||||
*
|
||||
* This call is non-blocking and safe from control-plane worker tasks. It must
|
||||
* not be called directly from an ISR.
|
||||
* If hardware is in light standby, this call restores configured hardware
|
||||
* before returning. Transition serialization is bounded; the API must not be
|
||||
* called directly from an ISR.
|
||||
*/
|
||||
esp_err_t control_plane_light_standby_record_activity(
|
||||
control_plane_light_standby_activity_t activity);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "esp_log.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/task.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
@@ -25,19 +26,41 @@
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LIGHT_STANDBY_TASK_STACK_BYTES
|
||||
#define CONFIG_TQ_LIGHT_STANDBY_TASK_STACK_BYTES 3072
|
||||
#define CONFIG_TQ_LIGHT_STANDBY_TASK_STACK_BYTES 4096
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LIGHT_STANDBY_TASK_PRIORITY
|
||||
#define CONFIG_TQ_LIGHT_STANDBY_TASK_PRIORITY 3
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT
|
||||
#define CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT
|
||||
#define CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT 10
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
|
||||
#define CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD
|
||||
#define CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD 0
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT
|
||||
#define CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT 100
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_TQ_LOCAL_TASK_CORE
|
||||
#define CONFIG_TQ_LOCAL_TASK_CORE 1
|
||||
#endif
|
||||
|
||||
#define CONTROL_PLANE_LIGHT_STANDBY_ELIGIBILITY_RECHECK_MS 1000U
|
||||
#define CONTROL_PLANE_LIGHT_STANDBY_MOTOR_RELEASE_WAIT_MS 200U
|
||||
#define CONTROL_PLANE_LIGHT_STANDBY_STOP_POLL_MS 20U
|
||||
#define CONTROL_PLANE_LIGHT_STANDBY_TRANSITION_WAIT_MS 1000U
|
||||
|
||||
#if CONFIG_TQ_LOCAL_TASK_CORE < 0
|
||||
#define CONTROL_PLANE_LIGHT_STANDBY_TASK_CORE tskNO_AFFINITY
|
||||
@@ -59,6 +82,8 @@ typedef struct {
|
||||
static const char *TAG = "cp_light_standby";
|
||||
static control_plane_light_standby_runtime_t s_standby;
|
||||
static portMUX_TYPE s_standby_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||
static StaticSemaphore_t s_transition_lock_storage;
|
||||
static SemaphoreHandle_t s_transition_lock;
|
||||
|
||||
static uint64_t control_plane_light_standby_now_ms(void)
|
||||
{
|
||||
@@ -139,10 +164,103 @@ static bool control_plane_light_standby_runtime_is_eligible(void)
|
||||
!state.local_responding_started;
|
||||
}
|
||||
|
||||
static bool control_plane_light_standby_lock_transition(void)
|
||||
{
|
||||
return s_transition_lock != NULL &&
|
||||
xSemaphoreTake(
|
||||
s_transition_lock,
|
||||
pdMS_TO_TICKS(CONTROL_PLANE_LIGHT_STANDBY_TRANSITION_WAIT_MS)) == pdTRUE;
|
||||
}
|
||||
|
||||
static void control_plane_light_standby_unlock_transition(void)
|
||||
{
|
||||
if (s_transition_lock != NULL) {
|
||||
(void)xSemaphoreGive(s_transition_lock);
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t control_plane_light_standby_apply_wake_policy(void)
|
||||
{
|
||||
esp_err_t first_error = ESP_OK;
|
||||
|
||||
#if CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT
|
||||
esp_err_t backlight_err = domain_runtime_set_lcd_backlight_brightness(
|
||||
CONFIG_TQ_SCREEN_BACKLIGHT_BRIGHTNESS_PERCENT);
|
||||
if (backlight_err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Restore backlight failed: %s",
|
||||
esp_err_to_name(backlight_err));
|
||||
first_error = backlight_err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
|
||||
esp_err_t speaker_err =
|
||||
domain_runtime_multimodal_audio_set_speaker_output_enabled(true);
|
||||
if (speaker_err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Restore speaker output failed: %s",
|
||||
esp_err_to_name(speaker_err));
|
||||
if (first_error == ESP_OK) {
|
||||
first_error = speaker_err;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return first_error;
|
||||
}
|
||||
|
||||
static esp_err_t control_plane_light_standby_apply_entry_policy(void)
|
||||
{
|
||||
#if CONFIG_TQ_LIGHT_STANDBY_RELEASE_MOTOR_HOLD
|
||||
esp_err_t motor_err = domain_direction_release_motor_hold(
|
||||
CONTROL_PLANE_LIGHT_STANDBY_MOTOR_RELEASE_WAIT_MS);
|
||||
if (motor_err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Release motor hold failed: %s",
|
||||
esp_err_to_name(motor_err));
|
||||
return motor_err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
|
||||
esp_err_t speaker_err =
|
||||
domain_runtime_multimodal_audio_set_speaker_output_enabled(false);
|
||||
if (speaker_err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Disable speaker output failed: %s",
|
||||
esp_err_to_name(speaker_err));
|
||||
return speaker_err;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CONFIG_TQ_LIGHT_STANDBY_DIM_BACKLIGHT
|
||||
esp_err_t backlight_err = domain_runtime_set_lcd_backlight_brightness(
|
||||
CONFIG_TQ_LIGHT_STANDBY_BACKLIGHT_PERCENT);
|
||||
if (backlight_err != ESP_OK) {
|
||||
ESP_LOGE(TAG,
|
||||
"Dim backlight failed: %s",
|
||||
esp_err_to_name(backlight_err));
|
||||
#if CONFIG_TQ_LIGHT_STANDBY_DISABLE_SPEAKER_OUTPUT
|
||||
(void)domain_runtime_multimodal_audio_set_speaker_output_enabled(true);
|
||||
#endif
|
||||
return backlight_err;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void control_plane_light_standby_enter(uint64_t now_ms)
|
||||
{
|
||||
if (!control_plane_light_standby_lock_transition()) {
|
||||
ESP_LOGW(TAG, "Enter transition lock timed out");
|
||||
return;
|
||||
}
|
||||
|
||||
control_plane_light_standby_state_t from = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||
bool entering = false;
|
||||
uint64_t entry_activity_ms = 0U;
|
||||
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
if (s_standby.running && !s_standby.stop_requested &&
|
||||
@@ -150,6 +268,7 @@ static void control_plane_light_standby_enter(uint64_t now_ms)
|
||||
(now_ms - s_standby.last_activity_ms) >=
|
||||
(uint64_t)CONFIG_TQ_LIGHT_STANDBY_IDLE_TIMEOUT_MS) {
|
||||
from = s_standby.state;
|
||||
entry_activity_ms = s_standby.last_activity_ms;
|
||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING;
|
||||
s_standby.transition_count++;
|
||||
entering = true;
|
||||
@@ -157,20 +276,37 @@ static void control_plane_light_standby_enter(uint64_t now_ms)
|
||||
portEXIT_CRITICAL(&s_standby_lock);
|
||||
|
||||
if (!entering) {
|
||||
control_plane_light_standby_unlock_transition();
|
||||
return;
|
||||
}
|
||||
control_plane_light_standby_log_transition(from,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING,
|
||||
"idle_timeout");
|
||||
|
||||
esp_err_t policy_err = control_plane_light_standby_apply_entry_policy();
|
||||
bool entered = false;
|
||||
bool restore_required = false;
|
||||
const char *completion_reason = "hardware_policy";
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING &&
|
||||
!s_standby.stop_requested) {
|
||||
!s_standby.stop_requested &&
|
||||
s_standby.last_activity_ms == entry_activity_ms &&
|
||||
policy_err == ESP_OK) {
|
||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY;
|
||||
s_standby.standby_since_ms = now_ms;
|
||||
s_standby.transition_count++;
|
||||
entered = true;
|
||||
} else if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING) {
|
||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING;
|
||||
s_standby.transition_count++;
|
||||
restore_required = true;
|
||||
if (policy_err != ESP_OK) {
|
||||
completion_reason = "hardware_policy_failed";
|
||||
} else if (s_standby.stop_requested) {
|
||||
completion_reason = "stop_requested";
|
||||
} else {
|
||||
completion_reason = "activity_during_entry";
|
||||
}
|
||||
}
|
||||
portEXIT_CRITICAL(&s_standby_lock);
|
||||
|
||||
@@ -178,8 +314,38 @@ static void control_plane_light_standby_enter(uint64_t now_ms)
|
||||
control_plane_light_standby_log_transition(
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY,
|
||||
"phase1_state_only");
|
||||
completion_reason);
|
||||
control_plane_light_standby_unlock_transition();
|
||||
return;
|
||||
}
|
||||
|
||||
control_plane_light_standby_log_transition(
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||
completion_reason);
|
||||
if (restore_required) {
|
||||
(void)control_plane_light_standby_apply_wake_policy();
|
||||
}
|
||||
|
||||
bool activated = false;
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING) {
|
||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||
s_standby.standby_since_ms = 0U;
|
||||
if (policy_err != ESP_OK) {
|
||||
s_standby.last_activity_ms = now_ms;
|
||||
}
|
||||
s_standby.transition_count++;
|
||||
activated = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_standby_lock);
|
||||
if (activated) {
|
||||
control_plane_light_standby_log_transition(
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE,
|
||||
completion_reason);
|
||||
}
|
||||
control_plane_light_standby_unlock_transition();
|
||||
}
|
||||
|
||||
static void control_plane_light_standby_task(void *arg)
|
||||
@@ -232,6 +398,38 @@ static void control_plane_light_standby_task(void *arg)
|
||||
(void)ulTaskNotifyTake(pdTRUE, wait_ticks);
|
||||
}
|
||||
|
||||
if (control_plane_light_standby_lock_transition()) {
|
||||
control_plane_light_standby_state_t from =
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||
bool restore_required = false;
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY ||
|
||||
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING ||
|
||||
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING) {
|
||||
from = s_standby.state;
|
||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING;
|
||||
s_standby.transition_count++;
|
||||
restore_required = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_standby_lock);
|
||||
|
||||
if (restore_required) {
|
||||
control_plane_light_standby_log_transition(
|
||||
from,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||
"coordinator_stop");
|
||||
esp_err_t wake_err = control_plane_light_standby_apply_wake_policy();
|
||||
if (wake_err != ESP_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
"Hardware restore during stop failed: %s",
|
||||
esp_err_to_name(wake_err));
|
||||
}
|
||||
}
|
||||
control_plane_light_standby_unlock_transition();
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Stop transition lock timed out");
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
s_standby.task = NULL;
|
||||
s_standby.running = false;
|
||||
@@ -247,6 +445,13 @@ esp_err_t control_plane_light_standby_start(void)
|
||||
#if !CONFIG_TQ_LIGHT_STANDBY_ENABLE
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
if (s_transition_lock == NULL) {
|
||||
s_transition_lock = xSemaphoreCreateMutexStatic(&s_transition_lock_storage);
|
||||
if (s_transition_lock == NULL) {
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t now_ms = control_plane_light_standby_now_ms();
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
if (s_standby.running) {
|
||||
@@ -348,9 +553,8 @@ esp_err_t control_plane_light_standby_record_activity(
|
||||
#if !CONFIG_TQ_LIGHT_STANDBY_ENABLE
|
||||
return ESP_ERR_NOT_SUPPORTED;
|
||||
#else
|
||||
control_plane_light_standby_state_t from = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||
TaskHandle_t task = NULL;
|
||||
bool waking = false;
|
||||
bool wake_required = false;
|
||||
uint64_t now_ms = control_plane_light_standby_now_ms();
|
||||
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
@@ -363,6 +567,27 @@ esp_err_t control_plane_light_standby_record_activity(
|
||||
task = s_standby.task;
|
||||
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY ||
|
||||
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING) {
|
||||
wake_required = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_standby_lock);
|
||||
|
||||
if (task != NULL) {
|
||||
xTaskNotifyGive(task);
|
||||
}
|
||||
if (!wake_required) {
|
||||
return ESP_OK;
|
||||
}
|
||||
if (!control_plane_light_standby_lock_transition()) {
|
||||
ESP_LOGE(TAG, "Wake transition lock timed out");
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
control_plane_light_standby_state_t from = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||
bool waking = false;
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
if (s_standby.running && !s_standby.stop_requested &&
|
||||
(s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_STANDBY ||
|
||||
s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_ENTERING)) {
|
||||
from = s_standby.state;
|
||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING;
|
||||
s_standby.transition_count++;
|
||||
@@ -370,32 +595,36 @@ esp_err_t control_plane_light_standby_record_activity(
|
||||
}
|
||||
portEXIT_CRITICAL(&s_standby_lock);
|
||||
|
||||
if (waking) {
|
||||
const char *reason = control_plane_light_standby_activity_name(activity);
|
||||
control_plane_light_standby_log_transition(
|
||||
from,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||
reason);
|
||||
|
||||
bool activated = false;
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING) {
|
||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||
s_standby.standby_since_ms = 0U;
|
||||
s_standby.transition_count++;
|
||||
activated = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_standby_lock);
|
||||
if (activated) {
|
||||
control_plane_light_standby_log_transition(
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE,
|
||||
reason);
|
||||
}
|
||||
if (!waking) {
|
||||
control_plane_light_standby_unlock_transition();
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
if (task != NULL) {
|
||||
xTaskNotifyGive(task);
|
||||
const char *reason = control_plane_light_standby_activity_name(activity);
|
||||
control_plane_light_standby_log_transition(
|
||||
from,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||
reason);
|
||||
esp_err_t wake_err = control_plane_light_standby_apply_wake_policy();
|
||||
|
||||
bool activated = false;
|
||||
portENTER_CRITICAL(&s_standby_lock);
|
||||
if (s_standby.state == CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING) {
|
||||
s_standby.state = CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE;
|
||||
s_standby.standby_since_ms = 0U;
|
||||
s_standby.transition_count++;
|
||||
activated = true;
|
||||
}
|
||||
portEXIT_CRITICAL(&s_standby_lock);
|
||||
if (activated) {
|
||||
control_plane_light_standby_log_transition(
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_WAKING,
|
||||
CONTROL_PLANE_LIGHT_STANDBY_STATE_ACTIVE,
|
||||
wake_err == ESP_OK ? reason : "hardware_restore_failed");
|
||||
}
|
||||
control_plane_light_standby_unlock_transition();
|
||||
if (wake_err != ESP_OK) {
|
||||
return wake_err;
|
||||
}
|
||||
return ESP_OK;
|
||||
#endif
|
||||
|
||||
@@ -227,6 +227,8 @@ int domain_get_rotate_speed_us(void);
|
||||
*/
|
||||
esp_err_t domain_rotate_to_angle_with_timeout(float target_angle, int timeout_ms);
|
||||
esp_err_t domain_apply_rotation_delta(float delta);
|
||||
/** Release stepper holding current; the next movement re-energizes automatically. */
|
||||
esp_err_t domain_direction_release_motor_hold(int timeout_ms);
|
||||
void domain_set_calibration(const domain_direction_calibration_t *config);
|
||||
bool domain_get_calibration(domain_direction_calibration_t *out_config);
|
||||
/**
|
||||
@@ -270,6 +272,8 @@ esp_err_t domain_runtime_multimodal_audio_start_uplink(
|
||||
esp_err_t domain_runtime_multimodal_audio_stop_uplink(int timeout_ms);
|
||||
bool domain_runtime_multimodal_audio_is_uplink_running(void);
|
||||
bool domain_runtime_multimodal_audio_is_initialized(void);
|
||||
/** Gate speaker output while preserving the microphone capture path. */
|
||||
esp_err_t domain_runtime_multimodal_audio_set_speaker_output_enabled(bool enabled);
|
||||
/**
|
||||
* @brief Start local MultiNet detection for the Chinese phrase "喵掌柜".
|
||||
*
|
||||
@@ -608,6 +612,8 @@ esp_err_t domain_runtime_mini_program_provisioning_notify(
|
||||
esp_err_t domain_runtime_mini_program_provisioning_stop(int timeout_ms);
|
||||
|
||||
esp_err_t domain_runtime_init_lcd_device(void);
|
||||
/** Set LCD backlight PWM brightness from 0 to 100 percent. */
|
||||
esp_err_t domain_runtime_set_lcd_backlight_brightness(uint8_t brightness_percent);
|
||||
void domain_runtime_start_lcd_render_loop(void);
|
||||
/**
|
||||
* @brief Stop LCD render loop with bounded teardown.
|
||||
|
||||
@@ -42,6 +42,7 @@ esp_err_t domain_runtime_platform_init(void);
|
||||
esp_err_t domain_runtime_stepper_rotate_angle_with_accel_timeout(float angle,
|
||||
int target_delay_us,
|
||||
int timeout_ms);
|
||||
esp_err_t domain_runtime_stepper_release_hold(int timeout_ms);
|
||||
void domain_runtime_stepper_power_off(void);
|
||||
esp_err_t domain_runtime_digital_input_configure(const domain_digital_input_config_t *config);
|
||||
bool domain_runtime_digital_input_is_active(const domain_digital_input_config_t *config);
|
||||
|
||||
@@ -382,6 +382,11 @@ esp_err_t domain_apply_rotation_delta(float delta)
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t domain_direction_release_motor_hold(int timeout_ms)
|
||||
{
|
||||
return domain_runtime_stepper_release_hold(timeout_ms);
|
||||
}
|
||||
|
||||
void domain_set_calibration(const domain_direction_calibration_t *config)
|
||||
{
|
||||
domain_calibration_store_set(config);
|
||||
|
||||
@@ -714,6 +714,11 @@ bool domain_runtime_multimodal_audio_is_initialized(void)
|
||||
return platform_multimodal_audio_is_initialized();
|
||||
}
|
||||
|
||||
esp_err_t domain_runtime_multimodal_audio_set_speaker_output_enabled(bool enabled)
|
||||
{
|
||||
return platform_multimodal_audio_set_speaker_output_enabled(enabled);
|
||||
}
|
||||
|
||||
esp_err_t domain_runtime_multimodal_audio_play_downstream_opus(const uint8_t *data, size_t len)
|
||||
{
|
||||
return platform_multimodal_audio_play_downstream_opus(data, len);
|
||||
@@ -1190,6 +1195,11 @@ void domain_runtime_stepper_power_off(void)
|
||||
platform_stepper_power_off();
|
||||
}
|
||||
|
||||
esp_err_t domain_runtime_stepper_release_hold(int timeout_ms)
|
||||
{
|
||||
return platform_stepper_release_hold(timeout_ms);
|
||||
}
|
||||
|
||||
esp_err_t domain_runtime_digital_input_configure(const domain_digital_input_config_t *config)
|
||||
{
|
||||
if (config == NULL) {
|
||||
@@ -1329,6 +1339,11 @@ esp_err_t domain_runtime_init_lcd_device(void)
|
||||
return platform_init_lcd_device();
|
||||
}
|
||||
|
||||
esp_err_t domain_runtime_set_lcd_backlight_brightness(uint8_t brightness_percent)
|
||||
{
|
||||
return platform_lcd_set_backlight_brightness(brightness_percent);
|
||||
}
|
||||
|
||||
void domain_runtime_start_lcd_render_loop(void)
|
||||
{
|
||||
platform_start_lcd_render_loop();
|
||||
|
||||
@@ -301,6 +301,13 @@ esp_err_t platform_read_firmware_version(char *out, size_t out_len);
|
||||
esp_err_t platform_stepper_rotate_angle_with_accel_timeout(float angle,
|
||||
int target_delay_us,
|
||||
int timeout_ms);
|
||||
/**
|
||||
* @brief Release stepper holding current with bounded lock acquisition.
|
||||
*
|
||||
* A negative timeout is invalid, zero is non-blocking, and a positive value
|
||||
* bounds the wait for an in-progress movement to finish.
|
||||
*/
|
||||
esp_err_t platform_stepper_release_hold(int timeout_ms);
|
||||
void platform_stepper_power_off(void);
|
||||
|
||||
typedef enum {
|
||||
@@ -425,6 +432,13 @@ esp_err_t platform_multimodal_audio_start_uplink(
|
||||
esp_err_t platform_multimodal_audio_stop_uplink(int timeout_ms);
|
||||
bool platform_multimodal_audio_is_uplink_running(void);
|
||||
bool platform_multimodal_audio_is_initialized(void);
|
||||
/**
|
||||
* @brief Gate the speaker power amplifier without stopping microphone capture.
|
||||
*
|
||||
* Disabling first mutes the codec output and then powers down the speaker PA.
|
||||
* Enabling restores the PA while leaving output muted until playback resumes.
|
||||
*/
|
||||
esp_err_t platform_multimodal_audio_set_speaker_output_enabled(bool enabled);
|
||||
/**
|
||||
* @brief Register the single mono PCM observer for the active capture path.
|
||||
*
|
||||
|
||||
@@ -39,6 +39,7 @@ esp_err_t multimodal_audio_start_uplink(multimodal_audio_uplink_cb_t cb, void *c
|
||||
esp_err_t multimodal_audio_stop_uplink(int timeout_ms);
|
||||
bool multimodal_audio_is_uplink_running(void);
|
||||
bool multimodal_audio_is_initialized(void);
|
||||
esp_err_t multimodal_audio_set_speaker_output_enabled(bool enabled);
|
||||
esp_err_t multimodal_audio_register_pcm_observer(
|
||||
multimodal_audio_pcm_observer_t observer,
|
||||
void *observer_ctx);
|
||||
|
||||
@@ -14,6 +14,7 @@ esp_err_t platform_stepper_driver_init(void);
|
||||
esp_err_t platform_stepper_driver_rotate_angle_with_accel(float angle,
|
||||
int target_delay_us,
|
||||
int timeout_ms);
|
||||
esp_err_t platform_stepper_driver_release_hold(int timeout_ms);
|
||||
void platform_stepper_driver_power_off(void);
|
||||
void platform_power_hold_init(void);
|
||||
esp_err_t platform_io_effects_init(void);
|
||||
|
||||
@@ -1369,6 +1369,37 @@ bool multimodal_audio_is_initialized(void)
|
||||
return s_audio.initialized;
|
||||
}
|
||||
|
||||
esp_err_t multimodal_audio_set_speaker_output_enabled(bool enabled)
|
||||
{
|
||||
multimodal_audio_ctx_t *ctx = &s_audio;
|
||||
if (!ctx->initialized || ctx->codec_dev == NULL || ctx->gpio_if == NULL ||
|
||||
ctx->gpio_if->set == NULL) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
if (!enabled) {
|
||||
esp_err_t mute_err = set_downstream_output_mute_if_needed(
|
||||
ctx,
|
||||
true,
|
||||
"Light standby");
|
||||
if (mute_err != ESP_OK) {
|
||||
return mute_err;
|
||||
}
|
||||
}
|
||||
|
||||
int ret = ctx->gpio_if->set(CONFIG_MULTIMODAL_AUDIO_PA_GPIO, enabled);
|
||||
if (ret != ESP_CODEC_DEV_OK) {
|
||||
ESP_LOGW(TAG,
|
||||
"Speaker PA power %s failed: 0x%x",
|
||||
enabled ? "on" : "off",
|
||||
ret);
|
||||
return (esp_err_t)ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Speaker PA power %s", enabled ? "on" : "off");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t multimodal_audio_register_downstream_playback_callback(
|
||||
multimodal_audio_downstream_playback_callback_t cb,
|
||||
void *cb_ctx)
|
||||
|
||||
@@ -404,18 +404,21 @@ esp_err_t platform_stepper_driver_rotate_angle_with_accel(float angle,
|
||||
return err;
|
||||
}
|
||||
|
||||
void platform_stepper_driver_power_off(void)
|
||||
esp_err_t platform_stepper_driver_release_hold(int timeout_ms)
|
||||
{
|
||||
if (timeout_ms < 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (!ensure_stepper_motion_mutex()) {
|
||||
return;
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
TickType_t lock_ticks = pdMS_TO_TICKS(PLATFORM_STEPPER_POWER_OFF_WAIT_MS);
|
||||
if (lock_ticks == 0) {
|
||||
TickType_t lock_ticks = pdMS_TO_TICKS(timeout_ms);
|
||||
if (timeout_ms > 0 && lock_ticks == 0) {
|
||||
lock_ticks = 1;
|
||||
}
|
||||
if (xSemaphoreTake(s_stepper_motion_mutex, lock_ticks) != pdTRUE) {
|
||||
return;
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
portENTER_CRITICAL(&s_stepper_mux);
|
||||
@@ -423,4 +426,10 @@ void platform_stepper_driver_power_off(void)
|
||||
portEXIT_CRITICAL(&s_stepper_mux);
|
||||
|
||||
(void)xSemaphoreGive(s_stepper_motion_mutex);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void platform_stepper_driver_power_off(void)
|
||||
{
|
||||
(void)platform_stepper_driver_release_hold(PLATFORM_STEPPER_POWER_OFF_WAIT_MS);
|
||||
}
|
||||
|
||||
@@ -300,6 +300,18 @@ void platform_stepper_power_off(void)
|
||||
platform_stepper_driver_power_off();
|
||||
}
|
||||
|
||||
esp_err_t platform_stepper_release_hold(int timeout_ms)
|
||||
{
|
||||
if (timeout_ms < 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
#if !CONFIG_TQ_STEPPER_ENABLE
|
||||
return ESP_OK;
|
||||
#else
|
||||
return platform_stepper_driver_release_hold(timeout_ms);
|
||||
#endif
|
||||
}
|
||||
|
||||
esp_err_t platform_configure_digital_input(const platform_digital_input_config_t *config)
|
||||
{
|
||||
return platform_digital_input_driver_configure(config);
|
||||
|
||||
@@ -210,6 +210,11 @@ bool platform_multimodal_audio_is_initialized(void)
|
||||
return multimodal_audio_is_initialized();
|
||||
}
|
||||
|
||||
esp_err_t platform_multimodal_audio_set_speaker_output_enabled(bool enabled)
|
||||
{
|
||||
return multimodal_audio_set_speaker_output_enabled(enabled);
|
||||
}
|
||||
|
||||
esp_err_t platform_multimodal_audio_register_pcm_observer(
|
||||
platform_multimodal_audio_pcm_observer_t observer,
|
||||
void *user_data)
|
||||
|
||||
Reference in New Issue
Block a user