1818static rt_thread_t xiaozhi_tid = RT_NULL ;
1919static const char * client_id = "af7ac552-9991-4b31-b660-683b210ae95f" ;
2020static uint8_t WEBSOCKET_RECONNECT_FLAG = 0 ;
21+ static uint8_t iot_initialized = 0 ; // 添加IoT初始化标志
22+ static uint32_t last_reconnect_time = 0 ; // 添加重连时间戳,防止频繁重连
2123static const char * mode_str [] = {"auto" , "manual" , "realtime" };
2224static char mac_address_string [20 ] = {0 };
2325static char client_id_string [40 ] = {0 };
@@ -112,44 +114,67 @@ void xz_button_thread_entry(void *param)
112114 RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR ,
113115 RT_WAITING_FOREVER , & evt );
114116
115- if (g_state == kDeviceStateUnknown || !g_xz_ws .is_connected )
117+ /* 优雅的按键处理:先检查连接状态,再处理具体事件 */
118+ if (evt & BUTTON_EVENT_PRESSED )
116119 {
117- LOG_I ("Wake up...\n" );
118- if (evt & BUTTON_EVENT_PRESSED )
120+ if (g_state == kDeviceStateUnknown || !g_xz_ws .is_connected )
119121 {
122+ /* 检查是否正在重连中 */
123+ if (WEBSOCKET_RECONNECT_FLAG == 1 )
124+ {
125+ LOG_D ("Reconnection already in progress, ignoring button press\n" );
126+ xiaozhi_ui_chat_status (" Connecting" );
127+ xiaozhi_ui_chat_output ("Still connecting..." );
128+ continue ;
129+ }
130+
131+ LOG_I ("Device not connected, initiating wake up...\n" );
120132 xiaozhi_ui_chat_status (" Connecting" );
133+ xiaozhi_ui_chat_output ("Connecting to Xiaozhi..." );
121134 reconnect_websocket ();
122135 }
123- }
124- else
125- {
126- if (evt & BUTTON_EVENT_PRESSED )
136+ else
127137 {
138+ /* 设备已连接,处理具体功能 */
128139 if (g_state == kDeviceStateSpeaking )
129140 {
130- LOG_I ("Speaking aborted\n" );
141+ LOG_I ("Speaking aborted by user \n" );
131142 xz_speaker (0 );
132143 }
144+
133145 if (g_state != kDeviceStateListening )
134146 {
135147 ws_send_listen_start (& g_xz_ws .clnt , g_xz_ws .session_id ,
136148 kListeningModeAutoStop );
137- LOG_I ("Listening... \n" );
149+ LOG_I ("Starting listening mode \n" );
138150 xiaozhi_ui_chat_status (" Listening" );
151+ xiaozhi_ui_chat_output ("Listening..." );
139152 xz_mic (1 );
140153 g_state = kDeviceStateListening ;
141154 }
142- }
143- if (evt & BUTTON_EVENT_RELEASED )
144- {
145- if (g_state == kDeviceStateListening )
155+ else
146156 {
147- ws_send_listen_stop (& g_xz_ws .clnt , g_xz_ws .session_id );
148- xz_mic (0 );
149- g_state = kDeviceStateIdle ;
157+ LOG_D ("Already in listening mode\n" );
150158 }
151159 }
152160 }
161+ else if (evt & BUTTON_EVENT_RELEASED )
162+ {
163+ /* 只在设备连接且正在监听时处理释放事件 */
164+ if (g_xz_ws .is_connected && g_state == kDeviceStateListening )
165+ {
166+ ws_send_listen_stop (& g_xz_ws .clnt , g_xz_ws .session_id );
167+ xz_mic (0 );
168+ g_state = kDeviceStateIdle ;
169+ xiaozhi_ui_chat_status (" On standby" );
170+ xiaozhi_ui_chat_output ("Ready" );
171+ LOG_D ("Listening stopped\n" );
172+ }
173+ else if (!g_xz_ws .is_connected )
174+ {
175+ LOG_D ("Button released but device not connected, ignoring\n" );
176+ }
177+ }
153178 }
154179}
155180
@@ -273,6 +298,9 @@ err_t my_wsapp_fn(int code, char *buf, size_t len)
273298 LOG_I ("WebSocket closed\n" );
274299 g_xz_ws .is_connected = 0 ;
275300 g_state = kDeviceStateUnknown ;
301+
302+ /* 清除重连时间戳,允许立即重连 */
303+ last_reconnect_time = 0 ;
276304 break ;
277305 case WS_TEXT :
278306 Message_handle ((const uint8_t * )buf , len );
@@ -291,26 +319,62 @@ void reconnect_websocket(void)
291319{
292320 err_t result ;
293321 uint32_t retry = 10 ;
294-
322+ uint32_t current_time = rt_tick_get ();
323+
324+ /* 防止频繁重连:距离上次重连至少5秒 */
325+ if (WEBSOCKET_RECONNECT_FLAG == 1 )
326+ {
327+ LOG_D ("Reconnection already in progress, ignoring duplicate request\n" );
328+ return ;
329+ }
330+
331+ if (current_time - last_reconnect_time < rt_tick_from_millisecond (5000 ))
332+ {
333+ LOG_D ("Reconnection too frequent, ignoring request\n" );
334+ return ;
335+ }
336+
337+ last_reconnect_time = current_time ;
338+
295339 /* Set reconnect flag to ignore disconnect callbacks during reconnection */
296340 WEBSOCKET_RECONNECT_FLAG = 1 ;
297-
341+
298342 while (retry -- > 0 )
299343 {
300- /* Only close if previously initialized */
301- if (g_xz_ws .clnt .pcb != RT_NULL || g_xz_ws . is_connected )
344+ /* 检查 WebSocket 的 TCP 控制块状态,避免在不合适时机重连 */
345+ if (g_xz_ws .clnt .pcb != RT_NULL )
302346 {
303- wsock_close (& g_xz_ws .clnt , WSOCK_RESULT_LOCAL_ABORT , ERR_OK );
304- /* Give system time to clean up resources after close */
305- rt_thread_mdelay (100 );
347+ LOG_D ("WebSocket PCB exists, current state: %d\n" , g_xz_ws .clnt .pcb -> state );
348+
349+ /* 只有在状态异常时才进行清理 */
350+ if (g_xz_ws .clnt .pcb -> state != CLOSED && g_xz_ws .clnt .pcb -> state != CLOSE_WAIT )
351+ {
352+ LOG_I ("Cleaning up WebSocket connection in state %d\n" , g_xz_ws .clnt .pcb -> state );
353+
354+ /* 先重置连接标志,避免重连回调干扰 */
355+ g_xz_ws .is_connected = 0 ;
356+
357+ /* 尝试正常关闭 */
358+ err_t close_result = wsock_close (& g_xz_ws .clnt , WSOCK_RESULT_LOCAL_ABORT , ERR_OK );
359+ LOG_D ("wsock_close result: %d\n" , close_result );
360+
361+ /* 给系统充分时间清理资源 */
362+ rt_thread_mdelay (2000 );
363+
364+ /* 检查是否成功关闭 */
365+ if (g_xz_ws .clnt .pcb != RT_NULL && g_xz_ws .clnt .pcb -> state != CLOSED )
366+ {
367+ LOG_W ("WebSocket PCB still exists after close, forcing cleanup\n" );
368+ /* 强制清理,但要小心 */
369+ rt_thread_mdelay (1000 );
370+ memset (& g_xz_ws .clnt , 0 , sizeof (wsock_state_t ));
371+ }
372+ }
306373 }
307374
308- /* Reset connection state flag */
375+ /* 确保连接状态重置 */
309376 g_xz_ws .is_connected = 0 ;
310377
311- /* Clear wsock_state_t struct to ensure wsock_init can initialize properly */
312- memset (& g_xz_ws .clnt , 0 , sizeof (wsock_state_t ));
313-
314378 if (!g_xz_ws .sem )
315379 {
316380 g_xz_ws .sem = rt_sem_create ("xz_ws" , 0 , RT_IPC_FLAG_FIFO );
@@ -322,16 +386,21 @@ void reconnect_websocket(void)
322386 }
323387
324388 char * client_id = get_client_id ();
389+
390+ /* 确保WebSocket结构体完全清理 */
391+ memset (& g_xz_ws .clnt , 0 , sizeof (wsock_state_t ));
392+
325393 wsock_init (& g_xz_ws .clnt , 1 , 1 , my_wsapp_fn );
326394 result = wsock_connect (& g_xz_ws .clnt , MAX_WSOCK_HDR_LEN ,
327395 XIAOZHI_HOST , XIAOZHI_WSPATH ,
328396 LWIP_IANA_PORT_HTTPS , XIAOZHI_TOKEN , NULL ,
329397 "Protocol-Version: 1\r\nDevice-Id: %s\r\nClient-Id: %s\r\n" ,
330398 get_mac_address (), client_id );
331- LOG_I ("Web socket connection %d \n" , result );
399+ LOG_I ("Web socket connection attempt %d: %d \n" , 10 - retry , result );
332400 if (result == 0 )
333401 {
334- if (rt_sem_take (g_xz_ws .sem , 10000 ) == RT_EOK )
402+ /* 使用更长的超时时间,参考优秀实践 */
403+ if (rt_sem_take (g_xz_ws .sem , 50000 ) == RT_EOK )
335404 {
336405 if (g_xz_ws .is_connected )
337406 {
@@ -340,28 +409,45 @@ void reconnect_websocket(void)
340409 result = wsock_write (& g_xz_ws .clnt , HELLO_MESSAGE ,
341410 strlen (HELLO_MESSAGE ), OPCODE_TEXT );
342411 LOG_I ("Web socket write %d\r\n" , result );
343- return ;
412+ if (result == ERR_OK )
413+ {
414+ LOG_I ("WebSocket reconnection successful\n" );
415+ return ;
416+ }
417+ else
418+ {
419+ LOG_E ("Failed to send hello message: %d, retrying...\n" , result );
420+ }
344421 }
345422 else
346423 {
347- LOG_E ("Web socket disconnected \n" );
424+ LOG_E ("Web socket connection established but not properly initialized, retrying... \n" );
348425 }
349426 }
350427 else
351428 {
352- LOG_E ("Web socket connected timeout\n" );
429+ LOG_E ("Web socket connection timeout after 50 seconds, retrying... \n" );
353430 }
354431 }
355432 else
356433 {
357- LOG_E ("Web socket connect failed: %d\n" , result );
358- rt_thread_mdelay (1000 );
434+ LOG_E ("Web socket connect failed: %d, retry %d remaining...\n" , result , retry );
359435 }
436+
437+ /* 智能重连间隔:失败次数越多,间隔越长 */
438+ uint32_t delay_ms = 2000 + (10 - retry ) * 500 ; // 2s-6s递增
439+ LOG_D ("Waiting %d ms before next retry...\n" , delay_ms );
440+ rt_thread_mdelay (delay_ms );
360441 }
361-
442+
362443 /* Reconnection failed, clear reconnect flag */
363444 WEBSOCKET_RECONNECT_FLAG = 0 ;
364445 LOG_E ("Web socket reconnect failed after all retries\n" );
446+
447+ // 重置状态
448+ g_state = kDeviceStateUnknown ;
449+ xiaozhi_ui_chat_status (" Connection Failed" );
450+ xiaozhi_ui_chat_output ("Please try again" );
365451}
366452
367453void xz_ws_audio_init (void )
@@ -481,6 +567,21 @@ void Message_handle(const uint8_t *data, uint16_t len)
481567 strncpy (g_xz_ws .session_id , session_id , 9 );
482568 g_state = kDeviceStateIdle ;
483569 xz_ws_audio_init ();
570+
571+ /* 优雅处理IoT初始化:确保只在首次连接时初始化 */
572+ if (!iot_initialized )
573+ {
574+ LOG_I ("Initializing IoT devices for first time\n" );
575+ extern void iot_initialize (void );
576+ iot_initialize ();
577+ iot_initialized = 1 ;
578+ }
579+ else
580+ {
581+ LOG_D ("IoT already initialized, skipping repeated initialization\n" );
582+ }
583+
584+ /* 每次重连后都需要重新发送设备信息 */
484585 send_iot_descriptors ();
485586 send_iot_states ();
486587 xiaozhi_ui_chat_status (" On standby" );
@@ -701,48 +802,92 @@ int http_xiaozhi_data_parse_ws(char *json_data)
701802void xiaozhi_ws_connect (void )
702803{
703804 err_t err ;
704- while (1 )
805+ uint32_t retry = 10 ;
806+
807+ while (retry -- > 0 )
705808 {
706- wsock_close (& g_xz_ws .clnt , WSOCK_RESULT_LOCAL_ABORT , ERR_OK );
809+ /* 检查网络连接状态 */
810+ if (!check_internet_access ())
811+ {
812+ LOG_I ("Waiting internet ready... (%d retries remaining)\n" , retry );
813+ xiaozhi_ui_chat_status (" Waiting Network" );
814+ xiaozhi_ui_chat_output ("Checking internet connection..." );
815+ rt_thread_mdelay (2000 );
816+ continue ;
817+ }
818+
819+ /* 确保WebSocket处于正确的状态 */
820+ if (g_xz_ws .clnt .pcb != RT_NULL && g_xz_ws .clnt .pcb -> state != CLOSED )
821+ {
822+ LOG_D ("Cleaning up existing WebSocket connection\n" );
823+ wsock_close (& g_xz_ws .clnt , WSOCK_RESULT_LOCAL_ABORT , ERR_OK );
824+ rt_thread_mdelay (1000 );
825+ }
826+
707827 if (!g_xz_ws .sem )
708828 {
709829 g_xz_ws .sem = rt_sem_create ("xz_ws" , 0 , RT_IPC_FLAG_FIFO );
710830 }
831+
711832 wsock_init (& g_xz_ws .clnt , 1 , 1 , my_wsapp_fn );
712833 char * client_id = get_client_id ();
713834 err = wsock_connect (& g_xz_ws .clnt , MAX_WSOCK_HDR_LEN ,
714835 XIAOZHI_HOST , XIAOZHI_WSPATH ,
715836 LWIP_IANA_PORT_HTTPS , XIAOZHI_TOKEN , NULL ,
716837 "Protocol-Version: 1\r\nDevice-Id: %s\r\nClient-Id: %s\r\n" ,
717838 get_mac_address (), client_id );
718- LOG_I ("Web socket connection %d\r\n" , err );
839+ LOG_I ("Web socket connection attempt %d: %d\n" , 10 - retry , err );
840+
719841 if (err == 0 )
720842 {
721- if (rt_sem_take (g_xz_ws .sem , 10000 ) == RT_EOK )
843+ /* 连接成功,等待握手完成 */
844+ if (rt_sem_take (g_xz_ws .sem , 50000 ) == RT_EOK )
722845 {
723- LOG_I ("g_xz_ws.is_connected = %d\n" , g_xz_ws .is_connected );
846+ LOG_I ("WebSocket handshake completed, connected= %d\n" , g_xz_ws .is_connected );
724847 if (g_xz_ws .is_connected )
725848 {
726849 err = wsock_write (& g_xz_ws .clnt , HELLO_MESSAGE ,
727850 strlen (HELLO_MESSAGE ), OPCODE_TEXT );
728- break ;
851+ if (err == ERR_OK )
852+ {
853+ LOG_I ("Initial WebSocket connection established successfully\n" );
854+ return ;
855+ }
856+ else
857+ {
858+ LOG_E ("Failed to send hello message: %d\n" , err );
859+ }
729860 }
730861 else
731862 {
732- LOG_E ("Web socket disconnected \n" );
863+ LOG_E ("WebSocket connected but not properly initialized \n" );
733864 }
734865 }
735866 else
736867 {
737- LOG_E ("Web socket connected timeout\n" );
868+ LOG_E ("WebSocket connection timeout after 50 seconds \n" );
738869 }
739870 }
740871 else
741872 {
742- LOG_I ("Waiting internet ready...\n" );
743- rt_thread_mdelay (1000 );
873+ LOG_E ("WebSocket connection failed: %d, %d retries remaining\n" , err , retry );
874+ }
875+
876+ /* 连接失败,更新UI状态 */
877+ if (retry > 0 )
878+ {
879+ xiaozhi_ui_chat_status (" Connection Failed" );
880+ char retry_msg [64 ];
881+ rt_snprintf (retry_msg , sizeof (retry_msg ), "Retrying... (%d)" , 10 - retry );
882+ xiaozhi_ui_chat_output (retry_msg );
883+ rt_thread_mdelay (3000 ); /* 增加初始连接失败的重试间隔 */
744884 }
745885 }
886+
887+ /* 所有重试都失败了 */
888+ LOG_E ("WebSocket connection failed after all attempts\n" );
889+ xiaozhi_ui_chat_status (" Connection Failed" );
890+ xiaozhi_ui_chat_output ("Please check network and retry" );
746891}
747892
748893/* Main Entry */
@@ -755,8 +900,6 @@ void xiaozhi_entry(void *p)
755900 if (my_ota_version )
756901 {
757902 http_xiaozhi_data_parse_ws (my_ota_version );
758- extern void iot_initialize (void );
759- iot_initialize ();
760903 rt_free (my_ota_version );
761904 break ;
762905 }
0 commit comments