From 7945da0c8436bece9a095e635d444e9e2ff036d4 Mon Sep 17 00:00:00 2001 From: Terrence Date: Mon, 3 Mar 2025 07:29:22 +0800 Subject: [PATCH] 1.3.1 Updates - Add startup and network failure sound effects - 12864 OLED scroll text - Internalization of volume actions --- CMakeLists.txt | 2 +- main/CMakeLists.txt | 9 +++-- main/application.cc | 38 +++++++++++------- main/application.h | 3 +- main/assets/common/exclamation.p3 | Bin 0 -> 1936 bytes main/assets/common/success.p3 | Bin 0 -> 1827 bytes main/assets/common/vibration.p3 | Bin 0 -> 1946 bytes main/assets/en-US/language.json | 19 ++++++--- main/assets/zh-CN/language.json | 12 +++++- .../compact_ml307_board.cc | 9 +++-- .../bread-compact-wifi/compact_wifi_board.cc | 9 +++-- main/boards/common/board.cc | 2 +- main/boards/common/ml307_board.cc | 4 +- main/boards/common/wifi_board.cc | 4 +- main/boards/kevin-box-1/kevin_box_board.cc | 9 +++-- main/boards/kevin-box-2/kevin_box_board.cc | 25 ++++++++---- .../magiclick-2p4/magiclick_2p4_board.cc | 14 ++++--- .../magiclick-2p5/magiclick_2p5_board.cc | 12 +++--- main/boards/tudouzi/kevin_box_board.cc | 9 +++-- .../xingzhi-cube-0.96oled-ml307.cc | 9 +++-- .../xingzhi_ssd1306_display.cc | 20 ++++++--- .../xingzhi-cube-0.96oled-wifi.cc | 9 +++-- .../xingzhi_ssd1306_display.cc | 20 ++++++--- .../xingzhi-cube-1.54tft-ml307.cc | 13 +++--- .../xingzhi-cube-1.54tft-wifi.cc | 15 +++---- main/display/ssd1306_display.cc | 25 ++++++++++-- main/idf_component.yml | 2 +- scripts/convert_audio_to_p3.py | 12 +++++- scripts/gen_lang.py | 12 ++++++ scripts/release.py | 4 ++ 30 files changed, 217 insertions(+), 104 deletions(-) create mode 100644 main/assets/common/exclamation.p3 create mode 100644 main/assets/common/success.p3 create mode 100644 main/assets/common/vibration.p3 mode change 100644 => 100755 scripts/release.py diff --git a/CMakeLists.txt b/CMakeLists.txt index 897bc4f4..efe6e354 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ # CMakeLists in this exact order for cmake to work correctly 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_compile_options(-Wno-missing-field-initializers) diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index f48e170f..591d203d 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -138,7 +138,8 @@ 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) +file(GLOB LANG_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/${LANG_DIR}/*.p3) +file(GLOB COMMON_SOUNDS ${CMAKE_CURRENT_SOURCE_DIR}/assets/common/*.p3) # 如果目标芯片是 ESP32,则排除特定文件 if(CONFIG_IDF_TARGET_ESP32) @@ -149,14 +150,14 @@ if(CONFIG_IDF_TARGET_ESP32) endif() idf_component_register(SRCS ${SOURCES} - EMBED_FILES ${ASSETS} + EMBED_FILES ${LANG_SOUNDS} ${COMMON_SOUNDS} INCLUDE_DIRS ${INCLUDE_DIRS} WHOLE_ARCHIVE ) -# 使用 target_compile_definitions 来定义 BOARD_TYPE +# 使用 target_compile_definitions 来定义 BOARD_TYPE, BOARD_NAME target_compile_definitions(${COMPONENT_LIB} - PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" + PRIVATE BOARD_TYPE=\"${BOARD_TYPE}\" BOARD_NAME=\"${BOARD_NAME}\" ) # 添加生成规则 diff --git a/main/application.cc b/main/application.cc index ab23c4fd..755f6698 100644 --- a/main/application.cc +++ b/main/application.cc @@ -153,7 +153,7 @@ void Application::CheckNewVersion() { SetDeviceState(kDeviceStateIdle); display->SetChatMessage("system", ""); - + PlaySound(Lang::Sounds::P3_SUCCESS); // Exit the loop if upgrade or idle break; } @@ -189,7 +189,7 @@ 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->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->SetChatMessage("system", message); if (!sound.empty()) { - PlayLocalFile(sound.data(), sound.size()); + PlaySound(sound); } } -void Application::PlayLocalFile(const char* data, size_t size) { - ESP_LOGI(TAG, "PlayLocalFile: %zu bytes", size); +void Application::DismissAlert() { + 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(); codec->EnableOutput(true); SetDecodeSampleRate(16000); + const char* data = sound.data(); + size_t size = sound.size(); for (const char* p = data; p < data + size; ) { auto p3 = (BinaryProtocol3*)p; p += sizeof(BinaryProtocol3); @@ -240,7 +250,6 @@ void Application::ToggleChatState() { if (device_state_ == kDeviceStateIdle) { SetDeviceState(kDeviceStateConnecting); if (!protocol_->OpenAudioChannel()) { - SetDeviceState(kDeviceStateIdle); return; } @@ -272,7 +281,6 @@ void Application::StartListening() { if (!protocol_->IsAudioChannelOpened()) { SetDeviceState(kDeviceStateConnecting); if (!protocol_->OpenAudioChannel()) { - SetDeviceState(kDeviceStateIdle); return; } } @@ -353,7 +361,8 @@ void Application::Start() { protocol_ = std::make_unique(); #endif 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&& data) { std::lock_guard lock(mutex_); @@ -446,7 +455,7 @@ 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); + ota_.SetHeader("Accept-Language", Lang::CODE); xTaskCreate([](void* arg) { Application* app = (Application*)arg; @@ -489,8 +498,6 @@ void Application::Start() { wake_word_detect_.EncodeWakeWordData(); if (!protocol_->OpenAudioChannel()) { - ESP_LOGE(TAG, "Failed to open audio channel"); - SetDeviceState(kDeviceStateIdle); wake_word_detect_.StartDetection(); 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 (ota_.HasServerTime()) { - Schedule([this]() { - if (device_state_ == kDeviceStateIdle) { + if (device_state_ == kDeviceStateIdle) { + Schedule([this]() { // Set status to clock "HH:MM" time_t now = time(NULL); char time_str[64]; strftime(time_str, sizeof(time_str), "%H:%M ", localtime(&now)); Board::GetInstance().GetDisplay()->SetStatus(time_str); - } - }); + }); + } } } } @@ -721,6 +728,7 @@ void Application::SetDeviceState(DeviceState state) { break; case kDeviceStateConnecting: display->SetStatus(Lang::Strings::CONNECTING); + display->SetEmotion("neutral"); display->SetChatMessage("system", ""); break; case kDeviceStateListening: diff --git a/main/application.h b/main/application.h index 359671f5..bb94b93e 100644 --- a/main/application.h +++ b/main/application.h @@ -58,6 +58,7 @@ public: void Schedule(std::function callback); void SetDeviceState(DeviceState state); void Alert(const char* status, const char* message, const char* emotion = "", const std::string_view& sound = ""); + void DismissAlert(); void AbortSpeaking(AbortReason reason); void ToggleChatState(); void StartListening(); @@ -65,6 +66,7 @@ public: void UpdateIotStates(); void Reboot(); void WakeWordInvoke(const std::string& wake_word); + void PlaySound(const std::string_view& sound); private: Application(); @@ -107,7 +109,6 @@ private: void CheckNewVersion(); void ShowActivationCode(); void OnClockTimer(); - void PlayLocalFile(const char* data, size_t size); }; #endif // _APPLICATION_H_ diff --git a/main/assets/common/exclamation.p3 b/main/assets/common/exclamation.p3 new file mode 100644 index 0000000000000000000000000000000000000000..65396687365ec1591741bbfd1cd2c1efe22c1733 GIT binary patch literal 1936 zcmV;B2XFWQ001ah0{JjayA9?&*_e>A5$tUBl>vf|aBBfve z001gj0-Hk(p_Ql9&(>paD#vivA#DFnHq!oSuIF0{T9V#EoiE}OCiHKg)&KwiEm#7Z zJnlDtfQD#Gu+=Zus+dk4JIa6*`C~6l?}nLcxXp1KJ4zlE=;er2*Kq&<05wzT?%U1}^MQ#C%h_@82MeD1d9A8z&&Jgk9nX8W)6~V4>}oPKvH8FN003oJ zVximtbX){Qk0xKY(?u_?UO6j-wdEXq%u!>Krct>4O`bmZ$!*xWG!!q9{A=atqocI* z&CEzwuO#kpi{I`Yq^`|24?yv?*+SRa*=Cgw4|JJw_6hr@)$arWRlX6$?vsI}0001w zSm{R1#F8JSgx^`&Ou8+)5mXi?LqnnN*j^z;GtM9}Ll`C5b)L=}(hbMm`61@V_sX+^ zyIS%)CqC1zFy?%Y^`D$XwEr2LRryO?WLBK_$*pgg(7do*fBtxS0X;OkE60JoOrR^b zF`u!XKlJVBev76YVvVdyZ|NXgl@}&i^LYBq9YB<}3Gko*005O(=|;`Ok_FjS zW!RU`Fh+9adQ1M7<#PcbIa`QC6#~hdt!aF!QVkdDCg3cWRE!FJoogCimm|lTxmkN5 z0k#I`{WC#uG(n?SmS2-9Kj`kuKr;cderRR|HnVCiNllPF_M^r)a{STHFPH*0{!SEp zKjD|t7JG>6IyB5@6w&}@;`|+jhGUh2K|2@#E;Z^9^2OQU0001YSjOB7MH{^)1$(NX zvZ$gpH?xc7Z6&SsgzDly_y$_a(!O)D;=pKV^h#^J42dA>4J0D7D=v`F`8>^iaS;?D zJ$DvQchF*rKwu)ZYmA1WoxGpllv6(FL9+b+E-%69=E_Iyfbkq{D(1IizV2~FxO!bQ z;M~9f002{1BNZ2|T-MQdnP%TKtEIuslZ>gHKukzMX)#n8e3n!{MNoz%z`&u9rmH0m zEw=0<)>dQGqm+jeme@90#1(`AWq~W`Mf+20<8-cj@867|8&d!P0EbxVnA>+`C+|F# zA4VJ6zKdK^GY+#%5!kiXArFT++1l%p+<5sc=%z&P2^WrDE+TD+HuQc(g}tXTMWzZu5}b0t+3&b8Lf4B!6YlPJKc`jNeP_`VXXO68$Hy4%s}lG4wo3 z14lhfn8j1(265m3003)P$BC;k6A5|RmBGwVdE$RO%^(*{_^v3i`>(ql*`4=`4UjU9 z$6B?evnvaY-=(ltu+%cRZ%BWiJ_~mZ32R$LmGmxiSQjO|3}nsAG`=>{PEVOT7J?Y{ z`+FfFNOBqAb9HNO`fU`l-8cXM083aTY05)~;?+;8!0+g-@38x3)c>DO3iK9ezX)Dw}uMBgYSct4=SLOdNRWaC!_3dOFD*Ma;VC30%RGynhqlvwFt zh1H3a_wFtsQA4z0k7+Ju_oR-3$T2jZ7xekcm4of_xiQZQRacO{8Z~fR!T9qbq)VGi z-5B9c*VA$q5;sC#C=V57Ggiz~CKmY&$l33{SH8Fu- z1gB+y1ra@tBx7LGMRg~)i3T}iB@Rp@f4lyZbmD!96v+rG#n@@h1(e&zSG&qT-WCe- zK!~_7ca$fH5vaOT@w0D+8JrqGL(jip)Hk3200633==Le4UgQy}6bq72 zFkeb2H|Gdv!I_7fnUziG@D}F$CrrLEd7MsB!VO7#r5$X^F=+Zq|Hm4rZ0mSB$BKu{ z&+q29YJK@T@gpx7s@!WxSNr#EdL+LElm0Xpcv*5f&1{-}hVSnOH18HX-EgqXmz>fv zBvJv`C!-hG2VUvE#*t!!H7C#HYBviRDQZeZO%9s`Is2x}6SCR$UD;)yyYKZAbkpjl zkN^Mxs#xgqH8Fu;0ZyaJOVMH^y#6ZP%~|2| zgyxxw<^zAIv*WX`*Mp%IfTgy<1$EAs!piRDTavvoIJZ`292+=un1bgFOSNU3PdVSK zgqj;HG30hV6bJ_o#r!HiCPUlK;0&sKt#Y1PqKmM$-AVnZ`}cco=>{!T4&@CXO{-mK WV?V@q!Gr9Ln`LTQ;@V`UoIS{ojjQnh literal 0 HcmV?d00001 diff --git a/main/assets/common/success.p3 b/main/assets/common/success.p3 new file mode 100644 index 0000000000000000000000000000000000000000..353a1b28909f2f7dce5ac0a9620a94501eba596c GIT binary patch literal 1827 zcmV+;2i*7o0022yBKa^)yA9?)ZH{NzZ9%M|SrwA84Vb;|TtdLfib}WcAkbg$9^*Wp z@5v1?l`R)`utYh|zEJmo0001wSmvwlfANwDprDXAoV}MSV$cSozuX21Y_%Mnb0x*@ zb~d}*+GDG0@O0%fOsns4pT`8@o{l^j{o(kna%2@$FWF;+GvCC%@C7YZa_-MK)(%C(34>0ZoD@|h`p#&x|}Br;N}Q^BiGpwDcwyzTBkz--cy*P&~yDosVcn&n*LF zwE!x-{y%iB$9`cWB%D?N00664=iOsLzE?LWa@+|tZTTeBul&W>u_FR4Ui$Y9!!*Lt zHE%U*p%zI@seEPUlSI%N^N7asX@a=U{Qiv%P;YBT1}r{W7XD<}Z&ADH^*<9W=HOQu zw~%=4?Kv@-`<7<8|3nhQSz0I@#uA7bRH!>!7SKlp4=73vU-9swjZ!jVktfw@qT#`Y zfA#M6?eSm^W{SU&+AuruuTzOxI4QYkX1uSTYHX>EDJB2_0JK=tz1xzHnO=rvtg2_GvuevlhpE1Q zoK3ZQ0+KmZURI55SgVlo{ca1Hu#YPNEN>t9H)7gcfes@R=n;#Lq<6)U?cAw|Ec8jBsSEJ2X#MdVc zrD?90%QrI9yqbn%7cP*>rz4qjx%{VV@trPl=(nX!u8hY1*_;AXO$d6*$O(5)zG%p2 z%lQm#E!he97LoHCL`<#k63%td?p}}p004kk;2LtiFU_#Fg6+xtv zJ9)^trtOri?zufyP{mTvpHNL6pI9I%gTZhGrJw`vM8_u$RTZWD{^h^Yhs5cLNiw}cAx1`R1w-<4)fL`Je!qv5;gi7Mj$gLZzLsGC z004DZV0)VjtUnN!u4_Y@XqzipYCRh*l(<{@Dbv!tLDurtV0Lc2x-4bi5eP{^r32G^ z7ckL`l);@(CDRSv!)!fzlb{>yIsp0ouPTSCdupx)GAXPwbziX=boF4*XX4QmZnNj7 zTb4Kahi35L zdOnQyr(Bz->q^rO+tuQnEXAWibr=MtX6TMYf6Q&l=K>`Yqi<-82$x!Y%`mHtXjzj# z_Fw=2083Z{O5nd0PS;N)-l}NsufZ%@9w>(!=f{(`5s9eke`WptU`0h?mNg>K2PM&N z(dty!W8C>8Epu?UNd`j9Wze~;6L3hG=P<3BKmY&$MOXt$;J+%WsgLmb@%q^_U)wne zAkj5*o{{$*oj#~nLoCoENV+LDtD@pZDxwQ^1l29RbGX^6ac+2%Dfgk3GR-mPldu2) z07_T_+VcDym^u9pybFJmi7+pMmYGlVCr%tK z3o}5XNft2`iKgr@r{X;we`Kbm|*vP^uy0000zSOVAx zcw=K?Jqw?md`&W&(3_oSrKvpqdGJ(*6F&(Bwd<)B z0YGX!5go8zd?Zg^(BeJmzbPZMzqe)a!pQFU;LC}ORSf~rw)tEGKmY&$Nmv5b`o3L~ zC6@-qcAwdBf5})D8V_Ix0^@fr;J(dGZ=VM~24`P9(ysrmP8h$nzBPeQyv#jDS RxPYG!;0YtiF`ZZ@CqP@tc+mg= literal 0 HcmV?d00001 diff --git a/main/assets/common/vibration.p3 b/main/assets/common/vibration.p3 new file mode 100644 index 0000000000000000000000000000000000000000..dc876acd28485be0dbdeec77c1fe58190bdd289d GIT binary patch literal 1946 zcmV;L2W9vG0028!V)-ylyA9+!8J3$__MqVS^gyj|k>ovv2PBqJ@_}b*R84NmNat-! z{nG^lW$TdqK5%Br-Ujv;#8?0T0BTs_(50~PCRcSn5dIYC=>}DwZ2i@qReFRsojPT5 zgB7J?T>0@?>8-)-ekN4h^&JddB9!l_DLZ^HHUa$JYH`ZHWV&r%Ra>zG~fMw^(j#?Y$+6pV~t- z<0=@=_*SwFX8C$h@k*>2^6ykA=5lfu>p2h&KSFwIOn%zcH}PWy00015SP4Rh@s@Mt z8I~E96{-<<7&SrIr!#F?`iPc+X2 zV&+0kfMpM)@wcA-`_X+Z6O5|H#u9qMuR9V!>)-$Y09RNDc|T0OE0NIRUEz0e00vl6 zy(#*JBma&uGz)HuLZ6=a3f52CIb?)uX8DHEm<*bH%_fIyZKamS%vjgBWtng9{22yY z_53Z*U*EM|{$j6*jmCO=zyJUMZdl?UibfO${LcoK$Lpx_>azt~$V2z{tX#1;{`bAs zx+`x3F>;b#(u6LV73U-Yt+e5GZOZOHIZA7Mg~|CahoCX#BL>-!5jp@`QtAENmRP?@ zfAY2e5KcLt*sOo*Ug26!NO$gs;V5e7;g$pd004Se;4+S5kNp2gp!q|;T}gg!t%sA9 z#W_=olZaRO#|I$=q1z+Iss;p3V8l@ZbS8Z0Le~%U$lD3Eq`8%I0hsjB49CYUuFVWv zFQYxE4abLVsBeR{D`VR|OpM|;w%1n(c|lQfLs1mirj=YiWyIB`VB1dJkcB9Q&Hw-a zj9BVhea9z@)qvdf%BNt2nbYdu4kJ|g8jHP4t(wu&*nA`(Io&LDQ-fb%7aCTOwfGAZ=^y{ZdI(!l!bUO&BQf| z)wFl2-(=a_%WsgrIxB#6Eni(^x$43^G~+6ef^vK{g2bi}9smFUnpo;;R+Pht8N&or zIb)9s?GC%5*6K*z5 z34JY2=y$A>|*$9 zzdlN3UoY#V<^kv06|1y1JE3njW+744jXf>l$#}ofmV-8+dDnevJkDcuDkxo+7vyUB z0001LSn6G&Gi0Gl2Foe~v?Haq4YC>N(CW;`3#?UpD>+bgt!tWCw`O;RPK595W@2A0 zO~`fn=Cn>-IydAkL+~v#j~^dr?}dn|l8O#K7zYktCl}7BkC?uG1t@x^F%vS>iFR8` zC(D4F{onuq0E1ZSgt2~5`jrUb*kC>49yS+ez{BcjIg`*_WZkqGZC)UAiBsBlM>Z6a zmCm8)Pf}K3I4c0{1IN*?X8E(rF@@{J#4x^4e?EwS)iKxd1YC=gB2HOeSPTABAqFXn z9o@Gbu__34cZIe!C|XaE2JfLQAl`vU0nQSz=R zL1FOf+0|=uy7b&oZ}=065TA*x@Epm--Mg#KjZMJKG&qv|M(Wmyr*w3ystTcBm@!^0 zw+F2m3(;;rKyoqP+lOx&Sx+%6VmcWbOtEt^l~>;8(GASWYdL_$IvtcM=S=lhoyAra z<_AT`dg~zFjtp9^AOHXWU04kioWw~}nijG&o;#>0VhUVA#VR!;AbL3?XrTlwl(jXx zrVEK%8&p70`|yF0a)?7JsDucc{~~!QVpwOgC}HeGK%@y)&W#gOLaITjg2Ic``$n1? zsi-St&UDCj3Lu?s9B+C_qw1)nsRPSYKZ z`0gc^sYk&H5sdOni)~Ho@GHpKs#s> #include @@ -58,12 +59,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -73,12 +74,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/boards/bread-compact-wifi/compact_wifi_board.cc b/main/boards/bread-compact-wifi/compact_wifi_board.cc index 669afe8c..9612e26e 100644 --- a/main/boards/bread-compact-wifi/compact_wifi_board.cc +++ b/main/boards/bread-compact-wifi/compact_wifi_board.cc @@ -7,6 +7,7 @@ #include "config.h" #include "iot/thing_manager.h" #include "led/single_led.h" +#include "assets/lang_config.h" #include #include @@ -63,12 +64,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -78,12 +79,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/boards/common/board.cc b/main/boards/common/board.cc index 67b9e47f..b4919647 100644 --- a/main/boards/common/board.cc +++ b/main/boards/common/board.cc @@ -18,7 +18,7 @@ Board::Board() { uuid_ = GenerateUuid(); 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() { diff --git a/main/boards/common/ml307_board.cc b/main/boards/common/ml307_board.cc index 587506a7..b000a3f1 100644 --- a/main/boards/common/ml307_board.cc +++ b/main/boards/common/ml307_board.cc @@ -106,8 +106,8 @@ const char* Ml307Board::GetNetworkStateIcon() { std::string Ml307Board::GetBoardJson() { // 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 += "\"carrier\":\"" + modem_.GetCarrierName() + "\","; board_json += "\"csq\":\"" + std::to_string(modem_.GetCsq()) + "\","; diff --git a/main/boards/common/wifi_board.cc b/main/boards/common/wifi_board.cc index 0ef0b692..1c353800 100644 --- a/main/boards/common/wifi_board.cc +++ b/main/boards/common/wifi_board.cc @@ -154,8 +154,8 @@ const char* WifiBoard::GetNetworkStateIcon() { std::string WifiBoard::GetBoardJson() { // Set the board type for OTA 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_) { board_json += "\"ssid\":\"" + wifi_station.GetSsid() + "\","; board_json += "\"rssi\":" + std::to_string(wifi_station.GetRssi()) + ","; diff --git a/main/boards/kevin-box-1/kevin_box_board.cc b/main/boards/kevin-box-1/kevin_box_board.cc index fa36422d..04b1cb14 100644 --- a/main/boards/kevin-box-1/kevin_box_board.cc +++ b/main/boards/kevin-box-1/kevin_box_board.cc @@ -6,6 +6,7 @@ #include "config.h" #include "iot/thing_manager.h" #include "led/single_led.h" +#include "assets/lang_config.h" #include #include @@ -98,12 +99,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -113,12 +114,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/boards/kevin-box-2/kevin_box_board.cc b/main/boards/kevin-box-2/kevin_box_board.cc index b5404397..b6c49df0 100644 --- a/main/boards/kevin-box-2/kevin_box_board.cc +++ b/main/boards/kevin-box-2/kevin_box_board.cc @@ -7,6 +7,7 @@ #include "axp2101.h" #include "iot/thing_manager.h" #include "led/single_led.h" +#include "assets/lang_config.h" #include #include @@ -26,8 +27,8 @@ private: Button boot_button_; Button volume_up_button_; Button volume_down_button_; - uint8_t _data_buffer[2]; esp_timer_handle_t power_save_timer_ = nullptr; + bool show_low_power_warning_ = false; void InitializePowerSaveTimer() { esp_timer_create_args_t power_save_timer_args = { @@ -48,15 +49,25 @@ private: // 电池放电模式下,如果待机超过一定时间,则自动关机 const int seconds_to_shutdown = 600; static int seconds = 0; - if (Application::GetInstance().GetDeviceState() != kDeviceStateIdle) { + auto& app = Application::GetInstance(); + if (app.GetDeviceState() != kDeviceStateIdle) { seconds = 0; return; } if (!axp2101_->IsDischarging()) { seconds = 0; + if (show_low_power_warning_) { + app.DismissAlert(); + show_low_power_warning_ = false; + } 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++; if (seconds >= seconds_to_shutdown) { axp2101_->PowerOff(); @@ -124,12 +135,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -139,12 +150,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/boards/magiclick-2p4/magiclick_2p4_board.cc b/main/boards/magiclick-2p4/magiclick_2p4_board.cc index 00194065..4a9273b5 100644 --- a/main/boards/magiclick-2p4/magiclick_2p4_board.cc +++ b/main/boards/magiclick-2p4/magiclick_2p4_board.cc @@ -6,13 +6,15 @@ #include "led/circular_strip.h" #include "iot/thing_manager.h" #include "config.h" +#include "font_awesome_symbols.h" +#include "assets/lang_config.h" + #include #include #include #include #include -#include "esp_lcd_nv3023.h" -#include "font_awesome_symbols.h" +#include #define TAG "magiclick_2p4" @@ -100,12 +102,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); left_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); right_button_.OnClick([this]() { @@ -115,12 +117,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); right_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); } diff --git a/main/boards/magiclick-2p5/magiclick_2p5_board.cc b/main/boards/magiclick-2p5/magiclick_2p5_board.cc index 7a853bd5..a5a45b29 100644 --- a/main/boards/magiclick-2p5/magiclick_2p5_board.cc +++ b/main/boards/magiclick-2p5/magiclick_2p5_board.cc @@ -6,6 +6,9 @@ #include "led/circular_strip.h" #include "iot/thing_manager.h" #include "config.h" +#include "assets/lang_config.h" +#include "font_awesome_symbols.h" + #include #include #include @@ -14,7 +17,6 @@ #include #include #include -#include "font_awesome_symbols.h" #define TAG "magiclick_2p5" @@ -136,12 +138,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); left_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); right_button_.OnClick([this]() { @@ -151,12 +153,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); right_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); diff --git a/main/boards/tudouzi/kevin_box_board.cc b/main/boards/tudouzi/kevin_box_board.cc index 0744c2a7..e3189a57 100644 --- a/main/boards/tudouzi/kevin_box_board.cc +++ b/main/boards/tudouzi/kevin_box_board.cc @@ -7,6 +7,7 @@ #include "axp2101.h" #include "iot/thing_manager.h" #include "led/single_led.h" +#include "assets/lang_config.h" #include #include @@ -135,12 +136,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -150,12 +151,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/boards/xingzhi-cube-0.96oled-ml307/xingzhi-cube-0.96oled-ml307.cc b/main/boards/xingzhi-cube-0.96oled-ml307/xingzhi-cube-0.96oled-ml307.cc index cafd32aa..f2663eca 100644 --- a/main/boards/xingzhi-cube-0.96oled-ml307/xingzhi-cube-0.96oled-ml307.cc +++ b/main/boards/xingzhi-cube-0.96oled-ml307/xingzhi-cube-0.96oled-ml307.cc @@ -7,6 +7,7 @@ #include "config.h" #include "iot/thing_manager.h" #include "led/single_led.h" +#include "assets/lang_config.h" #include #include @@ -52,12 +53,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -67,12 +68,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/boards/xingzhi-cube-0.96oled-ml307/xingzhi_ssd1306_display.cc b/main/boards/xingzhi-cube-0.96oled-ml307/xingzhi_ssd1306_display.cc index f54b5027..f998f3b4 100644 --- a/main/boards/xingzhi-cube-0.96oled-ml307/xingzhi_ssd1306_display.cc +++ b/main/boards/xingzhi-cube-0.96oled-ml307/xingzhi_ssd1306_display.cc @@ -23,6 +23,7 @@ #include "application.h" #include "driver/rtc_io.h" #include "led/single_led.h" +#include "assets/lang_config.h" #define TAG "XINGZHI_Ssd1306Display" @@ -356,8 +357,17 @@ void XINGZHI_Ssd1306Display::SetupUI_128x64() { 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_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_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 */ 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_); 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_, "通知"); + 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_, "正在初始化"); + 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_); @@ -460,10 +470,10 @@ void XINGZHI_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_, "正在初始化"); + lv_label_set_text(status_label_, Lang::Strings::INITIALIZING); 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_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); diff --git a/main/boards/xingzhi-cube-0.96oled-wifi/xingzhi-cube-0.96oled-wifi.cc b/main/boards/xingzhi-cube-0.96oled-wifi/xingzhi-cube-0.96oled-wifi.cc index 4a39358f..10ec1ae7 100644 --- a/main/boards/xingzhi-cube-0.96oled-wifi/xingzhi-cube-0.96oled-wifi.cc +++ b/main/boards/xingzhi-cube-0.96oled-wifi/xingzhi-cube-0.96oled-wifi.cc @@ -7,6 +7,7 @@ #include "config.h" #include "iot/thing_manager.h" #include "led/single_led.h" +#include "assets/lang_config.h" #include #include @@ -56,12 +57,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -71,12 +72,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/boards/xingzhi-cube-0.96oled-wifi/xingzhi_ssd1306_display.cc b/main/boards/xingzhi-cube-0.96oled-wifi/xingzhi_ssd1306_display.cc index f54b5027..f998f3b4 100644 --- a/main/boards/xingzhi-cube-0.96oled-wifi/xingzhi_ssd1306_display.cc +++ b/main/boards/xingzhi-cube-0.96oled-wifi/xingzhi_ssd1306_display.cc @@ -23,6 +23,7 @@ #include "application.h" #include "driver/rtc_io.h" #include "led/single_led.h" +#include "assets/lang_config.h" #define TAG "XINGZHI_Ssd1306Display" @@ -356,8 +357,17 @@ void XINGZHI_Ssd1306Display::SetupUI_128x64() { 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_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_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 */ 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_); 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_, "通知"); + 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_, "正在初始化"); + 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_); @@ -460,10 +470,10 @@ void XINGZHI_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_, "正在初始化"); + lv_label_set_text(status_label_, Lang::Strings::INITIALIZING); 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_add_flag(notification_label_, LV_OBJ_FLAG_HIDDEN); diff --git a/main/boards/xingzhi-cube-1.54tft-ml307/xingzhi-cube-1.54tft-ml307.cc b/main/boards/xingzhi-cube-1.54tft-ml307/xingzhi-cube-1.54tft-ml307.cc index 61cabdd0..44d05e37 100644 --- a/main/boards/xingzhi-cube-1.54tft-ml307/xingzhi-cube-1.54tft-ml307.cc +++ b/main/boards/xingzhi-cube-1.54tft-ml307/xingzhi-cube-1.54tft-ml307.cc @@ -1,16 +1,17 @@ #include "ml307_board.h" - #include "audio_codecs/no_audio_codec.h" #include "xingzhi_lcd_display.h" #include "system_reset.h" #include "application.h" #include "button.h" - #include "config.h" #include "iot/thing_manager.h" #include "led/single_led.h" +#include "assets/lang_config.h" + #include #include + #define TAG "XINGZHI_CUBE_1_54TFT_ML307" LV_FONT_DECLARE(font_puhui_16_4); @@ -48,12 +49,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -63,12 +64,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/boards/xingzhi-cube-1.54tft-wifi/xingzhi-cube-1.54tft-wifi.cc b/main/boards/xingzhi-cube-1.54tft-wifi/xingzhi-cube-1.54tft-wifi.cc index 517e541d..b3bc8a77 100644 --- a/main/boards/xingzhi-cube-1.54tft-wifi/xingzhi-cube-1.54tft-wifi.cc +++ b/main/boards/xingzhi-cube-1.54tft-wifi/xingzhi-cube-1.54tft-wifi.cc @@ -1,17 +1,18 @@ #include "wifi_board.h" - #include "audio_codecs/no_audio_codec.h" #include "xingzhi_lcd_display.h" #include "system_reset.h" #include "application.h" #include "button.h" - -#include #include "config.h" #include "iot/thing_manager.h" #include "led/single_led.h" +#include "assets/lang_config.h" + #include #include +#include + #define TAG "XINGZHI_CUBE_1_54TFT_WIFI" LV_FONT_DECLARE(font_puhui_16_4); @@ -52,12 +53,12 @@ private: volume = 100; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_up_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(100); - GetDisplay()->ShowNotification("最大音量"); + GetDisplay()->ShowNotification(Lang::Strings::MAX_VOLUME); }); volume_down_button_.OnClick([this]() { @@ -67,12 +68,12 @@ private: volume = 0; } codec->SetOutputVolume(volume); - GetDisplay()->ShowNotification("音量 " + std::to_string(volume)); + GetDisplay()->ShowNotification(Lang::Strings::VOLUME + std::to_string(volume)); }); volume_down_button_.OnLongPress([this]() { GetAudioCodec()->SetOutputVolume(0); - GetDisplay()->ShowNotification("已静音"); + GetDisplay()->ShowNotification(Lang::Strings::MUTED); }); } diff --git a/main/display/ssd1306_display.cc b/main/display/ssd1306_display.cc index 6af24a6c..ec1bcd95 100644 --- a/main/display/ssd1306_display.cc +++ b/main/display/ssd1306_display.cc @@ -171,7 +171,7 @@ void Ssd1306Display::SetupUI_128x64() { 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); @@ -187,7 +187,7 @@ void Ssd1306Display::SetupUI_128x64() { 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); @@ -204,9 +204,18 @@ void Ssd1306Display::SetupUI_128x64() { 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_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); 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 */ 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_label_set_long_mode(chat_message_label_, LV_LABEL_LONG_SCROLL_CIRCULAR); 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); } diff --git a/main/idf_component.yml b/main/idf_component.yml index a65c2623..ca9c6992 100644 --- a/main/idf_component.yml +++ b/main/idf_component.yml @@ -8,7 +8,7 @@ dependencies: espressif/esp_io_expander_tca9554: "==2.0.0" espressif/esp_lcd_panel_io_additions: "^1.0.1" 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-ml307: "~1.7.2" 78/xiaozhi-fonts: "~1.3.2" diff --git a/scripts/convert_audio_to_p3.py b/scripts/convert_audio_to_p3.py index 6942deed..1d38d6dd 100644 --- a/scripts/convert_audio_to_p3.py +++ b/scripts/convert_audio_to_p3.py @@ -8,19 +8,27 @@ import numpy as np def encode_audio_to_opus(input_file, output_file): # 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 if audio.ndim == 2: audio = audio[0] + # Convert audio data back to int16 after resampling + audio = (audio * 32767).astype(np.int16) + # Initialize Opus encoder encoder = opuslib.Encoder(sample_rate, 1, opuslib.APPLICATION_VOIP) # Encode audio data to Opus packets # Save encoded data to file with open(output_file, 'wb') as f: - sample_rate = 16000 # 16000Hz duration = 60 # 60ms every frame frame_size = int(sample_rate * duration / 1000) for i in tqdm.tqdm(range(0, len(audio) - frame_size, frame_size)): diff --git a/scripts/gen_lang.py b/scripts/gen_lang.py index dd0ac83b..286b5c2a 100644 --- a/scripts/gen_lang.py +++ b/scripts/gen_lang.py @@ -52,6 +52,18 @@ def generate_header(input_path, output_path): static_cast(p3_{base_name}_start), static_cast(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(p3_{base_name}_start), + static_cast(p3_{base_name}_end - p3_{base_name}_start) + }};''') # 填充模板 content = HEADER_TEMPLATE.format( diff --git a/scripts/release.py b/scripts/release.py old mode 100644 new mode 100755 index 54f76196..75c4bdc7 --- a/scripts/release.py +++ b/scripts/release.py @@ -106,6 +106,10 @@ def release(board_type, board_config): f.write("\n") for append in sdkconfig_append: 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 if os.system("idf.py merge-bin") != 0: print("merge-bin failed")