ESP32+HX711智能快递秤:防丢告警与Android App全链路实战 1. 从快递被顺走说起这个秤到底要解决什么问题住在公寓楼里的人大概都有过这种经历快递员把包裹往门口一放拍张照走人等你下班回家门口空空如也。找快递公司对方甩出那张已送达的照片你哑口无言。找物业调监控流程繁琐不说很多时候角度根本拍不到。这个痛点催生了大量门口快递防盗的需求而我要分享的这套Smart Parcel Scale System思路和常见的摄像头方案完全不同——它不靠看靠称。核心逻辑非常朴素在门口放一块压力感应平台快递员把包裹放上去的瞬间系统记录下重量同时通过手机 App 推送通知给你。包裹被拿走时重量归零系统再次记录时间点。如果这个拿走发生在你本人确认之前那就是异常事件App 立刻告警。整套系统的关键词是ESP32、IoT、HX711、Android Studio、3D Printer——主控用 ESP32称重传感器信号调理用 HX711 模块手机端用 Android Studio 开发原生 App外壳和承重结构用 3D 打印机自己做。这套东西适合谁我认为有三类人值得动手一是住在公寓、经常收快递但门口无人看管的上班族二是想系统学习 ESP32 传感器 移动端全链路 IoT 项目的开发者三是喜欢用 3D 打印解决实际生活问题的手工爱好者。它不需要你精通射频或嵌入式底层但需要你有基本的接线能力、能看懂 Arduino 代码、愿意折腾 Android 环境。下面我会把整个项目从硬件选型、结构设计、固件逻辑到 App 通信一层层拆开讲清楚包括我踩过的坑和最后怎么绕过去的。2. 硬件选型为什么是 ESP32 HX711 这套组合2.1 ESP32 在这个项目里承担的角色选 ESP32 而不是 Arduino Uno 或树莓派理由很实际。这个系统需要同时做三件事读取称重传感器、维持 Wi-Fi 连接并和手机通信、在本地做简单的状态判断。Arduino Uno 没有原生 Wi-Fi加 ESP8266 模块会让接线和代码复杂度翻倍树莓派功耗高、成本高而且对于读一个传感器 发个通知这种任务属于杀鸡用牛刀。ESP32 自带 Wi-Fi 和蓝牙双模双核处理器价格在二十到四十块之间深度睡眠模式下功耗可以压到微安级——这对一个需要 24 小时通电、但又不想天天充电的设备来说太关键了。具体到型号我建议用ESP32-WROOM-32模组开发板也就是市面上最常见的那个版本。它引出足够多的 GPIO供电范围 3.0V 到 3.6V板载 USB 转串口芯片方便烧录。如果你手头有 ESP32-S3 或 C3也能用但要注意引脚定义不同HX711 的数据引脚和时钟引脚需要重新映射。我实测下来WROOM-32 的社区资料最全遇到问题最容易搜到答案新手别一上来就选冷门型号。2.2 HX711 与称重传感器的匹配计算HX711 是一颗 24 位模数转换芯片专门为高精度称重设计。它内部集成了稳压器和时钟只需要两个 GPIO 就能和 ESP32 通信一个负责数据DT一个负责时钟SCK。称重传感器我选的是50kg 量程的电阻应变式传感器也就是常见的人体秤传感器那种长条金属块。这里有个关键计算必须做HX711 是 24 位 ADC但有效位数受噪声影响实际可用大约 20 位左右。50kg 量程分摊到 20 位理论分辨率是 50000g / 2^20 ≈ 0.048g。听起来很美好对吧但实际因为传感器本身的非线性、温漂和机械安装误差真实可用精度大概在 5g 到 10g 之间。对于判断包裹在不在这个需求5g 精度绰绰有余——一个空纸箱都不止 50g。所以选 50kg 量程是合理的既覆盖了大件快递又保证了足够的分辨率。如果你只收小件可以换 5kg 或 10kg 传感器精度会更高但量程余量就小了。接线方面传感器有四根线红E、黑E-、白A-、绿A。红黑接 HX711 的 E 和 E-白绿接 A- 和 A。注意激励电压HX711 的 VCC 接 ESP32 的 3.3V 还是 5V 会影响输出灵敏度我建议接 3.3V和 ESP32 逻辑电平一致避免电平转换的麻烦。HX711 的 DT 接 ESP32 的 GPIO 16SCK 接 GPIO 4这两个引脚在 WROOM-32 上都是普通 IO没有特殊功能冲突。2.3 供电与结构件的取舍供电我用的是5V 2A 的 USB 电源适配器通过开发板的 USB 口供电。为什么不加电池因为门口这个位置通常有插座而且 Wi-Fi 常连的功耗不低电池方案需要频繁充电体验很差。如果你确实需要电池供电可以加一块 18650 电池和 TP4056 充电模块但代码里必须加入深度睡眠逻辑——没有包裹时每 30 秒唤醒一次读重量有包裹时保持常连。这个改动会让固件复杂度上升不少新手建议先跑通有线供电版本。结构件是这套系统里最容易被低估的部分。称重传感器必须被正确安装才能准确读数它的一端固定在底座上另一端悬空承受平台重量。如果两端都被螺丝锁死传感器无法形变读数就是废的。我用3D 打印机打了一个底座和一个承重平台底座上留了传感器一端的固定孔平台则压在传感器悬空端。材料用 PLA 就行但要注意平台面积不要太大否则杠杆效应会放大误差。我第一版平台做了 20cm x 20cm结果放偏一点读数就飘后来缩到 15cm x 15cm稳定性明显好转。3. 固件开发HX711 读数、滤波与状态机设计3.1 让 HX711 稳定出数的三个关键设置HX711 的 Arduino 库有好几个版本我推荐用bogde/HX711这个库稳定且文档清晰。初始化时有两个参数必须设置set_scale()和tare()。set_scale()是校准系数需要你用已知重量的物体反复测试得出。我的做法是先放一个 500g 的砝码读出原始值再放 1000g 砝码读出原始值用差值算出每克对应的原始计数取倒数就是 scale 值。这个过程我重复了五次取平均最后得到的系数让 500g 砝码的读数误差控制在 2g 以内。tare()是去皮必须在系统启动、平台空载时调用。但这里有个坑如果启动时平台上恰好有东西去皮就会把那个重量当成零点。我的解决方案是在固件里加一个启动检查——如果去皮后的读数绝对值大于 20g就闪烁板载 LED 提示请清空平台等待 5 秒后重新去皮。这个逻辑看起来简单但实际部署时救了我好几次因为快递员可能在你重启设备的瞬间正好放了个包裹。第三个关键是采样率和滤波。HX711 默认输出速率是 10Hz每秒 10 个样本可以通过修改寄存器改成 80Hz但速率越高噪声越大。我的做法是保持 10Hz然后在代码里做滑动平均连续读 10 个样本去掉最大值和最小值剩下 8 个取平均。这样一次有效读数需要 1 秒对于称重场景完全够用。实测下来滤波后的读数波动在 ±3g 以内比原始数据稳定了一个数量级。3.2 状态机从重量变化到事件的映射光有重量数据没用系统需要把重量变化翻译成有意义的事件。我设计了一个四状态机IDLE空闲、PLACED已放置、CONFIRMED已确认、ALERT告警。状态转换逻辑如下IDLE 状态下如果重量从接近 0 跳变到大于 50g进入 PLACED记录时间戳和重量通过 Wi-Fi 推送有新包裹通知到手机。PLACED 状态下如果重量回到接近 0说明包裹被拿走。此时检查手机端是否发来过确认收货指令。如果有进入 CONFIRMED然后回到 IDLE如果没有进入 ALERT推送包裹异常移动告警。ALERT 状态下持续推送告警直到用户在 App 里手动解除然后回到 IDLE。这个状态机的关键在于阈值设定。50g 的放置阈值是为了过滤掉风吹、猫踩、平台自身热胀冷缩带来的微小波动。而接近 0我定义为小于 20g留出一点余量。这两个阈值我调了大概一周最后发现 50g 和 20g 的组合误报率最低。如果你住在风大的走廊可以把放置阈值提到 100g但那样小件快递可能识别不到需要自己权衡。3.3 Wi-Fi 通信协议MQTT 还是 HTTPESP32 和手机通信有两条路MQTT 和 HTTP。我一开始用 HTTPESP32 作为服务器手机定时轮询。问题很明显轮询间隔短了费电间隔长了事件延迟大而且手机在后台时轮询会被系统限制。后来换成MQTTESP32 作为客户端连接到公共 MQTT 代理比如 EMQX 的免费实例手机也连同一个代理订阅同一个主题。这样事件是推送式的延迟在 1 秒以内而且 ESP32 不需要一直保持 HTTP 连接功耗更低。MQTT 的主题设计我用了三层结构parcel/scale01/event用于事件推送parcel/scale01/command用于手机下发指令。消息体用 JSON比如{type:placed,weight:1250,ts:1690000000}。这里要注意QoS 等级我选的是 QoS 1至少送达一次因为包裹事件丢一条可能就意味着一次误判。QoS 2 虽然更可靠但开销大对于这个场景没必要。还有一个细节ESP32 的 MQTT 客户端要设置keepalive和last will。keepalive 我设了 60 秒last will 主题设为parcel/scale01/status消息为offline。这样如果 ESP32 掉线手机会收到离线通知而不是傻等。这个设计在实际使用中很有用有一次我家路由器重启手机立刻收到了离线告警而不是以为包裹一直没动。4. Android App从 MQTT 订阅到告警通知的完整链路4.1 用 Android Studio 搭建最小可用工程Android 端我用Android Studio开发语言选 Kotlin最低 API 等级设到 26Android 8.0这样能覆盖绝大多数在用设备同时可以用通知渠道Notification Channel等新特性。工程结构很简单一个 MainActivity 显示当前状态和重量一个 MqttService 负责后台连接和消息接收一个 NotificationHelper 负责推送告警。MQTT 客户端库我用的是Eclipse Paho Android在build.gradle里加依赖org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5和org.eclipse.paho:org.eclipse.paho.android.service:1.1.1。注意 Paho 的 Android Service 版本比较老在新版 Android 上需要手动处理前台服务权限否则后台连接会被杀。我的做法是把 MqttService 设为前台服务在通知栏常驻一个快递秤运行中的通知这样系统就不会回收它。连接参数方面broker 地址、端口、用户名密码我放在local.properties里不硬编码在代码中。端口用 8883MQTT over TLS虽然公共代理的免费实例不一定支持 TLS但如果你自己搭 broker强烈建议开 TLS否则重量数据在公网上裸奔。客户端 ID 用android-加设备唯一标识避免多个手机连同一个 broker 时互相踢下线。4.2 消息解析与本地状态同步App 收到 MQTT 消息后需要解析 JSON 并更新 UI。这里有个常见问题MQTT 回调在后台线程直接更新 UI 会崩溃。我的做法是用LiveData或StateFlow把消息从 Service 传到 Activity。具体来说MqttService 里维护一个MutableLiveDataParcelEvent收到消息后postValueActivity 里observe这个 LiveData 并更新界面。这样线程安全代码也清晰。本地状态同步是另一个容易出问题的地方。App 需要知道当前是有包裹还是无包裹这个状态不能只靠最后一条消息判断因为消息可能丢失。我的方案是在 App 本地维护一个状态变量每次收到placed事件就设为有包裹收到removed事件就设为无包裹同时记录时间戳。如果超过 24 小时没有收到任何事件就显示状态未知请检查设备。这个兜底逻辑避免了 App 显示的状态和实际不符。4.3 告警通知的优先级与免打扰处理告警通知是这套系统的核心价值如果通知被系统静音或折叠整个项目就白做了。我在 NotificationHelper 里做了三件事第一创建独立的通知渠道重要性设为HIGH这样通知会弹出横幅并响铃第二设置setCategory(Notification.CATEGORY_ALARM)让系统知道这是告警类通知第三对于包裹异常移动这种紧急事件使用setFullScreenIntent在锁屏上直接显示。但这里有个平衡如果用户在家包裹被家人拿进屋也会触发告警。我的解决方案是在 App 里加一个我已知晓按钮用户点击后App 向parcel/scale01/command发送{action:confirm}ESP32 收到后进入 CONFIRMED 状态不再告警。同时 App 本地记录这次确认如果 5 分钟内再次发生重量变化不再重复告警。这个逻辑把误报的干扰降到了最低。还有一个细节Android 的通知权限在 API 33 之后需要动态申请我在 MainActivity 启动时就请求POST_NOTIFICATIONS权限如果用户拒绝就在界面上显示一个红色横幅提示通知权限未开启告警功能不可用。这个提示很直白但确实有效我测试时有好几次就是因为没给权限导致以为系统坏了。5. 3D 打印结构件从建模到装配的实战细节5.1 底座与承重平台的建模要点3D 打印部分我用的是Fusion 360建模导出 STL 后用 Cura 切片。底座的设计目标是稳固、能固定传感器一端、走线方便。我画了一个 120mm x 80mm x 15mm 的矩形底座内部挖空减轻重量四个角留了 M3 螺丝孔用于固定传感器。传感器的一端用两颗 M3 螺丝锁在底座的凸台上另一端悬空悬空端上方留出 5mm 间隙让承重平台可以压下去。承重平台是一个 150mm x 150mm x 8mm 的平板底部有一个凸起的圆柱正好压在传感器的悬空端。这个圆柱的位置很关键必须对准传感器的受力点否则会产生侧向力矩导致读数不准。我第一版把圆柱放在平台正中央结果传感器受力不均读数漂移严重。后来把圆柱移到平台中心偏传感器一侧 10mm 的位置读数立刻稳定了。这个偏移量需要根据你的传感器安装位置实际调整没有固定公式。5.2 打印参数与材料选择打印参数我用的是一套比较保守的设置层高 0.2mm壁厚 1.2mm3 层填充 20% 的网格。底座和平台都用PLA因为室内使用PLA 的刚性和尺寸稳定性足够而且打印容易。如果你住在温差大的地方可以考虑 PETG韧性更好但打印温度要高一些。这里有个坑PLA 在持续受力下会发生蠕变也就是慢慢变形。承重平台如果长期压着传感器几个月后可能会轻微弯曲导致零点漂移。我的应对方法是把平台设计得厚一点8mm并且在固件里加入自动重新去皮逻辑——每天凌晨 3 点如果重量读数在 ±10g 以内就自动执行一次tare()。这个逻辑运行了半年零点漂移基本被控制住了。装配时传感器的固定螺丝不要拧太紧。我一开始用蛮力拧紧结果传感器金属体被压变形读数直接废了。正确的做法是拧到感觉有阻力就停然后用螺纹胶固定防止松动。这个教训很深刻一颗传感器十几块钱但重新拆装和校准花了我一个下午。5.3 防水与线缆管理门口这个位置防水是个现实问题。我的做法是在底座底部贴一圈橡胶垫把电路板藏在底座内部的空腔里只留一个 USB 线出口。USB 线出口朝下防止雨水倒灌。ESP32 和 HX711 板子我用热熔胶固定在底座内壁避免晃动。如果你住在多雨地区可以在底座上盖一块亚克力板用螺丝固定但要注意不要挡住传感器。线缆管理方面传感器到 HX711 的四根线我用热缩管包好沿着底座内壁走线用扎带固定。电源线从底座侧面引出留出 10cm 的余量避免拉扯。整个装配完成后我用手按压平台各个角落确认读数一致性在 ±5g 以内才算合格。这个测试很重要因为如果平台某个角受力时读数偏差大说明传感器安装有侧向应力需要重新调整。6. 校准、测试与长期运行中的真实问题6.1 校准流程从原始值到可信重量校准是这套系统精度的生命线。我的校准流程分三步第一步空载去皮记录零点原始值第二步放 500g 标准砝码记录原始值计算scale (raw_500 - raw_0) / 500第三步放 2000g 砝码验证如果误差超过 10g就调整 scale 值重新验证。这个过程我重复了三轮最后 scale 值稳定在 2280 左右不同传感器这个值不同。但校准不是一劳永逸的。温度变化会影响传感器的输出我实测在 10°C 到 30°C 的范围内零点漂移大约 15g。对于判断包裹有无来说15g 的漂移可以接受但如果你要精确称重就需要加温度补偿。我的做法是在固件里加一个简单的线性补偿如果温度传感器读数低于 15°C零点阈值上调 10g高于 25°C下调 10g。这个补偿很粗糙但实际用下来误报率从每周两三次降到了每月一次。6.2 实测中遇到的三个典型问题问题一Wi-Fi 断连后不重连。ESP32 的 Wi-Fi 库在路由器重启后有时不会自动重连。我的解决方案是在loop()里加一个看门狗逻辑每 30 秒检查一次 Wi-Fi 状态如果断开就调用WiFi.reconnect()连续失败 5 次就重启 ESP32。这个逻辑加上后再也没出现过设备在线但收不到消息的情况。问题二MQTT 消息重复。因为用了 QoS 1消息可能重复送达。App 端如果不做去重会收到两条有新包裹通知。我的做法是在消息体里加一个msgIdApp 维护一个最近 100 条 msgId 的集合收到重复的就丢弃。这个改动很小但体验提升明显。问题三手机后台被杀。国产 Android 系统对后台服务管控很严Paho 的 Service 经常被清理。我的应对是引导用户在系统设置里把 App 加入电池优化白名单同时在 App 里加一个保持后台运行的开关开启后显示常驻通知。这个方案不完美但比什么都不做强。6.3 长期运行的数据观察与优化这套系统我连续运行了 8 个月积累了一些数据。平均每天触发 3 到 5 次事件其中误报大约每周 1 次主要来源是清洁阿姨打扫时碰到平台、以及大风天气。针对清洁阿姨的问题我在 App 里加了临时静默功能用户可以设置一个时间段比如上午 9 点到 11 点期间不推送告警只记录事件。这个功能很实用建议你开发时就加上。功耗方面ESP32 常连 Wi-Fi 的电流大约 80mA加上 HX711 和传感器整体约 100mA。5V 2A 的电源绰绰有余但如果你用劣质电源电压波动会导致 HX711 读数不稳。我换了一个带稳压的电源后读数噪声明显降低。这个细节容易被忽略但影响很大。7. 这套系统还能怎么扩展如果你已经跑通了基础版本有几个扩展方向值得尝试。第一个是加装摄像头用 ESP32-CAM 在重量变化时拍一张照片通过 MQTT 把图片的 Base64 编码发给手机。这样你不仅知道包裹到了还能看到包裹长什么样。但 ESP32-CAM 和普通 ESP32 的引脚冲突较多需要换用带 PSRAM 的型号代码也要重新整合。第二个是多设备组网。如果你有多个门口或多个住户共用可以用多个秤节点每个节点用不同的 MQTT 主题App 端做设备切换。这个改动主要在 App 端固件只需要改主题前缀。第三个是接入语音助手。通过 IFTTT 或类似的自动化平台把 MQTT 事件转发到语音助手实现包裹到了的语音播报。这个方案需要额外的云服务配置但不需要改硬件。我个人最推荐的扩展是第一个因为视觉确认能极大降低误报的困扰。不过要注意隐私问题摄像头角度要调好只拍包裹区域不要拍到公共走廊或邻居门口。这一点在公寓环境里尤其重要动手前最好和物业沟通一下。最后分享一个我在调试阶段用的小技巧在 ESP32 固件里加一个串口命令接口通过 USB 串口发送tare、scalexxxx、status等命令可以不用重新烧录就完成校准和状态查询。这个接口在前期调试时节省了大量时间强烈建议你在开发阶段就加上。