forked from xiaozhi/xiaozhi-esp32
Enhance device activation and OTA update process
- Add support for device activation with audio feedback - Refactor OTA update flow to include activation code handling - Update asset management for localized sound resources - Improve error handling and device state management - Reorganize binary asset includes and CMake configuration
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
#include "websocket_protocol.h"
|
||||
#include "font_awesome_symbols.h"
|
||||
#include "iot/thing_manager.h"
|
||||
#include "assets/zh/binary.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <esp_log.h>
|
||||
@@ -17,14 +18,6 @@
|
||||
|
||||
#define TAG "Application"
|
||||
|
||||
extern const char p3_err_reg_start[] asm("_binary_err_reg_p3_start");
|
||||
extern const char p3_err_reg_end[] asm("_binary_err_reg_p3_end");
|
||||
extern const char p3_err_pin_start[] asm("_binary_err_pin_p3_start");
|
||||
extern const char p3_err_pin_end[] asm("_binary_err_pin_p3_end");
|
||||
extern const char p3_wificonfig_start[] asm("_binary_wificonfig_p3_start");
|
||||
extern const char p3_wificonfig_end[] asm("_binary_wificonfig_p3_end");
|
||||
extern const char p3_upgrade_start[] asm("_binary_upgrade_p3_start");
|
||||
extern const char p3_upgrade_end[] asm("_binary_upgrade_p3_end");
|
||||
|
||||
static const char* const STATE_STRINGS[] = {
|
||||
"unknown",
|
||||
@@ -35,6 +28,7 @@ static const char* const STATE_STRINGS[] = {
|
||||
"listening",
|
||||
"speaking",
|
||||
"upgrading",
|
||||
"activating",
|
||||
"fatal_error",
|
||||
"invalid_state"
|
||||
};
|
||||
@@ -42,9 +36,6 @@ static const char* const STATE_STRINGS[] = {
|
||||
Application::Application() {
|
||||
event_group_ = xEventGroupCreate();
|
||||
background_task_ = new BackgroundTask(4096 * 8);
|
||||
|
||||
ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
|
||||
ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
|
||||
}
|
||||
|
||||
Application::~Application() {
|
||||
@@ -61,13 +52,9 @@ void Application::CheckNewVersion() {
|
||||
ota_.SetPostData(board.GetJson());
|
||||
|
||||
while (true) {
|
||||
bool success = ota_.CheckVersion();
|
||||
if (ota_.HasActivationCode()) {
|
||||
DisplayActivationCode();
|
||||
}
|
||||
if (success) {
|
||||
if (ota_.CheckVersion()) {
|
||||
if (ota_.HasNewVersion()) {
|
||||
Alert("Info", "正在升级固件");
|
||||
Alert("OTA 升级", "正在升级系统", "happy", std::string(p3_upgrade_start, p3_upgrade_end - p3_upgrade_start));
|
||||
// Wait for the chat state to be idle
|
||||
do {
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
@@ -78,7 +65,7 @@ void Application::CheckNewVersion() {
|
||||
SetDeviceState(kDeviceStateUpgrading);
|
||||
|
||||
display->SetIcon(FONT_AWESOME_DOWNLOAD);
|
||||
display->SetStatus("新版本 " + ota_.GetFirmwareVersion());
|
||||
display->SetChatMessage("system", "新版本 " + ota_.GetFirmwareVersion());
|
||||
|
||||
board.SetPowerSaveMode(false);
|
||||
#if CONFIG_USE_AUDIO_PROCESSING
|
||||
@@ -100,20 +87,29 @@ void Application::CheckNewVersion() {
|
||||
ota_.StartUpgrade([display](int progress, size_t speed) {
|
||||
char buffer[64];
|
||||
snprintf(buffer, sizeof(buffer), "%d%% %zuKB/s", progress, speed / 1024);
|
||||
display->SetStatus(buffer);
|
||||
display->SetChatMessage("system", buffer);
|
||||
});
|
||||
|
||||
// If upgrade success, the device will reboot and never reach here
|
||||
display->SetStatus("更新失败");
|
||||
ESP_LOGI(TAG, "Firmware upgrade failed...");
|
||||
vTaskDelay(pdMS_TO_TICKS(3000));
|
||||
esp_restart();
|
||||
Reboot();
|
||||
});
|
||||
} else {
|
||||
ota_.MarkCurrentVersionValid();
|
||||
display->ShowNotification("版本 " + ota_.GetCurrentVersion());
|
||||
|
||||
// Check if the activation code is valid
|
||||
if (ota_.HasActivationCode()) {
|
||||
SetDeviceState(kDeviceStateActivating);
|
||||
ShowActivationCode();
|
||||
} else {
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
display->SetChatMessage("system", "");
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Check again in 60 seconds
|
||||
@@ -121,31 +117,53 @@ void Application::CheckNewVersion() {
|
||||
}
|
||||
}
|
||||
|
||||
void Application::DisplayActivationCode() {
|
||||
ESP_LOGW(TAG, "Activation Message: %s", ota_.GetActivationMessage().c_str());
|
||||
ESP_LOGW(TAG, "Activation Code: %s", ota_.GetActivationCode().c_str());
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->ShowNotification(ota_.GetActivationMessage(), 30000);
|
||||
void Application::ShowActivationCode() {
|
||||
auto& message = ota_.GetActivationMessage();
|
||||
auto& code = ota_.GetActivationCode();
|
||||
|
||||
struct digit_sound {
|
||||
char digit;
|
||||
const char* sound_data_start;
|
||||
const char* sound_data_end;
|
||||
};
|
||||
digit_sound digit_sounds[] = {
|
||||
{'0', p3_0_start, p3_0_end},
|
||||
{'1', p3_1_start, p3_1_end},
|
||||
{'2', p3_2_start, p3_2_end},
|
||||
{'3', p3_3_start, p3_3_end},
|
||||
{'4', p3_4_start, p3_4_end},
|
||||
{'5', p3_5_start, p3_5_end},
|
||||
{'6', p3_6_start, p3_6_end},
|
||||
{'7', p3_7_start, p3_7_end},
|
||||
{'8', p3_8_start, p3_8_end},
|
||||
{'9', p3_9_start, p3_9_end},
|
||||
};
|
||||
std::string sound = std::string(p3_activation_start, p3_activation_end - p3_activation_start);
|
||||
for (const auto& digit : code) {
|
||||
auto it = std::find_if(digit_sounds, digit_sounds + sizeof(digit_sounds) / sizeof(digit_sound),
|
||||
[digit](const digit_sound& ds) { return ds.digit == digit; });
|
||||
if (it != digit_sounds + sizeof(digit_sounds) / sizeof(digit_sound)) {
|
||||
sound += std::string(it->sound_data_start, it->sound_data_end - it->sound_data_start);
|
||||
}
|
||||
}
|
||||
Alert("激活设备", message, "happy", sound);
|
||||
}
|
||||
|
||||
void Application::Alert(const std::string& title, const std::string& message) {
|
||||
ESP_LOGW(TAG, "Alert: %s, %s", title.c_str(), message.c_str());
|
||||
void Application::Alert(const std::string& status, const std::string& message, const std::string& emotion, const std::string& sound) {
|
||||
ESP_LOGW(TAG, "Alert %s: %s [%s]", status.c_str(), message.c_str(), emotion.c_str());
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->ShowNotification(message);
|
||||
|
||||
if (message == "进入配网模式") {
|
||||
PlayLocalFile(p3_wificonfig_start, p3_wificonfig_end - p3_wificonfig_start);
|
||||
} else if (message == "正在升级固件") {
|
||||
PlayLocalFile(p3_upgrade_start, p3_upgrade_end - p3_upgrade_start);
|
||||
} else if (message == "请插入SIM卡") {
|
||||
PlayLocalFile(p3_err_pin_start, p3_err_pin_end - p3_err_pin_start);
|
||||
} else if (message == "无法接入网络,请检查流量卡状态") {
|
||||
PlayLocalFile(p3_err_reg_start, p3_err_reg_end - p3_err_reg_start);
|
||||
display->SetStatus(status);
|
||||
display->SetEmotion(emotion);
|
||||
display->SetChatMessage("system", message);
|
||||
if (!sound.empty()) {
|
||||
PlayLocalFile(sound.data(), sound.size());
|
||||
}
|
||||
}
|
||||
|
||||
void Application::PlayLocalFile(const char* data, size_t size) {
|
||||
ESP_LOGI(TAG, "PlayLocalFile: %zu bytes", size);
|
||||
auto codec = Board::GetInstance().GetAudioCodec();
|
||||
codec->EnableOutput(true);
|
||||
SetDecodeSampleRate(16000);
|
||||
for (const char* p = data; p < data + size; ) {
|
||||
auto p3 = (BinaryProtocol3*)p;
|
||||
@@ -164,6 +182,11 @@ void Application::PlayLocalFile(const char* data, size_t size) {
|
||||
|
||||
void Application::ToggleChatState() {
|
||||
Schedule([this]() {
|
||||
if (device_state_ == kDeviceStateActivating) {
|
||||
Reboot();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!protocol_) {
|
||||
ESP_LOGE(TAG, "Protocol not initialized");
|
||||
return;
|
||||
@@ -172,7 +195,7 @@ void Application::ToggleChatState() {
|
||||
if (device_state_ == kDeviceStateIdle) {
|
||||
SetDeviceState(kDeviceStateConnecting);
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
Alert("Error", "Failed to open audio channel");
|
||||
Alert("ERROR", "无法建立音频通道");
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
return;
|
||||
}
|
||||
@@ -190,6 +213,11 @@ void Application::ToggleChatState() {
|
||||
|
||||
void Application::StartListening() {
|
||||
Schedule([this]() {
|
||||
if (device_state_ == kDeviceStateActivating) {
|
||||
Reboot();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!protocol_) {
|
||||
ESP_LOGE(TAG, "Protocol not initialized");
|
||||
return;
|
||||
@@ -201,7 +229,7 @@ void Application::StartListening() {
|
||||
SetDeviceState(kDeviceStateConnecting);
|
||||
if (!protocol_->OpenAudioChannel()) {
|
||||
SetDeviceState(kDeviceStateIdle);
|
||||
Alert("Error", "Failed to open audio channel");
|
||||
Alert("ERROR", "无法建立音频通道");
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -275,14 +303,14 @@ void Application::Start() {
|
||||
board.StartNetwork();
|
||||
|
||||
// Initialize the protocol
|
||||
display->SetStatus("初始化协议");
|
||||
display->SetStatus("加载协议...");
|
||||
#ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET
|
||||
protocol_ = std::make_unique<WebsocketProtocol>();
|
||||
#else
|
||||
protocol_ = std::make_unique<MqttProtocol>();
|
||||
#endif
|
||||
protocol_->OnNetworkError([this](const std::string& message) {
|
||||
Alert("Error", std::move(message));
|
||||
Alert("ERROR", message);
|
||||
});
|
||||
protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
@@ -371,6 +399,9 @@ void Application::Start() {
|
||||
});
|
||||
|
||||
// Check for new firmware version or get the MQTT broker address
|
||||
ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
|
||||
ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
|
||||
ota_.SetHeader("Client-Id", board.GetUuid());
|
||||
xTaskCreate([](void* arg) {
|
||||
Application* app = (Application*)arg;
|
||||
app->CheckNewVersion();
|
||||
@@ -664,3 +695,8 @@ void Application::UpdateIotStates() {
|
||||
protocol_->SendIotStates(states);
|
||||
}
|
||||
}
|
||||
|
||||
void Application::Reboot() {
|
||||
ESP_LOGI(TAG, "Rebooting...");
|
||||
esp_restart();
|
||||
}
|
||||
Reference in New Issue
Block a user