forked from xiaozhi/xiaozhi-esp32
reconstruct led control
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user