基于ESP32与Azure IoT的智能称重系统:从传感器到云端全链路实践
2026/5/17 2:30:24 网站建设 项目流程

1. 项目概述:从厨房到云端的智能称重

如果你和我一样,养了一只对食物斤斤计较的猫主子,或者只是想更科学地管理家里的米面粮油库存,那么一个能自动记录、云端同步的智能食物称重系统,绝对是个能提升生活幸福感的“硬核”小工具。这个项目的核心,就是让一个传统的厨房秤“开口说话”——它不仅能实时显示重量,还能把每一次的称重数据,通过Wi-Fi默默发送到云端,形成长期的数据图表,甚至在食物快见底时,给你的手机发条提醒短信。

听起来很复杂?其实拆解开来,它是由三个清晰的层次构成的:感知层、边缘层和云端层。感知层就是我们的“电子秤”,核心是一块应变片传感器和一个高精度的24位模数转换器(ADC)芯片NAU7802,负责把物理世界的“重量”转化为微弱的电信号,再变成单片机可以理解的数字。边缘层,我们选用了一块Adafruit QT Py ESP32-S2开发板,它内置Wi-Fi,并且能用CircuitPython编程。CircuitPython的魅力在于,你不需要复杂的编译环境和底层知识,就像在电脑上写Python脚本一样,用几行代码就能驱动传感器、连接网络。最后是云端层,我们选择了Microsoft Azure IoT Central。它就像一个为物联网设备量身定做的“数据中控室”,你无需自己搭建服务器和数据库,只需在网页上点一点,就能创建设备身份、定义数据格式、绘制实时图表,甚至设置自动化规则。

整个项目的价值,远不止于称猫粮。它是一套标准的物联网(IoT)最小可行产品(MVP)原型范式。通过它,你可以透彻理解从物理信号采集、嵌入式系统处理、无线数据传输到云端应用展示的全链路。无论你是想监控花盆的土壤湿度、车库的停车状态,还是工作室的噪音水平,这套技术栈都能快速复用。接下来,我将带你从硬件焊接开始,一步步走到云端看板,把每一个环节的原理、踩过的坑和偷懒的技巧,都毫无保留地分享给你。

2. 硬件选型与核心原理剖析

工欲善其事,必先利其器。一套稳定可靠的硬件是项目成功的基石。这里的每一个组件都不是随意选择的,背后都有其针对物联网边缘设备特性的考量。

2.1 核心控制器:为什么是QT Py ESP32-S2?

在众多开发板中,我选择了Adafruit的QT Py ESP32-S2。原因有三点。第一是集成度与尺寸。它集成了ESP32-S2芯片,这意味着它自带Wi-Fi功能,无需外接模块,极大地简化了电路设计和编程复杂度。其“QT Py”的微型封装(大约只有大拇指指甲盖大小),非常适合嵌入到最终的作品中,而不是永远停留在面包板上。第二是CircuitPython的完美支持。Adafruit是CircuitPython的主要推动者,其板型通常能第一时间获得最稳定、库支持最完善的固件,这对于快速开发至关重要。第三是STEMMA QT连接器。这是一个革命性的设计,它采用防反插的JST SH 4针连接器,通过标准的I2C总线(3.3V, GND, SDA, SCL)连接传感器和外设。这意味着你几乎不需要焊接,用现成的线缆“咔哒”一扣,就能完成连接,极大地降低了硬件入门的门槛,也提高了原型的整洁度和可靠性。

2.2 重量感知的核心:NAU7802与应变片原理

称重的核心在于将“力”转换为“电信号”。我们用的是最常见的电阻应变片。你可以把它想象成一段极细的、呈栅状排列的金属丝,粘贴在一个弹性体(通常是金属梁)上。当弹性体受力弯曲时,金属丝随之被拉伸或压缩,其电阻值会发生微小的、线性的变化(这就是“应变效应”)。

然而,这个电阻变化量极其微小,通常只有零点几个欧姆,直接测量几乎不可能。于是我们引入了惠斯通电桥电路。把四个电阻应变片(或用一个应变片配合三个固定电阻)连接成一个桥路。当没有受力时,电桥平衡,输出电压为零。一旦受力,其中一个应变片的电阻变化会打破平衡,在电桥的两端产生一个与受力成正比的微小差分电压(通常是毫伏级)。

这个毫伏级的信号,就是NAU7802大显身手的地方。NAU7802是一颗24位Σ-Δ型ADC。高位数(24位)意味着极高的分辨率,它能将微小的电压变化分解成数百万个数字等级,从而实现毫克级的高精度测量。Σ-Δ架构则擅长抑制噪声,通过过采样和数字滤波,从嘈杂的环境中提取出有效的信号。其内置的可编程增益放大器(PGA),在本项目中设置为128倍,正是为了将这个毫伏信号放大到ADC最适合测量的范围。简单来说,应变片感知形变,惠斯通电桥将其转为电压,NAU7802则负责将这个微小电压高保真地“翻译”成单片机可以精确读取的数字

2.3 人机交互与供电设计

为了有一个清晰的本地反馈,我选用了一块4位14段数码管显示屏(带I2C背板)。它比LCD屏更省电,显示数字和简单字母非常清晰,且通过I2C总线仅需两根数据线就能驱动,节省了QT Py宝贵的GPIO引脚。两个带LED指示的16mm自锁按钮,一个用于切换模式(如盎司/克显示、进入校准),另一个用于确认操作。LED的灯光能直观反馈设备状态(如连接中、发送数据中),这对于没有屏幕的调试阶段尤其有用。

供电部分,QT Py ESP32-S2通过USB-C口供电,电压为5V。这是一个非常关键的点:整个系统的模拟部分(NAU7802和应变片电桥)必须使用一个干净、稳定的电源。虽然开发板的3.3V输出可以给NAU7802供电,但在高精度测量场景下,开关电源的噪声可能会被引入测量系统,影响读数稳定性。一个最佳实践是,如果条件允许,为NAU7802单独使用一个线性稳压器(如AMS1117-3.3)供电。在本项目中,由于QT Py的3.3V LDO稳压器性能尚可,且我们通过软件滤波(多次采样求平均)来抑制噪声,因此直接使用板载3.3V是可行的简化方案。但在追求极致精度时,独立的模拟供电是必须考虑的。

注意:模拟地与数字地。在PCB设计或飞线连接时,所有模拟器件(NAU7802的AGND)和数字器件(QT Py的GND)最终应单点连接在一起,通常连接在电源滤波电容的接地端。这能防止数字电路的开关噪声通过地线串扰到敏感的模拟信号中。

3. 软件架构与CircuitPython代码深度解析

硬件是骨架,软件是灵魂。这套系统的软件逻辑完全由CircuitPython代码驱动,其结构清晰反映了物联网边缘设备的典型工作流:初始化 -> 本地感知与控制 -> 云端同步。

3.1 工程结构与核心库依赖

将项目包下载并解压后,你的CIRCUITPY驱动器应呈现如下结构:

CIRCUITPY/ ├── code.py ├── calibration.py ├── settings.toml └── lib/ ├── adafruit_ht16k33/ ├── adafruit_azureiot/ ├── adafruit_ntp.mpy ├── adafruit_requests.mpy └── cedargrove_nau7802.mpy
  • code.py:主程序文件,设备上电后自动执行。
  • calibration.py:校准配置文件,存储关键的offset_val(偏移值)和weight(已知校准砝码重量)。
  • settings.toml:机密配置文件,存储Wi-Fi密码和Azure设备凭证。切记不要将此文件上传到任何公开的代码仓库!
  • lib/:存放所有依赖的库文件。这是CircuitPython生态的优势,只需将库文件复制到此目录即可调用。

核心库的作用:

  • cedargrove_nau7802:驱动NAU7802 ADC芯片,负责配置增益、通道、读取原始数据。
  • adafruit_ht16k33:驱动I2C数码管显示屏,用于显示重量和状态信息。
  • adafruit_azureiot:Azure IoT Central的客户端库,封装了MQTT协议,用于设备与云端的认证、连接和数据发送。
  • adafruit_ntp:网络时间协议客户端,用于从互联网获取准确时间,为数据打上时间戳。

3.2 主循环状态机与用户交互逻辑

整个code.py的核心是一个基于状态机的循环。状态机是嵌入式系统处理复杂逻辑的经典模式,它将程序运行划分为几个明确的状态,根据输入(按钮事件)和条件(定时器)在不同状态间切换。

# 状态变量定义 mode = "run" # 主状态:运行、菜单选择等 run_mode = True # 子状态:是否处于持续称重模式 show_oz = True # 显示单位:盎司 calibrate_mode = False # 是否进入校准流程 zero_out = False # 是否执行清零操作

主循环while True主要做以下几件事:

  1. 定时采样:如果run_mode为真,则每2秒读取一次NAU7802的原始值,经过校准参数计算后得到克和盎司重量,并滚动更新显示。
  2. 按钮扫描与消抖:持续检测两个按钮的引脚电平。采用“边沿检测”逻辑配合_pressed标志位来实现软件消抖,确保一次物理按压只触发一次逻辑动作。
  3. 模式菜单导航:按下“模式”按钮(绿色)会退出run_mode,进入一个由mode变量控制的环形菜单(mode_names列表)。菜单项包括切换显示单位、清零、校准、查看偏移值、发送数据到Azure。
  4. 状态执行:按下“确认”按钮(蓝色)会根据当前的mode值执行相应操作。例如,在“TO AZURE”模式下按下蓝色按钮,就会调用send_to_azure()函数。

这种设计的好处是逻辑清晰,易于扩展。如果你想增加一个新功能(比如设置一个目标重量报警),只需在菜单列表中添加一个描述,并在对应的if分支中实现其逻辑即可。

3.3 校准算法的原理与实现

校准是电子秤准确与否的生命线。代码中实现了两种校准:内部校准外部砝码校准

内部校准(zero_channel()函数):这利用了NAU7802芯片的内部功能。当调用nau7802.calibrate(“INTERNAL”)nau7802.calibrate(“OFFSET”)时,芯片会在内部进行一系列操作,自动调整其偏移和增益,以消除零点误差和增益误差。这个过程要求秤盘上没有任何负载。它主要用于快速去除因温度漂移或初始误差导致的零点不准问题,对应面板上的“ZERO”功能。

外部砝码校准(calibrate_mode流程):这是获得绝对精度的关键。其数学原理很简单:比例系数 = (有负载时的读数 - 空载时的读数) / 已知标准重量

代码将其分解为一个多步骤的向导式流程:

  1. Stage 0 -> 1: 提示“REMOVE”,清空秤盘。
  2. Stage 1 -> 2: 执行内部清零(zero_channel),获取当前的空载基准值zero_avg
  3. Stage 2 -> 3: 提示“PUT ITEM”,放置已知重量的标准砝码(比如100克)。
  4. Stage 3 -> 4: 测量砝码重量,获取负载读数weight_avg
  5. Stage 4 -> 5: 计算新的offset_val(weight_avg - zero_avg) / calibration[‘weight’]。这个offset_val会被保存到calibration.py文件中,此后每次测量,都将原始读数除以这个系数,即可得到以克为单位的真实重量。

实操心得:校准砝码的选择。不要用一串钥匙或一袋零食来校准!务必使用已知精确质量的校准砝码。网上可以买到小套的E2/F1等级砝码。校准的重量应接近你日常称量的最大重量的一半左右,这样在整个量程内线性度最好。例如,如果你的秤最大称量500克,那么用一个200克或250克的砝码校准比较合适。

3.4 云端连接与数据上传机制

与Azure IoT Central的交互被封装在send_to_azure()函数中。其流程严谨且具有充分的用户反馈:

def send_to_azure(current_oz, current_grams): green.value = True # 绿灯亮:开始准备 display.print(“DIALING*”) # 显示连接状态 device.reconnect() # 重新建立MQTT连接 blue.value = True # 蓝灯亮:已连接 display.print(“CONNECTD”) time.sleep(1) display.print(“SENDING!”) # 显示发送状态 message = {“Ounces”: current_oz, “Grams”: current_grams} # 构造JSON数据 device.send_telemetry(json.dumps(message)) # 发送遥测数据 display.fill(0) display.print(“SENT!”) # 显示发送成功 device.disconnect() # 断开连接,省电 green.value = False blue.value = False

关键点解析

  1. 连接管理:不是保持长连接,而是按需连接(reconnect),发送后立即断开(disconnect)。这对于电池供电的设备是至关重要的节能策略,因为维持Wi-Fi和MQTT连接非常耗电。
  2. 数据格式:数据以JSON格式发送,键名(“Ounces”, “Grams”)必须与后续在Azure IoT Central中创建的设备模板属性名称完全匹配,否则云端无法正确解析。
  3. 用户反馈:通过双色LED和数码管的文字提示,将“连接中”、“发送中”、“成功”等状态清晰地告知用户,提升了产品的交互友好度,也便于调试时观察问题所在。

4. 云端平台配置与数据可视化实战

设备端的工作完成后,我们需要在云端建立一个“接收站”和“指挥中心”。Azure IoT Central极大地简化了这一过程。

4.1 创建设备模板与数据建模

设备模板是云端理解你设备的“说明书”。它定义了设备会发送哪些数据(遥测)、有哪些可设置的属性、以及能执行什么命令。

  1. 创建设备实例:在IoT Central应用中,点击“设备”->“新建”。为你的食物秤起一个名字,如“Kitchen-Food-Scale-01”。创建成功后,进入该设备页面,点击“连接”。你会看到至关重要的三要素:ID范围、设备ID、主密钥。将它们立即填入本地的settings.toml文件。

  2. 自动生成模板:当你的设备首次成功发送数据后,这些数据在云端会被标记为“未建模数据”。此时,在设备页面点击“管理模板”->“自动创建模板”,IoT Central会自动分析收到的JSON数据,并为你创建包含“Ounces”和“Grams”两个遥测属性的模板。这简直是“懒人”福音!

  3. 手动精修模板(可选):你也可以点击“添加功能”手动定义。例如,你可以添加一个“单位切换”的可写属性,这样就能从云端直接控制秤是显示盎司还是克。或者添加一个“立即称重”的命令,远程触发一次数据上报。这为未来功能扩展留下了空间。

4.2 构建可视化仪表板

数据只有被看见,才有价值。IoT Central的仪表板功能让你能像搭积木一样创建数据可视化界面。

  1. 进入“仪表板”页面,点击“编辑”。
  2. 从左侧的磁贴库中,拖拽你需要的组件到画布上。对于这个项目,我推荐组合使用:
    • 折线图:用于展示重量随时间的变化趋势。你可以清晰地看到宠物一天进食的频率、周末的消耗量是否更大。
    • 最后已知值:一个大字体的磁贴,实时显示当前的重量(克或盎司)。
    • 事件历史记录:可以记录每次“发送到Azure”这个动作的发生时间。
  3. 点击每个磁贴的铅笔图标进行配置。关键步骤是:在“配置”中,设备选择你创建的“Kitchen-Food-Scale-01”,然后在“功能”中添加“Ounces”或“Grams”作为数据源。你可以设置时间范围(如过去24小时)、图表颜色等。
  4. 布局技巧:将“最后已知值”放在仪表板顶部最显眼的位置,折线图放在下方占据主要区域。你还可以添加一个“KPI”磁贴,计算“昨日消耗总量”,让洞察一目了然。

4.3 设置智能告警规则

物联网的终极价值之一是自动化响应。我们可以设置一个规则:当食物重量低于某个阈值时,自动发送短信提醒。

  1. 创建操作组:这是告警的“接收人列表”。在Azure门户中(注意,此功能需在Azure门户完成,IoT Central内可引导创建),搜索“监视器”->“警报”->“操作组”。创建一个新的操作组,比如叫“FoodLowAlert”。添加一个“短信”操作,输入你的手机号。Azure会发送验证码进行验证。
  2. 在IoT Central中创建规则:回到你的IoT Central应用,进入“规则”页面,点击“新建”。
    • 条件:选择你的设备“Kitchen-Food-Scale-01”, 遥测选择“Grams”, 操作符选择“小于”, 阈值设为“100”(假设100克时就需要补货)。
    • 操作:选择“电子邮件”或关联你在上一步创建的“操作组”。选择“操作组”功能更强大,未来可以轻松添加邮件、推送等多种通知方式。
  3. 设置聚合与频率:为了避免因瞬时波动误触发,可以设置“每当平均重量在5分钟内持续低于100克时”才触发告警。这样就更智能了。

避坑指南:免费套餐限制。Azure IoT Central提供免费套餐,但对于个人学习和原型开发,有一个致命限制:数据只保留7天,7天后应用和设备会被自动删除。如果你打算长期使用,务必在创建应用后的7天内,将其转换为“标准”付费套餐(按设备数量月度付费),或者定期导出你的数据。千万不要在第七天晚上才想起来,那时数据已经找不回了。

5. 系统集成、调试与优化心得

将硬件、固件、云端三者无缝对接,是整个项目从“能跑”到“好用”的关键。这个过程充满了调试和优化。

5.1 分阶段集成与调试策略

不要试图一次性写完所有代码并期望它完美运行。我强烈建议采用分阶段集成法:

  1. 阶段一:本地称重功能。首先,完全注释掉所有与Wi-Fi和Azure相关的代码。只专注于让NAU7802驱动起来,能在数码管上稳定显示重量。使用print()函数通过串口监视器(如Mu编辑器或Thonny)输出原始读数、计算后的重量,验证校准是否准确。这是基础,必须打牢。
  2. 阶段二:独立网络测试。在settings.toml中配置好Wi-Fi,单独测试网络连接和NTP对时功能。写一个简单的测试脚本,只连接Wi-Fi,获取时间并打印出来。确保你的网络环境(尤其是某些企业或校园网)不会阻止设备接入。
  3. 阶段三:云端连接测试。注释掉称重主循环,只保留Azure连接部分。编写一个最小化的send_to_azure(1.0, 28.35)测试函数,手动触发,看能否在IoT Central的“原始数据”视图中看到发送的测试数据。这一步验证了设备凭证和网络连通性。
  4. 阶段四:功能合并与状态机整合。当前三步都稳定后,再将所有代码整合到一起。此时,重点调试状态机的逻辑,确保按钮按下后,菜单切换、模式进入退出、LED和显示反馈都符合预期。

5.2 稳定性优化与常见问题排查

在实际运行中,你可能会遇到以下问题及解决方案:

问题一:重量读数跳动或漂移。

  • 原因A:机械结构不稳定。检查3D打印的秤台结构是否牢固,应变片是否用强力胶完全粘贴在金属梁的应变区中心,且没有气泡。整个秤体应放置在平稳、无振动的台面上。
  • 原因B:电源噪声。尝试用移动电源或电池为整个系统供电,排除电网干扰。在NAU7802的电源引脚靠近芯片处,并联一个10μF的钽电容和一个0.1μF的陶瓷电容,进行退耦滤波。
  • 原因C:软件滤波不足。代码中使用了读取100个样本求平均的read_raw_value(100)函数。如果跳动依然明显,可以尝试增加采样次数(如200),或者在主循环中采用滑动平均滤波,即维护一个最近N次读数的队列,始终计算这个队列的平均值作为输出。

问题二:发送数据到Azure失败,设备卡在“DIALING”。*

  • 排查步骤
    1. 检查Wi-Fi:确保settings.toml中的SSID和密码正确,且网络可达。
    2. 检查Azure凭证:核对id_scope,device_id,device_primary_key是否与IoT Central设备连接页面显示的信息完全一致,包括大小写。
    3. 检查网络防火墙:某些严格的网络环境可能屏蔽了MQTT端口(8883)。尝试将设备连接到手机热点进行测试。
    4. 启用详细日志:在code.pydevice.connect()前后添加更多的print语句,或者使用adafruit_azureiot库的调试模式(如果支持),查看具体的错误信息。

问题三:设备运行一段时间后无故重启。

  • 原因A:内存泄漏。CircuitPython具有垃圾回收机制,但在长时间运行的循环中,如果持续创建大量对象(如列表、字符串),可能导致内存耗尽。确保在函数内部创建的临时列表(如avg_read)在函数使用完毕后及时.clear()
  • 原因B:看门狗超时。ESP32-S2有硬件看门狗。如果主循环中某个操作(如网络连接)阻塞时间过长,可能导致看门狗复位。确保device.reconnect()device.send_telemetry()等阻塞操作有合理的超时设置,或者考虑使用asyncio库进行异步处理,让主循环保持“心跳”。
  • 原因C:电源电压跌落。在Wi-Fi射频发射的瞬间,电流需求骤增,可能导致电压瞬间跌落,引发单片机复位。在QT Py的VBUS(5V输入)和GND之间并联一个大容量(如100-470μF)的电解电容,可以起到“能量水池”的作用,平滑这种电流冲击。

5.3 项目扩展思路

这个项目是一个完美的起点,你可以基于它进行无限扩展:

  1. 低功耗改造:目前设备需持续供电。你可以改用ESP32-S2的深度睡眠功能。修改代码,让设备每12小时(或根据重量变化)唤醒一次,测量重量并发送数据,然后立即重新进入深度睡眠。这样配合一块小型锂电池,可以续航数周甚至数月。
  2. 多传感器融合:在I2C总线上增加一个温湿度传感器(如SHT30)。在发送重量数据时,一并上报环境温湿度。你可以在Azure仪表板上关联分析食物消耗速度与环境湿度的关系(例如,潮湿天气下宠物是否吃得更多)。
  3. 本地数据缓存与断网续传:增加一个SPI Flash芯片或SD卡模块。当网络不可用时,将数据临时存储在本地。等网络恢复后,先检查本地是否有未发送的数据,优先发送这些历史数据,再发送当前数据,确保数据不丢失。
  4. 云端功能增强:利用Azure IoT Central的作业功能,批量管理设备。或者使用Azure Functions(无服务器计算)在云端编写一小段代码,当收到低重量告警时,自动在电商平台(如通过API)下单购买对应的宠物食品,实现真正的全自动化补货。

通过这个项目,你收获的不仅仅是一个智能秤,更是一套应对物联网挑战的方法论:从信号调理、嵌入式编程、无线通信到云服务集成。当你下次看到任何想要“联网”的物理对象时,这套从传感器到云端的思维框架,将能帮助你快速勾勒出实现路径。

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

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

立即咨询