本篇文章记录我使用onenet云平台下发数据至单片机,并且使用OLED实时显示数据时遇到的一些问题以及解决办法。
单片机解析云平台下发的数据的解析代码如下,数据交互使用MQTT协议,json格式。在使用以下代码时,发现OLED屏幕会出现卡死现象,利用串口调试后,发现stm32程序也被卡死了。
改善前代码:
cpp
case MQTT_PKT_PUBLISH: //接收的Publish消息
result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);
if(result == 0)
{
UsartPrintf(USART_DEBUG, "topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",
cmdid_topic, topic_len, req_payload, req_len);
// 对数据包req_payload进行JSON格式解析
json = cJSON_Parse(req_payload);
params_json = cJSON_GetObjectItem(json,"params");
// led_json=cJSON_GetObjectItem(params_json,"Led");
// Alarm_json=cJSON_GetObjectItem(params_json,"alarm");
A_score_json=cJSON_GetObjectItem(params_json,"A_score");
B_score_json=cJSON_GetObjectItem(params_json,"B_score");
number_game_json=cJSON_GetObjectItem(params_json,"number_game");
if(A_score_json!=NULL)
{
A_score=A_score_json->valueint;
}
if(B_score_json!=NULL)
{
B_score=B_score_json->valueint;
}
if(number_game_json!=NULL)
{
number_game=number_game_json->valueint;
UsartPrintf(USART_DEBUG, "number_game:%d\r\n",number_game);
}
cJSON_Delete(json);
}
break;
分析原因后,得知程序卡死的原因可能在于json解析到了空指针,所以导致硬件错误从而死机。主要修改部分就是如果解析到了空指针,就退出。
改善后代码:
cpp
case MQTT_PKT_PUBLISH: //接收的Publish消息
result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);
if(result == 0)
{
UsartPrintf(USART_DEBUG, "topic: %s, topic_len: %d, payload: %s, payload_len: %d\r\n",
cmdid_topic, topic_len, req_payload, req_len);
// ===================== 【安全区 绝对不会死机】 =====================
req_payload[req_len] = '\0'; // 强制结束符,必加!
json = cJSON_Parse(req_payload);
// 关键:解析失败直接退出,绝不往下走
if(json == NULL)
{
UsartPrintf(USART_DEBUG, "JSON解析失败\r\n");
}
else
{
params_json = cJSON_GetObjectItem(json, "params");
if(params_json != NULL)
{
A_score_json = cJSON_GetObjectItem(params_json, "A_score");
B_score_json = cJSON_GetObjectItem(params_json, "B_score");
number_game_json = cJSON_GetObjectItem(params_json, "number_game");
if(A_score_json != NULL)
{
A_score = A_score_json->valueint;
}
if(B_score_json != NULL)
{
B_score = B_score_json->valueint;
}
if(number_game_json != NULL)
{
number_game = number_game_json->valueint;
UsartPrintf(USART_DEBUG, "number_game:%d\r\n", number_game);
}
}
cJSON_Delete(json);
}
}
break;
改善代码后重新调试,发现虽然stm32虽然能正常刷新了。但是却解析不到云平台实时下发的正确的数据,短暂的获取到正确数据之后,又被错误数据给覆盖了。
分析后,发现原因可能在于解析数据时出现了问题,根据串口信息显示,单片机能够正确接收到云平台下发的数据,但是就是解析的时候出现错误,导致OLED屏不能正确显示数据。
改善前代码:
cpp
case MQTT_PKT_PUBLISH:
result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);
if(result == 0)
{
UsartPrintf(USART_DEBUG, "topic: %s, payload: %s, len: %d\r\n", cmdid_topic, req_payload, req_len);
req_payload[req_len] = '\0';
json = cJSON_Parse(req_payload);
if(json != NULL)
{
params_json = cJSON_GetObjectItem(json, "params");
if(params_json != NULL)
{
// 只解析标准键值对格式,忽略嵌套格式
A_score_json= cJSON_GetObjectItem(params_json, "A_score");
if(A_score_json != NULL && A_score_json->type == cJSON_Number)
{
A_score = A_score_json->valueint;
}
B_score_json= cJSON_GetObjectItem(params_json, "B_score");
if(B_score_json != NULL && B_score_json->type == cJSON_Number)
{
B_score = B_score_json->valueint;
}
number_game_json = cJSON_GetObjectItem(params_json, "number_game");
if(number_game_json != NULL && number_game_json ->type == cJSON_Number)
{
number_game = number_game_json ->valueint;
}
UsartPrintf(USART_DEBUG, "Update: A=%d, B=%d, Game=%d\r\n", A_score, B_score, number_game);
}
cJSON_Delete(json);
}
else if(json == NULL)
{
UsartPrintf(USART_DEBUG, "error!\r\n");
}
}
break;
因为这里的cJSON_Parse(req_payload)函数已经被破坏了,所以现在最快捷的办法就是自己写一个解析函数。
改善后代码:
cpp
case MQTT_PKT_PUBLISH:
result = MQTT_UnPacketPublish(cmd, &cmdid_topic, &topic_len, &req_payload, &req_len, &qos, &pkt_id);
if(result == 0)
{
char temp_buf[256] = {0};
if(req_len < 255)
{
memcpy(temp_buf, req_payload, req_len);
}
UsartPrintf(USART_DEBUG, "接收:%s\r\n", temp_buf);
// ==============================
// 纯手写解析,不依赖任何库!
// ==============================
int a_score = 0, b_score = 0, num_game = 0;
// 提取 A_score
char *p = strstr(temp_buf, "\"A_score\":");
if(p != NULL)
{
sscanf(p, "\"A_score\":%d", &a_score);
}
// 提取 B_score
p = strstr(temp_buf, "\"B_score\":");
if(p != NULL)
{
sscanf(p, "\"B_score\":%d", &b_score);
}
// 提取 number_game
p = strstr(temp_buf, "\"number_game\":");
if(p != NULL)
{
sscanf(p, "\"number_game\":%d", &num_game);
}
// 赋值给全局变量
A_score = a_score;
B_score = b_score;
number_game = num_game;
UsartPrintf(USART_DEBUG, "解析成功:A=%d B=%d 局数=%d\r\n", A_score, B_score, number_game);
}
break;
改善后代码可以正常使用,OLED屏能正常实时显示数据了。
这个示例说明了库不是完全有效的,难免会遇到解析时出现错误,即使接收到的数据是正确的,但是就是解析不出来,这个时候就要考虑是不是库坏了,才导致解析不出来数据。