1. 项目缘起:一个被忽视的日常痛点
你有没有过这样的经历?早上九点坐到工位,打开电脑,接满一杯水,然后……就再也没有然后了。直到下午三四点,被一阵突如其来的口干舌燥和隐隐头痛提醒,才猛然发现,那杯水还满满地放在桌角,一口未动。或者,在健身房挥汗如雨,教练反复叮嘱“小口多次补水”,但训练一上头,要么忘得一干二净,要么猛灌一通,结果胃里翻江倒海。又或者,家里的老人、孩子,总需要你追着提醒“该喝水了”。我们的大脑被无数重要或不重要的信息占据,却常常忽略了维持生命最基本、最廉价也最有效的“燃料”——水。
“Mr. Hydrate”这个项目的诞生,就源于这个看似微小却普遍存在的痛点:主动、规律地饮水,对现代人来说,成了一件需要刻意提醒才能完成的事。脱水带来的影响是潜移默化且广泛的:从短暂的注意力下降、疲劳感增强,到长期的皮肤状态变差、新陈代谢减缓,甚至增加泌尿系统结石的风险。市面上的解决方案不少,手机里各种健康App都有饮水提醒功能,智能水杯也能记录饮水量。但它们要么过于被动(一个容易被忽略的弹窗),要么成本高昂(一个几百上千的杯子),要么数据孤立(记录完就结束了,缺乏互动和激励)。
“Mr. Hydrate”想做的,就是跳出单纯的“计时器”或“记录仪”思维,打造一个低成本、高互动性、甚至带点趣味性的实体提醒装置。它不依赖于昂贵的传感器或复杂的物联网生态,而是回归提醒的本质:用一种无法轻易忽视的、物理的方式,在你需要喝水的时候,“戳”你一下。
2. 核心设计哲学:从“被动通知”到“主动干预”
大多数数字提醒失败的原因在于,它们太容易被“稍后处理”了。手机通知可以被划掉,App可以被关闭。而“Mr. Hydrate”的设计核心,是创造一种轻微的、持续的物理存在感,直到你完成喝水这个动作,它才会停止“干预”。这借鉴了行为设计学中的“承诺机制”和“即时反馈”原理。
2.1 形态与交互设计
“Mr. Hydrate”的实体形态可以是一个小巧的桌面摆件。它的核心交互逻辑极其简单:
- 设定目标:用户通过一个简单的旋钮或按钮,设定自己今日的饮水目标杯数(例如8杯)。
- 开始计时:用户喝下第一杯水后,按下“开始”或“记录”按钮。
- 等待与提醒:装置内部开始计时(例如,根据设定,每间隔1.5-2小时提醒一次)。
- 物理提醒:到达预定时间后,装置并非只是亮灯或哔一声。它的设计是会产生一种需要用户动手去关闭的提醒。比如,一个可爱的小人模型(Mr. Hydrate本人)会缓缓“站”起来,或者头顶的“小水花”装置开始缓慢旋转。这个动作是安静、缓慢但持续的。
- 完成闭环:用户必须走到装置前,手动将其“安抚”回原始状态(比如把小人按下去,或暂停水花旋转)。这个动作本身,就是一次对“该喝水了”这个念头的强化。同时,装置上的一个简易计数器会+1,显示今日已完成杯数。
- 达成庆祝:当完成当日目标时,装置会有一个特别的、更积极的反馈,比如发出一段轻松的音乐,或所有指示灯温柔地闪烁几秒,给予用户正向激励。
为什么是物理装置而非纯App?
- 降低认知负荷:它独立于手机和电脑,避免被淹没在信息流中。
- 创造仪式感:手动按压的动作,将喝水的意图从“想法”转化为“行动”,加强了行为链路。
- 视觉锚点:始终放在水杯旁的它,本身就是一个持续的视觉暗示。
- 无屏幕焦虑:它不连接网络,不收集数据,只是一个简单的工具,减少了数字时代的焦虑感。
3. 技术实现方案:极简主义的电子工艺
为了实现上述交互,“Mr. Hydrate”在技术上可以做得非常精简而优雅,核心成本可以控制在百元以内,非常适合个人DIY或小批量创意制作。
3.1 核心硬件选型
- 主控单元:Arduino Nano 或 ATtiny85。这是项目的“大脑”。如果功能简单(仅计时、控制一个舵机、一个按钮、一个数码管),ATtiny85这种8引脚的单片机绰绰有余,成本极低且体积小巧。若需要更复杂的灯光效果或声音,Arduino Nano提供更多IO口和更便捷的开发环境。
- 计时模块:DS3231高精度实时时钟(RTC)模块。这是关键!普通单片机的内部计时误差较大,一天可能差出几分钟甚至更多。DS3231芯片年误差可控制在分钟级,能确保提醒时间的精确性,且断电后依靠纽扣电池继续走时,无需每次上电重设。
- 执行器(提醒机构):微型舵机(如SG90)。这是实现“物理提醒”动作的核心。舵机可以精确控制角度,用来驱动小人“起身”、水花“旋转”或举起一个“喝水啦”的小旗子。选择舵机而非振动马达,是因为它的动作更柔和、可视,且更安静。
- 用户输入:** tactile按钮** 和旋转编码器。一个按钮用于“重置/记录”饮水动作。一个旋转编码器用于设定目标杯数和时间间隔,操作直观(旋转调节,按下确认)。
- 反馈单元:
- 显示:一位或两位的7段数码管或OLED小屏幕(0.96英寸)。数码管成本低,只显示数字(已完成杯数/目标杯数)足够;OLED屏可以显示更多信息(如时间、下次提醒倒计时),更具科技感。
- 灯光:若干WS2812B可编程LED(NeoPixel)。用于状态指示(待机、提醒中、目标达成)和庆祝灯光效果。它的优点是单线控制,颜色可变,非常灵活。
- 声音:无源蜂鸣器。用于达成目标时的庆祝音效。选择无源蜂鸣器是因为可以通过编程控制频率,播放简单旋律,成本比MP3模块低得多。
- 电源:USB供电(5V)或3.7V锂电池+充电管理模块。桌面使用USB最方便;若想完全无线化,可加入小容量锂电池,但需考虑续航和充电。
3.2 系统工作流程与电路连接要点
整个系统的工作流可以概括为:RTC提供精确时间 -> 主控芯片判断是否到达提醒点 -> 若到达,则启动舵机并点亮特定灯光 -> 等待用户按下按钮 -> 按钮按下,停止舵机,记录杯数+1,更新显示,重置RTC计时 -> 判断是否达成目标,若达成则触发庆祝效果。
在电路连接上,有以下几个需要特别注意的点:
- 电源管理:所有模块务必统一供电电压。舵机在动作时电流较大(可达500mA-1A),可能导致单片机复位。务必为舵机单独供电,或使用一个大容量(如5V/2A)的USB电源适配器,并在单片机电源入口处加一个100μF以上的电解电容进行滤波稳压。
- 信号防抖:按钮和旋转编码器的信号线必须接入单片机,并在软件中实现消抖处理。硬件上也可以在按钮两端并联一个0.1μF的电容,效果更佳。
- RTC的备份电池:DS3231模块上的CR2032纽扣电池必须安装,否则断电后时间会丢失。
- 舵机保护:在代码中要限制舵机的转动角度范围,避免机械结构卡死时舵机堵转烧毁。
3.3 核心代码逻辑解析
以下是基于Arduino框架的核心逻辑伪代码,说明了程序是如何运行的:
#include <Wire.h> #include <RTClib.h> // DS3231库 #include <Servo.h> // 舵机库 RTC_DS3231 rtc; Servo myServo; int targetCups = 8; // 目标杯数 int currentCup = 0; // 当前已喝杯数 int intervalMinutes = 120; // 提醒间隔(分钟) DateTime lastDrinkTime; // 上次喝水时间 bool isReminding = false; // 是否正在提醒状态 void setup() { // 初始化串口、RTC、舵机、按钮引脚、显示屏、LED等 if (!rtc.begin()) { // 处理RTC初始化失败 } if (rtc.lostPower()) { // 如果RTC断电,可以在这里设置一个初始时间(仅首次) // rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); } myServo.attach(servoPin); myServo.write(0); // 初始位置,例如“躺下” lastDrinkTime = rtc.now(); // 程序启动时记录为上次喝水时间 } void loop() { DateTime now = rtc.now(); // 获取当前精确时间 // 1. 检查是否到了提醒时间 if (!isReminding) { long minutesPassed = (now.unixtime() - lastDrinkTime.unixtime()) / 60; if (minutesPassed >= intervalMinutes && currentCup < targetCups) { startReminder(); } } // 2. 检查用户是否按下了“我喝了”按钮 if (digitalRead(buttonPin) == LOW) { // 假设按钮按下为低电平 delay(50); // 消抖 if (digitalRead(buttonPin) == LOW) { if (isReminding) { // 如果在提醒中按下,算作完成一次饮水 finishDrinking(); } else { // 如果非提醒时按下,可以作为手动记录一杯水 manualRecordDrink(); } while(digitalRead(buttonPin) == LOW); // 等待按钮释放 } } // 3. 检查旋转编码器,用于调整目标杯数或间隔(代码略) // 4. 更新显示屏(代码略) } void startReminder() { isReminding = true; // 舵机缓慢动作到提醒位置(如90度) for (int pos = 0; pos <= 90; pos += 1) { myServo.write(pos); delay(20); // 控制动作速度 } // 点亮提醒状态的LED(例如红色呼吸灯) } void finishDrinking() { isReminding = false; // 舵机缓慢回归原位 for (int pos = 90; pos >= 0; pos -= 1) { myServo.write(pos); delay(20); } currentCup++; lastDrinkTime = rtc.now(); // 重置计时起点 // 更新显示屏杯数 // 关闭提醒LED if (currentCup >= targetCups) { celebrate(); // 触发庆祝效果 } } void celebrate() { // 播放一段旋律(控制蜂鸣器) // 跑马灯或彩色闪烁效果(控制WS2812B) delay(3000); // 庆祝持续一段时间 // 恢复待机状态 }这段代码清晰地展示了状态机思维:装置始终在“等待”、“提醒”、“记录”几个状态间切换,由时间和用户输入驱动。
4. 外壳与体验设计:赋予它“人格”
“Mr. Hydrate”的成功,一半在电路,另一半在外壳和体验设计。它的目标是友好的、鼓励性的,而不是冷冰冰的监控设备。
4.1 外观构思
- 材料:3D打印是最佳选择。PLA材料成本低,颜色多样,可以打印出非常复杂的有机形态。设计一个圆润、可爱的卡通水滴形象,或者一个举着杯子的小人,让它的物理动作(如点头、举手)更富有情感。
- 结构:外壳需要为内部元件设计精确的卡槽和固定柱。特别是舵机,需要将其输出轴与外壳的可动部分(如手臂、脑袋)牢固连接。通常需要在舵机摇臂上安装一个连接件,再通过螺丝与外壳部分固定。
- 人性化细节:
- 按钮手感:选择那种按压感清晰、声音清脆的微动开关,给予用户明确的反馈。
- 灯光柔和:LED光线不能刺眼。可以在LED灯珠上方加一层乳白色的亚克力或磨砂塑料作为柔光罩,让光线均匀扩散,显得更高级、更舒适。
- 线材管理:如果使用USB供电,可以在外壳底部设计一个凹槽,让USB线可以整洁地引出。
4.2 交互体验调优
- 提醒的“度”:舵机的动作要慢而坚定,太快显得急躁,太慢则可能被忽略。整个提醒周期可以设计为:到达时间后,先让指示灯慢闪30秒,如果用户无反应,再启动舵机动作。给用户一个短暂的“缓冲期”。
- 庆祝的“惊喜感”:达成目标的庆祝效果要与日常提醒有明显区别。可以设计一段独特的、欢快的旋律(如《欢乐颂》前几个小节),配合彩虹渐变灯光,让成就感可视化、可听见。
- 容错与重置:长按某个按钮5秒,可以重置当日杯数。防止误操作或中途想重新开始。这个逻辑要在代码中实现,并在外壳上用简图或符号标示。
5. 迭代与扩展可能性
基础版的“Mr. Hydrate”已经能解决核心问题。但作为一个创客项目,它还有巨大的扩展空间:
- 数据可视化(轻度联网):增加一个ESP8266WiFi模块。每次记录饮水后,通过MQTT协议或HTTP请求,将数据(时间、杯数)发送到本地的Home Assistant服务器或一个简单的云数据库。这样你可以在电脑或手机仪表盘上回顾一周、一月的饮水曲线。关键点:务必在代码中做好网络连接失败的处理,绝不能因为连不上网而影响核心的本地提醒功能。
- 环境自适应:增加一个温湿度传感器(如DHT11)。在代码逻辑中加入:如果环境温度过高或湿度太低,自动缩短提醒间隔。让提醒变得更“智能”和贴心。
- 多人模式:设计一个外壳上有多个按钮,对应不同的家庭成员或同事。主显示屏轮流显示每个人的进度。这适合家庭或小型办公室场景,增加社交性和趣味竞争。
- 与智能水杯联动:如果拥有带称重传感器的智能水杯,可以通过蓝牙(HC-05模块)接收水杯的实际饮水重量数据,实现更精确的自动记录,无需手动按钮。但这会显著增加复杂度和成本。
6. 制作避坑指南与心得分享
从我实际制作和调试的过程来看,有几个坑是新手极易踩中的:
6.1 电源之坑:舵机是“电老虎”这是最大的坑。最初我用电脑USB口(500mA)给整个系统供电,当舵机动作时,单片机频繁重启。原因是舵机启动瞬间电流冲击导致电压骤降。解决方案:
- 使用独立电源:为舵机准备单独的5V/2A电源适配器,与单片机的电源共地。
- 加大电容:在单片机的VCC和GND之间并联一个470μF甚至1000μF的电解电容,作为“能量水池”,平滑电压波动。
- 选择扭力合适的舵机:SG90舵机在空载和轻载下工作电流并不大,但如果你的机械结构阻力较大,就需要更大扭力的舵机,同时也意味着需要更强的电源。
6.2 机械结构之坑:设计要考虑公差和疲劳3D打印的零件存在收缩率,孔位和轴配合不能设计成“严丝合缝”。我的第一版外壳,舵机轴完全卡死。解决方案:
- 留出间隙:在3D设计软件中,对于需要转动的轴孔配合,单边至少留出0.2mm的间隙。
- 使用轴承:对于需要频繁、平滑转动的部位,可以考虑嵌入微型滚珠轴承(如623ZZ),能极大提升顺滑度和寿命。
- 测试打印件:关键的活动部件,先用最低精度和填充率快速打印一个测试件,验证活动范围是否干涉,确认无误后再打最终版。
6.3 代码之坑:阻塞式延迟(delay)的滥用在startReminder()和finishDrinking()函数中,我使用了for循环加delay()来控制舵机缓慢运动。这会导致一个问题:在舵机运动的这几秒钟内,整个程序被delay()阻塞了,无法检测按钮按下!如果用户在这期间按下按钮,响应会有延迟。更优的解决方案是使用非阻塞式定时,利用millis()函数来管理时间。例如:
unsigned long servoMoveStartTime = 0; int servoTargetPos = 0; int servoCurrentPos = 0; bool isServoMoving = false; void updateServo() { if (isServoMoving) { if (millis() - servoMoveStartTime > 20) { // 每20ms移动一步 servoMoveStartTime = millis(); if (servoCurrentPos < servoTargetPos) { servoCurrentPos++; myServo.write(servoCurrentPos); } else if (servoCurrentPos > servoTargetPos) { servoCurrentPos--; myServo.write(servoCurrentPos); } else { isServoMoving = false; // 到达目标位置,停止移动 } } } } void loop() { // 其他逻辑... updateServo(); // 非阻塞地更新舵机位置 // 可以随时响应按钮,不会被阻塞 }这样,舵机的平滑运动与主循环的即时响应可以兼得,用户体验更佳。
6.4 用户体验之坑:反馈不明确最初版本只有一个舵机动作,用户有时不确定按下按钮后是否记录成功。改进:在按下按钮记录成功后,除了舵机归位,立刻让绿色LED快速闪烁两下,并让蜂鸣器发出一个短促的“嘀”声作为确认音。多模态的正面反馈能极大地增强操作的确定感和愉悦感。
制作“Mr. Hydrate”的过程,远不止是焊接电路和敲代码。它是一次对日常生活的细微观察,一次将抽象需求转化为具体物理交互的实践,更是一次与自己的习惯对话的过程。当这个自己亲手制作的小家伙,在某个下午准时地、慢悠悠地“站”起来提醒你时,那种被关怀的感觉和按下按钮完成任务的满足感,是任何手机App通知都无法给予的。它不仅仅是一个饮水提醒器,更是一个放在桌角的、关于关注自身健康的小小承诺。