Files
xiaozhi-esp32/main/boards/common/sleep_timer.cc
T
3e78cd73bd v2.4.0: Migrate to IDF 6.0 (#2120)
* Migrate board builds to ESP-IDF 6.0.1

* Document upstream IDF 6 CI blockers

* Use IDF 6 patched registry components

* Fix board-specific ESP-IDF 6 build errors

* Fix remaining ESP-IDF 6 board builds

* Document final ESP-IDF 6 matrix results [skip ci]

* update IDF 6 component releases

* rebase IDF 6 migration and use upstream SSCMA

* Enhance audio input management in AudioService

- Introduced AS_EVENT_AUDIO_INPUT_STOP_REQUEST to manage audio input stopping more effectively.
- Updated AudioService::Start() to clear the new stop request event.
- Modified AudioService::AudioInputTask() to handle the stop request and ensure proper input disabling.
- Adjusted AdcPdmAudioCodec::Start() to reflect lazy input opening, ensuring start/stop operations run in the same task.

* Refactor audio codec management and configuration

- Added output_device_opened_ flag to track the state of the output device in Es8388 and Es8389 codecs.
- Updated EnableOutput method to prevent unnecessary device reopening and added mute functionality to manage audio output more effectively.
- Enhanced error handling in Es8311AudioCodec by ensuring proper closure and deletion of the codec device.
- Adjusted sample rates in board configurations for atk-dnesp32s3-box2 to 24000 Hz and introduced AUDIO_CODEC_USE_MCLK for improved clock management.

* Update build configurations and documentation for ESP-IDF 6 compatibility

- Added container specification for the build workflow using espressif/idf:v6.0.1.
- Updated the version of the espressif/esp_video component to ^2.3.0.
- Enhanced documentation to clarify the support status of ESP32-P4 variants, specifying that legacy Rev < 3 variants are excluded from the IDF 6 matrix.
- Adjusted board configurations to include IDF version constraints and necessary SDK configurations for legacy support.
- Improved handling of YUV formats in EspVideo to maintain compatibility with previous versions.

* Implement playback drained event handling in Application

- Added MAIN_EVENT_PLAYBACK_DRAINED to manage playback state transitions.
- Introduced callbacks for playback drained events in AudioService to trigger listening state changes.
- Refactored Application::Run() to handle deferred listening start based on playback queue status.
- Enhanced audio processing logic to prevent audio truncation during state changes.
- Updated related methods to ensure proper wake word detection configuration during listening mode.

* Fix variant selection shell in CI

* Update project version to 2.4.0 and adjust component dependencies

- Bump project version in CMakeLists.txt to 2.4.0.
- Change espressif/esp_video component version to ^2.0.1 in idf_component.yml.
- Modify AUDIO_INPUT_REFERENCE setting in config.h to false for m5stack-core-s3.
- Remove unnecessary infinite loops in xmini_c3_board.cc during initialization.

---------

Co-authored-by: Xiaoxia <terrence.huang@tenclass.com>
2026-07-17 13:41:52 +08:00

146 lines
4.6 KiB
C++

#include "sleep_timer.h"
#include "application.h"
#include "board.h"
#include "display.h"
#include "settings.h"
#include <esp_log.h>
#include <esp_sleep.h>
#include <esp_lvgl_port.h>
#include <esp_idf_version.h>
#include <inttypes.h>
#define TAG "SleepTimer"
SleepTimer::SleepTimer(int seconds_to_light_sleep, int seconds_to_deep_sleep)
: seconds_to_light_sleep_(seconds_to_light_sleep), seconds_to_deep_sleep_(seconds_to_deep_sleep) {
esp_timer_create_args_t timer_args = {
.callback = [](void* arg) {
auto self = static_cast<SleepTimer*>(arg);
self->CheckTimer();
},
.arg = this,
.dispatch_method = ESP_TIMER_TASK,
.name = "sleep_timer",
.skip_unhandled_events = true,
};
ESP_ERROR_CHECK(esp_timer_create(&timer_args, &sleep_timer_));
}
SleepTimer::~SleepTimer() {
esp_timer_stop(sleep_timer_);
esp_timer_delete(sleep_timer_);
}
void SleepTimer::SetEnabled(bool enabled) {
if (enabled && !enabled_) {
Settings settings("wifi", false);
if (!settings.GetBool("sleep_mode", true)) {
ESP_LOGI(TAG, "Power save timer is disabled by settings");
return;
}
ticks_ = 0;
enabled_ = enabled;
ESP_ERROR_CHECK(esp_timer_start_periodic(sleep_timer_, 1000000));
ESP_LOGI(TAG, "Sleep timer enabled");
} else if (!enabled && enabled_) {
ESP_ERROR_CHECK(esp_timer_stop(sleep_timer_));
enabled_ = enabled;
WakeUp();
ESP_LOGI(TAG, "Sleep timer disabled");
}
}
void SleepTimer::OnEnterLightSleepMode(std::function<void()> callback) {
on_enter_light_sleep_mode_ = callback;
}
void SleepTimer::OnExitLightSleepMode(std::function<void()> callback) {
on_exit_light_sleep_mode_ = callback;
}
void SleepTimer::OnEnterDeepSleepMode(std::function<void()> callback) {
on_enter_deep_sleep_mode_ = callback;
}
void SleepTimer::CheckTimer() {
auto& app = Application::GetInstance();
if (!app.CanEnterSleepMode()) {
ticks_ = 0;
return;
}
ticks_++;
if (seconds_to_light_sleep_ != -1 && ticks_ >= seconds_to_light_sleep_) {
if (!in_light_sleep_mode_) {
in_light_sleep_mode_ = true;
if (on_enter_light_sleep_mode_) {
on_enter_light_sleep_mode_();
}
auto& audio_service = app.GetAudioService();
bool is_wake_word_running = audio_service.IsWakeWordRunning();
if (is_wake_word_running) {
audio_service.EnableWakeWordDetection(false);
vTaskDelay(pdMS_TO_TICKS(100));
}
app.Schedule([this, &app]() {
while (in_light_sleep_mode_) {
auto& board = Board::GetInstance();
board.GetDisplay()->UpdateStatusBar(true);
lv_refr_now(nullptr);
lvgl_port_stop();
// 配置timer唤醒源(30秒后自动唤醒)
esp_sleep_enable_timer_wakeup(30 * 1000000);
// 进入light sleep模式
esp_light_sleep_start();
lvgl_port_resume();
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(6, 0, 0)
// IDF 6 deprecates esp_sleep_get_wakeup_cause() in favor of
// the bitmap-based API that can report simultaneous sources.
auto wakeup_causes = esp_sleep_get_wakeup_causes();
ESP_LOGI(TAG, "Wake up from light sleep, wakeup_causes: 0x%" PRIx32, wakeup_causes);
if (!(wakeup_causes & (1UL << ESP_SLEEP_WAKEUP_TIMER))) {
break;
}
#else
auto wakeup_reason = esp_sleep_get_wakeup_cause();
ESP_LOGI(TAG, "Wake up from light sleep, wakeup_reason: %d", wakeup_reason);
if (wakeup_reason != ESP_SLEEP_WAKEUP_TIMER) {
break;
}
#endif
}
WakeUp();
});
if (is_wake_word_running) {
audio_service.EnableWakeWordDetection(true);
}
}
}
if (seconds_to_deep_sleep_ != -1 && ticks_ >= seconds_to_deep_sleep_) {
if (on_enter_deep_sleep_mode_) {
on_enter_deep_sleep_mode_();
}
esp_deep_sleep_start();
}
}
void SleepTimer::WakeUp() {
ticks_ = 0;
if (in_light_sleep_mode_) {
in_light_sleep_mode_ = false;
if (on_exit_light_sleep_mode_) {
on_exit_light_sleep_mode_();
}
}
}