Projects · · 5 分钟阅读

我做了一个智能水杯秤,试着让喝水记录真正无感

用 ESP32、称重传感器和稳定段算法,自动识别办公室里的喝水、加水与拿杯动作。

ESP32 HX711 称重传感器 3D 打印 IoT

「每天多喝水」是一件所有人都知道,却很难长期坚持的事情。

很多饮水 App 的问题并不在提醒,而在记录:每喝一次水,都要拿起手机、打开 App、选择容量、再点确认。坚持几天后,这套流程很容易被放弃。

所以我想做一个不需要主动操作的喝水记录设备。它看起来只是工位上的一个杯垫,却能感知杯子重量变化,自动判断喝了多少、什么时候加了水,以及一天喝水的频率。

项目的基本想法

核心结构很简单:

text
水杯
  │
承重面板
  │
称重传感器
  │
HX711
  │
ESP32
  │
Wi-Fi
  │
云端 API / 数据库

ESP32 读取称重数据,通过 Wi-Fi 把有效事件上传到服务端。但真正设计时马上遇到一个问题:

重量下降,并不一定意味着喝水。

用户可能只是碰了一下杯子,也可能端起来又原样放回,甚至把手机、钥匙临时放到杯垫上。如果只看前后重量差,误报会非常多。

因此,这个项目真正的核心不是称重,而是 如何理解重量变化

不记录每一次波动,只记录稳定状态

称重传感器的数据一直在变。桌面震动、杯中水晃动、传感器漂移,都会带来几克甚至十几克的波动。

所以我没有直接使用瞬时重量,而是先判断「稳定段」。一个稳定值需要满足:

  • 连续约 3 秒重量变化处于允许范围
  • 剔除明显异常值
  • 多次采样后取平均或中位数
  • 重量变化小于约 10 克时,不作为饮水事件上传

系统处理的不再是每一条原始数据,而是一串稳定状态:

text
稳定 520g
稳定 0g
稳定 460g

这三个状态,比一条不断抖动的重量曲线更有意义。

如何识别一次喝水

一次典型的喝水过程:

  1. 杯子原本放在底座上
  2. 用户拿起杯子
  3. 杯垫重量变成 0
  4. 用户喝水
  5. 杯子重新放回
  6. 新重量小于拿起前的重量

因此喝水事件可以表示为:

text
稳定 A → 稳定 0 → 稳定 B

A > B 时:

text
喝水量 = A - B

例如 520g → 0g → 455g,可判断本次约喝水 65g。水的密度接近 1g/ml,可近似记为 65ml。

更明确的历史条件是:

上一次稳定重量是 0,并且上上次稳定重量大于当前重量,则判断为喝水。

这正是项目中最关键的一条规则。

如何识别加水

加水与喝水相似,只是最终重量变大:

text
稳定 A → 稳定 0 → 稳定 C

C > A 时,加水量 = C - A。例如 180g → 0g → 610g,可理解为拿起后加入了约 430g 水。

系统不需要用户点击「加水」,也不需要知道杯子标称容量,只需要观察一次完整的拿起和放回。

为什么必须经过 0

如果重量从 520g 直接变成 460g,并不能确认发生了喝水——可能是杯子被推到边缘受力异常,也可能是其他物品被拿走。

而经过稳定 0,意味着杯子曾经完整离开承重面板。系统才能把事件理解为:

text
拿起杯子 → 发生某种行为 → 放回杯子

因此 A → 0 → B 比单纯的 A → B 稳定得多。

每天的统计方式

早期还考虑过更简单的逻辑:每天 0 点新开一轮、累积重量减少量、小于 10g 不上报。实现成本低,但换杯、移动和临时放置物品仍容易干扰。

更稳妥的方案是以事件为核心:

json
{
  "event": "drink",
  "before_weight": 520,
  "after_weight": 455,
  "amount": 65,
  "timestamp": "2026-07-20T10:32:14+08:00"
}

服务端再按天聚合:当日总量、次数、平均单次量、最长未饮水时长、时间段分布、加水次数。

ESP32 的配网与运行

设备放在办公室工位上,不能依赖串口配网。希望首次启动时创建临时热点,用户连上后打开网页完成配置:

  1. ESP32 创建配置热点
  2. 手机连接热点
  3. 打开本地配置页
  4. 选择办公室 Wi-Fi 并输入密码
  5. 配置设备名称和服务端地址
  6. 保存并重启
  7. 页面提示如何开始运行

配网完成后进入普通模式,自动连 Wi-Fi 并向云端上传事件。用 ESP-IDF 开发学习成本更高,但对长期运行、网络管理和状态控制更合适。

云端不需要接收所有原始数据

常见错误是把所有重量采样都上传。称重频率可能每秒数十次,绝大多数没有业务价值。

更合理的方式:

  • 原始采样在 ESP32 本地处理
  • 本地完成滤波和稳定段识别
  • 只有稳定值或饮水事件才上传
  • 异常诊断时临时开启原始数据模式

这样既减少网络和存储成本,也能在短暂断网时继续工作——事件先入本地队列,网络恢复后再补传。

结构设计比想象中更重要

为了适合长期摆在工位上,我还计划做一个 3D 打印底座,大致四层:

text
1. 水杯与承重面板
2. 传感器受力结构
3. HX711、ESP32 与供电
4. 防滑底壳

设计重点包括:杯子放在不同位置仍准确;承重面板不碰底壳;泼洒后不易进水;外壳防滑;传感器不受侧向力;拆装和校准不能太复杂。

外观上,我不想做成普通电子秤,更喜欢类似《健身环大冒险》通关站立台那种轻微仪式感,同时保持简洁。它不是医疗设备,也不是强制管理工具,而是桌面上的轻量反馈装置。

产品真正的价值是什么

表面上在记录喝水量,更有价值的部分是「无感」。用户不必改变原有习惯:

  • 仍然用自己的杯子
  • 仍然像平时一样拿起放下
  • 不需要点按钮
  • 不需要每次打开 App
  • 不需要买带特殊结构的智能水杯

设备只负责观察重量变化,并把行为转换成记录。这类产品的关键,不是增加更多提醒,而是尽量减少用户需要完成的步骤。

仍然存在的问题

还需要继续处理边界情况:

  • 更换杯子
  • 杯子没有完全放回中央
  • 杯子和其他物品同时放在底座上
  • 端起杯子但没有喝水
  • 用吸管时杯子始终不离开底座
  • 水温变化引起微小重量变化
  • 长时间运行后零点漂移
  • 桌面震动导致稳定段判断失败

其中吸管场景尤其特殊。如果杯子始终不离开底座,需要另一种模式:在足够长的稳定时间内,检测到持续、缓慢且超过阈值的重量下降。

但第一版我仍更倾向只支持「拿起杯子喝水」——清晰的行为边界,比覆盖所有场景更重要。

写在最后

我喜欢这个项目,是因为它没有试图发明一种新的喝水方式,只是让原本已经发生的行为可以被可靠地记录。

一块称重传感器、一个 HX711 和一块 ESP32 并不复杂。真正需要设计的,是设备如何从一串不稳定的重量数字中,理解用户刚刚做了什么。

当硬件不再要求用户配合,而是安静地适应用户原有习惯时,它才开始真正变得有用。