2525#define MAX_CLIENT_ID_LEN 40
2626#define MAX_MAC_ADDR_LEN 20
2727#define WEBSOCKET_RECONNECT_DELAY_MS 5000
28- #define WEBSOCKET_CONNECTION_TIMEOUT_MS 50000
28+ #define WEBSOCKET_CONNECTION_TIMEOUT_MS 5000
2929#define NETWORK_CHECK_DELAY_MS 500
3030#define RETRY_DELAY_BASE_MS 1000
3131#define RETRY_DELAY_INCREMENT_MS 200
@@ -50,7 +50,9 @@ xiaozhi_app_t g_app =
5050 .wakeword_initialized_session = 0 ,
5151 .multi_turn_conversation_enabled = RT_TRUE,
5252 .tts_sentence_end_timer = RT_NULL,
53- .tts_stop_workqueue = RT_NULL};
53+ .tts_stop_workqueue = RT_NULL,
54+ .pending_listen_start = RT_FALSE,
55+ .pending_play_wake_sound = RT_FALSE};
5456
5557#include " ui/xiaozhi_ui.h"
5658#include " iot/iot_c_api.h"
@@ -73,7 +75,11 @@ void xz_wakeword_detected_callback(const char *wake_word, float confidence)
7375 {
7476 LOG_D (" Wake word detected during speaking - interrupting" );
7577 xz_speaker (0 );
76- g_app.state = kDeviceStateIdle ;
78+ rt_bool_t in_listening = (g_app.state == kDeviceStateListening ) || xz_mic_is_enabled ();
79+ if (!in_listening && g_app.state != kDeviceStateSpeaking )
80+ {
81+ g_app.state = kDeviceStateIdle ;
82+ }
7783 }
7884 else if (g_app.state == kDeviceStateListening )
7985 {
@@ -90,6 +96,8 @@ void xz_wakeword_detected_callback(const char *wake_word, float confidence)
9096 LOG_D (" Wake word detected but not connected, initiating connection..." );
9197 xiaozhi_ui_chat_status (" 连接中" );
9298 xiaozhi_ui_chat_output (" 正在连接..." );
99+ g_app.pending_listen_start = RT_TRUE;
100+ g_app.pending_play_wake_sound = RT_FALSE;
93101 reconnect_websocket ();
94102
95103 /* Use a shorter wait time with periodic checks for better responsiveness */
@@ -109,9 +117,24 @@ void xz_wakeword_detected_callback(const char *wake_word, float confidence)
109117 }
110118 }
111119
120+ /* Ensure session is ready before starting listening */
121+ if (g_app.ws .session_id [0 ] == ' \0 ' )
122+ {
123+ LOG_D (" Session not ready after wake word detection, deferring listen start" );
124+ g_app.pending_listen_start = RT_TRUE;
125+ g_app.pending_play_wake_sound = RT_FALSE;
126+ if (xz_wakeword_is_enabled ())
127+ {
128+ xz_wakeword_stop ();
129+ }
130+ return ;
131+ }
132+
112133 /* Start listening mode */
113134 LOG_D (" Starting conversation after wake word detection" );
114135 g_app.state = kDeviceStateListening ;
136+ g_app.pending_listen_start = RT_FALSE;
137+ g_app.pending_play_wake_sound = RT_FALSE;
115138
116139 /* Completely stop wake word detection during conversation to avoid mic0 conflict */
117140 if (xz_wakeword_is_enabled ())
@@ -124,11 +147,24 @@ void xz_wakeword_detected_callback(const char *wake_word, float confidence)
124147 xz_mic (1 );
125148
126149 /* Send listen start message to server */
127- ws_send_listen_start (&g_app.ws .clnt , g_app.ws .session_id , kListeningModeAutoStop );
128-
129- /* Update UI */
130- xiaozhi_ui_chat_status (" 聆听中" );
131- xiaozhi_ui_chat_output (" 聆听中..." );
150+ if (ws_send_listen_start (&g_app.ws .clnt , g_app.ws .session_id , kListeningModeAutoStop ))
151+ {
152+ /* Update UI */
153+ xiaozhi_ui_chat_status (" 聆听中" );
154+ xiaozhi_ui_chat_output (" 聆听中..." );
155+ }
156+ else
157+ {
158+ LOG_W (" Listen start failed after wake word detection" );
159+ g_app.state = kDeviceStateIdle ;
160+ xz_mic (0 );
161+ xiaozhi_ui_chat_status (" 就绪" );
162+ xiaozhi_ui_chat_output (" 就绪" );
163+ if (!xz_wakeword_is_enabled ())
164+ {
165+ xz_wakeword_start ();
166+ }
167+ }
132168}
133169
134170/* TTS sentence end timeout handler */
@@ -349,6 +385,9 @@ void xz_event_thread_entry(void *param)
349385 {
350386 if (!g_app.ws .is_connected )
351387 {
388+ g_app.pending_listen_start = RT_TRUE;
389+ g_app.pending_play_wake_sound = RT_TRUE;
390+
352391 /* Check if reconnecting */
353392 if (g_app.websocket_reconnect_flag == 1 )
354393 {
@@ -378,6 +417,9 @@ void xz_event_thread_entry(void *param)
378417 {
379418 LOG_D (" Starting listening mode - press once to talk\n " );
380419
420+ g_app.pending_listen_start = RT_FALSE;
421+ g_app.pending_play_wake_sound = RT_FALSE;
422+
381423 /* Play wake sound for button wake-up */
382424 xz_play_wake_sound ();
383425
@@ -678,6 +720,9 @@ err_t my_wsapp_fn(int code, char *buf, size_t len)
678720 LOG_D (" Stopped speaker due to disconnection\n " );
679721 }
680722
723+ /* Ensure mic is closed when entering sleep */
724+ xz_mic (0 );
725+
681726 /* Stop TTS sentence end timer if running */
682727 if (g_app.tts_sentence_end_timer )
683728 {
@@ -1032,7 +1077,11 @@ void Message_handle(const uint8_t *data, uint16_t len)
10321077 g_app.ws .sample_rate = cJSON_GetObjectItem (audio_param, " sample_rate" )->valueint ;
10331078 g_app.ws .frame_duration = cJSON_GetObjectItem (audio_param, " frame_duration" )->valueint ;
10341079 strncpy (g_app.ws .session_id , session_id, 9 );
1035- g_app.state = kDeviceStateIdle ;
1080+ rt_bool_t in_listening = (g_app.state == kDeviceStateListening ) || xz_mic_is_enabled ();
1081+ if (!in_listening && g_app.state != kDeviceStateSpeaking )
1082+ {
1083+ g_app.state = kDeviceStateIdle ;
1084+ }
10361085 xz_ws_audio_init ();
10371086
10381087 /* Initialize only on first connection */
@@ -1051,26 +1100,38 @@ void Message_handle(const uint8_t *data, uint16_t len)
10511100 /* Resend device info after each reconnect */
10521101 send_iot_descriptors ();
10531102 send_iot_states ();
1054- xiaozhi_ui_chat_status (" 待命中" );
1055- xiaozhi_ui_chat_output (" " );
1056- xiaozhi_ui_update_emoji (" neutral" );
1057- LOG_I (" Waiting...\n " );
1103+ rt_bool_t pending_listen = g_app.pending_listen_start ;
1104+ if (!pending_listen && !in_listening)
1105+ {
1106+ xiaozhi_ui_chat_status (" 待命中" );
1107+ xiaozhi_ui_chat_output (" " );
1108+ xiaozhi_ui_update_emoji (" neutral" );
1109+ LOG_I (" Waiting...\n " );
1110+ }
1111+ else
1112+ {
1113+ LOG_I (" Pending wake-up detected on hello, preparing to listen\n " );
1114+ }
10581115
10591116 /* Initialize wake word detection once */
10601117 if (!g_app.wakeword_initialized_session )
10611118 {
10621119 LOG_I (" Initializing wake word detection..." );
10631120 if (xz_wakeword_init () == 0 )
10641121 {
1065- /* Start detection after initialization - it should run continuously */
1066- if (xz_wakeword_start () == 0 )
1067- {
1068- LOG_D (" Wake word detection started successfully" );
1069- g_app.wakeword_initialized_session = 1 ;
1070- }
1071- else
1122+ g_app.wakeword_initialized_session = 1 ;
1123+
1124+ /* Start detection only if no pending listen */
1125+ if (!pending_listen && !in_listening)
10721126 {
1073- LOG_E (" Failed to start wake word detection" );
1127+ if (xz_wakeword_start () == 0 )
1128+ {
1129+ LOG_D (" Wake word detection started successfully" );
1130+ }
1131+ else
1132+ {
1133+ LOG_E (" Failed to start wake word detection" );
1134+ }
10741135 }
10751136 }
10761137 else
@@ -1080,16 +1141,72 @@ void Message_handle(const uint8_t *data, uint16_t len)
10801141 }
10811142 else
10821143 {
1083- /* Just ensure wake word is running */
1084- if (!xz_wakeword_is_enabled ())
1144+ if (pending_listen)
10851145 {
1086- LOG_D (" Restarting wake word detection" );
1087- xz_wakeword_start ();
1146+ if (xz_wakeword_is_enabled ())
1147+ {
1148+ LOG_D (" Stopping wake word detection before pending listen start" );
1149+ xz_wakeword_stop ();
1150+ }
1151+ }
1152+ else
1153+ {
1154+ /* Just ensure wake word is running */
1155+ if (!xz_wakeword_is_enabled () && !in_listening)
1156+ {
1157+ LOG_D (" Restarting wake word detection" );
1158+ xz_wakeword_start ();
1159+ }
1160+ }
1161+ }
1162+
1163+ if (pending_listen)
1164+ {
1165+ if (g_app.state == kDeviceStateListening || g_app.state == kDeviceStateSpeaking )
1166+ {
1167+ g_app.pending_listen_start = RT_FALSE;
1168+ g_app.pending_play_wake_sound = RT_FALSE;
1169+ return ;
1170+ }
1171+
1172+ g_app.pending_listen_start = RT_FALSE;
1173+
1174+ if (g_app.pending_play_wake_sound )
1175+ {
1176+ xz_play_wake_sound ();
1177+ }
1178+ g_app.pending_play_wake_sound = RT_FALSE;
1179+
1180+ if (xz_wakeword_is_enabled ())
1181+ {
1182+ xz_wakeword_stop ();
1183+ }
1184+
1185+ g_app.state = kDeviceStateListening ;
1186+ xz_mic (1 );
1187+ if (ws_send_listen_start (&g_app.ws .clnt , g_app.ws .session_id , kListeningModeAutoStop ))
1188+ {
1189+ xiaozhi_ui_chat_status (" 聆听中" );
1190+ xiaozhi_ui_chat_output (" 聆听中..." );
1191+ }
1192+ else
1193+ {
1194+ LOG_W (" Listen start failed after hello, falling back to idle" );
1195+ g_app.state = kDeviceStateIdle ;
1196+ xz_mic (0 );
1197+ xiaozhi_ui_chat_status (" 就绪" );
1198+ xiaozhi_ui_chat_output (" 就绪" );
1199+ if (!xz_wakeword_is_enabled ())
1200+ {
1201+ xz_wakeword_start ();
1202+ }
10881203 }
10891204 }
10901205 }
10911206 else if (strcmp (type, " goodbye" ) == 0 )
10921207 {
1208+ /* Ensure mic is closed when entering sleep */
1209+ xz_mic (0 );
10931210 xiaozhi_ui_chat_status (" 休眠中" );
10941211 xiaozhi_ui_chat_output (" 等待唤醒" );
10951212 xiaozhi_ui_update_emoji (" sleepy" );
0 commit comments