基于STM32的HX711称重与ESP8266 MQTT上云OneNet实现
2026/9/11 5:43:58 网站建设 项目流程

简介:这套STM32物联网称重综合源码包,面向有一定单片机基础、希望学习Wi-Fi与云平台对接的嵌入式开发者,也适合高校实验课程与毕设参考。项目围绕ESP8266、OneNet与HX711展开,完整覆盖称重数据采集、本地滤波处理、Wi-Fi联网、MQTT消息上云等链路,可直接用于智能称重、健康监测、工业物料监控或物联网教学。压缩包共142个文件,核心为16个C源码与14个头文件,另有MDK工程文件、编译中间产物、链接脚本及Hex固件,整体8.87MB,目录中可看到LCD显示、定时器、系统配置等模块,便于按功能检索、移植与二次开发。已有188人学习下载。使用者能得到一套可运行的工程骨架,既包含HX711驱动与MQTT客户端对接示例,也包含Wi-Fi连接管理和数据解析逻辑,有助于快速搭建原型并理解多模块协作的嵌入式项目结构。

1. 从HX711称重到OneNet上云:一套能直接跑的STM32物联网工程

手头这套综合源程序,把四样东西串成了一条完整链路:STM32F4作为主控负责调度,HX711采集重量数据,ESP8266通过Wi-Fi接入网络,最后经MQTT协议把数据推到OneNet云平台。拿到源码包时第一反应是——终于有人把HX711校准、ESP8266 AT指令集、OneNet MQTT接入这三件最容易翻车的事放在同一个工程里了。

很多做智能称重、仓库物料监控的开发者卡在同一个地方:单模块调试都正常,一合起来就崩。要么HX711读数漂移,要么ESP8266连不上网,要么MQTT收发格式不对被平台拒收。这套代码的价值在于它给出了一个可运行的参考实现,从硬件初始化到云平台数据可视化都覆盖到了,适合做毕设、课程设计,也适合想快速验证物联网方案的工程师。

源码包里值得关注的文件是MqttKit.cHX711驱动和main.c的主流程编排,后面会逐一拆开讲实现细节和踩过的坑。需要明确的一点是,这套工程基于标准外设库而非HAL库,寄存器操作比较多,阅读时需要有意识地对照芯片参考手册。

2. 系统架构与器件选型:为什么是F4 + ESP8266 + HX711的组合

2.1 三层物联网架构在嵌入式侧的落地

典型的物联网终端设备在逻辑上分三层:感知层负责数据采集,网络层负责传输,应用层负责数据展示与控制。这个项目在硬件上对应得非常清晰——HX711加压力传感器构成感知层,ESP8266承担网络层职责,OneNet云平台及其可视化界面就是应用层。

STM32F4系列(特别是STM32F407)在这个架构里扮演的是“大脑”角色。之所以选F4而非F103,是因为F4主频高(168MHz)、带硬件浮点单元FPU,处理HX711的24bit原始数据转换和后续的滤波算法时,实时性有明显的优势。在源码包里看到system_stm32f4xx.c,也进一步确认了这是F4系列工程。

HX711之所以成为称重方案的首选,关键在三个参数:24bit ADC精度、内置稳压和可编程增益放大器(PGA),增益可选32/64/128。对于常见的5kg~50kg量程的悬臂梁或S型传感器,增益128配合合适的激励电压就能拿到足够的分辨率。值得留意的是HX711是低速ADC,输出速率默认10Hz或可选80Hz,这意味着它天然适合称重这类慢变量场景,不适合振动等动态测量。

2.2 ESP8266的三种工作模式与选型定夺

ESP8266在物联网开发中有三种主流用法,不少初学者容易混淆,这里先把差异说透。

第一种是AT指令模式,也是这套工程使用的方案。ESP8266内部烧录官方AT固件,STM32通过UART发送AT指令来控制Wi-Fi连接、TCP/UDP通信和MQTT协议交互。优点是逻辑清晰、调试方便,缺点是AT指令解析需要占用主控资源,且AT固件的MQTT功能受限于固件版本,有些功能实现不了。

第二种是SDK二次开发模式,直接用乐鑫官方ESP8266_RTOS_SDK编写应用程序,设备上电自动连接Wi-Fi并发布数据。这种方案功耗更低、实时性更好,但开发门槛明显更高,需要搭建交叉编译环境。

第三种是配合NodeMCU开发板使用Lua脚本,适合快速原型验证,不适合做产品级固件。

这个项目选AT模式是合理的,因为工程里MqttKit.c的核心工作就是封装AT指令:配置ESP8266为Station模式、连接Wi-Fi热点、建立与OneNet的MQTT连接、发布消息。整个流程用串口命令就能追踪,对学习物联网协议栈非常有帮助。我在实际调试中会先把ESP8266单独接USB转TTL,用PC串口助手手动发一遍AT指令确认固件正常,再接入STM32工程联调,这样可以快速定位问题是出在硬件还是代码。

2.3 HX711与压力传感器的电气连接要点

HX711模块通常有10个引脚,实际必须接的有7个:VCC、GND、SCK、DOUT,以及传感器侧的E+、E-、A+、A-。模块有两种供电方式——直接从STM32的3.3V取电,或者独立5V供电。需要注意HX711模块上的AVDD(模拟电源)通常由模块板载稳压电路产生,不要额外外接电源,否则容易损坏模块。

传感器侧接线遵循颜色规范:红接E+,黑接E-,绿接A+,白接A-。如果接反了,读数为负值——这实际上是判断接线是否正确的一个快速测试方法。对地线,如果传感器屏蔽线存在,应接到模块的GND。

HX711与STM32之间的数据接口只有两根线:PD_SCK(时钟)和DOUT(数据),用普通GPIO即可模拟时序,不占用硬件SPI或I2C外设,这也是HX711在MCU项目中广泛使用的原因。时序上只需注意一点:DOUT为低电平时表示数据准备好,此时SCK需要施加25个以上脉冲才能完成一次读数,前24个脉冲移出24bit数据,第25个脉冲用于通道和增益选择。

3. 主控初始化与HX711驱动实现:从寄存器到稳定读数

3.1 时钟树配置与引脚复用

STM32F4的初始化看system_stm32f4xx.c,但这部分代码在标准外设库中是模板化的,真正需要关注的是GPIO和USART的复用在main.c里如何配置。

void GPIO_Configuration(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIOA、GPIOB、GPIOC时钟 RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOB | RCC_AHB1Periph_GPIOC, ENABLE); // 串口1用于调试输出(PA9-TX, PA10-RX) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // 复用功能模式 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽输出 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; // 上拉 GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1); GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1); // 串口2用于ESP8266通信(PA2-TX, PA3-RX) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; GPIO_Init(GPIOA, &GPIO_InitStructure); GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); // HX711数据引脚(示例:PC0-DOUT, PC1-SCK) GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN; GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; GPIO_Init(GPIOC, &GPIO_InitStructure); GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; GPIO_Init(GPIOC, &GPIO_InitStructure); }

这段代码的核心逻辑是把STM32F4的GPIO从默认状态切换为特定的复用功能(AF)。GPIO_PinAFConfig这一句决定了引脚具体映射到哪个外设——比如PA9映射到USART1_TX,PA2映射到USART2_TX。F4的GPIO模式配置比F103多一点,GPIO_OTypeGPIO_PuPd分别控制输出类型和上下拉,这两项漏配是常见的通信异常的根源。

串口1和串口2的分工必须明确:串口1接PC用于打印调试日志,串口2接ESP8266用于数据交互。两个串口的中断优先级要区分开,调试串口可以稍低,ESP8266通信串口要保证数据接收不丢失。常见的坑是串口2的RX中断优先级设置不当,导致Wi-Fi模块返回的MQTT应答包丢失。

3.2 HX711时序驱动的完整实现

HX711的驱动核心是模拟时序,HX711.c中的关键函数是读取一次转换结果的ReadCount,其实现依赖SCK脉冲的精确控制。

// 读取HX711转换结果 // 返回值: 24bit有符号数,范围-8388608~8388607 unsigned long HX711_Read(void) { unsigned long count = 0; unsigned char i; HX711_DOUT_SET_INPUT_MODE(); // DOUT设为输入 HX711_SCK_SET_LOW(); // SCK拉低,进入等待状态 // 等待DOUT变低,表示转换完成 while(HX711_DOUT_READ() == 1); // 读取24位数据,MSB先行 for(i = 0; i < 24; i++) { HX711_SCK_SET_HIGH(); // 上升沿移出数据 delay_us(1); // 脉冲宽度至少1us HX711_SCK_SET_LOW(); delay_us(1); count = count << 1; // 左移一位,准备接收下一位 if(HX711_DOUT_READ() == 1) // 读取当前位 { count++; } } // 第25个脉冲:选择通道A、增益128 HX711_SCK_SET_HIGH(); delay_us(1); HX711_SCK_SET_LOW(); return count; }

代码逻辑分为三个阶段:等待数据就绪、移位读取24位数据、发送第25个脉冲选择下一次转换的通道和增益。其中第25个脉冲发送后,HX711会自动开始下一次转换,持续时间为转换周期(10Hz模式下约100ms),这就是为什么连续读取时需要适当的间隔。

参数上有几个需要留意的细节。delay_us(1)是最低要求,实际STM32主频较高,delay_us函数如果基于SysTick实现,误差可以控制在微秒级。如果SCK频率太高导致读数不稳定,可以适当增加延时。HX711的DOUT在24个SCK脉冲之后会恢复高电平,如果发现DOUT始终为高或读出的数据为全1,大概率是传感器接线松动或者供电异常。

读回的24bit数据是无符号形式,但实际是二进制补码的有符号数。数据手册中给出的换算关系是:重量 = (原始值 - 零点值) / 灵敏度系数。因此需要一个校准流程:空载时记录零点值,放上已知重量的砝码记录满量程值,然后线性换算。

3.3 滤波与校准:让称重数据稳定可用的关键一步

裸读HX711的数据在静态称重场景下会有正负几个码的跳动,对应到重量上可能波动10g~20g,这在计重场景里不够用。工程中通常的做法是做均值滤波加中值滤波的组合。

// 中值滤波:取5次采样排序后取中间值 long Median_Filter(void) { long buf[5]; unsigned char i, j; long temp; // 连续采样5次 for(i = 0; i < 5; i++) { buf[i] = (long)HX711_Read(); delay_ms(10); // 等待下一次转换完成 } // 冒泡排序,代码简洁优先 for(i = 0; i < 4; i++) { for(j = 0; j < 4 - i; j++) { if(buf[j] > buf[j+1]) { temp = buf[j]; buf[j] = buf[j+1]; buf[j+1] = temp; } } } return buf[2]; // 返回中间值 }

中值滤波对脉冲干扰有很好的抑制效果,代价是每次读数需要50ms左右。如果要求更高的响应速度,可以降为3次采样取中值。delay_ms(10)的间隔设置要大于HX711的转换周期,实测10Hz输出速率下,两次转换之间的最小间隔约为100ms,这里的10ms可能偏小,实际调试中建议改为100ms以稳定读取。

校准的核心是两点:标定零点、标定斜率。操作流程是上电预热30秒后空秤读数10次取平均作为零点偏移值,然后放上标准砝码(比如1kg)记录读数,计算出每克对应的ADC码值。这个斜率值要写入Flash或者常量区,掉电不能丢失。工程里相关参数一般在main.c顶部以宏定义形式给出,拿到源码后第一件事就是确认这两个值是否与自己的传感器匹配,否则显示重量会完全不对。

4. ESP8266接入与MQTT上报:从AT指令到OneNet云端

4.1 ESP8266初始化:从启动到连接Wi-Fi的完整流程

ESP8266上电后的初始化顺序非常讲究,esp8266_init函数里包含了一系列必要的AT指令,逐条发送并检查返回值。

// ESP8266初始化函数 // 返回值: 0-成功, -1-失败 int ESP8266_Init(void) { char buf[128]; // 1. 设置Wi-Fi模式为Station // AT+CWMODE=1 表示Station模式,2表示AP模式,3表示双模式 ESP8266_SendCmd("AT+CWMODE=1\r\n", "OK", 3000); // 2. 连接Wi-Fi热点,替换为实际SSID和密码 // ESP8266会返回WIFI CONNECTED和WIFI GOT IP sprintf(buf, "AT+CWJAP=\"%s\",\"%s\"\r\n", WIFI_SSID, WIFI_PASSWORD); ESP8266_SendCmd(buf, "WIFI GOT IP", 10000); // 3. 启用多路连接模式,MQTT通常需要场景下建议开启 // 0-单连接,1-多连接 ESP8266_SendCmd("AT+CIPMUX=1\r\n", "OK", 1000); // 4. 关闭透明传输模式,使用普通传输模式便于指令交互 ESP8266_SendCmd("AT+CIPMODE=0\r\n", "OK", 1000); return 0; }

这段代码的关键处理是每个AT指令的等待应答机制。ESP8266_SendCmd函数会阻塞等待串口收到指定字符串,超时后返回失败。在实际调试时,串口返回的数据可能夹杂着Wi-Fi事件通知(例如WIFI DISCONNECT),需要用字符串匹配的方式在完整接收缓冲区中查找目标应答。

Wi-Fi连接成功的时间取决于路由器信号强度,代码里超时设置的10秒通常足够。AT指令的回车换行\r\n必须带全,乐鑫固件对指令末尾的\r\n有严格要求,只发\n可能导致指令不被识别。连接Wi-Fi时要注意ESP8266只能连接2.4GHz频段,5GHz热点是完全搜不到的,这是家里人用双频路由器时最常见的故障点。

AT+CIPMUX=1启用多连接模式后,后续所有TCP或MQTT操作都需要带link ID参数,这个参数决定了数据走哪条连接通道。OneNet平台连接一般使用link ID 0即可。

4.2 MQTT协议在OneNet上的接入细节

OneNet的MQTT接入与其他平台(如阿里云IoT、AWS IoT)有不少差异,主要体现在鉴权方式和topic设计上。看MqttKit.c能直接看到作者的实现思路。

// 连接OneNet MQTT服务器 // 参数: none // 返回值: 0成功,-1失败 int MQTT_Connect(void) { char cmd[128]; // OneNet MQTT服务器地址和端口 // 产品ID在OneNet控制台创建产品后获取 // 鉴权信息由产品ID和设备ID拼接而成 // 设置MQTT连接的host sprintf(cmd, "AT+MQTTHOST=\"183.230.40.39\",1883\r\n"); ESP8266_SendCmd(cmd, "OK", 5000); // MQTT客户端ID格式: 产品ID+设备ID(OneNet平台规则) // 用户名: 产品ID + token // 密码: apiKey(在OneNet控制台生成) sprintf(cmd, "AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n", CLIENT_ID, USERNAME, API_KEY); ESP8266_SendCmd(cmd, "OK", 5000); // 连接MQTT服务器 ESP8266_SendCmd("AT+MQTTCONN=0,\"183.230.40.39\",1883,1\r\n", "OK", 5000); return 0; }

需要特别说明的是,这里的用户名、密码配置并不是OneNet注册时的账号密码,而是平台为设备分配的鉴权参数。OneNet的MQTT接入要求:ClientID格式为产品ID+设备ID,User Name为产品ID+token,Password为apiKey。初学者最常见的问题是直接在控制台注册用户的账号密码填进来,结果一直报鉴权失败。

AT指令版本中AT+MQTTUSERCFG的参数依次为:linkID(0)、使能标志(1)、ClientID、Username、Password、证书使能(0)、证书类型(0)、证书内容(空)。这里0,0,""对应当前不使用TLS加密连接,OneNet的1883端口也支持明文MQTT。

工程源码里有onenet的apiKey相关的宏定义,在接入前需要去OneNet控制台找到对应产品,在设备列表里生成或复制apiKey。每个产品的apiKey独立,一旦复制错产品,连接后会出现在线但数据流无法创建的情况。

4.3 数据上报与应答处理

数据上报是MQTT通信的核心环节。OneNet平台的数据流命名规则是datastream_id,上报格式是JSON或类型值,具体看数据流的定义。

// 上报重量数据到OneNet // 参数: weight - 重量值(单位g) // 返回值: 0成功,-1失败 int MQTT_Publish_Weight(int weight) { char payload[64]; char cmd[128]; // OneNet平台的标准JSON报文格式 // ds_id为数据流ID,在平台上需提前定义或在首次上报时自动创建 sprintf(payload, "{\"weight\":%d}", weight); // AT+MQTTPUB=linkID, topic, payload长度, QoS, retain // topic固定为"$dp",表示向OneNet上传数据点 sprintf(cmd, "AT+MQTTPUB=0,\"$dp\",%d,0,0\r\n", strlen(payload)); ESP8266_SendCmd(cmd, ">", 2000); // 等待>提示符 // 发送实际数据 ESP8266_SendRaw(payload); ESP8266_SendCmd("\r\n", "OK", 3000); return 0; }

MQTT发布数据的AT指令序列是两步交互:先发送AT+MQTTPUB命令和payload长度,固件回应>提示符后,再发送实际payload内容,最后以\r\n结束。$dp是OneNet平台特殊topic,专门用于设备上传数据点,与平台默认的数据流解析规则绑定。

在OneNet平台上创建数据流时,一个常见的坑是数据流名称与代码中JSON的key不一致。比如代码里写{"weight":100},数据流名称就要定义为weight,不能写成Weight压力。平台大小写敏感,一个字符对不上,在设备日志里能看到数据上报成功,但在数据流查看页面却始终没有新记录。

QoS参数这里设为0(最多一次),称重场景下数据丢一两帧不影响整体曲线,但如果做设备控制指令下发,QoS应该设为1以保证送达。将发送频率控制在合理范围也很重要,OneNet对单设备上报速率有限制,实测保守控制在每秒1次以内的上报频率比较安全,频繁上报可能触发平台侧限流。

5. 串口打印、LCD显示与主循环设计

5.1 双串口日志系统的实现

工程中分两路输出调试信息,一路通过串口1到PC串口助手,另一路输出到LCD显示屏。串口1的日志实现其实是一个轻量级的printf重定向。

// 重定向printf到串口1 int fputc(int ch, FILE *f) { // 等待发送寄存器为空 while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); USART_SendData(USART1, (uint8_t)ch); return ch; }

fputc重定向之后,工程里所有printf语句都会输出到串口1,在PC端用串口助手以115200波特率即可查看日志。这套机制在调试时的价值非常大,因为能实时看到ESP8266返回的AT应答和MQTT连接状态,而不需要外接逻辑分析仪。

日志打印要讲究级别控制,我一般会把代码分成INFO、WARN、ERROR三种输出。实际使用时,调试早期把全部打印都打开,联调阶段只保留ERROR级别,避免大量Wi-Fi事件刷屏导致串口数据被淹没。

5.2 LCD驱动与界面刷新逻辑

LCD.c中实现的是典型的SPI或并行接口屏幕驱动,取决于具体屏幕型号。代码中的LCD显示内容包含三块区域:顶部显示系统状态(Wi-Fi连接、MQTT连接、云端在线),中部显示当前重量值(大字显示),底部显示时间或计数。

// 刷新LCD显示内容 // 参数: weight - 当前重量, state - 系统状态码 void LCD_Refresh_Display(int weight, unsigned char state) { char buf[32]; // 清空重量显示区域 LCD_Fill_Rect(0, 40, 239, 80, WHITE); // 显示重量数值,保留一位小数 sprintf(buf, "%.1f kg", weight / 1000.0f); LCD_ShowString(20, 50, buf, BLACK, WHITE, 32); // 显示连接状态 LCD_ShowString(20, 10, "State:", BLACK, WHITE, 16); switch(state) { case 0: LCD_ShowString(80, 10, "WIFI OFF ", BLACK, WHITE, 16); break; case 1: LCD_ShowString(80, 10, "WIFI ON ", BLACK, WHITE, 16); break; case 2: LCD_ShowString(80, 10, "MQTT OK ", BLACK, WHITE, 16); break; default: break; } }

LCD刷新要注意一个性能问题——在STM32上刷新整块屏幕比较耗时,特别是无硬件加速的RGB屏幕。实际工程中只刷新变化区域,重量显示区域每次更新,状态区域只有状态变化时才更新,这是嵌入式UI开发中常用的局部刷新思想。

5.3 主循环的状态机设计

main.c里主循环是一个典型的状态机结构。用状态机而非顺序执行,是为了让系统在Wi-Fi断线、MQTT掉线等异常情况下能够自主恢复。

// 系统状态定义 #define SYS_STATE_INIT 0 // 系统初始化 #define SYS_STATE_WIFI 1 // WiFi连接 #define SYS_STATE_MQTT 2 // MQTT连接 #define SYS_STATE_RUN 3 // 正常运行 #define SYS_STATE_ERROR 4 // 异常处理 int main(void) { unsigned char state = SYS_STATE_INIT; unsigned char lastState = 0xFF; int weight = 0; // 硬件初始化 SystemInit(); GPIO_Configuration(); USART_Configuration(); HX711_Init(); LCD_Init(); while(1) { switch(state) { case SYS_STATE_INIT: // 先让传感器稳定,再启动Wi-Fi模块 delay_ms(1000); state = SYS_STATE_WIFI; break; case SYS_STATE_WIFI: // 连接Wi-Fi,失败则重试 if(ESP8266_Init() == 0) state = SYS_STATE_MQTT; else delay_ms(3000); // 3秒后重试 break; case SYS_STATE_MQTT: // 连接OneNet MQTT服务器 if(MQTT_Connect() == 0) state = SYS_STATE_RUN; else state = SYS_STATE_ERROR; break; case SYS_STATE_RUN: // 读取重量数据并上报 weight = Get_Filtered_Weight(); // 滤波和标定后的重量 // 打印和显示 printf("Weight: %d g\r\n", weight); if(weight != lastDisplayWeight) { LCD_Refresh_Display(weight, state); MQTT_Publish_Weight(weight); } delay_ms(1000); break; case SYS_STATE_ERROR: // MQTT连接失败,停留3秒后回到Wi-Fi重连 LCD_ShowString(20, 10, "NET ERR ", BLACK, WHITE, 16); delay_ms(3000); state = SYS_STATE_WIFI; break; } } }

状态机设计的精妙之处在于异常恢复路径清晰。MQTT连接失败后直接回到Wi-Fi连接状态,因为很多时候MQTT连接失败的原因是网络已经断开,需要重新建立Wi-Fi会话。SYS_STATE_INIT里等待1秒是为了让HX711完成上电稳定,这段时间内芯片内部还在自校准,如果立即读取会得到无效数据。

运行状态下每秒检测一次重量变化并上报,这个刷新率在称重场景足够了。lastDisplayWeight用于避免重复上报——如果重量没有变化就不发MQTT消息,既省流量又减少云端冗余数据。实际使用中如果发现重量缓慢漂移导致频繁上报,可以在比较时加一个死区判断,例如变化超过5g才触发上报。

6. 编译烧录与常见问题排错

6.1 Keil工程配置与芯片包安装

打开工程后第一件事是确认Keil版本和芯片包。工程文件表中出现了HX711.uvguix.23674,这是Keil MDK的用户界面布局文件,而.uvprojx才是真正的工程文件。如果打开工程时提示找不到设备,说明STM32F4的芯片支持包(DFP)没有安装。

在Keil5中安装芯片包的步骤是:点击菜单栏的Pack Installer图标,在搜索框输入STM32F4,找到对应型号的设备支持包并安装。需要注意Keil5的Pack安装器界面是全英文的,在Packs标签页中勾选STMicroelectronics目录下的STM32F4xx_DFP,版本选择较新的稳定版即可。

编译时如果报Error: no STM32 target found!,这个错误与芯片包无关,而是调试器连接问题。常见原因是STM32芯片内部的调试接口被禁用(读保护或SWD引脚被复用),解法是按住复位键的同时点击下载,在芯片复位瞬间建立连接,或者用ST-Link Utility的Connect under reset模式连接。如果芯片已经全片擦除后仍连不上,检查ST-Link的固件版本是否需要升级,旧的ST-Link固件对F4系列支持不完善。

6.2 硬件排错:数据异常先查信号再查代码

拿到源程序后最容易踩的坑集中在三个环节:HX711度数异常、ESP8266连接失败、MQTT连接成功但云端无数据。把每个环节的排错路径列出来,按检查顺序从硬件到软件依次验证。

HX711度数异常有两种典型现象。第一种是读数恒为0x7FFFFF或0x800000,这说明DOUT引脚电平没有正确跳变,用万用表测DOUT引脚电压,正常情况应该在3.3V或0V之间摆动,如果保持恒定高电平,大概率是SCK没有产生时钟脉冲,排查GPIO配置是否正确,或程序是否卡在while(HX711_DOUT_READ() == 1)这个死循环里。第二种是读数漂移较大,先检查传感器供电是否稳定,HX711模块上的电源指示灯是否常亮,然后用示波器看SCK波形——如果SCK存在毛刺(上升沿不陡峭),可能是杜邦线过长,建议换成双绞线或屏蔽线。

ESP8266连接失败时,先用USB-TTL模块单独测试模块。将ESP8266的TX接USB-TTL的RX,RX接USB-TTL的TX,VCC接3.3V,GND共地,串口助手发送ATE0关闭回显,再发AT\r\n,如果返回OK则硬件正常。接着发AT+CWJAP="SSID","密码"测试Wi-Fi连接能力,如果返回ERROR检查密码或确认热点是否在2.4GHz频段。很多ESP8266模组是3.3V供电,电流峰值可达300mA,劣质USB-TTL的3.3V输出能力不足会导致模块频繁重启。

MQTT连接成功但云端无数据,要重点检查数据流命名。登录OneNet控制台进入设备详情页,查看数据流标签下是否有新数据点,如果没有则说明上报的topic或者数据流ID有误。查看设备日志,OneNet平台会记录设备的每条上行消息,通过返回的code(如9200)可以定位是格式错误还是权限问题。常见的code含义:9200表示成功,9210表示数据流不存在,9220表示权限不足。代码里数据流ID是一开始就写死的,如果平台侧修改过数据流名称,需要同步修改代码中的JSON的key。

6.3 进阶建议:从这套工程学到什么

这套工程把感知采集、联网通信、云平台接入三个模块拧在了一个主循环里,很适合作为物联网开发的第一块跳板。读完这套代码后,值得动手做的改进有以下几项。

一是把AT指令模式换成MQTT透传模式,ESP8266的AT固件在QoS非0时存在已知的稳定性问题,如果要用于产品原型,可将OneNet的MQTT协议栈移植到ESP8266内部,用SDK模式取代AT模式,减少一次串口中转,降低丢包率。

二是引入RTOS。主循环状态机在简单场景下够用,但加入按键扫描、LCD刷新、看门狗喂狗、MQTT心跳等任务后会变得凌乱。如果能上FreeRTOS,每个功能模块拆成独立任务,用消息队列传递重量数据和网络状态,整个架构的可维护性会有质的提升。

三是校准数据存储到Flash。代码中校准值如果写在宏定义里,每次重新烧录都要反复标定,可以改成在首次校准时写入STM32内部Flash的最后一个扇区,上电时先读取Flash中的校准值,再配合一个校准按键触发重新标定。这样在实际部署时省力很多。

四是增加本地显示和超限报警。在LCD上画出最近一小时的重量趋势图,设定上下限阈值,超限时通过MQTT向OneNet下发报警数据流,再用OneNet的应用编辑功能做短信或邮件通知,这套扩展路径在工业物料监控场景中很实用。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询