
1. 项目概述与核心需求解析钓虾这个在台湾地区风靡的周末活动乐趣在于与虾的博弈和最终的美味。但每次钓虾结束后蹲在水池边一只只数虾或者一边盯着钓竿一边还要分心计算收竿的剩余时间这些琐事确实挺扫兴的。作为一个喜欢鼓捣点电子玩意儿的人我总觉得这些重复、低效的环节应该有更聪明的解决办法。于是我动手做了一个专门用于钓虾场景的智能助手核心就两件事自动帮你数钓上了多少虾以及清晰显示你还有多少时间可以钓。这个装置的核心思路并不复杂就是用超声波传感器来非接触式地“感知”虾或你的手经过虾网入口的动作从而实现自动计数。同时通过一个简单的按钮设置垂钓时长再用LCD屏幕实时显示倒计时和当前虾的数量。整个系统基于Arduino Leonardo搭建成本低易于制作目的就是让钓虾的过程更纯粹、更省心。无论你是电子制作的爱好者还是单纯想提升钓虾体验的玩家这个项目都能提供一个从想法到实物的完整参考。2. 硬件选型与电路设计思路2.1 核心控制器为什么是Arduino Leonardo在微控制器选型上我选择了Arduino Leonardo。可能有人会问更常见的Uno不行吗这里有几个关键的考量。首先Leonardo使用的是ATmega32u4芯片其最大特点是原生支持USB通信可以被电脑识别为鼠标、键盘等HID设备。虽然在本项目中我们并未用到这一高级功能但它意味着Leonardo在USB串口通信上更稳定且芯片本身集成度高。其次与Uno相比Leonardo的模拟输入引脚更多12个数字IO引脚也足够本项目使用。最重要的是Leonardo的5V逻辑电平与我们将要使用的传感器、显示屏完全兼容无需额外的电平转换电路对于快速原型开发来说这大大简化了连接复杂度。注意如果你手头只有Arduino Uno也完全可以胜任本项目。两者的主要区别在于USB接口芯片在基础的数字输入输出和模拟读取功能上是一致的。只需在Arduino IDE中选择对应的板卡型号即可。2.2 感知核心HC-SR04超声波传感器工作原理计数功能的核心是这颗HC-SR04超声波传感器。它的工作原理是经典的“回声测距”。模块上有一个超声波发射器和一个接收器。工作时我们通过单片机给Trig引脚一个至少10微秒的高电平脉冲这会触发发射器发出一束8个40kHz的超声波。这束声波在空气中传播遇到障碍物比如你的手或虾后反射回来被接收器捕获。模块的Echo引脚会输出一个高电平脉冲这个脉冲的宽度与超声波从发射到返回所经历的时间成正比。我们只需要在代码中测量这个高电平的持续时间单位微秒然后利用一个简单的物理公式就能算出距离距离 (高电平时间 * 声速) / 2。声速在常温下大约为340米/秒即每微秒0.034厘米除以2是因为声音走了来回两段路程。在本项目中我们并不需要知道精确的距离数值。我们利用的是“距离突变”这一事件。当传感器前方一定范围内例如5-15厘米突然出现物体时测得的距离值会急剧减小。我们通过代码设定一个距离阈值当实测距离小于这个阈值时就判定为一次有效的“经过”事件从而触发计数。2.3 人机交互LCD1602 I2C显示屏与按钮为了清晰地显示信息我选用了一块带有I2C接口的LCD1602液晶屏。传统的1602屏需要连接多达6根数据线和控制线而I2C版本仅需4根线VCC, GND, SDA, SCL通过一个小板载芯片进行协议转换极大地节省了Arduino的IO口也让布线更加清爽。屏幕第一行用于显示倒计时时间格式为“Time: XX:XX”第二行则显示当前计数值“Shrimp: XX”。设置垂钓时长的输入设备我选择了一个最普通的常开型自复位按钮开关。通过软件去抖和状态检测每按一次按钮设定的时长就增加一小时。这种交互方式直观且硬件成本极低。按钮的一端接在Arduino的某个数字引脚如D9上另一端通过一个10kΩ的下拉电阻接地。当按钮未按下时引脚通过下拉电阻稳定在低电平按下时引脚连接到5V变为高电平。代码通过检测这个从低到高的跳变来识别按键动作。2.4 电路连接详解与布线技巧整个系统的电路连接遵循“电源分区”和“信号独立”的原则。下面是一个详细的连接表格元件引脚/端口连接至 Arduino功能说明HC-SR04VCC面包板5V供电Trig数字引脚 D5触发测距信号Echo数字引脚 D6接收回波信号GND面包板 GND接地LCD1602 (I2C)VCC面包板5V供电GND面包板 GND接地SDA模拟引脚 A4 (或标SDA)I2C数据线SCL模拟引脚 A5 (或标SCL)I2C时钟线按钮开关引脚1数字引脚 D9信号输入引脚2通过10kΩ电阻接GND下拉电阻引脚2另一侧面包板5V上拉源按下时接通电源移动电源Arduino USB口为整个系统供电在面包板上布线时建议先用跳线建立好电源总线和地线总线。也就是在面包板两侧的长条孔位上分别用红色跳线连接所有需要5V的节点用黑色或蓝色跳线连接所有需要GND的节点。然后再将Arduino的5V和GND引脚连接到这两条总线上。这样做的好处是线路清晰避免电源线杂乱交叉也减少了接触不良的概率。实操心得连接超声波传感器时Trig和Echo的信号线尽量不要与电源线长距离平行走线以减少干扰。如果发现传感器读数偶尔不稳定可以尝试在VCC和GND之间并联一个10uF-100uF的电解电容以平滑电源波动。3. 软件逻辑与代码实现深度解析代码是整个项目的“大脑”它需要稳健地处理传感器数据、管理时间、响应按键并更新显示。下面我将分模块详细拆解代码逻辑并提供完整的、带有详细注释的代码。3.1 全局变量与初始化设定首先我们需要引入必要的库并定义所有用到的引脚和变量。使用I2C LCD库可以极大简化屏幕操作。#include Wire.h #include LiquidCrystal_I2C.h // 引脚定义 const int trigPin 5; // 超声波Trig引脚连接D5 const int echoPin 6; // 超声波Echo引脚连接D6 const int buttonPin 9; // 按钮引脚连接D9 // 超声波测距相关变量 long duration; // 存储高电平脉冲时间 int distance; // 计算出的距离厘米 const int detectionThreshold 10; // 检测阈值单位厘米。小于此距离认为有物体经过。 // 计数与状态变量 int shrimpCount 0; // 虾的计数 bool objectDetected false; // 防止一分钟内重复检测的标志 unsigned long lastDetectionTime 0; // 上次检测到物体的时间戳 const unsigned long debounceInterval 60000; // 防重复检测间隔60秒1分钟 // 时间管理变量 int fishingHours 1; // 默认垂钓时长单位小时 unsigned long fishingDurationMillis; // 垂钓总时长毫秒 unsigned long startTime 0; // 开始垂钓的时间戳 bool isFishing false; // 是否开始垂钓的标志 // LCD对象初始化地址通常是0x27或0x3F需根据实际模块调整 LiquidCrystal_I2C lcd(0x27, 16, 2);关键点解析debounceInterval设置为60000毫秒1分钟这是为了防止在将虾放入网中的短暂过程中传感器因物体持续在探测范围内而多次触发计数。这是一个重要的“软件防抖”逻辑。fishingDurationMillis用于存储用户设定的总时间转换为毫秒便于与Arduino的millis()函数返回的时间戳进行计算比较。LCD的I2C地址需要确认。常见的1602 I2C模块地址是0x27但也可能是0x3F。如果屏幕不亮可以尝试用I2C扫描程序确认地址。3.2 核心函数超声波测距与计数逻辑测距函数需要稳定、准确地读取距离而计数逻辑则需要智能地判断一次有效的“放入”动作。int getDistance() { // 1. 确保Trig引脚为低电平然后给出一个10微秒的高脉冲 digitalWrite(trigPin, LOW); delayMicroseconds(2); digitalWrite(trigPin, HIGH); delayMicroseconds(10); digitalWrite(trigPin, LOW); // 2. 读取Echo引脚高电平的持续时间微秒 duration pulseIn(echoPin, HIGH); // 3. 计算距离厘米。声速约340m/s即0.034cm/微秒。距离时间*速度/2。 distance duration * 0.034 / 2; return distance; } void checkForShrimp() { int currentDistance getDistance(); unsigned long currentTime millis(); // 检测逻辑当距离小于阈值且之前一段时间内没有检测过则计数 if (currentDistance detectionThreshold) { if (!objectDetected (currentTime - lastDetectionTime debounceInterval)) { shrimpCount; // 虾数加一 objectDetected true; // 标记为已检测防止本次连续触发 lastDetectionTime currentTime; // 更新最后检测时间 Serial.print(Shrimp Detected! Total: ); // 串口输出用于调试 Serial.println(shrimpCount); updateDisplay(); // 更新屏幕显示 } } else { // 当物体离开检测区域后重置检测标志为下一次检测做准备 objectDetected false; } }为什么这样设计防抖逻辑在钓虾的实际操作中你将虾从钩上取下然后手伸过传感器放入网中这个动作可能持续几秒钟。如果不加防抖在这几秒内传感器会持续报告“有物体”导致一次动作被误计为多次。我们的逻辑是一旦检测到物体并计数立即设置一个“冷却期”这里是1分钟。在冷却期内即使物体仍在传感器前也不会再次计数。直到物体离开探测范围distance threshold且冷却期过后系统才准备进行下一次计数。这完美模拟了“放入一只虾”这个离散事件。3.3 时间管理与倒计时显示时间管理是项目的另一核心。我们使用Arduino的millis()函数来管理时间它返回自开发板启动以来的毫秒数避免了使用delay()导致程序卡住的问题。void updateTimer() { if (!isFishing) return; // 如果未开始垂钓则不更新时间 unsigned long currentTime millis(); unsigned long elapsedTime currentTime - startTime; // 已过去的时间 unsigned long remainingTimeMillis fishingDurationMillis - elapsedTime; // 剩余时间 // 如果时间到 if (remainingTimeMillis 0) { remainingTimeMillis 0; isFishing false; // 停止垂钓 lcd.setCursor(0, 0); lcd.print(Times Up! ); // 显示时间到 } // 将剩余毫秒转换为分钟和秒 int remainingSeconds remainingTimeMillis / 1000; int minutes remainingSeconds / 60; int seconds remainingSeconds % 60; // 在LCD第一行显示时间 lcd.setCursor(0, 0); lcd.print(Time: ); if (minutes 10) lcd.print(0); lcd.print(minutes); lcd.print(:); if (seconds 10) lcd.print(0); lcd.print(seconds); lcd.print( ); // 用空格覆盖可能残留的字符 } void updateDisplay() { // 更新第二行的虾数量显示 lcd.setCursor(0, 1); lcd.print(Shrimp: ); lcd.print(shrimpCount); lcd.print( ); }使用millis()的注意事项millis()函数在大约50天后会溢出归零但对于本项目最长几小时的运行时间来说完全不是问题。这种“非阻塞式”的时间处理方式使得系统在等待时间流逝的同时还能同时处理按键、检测传感器程序响应非常流畅。3.4 按钮交互与状态机控制按钮用于设定垂钓时长并启动系统。我们需要实现一个简单的状态机上电后10秒内为设置模式按按钮设定小时数10秒后或按下按钮后自动开始倒计时。void checkButton() { static unsigned long lastDebounceTime 0; static int buttonState; static int lastButtonState LOW; int reading digitalRead(buttonPin); // 简单的按钮消抖如果读数变化重置消抖计时器 if (reading ! lastButtonState) { lastDebounceTime millis(); } // 如果读数稳定时间超过50毫秒则认为状态有效 if ((millis() - lastDebounceTime) 50) { if (reading ! buttonState) { buttonState reading; // 检测上升沿按钮按下 if (buttonState HIGH) { onButtonPressed(); } } } lastButtonState reading; } void onButtonPressed() { static unsigned long setupWindowStart millis(); // 记录上电时间 const unsigned long setupWindow 10000; // 10秒设置窗口 // 如果还在10秒的设置窗口内 if (millis() - setupWindowStart setupWindow) { fishingHours; // 每次按下时长增加1小时 if (fishingHours 8) { // 假设最多钓8小时可调整 fishingHours 1; } fishingDurationMillis fishingHours * 3600000UL; // 转换为毫秒 // 更新屏幕第二行显示设置的小时数 lcd.setCursor(0, 1); lcd.print(Set: ); lcd.print(fishingHours); lcd.print( hour(s) ); delay(300); // 短暂延时提供设置反馈 } else if (!isFishing) { // 如果设置窗口已过且未开始则按钮按下视为开始垂钓 startFishing(); } } void startFishing() { startTime millis(); // 记录开始时刻 isFishing true; shrimpCount 0; // 开始新的垂钓计数清零 lcd.clear(); lcd.setCursor(0, 0); lcd.print(Start Fishing!); delay(1000); updateDisplay(); // 显示初始计数0 }状态机设计解析上电后的前10秒是一个“黄金设置期”。此时屏幕会提示你按按钮设置小时数。每按一次时长增加并显示。10秒过后系统自动进入待命状态此时再按一次按钮则正式启动倒计时和计数功能。这种设计避免了额外增加一个“开始/确认”按钮简化了操作。3.5 主循环与系统整合最后在setup()中完成初始化在loop()中将所有功能整合起来。void setup() { Serial.begin(9600); // 初始化串口用于调试输出 // 初始化引脚模式 pinMode(trigPin, OUTPUT); pinMode(echoPin, INPUT); pinMode(buttonPin, INPUT); // 注意依靠外部下拉电阻此处为INPUT即可 // 初始化LCD lcd.init(); lcd.backlight(); lcd.clear(); lcd.setCursor(0, 0); lcd.print(Set Time (10s)); lcd.setCursor(0, 1); lcd.print(Press Button); // 初始化默认垂钓时长1小时 fishingDurationMillis fishingHours * 3600000UL; } void loop() { checkButton(); // 持续检测按钮状态 checkForShrimp(); // 持续检测是否有虾经过 updateTimer(); // 更新并显示倒计时 // 可以添加一个短暂延时降低循环频率减少CPU占用非必须 // delay(50); }主循环非常简洁它不断地轮询三个核心任务检查按钮输入、检查传感器、更新时间显示。这三个函数都是非阻塞的因此系统运行起来响应迅速感觉不到卡顿。4. 外壳制作与现场部署要点硬件和软件完成后一个耐用的外壳和正确的部署方式决定了它的实用性和可靠性。4.1 外壳设计与加工我选择了一个大小合适的硬纸盒作为外壳因为它易于加工、成本低廉且绝缘。制作步骤如下定位与开孔首先将组装好的面包板和Arduino放入盒中规划好各个元件的位置。超声波传感器在盒子顶部开两个直径与传感器超声波收发头一致的圆孔。确保传感器正面印有字的一面朝外且前方无遮挡。可以用热熔胶将传感器从内部固定。LCD屏幕在盒子正面开一个矩形窗口大小略小于屏幕显示区域以便用胶水将屏幕的边框粘在盒子内壁让屏幕正好露出。按钮在盒子侧面开一个小孔将按钮的螺母拧紧固定。电源线在盒子背面或侧面开一个孔让Micro USB线可以穿入连接到Arduino。内部固定与散热使用尼龙扎带或大量热熔胶将Arduino主板、面包板、移动电源固定在盒子底部防止在携带过程中晃动导致线缆脱落。注意移动电源在工作时可能轻微发热不要将其与其他元件紧密贴合留出一点空隙。防水防潮考虑进阶纸盒不防水。如果常在潮湿的钓虾场使用可以考虑升级外壳。一个简单的办法是在纸盒内部整体铺上一层塑料薄膜或防水胶带。更专业的做法是使用防水接线盒如PG盒并在开孔处使用防水格兰头来穿线。4.2 现场部署与使用流程正确的部署是准确计数的关键开机与设置将移动电源连接好打开开关。设备启动屏幕显示“Set Time (10s) Press Button”。在接下来的10秒内按按钮设定你想要垂钓的小时数例如按3下代表3小时。屏幕第二行会实时显示设置的小时数。放置设备10秒设置窗口过后屏幕会显示默认的倒计时和“Shrimp: 0”。此时将整个设备牢固地放置在虾网的网框边缘确保超声波传感器的探测方向垂直朝向虾网入口的正上方。探测区域应该覆盖你用手将虾放入网内的必经路径。开始垂钓与计数按一下按钮倒计时正式开始。之后每次钓到虾将其从钩上取下在将虾投入网中的过程中确保虾或你的手会从超声波传感器的正前方经过距离传感器大约5-10厘米。听到蜂鸣器如果添加了响一下或看到屏幕上的数字增加即表示计数成功。防误触与校准避免在非放虾时让其他物体如饵料盒、毛巾在传感器前晃动。首次使用时可以测试一下用手在传感器前快速划过观察计数是否增加并调整传感器的角度和探测阈值detectionThreshold变量直到反应灵敏且不易误触发。重要提示超声波传感器在极端天气如非常潮湿、炎热下性能可能受影响。如果发现计数不灵可以检查传感器表面是否凝结水珠并擦干。同时确保传感器前方没有蜘蛛网等细小障碍物。5. 常见问题排查与功能扩展在实际制作和使用中你可能会遇到一些问题。这里列出一些常见情况及其解决方法。5.1 硬件连接与电源问题现象可能原因排查步骤与解决方案LCD屏幕不亮1. I2C地址错误2. 电源接反或未接通3. 对比度电位器未调好1. 运行I2C扫描程序确认地址并修改代码中的0x27。2. 用万用表检查VCC和GND间是否有5V电压。3. 找到屏幕背面的蓝色电位器用小螺丝刀缓慢旋转直到字符显示。超声波传感器无反应或读数固定1. Trig/Echo线接反2. 电源不足3. 传感器故障1. 检查接线是否与代码定义一致Trig输出Echo输入。2. 尝试单独为传感器供电或检查Arduino的5V输出是否稳定。3. 将Trig和Echo短接如果测出的距离为0或极小则传感器可能正常否则可能损坏。按钮按下无反应1. 下拉电阻未接或虚焊2. 引脚模式设置错误3. 按钮损坏1. 确保按钮信号引脚通过10kΩ电阻可靠接地。2. 确认代码中buttonPin设置为INPUT模式。3. 用万用表通断档测试按钮按下时是否导通。系统运行不稳定偶尔重启移动电源输出电流不足或线缆质量差更换输出电流大于1A的移动电源并使用质量好的USB数据线非仅充电线。5.2 软件与逻辑问题现象可能原因排查步骤与解决方案计数重复或漏计1. 防抖时间间隔debounceInterval设置不当2. 探测阈值detectionThreshold不合理1. 根据你放虾的动作速度调整debounceInterval如果动作慢可适当增加如90000毫秒1.5分钟。2. 通过串口监视器观察实际距离将阈值设置为略小于手/虾经过时的最小距离值。倒计时时间不准代码中存在长时间delay()影响millis()计时检查代码确保在loop()和所有被调用的函数中没有使用长的delay()。所有延时都应使用基于millis()的非阻塞方式。设置时间后不开始状态机逻辑错误isFishing标志未正确置位添加串口调试输出打印isFishing、fishingHours等关键变量的值跟踪按钮按下后的程序流程。5.3 功能扩展与优化建议基础版本已经可用但如果你有兴趣可以进一步升级增加声音反馈连接一个有源蜂鸣器到另一个数字引脚。在checkForShrimp()函数中计数成功时让蜂鸣器短响一声提供听觉确认在嘈杂环境中尤其有用。添加蓝牙模块接入一个HC-05或HM-10蓝牙模块将虾的数量和剩余时间实时发送到手机APP上实现远程查看无需总是低头看设备。数据存储增加一个SD卡模块每次钓虾结束后将日期、时长、最终虾数保存到CSV文件中用于长期记录和分析你的“战果”。低功耗优化如果希望用电池供电更长时间可以考虑使用Arduino Pro Mini3.3V版本并关闭不必要的功能如LED、稳压器在传感器不检测时让单片机进入休眠模式。防水外壳升级使用3D打印或购买现成的防水盒制作一个真正全天候的钓虾计数器。这个项目从想法到实现最让我有成就感的部分不是代码调试成功的那一刻而是第一次把它带到虾场看到它真的能准确无误地替我数虾让我能更专注地盯着浮标的那一刻。技术的目的终究是服务于人解决那些小而具体的烦恼。希望这个详细的分享能帮你做出属于自己的智能钓虾助手或者给你带来一些将电子制作融入生活乐趣的灵感。如果在制作过程中遇到任何问题回顾一下第五部分的排查表格大部分都能找到答案。祝你每次出行都能收获满满享受科技带来的那份从容。