基于Arduino的智能感应洗手液机DIY:定时延迟与防误触设计
1. 项目概述:一个更聪明的免接触消毒方案
在公共空间或家庭入口处,我们早已习惯了伸手即出液的自动感应洗手液机。但你是否遇到过这样的尴尬:机器反应灵敏,手一伸过去,消毒液“滋”地一下就喷出来了,可还没来得及搓手,液滴就已经顺着指缝流走大半,不仅浪费,消毒效果也大打折扣。或者,在给小朋友使用时,他们觉得好玩反复伸手,机器就跟着反复出液,一瓶洗手液没几天就见底了。这些看似微小的痛点,恰恰是现有产品体验的短板。
今天要拆解和实现的,就是一个针对这些痛点进行优化的“带定时延迟的自动洗手液分配器”。它不仅仅是一个“感应-出液”的简单开关,而是引入了一个可调节的“大脑”——定时延迟电路。核心目标很明确:在感应到手部后,不是立即出液,而是延迟一个极短且可调的时间(例如0.5-2秒)再启动泵头,并且在单次触发后,设置一个“不响应期”(即延迟)。前者确保了你的手已经移动到泵头下方最佳承接位置,液体能被有效利用;后者则有效防止了误触发和恶意连续触发造成的浪费。
这个项目非常适合电子爱好者、创客入门,以及希望改善公共设施的管理者。它涉及了基础的传感器应用、定时电路设计以及执行机构控制,是理解“感知-决策-执行”这一经典自动化逻辑的绝佳实践。接下来,我将从设计思路、硬件选型、电路搭建、程序逻辑到外壳制作,完整还原一个稳定可靠、可直接复现的DIY过程。
2. 核心设计思路与方案选型
实现“感应延迟出液”和“触发后锁定”这两个核心功能,在技术路径上有多种选择,从纯硬件电路到微控制器编程都能实现。选型的依据在于稳定性、成本、可调性以及制作的复杂度。
2.1 核心功能逻辑拆解
首先,我们必须把需求翻译成明确的电子信号逻辑:
- 待机状态:系统上电,红外感应模块持续检测,泵电机不工作。
- 感应触发:当手进入有效探测范围,红外传感器输出信号跳变(如从高电平变为低电平)。
- 延迟等待:控制器接收到触发信号后,并不立即动作,而是启动一个出液延迟定时器(如1秒)。这个阶段,手有足够时间稳定放置在出液口下。
- 执行出液:延迟时间到,控制器驱动电机或泵工作一个固定的短时间(如0.3秒),吐出定量的洗手液。
- 锁定间隔:出液动作结束后,系统进入一个触发锁定延迟期(如5秒)。在此期间,即使传感器再次被触发,控制器也忽略信号,防止连续出液。
- 恢复待机:锁定间隔结束后,系统恢复到待机状态,等待下一次有效触发。
2.2 技术方案对比与选型
基于上述逻辑,主要有三种实现方案:
方案一:纯数字逻辑电路(如555定时器组合)
- 原理:使用红外传感器模块(输出数字信号)触发第一个555单稳态电路(出液延迟),其输出再触发第二个555单稳态电路(出液脉冲宽度),第二个555的输出驱动电机。锁定功能可能需要额外的RC延时电路或第三个555。
- 优点:成本极低,无需编程,电路稳定可靠,抗干扰能力强。
- 缺点:延迟时间通过电阻电容调节,精度一般且调整不便。功能扩展性差(如要增加液晶显示、多档调节很困难)。
- 适用场景:追求极致低成本、大批量、功能固定的应用。
方案二:基于通用微控制器(如Arduino、STM32)
- 原理:传感器信号送入MCU的IO口,所有延迟、定时、驱动逻辑全部由程序代码实现。
- 优点:灵活性极高,延迟时间可精确到毫秒且易于调整(通过程序修改或旋钮输入)。极易扩展功能,如添加数码管显示延迟时间、电池电量检测、出液量多档调节、连接网络等。
- 缺点:成本相对稍高,需要编程基础。
- 适用场景:DIY创客、功能原型开发、需要灵活调整和扩展的场合。
方案三:基于专用延时继电器模块
- 原理:市面上有现成的“延时触发继电器模块”,传感器可接入其触发端,模块本身可设置“触发延迟时间”和“输出保持时间”。
- 优点:几乎无需额外电路,接线即用,非常方便。
- 缺点:模块通常只提供一种延迟模式,可能无法完美匹配“先延迟再短脉冲”的复杂时序,且参数调整范围可能有限。
- 适用场景:快速验证概念或对电路知识要求极低的简单应用。
我的选型建议与理由:对于大多数DIY场景,方案二(Arduino)是平衡灵活性、易实现性和学习价值的最佳选择。它让我们可以专注于逻辑实现而非繁琐的硬件调试,并且程序的可调性让项目能完美适配不同粘稠度的洗手液和不同的出液口高度。本篇文章也将以Arduino Nano为核心进行详细展开。选择Nano是因为其尺寸小巧,价格便宜,且拥有足够的IO口。
2.3 系统整体架构框图
最终确定的系统硬件架构如下:
[5V/12V电源] -> [电压稳压模块(如需)] -> [Arduino Nano控制核心] | |--- [红外避障传感器] (输入) |--- [直流微型蠕动泵/电磁阀] (输出,通过电机驱动模块) |--- [电位器] (用于调节延迟时间,输入) |--- [可选:数码管/OLED] (用于显示参数,输出)电源部分需要根据泵的电压要求决定。如果泵是12V,那么可以采用12V电源输入,一路通过降压模块给Arduino和传感器提供5V,另一路直接用于驱动泵。
3. 硬件详解与关键物料选型
硬件是项目的骨架,选对部件事半功倍。这里我会详细解释每个部分的选择依据和注意事项。
3.1 感知单元:红外传感器
这是系统的“眼睛”。常见的有两种:
- 红外避障传感器(如E18-D80NK):这是一种调制型光电传感器,发射红外光,接收反射光。输出方式通常是数字量(检测到障碍物输出低电平)。它的探测距离可调(几厘米到几十厘米),抗环境光干扰能力强,非常适合本项目。
- 红外接收管+发射管对管:需要自己搭建发射电路和接收放大比较电路,电路稍复杂,易受环境光影响,不推荐新手使用。
选型要点:
- 输出类型:务必选择数字输出(DO)的模块,这样可以直接连接到Arduino的数字输入引脚,省去了模拟量阈值判断的麻烦。
- 探测距离:选择探测距离在5-15cm可调的型号。距离太近容易触发不及时,太远则容易误触发。
- 安装注意:传感器窗口应保持清洁,避免被洗手液或灰尘污染。安装时最好略微向下倾斜,形成一个探测圆锥,确保手是从侧面伸入而非正对。
3.2 控制核心:Arduino Nano
为什么是Nano而不是更小的ATTiny或更大的Uno?
- 引脚数量:我们需要至少1个数字输入(传感器),1个数字输出(控制泵),1个模拟输入(调节电位器)。如果加上显示,则需要更多。Nano的引脚数量绰绰有余。
- 编程便利性:Nano使用标准的USB口烧录程序,比ATTiny需要编程器方便太多。
- 成本与体积:比Uno小巧便宜,更适合嵌入到最终外壳中。
3.3 执行单元:液体泵与驱动
这是项目的“手”,也是最容易出问题的部分。
泵的类型:
- 微型直流蠕动泵:这是最推荐的选择。它通过滚轮挤压软管来输送液体,液体只接触软管内壁,非常适合输送洗手液、消毒液等有粘性、易腐蚀的液体。更换液体时只需更换软管,清洁极其方便。
- 微型隔膜泵:压力较大,但液体会经过泵腔,清洗不便,长期使用消毒液可能腐蚀泵体。
- 电磁阀:需要配合储液罐的压力(如重力或气泵)使用,通常用于大流量,对于小剂量出液控制反而不如蠕动泵精确。
泵的参数:
- 电压:常见有5V、6V、12V。根据你的电源方案选择。
- 流量:单位是毫升/分钟(ml/min)。假设我们希望单次出液1-2毫升,出液时间0.3秒,那么所需的瞬时流量大约是 (1.5ml / 0.3s) * 60s/min = 300 ml/min。选择一个流量在200-400 ml/min的泵比较合适。
- 管径:配套软管的内径,常见3mm、4mm。决定了出液口的粗细。
驱动电路: 泵是电机负载,工作电流远超Arduino引脚能提供的(通常>100mA)。绝对不可以将泵直接接在Arduino引脚上!必须使用驱动模块。
- MOS管驱动模块(如IRF520模块):适合驱动单个泵,电路简单,可通过PWM调速(本项目不需要调速,仅开关控制)。
- 继电器模块:有机械寿命,动作有声音,但可以控制交流泵。对于直流小泵,MOS管是更安静、寿命更长的选择。
实操心得:泵的选型陷阱我曾在一个早期版本中使用了廉价的3V微型水泵,结果发现其推力根本无法克服洗手液的粘滞阻力,出液断断续续。后来换用标称12V 280ml/min的蠕动泵,在9V电压下工作,力量充沛,出液稳定。教训是:对于粘性液体,务必选择标称流量比你计算值大50%以上的泵,并预留较高的电压余量。
3.4 人机交互与调节单元
- 延迟调节:使用一个10kΩ的旋转电位器。将其两端接在Arduino的5V和GND,中间抽头接模拟输入引脚(如A0)。通过读取模拟值(0-1023)来映射为延迟时间(如0-3000毫秒)。
- 状态显示(可选但推荐):一个0.96英寸的I2C OLED屏(约10元)能极大提升体验。它可以实时显示当前设置的延迟时间、电池电压、出液次数等,方便调试和使用。
3.5 电源系统
- 方案A(桌面使用):直接使用9V或12V的直流电源适配器供电。
- 方案B(便携/无绳使用):使用一块3.7V的18650锂电池,配合一个升压模块,升压至泵的工作电压(如9V)。同时,从电池端接一个降压模块,稳定输出5V给Arduino和传感器。务必选择带充电和保护电路的18650电池盒。
4. 电路连接与程序实现详解
这是项目的“神经”和“灵魂”。我们将一步步连接电路并编写逻辑清晰的代码。
4.1 电路接线图(文字描述)
假设我们使用以下引脚:
D2: 连接红外传感器的数字输出(DO)。D9: 连接MOS管驱动模块的信号输入(IN),驱动泵。A0: 连接电位器的中间抽头。A4 (SDA), A5 (SCL): 连接I2C OLED显示屏。
接线步骤:
- Arduino Nano的
5V和GND分别接到面包板或PCB的正负电源轨。 - 红外传感器:
VCC->5V,GND->GND,DO->D2。 - 电位器:一侧引脚 ->
5V, 另一侧 ->GND, 中间引脚 ->A0。 - MOS管模块:
VCC->5V(逻辑电源),GND->GND,IN->D9。泵的正负极接在模块的OUT+和OUT-上。模块的V+和V-接驱动泵的高压电源(如9V或12V)。注意:逻辑电源和泵电源的GND必须共地! - OLED屏:
VCC->5V,GND->GND,SDA->A4,SCL->A5。
4.2 Arduino程序逻辑与代码解析
程序的核心是状态机和定时器。我们使用millis()函数进行非阻塞延时,确保程序在等待期间还能响应其他操作(如读取电位器)。
#include <Wire.h> #include <Adafruit_SSD1306.h> // 假设使用SSD1306驱动的OLED // 引脚定义 const int sensorPin = 2; const int pumpPin = 9; const int potPin = A0; // 变量定义 unsigned long delayTime = 1000; // 出液延迟,默认1秒 unsigned long pumpDuration = 300; // 泵工作时间,默认0.3秒 unsigned long lockoutPeriod = 5000; // 触发后锁定时间,默认5秒 enum State { IDLE, DETECTED, PUMPING, LOCKOUT }; State currentState = IDLE; unsigned long stateEntryTime = 0; // 记录进入当前状态的时间 int lastSensorValue = HIGH; // 传感器上次状态,初始为高(无触发) // OLED对象 Adafruit_SSD1306 display(128, 64, &Wire, -1); void setup() { pinMode(sensorPin, INPUT_PULLUP); // 启用内部上拉电阻 pinMode(pumpPin, OUTPUT); digitalWrite(pumpPin, LOW); // 确保泵初始关闭 Serial.begin(9600); // 初始化OLED if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println(F("SSD1306 allocation failed")); for(;;); // 卡住 } display.display(); delay(2000); display.clearDisplay(); } void loop() { // 1. 读取电位器,更新延迟时间(例如映射到500-2500ms) int potValue = analogRead(potPin); delayTime = map(potValue, 0, 1023, 500, 2500); // 2. 读取传感器当前状态 int sensorValue = digitalRead(sensorPin); // 3. 状态机逻辑 switch (currentState) { case IDLE: // 在待机状态,检测传感器是否从高变低(下降沿触发,防抖动) if (lastSensorValue == HIGH && sensorValue == LOW) { currentState = DETECTED; stateEntryTime = millis(); // 记录进入“已检测”状态的时间 Serial.println("Hand detected, entering delay."); } updateDisplay("Ready", delayTime); break; case DETECTED: updateDisplay("Wait...", delayTime - (millis() - stateEntryTime)); // 检查出液延迟时间是否已到 if (millis() - stateEntryTime >= delayTime) { currentState = PUMPING; stateEntryTime = millis(); digitalWrite(pumpPin, HIGH); // 启动泵 Serial.println("Pump ON"); } break; case PUMPING: updateDisplay("Dispensing", 0); // 检查泵工作时间是否已到 if (millis() - stateEntryTime >= pumpDuration) { digitalWrite(pumpPin, LOW); // 关闭泵 currentState = LOCKOUT; stateEntryTime = millis(); Serial.println("Pump OFF. Entering lockout."); } break; case LOCKOUT: { unsigned long timeLeft = lockoutPeriod - (millis() - stateEntryTime); updateDisplay("Lockout", timeLeft); // 检查锁定时间是否已到 if (millis() - stateEntryTime >= lockoutPeriod) { currentState = IDLE; Serial.println("Lockout over. Back to IDLE."); } } break; } // 更新上一次的传感器状态,用于边缘检测 lastSensorValue = sensorValue; // 一个小延迟,降低CPU占用,非必须 delay(10); } // 更新OLED显示的函数 void updateDisplay(const char* state, unsigned long param) { display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0,0); display.print("State: "); display.println(state); display.print("Delay: "); display.print(delayTime); display.println(" ms"); display.print("Param: "); display.print(param); display.println(" ms"); display.display(); }代码关键点解析:
- 非阻塞延时:整个逻辑依赖于
millis()记录的时间戳进行时间判断,而不是delay()函数。这使得在等待延迟和锁定的过程中,程序依然能循环执行,实时读取电位器值和更新显示。 - 状态机(State Machine):使用
enum定义了四个明确的状态(IDLE, DETECTED, PUMPING, LOCKOUT)。程序在任何时刻都处于其中一个状态,并根据条件和时间切换到下一个状态。这种结构逻辑清晰,易于调试和扩展。 - 边沿触发:在
IDLE状态中,我们判断的是传感器信号从HIGH到LOW的下降沿,而不是简单的LOW电平。这有效避免了手一直放在那里传感器持续输出LOW而导致系统重复触发的问题。 - 参数可调:
delayTime通过map函数与电位器读数关联,你可以轻松调节映射范围来改变可调节的延迟上下限。
5. 机械结构与外壳制作要点
电路和程序是内核,好的外壳则是项目成功交付的关键。它需要容纳所有组件、固定泵和传感器、提供出液口,并且美观耐用。
5.1 内部布局设计
液路设计:
- 储液瓶:可以使用原装洗手液瓶,或用一个更大的瓶子作为储液罐。关键是在瓶盖上打两个孔,插入两根管子。
- 进液管:一根长管从泵的进口接到瓶底,用于吸取液体。
- 出液管:泵的出口接一根短管,通向外壳的出液嘴。
- 气密性:如果使用原装瓶且泵的吸力不强,需要在瓶盖再开一个很小的通气孔,平衡气压,否则可能吸不上液。
组件固定:
- 泵:蠕动泵运行时会有轻微振动,最好用螺丝或扎带将其固定在底板上,避免长期运行后移位。
- 电路板:使用铜柱或塑料柱将Arduino、驱动模块等固定在底板上。
- 传感器:需要精确固定其位置和角度。可以设计一个带孔的支架,将其对准外壳上的探测窗口。
5.2 外壳设计与加工
- 材料:亚克力板、PVC板、3D打印是常见选择。对于DIY,激光切割亚克力是快速美观的方案。
- 设计软件:使用Fusion 360, SketchUp或甚至Inkscape(用于2D激光切割图纸)进行设计。
- 关键结构:
- 前后面板:前面板开有出液口、传感器窗口、电位器旋钮孔和显示屏窗口。
- 内部隔层:可以将“干区”(电路)和“湿区”(泵和管路)用一块隔板分开,万一漏液也能保护电路。
- 上盖/门:方便添加洗手液和维护内部组件。
- 安装方式:可以设计成壁挂式(背面有钥匙孔或挂钩)或台式。
实操心得:防漏液处理洗手液具有一定的腐蚀性和粘性,漏液是最大风险。我的经验是:
- 所有管接头处,使用小型 hose clamp(扎带型管卡)或至少用铁丝扎紧,不要依赖插紧。
- 泵的进出口使用宝塔接头,并将软管用热水泡软后用力套到底。
- 外壳内部,特别是泵的下方,可以贴一层防水胶垫或放置一个小的吸水垫,万一微漏也能吸收,避免流到电路上。
6. 系统调试、校准与优化
组装完成后,需要进行系统性的调试,确保功能稳定可靠。
6.1 上电调试步骤
- 分模块测试:先不接泵,只给Arduino和传感器上电。打开串口监视器,用手在传感器前晃动,观察串口输出的状态变化是否正常,同时观察OLED显示是否更新。
- 测试驱动电路:将泵的驱动模块与泵断开,用万用表测量驱动模块输出端在触发时的电压,确认控制信号能正常使模块导通。
- 空载测试泵:将泵接上驱动模块,但出口不接管子,放入空水杯。触发系统,观察泵是否在延迟后正常启停,转向是否正确(出液方向)。
- 通液测试:连接完整液路,用清水进行测试。调整
pumpDuration参数(代码中固定为300ms),直到单次出液量达到你满意的程度(建议1-2ml)。 - 延迟校准:旋转电位器,用秒表或观察OLED显示,确认延迟时间变化范围是否符合预期(如0.5s-2.5s)。
6.2 参数优化经验
- 出液延迟时间(
delayTime):这个时间需要根据传感器安装位置和出液口位置来调整。最佳效果是手自然伸到出液口下方时,液滴刚好落下。可以通过反复测试确定一个舒适的范围,并通过电位器映射固定下来。 - 泵工作时间(
pumpDuration):这直接决定了出液量。不同粘度的液体、不同的泵、不同的电源电压都会影响流量。务必针对你最终使用的洗手液进行校准。我的经验是,对于常见的凝胶状洗手液,300ms大约出液1.2ml。 - 锁定时间(
lockoutPeriod):5秒是一个比较安全的初始值。可以酌情缩短,但不建议短于3秒,以免使用者无意的快速挥手导致二次触发。
6.3 功耗优化(针对电池供电)
如果使用电池,功耗至关重要。
- 关闭不必要的功能:如果不用OLED,将其移除。在代码中,可以关闭Arduino的板载LED(
pinMode(LED_BUILTIN, INPUT))。 - 让Arduino休眠:在
IDLE和LOCKOUT状态,如果没有其他任务(如频繁读电位器),可以使用Arduino的低功耗休眠库(如LowPower库),让MCU进入空闲或掉电模式,仅通过外部中断(传感器触发)唤醒。这可以将待机电流从几十mA降至1mA以下。 - 优化电源路径:选择高效率的DC-DC降压/升压模块。
7. 常见问题排查与进阶玩法
即使按照步骤操作,也可能会遇到一些问题。这里列出一些典型故障及解决方法。
7.1 问题排查速查表
| 现象 | 可能原因 | 排查步骤 |
|---|---|---|
| 传感器无反应 | 1. 电源接反或未供电 2. 传感器距离调节不当 3. 传感器窗口脏污 4. 信号线接触不良 | 1. 检查VCC/GND电压。 2. 调节传感器上的电位器(如有),或改变安装距离。 3. 清洁传感器透镜。 4. 用万用表测传感器DO引脚电压,触发时应有跳变。 |
| 泵不工作 | 1. 泵电源未接通或电压不足 2. 驱动模块故障或未使能 3. 泵本身损坏或卡死 4. 程序控制引脚错误 | 1. 测量泵两端驱动电压。 2. 短接驱动模块的IN与VCC,看泵是否转。 3. 直接给泵加额定电压,看是否转动。 4. 用LED接在控制引脚上,看触发时是否闪烁。 |
| 泵持续工作不停 | 1. 驱动模块损坏(常通) 2. 程序逻辑错误,未退出PUMPING状态 | 1. 断开Arduino控制线,看泵是否停转。 2. 检查串口输出,看状态机是否卡在PUMPING。 |
| 出液量不稳定 | 1. 储液瓶液位过低 2. 管路有泄漏或进气 3. 泵动力不足(电压低或泵老化) 4. 洗手液过于粘稠 | 1. 保持液位高于泵体。 2. 检查所有接头,确保密封。 3. 提高驱动电压或更换更大功率泵。 4. 稀释洗手液或选择专用稀释型。 |
| OLED不显示 | 1. I2C地址错误 2. 接线错误(SDA/SCL反) 3. 库未安装或版本不兼容 | 1. 使用I2C扫描程序确认地址(通常是0x3C或0x3D)。 2. 检查四根线是否接对。 3. 在IDE中确认已安装Adafruit SSD1306和GFX库。 |
7.2 功能扩展与进阶思路
基础版本稳定后,你可以考虑以下升级,让设备更智能:
- 出液量多档调节:增加一个按钮或第二个电位器,用来调节
pumpDuration,实现“少量/标准/大量”三种模式,适应不同需求(如儿童用小量)。 - 电池电量监测与指示:通过分压电路读取电池电压,在OLED上显示电量百分比或低压报警。
- 统计计数功能:在EEPROM中存储出液次数,并显示在屏幕上,便于管理洗手液余量。
- 低功耗终极优化:如前所述,使用中断唤醒和休眠模式,配合MOS管彻底切断传感器和显示屏的电源,使待机电流降至微安级,用一节18650电池续航数月。
- 网络功能(Overkill但有趣):换用ESP8266或ESP32,连接Wi-Fi,可以将出液数据上报到物联网平台,实现远程监控余量、使用频率统计等。
这个项目从概念到实现,涵盖了电子、编程、结构三个方面的基础技能。最重要的是,它解决了一个真实存在的体验问题。当你亲手做出这个带延迟的消毒液机,并看到家人或访客能够从容、不浪费地完成手部消毒时,那种满足感远超做一个简单的流水灯。希望这份超详细的指南能帮你顺利复现,并启发你做出属于自己的改进版本。
