PetBoxX 育雏系统:温湿度分阶段管理与自动喷雾设计
记录 PetBoxX ESP32-S3 固件中喷雾育雏模块的完整设计思路,方便日后查阅。
涉及两个相互独立但协同工作的子系统:湿度区间状态机(humidity_ctrl)和水位自动补水任务(mist_autofill)。
整体架构
育雏期的湿度管理分两层,职责完全分离:
| 模块 | 职责 | 控制的硬件 |
|---|---|---|
humidity_ctrl | 按湿度区间决定什么时候喷雾 | 雾化器 PWM + 供水泵 |
mist_autofill | 检测雾化器工作状态,按时向水箱补水 | 仅供水泵 |
两者共用同一个供水泵(GPIO39 PWM),但时机不同,不会冲突:
humidity_ctrl喷雾时,泵和雾化器同时开启,为雾化器供水;mist_autofill补水时,雾化器不工作,泵单独运行,往水箱里加水。
一、湿度区间控制(humidity_ctrl)
设计思路
传统单点目标控制("低于目标就开泵")在湿度传感器有轻微抖动时会导致频繁启停。区间滞回控制(Hysteresis)能解决这个问题:
- 低于下限(humi_low)才启动:留有缓冲,不会在目标附近抖动
- 高于上限(humi_high)才停止:喷够了再停,不会刚启动就关
- 中间区间维持当前状态:既不触发新喷雾,也不打断正在进行的喷雾
状态机
humi < low && temp_ok
IDLE ──────────────────────────→ MISTING
↑ │
│ cooldown 结束 │ humi >= high
│ │ 或超时 max_on_s
└──── COOLDOWN ←──────────────────┘
等待 cooldown_s 秒
三个状态:
- IDLE:等待启动条件(湿度低于下限 + 温度允许)
- MISTING:喷雾中,雾化器和供水泵同时运行
- COOLDOWN:喷完后等待冷却,防止反复触发
温度保护
育雏箱升温阶段不应喷雾——低温加湿会让雏鸟受凉。设置 temp_guard 参数:
当 temp < setpoint - temp_guard 时,禁止启动喷雾
默认 0.5°C,即温度还差 0.5°C 没到目标时,不允许喷雾。
硬件动作
// 喷雾启动:泵先开,雾化器随后
static void mist_on(void) {
set_pump_pwm_output_percent(pump_pct); // 供水
set_mist_pwm_output_percent(mist_pct); // 雾化
}
// 喷雾停止:两者同时关
static void mist_off(void) {
set_mist_pwm_output_percent(0);
set_pump_pwm_output_percent(0);
}
PWM 占空比使用用户标定的 PUMP_CAL / MIST_CAL 值,未标定时默认 100%。
可配置参数
| 参数 | 默认值 | 说明 |
|---|---|---|
| humi_low | 55% | 启动阈值 |
| humi_high | 65% | 停止阈值 |
| temp_guard | 0.5°C | 温度保护阈值 |
| max_on_s | 5s | 单次最长喷雾时间(超时强制停止) |
| cooldown_s | 90s | 喷雾结束后的冷却等待时间 |
所有参数持久化到 NVS,重启后自动恢复。
UART 命令(屏→ESP32)
HUMI_TARGET=600 # 目标 60%,自动构建 ±5% 区间 [55, 65]
HUMI_BAND=550,650 # 直接设置上下限(×10 整数避免浮点)
HUMI_GUARD=5 # 温度保护 0.5°C
HUMI_TIMING=5,90 # 喷雾最长 5s,冷却 90s
HUMI_STAGE=1 # 快速应用阶段预设
阶段预设:
| Stage | 目标温度 | 区间 | 适用期 |
|---|---|---|---|
| 1 | 35.5~36.5°C(预设 36.0°C) | 55%~65% | 刚出壳~绒毛干燥初期 |
| 2 | 34.0~35.0°C(预设 34.5°C) | 50%~60% | 绒毛稳定、开始明显进食后 |
| 3 | 32.0~34.0°C(预设 33.0°C) | 45%~55% | 针羽/羽毛逐渐生长阶段 |
ESP32 → 屏推送
每次参数变更后主动推送到串口屏:
hc.low_x10 # 下限 ×10
hc.high_x10 # 上限 ×10
hc.guard_x10 # 温度保护 ×10
hc.max_on # 最长喷雾秒数
hc.cooldown # 冷却等待秒数
hc.state # 0=IDLE 1=MISTING 2=COOLDOWN
二、水位自动补水(mist_autofill)
设计思路
雾化器消耗水,水箱水位会持续下降。mist_autofill 的职责是定期往水箱里补水,与喷雾控制完全解耦。
它通过 XKT201 超声波雾化模块的工作状态引脚(GPIO21)判断雾化器当前是否在工作——XKT201 工作时该引脚会产生高频边沿跳变。
XKT201 工作状态检测
ISR(任意边沿中断)→ 边沿计数
↓ 每 100ms 采样一次
100ms 内边沿数 ≥ 2 → working = true
300ms 内有边沿 → working = true(recent edge 保持)
否则 → working = false
这个检测是补水逻辑的输入:
- working = true:雾化器在工作,水正在消耗,但现在不补(避免干扰喷雾)
- working = false 且需要加湿:雾化器停了,可以开泵补水
补水流程
条件满足?
需要加湿(当前 RH < 目标 RH)
&& 雾化器当前不工作(XKT201 边沿静默)
&& 不在冷却期(上次补水已过 interval_min)
&& 不在锁定期
↓
开泵运行 duration_s 秒
↓
进入冷却期 interval_min 分钟
↓
冷却结束 → 检查本次补水是否有效(RH 变化 ≥ 1%)
├─ 有效 → 正常继续
└─ 无效 → 失败计数 +1
失败 ≥ 3 次 → 进入锁定(300s)
锁定期间 XKT201 恢复工作 → 自动解锁
失败检测与锁定
每次补水后记录当时的 RH 值,下次补水前比较:
if (RH_now - RH_after_last_refill) < 1.0%:
refill_failures++
if failures >= 3:
lockout = true # 停止补水 300s
这能检测"水箱已空、管路堵塞、泵失效"等异常:水泵转了但湿度没变化,就认为补水失败。
锁定自动解除条件:
- 300s 后自动过期
- XKT201 边沿重新出现(说明雾化器恢复工作)
- 手动发送
MIST_REFILL_NOW命令强制解锁
可配置参数
| 参数 | 默认值 | 范围 |
|---|---|---|
| refill_interval_min | 15 分钟 | 1~180 分钟 |
| refill_duration_s | 6 秒 | 1~60 秒 |
通过 UART 命令 PUMP_CAL=<pct>,<interval_min>,<duration_s> 一次性配置。
三、两系统的协作关系
SHT30 传感器(~1s 采样)
│
├── humidity_ctrl_tick(humi, temp, setpoint)
│ │
│ ├── IDLE: humi < low && temp_ok → mist_on()
│ │ └── pump ON + mist ON(喷雾供水)
│ │
│ └── MISTING: humi >= high || timeout || temp_fail → mist_off()
│ └── pump OFF + mist OFF
│
└── mist_autofill(独立任务,1s tick)
│
├── XKT201 working? → 雾化器在工作,跳过补水
└── NOT working && need_humi && cooldown done
└── pump ON(仅泵,雾化器关)→ 补水 duration_s
两者通过硬件信号松耦合:
humidity_ctrl直接控制硬件输出(泵 + 雾化器)mist_autofill通过检测 XKT201 硬件引脚边沿感知喷雾状态,而不是读软件标志
这意味着即使软件状态出现异常,补水逻辑仍然能正确判断硬件实际状态,不会在喷雾时同时补水。
四、喷雾触发场景举例
以下结合实际运行日志,说明各种情况下系统的行为。
参数设定:low=55% high=65% temp_guard=0.5°C max_on_s=5s cooldown_s=90s
场景一:湿度在区间内 → 维持不动
当前湿度 63.5%,区间 [55%, 65%]
55% ────────[63.5%]──── 65%
↑
当前湿度,在区间内
状态:IDLE,不喷雾
原因:湿度未跌破下限 55%,无需干预
状态机维持 IDLE,等待湿度自然下降至 55% 以下才会启动。这是育雏箱正常运行时最常见的状态。
场景二:升温阶段,温度保护锁定
目标 37.5°C,当前 25.8°C,temp_guard = 0.5°C
实际日志:
SP=37.50 T=25.80 err=11.702°C
判断:25.80 < 37.50 - 0.5 → 25.80 < 37.00 ✓ 触发温度保护
结果:即使湿度低于 55%,也禁止喷雾
设计意图:箱子从室温(约 2326°C)加热到目标(3738°C)需要较长时间。升温期间喷雾会让冷湿气加剧散热、延缓升温,还可能让刚出壳的雏鸟受凉。temp_guard = 0.5°C 确保只有温度已经稳定在目标附近才允许喷雾。
实际效果:目标 37.5°C 的育雏箱,须等温度升至 ≥ 37.0°C 后,喷雾才会解锁。
场景三:正常喷雾触发
湿度跌至 54%,温度 37.2°C(已达到目标区间)
判断链:
54% < low(55%) ✓ 湿度低于下限
37.2 >= 37.5 - 0.5 ✓ 温度允许(≥ 37.0°C)
状态 = IDLE ✓ 不在冷却期
→ 进入 MISTING:泵 ON + 雾化器 ON
喷雾过程:
t=0s 湿度 54%,开始喷雾
t=3s 湿度升至 65%(high)→ 触发停止
→ 进入 COOLDOWN,等待 90s
→ 90s 后回到 IDLE
若湿度上升较慢(大空间或雾化量不足),单次喷雾最长 5 秒(max_on_s)后强制停止,进入冷却,防止长时间干烧。
场景四:冷却期内,禁止再次触发
上次喷雾刚结束,cooldown_s = 90s
t=0s 喷雾结束 → 进入 COOLDOWN
t=20s 湿度再次跌至 53%(传感器滞后或空间大)
→ 仍在冷却期,不喷雾
t=90s 冷却结束 → 回到 IDLE
t=91s 检测到湿度 53% < 55% → 重新触发喷雾
设计意图:雾气从雾化器扩散到传感器有时间延迟,刚停喷时传感器读数可能还没反映真实湿度。冷却期保护避免"喷了其实够了,但传感器还没读到,又重新触发"的连续启停。
场景五:三阶段预设的实际应用
育雏不同时期对湿度和温度要求不同,前端提供四个快捷按钮一键切换全套参数:
| 按钮 | 目标温度 | low | high | 喷雾时长 | 冷却等待 | 适用期 |
|---|---|---|---|---|---|---|
| 默认 | 不变 | 目标湿度−5% | 目标湿度+5% | 5s | 90s | 以当前目标湿度为中心,自动生成 ±5% 区间,不改温度 |
| 阶段1 | 36.0°C(范围 35.5~36.5) | 55% | 65% | 5s | 90s | 刚出壳~绒毛干燥初期 |
| 阶段2 | 34.5°C(范围 34.0~35.0) | 50% | 60% | 5s | 90s | 绒毛稳定、开始明显进食后 |
| 阶段3 | 33.0°C(范围 32.0~34.0) | 45% | 55% | 5s | 90s | 针羽/羽毛逐渐生长阶段 |
点击阶段1~3 会将目标温度和湿度区间一并填入对应输入框,再分别点击"目标温度下发"和"下发并保存湿度区间参数"完成设置;点击"默认"只重置湿度参数,温度保持不变。
各阶段设计原因(参考 Brinsea、NP Vet、Tony Silva 等人工育雏资料):
- 阶段1(35.5
36.5°C,5565%):刚出壳雏鸟体温调节能力弱,需要较高温度和适当湿度,防止脱水和失温。不建议长期维持在 37.5°C 孵化温度,否则容易引发热应激、张嘴喘、脱水加快等问题。 - 阶段2(34.0
35.0°C,5060%):雏鸟绒毛逐渐干燥,进食和状态趋于稳定,体温调节能力开始增强,可适度降温降湿,同时减少喷雾对箱内温度控制的干扰。 - 阶段3(32.0
34.0°C,4555%):针羽或羽毛逐渐生长,保温能力进一步提升,可继续降温降湿,促进正常发育。湿度不宜低于 40%(避免皮肤干裂脱水),也不宜长期高湿(避免垫料潮湿、细菌滋生、呼吸道负担增加)。
切换示例:绒毛干燥后,从阶段1切换到阶段2:
切换前:目标温度 36.0°C,low=55% high=65%
操作:前端点击"阶段2"→ 参数填入 34.5°C / [50%, 60%]
分别点击温度下发 + 湿度区间下发
若切换时当前湿度是 62%:
62% 在新区间 [50%, 60%] 之外(高于 high=60%)
→ 系统等湿度自然降至 60% 以下后进入"在区间内"状态
→ 继续降至 50% 以下才触发喷雾
每次切换后参数立即写入 NVS,重启后继续生效。
四种场景对比
| 场景 | 湿度条件 | 温度条件 | 状态 | 结果 |
|---|---|---|---|---|
| 在区间内 | 55% ≤ RH ≤ 65% | 任意 | IDLE | 不喷 |
| 温度未达 | RH < 55% | T < 37.0°C | IDLE | 不喷(温度锁定) |
| 正常触发 | RH < 55% | T ≥ 37.0°C | IDLE | 喷雾,最长 5s |
| 冷却期 | RH < 55% | T ≥ 37.0°C | COOLDOWN | 不喷,等满 90s |
五、温度保护(temp_guard)该设多小?
状态机防护原理
喷雾不会"连续触发",因为状态机强制隔离了每次喷雾:
IDLE → (湿度 < low ∧ 温度达标) → MISTING(最长 max_on_s)
MISTING → (时间到 or 湿度 ≥ high) → COOLDOWN(固定 cooldown_s)
COOLDOWN → 时间到 → IDLE
即使 temp_guard = 0.1°C,单次喷雾结束后也必须等完整的冷却周期(默认 90s)才能再次判断。
典型干扰周期示例(max_on_s=5s, cooldown_s=90s):
t=0s IDLE → 触发喷雾(湿度低 ∧ 温度达标)
t=5s 喷雾停止 → 进入 COOLDOWN
t=5s~ 喷雾散热 → 传感器温度可能短暂下降 0.1~0.3°C
t=95s COOLDOWN 结束 → 回到 IDLE
t=96s 重新检测:若温度已恢复,且湿度仍低 → 再次喷雾
若温度还没恢复 → temp_guard 拦截,继续等
SHT30 精度限制
SHT30 温度精度 ±0.2°C(典型值 ±0.1°C)。设 temp_guard < 0.2°C 时,传感器噪声本身就可能超过阈值——即温度实际已经达标,但读数在 setpoint−0.1 和 setpoint+0.1 之间随机跳动,导致 temp_guard 时而拦截、时而放行,行为不稳定。
推荐设置
| temp_guard | 效果 | 适用场景 |
|---|---|---|
| 0.5°C | 温度需稳定在目标 ±0.5°C 内才允许喷雾 | 默认推荐,兼顾保护和响应速度 |
| 0.3°C | 更宽松,温度基本稳定就允许 | 温控已调优、需要更快湿度响应 |
| 0.2°C | 接近传感器噪声下限,偶尔抖动 | 极限值,不推荐日常使用 |
| < 0.1°C | 与"不设保护"无实质区别 | 不推荐 |
结论:temp_guard = 0.5°C 是合理默认值。设为 0.1~0.2°C 不会造成"连续喷雾"(状态机保护),但传感器噪声会让保护逻辑抖动,失去实际意义。除非明确需要极低阈值,否则保持默认即可。
小结
| 特性 | humidity_ctrl | mist_autofill |
|---|---|---|
| 触发源 | 湿度传感器(SHT30) | 时间间隔 + 湿度需求 |
| 控制目标 | 雾化器 + 供水泵(喷雾时) | 仅供水泵(补水时) |
| 状态判断 | 湿度区间滞回 | XKT201 硬件信号 |
| 保护机制 | 温度保护 + 单次超时 | 失败计数 + 自动锁定 |
| 参数持久化 | NVS | NVS |
| 独立性 | 完全解耦,互不调用 | ← 同左 |