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:
Terrence
2025-02-16 06:59:19 +08:00
parent 14a89cae33
commit 3a71c1e895
39 changed files with 220 additions and 86 deletions

View File

@@ -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();
}