ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于STM32与HX711的智能计价秤设计:从称重采集到ESP8266联网

基于STM32与HX711的智能计价秤设计:从称重采集到ESP8266联网 1. 为什么选择STM32做一台智能计价秤1.1 从菜市场到实验室一个被低估的嵌入式练手项目电子秤这东西几乎每天都在用菜市场、快递站、药店、厨房到处都是它的身影。但大多数人只把它当成一个工具从来没想过它内部是怎么跑的。我当初选这个题目说白了就是想找一个麻雀虽小五脏俱全的嵌入式项目——它得同时涉及模拟信号采集、数字通信、人机交互、数据存储最好还能联网。翻来覆去对比了一圈智能计价电子秤几乎是完美命中所有需求点。一台计价秤的核心逻辑其实很朴素称重传感器把重量转成微弱电压信号ADC把它变成数字量MCU做滤波和标定再乘以单价算出总价最后显示出来。听起来简单但真正动手做的时候你会发现光是称重这一环就够折腾好几天。HX711这个24位ADC芯片采样率到底选10Hz还是80Hz滤波窗口开多大零点漂移怎么处理每一个细节都直接影响最终精度。选STM32而不是51或者Arduino理由很直接。第一STM32的定时器资源丰富做按键扫描、数码管动态刷新、串口通信互不干扰第二内置Flash可以存单价和校准参数掉电不丢第三生态成熟HAL库、标准库、LL库随你挑网上资料多到看不过来。至于ESP8266那是给这台秤加联网属性的关键——称重数据上传到云平台远程就能看库存和销售记录这才配得上智能两个字。这个项目适合谁我觉得有三类人值得动手做一遍一是刚学完STM32基础外设、想找个综合项目练手的学生二是准备嵌入式岗位面试、需要一个能讲清楚的项目经历的求职者三是想给自己小店做一台定制计价秤的动手派。不管你是哪一类只要跟着把硬件搭起来、代码跑通你对嵌入式系统的理解会上一个台阶。1.2 整机方案怎么定模块化拆解思路做任何嵌入式项目我习惯先把系统拆成几个独立的功能块每块单独验证最后再拼起来。这台秤我拆成了五个模块称重采集模块、主控与计算模块、人机交互模块、数据存储模块、联网通信模块。称重采集用HX711配电阻应变式传感器这是目前低成本高精度称重的标配组合。HX711内部集成了稳压电源、时钟振荡器和24位ADC外围电路极其简单两个普通IO口就能读数据。主控选STM32F103C8T6也就是大家常说的最小系统板72MHz主频、64KB Flash、20KB RAM跑这个项目绰绰有余关键是便宜好买。人机交互我用了0.96寸OLED加四个机械按键。有人喜欢用数码管亮度高、看着像正经电子秤但数码管需要动态扫描占用定时器资源而且显示内容有限。OLED用I2C接口两根线搞定能显示中文、能画图标调试的时候还能当printf用性价比更高。按键方面去皮、单价设置、确认、清零四个键覆盖所有操作。数据存储直接用STM32内部Flash模拟EEPROM存单价、校准系数、累计销售额。这里有个坑要注意STM32的Flash擦写寿命大约1万次如果每次称重都写很快就废了。我的做法是只在单价修改和校准完成时写入日常称重数据放RAM里定时或关机前再存。联网模块用ESP8266-01S通过AT指令和STM32串口通信。为什么不用ESP32直接做主控因为ESP32的ADC精度和稳定性在称重场景下不如STM32配外置ADC而且分开设计的好处是联网挂了不影响称重系统更稳。ESP8266负责把重量、单价、总价、时间戳打包上传到云平台手机端就能远程查看。提示模块化设计最大的好处是调试时能快速定位问题。称重不准就只看HX711那一路联网不上就只看串口AT指令不会眉毛胡子一把抓。2. 硬件电路设计与关键器件选型2.1 称重传感器与HX711的搭配门道电阻应变式称重传感器的原理不复杂金属弹性体受力变形贴在表面的应变片阻值跟着变四个应变片组成惠斯通电桥输出一个和重量成正比的差分电压。常见的有5kg、10kg、20kg量程我建议做计价秤选5kg或10kg量程太大精度就稀释了。传感器输出灵敏度一般是1mV/V激励电压5V的话满量程输出才5mV。5kg量程对应5mV那1g重量只对应1微伏。这个信号有多微弱你用手靠近导线产生的工频干扰都比它大。所以HX711的24位ADC和内置低噪声放大器就派上用场了它能把信号放大128倍再采样等效分辨率轻松做到1g以下。接线的时候有个细节很多人忽略传感器四根线——激励正、激励负、信号正、信号负颜色各厂家不统一红黑绿白是最常见的但千万别凭颜色硬接。拿万用表量一下激励端电阻一般在1kΩ左右信号端在1kΩ左右两组之间应该不通。接反了不会烧但读数会反向或者乱跳。HX711的电路设计有几个关键点。第一模拟电源和数字电源要分开AVDD走独立的LC滤波否则数字电路的开关噪声会串进ADC。第二基准电压要稳HX711内部有稳压管但最好在VSUP和AVDD之间加个10μF钽电容。第三通道选择HX711有A、B两个通道A通道增益128B通道增益32称重必须用A通道。第四采样率选择芯片引脚RATE接低电平是10Hz接高电平是80Hz。10Hz抗干扰好但响应慢80Hz响应快但噪声大。计价秤我建议用10Hz因为人对一两秒的稳定时间完全能接受但读数跳动会让人怀疑秤不准。2.2 STM32最小系统与外围接口分配STM32F103C8T6的引脚分配得提前规划好不然画PCB或者搭洞洞板的时候会打架。我的分配方案是这样的功能模块引脚说明HX711数据PA0普通输入配置上拉HX711时钟PA1普通输出OLED SCLPB6I2C1时钟OLED SDAPB7I2C1数据按键去皮PA2输入上拉按键单价PA3输入上拉按键确认PA4输入上拉按键清零PA5输入上拉ESP8266 TXPA9USART1 TXESP8266 RXPA10USART1 RX调试串口PA13/PA14SWD下载调试这里有个经验HX711的时钟线不要和I2C或者串口共用同一组GPIO因为HX711读数据时时钟是软件模拟的时序要求严格如果被中断打断容易读错。我一开始把HX711的SCK放在PB6结果和OLED的I2C时钟冲突调试了半天才发现。电源部分整机用5V适配器供电AMS1117-3.3给STM32和OLED供电HX711直接用5V。ESP8266峰值电流能到300mA所以3.3V这一路要留足余量AMS1117至少选800mA的旁边并个220μF电解电容。2.3 ESP8266联网模块的接入方式ESP8266-01S只有8个引脚VCC、GND、TX、RX、CH_PD、RST、GPIO0、GPIO2。和STM32连接只需要四根线VCC、GND、TX、RX。注意ESP8266的TX接STM32的RXRX接STM32的TX交叉连接。CH_PD要拉高才能工作我直接接3.3V。GPIO0和GPIO2悬空就行正常运行时不需要管。供电是最大的坑ESP8266瞬时电流大如果和STM32共用一路3.3V且走线细联网瞬间电压跌落会导致STM32复位。我的做法是ESP8266单独用一个AMS1117供电或者至少在它的VCC脚旁边放一个100μF以上的电容。AT指令通信的波特率默认是115200但有些模块出厂是9600第一次用最好先发AT测试不回就换波特率再试。STM32这边用USART1开接收中断把ESP8266返回的数据存到环形缓冲区里主循环里解析。注意ESP8266和STM32的串口通信一定要加电平匹配。虽然ESP8266是3.3VSTM32也是3.3V理论上直连没问题但如果你的STM32板子是5V供电的TX输出5V电平会打坏ESP8266的RX。保险起见串个1kΩ电阻。3. 软件架构与核心算法实现3.1 主程序框架前后台还是RTOS这个项目要不要上RTOS我的答案是不用。任务数量不多实时性要求也不苛刻前后台架构完全够用而且代码更简单、更好调试。主程序就是一个大循环配合SysTick定时器做时间片调度。我的主循环结构是这样的int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); MX_USART1_UART_Init(); MX_TIM2_Init(); HX711_Init(); OLED_Init(); Flash_LoadParams(); ESP8266_Init(); while (1) { Key_Scan(); // 10ms调用一次 Weight_Process(); // 100ms调用一次 OLED_Refresh(); // 200ms调用一次 ESP8266_Process(); // 50ms调用一次 Price_Calculate(); // 重量更新后调用 } }每个任务的调用周期用SysTick的毫秒计数来控制比如if (tick - last_tick 10) { last_tick tick; Key_Scan(); }这种写法比RTOS简单得多而且不会出现任务优先级反转、栈溢出这些糟心事。对于计价秤这种单线程逻辑清晰的项目前后台架构是最优解。3.2 HX711驱动与称重滤波算法HX711的驱动核心是软件模拟时序。它没有标准SPI接口数据在时钟上升沿输出24个时钟脉冲读完24位数据第25个脉冲选择下一次的通道和增益。long HX711_Read(void) { long value 0; uint8_t i; while (HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) GPIO_PIN_SET); for (i 0; i 24; i) { HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); value value 1; HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); if (HAL_GPIO_ReadPin(HX711_DOUT_PORT, HX711_DOUT_PIN) GPIO_PIN_SET) value; } HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_SET); HAL_GPIO_WritePin(HX711_SCK_PORT, HX711_SCK_PIN, GPIO_PIN_RESET); value ^ 0x800000; return value; }读出来的原始值不能直接用必须滤波。我试过三种方案均值滤波、中值滤波、滑动窗口加权滤波。均值滤波简单但响应慢中值滤波抗脉冲干扰好但计算量大最后我选了去极值滑动平均——连续采10个值去掉最大最小各两个剩下6个求平均。这样既抑制了突发干扰又保证了响应速度。滤波之后是标定。标定分两步先空载读零点值再放标准砝码读满量程值算出斜率。weight (raw_value - zero_offset) * full_scale_weight / (full_scale_raw - zero_offset);这里有个细节零点会随温度漂移。早上校准好的秤中午可能就偏了几克。我的做法是每次称重前先判断重量是否小于某个阈值比如5g如果是就自动更新零点。这叫自动跟踪零点商用秤都有这个功能。3.3 计价逻辑与按键交互设计计价逻辑本身不复杂总价 重量 × 单价。但实际使用中有几个场景要处理好。去皮功能放上容器后按去皮键当前重量记为零点之后显示的重量是净重。这个功能在菜市场特别常用塑料袋、托盘都要去皮。单价设置按单价键进入设置模式用去皮和清零键加减数值确认键保存。单价支持到小数点后两位单位是元/千克。累计功能每笔交易完成后按确认键总价累加到累计销售额里存Flash。这个功能对小商户很实用一天下来不用自己加。按键处理我用状态机消抖。机械按键按下时会有10-20ms的抖动直接读会误触发。我的做法是每10ms扫描一次连续3次读到相同状态才确认。typedef struct { uint8_t current; uint8_t last; uint8_t count; uint8_t event; } Key_t; void Key_Scan(void) { for (int i 0; i KEY_NUM; i) { uint8_t read HAL_GPIO_ReadPin(keys[i].port, keys[i].pin); if (read ! keys[i].last) { keys[i].count 0; keys[i].last read; } else { if (keys[i].count 3) keys[i].count; if (keys[i].count 3 read 0) { keys[i].event 1; // 触发按键事件 } } } }3.4 OLED显示界面布局0.96寸OLED分辨率128×64能显示4行16×16的中文字符或者8行8×16的ASCII。我的界面布局是这样的第一行显示智能计价秤标题第二行显示当前重量第三行显示单价第四行显示总价。称重时重量数值用大字体设置模式下单价闪烁提示。OLED驱动用I2CSTM32的硬件I2C有时候会卡死我建议用软件模拟I2C虽然速度慢一点但稳定可靠。刷新率不用太高200ms刷一次肉眼完全看不出闪烁。显示内容要格式化比如重量显示1.234 kg单价显示12.00 元/kg总价显示14.81 元。浮点数转字符串用sprintf但要注意STM32的sprintf默认不支持浮点需要在工程设置里勾选Use MicroLIB或者自己写定点数转字符串函数。4. ESP8266联网与数据上云4.1 AT指令配置流程与避坑ESP8266出厂固件默认支持AT指令但不同版本指令集有差异。我用的AT固件版本是1.7.4配置流程如下AT # 测试模块是否响应 ATCWMODE1 # 设置为Station模式 ATCWJAPSSID,PWD # 连接WiFi ATCIPMUX0 # 单连接模式 ATCIPSTARTTCP,api.example.com,80 # 建立TCP连接 ATCIPSEND100 # 发送100字节数据每一步都要等模块返回OK才能进行下一步。这里最大的坑是超时处理。如果WiFi密码错了ATCWJAP会一直重试STM32如果死等就卡死了。我的做法是每个AT指令发出去后启动一个5秒定时器超时就报错重来。还有一个坑是ATCIPSEND之后要等提示符才能发数据很多人直接发数据导致失败。正确流程是ESP8266_SendAT(ATCIPSEND50\r\n); ESP8266_WaitFor(, 2000); ESP8266_SendData(payload, 50); ESP8266_WaitFor(SEND OK, 5000);4.2 数据上传协议设计上传的数据我用JSON格式简单通用{ device_id: SCALE_001, weight: 1.234, price: 12.00, total: 14.81, timestamp: 1234567890 }HTTP POST请求手动拼装void ESP8266_UploadData(float weight, float price, float total) { char json[128]; char request[256]; sprintf(json, {\weight\:%.3f,\price\:%.2f,\total\:%.2f}, weight, price, total); sprintf(request, POST /api/scale/data HTTP/1.1\r\n Host: api.example.com\r\n Content-Type: application/json\r\n Content-Length: %d\r\n \r\n %s, strlen(json), json); ESP8266_SendAT(ATCIPSTART\TCP\,\api.example.com\,80\r\n); ESP8266_WaitFor(OK, 5000); char cmd[32]; sprintf(cmd, ATCIPSEND%d\r\n, strlen(request)); ESP8266_SendAT(cmd); ESP8266_WaitFor(, 2000); ESP8266_SendData(request, strlen(request)); ESP8266_WaitFor(SEND OK, 5000); ESP8266_SendAT(ATCIPCLOSE\r\n); }提示HTTP请求的Content-Length必须和实际数据长度完全一致多一个字节少一个字节服务器都会拒绝。我一开始用strlen算长度结果JSON里有中文就出错了后来改成纯ASCII才稳定。4.3 断网重连与数据缓存策略联网最怕的就是网络不稳定。我的策略是称重数据先存本地联网成功后再批量上传。STM32的Flash里开一块区域做环形缓冲区每条记录16字节能存几百条。ESP8266每30秒尝试联网一次连上就把缓冲区里的数据全部发出去发完清空。断网检测靠AT指令的返回值。如果ATCIPSTART返回ERROR或者超时就标记网络断开等下一个周期再试。这里要注意不要频繁重连ESP8266重连太频繁会发热甚至死机我设的最小重连间隔是10秒。5. 调试过程中踩过的坑与排查实录5.1 称重读数跳动大的排查思路这是最常见的问题读数在±5g范围内乱跳。排查顺序如下第一步检查硬件。传感器接线是否牢固HX711的AVDD和DVDD是否干净模拟地和数字地是否单点连接。我遇到过因为杜邦线接触不良导致读数跳变的情况换了焊接线就好了。第二步检查电源。用示波器看HX711的VSUP引脚如果有明显的纹波加LC滤波。ESP8266联网瞬间的电流冲击也会影响HX711所以ESP8266的供电要和模拟部分隔离。第三步检查采样率。10Hz模式下读数稳定但慢80Hz模式下快但噪声大。如果用的是80Hz试试降到10Hz。第四步检查滤波算法。如果原始数据本身跳动就大滤波只能缓解不能根治。先打印原始值看波动范围如果原始值波动超过1000个LSB那就是硬件问题。第五步检查机械结构。传感器安装是否平整受力是否均匀有没有侧向力。传感器用螺丝固定时不要拧太紧否则会产生预紧力影响零点。5.2 ESP8266连不上WiFi的常见原因现象可能原因解决方法发AT无响应波特率不对依次试9600、115200发AT无响应CH_PD没拉高CH_PD接3.3V发AT无响应供电不足单独供电加100μF电容CWJAP返回ERROR密码错误检查大小写和特殊字符CWJAP返回ERROR路由器是5G频段ESP8266只支持2.4GCIPSTART超时服务器地址错误用IP地址代替域名试试频繁掉线电源纹波大加LC滤波换LDO我印象最深的一次是ESP8266死活连不上换了三个模块都不行最后发现是路由器开了AP隔离设备之间不能通信。这种问题查手册根本查不到只能靠经验。5.3 Flash参数丢失与写入异常处理STM32的Flash写入有几个硬性要求写入前必须先擦除擦除的最小单位是页1KB或2KB写入的数据必须是半字16位对齐。我一开始没注意对齐写进去的数据全是乱的。正确的Flash写入流程void Flash_WriteParams(Params_t *params) { HAL_FLASH_Unlock(); FLASH_EraseInitTypeDef erase; erase.TypeErase FLASH_TYPEERASE_PAGES; erase.PageAddress PARAMS_ADDR; erase.NbPages 1; uint32_t page_error; HAL_FLASHEx_Erase(erase, page_error); uint16_t *p (uint16_t *)params; for (int i 0; i sizeof(Params_t) / 2; i) { HAL_FLASH_Program(FLASH_TYPEPROGRAM_HALFWORD, PARAMS_ADDR i * 2, p[i]); } HAL_FLASH_Lock(); }还有一个坑Flash擦写期间CPU会暂停如果这时候有中断进来中断响应会延迟。对于计价秤影响不大但如果做实时控制就要注意了。6. 项目扩展方向与个人经验总结6.1 从计价秤到智能称重终端这台秤做完之后我发现可扩展的方向很多。加个热敏打印机就能打小票加个扫码枪就能扫商品条码自动调单价加个SD卡就能存几个月的销售记录。如果换成4G模块还能在没有WiFi的地方用。甚至可以把HX711换成更高精度的ADS1232做实验室级别的分析天平。软件层面可以把ESP8266换成ESP32直接跑MQTT协议数据实时推送到手机App。或者加个语音模块称重结果语音播报对老年人特别友好。6.2 给后来者的几点实在建议第一先跑通最小系统再扩展。别一上来就把所有模块都焊上去先让HX711能读出数再让OLED能显示再加按键最后加联网。每加一个模块就测试一次出问题好定位。第二善用串口打印。STM32的USART1接ESP8266了那就用USART2接USB转串口模块把调试信息打出来。没有串口打印的嵌入式调试就是盲人摸象。第三参数要能在线修改。校准系数、零点偏移、WiFi密码这些别写死在代码里做成可以通过按键或者串口命令修改的不然每次改都要重新烧录。第四注意电磁兼容。HX711是模拟电路STM32和ESP8266是数字电路两者要物理隔离。PCB布局时模拟部分和数字部分分开地平面单点连接。洞洞板搭的话HX711的线尽量短远离晶振和电源。第五多备几个传感器。应变式传感器很脆弱过载、摔打、受潮都可能损坏。我做项目期间坏了两个都是因为砝码放上去的时候没轻放。这个项目我从画图到调通大概花了三周其中一半时间在调称重精度四分之一在调ESP8266联网剩下四分之一在写界面和逻辑。如果你时间紧可以先不做联网把称重和计价做扎实那已经是一个完整的嵌入式项目了。联网部分可以后面慢慢加反正接口都留好了。
返回列表