1.3.1 Updates

- Add startup and network failure sound effects
- 12864 OLED scroll text
- Internalization of volume actions
This commit is contained in:
Terrence 2025-03-03 07:29:22 +08:00
parent 36d98ce1a4
commit 7945da0c84
30 changed files with 217 additions and 104 deletions

View File

@ -4,7 +4,7 @@
# CMakeLists in this exact order for cmake to work correctly # CMakeLists in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
set(PROJECT_VER "1.3.0") set(PROJECT_VER "1.3.1")
# Add this line to disable the specific warning # Add this line to disable the specific warning
add_compile_options(-Wno-missing-field-initializers) add_compile_options(-Wno-missing-field-initializers)

View File

@ -138,7 +138,8 @@ endif()
# #
set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/language.json") set(LANG_JSON "${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/language.json")
set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h") set(LANG_HEADER "${CMAKE_CURRENT_SOURCE_DIR}/assets/lang_config.h")
file(GLOB ASSETS ${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/*.p3) file(GLOB LANG_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/*.p3)
file(GLOB COMMON_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/common/*.p3)
# ESP32 # ESP32
if(CONFIG_IDF_TARGET_ESP32) if(CONFIG_IDF_TARGET_ESP32)
@ -149,14 +150,14 @@ if(CONFIG_IDF_TARGET_ESP32)
endif() endif()
idf_component_register(SRCS ${SOURCES} idf_component_register(SRCS ${SOURCES}
EMBED_FILES ${ASSETS} EMBED_FILES ${LANG_SOUNDS} ${COMMON_SOUNDS}
INCLUDE_DIRS ${INCLUDE_DIRS} INCLUDE_DIRS ${INCLUDE_DIRS}
WHOLE_ARCHIVE WHOLE_ARCHIVE
) )
# 使 target_compile_definitions BOARD_TYPE # 使 target_compile_definitions BOARD_TYPE, BOARD_NAME
target_compile_definitions(${COMPONENT_LIB} target_compile_definitions(${COMPONENT_LIB}
PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" BOARD_NAME=\"${BOARD_NAME}\"
) )
# #

View File

@ -153,7 +153,7 @@ void Application::CheckNewVersion() {
SetDeviceState(kDeviceStateIdle); SetDeviceState(kDeviceStateIdle);
display->SetChatMessage("system", ""); display->SetChatMessage("system", "");
PlaySound(Lang::Sounds::P3_SUCCESS);
// Exit the loop if upgrade or idle // Exit the loop if upgrade or idle
break; break;
} }
@ -189,7 +189,7 @@ void Application::ShowActivationCode() {
auto it = std::find_if(digit_sounds.begin(), digit_sounds.end(), auto it = std::find_if(digit_sounds.begin(), digit_sounds.end(),
[digit](const digit_sound& ds) { return ds.digit == digit; }); [digit](const digit_sound& ds) { return ds.digit == digit; });
if (it != digit_sounds.end()) { if (it != digit_sounds.end()) {
PlayLocalFile(it->sound.data(), it->sound.size()); PlaySound(it->sound);
} }
} }
} }
@ -201,15 +201,25 @@ void Application::Alert(const char* status, const char* message, const char* emo
display->SetEmotion(emotion); display->SetEmotion(emotion);
display->SetChatMessage("system", message); display->SetChatMessage("system", message);
if (!sound.empty()) { if (!sound.empty()) {
PlayLocalFile(sound.data(), sound.size()); PlaySound(sound);
} }
} }
void Application::PlayLocalFile(const char* data, size_t size) { void Application::DismissAlert() {
ESP_LOGI(TAG, "PlayLocalFile: %zu bytes", size); if (device_state_ == kDeviceStateIdle) {
auto display = Board::GetInstance().GetDisplay();
display->SetStatus(Lang::Strings::STANDBY);
display->SetEmotion("neutral");
display->SetChatMessage("system", "");
}
}
void Application::PlaySound(const std::string_view& sound) {
auto codec = Board::GetInstance().GetAudioCodec(); auto codec = Board::GetInstance().GetAudioCodec();
codec->EnableOutput(true); codec->EnableOutput(true);
SetDecodeSampleRate(16000); SetDecodeSampleRate(16000);
const char* data = sound.data();
size_t size = sound.size();
for (const char* p = data; p < data + size; ) { for (const char* p = data; p < data + size; ) {
auto p3 = (BinaryProtocol3*)p; auto p3 = (BinaryProtocol3*)p;
p += sizeof(BinaryProtocol3); p += sizeof(BinaryProtocol3);
@ -240,7 +250,6 @@ void Application::ToggleChatState() {
if (device_state_ == kDeviceStateIdle) { if (device_state_ == kDeviceStateIdle) {
SetDeviceState(kDeviceStateConnecting); SetDeviceState(kDeviceStateConnecting);
if (!protocol_->OpenAudioChannel()) { if (!protocol_->OpenAudioChannel()) {
SetDeviceState(kDeviceStateIdle);
return; return;
} }
@ -272,7 +281,6 @@ void Application::StartListening() {
if (!protocol_->IsAudioChannelOpened()) { if (!protocol_->IsAudioChannelOpened()) {
SetDeviceState(kDeviceStateConnecting); SetDeviceState(kDeviceStateConnecting);
if (!protocol_->OpenAudioChannel()) { if (!protocol_->OpenAudioChannel()) {
SetDeviceState(kDeviceStateIdle);
return; return;
} }
} }
@ -353,7 +361,8 @@ void Application::Start() {
protocol_ = std::make_unique<MqttProtocol>(); protocol_ = std::make_unique<MqttProtocol>();
#endif #endif
protocol_->OnNetworkError([this](const std::string& message) { protocol_->OnNetworkError([this](const std::string& message) {
Alert(Lang::Strings::ERROR, message.c_str(), "sad"); SetDeviceState(kDeviceStateIdle);
Alert(Lang::Strings::ERROR, message.c_str(), "sad", Lang::Sounds::P3_EXCLAMATION);
}); });
protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) { protocol_->OnIncomingAudio([this](std::vector<uint8_t>&& data) {
std::lock_guard<std::mutex> lock(mutex_); std::lock_guard<std::mutex> lock(mutex_);
@ -446,7 +455,7 @@ void Application::Start() {
ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL); ota_.SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str()); ota_.SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
ota_.SetHeader("Client-Id", board.GetUuid()); ota_.SetHeader("Client-Id", board.GetUuid());
ota_.SetHeader("X-Language", Lang::CODE); ota_.SetHeader("Accept-Language", Lang::CODE);
xTaskCreate([](void* arg) { xTaskCreate([](void* arg) {
Application* app = (Application*)arg; Application* app = (Application*)arg;
@ -489,8 +498,6 @@ void Application::Start() {
wake_word_detect_.EncodeWakeWordData(); wake_word_detect_.EncodeWakeWordData();
if (!protocol_->OpenAudioChannel()) { if (!protocol_->OpenAudioChannel()) {
ESP_LOGE(TAG, "Failed to open audio channel");
SetDeviceState(kDeviceStateIdle);
wake_word_detect_.StartDetection(); wake_word_detect_.StartDetection();
return; return;
} }
@ -535,15 +542,15 @@ void Application::OnClockTimer() {
// If we have synchronized server time, set the status to clock "HH:MM" if the device is idle // If we have synchronized server time, set the status to clock "HH:MM" if the device is idle
if (ota_.HasServerTime()) { if (ota_.HasServerTime()) {
Schedule([this]() { if (device_state_ == kDeviceStateIdle) {
if (device_state_ == kDeviceStateIdle) { Schedule([this]() {
// Set status to clock "HH:MM" // Set status to clock "HH:MM"
time_t now = time(NULL); time_t now = time(NULL);
char time_str[64]; char time_str[64];
strftime(time_str, sizeof(time_str), "%H:%M ", localtime(&now)); strftime(time_str, sizeof(time_str), "%H:%M ", localtime(&now));
Board::GetInstance().GetDisplay()->SetStatus(time_str); Board::GetInstance().GetDisplay()->SetStatus(time_str);
} });
}); }
} }
} }
} }
@ -721,6 +728,7 @@ void Application::SetDeviceState(DeviceState state) {
break; break;
case kDeviceStateConnecting: case kDeviceStateConnecting:
display->SetStatus(Lang::Strings::CONNECTING); display->SetStatus(Lang::Strings::CONNECTING);
display->SetEmotion("neutral");
display->SetChatMessage("system", ""); display->SetChatMessage("system", "");
break; break;
case kDeviceStateListening: case kDeviceStateListening:

View File

@ -58,6 +58,7 @@ public:
void Schedule(std::function<void()> callback); void Schedule(std::function<void()> callback);
void SetDeviceState(DeviceState state); void SetDeviceState(DeviceState state);
void Alert(const char* status, const char* message, const char* emotion = "", const std::string_view& sound = ""); void Alert(const char* status, const char* message, const char* emotion = "", const std::string_view& sound = "");
void DismissAlert();
void AbortSpeaking(AbortReason reason); void AbortSpeaking(AbortReason reason);
void ToggleChatState(); void ToggleChatState();
void StartListening(); void StartListening();
@ -65,6 +66,7 @@ public:
void UpdateIotStates(); void UpdateIotStates();
void Reboot(); void Reboot();
void WakeWordInvoke(const std::string& wake_word); void WakeWordInvoke(const std::string& wake_word);
void PlaySound(const std::string_view& sound);
private: private:
Application(); Application();
@ -107,7 +109,6 @@ private:
void CheckNewVersion(); void CheckNewVersion();
void ShowActivationCode(); void ShowActivationCode();
void OnClockTimer(); void OnClockTimer();
void PlayLocalFile(const char* data, size_t size);
}; };
#endif // _APPLICATION_H_ #endif // _APPLICATION_H_

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,15 +3,16 @@
"type": "en-US" "type": "en-US"
}, },
"strings": { "strings": {
"WARNING": "Warning",
"INFO": "Information",
"ERROR": "Error",
"VERSION": "Ver ", "VERSION": "Ver ",
"LOADING_PROTOCOL": "Loading Protocol...", "LOADING_PROTOCOL": "Loading Protocol...",
"INITIALIZING": "Initializing...", "INITIALIZING": "Initializing...",
"DETECTING_MODULE": "Detecting module...",
"REGISTERING_NETWORK": "Waiting for network...",
"ERROR": "Error",
"PIN_ERROR": "Please insert SIM card", "PIN_ERROR": "Please insert SIM card",
"REG_ERROR": "Unable to access network, please check SIM card status", "REG_ERROR": "Unable to access network, please check SIM card status",
"DETECTING_MODULE": "Detecting module...",
"REGISTERING_NETWORK": "Waiting for network...",
"STANDBY": "Standby", "STANDBY": "Standby",
"CONNECT_TO": "Connect to ", "CONNECT_TO": "Connect to ",
@ -37,6 +38,14 @@
"OTA_UPGRADE": "OTA Upgrade", "OTA_UPGRADE": "OTA Upgrade",
"UPGRADING": "System is upgrading...", "UPGRADING": "System is upgrading...",
"UPGRADE_FAILED": "Upgrade failed", "UPGRADE_FAILED": "Upgrade failed",
"ACTIVATION": "Activation" "ACTIVATION": "Activation",
"BATTERY_LOW": "Low battery",
"BATTERY_CHARGING": "Charging",
"BATTERY_FULL": "Battery full",
"VOLUME": "Volume ",
"MUTED": "Muted",
"MAX_VOLUME": "Max volume"
} }
} }

View File

@ -3,6 +3,8 @@
"type" :"zh-CN" "type" :"zh-CN"
}, },
"strings": { "strings": {
"WARNING":"警告",
"INFO":"信息",
"ERROR":"错误", "ERROR":"错误",
"VERSION": "版本 ", "VERSION": "版本 ",
"LOADING_PROTOCOL":"加载协议...", "LOADING_PROTOCOL":"加载协议...",
@ -35,6 +37,14 @@
"OTA_UPGRADE":"OTA 升级", "OTA_UPGRADE":"OTA 升级",
"UPGRADING":"正在升级系统...", "UPGRADING":"正在升级系统...",
"UPGRADE_FAILED":"升级失败", "UPGRADE_FAILED":"升级失败",
"ACTIVATION":"激活设备" "ACTIVATION":"激活设备",
"BATTERY_LOW":"电量不足",
"BATTERY_CHARGING":"正在充电",
"BATTERY_FULL":"电量已满",
"VOLUME":"音量 ",
"MUTED":"已静音",
"MAX_VOLUME":"最大音量"
} }
} }

View File

@ -7,6 +7,7 @@
#include "config.h" #include "config.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h> #include <esp_log.h>
#include <driver/i2c_master.h> #include <driver/i2c_master.h>
@ -58,12 +59,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -73,12 +74,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -7,6 +7,7 @@
#include "config.h" #include "config.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <wifi_station.h> #include <wifi_station.h>
#include <esp_log.h> #include <esp_log.h>
@ -63,12 +64,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -78,12 +79,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -18,7 +18,7 @@ Board::Board() {
uuid_ = GenerateUuid(); uuid_ = GenerateUuid();
settings.SetString("uuid", uuid_); settings.SetString("uuid", uuid_);
} }
ESP_LOGI(TAG, "UUID: %s", uuid_.c_str()); ESP_LOGI(TAG, "UUID=%s SKU=%s", uuid_.c_str(), BOARD_NAME);
} }
std::string Board::GenerateUuid() { std::string Board::GenerateUuid() {

View File

@ -106,8 +106,8 @@ const char* Ml307Board::GetNetworkStateIcon() {
std::string Ml307Board::GetBoardJson() { std::string Ml307Board::GetBoardJson() {
// Set the board type for OTA // Set the board type for OTA
std::string board_type = BOARD_TYPE; std::string board_json = std::string("{\"type\":\"" BOARD_TYPE "\",");
std::string board_json = std::string("{\"type\":\"" + board_type + "\","); board_json += "\"name\":\"" BOARD_NAME "\",";
board_json += "\"revision\":\"" + modem_.GetModuleName() + "\","; board_json += "\"revision\":\"" + modem_.GetModuleName() + "\",";
board_json += "\"carrier\":\"" + modem_.GetCarrierName() + "\","; board_json += "\"carrier\":\"" + modem_.GetCarrierName() + "\",";
board_json += "\"csq\":\"" + std::to_string(modem_.GetCsq()) + "\","; board_json += "\"csq\":\"" + std::to_string(modem_.GetCsq()) + "\",";

View File

@ -154,8 +154,8 @@ const char* WifiBoard::GetNetworkStateIcon() {
std::string WifiBoard::GetBoardJson() { std::string WifiBoard::GetBoardJson() {
// Set the board type for OTA // Set the board type for OTA
auto& wifi_station = WifiStation::GetInstance(); auto& wifi_station = WifiStation::GetInstance();
std::string board_type = BOARD_TYPE; std::string board_json = std::string("{\"type\":\"" BOARD_TYPE "\",");
std::string board_json = std::string("{\"type\":\"" + board_type + "\","); board_json += "\"name\":\"" BOARD_NAME "\",";
if (!wifi_config_mode_) { if (!wifi_config_mode_) {
board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\","; board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\",";
board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ","; board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ",";

View File

@ -6,6 +6,7 @@
#include "config.h" #include "config.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h> #include <esp_log.h>
#include <esp_spiffs.h> #include <esp_spiffs.h>
@ -98,12 +99,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -113,12 +114,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -7,6 +7,7 @@
#include "axp2101.h" #include "axp2101.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h> #include <esp_log.h>
#include <driver/gpio.h> #include <driver/gpio.h>
@ -26,8 +27,8 @@ private:
Button boot_button_; Button boot_button_;
Button volume_up_button_; Button volume_up_button_;
Button volume_down_button_; Button volume_down_button_;
uint8_t _data_buffer[2];
esp_timer_handle_t power_save_timer_ = nullptr; esp_timer_handle_t power_save_timer_ = nullptr;
bool show_low_power_warning_ = false;
void InitializePowerSaveTimer() { void InitializePowerSaveTimer() {
esp_timer_create_args_t power_save_timer_args = { esp_timer_create_args_t power_save_timer_args = {
@ -48,14 +49,24 @@ private:
// 电池放电模式下,如果待机超过一定时间,则自动关机 // 电池放电模式下,如果待机超过一定时间,则自动关机
const int seconds_to_shutdown = 600; const int seconds_to_shutdown = 600;
static int seconds = 0; static int seconds = 0;
if (Application::GetInstance().GetDeviceState() != kDeviceStateIdle) { auto& app = Application::GetInstance();
if (app.GetDeviceState() != kDeviceStateIdle) {
seconds = 0; seconds = 0;
return; return;
} }
if (!axp2101_->IsDischarging()) { if (!axp2101_->IsDischarging()) {
seconds = 0; seconds = 0;
if (show_low_power_warning_) {
app.DismissAlert();
show_low_power_warning_ = false;
}
return; return;
} }
// 电量低于 10% 时,显示低电量警告
if (axp2101_->GetBatteryLevel() <= 10 && !show_low_power_warning_) {
app.Alert(Lang::Strings::WARNING, Lang::Strings::BATTERY_LOW, "sad", Lang::Sounds::P3_VIBRATION);
show_low_power_warning_ = true;
}
seconds++; seconds++;
if (seconds >= seconds_to_shutdown) { if (seconds >= seconds_to_shutdown) {
@ -124,12 +135,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -139,12 +150,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -6,13 +6,15 @@
#include "led/circular_strip.h" #include "led/circular_strip.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "config.h" #include "config.h"
#include "font_awesome_symbols.h"
#include "assets/lang_config.h"
#include <esp_lcd_panel_vendor.h> #include <esp_lcd_panel_vendor.h>
#include <wifi_station.h> #include <wifi_station.h>
#include <esp_log.h> #include <esp_log.h>
#include <driver/i2c_master.h> #include <driver/i2c_master.h>
#include <driver/spi_common.h> #include <driver/spi_common.h>
#include "esp_lcd_nv3023.h" #include <esp_lcd_nv3023.h>
#include "font_awesome_symbols.h"
#define TAG "magiclick_2p4" #define TAG "magiclick_2p4"
@ -100,12 +102,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
left_button_.OnLongPress([this]() { left_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
right_button_.OnClick([this]() { right_button_.OnClick([this]() {
@ -115,12 +117,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
right_button_.OnLongPress([this]() { right_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
} }

View File

@ -6,6 +6,9 @@
#include "led/circular_strip.h" #include "led/circular_strip.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "config.h" #include "config.h"
#include "assets/lang_config.h"
#include "font_awesome_symbols.h"
#include <esp_lcd_panel_vendor.h> #include <esp_lcd_panel_vendor.h>
#include <wifi_station.h> #include <wifi_station.h>
#include <esp_log.h> #include <esp_log.h>
@ -14,7 +17,6 @@
#include <esp_lcd_panel_io.h> #include <esp_lcd_panel_io.h>
#include <esp_lcd_panel_ops.h> #include <esp_lcd_panel_ops.h>
#include <esp_lcd_gc9a01.h> #include <esp_lcd_gc9a01.h>
#include "font_awesome_symbols.h"
#define TAG "magiclick_2p5" #define TAG "magiclick_2p5"
@ -136,12 +138,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
left_button_.OnLongPress([this]() { left_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
right_button_.OnClick([this]() { right_button_.OnClick([this]() {
@ -151,12 +153,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
right_button_.OnLongPress([this]() { right_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });

View File

@ -7,6 +7,7 @@
#include "axp2101.h" #include "axp2101.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h> #include <esp_log.h>
#include <esp_spiffs.h> #include <esp_spiffs.h>
@ -135,12 +136,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -150,12 +151,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -7,6 +7,7 @@
#include "config.h" #include "config.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h> #include <esp_log.h>
#include <driver/i2c_master.h> #include <driver/i2c_master.h>
@ -52,12 +53,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -67,12 +68,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -23,6 +23,7 @@
#include "application.h" #include "application.h"
#include "driver/rtc_io.h" #include "driver/rtc_io.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#define TAG "XINGZHI_Ssd1306Display" #define TAG "XINGZHI_Ssd1306Display"
@ -356,8 +357,17 @@ void XINGZHI_Ssd1306Display::SetupUI_128x64() {
chat_message_label_ = lv_label_create(content_right_); chat_message_label_ = lv_label_create(content_right_);
lv_label_set_text(chat_message_label_, ""); lv_label_set_text(chat_message_label_, "");
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(chat_message_label_, lv_pct(100));
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_LEFT, 0); lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_width(chat_message_label_, width_ - 32);
lv_obj_set_style_pad_top(chat_message_label_, 14, 0);
// 延迟一定的时间后开始滚动字幕
static lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_delay(&a, 1000);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_obj_set_style_anim(chat_message_label_, &a, LV_PART_MAIN);
lv_obj_set_style_anim_duration(chat_message_label_, lv_anim_speed_clamped(60, 300, 60000), LV_PART_MAIN);
/* Status bar */ /* Status bar */
lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW);
@ -372,12 +382,12 @@ void XINGZHI_Ssd1306Display::SetupUI_128x64() {
notification_label_ = lv_label_create(status_bar_); notification_label_ = lv_label_create(status_bar_);
lv_obj_set_flex_grow(notification_label_, 1); lv_obj_set_flex_grow(notification_label_, 1);
lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0); lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text(notification_label_, "通知"); lv_label_set_text(notification_label_, "");
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
status_label_ = lv_label_create(status_bar_); status_label_ = lv_label_create(status_bar_);
lv_obj_set_flex_grow(status_label_, 1); lv_obj_set_flex_grow(status_label_, 1);
lv_label_set_text(status_label_, "正在初始化"); lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0); lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0);
mute_label_ = lv_label_create(status_bar_); mute_label_ = lv_label_create(status_bar_);
@ -460,10 +470,10 @@ void XINGZHI_Ssd1306Display::SetupUI_128x32() {
status_label_ = lv_label_create(status_bar_); status_label_ = lv_label_create(status_bar_);
lv_obj_set_style_pad_left(status_label_, 2, 0); lv_obj_set_style_pad_left(status_label_, 2, 0);
lv_label_set_text(status_label_, "正在初始化"); lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
notification_label_ = lv_label_create(status_bar_); notification_label_ = lv_label_create(status_bar_);
lv_label_set_text(notification_label_, "通知"); lv_label_set_text(notification_label_, "");
lv_obj_set_style_pad_left(notification_label_, 2, 0); lv_obj_set_style_pad_left(notification_label_, 2, 0);
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);

View File

@ -7,6 +7,7 @@
#include "config.h" #include "config.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <wifi_station.h> #include <wifi_station.h>
#include <esp_log.h> #include <esp_log.h>
@ -56,12 +57,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -71,12 +72,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -23,6 +23,7 @@
#include "application.h" #include "application.h"
#include "driver/rtc_io.h" #include "driver/rtc_io.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#define TAG "XINGZHI_Ssd1306Display" #define TAG "XINGZHI_Ssd1306Display"
@ -356,8 +357,17 @@ void XINGZHI_Ssd1306Display::SetupUI_128x64() {
chat_message_label_ = lv_label_create(content_right_); chat_message_label_ = lv_label_create(content_right_);
lv_label_set_text(chat_message_label_, ""); lv_label_set_text(chat_message_label_, "");
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(chat_message_label_, lv_pct(100));
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_LEFT, 0); lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_width(chat_message_label_, width_ - 32);
lv_obj_set_style_pad_top(chat_message_label_, 14, 0);
// 延迟一定的时间后开始滚动字幕
static lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_delay(&a, 1000);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_obj_set_style_anim(chat_message_label_, &a, LV_PART_MAIN);
lv_obj_set_style_anim_duration(chat_message_label_, lv_anim_speed_clamped(60, 300, 60000), LV_PART_MAIN);
/* Status bar */ /* Status bar */
lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW);
@ -372,12 +382,12 @@ void XINGZHI_Ssd1306Display::SetupUI_128x64() {
notification_label_ = lv_label_create(status_bar_); notification_label_ = lv_label_create(status_bar_);
lv_obj_set_flex_grow(notification_label_, 1); lv_obj_set_flex_grow(notification_label_, 1);
lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0); lv_obj_set_style_text_align(notification_label_, LV_TEXT_ALIGN_CENTER, 0);
lv_label_set_text(notification_label_, "通知"); lv_label_set_text(notification_label_, "");
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);
status_label_ = lv_label_create(status_bar_); status_label_ = lv_label_create(status_bar_);
lv_obj_set_flex_grow(status_label_, 1); lv_obj_set_flex_grow(status_label_, 1);
lv_label_set_text(status_label_, "正在初始化"); lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0); lv_obj_set_style_text_align(status_label_, LV_TEXT_ALIGN_CENTER, 0);
mute_label_ = lv_label_create(status_bar_); mute_label_ = lv_label_create(status_bar_);
@ -460,10 +470,10 @@ void XINGZHI_Ssd1306Display::SetupUI_128x32() {
status_label_ = lv_label_create(status_bar_); status_label_ = lv_label_create(status_bar_);
lv_obj_set_style_pad_left(status_label_, 2, 0); lv_obj_set_style_pad_left(status_label_, 2, 0);
lv_label_set_text(status_label_, "正在初始化"); lv_label_set_text(status_label_, Lang::Strings::INITIALIZING);
notification_label_ = lv_label_create(status_bar_); notification_label_ = lv_label_create(status_bar_);
lv_label_set_text(notification_label_, "通知"); lv_label_set_text(notification_label_, "");
lv_obj_set_style_pad_left(notification_label_, 2, 0); lv_obj_set_style_pad_left(notification_label_, 2, 0);
lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); lv_obj_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN);

View File

@ -1,16 +1,17 @@
#include "ml307_board.h" #include "ml307_board.h"
#include "audio_codecs/no_audio_codec.h" #include "audio_codecs/no_audio_codec.h"
#include "xingzhi_lcd_display.h" #include "xingzhi_lcd_display.h"
#include "system_reset.h" #include "system_reset.h"
#include "application.h" #include "application.h"
#include "button.h" #include "button.h"
#include "config.h" #include "config.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h> #include <esp_log.h>
#include <esp_lcd_panel_vendor.h> #include <esp_lcd_panel_vendor.h>
#define TAG "XINGZHI_CUBE_1_54TFT_ML307" #define TAG "XINGZHI_CUBE_1_54TFT_ML307"
LV_FONT_DECLARE(font_puhui_16_4); LV_FONT_DECLARE(font_puhui_16_4);
@ -48,12 +49,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -63,12 +64,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -1,17 +1,18 @@
#include "wifi_board.h" #include "wifi_board.h"
#include "audio_codecs/no_audio_codec.h" #include "audio_codecs/no_audio_codec.h"
#include "xingzhi_lcd_display.h" #include "xingzhi_lcd_display.h"
#include "system_reset.h" #include "system_reset.h"
#include "application.h" #include "application.h"
#include "button.h" #include "button.h"
#include <wifi_station.h>
#include "config.h" #include "config.h"
#include "iot/thing_manager.h" #include "iot/thing_manager.h"
#include "led/single_led.h" #include "led/single_led.h"
#include "assets/lang_config.h"
#include <esp_log.h> #include <esp_log.h>
#include <esp_lcd_panel_vendor.h> #include <esp_lcd_panel_vendor.h>
#include <wifi_station.h>
#define TAG "XINGZHI_CUBE_1_54TFT_WIFI" #define TAG "XINGZHI_CUBE_1_54TFT_WIFI"
LV_FONT_DECLARE(font_puhui_16_4); LV_FONT_DECLARE(font_puhui_16_4);
@ -52,12 +53,12 @@ private:
volume = 100; volume = 100;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_up_button_.OnLongPress([this]() { volume_up_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(100); GetAudioCodec()->SetOutputVolume(100);
GetDisplay()->ShowNotification("最大音量"); GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME);
}); });
volume_down_button_.OnClick([this]() { volume_down_button_.OnClick([this]() {
@ -67,12 +68,12 @@ private:
volume = 0; volume = 0;
} }
codec->SetOutputVolume(volume); codec->SetOutputVolume(volume);
GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume));
}); });
volume_down_button_.OnLongPress([this]() { volume_down_button_.OnLongPress([this]() {
GetAudioCodec()->SetOutputVolume(0); GetAudioCodec()->SetOutputVolume(0);
GetDisplay()->ShowNotification("已静音"); GetDisplay()->ShowNotification(Lang::Strings::MUTED);
}); });
} }

View File

@ -204,9 +204,18 @@ void Ssd1306Display::SetupUI_128x64() {
chat_message_label_ = lv_label_create(content_right_); chat_message_label_ = lv_label_create(content_right_);
lv_label_set_text(chat_message_label_, ""); lv_label_set_text(chat_message_label_, "");
lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_WRAP); lv_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_obj_set_width(chat_message_label_, lv_pct(100));
lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_LEFT, 0); lv_obj_set_style_text_align(chat_message_label_, LV_TEXT_ALIGN_LEFT, 0);
lv_obj_set_width(chat_message_label_, width_ - 32);
lv_obj_set_style_pad_top(chat_message_label_, 14, 0);
// 延迟一定的时间后开始滚动字幕
static lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_delay(&a, 1000);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_obj_set_style_anim(chat_message_label_, &a, LV_PART_MAIN);
lv_obj_set_style_anim_duration(chat_message_label_, lv_anim_speed_clamped(60, 300, 60000), LV_PART_MAIN);
/* Status bar */ /* Status bar */
lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW); lv_obj_set_flex_flow(status_bar_, LV_FLEX_FLOW_ROW);
@ -308,5 +317,13 @@ void Ssd1306Display::SetupUI_128x32() {
lv_obj_set_width(chat_message_label_, width_ - 32); 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_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR);
lv_label_set_text(chat_message_label_, ""); lv_label_set_text(chat_message_label_, "");
// 延迟一定的时间后开始滚动字幕
static lv_anim_t a;
lv_anim_init(&a);
lv_anim_set_delay(&a, 1000);
lv_anim_set_repeat_count(&a, LV_ANIM_REPEAT_INFINITE);
lv_obj_set_style_anim(chat_message_label_, &a, LV_PART_MAIN);
lv_obj_set_style_anim_duration(chat_message_label_, lv_anim_speed_clamped(60, 300, 60000), LV_PART_MAIN);
} }

View File

@ -8,7 +8,7 @@ dependencies:
espressif/esp_io_expander_tca9554: "==2.0.0" espressif/esp_io_expander_tca9554: "==2.0.0"
espressif/esp_lcd_panel_io_additions: "^1.0.1" espressif/esp_lcd_panel_io_additions: "^1.0.1"
78/esp_lcd_nv3023: "~1.0.0" 78/esp_lcd_nv3023: "~1.0.0"
78/esp-wifi-connect: "~2.3.0" 78/esp-wifi-connect: "~2.3.1"
78/esp-opus-encoder: "~2.1.0" 78/esp-opus-encoder: "~2.1.0"
78/esp-ml307: "~1.7.2" 78/esp-ml307: "~1.7.2"
78/xiaozhi-fonts: "~1.3.2" 78/xiaozhi-fonts: "~1.3.2"

View File

@ -8,19 +8,27 @@ import numpy as np
def encode_audio_to_opus(input_file, output_file): def encode_audio_to_opus(input_file, output_file):
# Load audio file using librosa # Load audio file using librosa
audio, sample_rate = librosa.load(input_file, sr=None, mono=False, dtype=np.int16) audio, sample_rate = librosa.load(input_file, sr=None, mono=False, dtype=np.float32)
# Convert sample rate to 16000Hz if necessary
target_sample_rate = 16000
if sample_rate != target_sample_rate:
audio = librosa.resample(audio, orig_sr=sample_rate, target_sr=target_sample_rate)
sample_rate = target_sample_rate
# Get left channel if stereo # Get left channel if stereo
if audio.ndim == 2: if audio.ndim == 2:
audio = audio[0] audio = audio[0]
# Convert audio data back to int16 after resampling
audio = (audio * 32767).astype(np.int16)
# Initialize Opus encoder # Initialize Opus encoder
encoder = opuslib.Encoder(sample_rate, 1, opuslib.APPLICATION_VOIP) encoder = opuslib.Encoder(sample_rate, 1, opuslib.APPLICATION_VOIP)
# Encode audio data to Opus packets # Encode audio data to Opus packets
# Save encoded data to file # Save encoded data to file
with open(output_file, 'wb') as f: with open(output_file, 'wb') as f:
sample_rate = 16000 # 16000Hz
duration = 60 # 60ms every frame duration = 60 # 60ms every frame
frame_size = int(sample_rate * duration / 1000) frame_size = int(sample_rate * duration / 1000)
for i in tqdm.tqdm(range(0, len(audio) - frame_size, frame_size)): for i in tqdm.tqdm(range(0, len(audio) - frame_size, frame_size)):

View File

@ -53,6 +53,18 @@ def generate_header(input_path, output_path):
static_cast<size_t>(p3_{base_name}_end - p3_{base_name}_start) static_cast<size_t>(p3_{base_name}_end - p3_{base_name}_start)
}};''') }};''')
# 生成公共音效
for file in os.listdir(os.path.join(os.path.dirname(output_path), 'common')):
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( content = HEADER_TEMPLATE.format(
lang_code=lang_code, lang_code=lang_code,

4
scripts/release.py Normal file → Executable file
View File

@ -106,6 +106,10 @@ def release(board_type, board_config):
f.write("\n") f.write("\n")
for append in sdkconfig_append: for append in sdkconfig_append:
f.write(f"{append}\n") f.write(f"{append}\n")
# Build with macro BOARD_NAME defined to name
if os.system(f"idf.py -DBOARD_NAME={name} build") != 0:
print("build failed")
sys.exit(1)
# Call merge-bin # Call merge-bin
if os.system("idf.py merge-bin") != 0: if os.system("idf.py merge-bin") != 0:
print("merge-bin failed") print("merge-bin failed")