基于STM32的ESP8266天气时钟(2)--------MCU串口透传与心知天气API对接实战
1. 串口透传基础与硬件准备第一次玩STM32和ESP8266的组合时我踩过不少坑。最头疼的就是串口通信不稳定要么数据丢包要么根本连不上。后来发现硬件连接和基础配置没做好后面所有操作都是白搭。先说说硬件清单。你需要准备这几样东西STM32最小系统板我用的是STM32F103C8T6性价比高资源足够ESP8266模块推荐ESP-01S体积小还自带天线USB转TTL模块CH340芯片的就行记得买带3.3V/5V切换的杜邦线若干建议用彩色线区分功能硬件连接有个关键点很多人会忽略——电平匹配。ESP8266是3.3V器件而STM32的IO口虽然标称支持3.3V但实际供电电压不同会影响通信稳定性。我的经验是ESP8266的VCC一定要接3.3VSTM32的串口TX要接ESP8266的RXRX接TX这个反接操作新手经常搞错共地线必须接不然会出现玄学通信故障具体接线方式STM32 ESP8266 3.3V - VCC GND - GND PB10 - RX PB11 - TX2. ESP8266的AT指令配置实战刚开始用AT指令配置ESP8266时我总遇到响应超时的问题。后来发现是波特率没设对。ESP8266默认波特率是115200但有些模块出厂可能是74880。建议先用串口调试助手测试找到正确的波特率。必须掌握的AT指令序列AT // 测试模块是否正常 ATCWMODE1 // 设置为Station模式 ATRST // 重启生效 ATCWJAPSSID,password // 连接WiFi这里有个坑ESP8266对指令格式极其敏感。我遇到过因为多打一个空格导致指令失效的情况。建议在代码里这样处理void sendATCommand(const char* cmd, const char* expect, int timeout) { printf(Send: %s\r\n, cmd); HAL_UART_Transmit(huart3, (uint8_t*)cmd, strlen(cmd), 100); // 等待响应处理... }透传模式配置是关键步骤ATCIPSTARTTCP,api.seniverse.com,80 // 连接心知天气服务器 ATCIPMODE1 // 开启透传 ATCIPSEND // 开始透传实测中发现每次获取天气数据后必须用退出透传否则下次通信会失败。这个细节官方文档没强调我花了三天才找到问题所在。3. HTTP请求构建与API对接心知天气的API是我用过最稳定的免费天气接口。注册账号后你会得到两个关键参数API密钥32位字符串城市代码如beijingGET请求的构建要注意几个细节URL必须包含unitc参数获取摄氏温度中文城市名需要URL编码结尾必须有两个\n这是我优化后的请求模板GET /v3/weather/now.json?key你的私钥locationchengdulanguagezh-Hansunitc\r\n Host: api.seniverse.com\r\n User-Agent: STM32F103\r\n Connection: close\r\n\r\n在代码中动态生成请求时建议用sprintf构造char request[256]; sprintf(request, GET /v3/weather/now.json?key%slocation%slanguagezh-Hansunitc\r\n Host: api.seniverse.com\r\n Connection: close\r\n\r\n, API_KEY, CITY_NAME);有个容易忽略的坑STM32的堆栈可能不够用。如果请求字符串太长会导致内存溢出建议把数组定义在全局区或者静态区。4. JSON数据流处理技巧收到的心知天气API响应是这样的JSON格式{ results: [{ location: { name: 成都, country: CN }, now: { text: 晴, temperature: 28, wind_direction: 东南 } }] }在STM32上解析JSON是个挑战。我试过三种方案字符串查找简单但脆弱适合固定格式cJSON库功能强大但耗内存JSMN解析器轻量级推荐这里分享一个用sscanf的取巧方法char weather[20], temp[10]; if(sscanf(response, \text\:\%[^\]\,\temperature\:\%[^\]\, weather, temp) 2) { printf(天气:%s 温度:%s℃, weather, temp); }对于内存紧张的STM32F103我建议预先定位关键字段char *p strstr(response, \temperature\:\); if(p) { p 16; // 跳过字段名 int i 0; while(p[i] ! ) { temp[i] p[i]; i; } temp[i] \0; }5. 错误处理与稳定性优化在实际部署中我发现这几个问题最常见WiFi断连后不会自动重连TCP连接有时会假死内存泄漏导致系统崩溃解决方案是加入状态监测和恢复机制void checkConnection() { if(HAL_GetTick() - lastResponseTime TIMEOUT) { esp8266Reset(); // 硬件复位模块 initWiFi(); // 重新初始化 } }内存管理也很重要。每次AT指令交互都应该这样处理char *buffer malloc(128); if(buffer) { // 使用buffer free(buffer); // 必须释放 }对于频繁获取天气的场景建议添加30秒以上的请求间隔缓存上次成功获取的数据实现超时重试机制我一般设3次6. 完整代码框架示例这是经过项目验证的核心代码结构// 在main.c中 while(1) { if(needUpdateWeather()) { if(getWeatherData()) { parseAndDisplay(); } else { showErrorIcon(); } } checkConnection(); HAL_Delay(100); } // 在esp8266.c中 uint8_t getWeatherData() { if(!wifiConnected) return 0; sendATCommand(ATCIPSTART\TCP\,\api.seniverse.com\,80, OK, 2000); sendATCommand(ATCIPMODE1, OK, 1000); sendATCommand(ATCIPSEND, , 1000); char request[150]; buildWeatherRequest(request); HAL_UART_Transmit(huart3, (uint8_t*)request, strlen(request), 1000); if(waitForResponse(5000)) { processResponse(); return 1; } return 0; }7. 调试技巧与常见问题用逻辑分析仪抓取的串口波形显示ESP8266的响应延迟可能达到300ms。因此超时设置不能太短建议普通AT指令500ms超时WiFi连接5000ms超时TCP连接2000ms超时常见错误代码及解决方法ERROR通常是指令格式错误busy p...模块忙等待100ms重试no ipWiFi未连接成功link is notTCP连接已断开调试时一定要启用串口打印我习惯这样设计调试输出#define DEBUG 1 void debugPrint(char* msg) { #if DEBUG printf([DEBUG] %s\r\n, msg); #endif }最后提醒心知天气免费版有每日请求次数限制1000次/天开发时建议本地缓存数据否则容易触发限流。