首页 / 资讯中心 / 文章详情

STM32F103+ESP8266+机智云:物联网节点从零搭建与避坑指南

STM32F103+ESP8266+机智云:物联网节点从零搭建与避坑指南 ★ FEATURED ARTICLE
1. 从零搭建物联网节点为什么选这套组合搞嵌入式物联网开发的朋友手里大概率都有一块正点原子的STM32F103开发板也囤过几个ESP8266模块。这两个东西单独玩都不难但要把它们串起来再接入一个云平台实现远程监控中间涉及的知识点就多了——串口通信、AT指令、协议适配、云端配置每一步都有坑。我这次做的项目就是用正点原子的STM32F103工程模板作为基础框架通过ESP8266模块连接机智云实现设备数据上报和远程控制。选这套组合的原因很实际STM32F103是ARM Cortex-M3内核主频72MHz资源对于中小型物联网节点绰绰有余ESP8266成本低、资料多AT指令集成熟机智云提供了自动代码生成工具能省掉大量协议对接的重复劳动。三者搭配属于性价比和开发效率都比较均衡的方案。这篇文章适合谁看如果你已经会用Keil或者STM32CubeIDE写简单的GPIO和串口程序对AT指令有基本概念但还没完整跑通过一个“MCUWiFi模块云平台”的链路那这篇内容就是为你准备的。我会从工程模板的裁剪开始一步步讲到云端数据点配置、代码生成、串口驱动适配、配网调试最后分享几个我在实测中踩过的坑和排查方法。整个过程不需要你精通TCP/IP协议栈但需要你有耐心跟着步骤走。注意本文涉及的云平台操作均基于通用物联网平台的标准流程具体界面可能随版本更新有所变化核心逻辑不变。2. 工程模板的裁剪与串口资源规划2.1 正点原子模板里哪些东西该留、哪些该删正点原子的STM32F103工程模板通常包含大量外设驱动LCD、触摸屏、SD卡、红外、温湿度传感器等等。如果你直接拿来用编译出来的固件体积会很大而且很多初始化代码会占用你需要的串口和定时器资源。我的做法是新建一个干净的工程目录只保留最核心的几个文件。具体来说保留SYSTEM文件夹下的delay、sys、usart三个子文件夹。delay用于毫秒和微秒级延时AT指令的发送和等待响应离不开它sys提供系统时钟配置和中断管理usart是串口驱动的基础。HARDWARE文件夹下只保留你实际用到的外设驱动比如LED和按键其他全部移除。CORE和STM32F10x_FWLib是启动文件和标准外设库必须保留。裁剪完成后在Keil的工程目标选项中把不用的.c文件从工程树里移除同时检查stm32f10x_conf.h里是否还使能了不需要的外设头文件。这一步能显著减少编译时间和固件体积。我实测下来裁剪后的工程编译出来大约占用Flash 20KB左右给后续的协议代码留足了空间。2.2 串口分配调试口和通信口必须分开这是很多新手容易忽略的一点。STM32F103通常有3个USART我建议这样分配串口用途波特率引脚USART1调试打印连接电脑串口助手115200PA9/PA10USART2连接ESP8266模块115200PA2/PA3USART3预留可接其他传感器9600PB10/PB11为什么调试口和通信口要分开因为ESP8266在配网和通信过程中会输出大量状态信息如果你把调试信息也混在同一个串口上日志会非常混乱排查问题时根本分不清哪条是模块返回的哪条是你自己打印的。分开之后USART1专门用来看程序运行状态USART2专门和ESP8266对话互不干扰。在代码层面你需要为USART2单独写一个发送函数和接收中断服务函数。正点原子的usart.c里默认只初始化了USART1你需要复制一份初始化代码把寄存器基址改成USART2引脚配置改成PA2TX和PA3RX并开启USART2的接收中断。接收中断里把收到的字节存入一个环形缓冲区供AT指令解析函数读取。2.3 环形缓冲区的实现细节AT指令的响应是不定长的而且可能分多次到达。如果你在中断里直接处理字符串很容易因为处理时间过长导致后续字节丢失。所以必须用一个环形缓冲区来暂存接收数据。我定义了一个大小为512字节的缓冲区用head和tail两个索引来管理读写位置。中断服务函数里只做一件事把接收到的字节写入buffer[head]然后head (head 1) % BUFFER_SIZE。主循环里的AT指令解析函数则从tail位置开始读取直到tail head为止。这样即使主循环在处理其他任务串口数据也不会丢失。提示缓冲区大小要根据你实际通信的数据量来定。如果只是收发AT指令和少量传感器数据512字节足够如果要传固件升级包建议扩大到2KB以上。3. 机智云数据点配置与代码生成3.1 数据点定义先想清楚你要传什么在机智云平台上创建一个新产品选择“WiFi/移动网络”接入方式然后开始定义数据点。数据点是云端和设备之间约定的数据单元每个数据点有名称、标识名、读写类型、数据类型和取值范围。以我做的环境监测节点为例我定义了以下几个数据点数据点名称标识名读写类型数据类型备注温度Temperature只读数值范围-20~80精度0.1湿度Humidity只读数值范围0~100精度0.1LED开关LED_OnOff可写布尔0关1开上报周期Report_Interval可写数值单位秒范围5~300定义数据点时要注意标识名一旦确定就不要随意修改因为生成的代码里会用到这个字符串作为变量名。读写类型决定了设备是只能上报还是也能接收控制指令。数据类型影响云端解析方式数值类型可以设置精度和步长布尔类型就是简单的0和1。3.2 自动生成代码的目录结构解读机智云的代码生成工具会输出一个压缩包解压后包含Gizwits、Utils、User等文件夹。Gizwits文件夹是协议核心里面有gizwits_protocol.c和gizwits_product.c两个关键文件。Utils里是数据点处理相关的工具函数。User里是用户回调函数的框架。你需要把这些文件添加到你的Keil工程中。注意机智云生成的代码默认使用一个串口和ESP8266通信你需要根据自己实际的串口分配修改gizwits_protocol.h里的UART_PORT宏定义把它指向USART2。同时生成的代码里会调用一个uartWrite函数来发送数据你需要把这个函数替换成你自己写的USART2发送函数。3.3 数据上报和事件处理的回调机制机智云的协议栈采用回调机制。当云端下发控制指令时协议栈会解析出对应的数据点然后调用gizwitsEventProcess函数。你需要在这个函数里根据dataPoint_t结构体的内容来执行具体操作比如检测到LED_OnOff为1就点亮LED为0就熄灭。数据上报则更简单。你只需要定期更新dataPoint_t结构体里的变量值然后调用gizwitsHandle函数协议栈会自动把变化的数据打包上报。上报周期可以通过云端配置也可以在设备端控制。我通常设置一个定时器每5秒调用一次gizwitsHandle这样既能保证数据实时性又不会过于频繁地占用网络。注意gizwitsHandle函数内部有状态机不要在中断里调用它。放在主循环里配合一个标志位来触发。4. ESP8266的AT指令驱动与配网流程4.1 ESP8266的固件版本与AT指令集差异ESP8266的AT指令集在不同固件版本之间有差异这一点必须提前确认。我手里这个模块出厂固件是AT 0.21版本支持ATCWJAP连接路由器、ATCIPSTART建立TCP连接、ATCIPSEND发送数据。但有些老版本固件用的是ATCWJAP后面直接跟SSID和密码新版本则要求SSID和密码用逗号分隔并加引号。我的建议是先用USB转TTL工具把ESP8266接到电脑上用串口助手发ATGMR查看版本号然后发ATCWMODE1设置为Station模式再发ATCWJAP你的WiFi名,你的WiFi密码测试能否连上路由器。这一步在电脑上先跑通能排除很多硬件和接线问题。4.2 用状态机管理AT指令的发送与等待在STM32上驱动ESP8266最忌讳的就是用delay死等。因为WiFi连接路由器可能需要几秒钟如果你在等待期间什么都不做看门狗可能会复位而且用户体验也很差。正确的做法是用状态机。我定义了几个状态WIFI_IDLE、WIFI_SEND_AT、WIFI_WAIT_OK、WIFI_CONNECTING、WIFI_CONNECTED、WIFI_ERROR。主循环里根据当前状态决定下一步动作。比如在WIFI_SEND_AT状态发送ATCWJAP指令后切换到WIFI_WAIT_OK并启动一个超时计数器。如果在规定时间内收到了OK就进入WIFI_CONNECTED如果收到ERROR或者超时就进入WIFI_ERROR等待一段时间后重试。这种非阻塞的方式让MCU在等待WiFi连接的同时还能处理按键扫描、传感器读取等任务。实测下来从发送连接指令到成功获取IP地址大约需要3到5秒具体取决于路由器响应速度。4.3 与机智云服务器的TCP连接建立WiFi连上路由器后下一步是连接机智云的服务器。机智云会为你的产品分配一个服务器地址和端口号这些信息在产品的“基本信息”页面可以找到。通常是一个域名加端口比如mqtt.gizwits.com:1883或者类似的TCP地址。你需要用ATCIPSTARTTCP,服务器地址,端口号来建立连接。这里有个细节ESP8266的AT指令不支持域名解析的情况很少但如果你用的是IP地址直接填IP即可。连接成功后模块会返回CONNECT和OK。然后你需要发送ATCIPMODE1进入透传模式再发ATCIPSEND开始透传。透传模式下你从USART2发给ESP8266的所有数据都会被原样发送到服务器服务器返回的数据也会从USART2出来。提示透传模式虽然方便但退出透传需要发送而且前后要有至少1秒的静默时间。如果你的程序逻辑比较复杂建议用非透传模式每次发送前用ATCIPSEND长度指定数据长度这样控制更精确。5. 联调过程中最容易卡住的几个环节5.1 串口乱码波特率、时钟和电平匹配串口乱码是最常见的问题原因通常有三个波特率不一致、系统时钟配置错误、电平不匹配。正点原子的模板默认使用外部8MHz晶振经过9倍频后得到72MHz系统时钟。如果你在sys.c里修改了时钟配置USART的波特率寄存器值会跟着变导致实际波特率偏离设定值。排查方法先用示波器或者逻辑分析仪测一下USART2的TX引脚波形看看一个位的时间宽度是否等于1/115200秒大约是8.68微秒。如果偏差超过5%通信就会不稳定。另外ESP8266的IO电平是3.3VSTM32F103的USART引脚也是3.3V直接连接没问题。但如果你用了5V的USB转TTL工具调试中间必须加电平转换电路否则可能损坏ESP8266。5.2 配网失败路由器设置和信号强度的隐形影响有时候AT指令返回OK但就是连不上路由器。我遇到过几次最后发现是路由器开了AP隔离或者WiFi密码里包含特殊字符。ESP8266的AT指令对密码中的逗号和引号敏感如果你的密码里有这些字符需要转义处理。另一个容易被忽略的是信号强度。ESP8266的板载天线增益有限如果路由器在另一个房间信号强度低于-80dBm时连接会非常不稳定。你可以在连接前发ATCWJAP?查询当前信号质量或者用ATCWLAP扫描周围热点看看目标路由器的RSSI值。如果太弱考虑加一个外置天线或者调整模块位置。5.3 云端收不到数据数据点绑定和上报格式检查设备显示已连接服务器但云端看不到数据这种情况通常是数据点没有正确绑定。机智云的协议要求设备上报的数据必须包含在已定义的数据点列表中而且格式要符合协议规范。如果你在代码里直接发送自定义的JSON字符串云端会直接丢弃。排查步骤先在机智云的“设备日志”页面查看是否有原始数据上报记录。如果有记录但解析失败说明数据格式有问题如果完全没有记录说明TCP连接虽然建立了但数据没有真正发出去。这时候要检查gizwitsHandle函数的调用频率以及dataPoint_t结构体里的变量是否真的被更新了。我遇到过一次是因为定时器中断里更新了变量但主循环里的gizwitsHandle没有及时读取导致上报的还是旧值。5.4 设备频繁掉线心跳间隔与路由器DHCP租期设备运行一段时间后自动掉线过一会儿又重连这种反复横跳很让人头疼。原因可能是心跳间隔设置得太长路由器或者服务器认为设备已经离线主动断开了连接。机智云默认的心跳间隔是180秒但有些路由器会在60秒无数据时断开TCP连接。我的做法是把心跳间隔改成30秒同时在设备端加一个TCP连接状态检测。如果连续三次心跳没有收到服务器响应就主动断开重连。另外路由器的DHCP租期也会影响如果租期到了而没有及时续约IP地址会变化导致TCP连接失效。建议在路由器里给ESP8266的MAC地址绑定一个静态IP彻底避免这个问题。6. 从能跑到好用稳定性优化的几个实操技巧6.1 看门狗与异常复位处理物联网设备经常部署在无人值守的环境程序跑飞了必须能自动恢复。STM32F103内置独立看门狗和窗口看门狗我通常用独立看门狗在主循环里定期喂狗。但要注意如果WiFi连接状态机卡在某个状态超过看门狗超时时间设备会复位。这其实是好事复位后重新初始化往往能恢复正常。不过频繁复位会影响用户体验。所以我在状态机里加了超时重试机制每个状态最多停留10秒超时后自动切换到错误处理状态尝试重新初始化ESP8266。只有连续三次重试都失败才让看门狗复位。这样既保证了自恢复能力又避免了不必要的重启。6.2 数据缓存与断线续传网络不稳定的时候传感器数据不能丢。我在SRAM里开辟了一个小型的循环队列用来缓存最近的上报数据。当TCP连接正常时队列里的数据按顺序发送当连接断开时数据继续入队但不出队。等连接恢复后先把队列里的历史数据补发再发送最新数据。这个队列的大小要根据你的上报频率和预期断线时长来定。假设每5秒上报一次每次数据20字节断线10分钟就是120条数据大约2.4KB。STM32F103有64KB SRAM拿出4KB做缓存完全没问题。实现时注意队列的读写指针要原子操作避免中断和主循环同时访问导致数据错乱。6.3 固件升级的预留设计产品定型后难免要修复bug或者增加功能。如果每次都要把设备拆下来接下载器那就太麻烦了。机智云支持OTA升级但需要你在代码里预留升级接口。具体来说就是在gizwitsProduct.c里实现gizwitsOtaInit和gizwitsOtaProcess函数并在主循环里调用。OTA升级的原理是云端把新固件分片下发设备收到后写入Flash的指定区域全部接收完成后跳转到Bootloader进行固件替换。这个过程需要你对STM32的Flash编程和中断向量表偏移有一定了解。如果暂时用不上至少要把Flash空间规划好给Bootloader留出足够的区域免得以后想加OTA时发现空间不够。6.4 日志分级与远程诊断调试阶段串口打印越详细越好产品部署后日志太多反而影响性能。我建议在代码里实现一个简单的日志分级机制用宏定义控制输出级别。比如LOG_LEVEL_ERROR只打印错误LOG_LEVEL_INFO打印关键状态变化LOG_LEVEL_DEBUG打印所有AT指令交互。更进一步可以把错误日志通过MQTT上报到云端的一个专用数据点。这样设备部署在外地你也能在云端看到它什么时候掉线、什么时候重连、错误码是什么。我实测下来这个远程诊断功能至少帮我节省了80%的现场排查时间。7. 我在这个项目里踩过的几个真实坑第一个坑是串口接收中断的优先级。我一开始把USART2的中断优先级设得比SysTick还高结果AT指令解析函数在SysTick中断里被调用时又触发了USART2中断导致栈溢出。后来把USART2的抢占优先级设为1SysTick设为0问题解决。记住中断优先级数值越小优先级越高但不要把所有中断都设成最高优先级。第二个坑是ESP8266的供电。ESP8266在发射WiFi信号时瞬间电流能达到300mA如果直接用STM32开发板上的3.3V稳压器供电电压会被拉低到2.8V以下导致模块复位。我的解决方案是给ESP8266单独用一个低压差稳压器输入接5V输出3.3V并且并联一个470uF的电解电容来缓冲电流波动。改完之后模块再也没有无故重启过。第三个坑是机智云的数据点精度设置。我在云端把温度数据点的精度设成了0.1但代码里上报的是整数。结果云端显示的温度永远是0。后来发现协议要求上报的数值必须乘以10再取整比如25.3度要上报253。这个细节在机智云的文档里写得很隐蔽我翻了好久才找到。所以定义数据点时一定要看清楚精度和步长的含义代码里做好单位换算。第四个坑是AT指令的响应超时。ESP8266在连接路由器时如果密码错误会一直重试而不返回ERROR。我的状态机原本设了10秒超时但模块重试了30秒才返回失败。后来我把超时时间改成35秒并且在超时后主动发送ATCWQAP断开当前连接再重新开始配网流程。这样即使密码错了也能在35秒内进入错误处理而不是无限等待。8. 后续可以继续折腾的方向这套基础框架跑通之后你可以往几个方向扩展。一是增加更多传感器比如PM2.5、二氧化碳、光照强度把数据点定义得更丰富做一个完整的环境监测站。二是加入本地控制逻辑比如温度超过阈值自动打开风扇不依赖云端指令提高响应速度。三是研究低功耗模式用STM32的Stop模式配合ESP8266的睡眠模式让设备用电池也能跑几个月。如果你对协议底层感兴趣可以尝试不用机智云的自动生成代码自己实现MQTT协议栈。这样虽然工作量大但能完全掌控通信过程也更容易适配其他云平台。不过对于大多数应用场景来说用现成的协议栈把产品快速做出来才是更务实的选择。毕竟能把东西跑通、稳定运行比什么都重要。
阅读完成 · 觉得有帮助?
咨询建站