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:
+1
-1
@@ -4,7 +4,7 @@
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
set(PROJECT_VER "1.1.2")
|
||||
set(PROJECT_VER "1.1.9")
|
||||
|
||||
# Add this line to disable the specific warning
|
||||
add_compile_options(-Wno-missing-field-initializers)
|
||||
|
||||
+9
-3
@@ -13,8 +13,6 @@ set(SOURCES "audio_codecs/audio_codec.cc"
|
||||
"display/ssd1306_display.cc"
|
||||
"boards/lilygo-t-circle-s3/esp_lcd_gc9d01n.c"
|
||||
"protocols/protocol.cc"
|
||||
"protocols/mqtt_protocol.cc"
|
||||
"protocols/websocket_protocol.cc"
|
||||
"iot/thing.cc"
|
||||
"iot/thing_manager.cc"
|
||||
"system_info.cc"
|
||||
@@ -107,6 +105,12 @@ endif()
|
||||
file(GLOB BOARD_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/boards/${BOARD_TYPE}/*.cc)
|
||||
list(APPEND SOURCES ${BOARD_SOURCES})
|
||||
|
||||
if(CONFIG_CONNECTION_TYPE_MQTT_UDP)
|
||||
list(APPEND SOURCES "protocols/mqtt_protocol.cc")
|
||||
elseif(CONFIG_CONNECTION_TYPE_WEBSOCKET)
|
||||
list(APPEND SOURCES "protocols/websocket_protocol.cc")
|
||||
endif()
|
||||
|
||||
if(CONFIG_USE_AUDIO_PROCESSING)
|
||||
list(APPEND SOURCES "audio_processing/audio_processor.cc" "audio_processing/wake_word_detect.cc")
|
||||
endif()
|
||||
@@ -119,8 +123,10 @@ if(CONFIG_IDF_TARGET_ESP32)
|
||||
"audio_codecs/es8388_audio_codec.cc")
|
||||
endif()
|
||||
|
||||
file(GLOB ASSETS ${CMAKE_CURRENT_SOURCE_DIR}/assets/zh/*.p3)
|
||||
|
||||
idf_component_register(SRCS ${SOURCES}
|
||||
EMBED_FILES "assets/err_reg.p3" "assets/err_pin.p3" "assets/wificonfig.p3" "assets/upgrade.p3"
|
||||
EMBED_FILES ${ASSETS}
|
||||
INCLUDE_DIRS ${INCLUDE_DIRS}
|
||||
WHOLE_ARCHIVE
|
||||
)
|
||||
|
||||
+78
-42
@@ -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();
|
||||
}
|
||||
+4
-2
@@ -35,6 +35,7 @@ enum DeviceState {
|
||||
kDeviceStateListening,
|
||||
kDeviceStateSpeaking,
|
||||
kDeviceStateUpgrading,
|
||||
kDeviceStateActivating,
|
||||
kDeviceStateFatalError
|
||||
};
|
||||
|
||||
@@ -55,12 +56,13 @@ public:
|
||||
bool IsVoiceDetected() const { return voice_detected_; }
|
||||
void Schedule(std::function<void()> callback);
|
||||
void SetDeviceState(DeviceState state);
|
||||
void Alert(const std::string& title, const std::string& message);
|
||||
void Alert(const std::string& status, const std::string& message, const std::string& emotion = "", const std::string& sound = "");
|
||||
void AbortSpeaking(AbortReason reason);
|
||||
void ToggleChatState();
|
||||
void StartListening();
|
||||
void StopListening();
|
||||
void UpdateIotStates();
|
||||
void Reboot();
|
||||
|
||||
private:
|
||||
Application();
|
||||
@@ -100,7 +102,7 @@ private:
|
||||
void ResetDecoder();
|
||||
void SetDecodeSampleRate(int sample_rate);
|
||||
void CheckNewVersion();
|
||||
void DisplayActivationCode();
|
||||
void ShowActivationCode();
|
||||
|
||||
void PlayLocalFile(const char* data, size_t size);
|
||||
};
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,39 @@
|
||||
#ifndef BINARY_H
|
||||
#define BINARY_H
|
||||
|
||||
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");
|
||||
extern const char p3_activation_start[] asm("_binary_activation_p3_start");
|
||||
extern const char p3_activation_end[] asm("_binary_activation_p3_end");
|
||||
extern const char p3_welcome_start[] asm("_binary_welcome_p3_start");
|
||||
extern const char p3_welcome_end[] asm("_binary_welcome_p3_end");
|
||||
|
||||
extern const char p3_0_start[] asm("_binary_0_p3_start");
|
||||
extern const char p3_0_end[] asm("_binary_0_p3_end");
|
||||
extern const char p3_1_start[] asm("_binary_1_p3_start");
|
||||
extern const char p3_1_end[] asm("_binary_1_p3_end");
|
||||
extern const char p3_2_start[] asm("_binary_2_p3_start");
|
||||
extern const char p3_2_end[] asm("_binary_2_p3_end");
|
||||
extern const char p3_3_start[] asm("_binary_3_p3_start");
|
||||
extern const char p3_3_end[] asm("_binary_3_p3_end");
|
||||
extern const char p3_4_start[] asm("_binary_4_p3_start");
|
||||
extern const char p3_4_end[] asm("_binary_4_p3_end");
|
||||
extern const char p3_5_start[] asm("_binary_5_p3_start");
|
||||
extern const char p3_5_end[] asm("_binary_5_p3_end");
|
||||
extern const char p3_6_start[] asm("_binary_6_p3_start");
|
||||
extern const char p3_6_end[] asm("_binary_6_p3_end");
|
||||
extern const char p3_7_start[] asm("_binary_7_p3_start");
|
||||
extern const char p3_7_end[] asm("_binary_7_p3_end");
|
||||
extern const char p3_8_start[] asm("_binary_8_p3_start");
|
||||
extern const char p3_8_end[] asm("_binary_8_p3_end");
|
||||
extern const char p3_9_start[] asm("_binary_9_p3_start");
|
||||
extern const char p3_9_end[] asm("_binary_9_p3_end");
|
||||
|
||||
|
||||
#endif
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -20,7 +20,10 @@ BackgroundTask::~BackgroundTask() {
|
||||
void BackgroundTask::Schedule(std::function<void()> callback) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
if (active_tasks_ >= 30) {
|
||||
ESP_LOGW(TAG, "active_tasks_ == %u", active_tasks_.load());
|
||||
int free_sram = heap_caps_get_free_size(MALLOC_CAP_INTERNAL);
|
||||
if (free_sram < 10000) {
|
||||
ESP_LOGW(TAG, "active_tasks_ == %u, free_sram == %u", active_tasks_.load(), free_sram);
|
||||
}
|
||||
}
|
||||
active_tasks_++;
|
||||
main_tasks_.emplace_back([this, cb = std::move(callback)]() {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "application.h"
|
||||
#include "display.h"
|
||||
#include "font_awesome_symbols.h"
|
||||
#include "assets/zh/binary.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
#include <esp_timer.h>
|
||||
@@ -24,7 +25,7 @@ std::string Ml307Board::GetBoardType() {
|
||||
|
||||
void Ml307Board::StartNetwork() {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->SetStatus("初始化模块");
|
||||
display->SetStatus("检测模组...");
|
||||
modem_.SetDebug(false);
|
||||
modem_.SetBaudRate(921600);
|
||||
|
||||
@@ -47,10 +48,10 @@ void Ml307Board::WaitForNetworkReady() {
|
||||
display->SetStatus("等待网络...");
|
||||
int result = modem_.WaitForNetworkReady();
|
||||
if (result == -1) {
|
||||
application.Alert("Error", "请插入SIM卡");
|
||||
application.Alert("PIN_ERROR", "请插入SIM卡", "sad", std::string(p3_err_pin_start, p3_err_pin_end - p3_err_pin_start));
|
||||
return;
|
||||
} else if (result == -2) {
|
||||
application.Alert("Error", "无法接入网络,请检查流量卡状态");
|
||||
application.Alert("REG_ERROR", "无法接入网络,请检查流量卡状态", "sad", std::string(p3_err_reg_start, p3_err_reg_end - p3_err_reg_start));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "system_info.h"
|
||||
#include "font_awesome_symbols.h"
|
||||
#include "settings.h"
|
||||
#include "assets/zh/binary.h"
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
@@ -37,7 +38,6 @@ std::string WifiBoard::GetBoardType() {
|
||||
|
||||
void WifiBoard::EnterWifiConfigMode() {
|
||||
auto& application = Application::GetInstance();
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
application.SetDeviceState(kDeviceStateWifiConfiguring);
|
||||
|
||||
auto& wifi_ap = WifiConfigurationAp::GetInstance();
|
||||
@@ -50,10 +50,8 @@ void WifiBoard::EnterWifiConfigMode() {
|
||||
hint += ",然后打开浏览器访问 ";
|
||||
hint += wifi_ap.GetWebServerUrl();
|
||||
|
||||
display->SetStatus(hint);
|
||||
|
||||
// 播报配置 WiFi 的提示
|
||||
application.Alert("Info", "进入配网模式");
|
||||
application.Alert("配网模式", hint, "", std::string(p3_wificonfig_start, p3_wificonfig_end - p3_wificonfig_start));
|
||||
|
||||
// Wait forever until reset after configuration
|
||||
while (true) {
|
||||
@@ -83,11 +81,11 @@ void WifiBoard::StartNetwork() {
|
||||
auto& wifi_station = WifiStation::GetInstance();
|
||||
wifi_station.OnScanBegin([this]() {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->ShowNotification("正在扫描 WiFi 网络", 30000);
|
||||
display->ShowNotification("扫描 WiFi...", 30000);
|
||||
});
|
||||
wifi_station.OnConnect([this](const std::string& ssid) {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
display->ShowNotification(std::string("正在连接 ") + ssid, 30000);
|
||||
display->ShowNotification(std::string("连接 ") + ssid + "...", 30000);
|
||||
});
|
||||
wifi_station.OnConnected([this](const std::string& ssid) {
|
||||
auto display = Board::GetInstance().GetDisplay();
|
||||
|
||||
@@ -210,6 +210,11 @@ void Display::SetIcon(const char* icon) {
|
||||
}
|
||||
|
||||
void Display::SetChatMessage(const std::string &role, const std::string &content) {
|
||||
DisplayLockGuard lock(this);
|
||||
if (chat_message_label_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
lv_label_set_text(chat_message_label_, content.c_str());
|
||||
}
|
||||
|
||||
void Display::SetBacklight(uint8_t brightness) {
|
||||
|
||||
@@ -42,6 +42,7 @@ protected:
|
||||
lv_obj_t *notification_label_ = nullptr;
|
||||
lv_obj_t *mute_label_ = nullptr;
|
||||
lv_obj_t *battery_label_ = nullptr;
|
||||
lv_obj_t* chat_message_label_ = nullptr;
|
||||
const char* battery_icon_ = nullptr;
|
||||
const char* network_icon_ = nullptr;
|
||||
bool muted_ = false;
|
||||
|
||||
@@ -274,14 +274,6 @@ void LcdDisplay::SetupUI() {
|
||||
lv_obj_set_style_text_font(battery_label_, fonts_.icon_font, 0);
|
||||
}
|
||||
|
||||
void LcdDisplay::SetChatMessage(const std::string &role, const std::string &content) {
|
||||
DisplayLockGuard lock(this);
|
||||
if (chat_message_label_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
lv_label_set_text(chat_message_label_, content.c_str());
|
||||
}
|
||||
|
||||
void LcdDisplay::SetEmotion(const std::string &emotion) {
|
||||
struct Emotion {
|
||||
const char* icon;
|
||||
|
||||
@@ -26,7 +26,6 @@ protected:
|
||||
lv_obj_t* content_ = nullptr;
|
||||
lv_obj_t* container_ = nullptr;
|
||||
lv_obj_t* side_bar_ = nullptr;
|
||||
lv_obj_t* chat_message_label_ = nullptr;
|
||||
|
||||
DisplayFonts fonts_;
|
||||
|
||||
@@ -46,7 +45,6 @@ public:
|
||||
DisplayFonts fonts);
|
||||
~LcdDisplay();
|
||||
|
||||
virtual void SetChatMessage(const std::string &role, const std::string &content) override;
|
||||
virtual void SetEmotion(const std::string &emotion) override;
|
||||
virtual void SetIcon(const char* icon) override;
|
||||
virtual void SetBacklight(uint8_t brightness) override;
|
||||
|
||||
@@ -132,6 +132,20 @@ void Ssd1306Display::Unlock() {
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
void Ssd1306Display::SetChatMessage(const std::string &role, const std::string &content) {
|
||||
DisplayLockGuard lock(this);
|
||||
if (content_right_ == nullptr) {
|
||||
lv_label_set_text(chat_message_label_, content.c_str());
|
||||
} else {
|
||||
if (content.empty()) {
|
||||
lv_obj_add_flag(content_right_, LV_OBJ_FLAG_HIDDEN);
|
||||
} else {
|
||||
lv_label_set_text(chat_message_label_, content.c_str());
|
||||
lv_obj_clear_flag(content_right_, LV_OBJ_FLAG_HIDDEN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Ssd1306Display::SetupUI_128x64() {
|
||||
DisplayLockGuard lock(this);
|
||||
|
||||
@@ -149,20 +163,46 @@ void Ssd1306Display::SetupUI_128x64() {
|
||||
|
||||
/* Status bar */
|
||||
status_bar_ = lv_obj_create(container_);
|
||||
lv_obj_set_size(status_bar_, LV_HOR_RES, 18);
|
||||
lv_obj_set_size(status_bar_, LV_HOR_RES, 16);
|
||||
lv_obj_set_style_border_width(status_bar_, 0, 0);
|
||||
lv_obj_set_style_pad_all(status_bar_, 0, 0);
|
||||
lv_obj_set_style_radius(status_bar_, 0, 0);
|
||||
|
||||
/* Content */
|
||||
content_ = lv_obj_create(container_);
|
||||
lv_obj_set_scrollbar_mode(content_, LV_SCROLLBAR_MODE_OFF);
|
||||
lv_obj_set_style_radius(status_bar_, 0, 0);
|
||||
lv_obj_set_style_radius(content_, 0, 0);
|
||||
lv_obj_set_style_pad_all(content_, 0, 0);
|
||||
lv_obj_set_width(content_, LV_HOR_RES);
|
||||
lv_obj_set_flex_grow(content_, 1);
|
||||
lv_obj_set_flex_flow(content_, LV_FLEX_FLOW_ROW);
|
||||
lv_obj_set_style_flex_main_place(content_, LV_FLEX_ALIGN_CENTER, 0);
|
||||
|
||||
emotion_label_ = lv_label_create(content_);
|
||||
// 创建左侧固定宽度的容器
|
||||
content_left_ = lv_obj_create(content_);
|
||||
lv_obj_set_size(content_left_, 32, LV_SIZE_CONTENT); // 固定宽度32像素
|
||||
lv_obj_set_style_pad_all(content_left_, 0, 0);
|
||||
lv_obj_set_style_border_width(content_left_, 0, 0);
|
||||
|
||||
emotion_label_ = lv_label_create(content_left_);
|
||||
lv_obj_set_style_text_font(emotion_label_, &font_awesome_30_1, 0);
|
||||
lv_label_set_text(emotion_label_, FONT_AWESOME_AI_CHIP);
|
||||
lv_obj_center(emotion_label_);
|
||||
lv_obj_set_style_pad_top(emotion_label_, 8, 0);
|
||||
|
||||
// 创建右侧可扩展的容器
|
||||
content_right_ = lv_obj_create(content_);
|
||||
lv_obj_set_size(content_right_, LV_SIZE_CONTENT, LV_SIZE_CONTENT);
|
||||
lv_obj_set_style_pad_all(content_right_, 0, 0);
|
||||
lv_obj_set_style_border_width(content_right_, 0, 0);
|
||||
lv_obj_set_flex_grow(content_right_, 1);
|
||||
lv_obj_add_flag(content_right_, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
chat_message_label_ = lv_label_create(content_right_);
|
||||
lv_label_set_text(chat_message_label_, "");
|
||||
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_WRAP);
|
||||
lv_obj_set_width(chat_message_label_, lv_pct(100));
|
||||
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_LEFT, 0);
|
||||
|
||||
/* Status bar */
|
||||
lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW);
|
||||
@@ -250,17 +290,19 @@ void Ssd1306Display::SetupUI_128x32() {
|
||||
lv_label_set_text(battery_label_, "");
|
||||
lv_obj_set_style_text_font(battery_label_, icon_font_, 0);
|
||||
|
||||
status_label_ = lv_label_create(side_bar_);
|
||||
lv_obj_set_flex_grow(status_label_, 1);
|
||||
lv_obj_set_width(status_label_, width_ - 32);
|
||||
lv_label_set_long_mode(status_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
|
||||
status_label_ = lv_label_create(status_bar_);
|
||||
lv_obj_set_style_pad_left(status_label_, 2, 0);
|
||||
lv_label_set_text(status_label_, "正在初始化");
|
||||
|
||||
notification_label_ = lv_label_create(side_bar_);
|
||||
lv_obj_set_flex_grow(notification_label_, 1);
|
||||
lv_obj_set_width(notification_label_, width_ - 32);
|
||||
lv_label_set_long_mode(notification_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
|
||||
notification_label_ = lv_label_create(status_bar_);
|
||||
lv_label_set_text(notification_label_, "通知");
|
||||
lv_obj_set_style_pad_left(notification_label_, 2, 0);
|
||||
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
|
||||
|
||||
chat_message_label_ = lv_label_create(side_bar_);
|
||||
lv_obj_set_flex_grow(chat_message_label_, 1);
|
||||
lv_obj_set_width(chat_message_label_, width_ - 32);
|
||||
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
|
||||
lv_label_set_text(chat_message_label_, "");
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ private:
|
||||
|
||||
lv_obj_t* status_bar_ = nullptr;
|
||||
lv_obj_t* content_ = nullptr;
|
||||
lv_obj_t* content_left_ = nullptr;
|
||||
lv_obj_t* content_right_ = nullptr;
|
||||
lv_obj_t* container_ = nullptr;
|
||||
lv_obj_t* side_bar_ = nullptr;
|
||||
|
||||
@@ -29,6 +31,8 @@ public:
|
||||
Ssd1306Display(void* i2c_master_handle, int width, int height, bool mirror_x, bool mirror_y,
|
||||
const lv_font_t* text_font, const lv_font_t* icon_font);
|
||||
~Ssd1306Display();
|
||||
|
||||
virtual void SetChatMessage(const std::string &role, const std::string &content);
|
||||
};
|
||||
|
||||
#endif // SSD1306_DISPLAY_H
|
||||
|
||||
@@ -211,8 +211,13 @@ void CircularStrip::OnStateChanged() {
|
||||
Blink(color, 100);
|
||||
break;
|
||||
}
|
||||
case kDeviceStateActivating: {
|
||||
StripColor color = { LOW_BRIGHTNESS, DEFAULT_BRIGHTNESS, LOW_BRIGHTNESS };
|
||||
Blink(color, 500);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid led strip event: %d", device_state);
|
||||
ESP_LOGW(TAG, "Unknown led strip event: %d", device_state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,8 +151,12 @@ void SingleLed::OnStateChanged() {
|
||||
SetColor(0, DEFAULT_BRIGHTNESS, 0);
|
||||
StartContinuousBlink(100);
|
||||
break;
|
||||
case kDeviceStateActivating:
|
||||
SetColor(0, DEFAULT_BRIGHTNESS, 0);
|
||||
StartContinuousBlink(500);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "Invalid led strip event: %d", device_state);
|
||||
ESP_LOGW(TAG, "Unknown led strip event: %d", device_state);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ bool Ota::CheckVersion() {
|
||||
return false;
|
||||
}
|
||||
|
||||
has_activation_code_ = false;
|
||||
cJSON *activation = cJSON_GetObjectItem(root, "activation");
|
||||
if (activation != NULL) {
|
||||
cJSON* message = cJSON_GetObjectItem(activation, "message");
|
||||
@@ -84,6 +85,7 @@ bool Ota::CheckVersion() {
|
||||
has_activation_code_ = true;
|
||||
}
|
||||
|
||||
has_mqtt_config_ = false;
|
||||
cJSON *mqtt = cJSON_GetObjectItem(root, "mqtt");
|
||||
if (mqtt != NULL) {
|
||||
Settings settings("mqtt", true);
|
||||
|
||||
@@ -10,8 +10,6 @@
|
||||
|
||||
#define TAG "WS"
|
||||
|
||||
#ifdef CONFIG_CONNECTION_TYPE_WEBSOCKET
|
||||
|
||||
WebsocketProtocol::WebsocketProtocol() {
|
||||
event_group_handle_ = xEventGroupCreate();
|
||||
}
|
||||
@@ -61,7 +59,7 @@ bool WebsocketProtocol::OpenAudioChannel() {
|
||||
websocket_->SetHeader("Authorization", token.c_str());
|
||||
websocket_->SetHeader("Protocol-Version", "1");
|
||||
websocket_->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
|
||||
websocket_->SetHeader("X-Uuid", Board::GetInstance().GetUuid().c_str());
|
||||
websocket_->SetHeader("Client-Id", Board::GetInstance().GetUuid().c_str());
|
||||
|
||||
websocket_->OnData([this](const char* data, size_t len, bool binary) {
|
||||
if (binary) {
|
||||
@@ -147,5 +145,3 @@ void WebsocketProtocol::ParseServerHello(const cJSON* root) {
|
||||
|
||||
xEventGroupSetBits(event_group_handle_, WEBSOCKET_PROTOCOL_SERVER_HELLO_EVENT);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user