1. 项目概述当水杯有了“思想”你有没有过这样的经历工作或学习太投入手边的水杯空了都浑然不觉等想起来时已经口干舌燥或者想喝口水却发现水温要么烫嘴要么太凉得反复试探这个用Beetle一种小巧的Arduino兼容开发板制作的“自动”水杯就是为了解决这些小小的、却真实存在的痛点而生的。它不是一个能自己跑去接水的机器人而是一个具备基础感知与交互能力的智能水杯附件。核心思路是通过传感器感知杯内水温和水位用OLED屏幕直观显示信息并在水位过低或水温达到适宜饮用范围时通过舵机驱动一个机械臂轻轻“推”一下你的手或杯子给你一个温和的提醒。整个项目融合了嵌入式开发、传感器应用、执行器控制和简单的结构设计非常适合作为Arduino爱好者的进阶综合实践。无论你是想给日常生活增添一点科技趣味还是希望深入学习如何将多个电子模块有机整合成一个完整的小系统这个项目都能提供一条清晰的路径。2. 核心硬件选型与设计思路2.1 主控大脑为什么是Beetle在众多Arduino开发板中选择DFRobot的Beetle基于ATmega32u4作为主控是经过深思熟虑的。首先体积是决定性因素。一个水杯附件必须足够小巧、轻便不能喧宾夺主。Beetle的尺寸仅有20mm x 22mm比一枚硬币大不了多少可以非常方便地集成在水杯的底座或侧面支架上几乎不增加额外负担。其次接口够用且灵活。这个项目需要连接温度传感器、水位传感器、OLED屏幕和舵机。Beetle虽然小巧但提供了足够的数字和模拟I/O口。ATmega32u4芯片原生支持USB通信这意味着它可以被电脑识别为鼠标、键盘或串口设备为未来扩展比如通过USB发送提醒到电脑留下了可能。相比之下经典的Arduino Uno虽然接口丰富但体积过大而像ESP8266这类Wi-Fi板虽然功能强大但功耗和复杂度对于这个简单的本地化项目来说有些“杀鸡用牛刀”。注意市面上有不同版本的Beetle务必确认你拿到的是基于ATmega32u4的版本其引脚定义和编程方式与Leonardo类似与Uno略有不同特别是在使用串口通信时。2.2 感知模块温度与水位的精准捕捉感知是智能的基础。我们选择了两种非常经典且可靠的传感器。DS18B20数字温度传感器是测温的不二之选。它采用单总线协议只需要一根数据线加上电源和地即可与主控通信极大地节省了宝贵的I/O口。其测量范围-55°C 到 125°C和精度±0.5°C完全满足水温测量需求。更重要的是它是防水的我们可以将其探头用食品级热缩管或环氧树脂妥善密封后直接浸入水中进行测量读数准确可靠。相比之下模拟温度传感器如LM35虽然接线简单但需要占用模拟口且抗干扰能力较弱。水位检测方案有多种这里我选择了简易的模拟水位传感器。它本质上是一系列暴露的平行导线利用水的导电性来改变两个电极间的电阻从而输出一个模拟电压值。水位越高导电面积越大输出模拟值越高。它的优点是成本极低、原理直观。当然它也有缺点长期浸泡可能导致电极氧化影响精度。因此在实际制作中我们不会让它一直浸在水里而是将其固定在杯口内侧仅探测是否有水以及水位的大致高度。对于追求长期稳定性的项目可以考虑电容式或超声波测距模块但成本和复杂度会相应增加。2.3 交互与执行模块看得见的提醒与温柔的动作信息需要被传达提醒需要被感知。我们通过一“显”一“动”来实现。0.96英寸OLED显示屏I2C接口负责“显”。I2C接口仅需两根信号线SDA, SCL就能驱动这块高对比度、自发光的屏幕。我们可以用它实时显示当前水温、水位状态如百分比或“高/中/低”、以及系统状态如“正常”、“缺水提醒”、“水温适宜”。比起LCD屏OLED在显示简单信息时更省电视觉效果也更好。SG90微型舵机负责“动”。它的任务不是举起水杯而是驱动一个简单的、3D打印或激光切割的连杆机构做一个轻微的“推”或“顶”的动作。SG90体积小、重量轻、扭矩适中1.6kg/cm左右足以完成这个轻柔的提醒动作。其控制采用标准的PWM脉冲宽度调制信号Arduino的Servo库可以轻松驱动。选择舵机而不是振动马达是因为动作更可视化、更具趣味性且力度和角度可以精确控制。2.4 整体系统架构设计整个系统的逻辑流程可以概括为循环感知 - 逻辑判断 - 交互执行。感知主控循环读取DS18B20的温度值以及模拟水位传感器的读数。判断程序内部设定两个阈值。一个是低水位阈值当水位低于此值时触发“缺水提醒”。另一个是适宜水温范围例如45°C-55°C当温度落入此区间时触发“水温适宜提醒”。执行根据判断结果OLED屏幕更新对应的提示信息。同时若触发任一提醒条件则控制舵机做出一次设定好的提醒动作比如转动60度再返回动作完成后舵机归位避免持续耗电和发热。电源方面由于Beetle、传感器、OLED屏和舵机在非转动时的功耗都不高可以使用一块小型的3.7V锂电池如503050规格配合一个微型升压模块输出5V来供电实现整个装置的无线化这才是“自动水杯”体验完整的关键。3. 核心电路连接与接线详解可靠的硬件连接是项目成功的基石。下面将详细拆解每个模块与Beetle的连接方法并解释每一根线的作用。3.1 Beetle主控板引脚分配策略Beetle的引脚资源需要精打细算。我们先进行全局规划数字引脚D9用于输出PWM信号控制舵机。数字引脚D2定义为数字输入连接DS18B20的数据线单总线协议可挂载在同一条线上但这里我们单独使用。模拟引脚A0用于读取模拟水位传感器的电压值。I2C引脚Beetle的D2(SDA)和D3(SCL)固定用于I2C通信连接OLED屏幕。这里需要特别注意DS18B20也接到了D2这与I2C的SDA线冲突了。因此我们必须为DS18B20更换一个引脚例如换到数字引脚D4。这是实际接线中容易踩的坑。3.2 分模块接线步骤与原理DS18B20温度传感器接线Beetle 5V - DS18B20 VDD (红线) Beetle GND - DS18B20 GND (黑线) Beetle D4 - DS18B20 DATA (黄线/蓝线)提示DS18B20的数据引脚DATA需要连接一个4.7kΩ的上拉电阻到5V以确保信号稳定。许多现成的模块已经集成了这个电阻购买时请选择“带板上拉电阻”的版本可以省去很多麻烦。模拟水位传感器接线Beetle 5V - 水位传感器 VCC Beetle GND - 水位传感器 GND Beetle A0 - 水位传感器 AO (模拟输出)注意水位传感器的DO数字输出引脚在本项目中暂不使用它会在水位超过某个固定阈值时输出高低电平不如模拟输出灵活。OLED屏幕 (I2C) 接线Beetle 5V - OLED VCC Beetle GND - OLED GND Beetle D2 - OLED SDA Beetle D3 - OLED SCLI2C设备的接线非常简洁这也是其优势所在。通常OLED模块背面有地址选择电阻默认地址为0x3C我们在代码中会用到。SG90舵机接线Beetle 5V - 舵机红线 (VCC) Beetle GND - 舵机棕/黑线 (GND) Beetle D9 - 舵机黄/橙线 (信号线)重要提醒舵机在转动瞬间电流较大如果与其他模块共用Beetle板载的5V输出可能导致电压骤降引起Beetle复位。强烈建议为舵机提供独立电源。最稳妥的方案是使用一块锂电池同时给一个升压模块输出5V给Beetle和传感器和另一个5V输出单独给舵机供电两者的GND必须连接在一起共地。3.3 电源方案与布线技巧为了实现无线化我推荐以下电源方案选择一块3.7V、500mAh以上的锂电池。使用一个微型DC-DC升压模块将电池电压稳定升至5V这个5V输出给Beetle、传感器和OLED供电。舵机的供电可以从升压模块的输入侧即电池直接接出经过一个独立的5V稳压模块如AMS1117-5.0后再供给舵机。这样可以将电机干扰与主控电路隔离。在电池和总电路之间加入一个拨动开关方便控制整个系统开关。布线时使用杜邦线进行原型连接但最终建议使用烙铁和导线进行焊接并用热缩管绝缘这样更牢固可靠。将电池、升压模块等集中固定在一个小型料盒或3D打印的外壳内与水杯底座结合。4. 软件编程与核心逻辑实现硬件搭好了接下来就是赋予它“灵魂”。我们将使用Arduino IDE进行编程。4.1 开发环境搭建与库管理首先确保你安装了Arduino IDE并且Beetle的板卡支持已添加。Beetle兼容Arduino Leonardo在IDE的“工具”-“开发板”中选择“Arduino Leonardo”即可。接下来需要安装三个核心库DallasTemperature OneWire用于驱动DS18B20。在IDE的“库管理器”中搜索“DallasTemperature”并安装它会自动关联安装“OneWire”库。Adafruit_SSD1306 Adafruit_GFX用于驱动OLED屏幕。搜索“Adafruit SSD1306”安装同样会提示安装依赖的“Adafruit GFX Library”。Servo用于控制舵机。这是Arduino的内置库无需额外安装。4.2 核心代码逻辑逐行解析下面是一个高度整合且带有详细注释的核心代码框架#include Wire.h #include Adafruit_GFX.h #include Adafruit_SSD1306.h #include OneWire.h #include DallasTemperature.h #include Servo.h // 1. 引脚定义 #define WATER_SENSOR_PIN A0 // 水位传感器模拟引脚 #define ONE_WIRE_BUS 4 // DS18B20数据引脚改到了D4 #define SERVO_PIN 9 // 舵机信号引脚 // 2. 阈值定义需要根据实际测试校准 const int WATER_LOW_THRESHOLD 300; // 模拟值低于此值认为缺水 const float TEMP_LOW 45.0; // 适宜温度下限 const float TEMP_HIGH 55.0; // 适宜温度上限 // 3. 对象初始化 Servo myServo; // 舵机对象 OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(oneWire); Adafruit_SSD1306 display(128, 64, Wire, -1); // OLED对象-1表示无RESET引脚 // 4. 全局变量 int waterLevel 0; float temperature 0.0; bool lastWaterLow false; // 上次缺水状态用于边缘触发 bool lastTempGood false; // 上次水温适宜状态 void setup() { Serial.begin(9600); // 初始化OLED if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F(SSD1306 allocation failed)); for(;;); // 卡死 } display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); // 初始化温度传感器 sensors.begin(); // 初始化舵机 myServo.attach(SERVO_PIN); myServo.write(90); // 初始位置设为90度可根据机械结构调整 delay(500); myServo.detach(); // 初始化后先分离避免抖动和耗电 // 显示启动画面 display.setCursor(0,0); display.println(Smart Cup v1.0); display.display(); delay(2000); } void loop() { // 5. 读取传感器数据 readSensors(); // 6. 逻辑判断与状态更新 bool currentWaterLow (waterLevel WATER_LOW_THRESHOLD); bool currentTempGood (temperature TEMP_LOW temperature TEMP_HIGH); // 7. 更新OLED显示 updateDisplay(currentWaterLow, currentTempGood); // 8. 执行提醒动作边缘触发防止重复动作 if(currentWaterLow !lastWaterLow) { // 从缺水变为缺水触发一次提醒 triggerReminder(Water Low!); } if(currentTempGood !lastTempGood) { // 从不适宜变为适宜触发一次提醒 triggerReminder(Temp Good!); } // 9. 更新上一次状态 lastWaterLow currentWaterLow; lastTempGood currentTempGood; delay(1000); // 每秒检测一次 } void readSensors() { // 读取水位模拟值0-1023值越小通常表示水越多注意传感器特性 waterLevel analogRead(WATER_SENSOR_PIN); // 实际中可能需要用 map(waterLevel, dryValue, wetValue, 0, 100) 转换为百分比 // 读取温度 sensors.requestTemperatures(); temperature sensors.getTempCByIndex(0); // 获取第一个传感器温度 } void updateDisplay(bool waterLow, bool tempGood) { display.clearDisplay(); display.setCursor(0,0); // 显示温度 display.print(Temp: ); display.print(temperature, 1); // 保留一位小数 display.println( C); if(tempGood) { display.println((Perfect!)); } // 显示水位状态 display.print(Water: ); if(waterLow) { display.println(LOW); } else { display.println(OK); } // 显示模拟值调试用 display.print(ADC:); display.println(waterLevel); display.display(); } void triggerReminder(String msg) { Serial.println(Reminder: msg); // 重新附着舵机 myServo.attach(SERVO_PIN); delay(50); // 执行提醒动作从90度转到120度再转回90度 myServo.write(120); delay(500); // 动作持续时间 myServo.write(90); delay(300); // 再次分离舵机以省电 myServo.detach(); }代码关键点解析边缘触发lastWaterLow和lastTempGood变量的使用是关键。它确保了只在状态发生变化时比如从“不缺水”变为“缺水”触发一次提醒动作而不是在持续缺水状态下让舵机不停地转动。这大大降低了功耗也避免了恼人的重复动作。舵机省电策略myServo.detach()函数在舵机不动作时被调用。这会释放控制引脚并切断舵机内部的保持电流对于电池供电系统至关重要。动作前再重新attach。传感器校准WATER_LOW_THRESHOLD这个值不是固定的。你需要在实际组装好后分别读取“空杯”和“满杯”时的模拟值然后取一个合理的中间值作为阈值。DS18B20的温度读取相对准确但首次使用也可以与一个可靠温度计对比校准。4.3 功能优化与扩展思路基础功能实现后可以考虑以下优化水位百分比显示在readSensors()函数中通过map()函数将模拟值映射到0-100的百分比并在OLED上显示一个简单的进度条。多级提醒除了低水位提醒可以增加“极低水位”提醒用不同的舵机动作幅度或频率来区分。历史记录如果使用像Beetle BLE蓝牙这样的版本可以将温度和饮水时间记录通过蓝牙发送到手机App生成简单的饮水报告。待机与唤醒增加一个震动传感器或触摸传感器只有拿起水杯时才唤醒系统全功能运行进一步节省电量。5. 机械结构设计与组装要点电子部分工作正常后需要一个可靠的机械结构来固定所有部件并实现舵机的“提醒”动作。5.1 杯体附着方案选择有几种思路可以将这个智能模块与水杯结合底座式设计一个圆形或方形的底座将水杯放在上面。所有电子部件集成在底座内水位传感器和温度探头通过柔性导线延伸到杯内。优点是通用性强不破坏杯子缺点是探头布置可能不美观。侧挂式3D打印一个带有卡扣的壳体像“夹子”一样固定在杯壁上。更紧凑但需要适配不同直径的杯子。杯盖集成式如果使用带盖的水杯可以将所有传感器集成在杯盖上。这是最一体化的方案但设计和密封要求最高。对于初次尝试推荐底座式。可以使用现成的塑料盒改造或者用亚克力板激光切割拼装。在底座中心开孔让传感器的线缆可以穿出。5.2 舵机提醒机构设计舵机如何“提醒”你这里提供两个经过验证的简单设计轻推手臂在舵机舵盘上安装一根细长的、顶端带有软垫如海绵的连杆。当舵机转动时连杆会轻轻推动你的手背或手腕。关键在于连杆要长这样即使舵机转角不大末端也能有足够的位移形成轻柔的触感。顶起杯垫将水杯放在一个轻质的杯垫上杯垫的一端与舵机连杆铰接。舵机转动时可以将杯垫的一端微微顶起使水杯产生一个轻微的晃动引起你的视觉和触觉注意。这个方案更间接也更有趣。设计时务必在3D建模软件如Fusion 360或纸上先画好草图计算好杠杆比例和运动范围避免干涉。5.3 组装流程与防水处理组装顺序建议如下内部集成先将Beetle、升压模块、锂电池等核心电路在底壳内布局好并用尼龙柱或热熔胶固定。确保线路整齐避免短路。传感器走线将DS18B20和水位传感器的探头线从底座开孔处引出。对DS18B20的探头和线缆连接处进行严格的防水处理使用防水胶如704硅橡胶或热缩管进行密封。水位传感器的感应部分不要密封但非感应部分的电路可以用绝缘漆或胶带保护。外部固定将OLED屏幕嵌入底座上盖的开口处并用胶固定。舵机固定在底座侧壁预设的位置上并安装好连杆机构。总装测试盖上盖子前连接所有线缆上传代码进行整体功能测试。确认温度、水位读取正常显示正确舵机动作符合预期。最终封闭测试无误后封闭底座。建议在盖子接合处使用少量橡皮泥或密封条既能防尘也方便再次打开调试。6. 调试、校准与常见问题排查即使按照教程一步步来第一次通电也难免遇到问题。别担心这是学习过程中最有价值的部分。6.1 上电前检查清单在连接电池之前请务必顺序检查[ ]电源极性用万用表确认电池、升压模块输入输出端的电压极性是否正确特别是给舵机供电的线路。[ ]线缆连接对照接线图逐一检查每根杜邦线是否插紧、插对位置。重点检查电源和地线有没有接反或短路。[ ]传感器状态DS18B20的防水处理是否完好水位传感器的金属探针有无相互触碰导致短路6.2 分模块调试方法当系统不工作时最有效的策略是“分而治之”。1. 基础系统测试首先只连接Beetle和OLED屏幕上传一个最简单的显示测试程序如Adafruit库中的示例程序看屏幕能否点亮并显示内容。这可以验证主控、电源和I2C总线是否基本正常。2. 温度传感器测试单独连接DS18B20到Beetle使用OneWire库的示例程序“ReadTemperature”进行测试。打开串口监视器查看是否能读到温度值。如果读数是“-127”或“85”通常是接线错误、上拉电阻缺失或传感器损坏。3. 水位传感器测试单独连接水位传感器上传一个简单的读取模拟值的程序在串口监视器中观察数值。将传感器探针分别置于空气中和浸入水中数值应有明显变化。如果没变化检查接线和传感器本身。4. 舵机测试最后单独测试舵机。使用Servo库的示例“Sweep”观察舵机能否正常转动。如果舵机只震动不转动或角度不对通常是电源功率不足或信号线接触不良。6.3 典型问题与解决方案速查表问题现象可能原因排查步骤与解决方案OLED屏幕不亮1. 电源未接通或接反2. I2C地址错误3. 屏幕损坏1. 检查VCC和GND连线用万用表测电压。2. 尝试更改代码中的I2C地址常见为0x3C或0x3D。3. 换一个屏幕测试。温度读数固定为-127或851. DS18B20接线错误2. 缺少4.7kΩ上拉电阻3. 传感器防水损坏短路1. 确认VCC, GND, DATA线序。2. 在DATA和VCC间焊接一个4.7kΩ电阻。3. 检查防水密封更换传感器。水位读数无变化1. 模拟口选错2. 传感器探针氧化或污染3. 代码中映射错误1. 确认连接到正确的模拟引脚如A0。2. 用酒精清洁探针或更换传感器。3. 检查analogRead的引脚定义。舵机抖动或不转1. 电源功率不足最主要原因2. 信号线接触不良3. 舵机损坏1.务必为舵机提供独立、充足的电源如5V 1A以上。2. 重新插拔信号线或更换引脚测试。3. 单独给舵机接5V电源测试。系统运行一段时间后复位1. 舵机动作时引起整体电压跌落2. 电池电量不足1. 严格执行舵机独立供电方案并确保电源线足够粗。2. 检查电池电压充电或更换电池。提醒动作重复触发1. 逻辑判断缺少边缘触发机制2. 传感器数值在阈值附近抖动1. 参考代码中lastWaterLow等变量的用法实现边缘触发。2. 在判断中加入“迟滞”区间例如低于280才算缺水高于320才恢复不缺水。6.4 传感器校准实战理论阈值需要在实际环境中校准水位校准将水位传感器固定在杯内预定位置。杯子空置时读取串口输出的模拟值记为dryValue。向杯子注满水至传感器完全浸没读取模拟值记为wetValue。你的低水位阈值WATER_LOW_THRESHOLD可以设为(dryValue wetValue) * 0.3意味着水位低于30%时触发。更高级的做法是在代码中使用map(analogRead(A0), dryValue, wetValue, 0, 100)函数直接得到水位百分比。温度校准将DS18B20探头与一个可靠的温度计如酒精温度计同时放入温水中等待读数稳定。比较两者差值如果DS18B20存在固定偏差可以在代码中加上一个偏移量进行补偿temperature sensors.getTempCByIndex(0) offset;。调试的过程就是与硬件对话的过程。耐心观察现象用串口打印输出关键变量值是定位问题最有效的手段。每一次成功的排查都会让你对这套系统的理解加深一层。当所有模块协同工作舵机在你忘记喝水时轻轻推你一下屏幕上显示着恰到好处的水温提示那种亲手创造智能的成就感就是DIY项目最大的乐趣所在。