Add English system sounds

This commit is contained in:
Terrence
2025-02-19 23:54:59 +08:00
parent d5594d01a3
commit 939564b175
52 changed files with 204 additions and 199 deletions

View File

@@ -115,6 +115,18 @@ if(CONFIG_USE_AUDIO_PROCESSING)
list(APPEND SOURCES "audio_processing/audio_processor.cc" "audio_processing/wake_word_detect.cc")
endif()
# 根据Kconfig选择语言目录
if(CONFIG_LANGUAGE_ZH_CN)
set(LANG_DIR "zh-CN")
elseif(CONFIG_LANGUAGE_EN_US)
set(LANG_DIR "en-US")
endif()
# 定义生成路径
set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/language.json")
set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h")
file(GLOB ASSETS ${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/*.p3)
# 如果目标芯片是 ESP32则排除特定文件
if(CONFIG_IDF_TARGET_ESP32)
# 排除 "audio_codecs/box_audio_codec.cc" 和 "audio_codecs/cores3_audio_codec.cc"
@@ -123,8 +135,6 @@ 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}
INCLUDE_DIRS ${INCLUDE_DIRS}
@@ -136,17 +146,6 @@ target_compile_definitions(${COMPONENT_LIB}
PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\"
)
# 根据Kconfig选择语言目录
if(CONFIG_LANGUAGE_ZH)
set(LANG_DIR "zh")
elseif(CONFIG_LANGUAGE_EN)
set(LANG_DIR "en-US")
endif()
# 定义生成路径
set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/language.json")
set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h")
# 添加生成规则
add_custom_command(
OUTPUT ${LANG_HEADER}

View File

@@ -9,13 +9,13 @@ config OTA_VERSION_URL
choice
prompt "语言选择"
default LANGUAGE_ZH
default LANGUAGE_ZH_CN
help
Select device display language
config LANGUAGE_ZH
config LANGUAGE_ZH_CN
bool "Chinese"
config LANGUAGE_EN
config LANGUAGE_EN_US
bool "English"
endchoice

View File

@@ -1,7 +1,6 @@
#include "application.h"
#include "board.h"
#include "display.h"
#include "ssd1306_display.h"
#include "system_info.h"
#include "ml307_ssl_transport.h"
#include "audio_codec.h"
@@ -9,7 +8,6 @@
#include "websocket_protocol.h"
#include "font_awesome_symbols.h"
#include "iot/thing_manager.h"
#include "assets/zh/binary.h"
#include "assets/lang_config.h"
#include <cstring>
@@ -60,17 +58,17 @@ void Application::CheckNewVersion() {
if (!ota_.CheckVersion()) {
retry_count++;
if (retry_count >= MAX_RETRY) {
ESP_LOGE(TAG, "版本检查失败次数过多,退出检查");
ESP_LOGE(TAG, "Too many retries, exit version check");
return;
}
ESP_LOGW(TAG, "版本检查失败,%d秒后重试 (%d/%d)", 60, retry_count, MAX_RETRY);
ESP_LOGW(TAG, "Check new version failed, retry in %d seconds (%d/%d)", 60, retry_count, MAX_RETRY);
vTaskDelay(pdMS_TO_TICKS(60000));
continue;
}
retry_count = 0;
if (ota_.HasNewVersion()) {
Alert("OTA 升级", "正在升级系统", "happy", std::string_view(p3_upgrade_start, p3_upgrade_end - p3_upgrade_start));
Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "happy", Lang::Sounds::P3_UPGRADE);
// Wait for the chat state to be idle
do {
vTaskDelay(pdMS_TO_TICKS(3000));
@@ -81,7 +79,8 @@ void Application::CheckNewVersion() {
SetDeviceState(kDeviceStateUpgrading);
display->SetIcon(FONT_AWESOME_DOWNLOAD);
display->SetChatMessage("system", "新版本 " + ota_.GetFirmwareVersion());
std::string message = std::string(Lang::Strings::NEW_VERSION) + ota_.GetFirmwareVersion();
display->SetChatMessage("system", message.c_str());
auto& board = Board::GetInstance();
board.SetPowerSaveMode(false);
@@ -108,7 +107,7 @@ void Application::CheckNewVersion() {
});
// If upgrade success, the device will reboot and never reach here
display->SetStatus("更新失败");
display->SetStatus(Lang::Strings::UPGRADE_FAILED);
ESP_LOGI(TAG, "Firmware upgrade failed...");
vTaskDelay(pdMS_TO_TICKS(3000));
Reboot();
@@ -119,7 +118,8 @@ void Application::CheckNewVersion() {
// No new version, mark the current version as valid
ota_.MarkCurrentVersionValid();
display->ShowNotification(Lang::Strings::VERSION + " " + ota_.GetCurrentVersion());
std::string message = std::string(Lang::Strings::VERSION) + ota_.GetCurrentVersion();
display->ShowNotification(message.c_str());
if (ota_.HasActivationCode()) {
// Activation code is valid
@@ -144,24 +144,23 @@ void Application::ShowActivationCode() {
struct digit_sound {
char digit;
const char* data;
size_t size;
const std::string_view& sound;
};
static const std::array<digit_sound, 10> digit_sounds{{
digit_sound{'0', p3_0_start, size_t(p3_0_end - p3_0_start)},
digit_sound{'1', p3_1_start, size_t(p3_1_end - p3_1_start)},
digit_sound{'2', p3_2_start, size_t(p3_2_end - p3_2_start)},
digit_sound{'3', p3_3_start, size_t(p3_3_end - p3_3_start)},
digit_sound{'4', p3_4_start, size_t(p3_4_end - p3_4_start)},
digit_sound{'5', p3_5_start, size_t(p3_5_end - p3_5_start)},
digit_sound{'6', p3_6_start, size_t(p3_6_end - p3_6_start)},
digit_sound{'7', p3_7_start, size_t(p3_7_end - p3_7_start)},
digit_sound{'8', p3_8_start, size_t(p3_8_end - p3_8_start)},
digit_sound{'9', p3_9_start, size_t(p3_9_end - p3_9_start)}
digit_sound{'0', Lang::Sounds::P3_0},
digit_sound{'1', Lang::Sounds::P3_1},
digit_sound{'2', Lang::Sounds::P3_2},
digit_sound{'3', Lang::Sounds::P3_3},
digit_sound{'4', Lang::Sounds::P3_4},
digit_sound{'5', Lang::Sounds::P3_5},
digit_sound{'6', Lang::Sounds::P3_6},
digit_sound{'7', Lang::Sounds::P3_7},
digit_sound{'8', Lang::Sounds::P3_8},
digit_sound{'9', Lang::Sounds::P3_9}
}};
// This sentence uses 9KB of SRAM, so we need to wait for it to finish
Alert("激活设备", message, "happy", std::string_view(p3_activation_start, p3_activation_end - p3_activation_start));
Alert(Lang::Strings::ACTIVATION, message.c_str(), "happy", Lang::Sounds::P3_ACTIVATION);
vTaskDelay(pdMS_TO_TICKS(1000));
background_task_->WaitForCompletion();
@@ -169,13 +168,13 @@ void Application::ShowActivationCode() {
auto it = std::find_if(digit_sounds.begin(), digit_sounds.end(),
[digit](const digit_sound& ds) { return ds.digit == digit; });
if (it != digit_sounds.end()) {
PlayLocalFile(it->data, it->size);
PlayLocalFile(it->sound.data(), it->sound.size());
}
}
}
void Application::Alert(const std::string& status, const std::string& message, const std::string& emotion, const std::string_view& sound) {
ESP_LOGW(TAG, "Alert %s: %s [%s]", status.c_str(), message.c_str(), emotion.c_str());
void Application::Alert(const char* status, const char* message, const char* emotion, const std::string_view& sound) {
ESP_LOGW(TAG, "Alert %s: %s [%s]", status, message, emotion);
auto display = Board::GetInstance().GetDisplay();
display->SetStatus(status);
display->SetEmotion(emotion);
@@ -220,7 +219,7 @@ void Application::ToggleChatState() {
if (device_state_ == kDeviceStateIdle) {
SetDeviceState(kDeviceStateConnecting);
if (!protocol_->OpenAudioChannel()) {
Alert("ERROR", Lang::Strings::UNABLE_TO_ESTABLISH_AUDIO_CHANNEL, "sad");
Alert(Lang::Strings::ERROR, Lang::Strings::UNABLE_TO_ESTABLISH_AUDIO_CHANNEL, "sad");
SetDeviceState(kDeviceStateIdle);
return;
}
@@ -254,7 +253,7 @@ void Application::StartListening() {
SetDeviceState(kDeviceStateConnecting);
if (!protocol_->OpenAudioChannel()) {
SetDeviceState(kDeviceStateIdle);
Alert("ERROR", Lang::Strings::UNABLE_TO_ESTABLISH_AUDIO_CHANNEL, "sad");
Alert(Lang::Strings::ERROR, Lang::Strings::UNABLE_TO_ESTABLISH_AUDIO_CHANNEL, "sad");
return;
}
}
@@ -328,14 +327,14 @@ void Application::Start() {
board.StartNetwork();
// Initialize the protocol
display->SetStatus(Lang::Strings::LOADING_PROTOCOL + "...");
display->SetStatus(Lang::Strings::LOADING_PROTOCOL);
#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", message, "sad");
Alert(Lang::Strings::ERROR, message.c_str(), "sad");
});
protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) {
std::lock_guard<std::mutex> lock(mutex_);
@@ -346,11 +345,11 @@ void Application::Start() {
protocol_->OnAudioChannelOpened([this, codec, &board]() {
board.SetPowerSaveMode(false);
if (protocol_->server_sample_rate() != codec->output_sample_rate()) {
ESP_LOGW(TAG, "服务器的音频采样率 %d 与设备输出的采样率 %d 不一致,重采样后可能会失真",
ESP_LOGW(TAG, "Server sample rate %d does not match device output sample rate %d, resampling may cause distortion",
protocol_->server_sample_rate(), codec->output_sample_rate());
}
SetDecodeSampleRate(protocol_->server_sample_rate());
// 物联网设备描述符
// IoT device descriptors
last_iot_states_.clear();
auto& thing_manager = iot::ThingManager::GetInstance();
protocol_->SendIotDescriptors(thing_manager.GetDescriptorsJson());
@@ -392,7 +391,7 @@ void Application::Start() {
if (text != NULL) {
ESP_LOGI(TAG, "<< %s", text->valuestring);
Schedule([this, display, message = std::string(text->valuestring)]() {
display->SetChatMessage("assistant", message);
display->SetChatMessage("assistant", message.c_str());
});
}
}
@@ -401,14 +400,14 @@ void Application::Start() {
if (text != NULL) {
ESP_LOGI(TAG, ">> %s", text->valuestring);
Schedule([this, display, message = std::string(text->valuestring)]() {
display->SetChatMessage("user", message);
display->SetChatMessage("user", message.c_str());
});
}
} else if (strcmp(type->valuestring, "llm") == 0) {
auto emotion = cJSON_GetObjectItem(root, "emotion");
if (emotion != NULL) {
Schedule([this, display, emotion_str = std::string(emotion->valuestring)]() {
display->SetEmotion(emotion_str);
display->SetEmotion(emotion_str.c_str());
});
}
} else if (strcmp(type->valuestring, "iot") == 0) {
@@ -428,6 +427,8 @@ void Application::Start() {
ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
ota_.SetHeader("Client-Id", board.GetUuid());
ota_.SetHeader("X-Language", Lang::CODE);
xTaskCreate([](void* arg) {
Application* app = (Application*)arg;
app->CheckNewVersion();
@@ -664,18 +665,18 @@ void Application::SetDeviceState(DeviceState state) {
switch (state) {
case kDeviceStateUnknown:
case kDeviceStateIdle:
display->SetStatus(Lang::Strings::STANDING_BY);
display->SetStatus(Lang::Strings::STANDBY);
display->SetEmotion("neutral");
#ifdef CONFIG_USE_AUDIO_PROCESSING
audio_processor_.Stop();
#endif
break;
case kDeviceStateConnecting:
display->SetStatus(Lang::Strings::CONNECTING+"...");
display->SetStatus(Lang::Strings::CONNECTING);
display->SetChatMessage("system", "");
break;
case kDeviceStateListening:
display->SetStatus(Lang::Strings::LISTENING+"...");
display->SetStatus(Lang::Strings::LISTENING);
display->SetEmotion("neutral");
ResetDecoder();
opus_encoder_->ResetState();
@@ -685,7 +686,7 @@ void Application::SetDeviceState(DeviceState state) {
UpdateIotStates();
break;
case kDeviceStateSpeaking:
display->SetStatus(Lang::Strings::SPEAKING+"...");
display->SetStatus(Lang::Strings::SPEAKING);
ResetDecoder();
codec->EnableOutput(true);
#if CONFIG_USE_AUDIO_PROCESSING

View File

@@ -56,7 +56,7 @@ public:
bool IsVoiceDetected() const { return voice_detected_; }
void Schedule(std::function<void()> callback);
void SetDeviceState(DeviceState state);
void Alert(const std::string& status, const std::string& message, const std::string& emotion = "", const std::string_view& sound = "");
void Alert(const char* status, const char* message, const char* emotion = "", const std::string_view& sound = "");
void AbortSpeaking(AbortReason reason);
void ToggleChatState();
void StartListening();

BIN
main/assets/en-US/0.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/1.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/2.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/3.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/4.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/5.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/6.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/7.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/8.p3 Normal file

Binary file not shown.

BIN
main/assets/en-US/9.p3 Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -1,34 +1,44 @@
{
"language": {
"type" :"en"
"type": "en"
},
"strings": {
"VERSION": "Version",
"LOADING_PROTOCOL":"Loading Protocol",
"INITIALIZING":"Initializing",
"NOTICE":"Notice",
"VERSION": "Ver ",
"LOADING_PROTOCOL": "Loading Protocol...",
"INITIALIZING": "Initializing...",
"DETECTING_MODULE": "Detecting module...",
"REGISTERING_NETWORK": "Waiting for network...",
"STANDING_BY":"Standing By",
"CONNECT":"Connect",
"CONNECTING":"Connecting",
"CONNECTION_SUCCESSFUL":"Connection Successful",
"ERROR": "Error",
"PIN_ERROR": "Please insert SIM card",
"REG_ERROR": "Unable to access network, please check SIM card status",
"LISTENING":"Listening",
"SPEAKING":"Speaking",
"STANDBY": "Standby",
"CONNECT_TO": "Connect to ",
"CONNECTING": "Connecting...",
"CONNECTION_SUCCESSFUL": "Connection Successful",
"CONNECTED_TO": "Connected to ",
"UNABLE_TO_CONNECT_TO_SERVICE":"Unable to connect to service",
"WAITING_FOR_RESPONSE_TIMEOUT":"Waiting for response timeout",
"SENDING_FAILED_PLEASE_CHECK_THE_NETWORK":"Sending failed, please check the network",
"LISTENING": "Listening...",
"SPEAKING": "Speaking...",
"UNABLE_TO_CONNECT_TO_SERVICE": "Unable to connect to service",
"WAITING_FOR_RESPONSE_TIMEOUT": "Waiting for response timeout",
"SENDING_FAILED_PLEASE_CHECK_THE_NETWORK": "Sending failed, please check the network",
"CONNECT_MOBILE_PHONE_TO_HOTSPOT":"Connect mobile phone to hotspot",
"ACCESS_VIA_BROWSER":"Access via browser",
"WIFI_CONFIGURATION_MODE":"Wi-Fi Configuration Mode",
"SCANNING_WIFI":"Scanning Wi-Fi",
"CONNECT_TO_HOTSPOT": "Hotspot: ",
"ACCESS_VIA_BROWSER": " Config URL: ",
"WIFI_CONFIG_MODE": "Wi-Fi Configuration Mode",
"ENTERING_WIFI_CONFIG_MODE": "Entering Wi-Fi configuration mode...",
"SCANNING_WIFI": "Scanning Wi-Fi...",
"UNABLE_TO_ESTABLISH_AUDIO_CHANNEL": "Unable to establish audio channel",
"TEST":"Test"
"TEST": "Test",
"NEW_VERSION": "New version ",
"OTA_UPGRADE": "OTA Upgrade",
"UPGRADING": "System is upgrading...",
"UPGRADE_FAILED": "Upgrade failed",
"ACTIVATION": "Activation"
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,42 @@
{
"language": {
"type" :"zh-CN"
},
"strings": {
"ERROR":"错误",
"VERSION": "版本 ",
"LOADING_PROTOCOL":"加载协议...",
"INITIALIZING":"正在初始化...",
"PIN_ERROR":"请插入 SIM 卡",
"REG_ERROR":"无法接入网络,请检查流量卡状态",
"DETECTING_MODULE":"检测模组...",
"REGISTERING_NETWORK":"等待网络...",
"STANDBY":"待命",
"CONNECT_TO":"连接 ",
"CONNECTING":"连接中...",
"CONNECTED_TO":"已连接 ",
"LISTENING":"聆听中...",
"SPEAKING":"说话中...",
"UNABLE_TO_CONNECT_TO_SERVICE":"无法连接服务",
"WAITING_FOR_RESPONSE_TIMEOUT":"等待响应超时",
"SENDING_FAILED_PLEASE_CHECK_THE_NETWORK":"发送失败,请检查网络",
"CONNECT_TO_HOTSPOT":"手机连接热点 ",
"ACCESS_VIA_BROWSER":",浏览器访问 ",
"WIFI_CONFIG_MODE":"配网模式",
"ENTERING_WIFI_CONFIG_MODE":"进入配网模式...",
"SCANNING_WIFI":"扫描 Wi-Fi...",
"UNABLE_TO_ESTABLISH_AUDIO_CHANNEL": "无法建立音频通道",
"TEST":"测试",
"NEW_VERSION": "新版本 ",
"OTA_UPGRADE":"OTA 升级",
"UPGRADING":"正在升级系统...",
"UPGRADE_FAILED":"升级失败",
"ACTIVATION":"激活设备"
}
}

View File

@@ -1,39 +0,0 @@
#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

View File

@@ -1,34 +0,0 @@
{
"language": {
"type" :"zh"
},
"strings": {
"VERSION": "版本",
"LOADING_PROTOCOL":"加载协议",
"INITIALIZING":"正在初始化",
"NOTICE":"通知",
"STANDING_BY":"待命",
"CONNECT":"连接",
"CONNECTING":"连接中",
"CONNECTION_SUCCESSFUL":"连接成功",
"LISTENING":"聆听中",
"SPEAKING":"说话中",
"UNABLE_TO_CONNECT_TO_SERVICE":"无法连接服务",
"WAITING_FOR_RESPONSE_TIMEOUT":"等待响应超时",
"SENDING_FAILED_PLEASE_CHECK_THE_NETWORK":"发送失败,请检查网络",
"CONNECT_MOBILE_PHONE_TO_HOTSPOT":"手机连接热点",
"ACCESS_VIA_BROWSER":"浏览器访问",
"WIFI_CONFIGURATION_MODE":"配网模式",
"SCANNING_WIFI":"扫描 WIFI",
"UNABLE_TO_ESTABLISH_AUDIO_CHANNEL": "无法建立音频通道",
"TEST":"测试"
}
}

Binary file not shown.

View File

@@ -1,12 +1,12 @@
#include "wifi_board.h"
#include "audio_codecs/es8311_audio_codec.h"
#include "display/lcd_display.h"
#include "display/no_display.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "i2c_device.h"
#include "iot/thing_manager.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
@@ -110,9 +110,9 @@ private:
InitializeSpi();
InitializeGc9107Display();
InitializeButtons();
display_->SetStatus("错误");
display_->SetStatus(Lang::Strings::ERROR);
display_->SetEmotion("sad");
display_->SetChatMessage("system", "Echo Base\n未连接");
display_->SetChatMessage("system", "Echo Base\nnot connected");
while (1) {
ESP_LOGE(TAG, "Atomic Echo Base is disconnected");

View File

@@ -1,12 +1,12 @@
#include "wifi_board.h"
#include "audio_codecs/es8311_audio_codec.h"
#include "display/lcd_display.h"
#include "display/no_display.h"
#include "application.h"
#include "button.h"
#include "config.h"
#include "i2c_device.h"
#include "iot/thing_manager.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <driver/i2c_master.h>
@@ -158,9 +158,10 @@ private:
InitializeSpi();
InitializeGc9107Display();
InitializeButtons();
display_->SetStatus("错误");
display_->SetStatus(Lang::Strings::ERROR);
display_->SetEmotion("sad");
display_->SetChatMessage("system", "Echo Base\n未连接");
display_->SetChatMessage("system", "Echo Base\nnot connected");
while (1) {
ESP_LOGE(TAG, "Atomic Echo Base is disconnected");

View File

@@ -2,6 +2,7 @@
#include "system_info.h"
#include "settings.h"
#include "display/no_display.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <esp_ota_ops.h>
@@ -99,6 +100,7 @@ std::string Board::GetJson() {
*/
std::string json = "{";
json += "\"version\":2,";
json += "\"language\":\"" + std::string(Lang::CODE) + "\",";
json += "\"flash_size\":" + std::to_string(SystemInfo::GetFlashSize()) + ",";
json += "\"minimum_free_heap_size\":" + std::to_string(SystemInfo::GetMinimumFreeHeapSize()) + ",";
json += "\"mac_address\":\"" + SystemInfo::GetMacAddress() + "\",";

View File

@@ -3,7 +3,7 @@
#include "application.h"
#include "display.h"
#include "font_awesome_symbols.h"
#include "assets/zh/binary.h"
#include "assets/lang_config.h"
#include <esp_log.h>
#include <esp_timer.h>
@@ -25,7 +25,7 @@ std::string Ml307Board::GetBoardType() {
void Ml307Board::StartNetwork() {
auto display = Board::GetInstance().GetDisplay();
display->SetStatus("检测模组...");
display->SetStatus(Lang::Strings::DETECTING_MODULE);
modem_.SetDebug(false);
modem_.SetBaudRate(921600);
@@ -45,13 +45,13 @@ void Ml307Board::StartNetwork() {
void Ml307Board::WaitForNetworkReady() {
auto& application = Application::GetInstance();
auto display = Board::GetInstance().GetDisplay();
display->SetStatus("等待网络...");
display->SetStatus(Lang::Strings::REGISTERING_NETWORK);
int result = modem_.WaitForNetworkReady();
if (result == -1) {
application.Alert("PIN_ERROR", "请插入SIM卡", "sad", std::string_view(p3_err_pin_start, p3_err_pin_end - p3_err_pin_start));
application.Alert(Lang::Strings::ERROR, Lang::Strings::PIN_ERROR, "sad", Lang::Sounds::P3_ERR_PIN);
return;
} else if (result == -2) {
application.Alert("REG_ERROR", "无法接入网络,请检查流量卡状态", "sad", std::string_view(p3_err_reg_start, p3_err_reg_end - p3_err_reg_start));
application.Alert(Lang::Strings::ERROR, Lang::Strings::REG_ERROR, "sad", Lang::Sounds::P3_ERR_REG);
return;
}

View File

@@ -5,7 +5,7 @@
#include "system_info.h"
#include "font_awesome_symbols.h"
#include "settings.h"
#include "assets/zh/binary.h"
#include "assets/lang_config.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
@@ -20,7 +20,6 @@
#include <wifi_station.h>
#include <wifi_configuration_ap.h>
#include <ssid_manager.h>
#include "assets/lang_config.h"
static const char *TAG = "WifiBoard";
@@ -46,14 +45,14 @@ void WifiBoard::EnterWifiConfigMode() {
wifi_ap.Start();
// 显示 WiFi 配置 AP 的 SSID 和 Web 服务器 URL
std::string hint = Lang::Strings::CONNECT_MOBILE_PHONE_TO_HOTSPOT + " ";
std::string hint = Lang::Strings::CONNECT_TO_HOTSPOT;
hint += wifi_ap.GetSsid();
hint += "\n"+ Lang::Strings::ACCESS_VIA_BROWSER + " ";
hint += Lang::Strings::ACCESS_VIA_BROWSER;
hint += wifi_ap.GetWebServerUrl();
hint += "\n\n";
// 播报配置 WiFi 的提示
application.Alert(Lang::Strings::WIFI_CONFIGURATION_MODE, hint, "", std::string(p3_wificonfig_start, p3_wificonfig_end - p3_wificonfig_start));
application.Alert(Lang::Strings::WIFI_CONFIG_MODE, hint.c_str(), "", Lang::Sounds::P3_WIFICONFIG);
// Wait forever until reset after configuration
while (true) {
@@ -87,11 +86,16 @@ void WifiBoard::StartNetwork() {
});
wifi_station.OnConnect([this](const std::string& ssid) {
auto display = Board::GetInstance().GetDisplay();
display->ShowNotification(std::string(Lang::Strings::CONNECT + " ") + ssid + "...", 30000);
std::string notification = Lang::Strings::CONNECT_TO;
notification += ssid;
notification += "...";
display->ShowNotification(notification.c_str(), 30000);
});
wifi_station.OnConnected([this](const std::string& ssid) {
auto display = Board::GetInstance().GetDisplay();
display->ShowNotification(std::string(Lang::Strings::CONNECTION_SUCCESSFUL) + ssid);
std::string notification = Lang::Strings::CONNECTED_TO;
notification += ssid;
display->ShowNotification(notification.c_str(), 30000);
});
wifi_station.Start();
@@ -167,12 +171,12 @@ void WifiBoard::SetPowerSaveMode(bool enabled) {
}
void WifiBoard::ResetWifiConfiguration() {
// Reset the wifi station
// Set a flag and reboot the device to enter the network configuration mode
{
Settings settings("wifi", true);
settings.SetInt("force_ap", 1);
}
GetDisplay()->ShowNotification("Enter the network configuration mode...");
GetDisplay()->ShowNotification(Lang::Strings::ENTERING_WIFI_CONFIG_MODE);
vTaskDelay(pdMS_TO_TICKS(1000));
// Reboot the device
esp_restart();

View File

@@ -67,22 +67,26 @@ Display::~Display() {
}
}
void Display::SetStatus(const std::string &status) {
void Display::SetStatus(const char* status) {
DisplayLockGuard lock(this);
if (status_label_ == nullptr) {
return;
}
lv_label_set_text(status_label_, status.c_str());
lv_label_set_text(status_label_, status);
lv_obj_clear_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
}
void Display::ShowNotification(const std::string &notification, int duration_ms) {
ShowNotification(notification.c_str(), duration_ms);
}
void Display::ShowNotification(const char* notification, int duration_ms) {
DisplayLockGuard lock(this);
if (notification_label_ == nullptr) {
return;
}
lv_label_set_text(notification_label_, notification.c_str());
lv_label_set_text(notification_label_, notification);
lv_obj_clear_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
lv_obj_add_flag(status_label_, LV_OBJ_FLAG_HIDDEN);
@@ -154,7 +158,7 @@ void Display::Update() {
}
void Display::SetEmotion(const std::string &emotion) {
void Display::SetEmotion(const char* emotion) {
struct Emotion {
const char* icon;
const char* text;
@@ -185,8 +189,9 @@ void Display::SetEmotion(const std::string &emotion) {
};
// 查找匹配的表情
std::string_view emotion_view(emotion);
auto it = std::find_if(emotions.begin(), emotions.end(),
[&emotion](const Emotion& e) { return e.text == emotion; });
[&emotion_view](const Emotion& e) { return e.text == emotion_view; });
DisplayLockGuard lock(this);
if (emotion_label_ == nullptr) {
@@ -209,12 +214,12 @@ void Display::SetIcon(const char* icon) {
lv_label_set_text(emotion_label_, icon);
}
void Display::SetChatMessage(const std::string &role, const std::string &content) {
void Display::SetChatMessage(const char* role, const char* content) {
DisplayLockGuard lock(this);
if (chat_message_label_ == nullptr) {
return;
}
lv_label_set_text(chat_message_label_, content.c_str());
lv_label_set_text(chat_message_label_, content);
}
void Display::SetBacklight(uint8_t brightness) {

View File

@@ -18,10 +18,11 @@ public:
Display();
virtual ~Display();
virtual void SetStatus(const std::string &status);
virtual void SetStatus(const char* status);
virtual void ShowNotification(const char* notification, int duration_ms = 3000);
virtual void ShowNotification(const std::string &notification, int duration_ms = 3000);
virtual void SetEmotion(const std::string &emotion);
virtual void SetChatMessage(const std::string &role, const std::string &content);
virtual void SetEmotion(const char* emotion);
virtual void SetChatMessage(const char* role, const char* content);
virtual void SetIcon(const char* icon);
virtual void SetBacklight(uint8_t brightness);

View File

@@ -257,15 +257,14 @@ void LcdDisplay::SetupUI() {
notification_label_ = lv_label_create(status_bar_);
lv_obj_set_flex_grow(notification_label_, 1);
lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text(notification_label_, (Lang::Strings::NOTICE).c_str());
lv_label_set_text(notification_label_, "");
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
status_label_ = lv_label_create(status_bar_);
lv_obj_set_flex_grow(status_label_, 1);
lv_label_set_long_mode(status_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_label_set_text(status_label_,(Lang::Strings::INITIALIZING + "...").c_str());
lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
mute_label_ = lv_label_create(status_bar_);
lv_label_set_text(mute_label_, "");
lv_obj_set_style_text_font(mute_label_, fonts_.icon_font, 0);
@@ -275,7 +274,7 @@ void LcdDisplay::SetupUI() {
lv_obj_set_style_text_font(battery_label_, fonts_.icon_font, 0);
}
void LcdDisplay::SetEmotion(const std::string &emotion) {
void LcdDisplay::SetEmotion(const char* emotion) {
struct Emotion {
const char* icon;
const char* text;
@@ -306,8 +305,9 @@ void LcdDisplay::SetEmotion(const std::string &emotion) {
};
// 查找匹配的表情
std::string_view emotion_view(emotion);
auto it = std::find_if(emotions.begin(), emotions.end(),
[&emotion](const Emotion& e) { return e.text == emotion; });
[&emotion_view](const Emotion& e) { return e.text == emotion_view; });
DisplayLockGuard lock(this);
if (emotion_label_ == nullptr) {

View File

@@ -45,7 +45,7 @@ public:
DisplayFonts fonts);
~LcdDisplay();
virtual void SetEmotion(const std::string &emotion) override;
virtual void SetEmotion(const char* emotion) override;
virtual void SetIcon(const char* icon) override;
virtual void SetBacklight(uint8_t brightness) override;
};

View File

@@ -221,12 +221,12 @@ void Ssd1306Display::SetupUI_128x64() {
notification_label_ = lv_label_create(status_bar_);
lv_obj_set_flex_grow(notification_label_, 1);
lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text(notification_label_, (Lang::Strings::NOTICE).c_str());
lv_label_set_text(notification_label_, "");
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
status_label_ = lv_label_create(status_bar_);
lv_obj_set_flex_grow(status_label_, 1);
lv_label_set_text(status_label_,(Lang::Strings::INITIALIZING + "...").c_str());
lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0);
mute_label_ = lv_label_create(status_bar_);
@@ -296,10 +296,10 @@ void Ssd1306Display::SetupUI_128x32() {
status_label_ = lv_label_create(status_bar_);
lv_obj_set_style_pad_left(status_label_, 2, 0);
lv_label_set_text(status_label_,(Lang::Strings::INITIALIZING + "...").c_str());
lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
notification_label_ = lv_label_create(status_bar_);
lv_label_set_text(notification_label_, (Lang::Strings::NOTICE).c_str());
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);

View File

@@ -6,18 +6,20 @@ import os
HEADER_TEMPLATE = """// Auto-generated language config
#pragma once
#include <string>
#include <string_view>
namespace Lang {{
// 语言元数据
constexpr std::string_view CODE_VIEW = "{lang_code}";
const std::string CODE = std::string(CODE_VIEW);
constexpr const char* CODE = "{lang_code}";
// 字符串资源
namespace Strings {{
{strings_view}
{strings_string}
{strings}
}}
// 音效资源
namespace Sounds {{
{sounds}
}}
}}
"""
@@ -33,18 +35,29 @@ def generate_header(input_path, output_path):
lang_code = data['language']['type']
# 生成字符串常量
strings_view = []
strings_string = []
strings = []
sounds = []
for key, value in data['strings'].items():
value = value.replace('"', '\\"')
strings_view.append(f' constexpr std::string_view {key.upper()}_VIEW = "{value}";')
strings_string.append(f' const std::string {key.upper()} = std::string({key.upper()}_VIEW);')
strings.append(f' constexpr const char* {key.upper()} = "{value}";')
# 生成音效常量
for file in os.listdir(os.path.dirname(input_path)):
if file.endswith('.p3'):
base_name = os.path.splitext(file)[0]
sounds.append(f'''
extern const char p3_{base_name}_start[] asm("_binary_{base_name}_p3_start");
extern const char p3_{base_name}_end[] asm("_binary_{base_name}_p3_end");
static const std::string_view P3_{base_name.upper()} {{
static_cast<const char*>(p3_{base_name}_start),
static_cast<size_t>(p3_{base_name}_end - p3_{base_name}_start)
}};''')
# 填充模板
content = HEADER_TEMPLATE.format(
lang_code=lang_code,
strings_view="\n".join(sorted(strings_view)),
strings_string="\n".join(sorted(strings_string))
strings="\n".join(sorted(strings)),
sounds="\n".join(sorted(sounds))
)
# 写入文件