Skip to content

Commit 5699cbb

Browse files
committed
xiaozhi解决了休眠后频繁重连导致的LwIP断言错误和连接异常问题
1 parent 5570205 commit 5699cbb

File tree

2 files changed

+193
-50
lines changed

2 files changed

+193
-50
lines changed

projects/Edgi-Talk_XiaoZhi/Edgi_Talk_M55_XiaoZhi/applications/xiaozhi/xiaozhi.c

Lines changed: 190 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
static rt_thread_t xiaozhi_tid = RT_NULL;
1919
static const char *client_id = "af7ac552-9991-4b31-b660-683b210ae95f";
2020
static uint8_t WEBSOCKET_RECONNECT_FLAG = 0;
21+
static uint8_t iot_initialized = 0; // 添加IoT初始化标志
22+
static uint32_t last_reconnect_time = 0; // 添加重连时间戳,防止频繁重连
2123
static const char *mode_str[] = {"auto", "manual", "realtime"};
2224
static char mac_address_string[20] = {0};
2325
static 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

367453
void 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)
701802
void 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

Comments
 (0)