拓冰建站拓冰建站
首页 / 资讯中心 / 正文

MiroFish:ESP32+MQTT智能水族箱监测与自动投喂系统

我养鱼这件事前前后后快八年了。最开始那几年翻缸翻得我怀疑人生——出差三天回来缸里少两条鱼是常事冬天加热棒半夜罢工第二天早上整缸鱼全浮头更坑的是水质缓慢恶化肉眼看着水清得很一周后鱼开始蹭缸、烂尾等你反应过来已经来不及了。后来我干脆自己动手把一套软硬件拼起来给它起了个名字叫MiroFish。MiroFish 不是一条鱼也不是哪个电商平台上的成品套装它是我自己攒出来的一套智能水族箱监测与自动投喂系统一块 ESP32 做边缘主控几个探头盯着水温、pH、电导率和水位一路继电器驱动加热棒、水泵、补光灯和喂食器数据通过 MQTT 上报到一台常年开机的小主机再用一个网页看板把曲线画出来手机随时能翻。它解决的问题非常具体没人喂鱼、设备悄悄罢工、水质缓慢漂移这三件事。这篇文章适合谁看养鱼的新手、想拿它当毕业设计的同学、刚上手嵌入式和物联网的创客以及所有想在真实场景里练一遍采集—传输—存储—告警完整链路的人。接下来我把自己踩过的坑、算过的参数、写过的代码按模块拆开讲一遍。1. 项目整体设计与思路拆解1.1 为什么把鱼缸当成一个典型的物联网系统来做很多人一听智能鱼缸第一反应是买个带 WiFi 的成品。我劝你先别急因为成品最大的问题不是功能少而是它不告诉你它的判定逻辑是什么。比如加热棒什么时候断、喂食器一次掉多少克、pH 报警阈值是怎么定的你全都不知道。一旦鱼出事你连复盘都做不了。MiroFish 的第一个设计原则就是所有判断逻辑都在我自己的代码里看得见、改得动。从系统结构上看一个鱼缸其实是典型的四层物联网架构。最底层是感知层温度、pH、电导率、水位这些物理量被探头转成电压或数字信号。往上是边缘层ESP32 负责采样、滤波、本地判断把原始电压变成有意义的物理量。再往上是传输与服务层MQTT 把数据送到服务端服务端负责存储、规则判断和通知。最上面是交互层网页看板和手机页面。这个分层不是为了好看而是为了故障隔离。网络断了边缘层还能自己控温、自己投喂服务端挂了鱼照样活着看板崩了也只是看不到曲线而已。我见过太多把逻辑全塞进云端的方案一旦家里宽带抽风加热棒就彻底失控——这在冬天是会死鱼的。所以 MiroFish 的核心判断温度上下限、水位下限、喂食超时保护全部放在 ESP32 本地执行云端只做锦上添花的展示和长周期分析。1.2 硬件选型主控、探头和执行器的取舍逻辑主控我选的是ESP32理由很实在双核、自带 WiFi、ADC 通道够、GPIO 数量够、价格便宜、Arduino 和 MicroPython 生态都成熟。有人会问为什么不用树莓派 Pico W 或者 STM32。Pico W 的 ADC 只有 3 个可用引脚接口数量在鱼缸这种多探头多执行器的场景下偏紧STM32 能力强但要外挂 WiFi 模组网络这块的调试成本对个人项目来说不划算。ESP32 算是能力和成本的甜蜜点。探头这块我走过弯路这里直接说结论。水温用 DS18B20 防水探头一线总线抗干扰好精度 ±0.5℃一条总线能挂好几个性价比没有对手。pH用的是玻璃电极配模拟调理板注意这玩意儿输出的是高阻抗信号对走线和供电噪声极其敏感绝对不能跟水泵、继电器共用一条地线回到主控。电导率/TDS用两电极式的模拟模块便宜但容易极化必须用交流激励才能长时间泡在水里。水位我用的是机械浮球加超声波双保险浮球做安全联锁超声波做连续液位显示。执行器这边要注意一个原则弱电控制强电中间必须有隔离。ESP32 的 GPIO 只有 3.3V、几十毫安直接驱动继电器模块的线圈都可能带不动更别说加热棒这种几百瓦的负载。我的做法是 GPIO 先过光耦或者 ULN2003 这类达林顿阵列再去推继电器继电器触点接 220V 回路而且加热棒回路里串一个独立的温控开关做物理兜底——软件死机了机械温控还能把温度压住。注意220V 部分千万不要在带电状态下接线调试。继电器模块和主控板要分两个接线盒进线端加漏电保护所有金属外壳必须可靠接地。这不是啰嗦我见过烧掉的板子不止一块。1.3 软件架构边缘采集、消息总线、服务端与看板软件部分我拆成三个可独立运行的东西。固件跑在 ESP32 上用 Arduino 框架写负责采样、滤波、本地联锁、上报和接收指令。服务端跑在一台低功耗迷你主机上Python 写的一个 MQTT 客户端订阅所有设备主题把数据写进时序库同时跑一个规则引擎做告警判断。前端是个单页应用直接调服务端的 HTTP 接口拿数据画图。中间那层为什么用 MQTT 而不是 HTTP 轮询因为鱼缸的数据是持续小流量的每 30 秒一条一天也就 2880 条用 HTTP 轮询会带来大量无意义的连接开销而且设备端要维护定时器和重试逻辑。MQTT 的长连接、QoS 机制和遗嘱消息LWT天生适合这个场景——设备掉线Broker 立刻把遗嘱发出来告警秒级触发不用等心跳超时。主题设计也很关键我用的是一套固定的层级mirofish/{device_id}/telemetry上报遥测数据mirofish/{device_id}/status用遗嘱消息上报在线状态mirofish/{device_id}/cmd下发命令mirofish/{device_id}/ack回执。设备 ID 我用了 MAC 地址后六位好处是换固件不用改配置坏处是可读性差所以我额外在服务端做了一张 ID 到昵称的映射表。1.4 三条设计红线稳定性、安全联锁和可维护性做这种东西我心里一直挂着三条线。第一条是稳定性优先于功能丰富。我不追求一口气测八个参数实际上很多参数家用场景根本没必要在线测后面会细说。每多一个探头就多一个漂移源、多一根线、多一个故障点。第二条是任何自动动作都必须有物理或逻辑兜底。自动投喂必须有单次上限和日累计上限自动加热必须有独立温控和缺水断电自动补水必须有溢出保护。软件再稳也有死机的时候而鱼不会给你第二次机会。第三条是可维护性。这条最容易被忽略。我的做法是所有配置项阈值、投喂时间、设备昵称都存在服务端设备启动时拉一次改参数不用重新烧固件固件版本号上报到 status 主题看板上直接显示哪天某台设备行为异常第一眼就能确认是不是版本不一致。这三点说起来简单但真正做起来能省掉后面 80% 的折腾。2. 核心指标解析哪些参数真的值得测2.1 温度、pH、电导率、溶解氧与氮化合物的取舍先把一个残酷的事实摆出来家用鱼缸在线监测性价比最高的只有三个参数——温度、pH、电导率TDS。剩下的要么太贵要么维护成本高到你会放弃。温度不用多说它是所有生化反应的速率控制器也是鱼体免疫力的直接开关。淡水热带鱼舒适区一般 24 到 28℃昼夜波动最好控制在 2℃ 以内。温度每偏离舒适区 3℃鱼的压力水平就明显上升。pH 反映的是水体酸碱度绝大多数淡水鱼适合 6.5 到 7.5。但 pH 有个特点它会因为光照、CO₂ 浓度、硝化作用呈现明显的昼夜节律早上偏低、下午偏高波动 0.3 到 0.5 是正常的。所以看 pH 不要只盯绝对值更要看波动幅度和长期趋势。电导率或换算出的 TDS反映的是溶解性总固体可以理解成水里溶了多少东西。它升高通常意味着蒸发浓缩或者代谢废物累积是判断该换水了的一个很好的辅助指标。溶解氧和氨氮、亚硝酸盐呢我不是说它们不重要恰恰相反氨氮和亚硝酸盐是真正会直接毒死鱼的东西。但在线监测它们的成本太高可靠的氨氮在线探头基本都是工业级价格家用的试剂比色法又需要人工操作。所以我的做法是这两个指标靠定期手工检测 规律换水来控制系统只负责提醒你该测了。溶解氧同理光学溶解氧探头价格不低而且家用缸有水泵和气泵溶氧一般不会是瓶颈除非你密度特别高。下面这张表是我实际用下来对各个参数的评估供你直接抄。参数常用探头方案典型精度建议校准周期成本档位是否建议在线水温DS18B20 防水探头±0.5℃一年或换探头低强烈建议pH玻璃电极 模拟调理板±0.1 pH2 到 4 周中建议电导率/TDS两电极模拟模块±2% 满量程1 到 2 个月低建议水位浮球开关 超声波浮球 ±2mm超声 ±3mm基本免校低建议浊度红外对射或散射式±5%1 个月中可选溶解氧光学或膜式电极±0.2 mg/L2 到 4 周高一般不建议氨氮离子选择电极或比色差异极大频繁很高一般不建议亚硝酸盐试剂比色半定量每次使用低手工定期测2.2 传感器的漂移规律与校准方法探头这东西买回来第一天和用了一个月读数能差出不少尤其是 pH 和电导率。理解漂移的原因是做好校准的前提。pH 电极的漂移主要来自三个地方玻璃膜表面污染蛋白、藻类附着、参比电极内液流失或结晶、以及电极老化导致的斜率下降。判断电极是否健康最靠谱的方法是做两点或三点校准用 pH 4.00 和 pH 6.86或者 9.18的标准缓冲液看电极实际输出的毫伏值。理想情况下25℃ 时每变化 1 个 pH 单位输出变化约 59.16 mV这叫能斯特斜率。如果你的电极斜率掉到 50 mV/pH 以下基本可以考虑换了。电导率探头的漂移主要来自电极极化和表面结垢。两电极式探头如果长期通直流电电极表面会析出气泡和沉积物读数一路往上飘。所以要么选交流激励的模块要么定期用软布加稀盐酸轻擦电极表面。校准用标准电导液常见的是 1413 µS/cm 和 12.88 mS/cm。DS18B20基本不怎么漂它的坑不在这里而在读取上。它上电默认返回 85℃如果转换没完成你就去读会拿到这个 85 的假值。所以代码里必须判断读到 85 或者明显偏离上次值超过 5℃直接丢弃等下一轮。提示所有探头的线缆接头都别裸露在潮湿空气里。我的做法是用热缩管套住再灌一层硅胶密封最后整条线走 PVC 线槽。水汽顺着线缆爬进接线盒是电子设备在鱼缸旁边最常见的死法。2.3 水体容积与设备功率的参数计算这一段是很多人直接拍脑袋的地方我把它算清楚。水体容积不能按缸的外形尺寸算要按实际装水量算。公式是长 × 宽 × 高厘米÷ 1000 升数但要注意扣除底砂、沉木、设备和留出的水面高度。经验上把理论容积乘 0.8 是比较接近实际的。举个例子一个 60×40×40 的缸理论容积是 96 升扣掉底砂和留空实际水体大约 75 到 80 升。加热棒功率的经验公式是P V × ΔT × k其中 V 是实际水体升数ΔT 是目标温度与冬季室温的差值k 是保温系数普通玻璃缸取 0.12 到 0.18 W/(L·℃)有盖有保温棉的可以取到 0.08。举个例子80 升水冬天室温 16℃目标 26℃ΔT 10℃取 k 0.15那么 P 80 × 10 × 0.15 120W。实际选型往上留 20% 到 30% 余量选 150W 比较稳。注意千万别为了升温快选超大功率功率过大的加热棒在水量少的时候升温极快一旦温控失效几分钟就能把鱼煮熟。水泵流量的通用经验是每小时循环整缸水体的 5 到 8 倍。80 升的缸需要 400 到 640 L/h 的标称流量。但标称流量是零扬程下的数据实际扬程每增加 1 米流量会掉一大截一般产品说明里会有扬程-流量曲线。如果你的过滤盒在缸上方 60 厘米那至少要再上浮 30% 选型。照明时长我固定 8 到 10 小时用定时器控制绝不随心情开关。光照时间过长超过 12 小时几乎必然爆藻这是我在自己缸里反复验证过的一条规律。2.4 投喂量与投喂频率的量化逻辑自动投喂最容易翻车因为它一旦失控是过量而不是不足。过量投喂会直接导致氨氮飙升、水体浑浊、鱼撑出肠炎。所以量化非常重要。通用经验是日投喂量按鱼体总重的1% 到 3%。小型热带鱼代谢快取 2% 到 3%金鱼、锦鲤这类取 1% 到 2%。举个例子缸里有 20 条平均 3 克的斑马鱼总重 60 克按 2.5% 算日投喂量约 1.5 克。如果分成早晚两次每次就是 0.75 克。问题来了你的喂食器一次到底掉多少克绝大多数人会跳过标定这一步然后全靠感觉。我的标定方法很土但很准把喂食器架在电子秤上精度 0.01 克连续执行 10 次单次投喂称总重除以 10得到单次平均投喂量再调转速或转动时间把单次量调到目标值。这个过程大概花 20 分钟但能避免后面几个月的反复试错。另外必须设置硬性上限单次投喂不超过日投喂量的 60%日累计不超过设定值的 110%一旦超过就锁定并告警。这是纯软件层面的保底。鱼饿三天基本没事撑一次可能就全军覆没。3. 硬件搭建与固件实现的实操要点3.1 电路连接、供电与防潮处理先讲供电。整套系统我用一个 5V/3A 的开关电源给主控和继电器模块供电220V 那边单独走一路。这样做的好处是弱电和强电彻底分开调试的时候可以只给弱电上电安全得多。模拟探头pH、电导率的供电要特别讲究。它们的输出是毫伏级信号最怕电源纹波和地线串扰。我的做法是给模拟部分单独用一颗低压差线性稳压芯片供电模拟地和数字地单点汇合pH 调理板的输出线用带屏蔽层的线屏蔽层只在主控这一端接地。继电器动作的瞬间会有很大的反向电动势如果地线共用你会看到 pH 读数瞬间跳一个很大的值——这就是典型的干扰。传感器连接上DS18B20 的数据线需要接一个 4.7kΩ 的上拉电阻到 3.3V。这个电阻不能省也不能随便换值太小了功耗大且可能驱动不了太大了上升沿变缓读数容易出错。走线超过 2 米的话建议用屏蔽双绞线数据线和地线绞在一起。防潮是长期可靠性里最关键的一环。我的具体做法分三步第一所有 PCB 板喷涂三防漆重点覆盖焊点和走线密集区第二接线盒选 IP65 级别进线孔朝下用防水接头锁紧第三探头线缆和水面之间做一个滴水弯让水珠顺着线往下滴而不是往接线盒方向爬。这三步做完我那套设备在湿度常年 70% 以上的环境里连续跑了一年多没出过问题。注意加热棒和补光灯的电源线一定要和信号线走不同方向实在避不开就垂直交叉绝不要平行贴着走几十厘米。平行走线是感应干扰的主要来源。3.2 ESP32 固件框架与采样滤波固件的整体结构我用的是定时器采样 状态机 非阻塞上报绝对不用delay()卡住主循环。原因很简单一旦你在某个函数里 delay 几秒这期间温度超标了你也不知道水位掉到探头以下也来不及反应。我见过用delay(5000)写温控的代码那不是温控那是赌博。采样这块原始 ADC 值必须滤波不然上报的曲线会像心电图。我用了三级处理先做去极值一次采 15 个点去掉最大最小各 3 个再做中位数滤波最后做一阶低通平滑。这样处理完读数既平滑又不会滞后太多。下面是我实际用的核心代码片段可以直接参考// 采样滤波去极值 中位数 一阶低通 const int SAMPLE_N 15; float g_filtered 0.0f; const float ALPHA 0.2f; // 低通系数越小越平滑但越滞后 float readFilteredAdc(int pin) { int buf[SAMPLE_N]; for (int i 0; i SAMPLE_N; i) { buf[i] analogRead(pin); delayMicroseconds(200); } // 简单插入排序样本少足够了 for (int i 1; i SAMPLE_N; i) { int key buf[i], j i - 1; while (j 0 buf[j] key) { buf[j 1] buf[j]; j--; } buf[j 1] key; } // 取中间 9 个的平均值天然去掉了极值 long sum 0; for (int i 3; i SAMPLE_N - 3; i) sum buf[i]; float med sum / (float)(SAMPLE_N - 6); // 一阶低通 g_filtered ALPHA * med (1 - ALPHA) * g_filtered; return g_filtered; }温度读取要处理 85℃ 的假值逻辑是这样的// DS18B20 读取与异常值过滤 float lastValidTemp 25.0f; float readWaterTemp() { sensors.requestTemperatures(); float t sensors.getTempCByIndex(0); // 85 是上电默认值-127 是断线 if (t 85.0f || t -100.0f) return lastValidTemp; // 单次变化超过 5℃ 视为异常丢弃 if (fabs(t - lastValidTemp) 5.0f) return lastValidTemp; lastValidTemp t; return t; }本地联锁我写得很直白就是一组 if 判断但条件必须是复合条件。以加热为例只有当水位正常且温度低于下限且距离上次加热停止超过 60 秒这三个条件同时满足才允许开加热。这样能防住误判、防住频繁启停、也防住了干烧。// 加热联锁水位 温度 最小间隔三重条件 if (waterLevelOk currentTemp TEMP_LOW millis() - lastHeatOff 60000UL) { digitalWrite(RELAY_HEATER, HIGH); heaterOn true; } else if (currentTemp TEMP_HIGH || !waterLevelOk) { digitalWrite(RELAY_HEATER, LOW); heaterOn false; lastHeatOff millis(); }3.3 MQTT 主题设计与断网缓存上报格式我最终选了 JSON虽然比二进制大一点但可读性带来的调试便利完全值这个代价。一条典型的遥测消息长这样{ ts: 1718000000, temp: 25.4, ph: 7.12, tds: 186, level: 32.5, heater: 1, fw: 1.4.2 }QoS 我设的是 1保证至少送达一次。有人会问为什么不用 QoS 2因为 QoS 2 的握手开销对 30 秒一条的频率来说没必要而且服务端写库时用时间戳做主键或者做去重重复消息不会造成问题。遗嘱消息是这样注册的连接时往mirofish/{device_id}/status发一条{online:true}且设置 retain 为真同时用will注册{online:false}。这样设备一掉线Broker 立刻把遗嘱发出去并覆盖旧状态看板上那个绿点秒变灰。断网缓存这块我踩过一个坑。最早的版本是网络断了就直接丢数据结果有次路由器重启断了两小时那段时间正好是凌晨温度最低的时候事后想复盘完全没数据。后来我改成用环形缓冲区缓存最近 200 条记录网络恢复后按时间戳补传。200 条按 30 秒一条算能撑 100 分钟覆盖绝大多数家庭网络故障时长。补传的时候要注意加一个标志位让服务端知道这是补传数据不要触发实时告警——否则你会在恢复网络的一瞬间收到几十条过期的告警推送。提示MQTT 客户端 ID 一定要保证唯一我直接用设备 MAC 生成。两台设备用同一个客户端 ID 会互相顶掉表现就是两台设备疯狂交替上下线非常难查。4. 服务端与看板的落地过程4.1 数据落库与时序表设计服务端我选的是 PythonMQTT 客户端用 paho-mqttWeb 框架用 FastAPI存储用的是 SQLite 加时间索引。有人会说 SQLite 撑不住时序数据我可以负责任地讲单缸 30 秒一条一年也就 100 万条出头SQLite 处理这个量级毫无压力查询加好索引完全够用。上 TimescaleDB 反而增加了运维负担。当然如果你要管几十个缸那就另说。表结构我设计得很简单一张遥测表加一张事件表CREATE TABLE telemetry ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_id TEXT NOT NULL, ts INTEGER NOT NULL, -- Unix 秒 temp REAL, ph REAL, tds REAL, level REAL, heater INTEGER, fw TEXT, backfill INTEGER DEFAULT 0 -- 1 表示补传数据 ); CREATE INDEX idx_tel_dev_ts ON telemetry(device_id, ts); CREATE TABLE events ( id INTEGER PRIMARY KEY AUTOINCREMENT, device_id TEXT NOT NULL, ts INTEGER NOT NULL, level TEXT NOT NULL, -- info / warn / critical code TEXT NOT NULL, message TEXT );服务端的接收逻辑要做几件事解析 JSON、校验字段范围比如温度不在 -10 到 50 之间就丢弃、去重同一设备同一时间戳只写一次、写库、把数据推给规则引擎。字段校验这步别省我遇到过探头接触不良导致上报温度 -127 的情况如果不过滤这条数据会把整条曲线的纵轴拉到没法看。4.2 告警规则引擎与分级通知规则引擎是整个服务端最有价值的部分因为它的质量直接决定了你会不会被告警疲劳逼疯。我的设计原则是持续时间判定 分级 静默期。持续时间的逻辑很关键。假设温度上限是 28℃如果探头有一个瞬时跳变到 30℃你立刻告警那这种告警一天能来十几条最后你会把通知全关掉然后错过真正的故障。我的做法是连续 3 次采样也就是 90 秒都超标才触发告警。这样瞬时干扰被自然过滤掉了。下面是我实际用的规则配置用 YAML 描述方便修改rules: - code: temp_high field: temp op: threshold: 28.0 duration_samples: 3 level: critical message: 水温过高已持续 {duration} 秒 - code: temp_low field: temp op: threshold: 22.0 duration_samples: 6 level: warn message: 水温偏低检查加热棒 - code: ph_swing field: ph op: abs_delta_24h threshold: 0.8 level: warn message: pH 24 小时波动过大 - code: tds_rise field: tds op: delta_7d threshold: 60 level: info message: TDS 上升明显考虑部分换水 - code: offline field: online op: threshold: false duration_samples: 1 level: critical message: 设备离线分级的意义在于通知渠道分流。info 级别只写库和显示在看板上不推送warn 级别推一条但有 6 小时静默期同类告警 6 小时内不重复推critical 级别立刻推送并且如果 15 分钟内没有恢复再推一次最多推三次。这样既不会漏掉严重问题也不会被琐事轰炸。4.3 看板设计与手机端展示看板这块我的原则是一屏看懂。打开页面第一眼要能看到四个数字当前温度、pH、TDS、水位下面一条 24 小时温度曲线再下面是最近 10 条事件右上角一个设备在线状态点。别的都可以往后放。为什么温度曲线要放最显眼的位置因为它是最能反映设备是否正常工作的曲线。正常运行的缸温度曲线应该是一条围绕设定值的小幅波动线。如果看到曲线变成一条直线说明探头可能卡死了看到锯齿特别大说明加热功率不够或者水流循环不好看到整体缓慢下移说明加热棒在衰减。这条曲线就是你的心电图。技术实现上我用的是轻量图表库画折线数据通过 FastAPI 拉。这里有个性能小技巧不要把全量数据都返回给前端按时间范围做降采样。看 24 小时就返回 5 分钟一个点看 7 天就返回 1 小时一个点。这样前端渲染永远不卡传输量也小。手机端我没做原生 App就是用响应式布局让网页自适应省掉了打包和上架的麻烦。提示看板一定要加一个数据最后更新时间的显示。有次我的服务端进程静默退出了看板上还显示着历史曲线一切正常的样子实际上已经六个小时没数据了。自从加了时间戳显示这种问题一眼就能发现。5. 常见问题与排查技巧实录5.1 传感器读数漂移与跳变的排查思路问题一pH 读数慢慢往上飘一周涨了 0.5。这种情况我的排查顺序是先看电极有没有藻类或蛋白附着有就取出来用软毛刷蘸中性洗涤剂轻刷玻璃球泡再用去离子水冲干净然后重新做两点校准看斜率正常不正常如果斜率低于 50 mV/pH基本可以换电极了。还有一个容易被忽略的原因参比电极的 KCl 内液干涸了这种情况补液还能救回来。另外如果你的 pH 探头线跟水泵电源线平行走了很长一段那漂移很可能是干扰造成的把线分开走就好了。问题二温度读数偶尔跳到 85℃。这就是前面说的上电默认值问题属于转换未完成时读取。解决方式就是在代码里过滤掉等于 85 的值。如果读数频繁变成 -127那是线路问题重点查上拉电阻是否焊牢、线缆有没有被水汽腐蚀断。问题三TDS 读数越来越高但实际没换水也没加东西。先判断是不是水蒸发导致的浓缩——看液位是不是下降了如果液位降了而你没补水TDS 升高是正常的补水后就会回落。如果液位没变但 TDS 持续上升那可能是电极表面结垢了需要清洁。还有一个隐蔽原因是电极极化如果供电是直流的长时间浸泡必然电极化只能换交流激励的模块。5.2 网络掉线与设备重启的排查现象设备每天定时上下线一两次。这种情况八成是 WiFi 省电模式导致的。ESP32 默认开启了 WiFi 省电在不发送数据的时候会进入休眠某些路由器的空闲超时策略会直接把连接踢掉。解决办法是在代码里关掉省电WiFi.setSleep(false)。这个改动我实测下来连接稳定性提升非常明显。现象设备完全失联重启才好。这是固件的健壮性问题。我的做法是三步一是加看门狗硬件看门狗和软件任务看门狗都开主循环超过 30 秒没喂狗就重启二是加心跳失败计数连续三次上报失败就主动重连 MQTT连续五次就重启 WiFi十次都不行就整个重启三是记录重启原因到 status 主题方便事后分析是掉电重启还是看门狗重启。这里要提醒一个细节别用阻塞式的重连逻辑。我见过有人在重连里写while (!mqtt.connected()) { mqtt.connect(); delay(1000); }这一写重连期间整个设备的温控逻辑全停摆如果正好赶上降温鱼就遭殃了。正确做法是把重连做成状态机里的一步每次主循环试一次不成功就先干别的。5.3 自动投喂翻车与机械故障处理自动投喂是最容易出事故的环节我把遇到过的问题都列出来。问题喂食器一次掉太多料。原因通常是饲料结块或者出料口有残留。解决方式是每次投喂后让电机反转一点点把卡在出料口的料抖掉。另外饲料一定要干燥密封存放受潮结块是万恶之源。问题电机转了但没料出来。这是堵料。检测方法很简单在出料口加一个红外对射投喂后 3 秒内如果没检测到料落下就判定为堵料立刻告警并停止后续投喂。这个成本不到十块钱但能救命。问题投喂时间和换水时间撞在一起。换水时水位下降如果这时候投喂饲料全被冲进过滤棉里。所以逻辑上要加互斥正在换水或者水位异常时禁止投喂。问题设备重启后重复投喂。这是状态保存的问题。投喂次数必须持久化到 Flash比如用 Preferences 库重启后读回来不能只存在内存里。我就吃过这个亏一个晚上重启了三次鱼被喂了三次。下面这张速查表是我整理出来的遇到问题可以先按这个顺序过一遍。现象最可能的原因快速验证方法处理方式温度恒定不变探头卡死或断线用手捏住探头看读数是否变化检查接线更换探头pH 缓慢单向漂移电极污染或老化做两点校准看斜率清洁、补液或更换TDS 持续上升蒸发浓缩或电极结垢对比液位变化补水或清洁电极设备定时掉线WiFi 省电被踢关闭省电模式观察 24 小时WiFi.setSleep(false)设备失联需重启固件死循环或内存泄漏查看重启原因上报开看门狗加分级重连投喂量忽多忽少饲料受潮或堵料称重标定 10 次取平均反转抖动加堵料检测重复投喂次数未持久化手动重启设备观察写入 Flash 保存温度波动过大加热功率不足或水流差对比加热棒功率计算值提高功率或加造浪藻类爆发光照时长超标查看补光灯定时设置降到 8 到 10 小时告警频繁误报缺少持续时间判定查看告警触发时间点分布加连续采样次数门槛5.4 几条用钱堆出来的经验最后分享几条我觉得最值钱的经验都是真金白银换来的。第一先跑通一款探头再加第二款。我最早想一次性把温度、pH、TDS 全接上结果三个探头互相干扰排查了两周都没定位到问题。后来拆开一个个上才发现是模拟部分共地的问题。一次只引入一个变量这是所有调试的基本功。第二所有阈值都写成配置不写死在代码里。鱼是活的季节是变的夏天和冬天的温度阈值能差 4℃。写死了就意味着每次调整都要重新烧固件你迟早会因为懒而不去调然后鱼就替你承受后果。第三给系统留一个全部暂停的物理按钮。软件再稳也有你想立刻停掉一切的时候——比如发现投喂器出问题、比如要大规模换水。一个简单的自锁按钮切断所有执行器的电源比在手机上点半天方便得多。这个按钮我装了之后用过三次每次都庆幸装了它。注意自动化的边界要清楚。MiroFish 能帮你控温、投喂、看数据但它替代不了换水和观察。换水是有规律的体力活观察鱼的行为是无价的。系统最好的价值是把你从忘记和漏看里解放出来而不是让你彻底甩手。我自己每周还是会亲手检查一遍过滤棉、看一遍鱼的状态这套设备让我省心但没让我变懒。我个人的体会是这个项目真正难的地方从来不是写代码或者接线而是想清楚哪些事情可以交给机器、哪些必须留给人。温度、投喂、水位这些有明确数值边界的交给机器很合适水质突变、鱼的行为异常、设备老化这些需要判断力的还是得靠人定期看。你要是也打算做一套类似的东西我的建议是先从温度监测 自动投喂这两个最小功能起步跑稳一个月再往上加 pH 和电导率。加的功能越少你就越有可能把它长期用下去而长期稳定运行才是一套监测系统唯一真正的价值。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门