1. 串口透传基础与硬件准备
第一次玩STM32和ESP8266的组合时,我踩过不少坑。最头疼的就是串口通信不稳定,要么数据丢包,要么根本连不上。后来发现,硬件连接和基础配置没做好,后面所有操作都是白搭。
先说说硬件清单。你需要准备这几样东西:
- STM32最小系统板:我用的是STM32F103C8T6,性价比高,资源足够
- ESP8266模块:推荐ESP-01S,体积小还自带天线
- USB转TTL模块:CH340芯片的就行,记得买带3.3V/5V切换的
- 杜邦线若干:建议用彩色线区分功能
硬件连接有个关键点很多人会忽略——电平匹配。ESP8266是3.3V器件,而STM32的IO口虽然标称支持3.3V,但实际供电电压不同会影响通信稳定性。我的经验是:
- ESP8266的VCC一定要接3.3V
- STM32的串口TX要接ESP8266的RX,RX接TX(这个反接操作新手经常搞错)
- 共地线必须接,不然会出现玄学通信故障
具体接线方式:
STM32 ESP8266 3.3V -> VCC GND -> GND PB10 -> RX PB11 -> TX2. ESP8266的AT指令配置实战
刚开始用AT指令配置ESP8266时,我总遇到响应超时的问题。后来发现是波特率没设对。ESP8266默认波特率是115200,但有些模块出厂可能是74880。建议先用串口调试助手测试,找到正确的波特率。
必须掌握的AT指令序列:
AT // 测试模块是否正常 AT+CWMODE=1 // 设置为Station模式 AT+RST // 重启生效 AT+CWJAP="SSID","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); // 等待响应处理... }透传模式配置是关键步骤:
AT+CIPSTART="TCP","api.seniverse.com",80 // 连接心知天气服务器 AT+CIPMODE=1 // 开启透传 AT+CIPSEND // 开始透传实测中发现,每次获取天气数据后必须用+++退出透传,否则下次通信会失败。这个细节官方文档没强调,我花了三天才找到问题所在。
3. HTTP请求构建与API对接
心知天气的API是我用过最稳定的免费天气接口。注册账号后,你会得到两个关键参数:
- API密钥(32位字符串)
- 城市代码(如"beijing")
GET请求的构建要注意几个细节:
- URL必须包含
unit=c参数获取摄氏温度 - 中文城市名需要URL编码
- 结尾必须有两个
\n
这是我优化后的请求模板:
GET /v3/weather/now.json?key=你的私钥&location=chengdu&language=zh-Hans&unit=c\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=%s&location=%s&language=zh-Hans&unit=c\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("AT+CIPSTART=\"TCP\",\"api.seniverse.com\",80", "OK", 2000); sendATCommand("AT+CIPMODE=1", "OK", 1000); sendATCommand("AT+CIPSEND", ">", 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 ip:WiFi未连接成功
- link is not:TCP连接已断开
调试时一定要启用串口打印,我习惯这样设计调试输出:
#define DEBUG 1 void debugPrint(char* msg) { #if DEBUG printf("[DEBUG] %s\r\n", msg); #endif }最后提醒:心知天气免费版有每日请求次数限制(1000次/天),开发时建议本地缓存数据,否则容易触发限流。