踩坑记录
踩坑:
1.队列通知本身就是种信号量的使用,可以来同步信息
低功耗Tickless模式
把它置位1就行了,在空闲任务里面判断空的时间足够进入之后才进入低功耗模式,关闭了SYSTICK中断

2.串口问题,莫名收不到数据
toolName: todo_write
status: success
Todos updated: 6 items
toolName: view_files
status: success
filePath: e:\MCU_code\F429IGT6\loT_gateway\USER\main.c
toolName: view_files
status: success
filePath: e:\MCU_code\F429IGT6\loT_gateway\USER\main.c
toolName: view_files
status: success
filePath: e:\MCU_code\F429IGT6\loT_gateway\ATK_MW8266D\atk_mw8266d.c
toolName: view_files
status: success
filePath: e:\MCU_code\F429IGT6\loT_gateway\ATK_MW8266D\atk_mw8266d_uart.c
toolName: view_files
status: success
filePath: e:\MCU_code\F429IGT6\loT_gateway\ATK_MW8266D\atk_mw8266d_uart.c
toolName: edit_file_search_replace
status: success
filePath: e:\MCU_code\F429IGT6\loT_gateway\ATK_MW8266D\atk_mw8266d_uart.c
changes: undefined
The issue you encountered where "ESP8266 initialization fails" despite the hardware being normal is caused by a logic error in the UART interrupt handler (`ATK_MW8266D_UART_IRQHandler`) within `atk_mw8266d_uart.c`.
### Problem Analysis
When we introduced the message pump (Queue) mechanism, a regression was introduced in the IDLE interrupt handler:
1. The code was setting `g_uart_rx_frame.sta.len = 0` **immediately** after detecting an IDLE frame.
2. This happened **before** `atk_mw8266d_init` (or any other function) could read the received data.
3. As a result, `atk_mw8266d_send_at_cmd` would wake up, see that a frame arrived (`finsh=1`), but when it tried to read the data, the length was 0, resulting in an empty string.
4. The function would then fail to find the "OK" response, causing the initialization to timeout and fail.
### The Fix
I have removed the premature `len = 0` resets in `atk_mw8266d_uart.c`. Now the logic is:
1. **ISR**: Sets `finsh = 1`, keeps `len` as is, and sends the length to the Queue.
2. **Consumer** (Init function or Task): Reads the valid data using `atk_mw8266d_uart_rx_get_frame()`.
3. **Cleanup**: The consumer is responsible for calling `atk_mw8266d_uart_rx_restart()` to reset `len` to 0 for the next frame (which is already correctly implemented in your code).
### Code Changes
**File:** [atk_mw8266d_uart.c](file:///e:/MCU_code/F429IGT6/loT_gateway/ATK_MW8266D/atk_mw8266d_uart.c)
```c
// In ATK_MW8266D_UART_IRQHandler
/* 3. IDLE Interrupt Handling */
if (__HAL_UART_GET_FLAG(&g_uart_handle, UART_FLAG_IDLE) != RESET)
{
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
__HAL_UART_CLEAR_IDLEFLAG(&g_uart_handle);
g_uart_rx_frame.sta.finsh = 1;
// REMOVED: g_uart_rx_frame.sta.len = 0; <-- This was the bug
/* 4. Send to Queue */
if (xUartRxQueue != NULL)
{
uint16_t len = g_uart_rx_frame.sta.len;
// REMOVED: g_uart_rx_frame.sta.len = 0; <-- This was also incorrect here
xQueueSendFromISR(xUartRxQueue, &len, &xHigherPriorityTaskWoken);
}
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
}
```
This change ensures that both the initialization sequence (polling based) and the new message pump (queue based) can correctly access the received UART data.3.透传模式后串口数据残留,发送MQTT报文时帧的格式不对,导致连接失败
// 4. 等待CONNACK包
len = MQTTPacket_read(mqtt_recv_buf, sizeof(mqtt_recv_buf), transport_getdata);
if (len > 0)
{
if (MQTTDeserialize_connack(&sessionPresent, &connack_rc, mqtt_recv_buf, len))
{
if (connack_rc == MQTT_CONNECTION_ACCEPTED)
{
printf("MQTT Connected!\r\n");
con_status = 1;
// 5. 订阅主题
subscribe_topic.cstring = MQTT_TOPIC_SUB;
int reqQoS[1] = {0};
len = MQTTSerialize_subscribe(mqtt_send_buf, sizeof(mqtt_send_buf), 0, sub_packetid++, 1, &subscribe_topic, reqQoS);
if (len > 0)
{
transport_sendPacketBuffer(mqtt_sock, mqtt_send_buf, len);
printf("MQTT Subscribe Sent.\r\n");
}
}
else
{
printf("MQTT Connect Rejected: %d\r\n", connack_rc);
transport_close(mqtt_sock);
mqtt_sock = -1;
con_status = 0;
}
}
else
{
printf("MQTT Connack Deserialize Failed!\r\n");
transport_close(mqtt_sock);
mqtt_sock = -1;
con_status = 0;
}
}
else
{
printf("MQTT Connack Receive Timeout!\r\n");
transport_close(mqtt_sock);
mqtt_sock = -1;
con_status = 0;
}
break;
Connecting to MQTT Broker...
Pinging 192.168.1.28 ...
Ping Success!
TCP Connected, Entered Transparent Mode.
MQTT Connack Receive Timeout!
哪里的问题?用中文回答
这个问题通常是由 串口缓冲区残留数据 引起的。
原因分析: 当 ESP8266 响应 AT+CIPSEND 命令进入透传模式时,它会返回 > 符号。在某些情况下,这个响应后面可能还跟着回车换行符 ( \r\n )。
1. 代码在检测到 > 后认为透传已就绪,开始发送 MQTT CONNECT 包。
2. 但是在接收 CONNACK 回复时,MCU 的串口接收缓冲区里可能还残留着之前的 \r\n 。
3. MQTTPacket_read 函数读取到的第一个字节是 \r (0x0D),而不是 CONNACK 的包头 (0x20)。
4. MCU 误将 \r 当作包头,将 \n 当作剩余长度(0x0A = 10字节),于是继续等待读取后续的 10 个字节。
5. 由于实际上并没有这 10 个字节的数据,读取操作最终 超时 (Timeout) 。
已为您修复: 我修改了 MQTT/transport.c 文件中的 transport_open 函数。
在确认进入透传模式后, 强制清空一次串口接收缓冲区 ,确保后续接收到的都是纯净的 MQTT 数据。
代码修改位置: transport.c
```
// 修改前
transport_transparent_mode = 1;
transport_rx_offset = 0;
// 修改后
transport_transparent_mode = 1;
// 进入透传模式后,清除可能存在的残留数据
(例如 ">" 响应后的回车换行等)
// 确保后续接收到的是纯净的 MQTT 数据
atk_mw8266d_uart_rx_restart();
transport_rx_offset = 0;
```问题已经解决,现在有新的问题了。
1.一旦我按下按键开始上传数据到MQTT服务器,屏幕上的采集显示就会停止
2.一旦我用MQTT服务发送了一次数据,串口打印出来了之后,之前MQTT订阅的服务就接收不到数据了,但是串口显示
MQTT Pub AP3216C OK
MQTT Pub AP3216C OK
MQTT Pub AP3216C OK
屏幕显示停止问题 :
- 原因 : lcd_task (显示任务)和 net_task (MQTT任务)都在竞争同一个队列 xAP3216CQueue 。由于 net_task 优先级更高(4 > 2),一旦它开始运行,就会抢在 lcd_task 之前把队列里的数据取走,导致 LCD 拿不到数据,因此屏幕停止更新。
- 解决 :我为您创建了一个专用的 MQTT 队列 xAP3216CQueueForMQTT 。现在传感器任务会将数据同时发送给 LCD 队列和 MQTT 队列,互不干扰。
MQTT 订阅接收不到数据问题 :
- 原因 :之前的代码在接收处理逻辑上可能存在漏洞,如果收到的包不是 PUBLISH 包(例如 SUBACK 或 PINGRESP),可能会导致解析失败且未做处理。此外,串口接收缓冲区的处理逻辑也需要确保不会丢包。
- 解决 :优化了 net_task 中的接收逻辑,增加了对不同 MQTT 包类型的判断和调试打印。如果收到了非数据包(如订阅确认或心跳响应),串口现在会打印提示,而不是默默丢弃。
代码修改详情:
main.c :
新增队列 :定义并初始化了 xAP3216CQueueForMQTT 。
修改发送方 : ap3216c_task 现在会把数据“双发”,一份给屏幕,一份给 MQTT。
修改接收方 : net_task 现在从专用队列读取数据,不再抢占 LCD 的数据。
优化接收逻辑 :重构了 MQTT 接收部分的代码,增加了对 SUBACK (订阅确认)和 PINGRESP (心跳响应)的处理,并添加了调试信息,如果收到未知包会打印 MQTT RX Ignored (Type=...) 。
4.2问题追溯
解决 MQTT 接收中断/丢包问题 (粘包处理)
问题描述 :发送一次数据后,MQTT 订阅功能失效,无法再接收服务器下发的指令。
原因分析 :串口接收缓冲区可能一次性包含多个 MQTT 包(例如 PUBACK + PUBLISH)。原代码只解析缓冲区的第一个包就结束了,导致缓冲区后半部分的后续数据包被直接丢弃。
解决方案 :重构接收逻辑,引入 while 循环。
- 根据 MQTT 协议头计算当前包长度。
- 逐个解析缓冲区内的所有数据包,直到缓冲区为空。
- 增加了对 SUBACK (订阅确认)和 PINGRESP (心跳)的处理,避免因未知包类型导致的解析中断。
涉及文件 : USER/main.c 5. 解决 MQTT 发布主题错乱 (变量污染)
6.解决 MQTT 发布主题错乱 (变量污染)
问题描述 :设备向订阅的主题(Control)发送了传感器数据,导致设备收到自己发的数据。
原因分析 : net_task 中 复用 了 topicString 变量。
- 接收流程:收到服务器消息时,MQTT 库修改 topicString 指向接收的主题。
- 发送流程:紧接着发送数据时,代码未完全重置该变量状态,导致使用了刚刚接收到的主题作为发送目标。
解决方案 : 变量分离 。
- 发送逻辑中使用全新的局部变量 pubTopicString ,与接收逻辑用的 topicString 彻底解耦。
- 同时修改了 Topic 定义为唯一值( user/dev1/data ),避免与公共测试服务器上的其他设备冲突。
涉及文件 : USER/main.c

image-20260225023223746
6.mqtt重连机制
我把key1按下后的逻辑改了,auto_reconnect=1,断开后马上自动重连,但是串口打印显示Pinging 192.168.1.28 ...
Ping Failed! Host unreachable or Timeout.
Transport Open Failed!为什么?什么情况?但是一开始按下KEY0的时候就直接成功了
猜测:发送ping的时候串口有残余,这个命令不会成功的
根本:发送等待成功的回应没有清除掉,失败的回应会清除掉,所以串口缓冲总有残余
if (strstr((const char *)ret, ack) != NULL)
{
return ATK_MW8266D_EOK;
}
else
{
atk_mw8266d_uart_rx_restart();
}uint8_t atk_mw8266d_send_at_cmd(char *cmd, char *ack, uint32_t timeout)
{
uint8_t *ret = NULL;
atk_mw8266d_uart_rx_restart();
atk_mw8266d_uart_printf("%s\r\n", cmd);
if ((ack == NULL) || (timeout == 0))
{
return ATK_MW8266D_EOK;
}
else
{
while (timeout > 0)
{
ret = atk_mw8266d_uart_rx_get_frame();
if (ret != NULL)
{
if (strstr((const char *)ret, ack) != NULL)
{
return ATK_MW8266D_EOK;
}
else
{
atk_mw8266d_uart_rx_restart();
}
}
timeout--;
delay_ms(1);
}
return ATK_MW8266D_ETIMEOUT;
}
}这里加了清除操作和WIFI操作
if (atk_mw8266d_get_ip(ip_buf) != ATK_MW8266D_EOK)
{
atk_mw8266d_join_ap(DEMO_WIFI_SSID, DEMO_WIFI_PWD);
}
atk_mw8266d_uart_rx_restart();
mqtt_sock = transport_open(MQTT_BROKER_IP, atoi(MQTT_BROKER_PORT));
if (mqtt_sock < 0)
{
printf("Transport Open Failed!\r\n");
con_status = 0;
}7.断线重连机制问题:
- pending 固定在 163、重启后还在、反复补发旧数据,是因为之前:
- 已发送记录没有真正落盘成“已发送”标记;
- 扫描空位时没把 0x00(FLASH_FLAG_SENT) 当可复用位,导致写入空间逻辑异常。
已修复

