这里写目录标题
- STM32F03C8T6通过AT指令获取天气API
- [STM32 天气API接口调试总结](#STM32 天气API接口调试总结)
-
- 问题现象
- 失败原因分析
-
- [1. **过度复杂的 JSON 解析逻辑**](#1. 过度复杂的 JSON 解析逻辑)
- [2. **cJSON 库的内存和字符串处理问题**](#2. cJSON 库的内存和字符串处理问题)
- [3. **字符串截断问题**](#3. 字符串截断问题)
- [4. **过多的调试输出干扰**](#4. 过多的调试输出干扰)
- 成功的关键改进
-
- [✅ 1. **简化 JSON 解析逻辑**](#✅ 1. 简化 JSON 解析逻辑)
- [✅ 2. **使用简单的字符串查找函数**](#✅ 2. 使用简单的字符串查找函数)
- [✅ 3. **移除 cJSON 依赖**](#✅ 3. 移除 cJSON 依赖)
- [✅ 4. **优化等待机制**](#✅ 4. 优化等待机制)
- [✅ 5. **精简调试输出**](#✅ 5. 精简调试输出)
- 核心经验教训
-
- [🎯 1. **简单就是美**](#🎯 1. 简单就是美)
- [🎯 2. **参考成功案例**](#🎯 2. 参考成功案例)
- [🎯 3. **嵌入式环境的特殊性**](#🎯 3. 嵌入式环境的特殊性)
- [🎯 4. **调试要适度**](#🎯 4. 调试要适度)
- 最终成功的代码特点
- 成功输出示例
- 总结
STM32F03C8T6通过AT指令获取天气API
浏览器控制台输出strlen
js
function calcHttpLength(apiKey, location) {
const req = `GET /v3/weather/now.json?key=${apiKey}&location=${location}&language=en&unit=c HTTP/1.1\r\nHost: api.seniverse.com\r\nConnection: close\r\n\r\n`;
console.log("Length:", req.length, "bytes");
console.log("AT Command: AT+CIPSEND=" + req.length);
return req.length;
}
STM32 天气API接口调试总结
问题现象
连续多次尝试获取天气数据失败,表现为:
- JSON 解析失败
- 数据被截断
- cJSON_Parse 返回错误
失败原因分析
1. 过度复杂的 JSON 解析逻辑
问题:
- 使用了复杂的嵌套查找逻辑
- 尝试从
"results"往前查找{ - 多层指针操作容易出错
表现:
'now' not found
JSON parse failed
2. cJSON 库的内存和字符串处理问题
问题:
- cJSON 对输入字符串要求严格,必须完整且格式正确
- 接收缓冲区中可能存在
\0字符导致字符串提前终止 - HTTP 响应头和 JSON 数据混在一起
表现:
cJSON_Parse failed
Error before: path":"Beijing...
JSON length: 260 (实际应该更长)
3. 字符串截断问题
问题:
- 使用
strlen()计算长度,遇到\0就停止 - HTTP 响应中可能包含二进制数据或控制字符
- JSON 数据被截断到 260-270 字节
表现:
Received 720 bytes
JSON length: 260 bytes // 不匹配!
4. 过多的调试输出干扰
问题:
- 大量的
printf调试信息 - 打印完整的 JSON 数据(可能很长)
- 影响程序执行时序
成功的关键改进
✅ 1. 简化 JSON 解析逻辑
改进:
c
// 之前:复杂的嵌套查找
p_results = strstr(json, "\"results\"");
p_location = strstr(p_results, "\"location\"");
p_now = strstr(p_results, "\"now\"");
// 然后从各个位置解析...
// 现在:直接在整个缓冲区中查找
if(strstr(ESP8266_RxBuffer, "\"results\""))
{
Weather_ParseJSON(ESP8266_RxBuffer);
}
优势:
- 不需要精确定位 JSON 起始位置
- 直接在整个缓冲区中搜索关键字
- 容错性更强
✅ 2. 使用简单的字符串查找函数
改进:
c
// 自定义的 GetStringBetween 函数
static uint8_t GetStringBetween(char *src, char *start, char *end,
char *output, uint16_t maxLen)
{
char *p_start = strstr(src, start);
if(p_start == NULL) return 0;
p_start += strlen(start);
char *p_end = strstr(p_start, end);
if(p_end == NULL) return 0;
// 复制数据
uint16_t len = p_end - p_start;
if(len >= maxLen) len = maxLen - 1;
strncpy(output, p_start, len);
output[len] = '\0';
return 1;
}
// 使用示例
GetStringBetween(json, "\"name\":\"", "\"", temp, sizeof(temp));
GetStringBetween(json, "\"text\":\"", "\"", temp, sizeof(temp));
GetStringBetween(json, "\"temperature\":\"", "\"", temp, sizeof(temp));
优势:
- 不依赖第三方库(cJSON)
- 直接处理原始字符串,不受
\0影响 - 代码简单易懂
- 参考了成功示例的实现方式
✅ 3. 移除 cJSON 依赖
原因:
- cJSON 对输入要求严格
- 需要完整、格式正确的 JSON 字符串
- 在嵌入式环境中可能有内存碎片问题
改进:
- 使用简单的字符串查找
- 只提取需要的字段
- 不需要解析完整的 JSON 结构
✅ 4. 优化等待机制
改进:
c
// 之前:固定延时
Delay_ms(3000);
// 现在:超时循环等待
uint16_t TimeOut = 0;
while(TimeOut < 100 && !strstr(ESP8266_RxBuffer, "OK"))
{
Delay_ms(10);
TimeOut++;
}
优势:
- 响应更快(收到数据立即处理)
- 有超时保护
- 参考了成功示例的实现
✅ 5. 精简调试输出
改进:
c
// 之前:大量详细的调试信息
DEBUG_PRINTF("========== 开始获取天气信息 ==========\r\n");
DEBUG_PRINTF("城市: %s\r\n", city);
DEBUG_PRINTF("API Key: %s\r\n", api_key);
DEBUG_PRINTF("关闭旧连接...\r\n");
// ... 还有很多
// 现在:简洁的关键信息
printf("[Weather] Getting weather for %s...\r\n", city);
printf("[Weather] Connecting to server...\r\n");
printf("[Weather] Request sent, waiting response...\r\n");
printf("[Weather] Success!\r\n");
优势:
- 减少串口输出时间
- 不影响程序时序
- 信息更清晰易读
核心经验教训
🎯 1. 简单就是美
- 不要过度设计
- 能用简单方法解决的,不要用复杂方案
- 字符串查找比完整 JSON 解析更适合嵌入式环境
🎯 2. 参考成功案例
- 看到别人成功的代码,直接参考其实现方式
- 不要重复造轮子
- 成功的示例已经验证过可行性
🎯 3. 嵌入式环境的特殊性
- 内存有限,避免使用复杂的库
- 字符串处理要小心
\0字符 - HTTP 响应包含头部和数据,需要容错处理
🎯 4. 调试要适度
- 过多的调试输出反而影响程序运行
- 关键节点输出即可
- 成功后可以进一步精简
最终成功的代码特点
✅ 简洁 :150 行代码(之前 300+ 行)
✅ 可靠 :基于成功示例的实现
✅ 高效 :超时等待机制,响应快
✅ 易维护 :逻辑清晰,易于理解
✅ 无依赖:不需要 cJSON 等第三方库
成功输出示例
[Weather] Getting weather for beijing...
[Weather] Connecting to server...
[Weather] Request sent, waiting response...
[Weather] Received 720 bytes
[Weather] Parsing JSON...
========================================
Weather Information
========================================
City: Beijing
Weather: Clear
Temperature: -1 C
Temp Range: -4 ~ 4 C
========================================
[Weather] Success!
总结
失败的根本原因: 过度复杂化,使用了不适合嵌入式环境的方案(cJSON + 复杂解析逻辑)
成功的关键: 回归简单,参考成功示例,使用基础的字符串查找函数
教训: 在嵌入式开发中,简单、可靠、易维护比功能强大更重要!