application.cc 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970
  1. #include "application.h"
  2. #include "board.h"
  3. #include "display.h"
  4. #include "system_info.h"
  5. #include "audio_codec.h"
  6. #include "mqtt_protocol.h"
  7. #include "websocket_protocol.h"
  8. #include "font_awesome_symbols.h"
  9. #include "assets/lang_config.h"
  10. #include "mcp_server.h"
  11. #include <cstring>
  12. #include <esp_log.h>
  13. #include <cJSON.h>
  14. #include <driver/gpio.h>
  15. #include <arpa/inet.h>
  16. #define TAG "Application"
  17. static const char* const STATE_STRINGS[] = {
  18. "unknown",
  19. "home"
  20. "starting",
  21. "configuring",
  22. "idle",
  23. "connecting",
  24. "listening",
  25. "speaking",
  26. "upgrading",
  27. "activating",
  28. "audio_testing",
  29. "fatal_error",
  30. "10min",
  31. "15min",
  32. "20min",
  33. "down",
  34. "invalid_state"
  35. };
  36. Application::Application(): countdown_seconds_(0),
  37. is_countdown_active_(false),
  38. last_countdown_state_(kDeviceStateUnknown)
  39. {
  40. event_group_ = xEventGroupCreate();
  41. #if CONFIG_USE_DEVICE_AEC && CONFIG_USE_SERVER_AEC
  42. #error "CONFIG_USE_DEVICE_AEC and CONFIG_USE_SERVER_AEC cannot be enabled at the same time"
  43. #elif CONFIG_USE_DEVICE_AEC
  44. aec_mode_ = kAecOnDeviceSide;
  45. #elif CONFIG_USE_SERVER_AEC
  46. aec_mode_ = kAecOnServerSide;
  47. #else
  48. aec_mode_ = kAecOff;
  49. #endif
  50. esp_timer_create_args_t clock_timer_args = {
  51. .callback = [](void* arg) {
  52. Application* app = (Application*)arg;
  53. app->OnClockTimer();
  54. },
  55. .arg = this,
  56. .dispatch_method = ESP_TIMER_TASK,
  57. .name = "clock_timer",
  58. .skip_unhandled_events = true
  59. };
  60. esp_timer_create(&clock_timer_args, &clock_timer_handle_);
  61. // 创建倒计时定时器
  62. esp_timer_create_args_t countdown_timer_args = {
  63. .callback = [](void* arg) {
  64. Application* app = (Application*)arg;
  65. app->OnCountdownTimer();
  66. },
  67. .arg = this,
  68. .dispatch_method = ESP_TIMER_TASK,
  69. .name = "countdown_timer",
  70. .skip_unhandled_events = false
  71. };
  72. esp_timer_create(&countdown_timer_args, &countdown_timer_handle_);
  73. }
  74. Application::~Application() {
  75. if (clock_timer_handle_ != nullptr) {
  76. esp_timer_stop(clock_timer_handle_);
  77. esp_timer_delete(clock_timer_handle_);
  78. }
  79. // 停止和删除倒计时定时器
  80. if (countdown_timer_handle_ != nullptr) {
  81. esp_timer_stop(countdown_timer_handle_);
  82. esp_timer_delete(countdown_timer_handle_);
  83. }
  84. vEventGroupDelete(event_group_);
  85. }
  86. void Application::CheckNewVersion(Ota& ota) {
  87. const int MAX_RETRY = 10;
  88. int retry_count = 0;
  89. int retry_delay = 10; // 初始重试延迟为10秒
  90. auto& board = Board::GetInstance();
  91. while (true) {
  92. SetDeviceState(kDeviceStateActivating);
  93. auto display = board.GetDisplay();
  94. display->SetStatus(Lang::Strings::CHECKING_NEW_VERSION);
  95. if (!ota.CheckVersion()) {
  96. retry_count++;
  97. if (retry_count >= MAX_RETRY) {
  98. ESP_LOGE(TAG, "Too many retries, exit version check");
  99. return;
  100. }
  101. char buffer[128];
  102. snprintf(buffer, sizeof(buffer), Lang::Strings::CHECK_NEW_VERSION_FAILED, retry_delay, ota.GetCheckVersionUrl().c_str());
  103. Alert(Lang::Strings::ERROR, buffer, "sad", Lang::Sounds::P3_EXCLAMATION);
  104. ESP_LOGW(TAG, "Check new version failed, retry in %d seconds (%d/%d)", retry_delay, retry_count, MAX_RETRY);
  105. for (int i = 0; i < retry_delay; i++) {
  106. vTaskDelay(pdMS_TO_TICKS(1000));
  107. if (device_state_ == kDeviceStateIdle) {
  108. break;
  109. }
  110. }
  111. retry_delay *= 2; // 每次重试后延迟时间翻倍
  112. continue;
  113. }
  114. retry_count = 0;
  115. retry_delay = 10; // 重置重试延迟时间
  116. if (ota.HasNewVersion()) {
  117. Alert(Lang::Strings::OTA_UPGRADE, Lang::Strings::UPGRADING, "happy", Lang::Sounds::P3_UPGRADE);
  118. vTaskDelay(pdMS_TO_TICKS(3000));
  119. SetDeviceState(kDeviceStateUpgrading);
  120. display->SetIcon(FONT_AWESOME_DOWNLOAD);
  121. std::string message = std::string(Lang::Strings::NEW_VERSION) + ota.GetFirmwareVersion();
  122. display->SetChatMessage("system", message.c_str());
  123. board.SetPowerSaveMode(false);
  124. audio_service_.Stop();
  125. vTaskDelay(pdMS_TO_TICKS(1000));
  126. bool upgrade_success = ota.StartUpgrade([display](int progress, size_t speed) {
  127. char buffer[64];
  128. snprintf(buffer, sizeof(buffer), "%d%% %uKB/s", progress, speed / 1024);
  129. display->SetChatMessage("system", buffer);
  130. });
  131. if (!upgrade_success) {
  132. // Upgrade failed, restart audio service and continue running
  133. ESP_LOGE(TAG, "Firmware upgrade failed, restarting audio service and continuing operation...");
  134. audio_service_.Start(); // Restart audio service
  135. board.SetPowerSaveMode(true); // Restore power save mode
  136. Alert(Lang::Strings::ERROR, Lang::Strings::UPGRADE_FAILED, "sad", Lang::Sounds::P3_EXCLAMATION);
  137. vTaskDelay(pdMS_TO_TICKS(3000));
  138. // Continue to normal operation (don't break, just fall through)
  139. } else {
  140. // Upgrade success, reboot immediately
  141. ESP_LOGI(TAG, "Firmware upgrade successful, rebooting...");
  142. display->SetChatMessage("system", "Upgrade successful, rebooting...");
  143. vTaskDelay(pdMS_TO_TICKS(1000)); // Brief pause to show message
  144. Reboot();
  145. return; // This line will never be reached after reboot
  146. }
  147. }
  148. // No new version, mark the current version as valid
  149. ota.MarkCurrentVersionValid();
  150. if (!ota.HasActivationCode() && !ota.HasActivationChallenge()) {
  151. xEventGroupSetBits(event_group_, MAIN_EVENT_CHECK_NEW_VERSION_DONE);
  152. // Exit the loop if done checking new version
  153. break;
  154. }
  155. display->SetStatus(Lang::Strings::ACTIVATION);
  156. // Activation code is shown to the user and waiting for the user to input
  157. if (ota.HasActivationCode()) {
  158. ShowActivationCode(ota.GetActivationCode(), ota.GetActivationMessage());
  159. }
  160. // This will block the loop until the activation is done or timeout
  161. for (int i = 0; i < 10; ++i) {
  162. ESP_LOGI(TAG, "Activating... %d/%d", i + 1, 10);
  163. esp_err_t err = ota.Activate();
  164. if (err == ESP_OK) {
  165. xEventGroupSetBits(event_group_, MAIN_EVENT_CHECK_NEW_VERSION_DONE);
  166. break;
  167. } else if (err == ESP_ERR_TIMEOUT) {
  168. vTaskDelay(pdMS_TO_TICKS(3000));
  169. } else {
  170. vTaskDelay(pdMS_TO_TICKS(10000));
  171. }
  172. if (device_state_ == kDeviceStateIdle) {
  173. break;
  174. }
  175. }
  176. }
  177. }
  178. void Application::ShowActivationCode(const std::string& code, const std::string& message) {
  179. #if 0
  180. struct digit_sound {
  181. char digit;
  182. const std::string_view& sound;
  183. };
  184. static const std::array<digit_sound, 10> digit_sounds{{
  185. digit_sound{'0', Lang::Sounds::P3_0},
  186. digit_sound{'1', Lang::Sounds::P3_1},
  187. digit_sound{'2', Lang::Sounds::P3_2},
  188. digit_sound{'3', Lang::Sounds::P3_3},
  189. digit_sound{'4', Lang::Sounds::P3_4},
  190. digit_sound{'5', Lang::Sounds::P3_5},
  191. digit_sound{'6', Lang::Sounds::P3_6},
  192. digit_sound{'7', Lang::Sounds::P3_7},
  193. digit_sound{'8', Lang::Sounds::P3_8},
  194. digit_sound{'9', Lang::Sounds::P3_9}
  195. }};
  196. #endif
  197. // This sentence uses 9KB of SRAM, so we need to wait for it to finish
  198. Alert(Lang::Strings::ACTIVATION, message.c_str(), "happy", Lang::Sounds::P3_ACTIVATION);
  199. #if 0
  200. for (const auto& digit : code) {
  201. auto it = std::find_if(digit_sounds.begin(), digit_sounds.end(),
  202. [digit](const digit_sound& ds) { return ds.digit == digit; });
  203. if (it != digit_sounds.end()) {
  204. audio_service_.PlaySound(it->sound);
  205. }
  206. }
  207. #endif
  208. }
  209. void Application::Alert(const char* status, const char* message, const char* emotion, const std::string_view& sound) {
  210. ESP_LOGW(TAG, "Alert %s: %s [%s]", status, message, emotion);
  211. auto display = Board::GetInstance().GetDisplay();
  212. display->SetStatus(status);
  213. display->SetEmotion(emotion);
  214. display->SetChatMessage("system", message);
  215. if (!sound.empty()) {
  216. audio_service_.PlaySound(sound);
  217. }
  218. }
  219. void Application::DismissAlert() {
  220. if (device_state_ == kDeviceStateIdle) {
  221. auto display = Board::GetInstance().GetDisplay();
  222. display->SetStatus(Lang::Strings::STANDBY);
  223. display->SetEmotion("neutral");
  224. display->SetChatMessage("system", "");
  225. }
  226. }
  227. void Application::ToggleChatState() {
  228. if (device_state_ == kDeviceStateActivating) {
  229. SetDeviceState(kDeviceStateIdle);
  230. return;
  231. } else if (device_state_ == kDeviceStateWifiConfiguring) {
  232. audio_service_.EnableAudioTesting(true);
  233. SetDeviceState(kDeviceStateAudioTesting);
  234. return;
  235. } else if (device_state_ == kDeviceStateAudioTesting) {
  236. audio_service_.EnableAudioTesting(false);
  237. SetDeviceState(kDeviceStateWifiConfiguring);
  238. return;
  239. }
  240. if (!protocol_) {
  241. ESP_LOGE(TAG, "Protocol not initialized");
  242. return;
  243. }
  244. if (device_state_ == kDeviceStateIdle) {
  245. Schedule([this]() {
  246. if (!protocol_->IsAudioChannelOpened()) {
  247. SetDeviceState(kDeviceStateConnecting);
  248. if (!protocol_->OpenAudioChannel()) {
  249. return;
  250. }
  251. }
  252. SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
  253. });
  254. } else if (device_state_ == kDeviceStateSpeaking) {
  255. Schedule([this]() {
  256. AbortSpeaking(kAbortReasonNone);
  257. });
  258. } else if (device_state_ == kDeviceStateListening) {
  259. Schedule([this]() {
  260. protocol_->CloseAudioChannel();
  261. });
  262. }
  263. }
  264. void Application::StartListening() {
  265. if (device_state_ == kDeviceStateActivating) {
  266. SetDeviceState(kDeviceStateIdle);
  267. return;
  268. } else if (device_state_ == kDeviceStateWifiConfiguring) {
  269. audio_service_.EnableAudioTesting(true);
  270. SetDeviceState(kDeviceStateAudioTesting);
  271. return;
  272. }
  273. if (!protocol_) {
  274. ESP_LOGE(TAG, "Protocol not initialized");
  275. return;
  276. }
  277. if (device_state_ == kDeviceStateIdle) {
  278. Schedule([this]() {
  279. if (!protocol_->IsAudioChannelOpened()) {
  280. SetDeviceState(kDeviceStateConnecting);
  281. if (!protocol_->OpenAudioChannel()) {
  282. return;
  283. }
  284. }
  285. SetListeningMode(kListeningModeManualStop);
  286. });
  287. } else if (device_state_ == kDeviceStateSpeaking) {
  288. Schedule([this]() {
  289. AbortSpeaking(kAbortReasonNone);
  290. SetListeningMode(kListeningModeManualStop);
  291. });
  292. }
  293. }
  294. void Application::StopListening() {
  295. if (device_state_ == kDeviceStateAudioTesting) {
  296. audio_service_.EnableAudioTesting(false);
  297. SetDeviceState(kDeviceStateWifiConfiguring);
  298. return;
  299. }
  300. const std::array<int, 3> valid_states = {
  301. kDeviceStateListening,
  302. kDeviceStateSpeaking,
  303. kDeviceStateIdle,
  304. };
  305. // If not valid, do nothing
  306. if (std::find(valid_states.begin(), valid_states.end(), device_state_) == valid_states.end()) {
  307. return;
  308. }
  309. Schedule([this]() {
  310. if (device_state_ == kDeviceStateListening) {
  311. protocol_->SendStopListening();
  312. SetDeviceState(kDeviceStateIdle);
  313. }
  314. });
  315. }
  316. void Application::Start() {
  317. auto& board = Board::GetInstance();
  318. SetDeviceState(kDeviceStateStarting);
  319. /* Setup the display */
  320. auto display = board.GetDisplay();
  321. /* Setup the audio service */
  322. auto codec = board.GetAudioCodec();
  323. audio_service_.Initialize(codec);
  324. audio_service_.Start();
  325. AudioServiceCallbacks callbacks;
  326. callbacks.on_send_queue_available = [this]() {
  327. xEventGroupSetBits(event_group_, MAIN_EVENT_SEND_AUDIO);
  328. };
  329. callbacks.on_wake_word_detected = [this](const std::string& wake_word) {
  330. xEventGroupSetBits(event_group_, MAIN_EVENT_WAKE_WORD_DETECTED);
  331. };
  332. callbacks.on_vad_change = [this](bool speaking) {
  333. xEventGroupSetBits(event_group_, MAIN_EVENT_VAD_CHANGE);
  334. };
  335. audio_service_.SetCallbacks(callbacks);
  336. /* Start the clock timer to update the status bar */
  337. esp_timer_start_periodic(clock_timer_handle_, 1000000);
  338. /* Wait for the network to be ready */
  339. board.StartNetwork();
  340. // Update the status bar immediately to show the network state
  341. display->UpdateStatusBar(true);
  342. // Check for new firmware version or get the MQTT broker address
  343. Ota ota;
  344. CheckNewVersion(ota);
  345. // Initialize the protocol
  346. display->SetStatus(Lang::Strings::LOADING_PROTOCOL);
  347. // Add MCP common tools before initializing the protocol
  348. McpServer::GetInstance().AddCommonTools();
  349. if (ota.HasMqttConfig()) {
  350. protocol_ = std::make_unique<MqttProtocol>();
  351. } else if (ota.HasWebsocketConfig()) {
  352. protocol_ = std::make_unique<WebsocketProtocol>();
  353. } else {
  354. ESP_LOGW(TAG, "No protocol specified in the OTA config, using MQTT");
  355. protocol_ = std::make_unique<MqttProtocol>();
  356. }
  357. protocol_->OnNetworkError([this](const std::string& message) {
  358. last_error_message_ = message;
  359. xEventGroupSetBits(event_group_, MAIN_EVENT_ERROR);
  360. });
  361. protocol_->OnIncomingAudio([this](std::unique_ptr<AudioStreamPacket> packet) {
  362. if (device_state_ == kDeviceStateSpeaking) {
  363. audio_service_.PushPacketToDecodeQueue(std::move(packet));
  364. }
  365. });
  366. protocol_->OnAudioChannelOpened([this, codec, &board]() {
  367. board.SetPowerSaveMode(false);
  368. if (protocol_->server_sample_rate() != codec->output_sample_rate()) {
  369. ESP_LOGW(TAG, "Server sample rate %d does not match device output sample rate %d, resampling may cause distortion",
  370. protocol_->server_sample_rate(), codec->output_sample_rate());
  371. }
  372. });
  373. protocol_->OnAudioChannelClosed([this, &board]() {
  374. board.SetPowerSaveMode(true);
  375. Schedule([this]() {
  376. auto display = Board::GetInstance().GetDisplay();
  377. display->SetChatMessage("system", "");
  378. SetDeviceState(kDeviceStateIdle);
  379. });
  380. });
  381. protocol_->OnIncomingJson([this, display](const cJSON* root) {
  382. // Parse JSON data
  383. auto type = cJSON_GetObjectItem(root, "type");
  384. if (strcmp(type->valuestring, "tts") == 0) {
  385. auto state = cJSON_GetObjectItem(root, "state");
  386. if (strcmp(state->valuestring, "start") == 0) {
  387. Schedule([this]() {
  388. aborted_ = false;
  389. if (device_state_ == kDeviceStateIdle || device_state_ == kDeviceStateListening) {
  390. SetDeviceState(kDeviceStateSpeaking);
  391. }
  392. });
  393. } else if (strcmp(state->valuestring, "stop") == 0) {
  394. Schedule([this]() {
  395. if (device_state_ == kDeviceStateSpeaking) {
  396. if (listening_mode_ == kListeningModeManualStop) {
  397. SetDeviceState(kDeviceStateIdle);
  398. } else {
  399. SetDeviceState(kDeviceStateListening);
  400. }
  401. }
  402. });
  403. } else if (strcmp(state->valuestring, "sentence_start") == 0) {
  404. auto text = cJSON_GetObjectItem(root, "text");
  405. if (cJSON_IsString(text)) {
  406. ESP_LOGI(TAG, "<< %s", text->valuestring);
  407. Schedule([this, display, message = std::string(text->valuestring)]() {
  408. display->SetChatMessage("assistant", message.c_str());
  409. });
  410. }
  411. }
  412. } else if (strcmp(type->valuestring, "stt") == 0) {
  413. auto text = cJSON_GetObjectItem(root, "text");
  414. if (cJSON_IsString(text)) {
  415. ESP_LOGI(TAG, ">> %s", text->valuestring);
  416. Schedule([this, display, message = std::string(text->valuestring)]() {
  417. display->SetChatMessage("user", message.c_str());
  418. });
  419. }
  420. } else if (strcmp(type->valuestring, "llm") == 0) {
  421. auto emotion = cJSON_GetObjectItem(root, "emotion");
  422. if (cJSON_IsString(emotion)) {
  423. Schedule([this, display, emotion_str = std::string(emotion->valuestring)]() {
  424. display->SetEmotion(emotion_str.c_str());
  425. });
  426. }
  427. } else if (strcmp(type->valuestring, "mcp") == 0) {
  428. auto payload = cJSON_GetObjectItem(root, "payload");
  429. if (cJSON_IsObject(payload)) {
  430. McpServer::GetInstance().ParseMessage(payload);
  431. }
  432. } else if (strcmp(type->valuestring, "system") == 0) {
  433. auto command = cJSON_GetObjectItem(root, "command");
  434. if (cJSON_IsString(command)) {
  435. ESP_LOGI(TAG, "System command: %s", command->valuestring);
  436. if (strcmp(command->valuestring, "reboot") == 0) {
  437. // Do a reboot if user requests a OTA update
  438. Schedule([this]() {
  439. Reboot();
  440. });
  441. } else {
  442. ESP_LOGW(TAG, "Unknown system command: %s", command->valuestring);
  443. }
  444. }
  445. } else if (strcmp(type->valuestring, "alert") == 0) {
  446. auto status = cJSON_GetObjectItem(root, "status");
  447. auto message = cJSON_GetObjectItem(root, "message");
  448. auto emotion = cJSON_GetObjectItem(root, "emotion");
  449. if (cJSON_IsString(status) && cJSON_IsString(message) && cJSON_IsString(emotion)) {
  450. Alert(status->valuestring, message->valuestring, emotion->valuestring, Lang::Sounds::P3_VIBRATION);
  451. } else {
  452. ESP_LOGW(TAG, "Alert command requires status, message and emotion");
  453. }
  454. #if CONFIG_RECEIVE_CUSTOM_MESSAGE
  455. } else if (strcmp(type->valuestring, "custom") == 0) {
  456. auto payload = cJSON_GetObjectItem(root, "payload");
  457. ESP_LOGI(TAG, "Received custom message: %s", cJSON_PrintUnformatted(root));
  458. if (cJSON_IsObject(payload)) {
  459. Schedule([this, display, payload_str = std::string(cJSON_PrintUnformatted(payload))]() {
  460. display->SetChatMessage("system", payload_str.c_str());
  461. });
  462. } else {
  463. ESP_LOGW(TAG, "Invalid custom message format: missing payload");
  464. }
  465. #endif
  466. } else {
  467. ESP_LOGW(TAG, "Unknown message type: %s", type->valuestring);
  468. }
  469. });
  470. bool protocol_started = protocol_->Start();
  471. SetDeviceState(kDeviceStateIdle);
  472. has_server_time_ = ota.HasServerTime();
  473. if (protocol_started) {
  474. std::string message = std::string(Lang::Strings::VERSION) + ota.GetCurrentVersion();
  475. display->ShowNotification(message.c_str());
  476. display->SetChatMessage("system", "");
  477. // Play the success sound to indicate the device is ready
  478. audio_service_.PlaySound(Lang::Sounds::P3_SUCCESS);
  479. }
  480. // Print heap stats
  481. SystemInfo::PrintHeapStats();
  482. // Enter the main event loop
  483. MainEventLoop();
  484. }
  485. int fist=1;
  486. void Application::OnClockTimer() {
  487. clock_ticks_++;
  488. auto display = Board::GetInstance().GetDisplay();
  489. display->UpdateStatusBar();
  490. // Print the debug info every 10 seconds
  491. if (clock_ticks_ % 10 == 0) {
  492. // SystemInfo::PrintTaskCpuUsage(pdMS_TO_TICKS(1000));
  493. // SystemInfo::PrintTaskList();
  494. SystemInfo::PrintHeapStats();
  495. if (Board::GetInstance().internetConnet==1)
  496. {
  497. if (clock_ticks_%50==0 || fist==1)
  498. {
  499. fist=0;
  500. Board::GetInstance().postAlive();
  501. }
  502. }
  503. }
  504. }
  505. // Add a async task to MainLoop
  506. void Application::Schedule(std::function<void()> callback) {
  507. {
  508. std::lock_guard<std::mutex> lock(mutex_);
  509. main_tasks_.push_back(std::move(callback));
  510. }
  511. xEventGroupSetBits(event_group_, MAIN_EVENT_SCHEDULE);
  512. }
  513. // The Main Event Loop controls the chat state and websocket connection
  514. // If other tasks need to access the websocket or chat state,
  515. // they should use Schedule to call this function
  516. void Application::MainEventLoop() {
  517. // Raise the priority of the main event loop to avoid being interrupted by background tasks (which has priority 2)
  518. vTaskPrioritySet(NULL, 3);
  519. while (true) {
  520. auto bits = xEventGroupWaitBits(event_group_, MAIN_EVENT_SCHEDULE |
  521. MAIN_EVENT_SEND_AUDIO |
  522. MAIN_EVENT_WAKE_WORD_DETECTED |
  523. MAIN_EVENT_VAD_CHANGE |
  524. MAIN_EVENT_ERROR, pdTRUE, pdFALSE, portMAX_DELAY);
  525. if (bits & MAIN_EVENT_ERROR) {
  526. SetDeviceState(kDeviceStateIdle);
  527. Alert(Lang::Strings::ERROR, last_error_message_.c_str(), "sad", Lang::Sounds::P3_EXCLAMATION);
  528. }
  529. if (bits & MAIN_EVENT_SEND_AUDIO) {
  530. while (auto packet = audio_service_.PopPacketFromSendQueue()) {
  531. if (!protocol_->SendAudio(std::move(packet))) {
  532. break;
  533. }
  534. }
  535. }
  536. if (bits & MAIN_EVENT_WAKE_WORD_DETECTED) {
  537. OnWakeWordDetected();
  538. }
  539. if (bits & MAIN_EVENT_VAD_CHANGE) {
  540. if (device_state_ == kDeviceStateListening) {
  541. auto led = Board::GetInstance().GetLed();
  542. led->OnStateChanged();
  543. }
  544. }
  545. if (bits & MAIN_EVENT_SCHEDULE) {
  546. std::unique_lock<std::mutex> lock(mutex_);
  547. auto tasks = std::move(main_tasks_);
  548. lock.unlock();
  549. for (auto& task : tasks) {
  550. task();
  551. }
  552. }
  553. }
  554. }
  555. void Application::OnWakeWordDetected() {
  556. if (!protocol_) {
  557. return;
  558. }
  559. if (device_state_ == kDeviceStateIdle) {
  560. audio_service_.EncodeWakeWord();
  561. if (!protocol_->IsAudioChannelOpened()) {
  562. SetDeviceState(kDeviceStateConnecting);
  563. if (!protocol_->OpenAudioChannel()) {
  564. audio_service_.EnableWakeWordDetection(true);
  565. return;
  566. }
  567. }
  568. auto wake_word = audio_service_.GetLastWakeWord();
  569. ESP_LOGI(TAG, "Wake word detected: %s", wake_word.c_str());
  570. #if CONFIG_USE_AFE_WAKE_WORD || CONFIG_USE_CUSTOM_WAKE_WORD
  571. // Encode and send the wake word data to the server
  572. while (auto packet = audio_service_.PopWakeWordPacket()) {
  573. protocol_->SendAudio(std::move(packet));
  574. }
  575. // Set the chat state to wake word detected
  576. protocol_->SendWakeWordDetected(wake_word);
  577. SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
  578. #else
  579. SetListeningMode(aec_mode_ == kAecOff ? kListeningModeAutoStop : kListeningModeRealtime);
  580. // Play the pop up sound to indicate the wake word is detected
  581. audio_service_.PlaySound(Lang::Sounds::P3_POPUP);
  582. #endif
  583. } else if (device_state_ == kDeviceStateSpeaking) {
  584. AbortSpeaking(kAbortReasonWakeWordDetected);
  585. } else if (device_state_ == kDeviceStateActivating) {
  586. SetDeviceState(kDeviceStateIdle);
  587. }
  588. }
  589. void Application::AbortSpeaking(AbortReason reason) {
  590. ESP_LOGI(TAG, "Abort speaking");
  591. aborted_ = true;
  592. protocol_->SendAbortSpeaking(reason);
  593. }
  594. void Application::SetListeningMode(ListeningMode mode) {
  595. listening_mode_ = mode;
  596. SetDeviceState(kDeviceStateListening);
  597. }
  598. void Application::SetDeviceState(DeviceState state) {
  599. // 如果倒计时正在进行中,只允许切换到其他倒计时状态或idle状态
  600. if (is_countdown_active_ &&
  601. state != kDeviceStateCountdown10Min &&
  602. state != kDeviceStateCountdown15Min &&
  603. state != kDeviceStateCountdown20Min &&
  604. state != kDeviceStateCountdownEnd &&
  605. state != kDeviceStateIdle) {
  606. ESP_LOGW(TAG, "Countdown active, ignoring state change to: %s", STATE_STRINGS[state]);
  607. return;
  608. }
  609. if (device_state_ == state) {
  610. return;
  611. }
  612. clock_ticks_ = 0;
  613. auto previous_state = device_state_;
  614. device_state_ = state;
  615. ESP_LOGI(TAG, "STATE: %s", STATE_STRINGS[device_state_]);
  616. // Send the state change event
  617. DeviceStateEventManager::GetInstance().PostStateChangeEvent(previous_state, state);
  618. auto& board = Board::GetInstance();
  619. auto display = board.GetDisplay();
  620. auto led = board.GetLed();
  621. led->OnStateChanged();
  622. auto current_time = std::chrono::system_clock::now();
  623. switch (state) {
  624. case kDeviceStateUnknown:
  625. case kDeviceStateIdle:
  626. // 停止倒计时定时器
  627. if (countdown_timer_handle_ != nullptr) {
  628. esp_timer_stop(countdown_timer_handle_);
  629. }
  630. // 重置倒计时状态
  631. is_countdown_active_ = false;
  632. countdown_seconds_ = 0;
  633. last_countdown_state_ = kDeviceStateUnknown;
  634. // 显示主界面
  635. display->ShowHomeScreen();
  636. display->SetStatus(Lang::Strings::STANDBY);
  637. display->UpdateHomeTime();
  638. last_home_update_time_ = current_time;
  639. // 音频处理设置
  640. audio_service_.EnableVoiceProcessing(false);
  641. audio_service_.EnableWakeWordDetection(true);
  642. break;
  643. case kDeviceStateConnecting:
  644. // 如果是倒计时期间进入连接状态,先停止倒计时
  645. if (is_countdown_active_) {
  646. if (countdown_timer_handle_ != nullptr) {
  647. esp_timer_stop(countdown_timer_handle_);
  648. }
  649. is_countdown_active_ = false;
  650. countdown_seconds_ = 0;
  651. }
  652. display->ShowHomeScreen();
  653. display->SetStatus(Lang::Strings::CONNECTING);
  654. display->UpdateHomeTime();
  655. last_home_update_time_ = current_time;
  656. audio_service_.EnableVoiceProcessing(false);
  657. audio_service_.EnableWakeWordDetection(true);
  658. break;
  659. case kDeviceStateListening:
  660. case kDeviceStateSpeaking:
  661. // 如果是倒计时期间,不允许进入对话状态
  662. if (is_countdown_active_) {
  663. ESP_LOGW(TAG, "Cannot enter conversation during countdown");
  664. device_state_ = last_countdown_state_; // 恢复到倒计时状态
  665. return;
  666. }
  667. // 对话状态隐藏主界面,显示原有UI
  668. display->HideHomeScreen();
  669. if (state == kDeviceStateListening) {
  670. display->SetStatus(Lang::Strings::LISTENING);
  671. display->SetEmotion("neutral");
  672. if (!audio_service_.IsAudioProcessorRunning()) {
  673. protocol_->SendStartListening(listening_mode_);
  674. audio_service_.EnableVoiceProcessing(true);
  675. audio_service_.EnableWakeWordDetection(false);
  676. }
  677. } else if (state == kDeviceStateSpeaking) {
  678. display->SetStatus(Lang::Strings::SPEAKING);
  679. if (listening_mode_ != kListeningModeRealtime) {
  680. audio_service_.EnableVoiceProcessing(false);
  681. #if CONFIG_USE_AFE_WAKE_WORD
  682. audio_service_.EnableWakeWordDetection(true);
  683. #else
  684. audio_service_.EnableWakeWordDetection(false);
  685. #endif
  686. }
  687. audio_service_.ResetDecoder();
  688. }
  689. break;
  690. case kDeviceStateCountdown10Min:
  691. is_countdown_active_ = true;
  692. last_countdown_state_ = state;
  693. countdown_seconds_ = 10 * 60; // 10分钟 = 600秒
  694. // 屏幕顺时针旋转90度,显示倒计时界面
  695. display->ShowCountdownScreen(countdown_seconds_, 270);
  696. display->SetStatus("倒计时10分钟");
  697. // 启动倒计时定时器(每秒触发一次)
  698. if (countdown_timer_handle_ != nullptr) {
  699. esp_timer_start_periodic(countdown_timer_handle_, 1000000); // 1秒 = 1000000微秒
  700. }
  701. // 禁用语音功能
  702. audio_service_.EnableVoiceProcessing(false);
  703. audio_service_.EnableWakeWordDetection(false);
  704. ESP_LOGI(TAG, "Countdown 10 minutes started");
  705. break;
  706. case kDeviceStateCountdown15Min:
  707. is_countdown_active_ = true;
  708. last_countdown_state_ = state;
  709. countdown_seconds_ = 15 * 60; // 15分钟 = 900秒
  710. // 屏幕顺时针旋转180度,显示倒计时界面
  711. display->ShowCountdownScreen(countdown_seconds_, 180);
  712. display->SetStatus("倒计时15分钟");
  713. // 启动倒计时定时器(每秒触发一次)
  714. if (countdown_timer_handle_ != nullptr) {
  715. esp_timer_start_periodic(countdown_timer_handle_, 1000000); // 1秒 = 1000000微秒
  716. }
  717. // 禁用语音功能
  718. audio_service_.EnableVoiceProcessing(false);
  719. audio_service_.EnableWakeWordDetection(false);
  720. ESP_LOGI(TAG, "Countdown 15 minutes started");
  721. break;
  722. case kDeviceStateCountdown20Min:
  723. is_countdown_active_ = true;
  724. last_countdown_state_ = state;
  725. countdown_seconds_ = 20 * 60; // 20分钟 = 1200秒
  726. // 屏幕顺时针旋转270度,显示倒计时界面
  727. display->ShowCountdownScreen(countdown_seconds_, 90);
  728. display->SetStatus("倒计时20分钟");
  729. // 启动倒计时定时器(每秒触发一次)
  730. if (countdown_timer_handle_ != nullptr) {
  731. esp_timer_start_periodic(countdown_timer_handle_, 1000000); // 1秒 = 1000000微秒
  732. }
  733. // 禁用语音功能
  734. audio_service_.EnableVoiceProcessing(false);
  735. audio_service_.EnableWakeWordDetection(false);
  736. ESP_LOGI(TAG, "Countdown 20 minutes started");
  737. break;
  738. case kDeviceStateCountdownEnd:
  739. ESP_LOGI(TAG, "Countdown finished, waiting for flip to return to idle");
  740. break;
  741. default:
  742. break;
  743. }
  744. }
  745. void Application::Reboot() {
  746. ESP_LOGI(TAG, "Rebooting...");
  747. esp_restart();
  748. }
  749. void Application::WakeWordInvoke(const std::string& wake_word) {
  750. if (device_state_ == kDeviceStateIdle) {
  751. ToggleChatState();
  752. Schedule([this, wake_word]() {
  753. if (protocol_) {
  754. protocol_->SendWakeWordDetected(wake_word);
  755. }
  756. });
  757. } else if (device_state_ == kDeviceStateSpeaking) {
  758. Schedule([this]() {
  759. AbortSpeaking(kAbortReasonNone);
  760. });
  761. } else if (device_state_ == kDeviceStateListening) {
  762. Schedule([this]() {
  763. if (protocol_) {
  764. protocol_->CloseAudioChannel();
  765. }
  766. });
  767. }
  768. }
  769. bool Application::CanEnterSleepMode() {
  770. if (device_state_ != kDeviceStateIdle) {
  771. return false;
  772. }
  773. if (protocol_ && protocol_->IsAudioChannelOpened()) {
  774. return false;
  775. }
  776. if (!audio_service_.IsIdle()) {
  777. return false;
  778. }
  779. // Now it is safe to enter sleep mode
  780. return true;
  781. }
  782. void Application::SendMcpMessage(const std::string& payload) {
  783. Schedule([this, payload]() {
  784. if (protocol_) {
  785. protocol_->SendMcpMessage(payload);
  786. }
  787. });
  788. }
  789. void Application::SetAecMode(AecMode mode) {
  790. aec_mode_ = mode;
  791. Schedule([this]() {
  792. auto& board = Board::GetInstance();
  793. auto display = board.GetDisplay();
  794. switch (aec_mode_) {
  795. case kAecOff:
  796. audio_service_.EnableDeviceAec(false);
  797. display->ShowNotification(Lang::Strings::RTC_MODE_OFF);
  798. break;
  799. case kAecOnServerSide:
  800. audio_service_.EnableDeviceAec(false);
  801. display->ShowNotification(Lang::Strings::RTC_MODE_ON);
  802. break;
  803. case kAecOnDeviceSide:
  804. audio_service_.EnableDeviceAec(true);
  805. display->ShowNotification(Lang::Strings::RTC_MODE_ON);
  806. break;
  807. }
  808. // If the AEC mode is changed, close the audio channel
  809. if (protocol_ && protocol_->IsAudioChannelOpened()) {
  810. protocol_->CloseAudioChannel();
  811. }
  812. });
  813. }
  814. void Application::PlaySound(const std::string_view& sound) {
  815. audio_service_.PlaySound(sound);
  816. }
  817. // 添加倒计时定时器回调函数
  818. void Application::OnCountdownTimer() {
  819. if (!is_countdown_active_ || countdown_seconds_ <= 0) {
  820. return;
  821. }
  822. // 减少倒计时时间
  823. countdown_seconds_--;
  824. // 更新显示
  825. auto& board = Board::GetInstance();
  826. auto display = board.GetDisplay();
  827. display->UpdateCountdown(countdown_seconds_);
  828. // 检查倒计时是否结束
  829. if (countdown_seconds_ <= 0) {
  830. ESP_LOGI(TAG, "Countdown finished!");
  831. if (countdown_timer_handle_ != nullptr) {
  832. esp_timer_stop(countdown_timer_handle_);
  833. }
  834. // 计时结束,显示完成界面
  835. display->ShowCountdownEndScreen();
  836. display->SetStatus("计时完成");
  837. // 重置倒计时状态
  838. is_countdown_active_ = false;
  839. countdown_seconds_ = 0;
  840. // 注意:这里不自动返回空闲状态
  841. // 等待用户翻转设备(分钟数为0)触发返回空闲状态
  842. ESP_LOGI(TAG, "Countdown finished, waiting for flip to return to idle");
  843. } else {
  844. ESP_LOGD(TAG, "Countdown: %d seconds remaining", countdown_seconds_);
  845. }
  846. }