reconstruct led control

This commit is contained in:
Terrence
2025-01-05 19:34:28 +08:00
parent 495b949d77
commit c7c5b74d37
28 changed files with 586 additions and 386 deletions

View File

@@ -26,11 +26,14 @@ extern const char p3_err_wificonfig_end[] asm("_binary_err_wificonfig_p3_end");
static const char* const STATE_STRINGS[] = {
"unknown",
"starting",
"configuring",
"idle",
"connecting",
"listening",
"speaking",
"upgrading",
"fatal_error",
"invalid_state"
};
@@ -57,9 +60,9 @@ void Application::CheckNewVersion() {
// Wait for the chat state to be idle
do {
vTaskDelay(pdMS_TO_TICKS(3000));
} while (GetChatState() != kChatStateIdle);
} while (GetDeviceState() != kDeviceStateIdle);
SetChatState(kChatStateUpgrading);
SetDeviceState(kDeviceStateUpgrading);
display->SetIcon(FONT_AWESOME_DOWNLOAD);
display->SetStatus("新版本 " + ota_.GetFirmwareVersion());
@@ -75,7 +78,7 @@ void Application::CheckNewVersion() {
// If upgrade success, the device will reboot and never reach here
ESP_LOGI(TAG, "Firmware upgrade failed...");
SetChatState(kChatStateIdle);
SetDeviceState(kDeviceStateIdle);
} else {
ota_.MarkCurrentVersionValid();
display->ShowNotification("版本 " + ota_.GetCurrentVersion());
@@ -127,20 +130,20 @@ void Application::ToggleChatState() {
return;
}
if (chat_state_ == kChatStateIdle) {
SetChatState(kChatStateConnecting);
if (device_state_ == kDeviceStateIdle) {
SetDeviceState(kDeviceStateConnecting);
if (!protocol_->OpenAudioChannel()) {
Alert("Error", "Failed to open audio channel");
SetChatState(kChatStateIdle);
SetDeviceState(kDeviceStateIdle);
return;
}
keep_listening_ = true;
protocol_->SendStartListening(kListeningModeAutoStop);
SetChatState(kChatStateListening);
} else if (chat_state_ == kChatStateSpeaking) {
SetDeviceState(kDeviceStateListening);
} else if (device_state_ == kDeviceStateSpeaking) {
AbortSpeaking(kAbortReasonNone);
} else if (chat_state_ == kChatStateListening) {
} else if (device_state_ == kDeviceStateListening) {
protocol_->CloseAudioChannel();
}
});
@@ -154,40 +157,39 @@ void Application::StartListening() {
}
keep_listening_ = false;
if (chat_state_ == kChatStateIdle) {
if (device_state_ == kDeviceStateIdle) {
if (!protocol_->IsAudioChannelOpened()) {
SetChatState(kChatStateConnecting);
SetDeviceState(kDeviceStateConnecting);
if (!protocol_->OpenAudioChannel()) {
SetChatState(kChatStateIdle);
SetDeviceState(kDeviceStateIdle);
Alert("Error", "Failed to open audio channel");
return;
}
}
protocol_->SendStartListening(kListeningModeManualStop);
SetChatState(kChatStateListening);
} else if (chat_state_ == kChatStateSpeaking) {
SetDeviceState(kDeviceStateListening);
} else if (device_state_ == kDeviceStateSpeaking) {
AbortSpeaking(kAbortReasonNone);
protocol_->SendStartListening(kListeningModeManualStop);
// FIXME: Wait for the speaker to empty the buffer
vTaskDelay(pdMS_TO_TICKS(120));
SetChatState(kChatStateListening);
SetDeviceState(kDeviceStateListening);
}
});
}
void Application::StopListening() {
Schedule([this]() {
if (chat_state_ == kChatStateListening) {
if (device_state_ == kDeviceStateListening) {
protocol_->SendStopListening();
SetChatState(kChatStateIdle);
SetDeviceState(kDeviceStateIdle);
}
});
}
void Application::Start() {
auto& board = Board::GetInstance();
auto led_strip = board.GetLedStrip();
led_strip->LightOn(kStartup);
SetDeviceState(kDeviceStateStarting);
/* Setup the display */
auto display = board.GetDisplay();
@@ -245,26 +247,27 @@ void Application::Start() {
wake_word_detect_.Initialize(codec->input_channels(), codec->input_reference());
wake_word_detect_.OnVadStateChange([this](bool speaking) {
Schedule([this, speaking]() {
auto led_strip = Board::GetInstance().GetLedStrip();
if (chat_state_ == kChatStateListening) {
if (device_state_ == kDeviceStateListening) {
if (speaking) {
led_strip->LightOn(kListeningAndSpeaking);
voice_detected_ = true;
} else {
led_strip->LightOn(kListening);
voice_detected_ = false;
}
auto led = Board::GetInstance().GetLed();
led->OnStateChanged();
}
});
});
wake_word_detect_.OnWakeWordDetected([this](const std::string& wake_word) {
Schedule([this, &wake_word]() {
if (chat_state_ == kChatStateIdle) {
SetChatState(kChatStateConnecting);
if (device_state_ == kDeviceStateIdle) {
SetDeviceState(kDeviceStateConnecting);
wake_word_detect_.EncodeWakeWordData();
if (!protocol_->OpenAudioChannel()) {
ESP_LOGE(TAG, "Failed to open audio channel");
SetChatState(kChatStateIdle);
SetDeviceState(kDeviceStateIdle);
wake_word_detect_.StartDetection();
return;
}
@@ -278,8 +281,8 @@ void Application::Start() {
protocol_->SendWakeWordDetected(wake_word);
ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
keep_listening_ = true;
SetChatState(kChatStateListening);
} else if (chat_state_ == kChatStateSpeaking) {
SetDeviceState(kDeviceStateListening);
} else if (device_state_ == kDeviceStateSpeaking) {
AbortSpeaking(kAbortReasonWakeWordDetected);
}
@@ -302,7 +305,7 @@ void Application::Start() {
});
protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) {
std::lock_guard<std::mutex> lock(mutex_);
if (chat_state_ == kChatStateSpeaking) {
if (device_state_ == kDeviceStateSpeaking) {
audio_decode_queue_.emplace_back(std::move(data));
}
});
@@ -321,7 +324,7 @@ void Application::Start() {
protocol_->OnAudioChannelClosed([this, &board]() {
board.SetPowerSaveMode(true);
Schedule([this]() {
SetChatState(kChatStateIdle);
SetDeviceState(kDeviceStateIdle);
});
});
protocol_->OnIncomingJson([this, display](const cJSON* root) {
@@ -332,19 +335,19 @@ void Application::Start() {
if (strcmp(state->valuestring, "start") == 0) {
Schedule([this]() {
aborted_ = false;
if (chat_state_ == kChatStateIdle || chat_state_ == kChatStateListening) {
SetChatState(kChatStateSpeaking);
if (device_state_ == kDeviceStateIdle || device_state_ == kDeviceStateListening) {
SetDeviceState(kDeviceStateSpeaking);
}
});
} else if (strcmp(state->valuestring, "stop") == 0) {
Schedule([this]() {
if (chat_state_ == kChatStateSpeaking) {
if (device_state_ == kDeviceStateSpeaking) {
background_task_.WaitForCompletion();
if (keep_listening_) {
protocol_->SendStartListening(kListeningModeAutoStop);
SetChatState(kChatStateListening);
SetDeviceState(kDeviceStateListening);
} else {
SetChatState(kChatStateIdle);
SetDeviceState(kDeviceStateIdle);
}
}
});
@@ -378,11 +381,7 @@ void Application::Start() {
}
});
// Blink the LED to indicate the device is running
display->SetStatus("待命");
led_strip->LightOn(kStandby);
SetChatState(kChatStateIdle);
SetDeviceState(kDeviceStateIdle);
}
void Application::Schedule(std::function<void()> callback) {
@@ -433,7 +432,7 @@ void Application::OutputAudio() {
std::unique_lock<std::mutex> lock(mutex_);
if (audio_decode_queue_.empty()) {
// Disable the output if there is no audio data for a long time
if (chat_state_ == kChatStateIdle) {
if (device_state_ == kDeviceStateIdle) {
auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - last_output_time_).count();
if (duration > max_silence_seconds) {
codec->EnableOutput(false);
@@ -442,7 +441,7 @@ void Application::OutputAudio() {
return;
}
if (chat_state_ == kChatStateListening) {
if (device_state_ == kDeviceStateListening) {
audio_decode_queue_.clear();
return;
}
@@ -513,7 +512,7 @@ void Application::InputAudio() {
wake_word_detect_.Feed(data);
}
#else
if (chat_state_ == kChatStateListening) {
if (device_state_ == kDeviceStateListening) {
background_task_.Schedule([this, data = std::move(data)]() mutable {
opus_encoder_->Encode(std::move(data), [this](std::vector<uint8_t>&& opus) {
Schedule([this, opus = std::move(opus)]() {
@@ -531,34 +530,32 @@ void Application::AbortSpeaking(AbortReason reason) {
protocol_->SendAbortSpeaking(reason);
}
void Application::SetChatState(ChatState state) {
if (chat_state_ == state) {
void Application::SetDeviceState(DeviceState state) {
if (device_state_ == state) {
return;
}
chat_state_ = state;
ESP_LOGI(TAG, "STATE: %s", STATE_STRINGS[chat_state_]);
device_state_ = state;
ESP_LOGI(TAG, "STATE: %s", STATE_STRINGS[device_state_]);
// The state is changed, wait for all background tasks to finish
background_task_.WaitForCompletion();
auto display = Board::GetInstance().GetDisplay();
auto led_strip = Board::GetInstance().GetLedStrip();
auto led = Board::GetInstance().GetLed();
led->OnStateChanged();
switch (state) {
case kChatStateUnknown:
case kChatStateIdle:
led_strip->LightOff();
case kDeviceStateUnknown:
case kDeviceStateIdle:
display->SetStatus("待命");
display->SetEmotion("neutral");
#ifdef CONFIG_IDF_TARGET_ESP32S3
audio_processor_.Stop();
#endif
break;
case kChatStateConnecting:
led_strip->LightOn(kConnecting);
case kDeviceStateConnecting:
display->SetStatus("连接中...");
break;
case kChatStateListening:
led_strip->LightOn(kListening);
case kDeviceStateListening:
display->SetStatus("聆听中...");
display->SetEmotion("neutral");
ResetDecoder();
@@ -568,20 +565,16 @@ void Application::SetChatState(ChatState state) {
#endif
UpdateIotStates();
break;
case kChatStateSpeaking:
led_strip->LightOn(kSpeaking);
case kDeviceStateSpeaking:
display->SetStatus("说话中...");
ResetDecoder();
#if CONFIG_IDF_TARGET_ESP32S3
audio_processor_.Stop();
#endif
break;
case kChatStateUpgrading:
led_strip->LightOn(kUpgrading);
break;
default:
ESP_LOGE(TAG, "Invalid chat state: %d", chat_state_);
return;
// Do nothing
break;
}
}