用C语言实现智能加湿器的传感器编程:精准控制环境湿度的闭环逻辑
智能加湿器“精准控湿”不是读个数再开关继电器就完事。DHT11这类单总线传感器受时序和温漂影响大,量产更稳的是SHT30一类I2C数字温湿度传感器 。软件层真正要做的是:定时采样→滤波→校验丢弃→滞回判定→驱动雾化片,别让主循环每毫秒读一次、别让继电器抖着起停。
一、传感器读取:I2C单次采样
SHT30默认地址0x44,发单次测量命令0x2400(时钟不拉伸),等十几毫秒读6字节:温度MSB/LSB/CRC、湿度MSB/LSB/CRC 。
#define SHT30_ADDR 0x44
#define CMD_MEAS_H 0x24
#define CMD_MEAS_L 0x00
int sht30_read(float *t, float *h){
uint8_t cmd[2]={CMD_MEAS_H,CMD_MEAS_L};
i2c_write(SHT30_ADDR, cmd, 2);
delay_ms(20);
uint8_t buf[6];
if(i2c_read(SHT30_ADDR, buf, 6)!=0) return -1;
uint16_t raw_t = (buf[0]<<8)|buf[1];
uint16_t raw_h = (buf[3]<<8)|buf[4];
*t = -45.0f + 175.0f*raw_t/65535.0f;
*h = 100.0f*raw_h/65535.0f;
return 0;
}
工程上别每帧都读,2~5s一次足够,房间湿度本来就是慢变量;读失败直接丢弃这次数据,不拿旧值凑数 。
二、采样滤波:IIR+突变丢弃
原始湿度会跳,尤其开门、喷雾回流、传感器靠近出雾口。先过一阶IIR,再滤掉异常跳变:
static float hum_filt;
float humidity_filter(float raw){
if(raw<0||raw>100) return hum_filt; // 量程外丢弃
float f = hum_filt + (raw-hum_filt)*0.3f;
if(fabsf(f-hum_filt) > 8.0f) // 单帧跳8%当干扰
return hum_filt;
hum_filt = f;
return f;
}
系数0.2~0.3够用,别拉到0.5以上,否则喷雾瞬间会把设定值“假饱和”。
三、闭环控制:滞回优先,别上来就PID
家用加湿器目标一般是“50%±5%”,不是“恒50.0%”。滞回控制最合适:低于下限开、高于上限关,中间保持原状态 。
#define HUM_TARGET 50
#define HUM_HYST 3
static uint8_t mist_on;
void humidity_control(float h){
if(h < HUM_TARGET-HUM_HYST){
mist_on = 1;
}else if(h > HUM_TARGET+HUM_HYST){
mist_on = 0;
}
mist_drive(mist_on);
}
回差别设太小。目标50只设47开/50关,继电器一天跳几千次,雾化片触点都扛不住 。
需要连续调雾量(机房恒湿、实验室)才上增量PID,位置式容易一开机推满:
float pid_inc(float err, float err_last){
static float out=0;
float d = err-err_last;
out += KP*d + KI*err; // 简化增量
if(out>100)out=100; if(out<0)out=0;
return out;
}
KI要按控制周期缩放,2s周期和200ms周期的Ki不是同一个数 。
四、驱动雾化片:PWM而非硬开关
超声雾化片别用继电器直接通断,寿命和噪声都差。MCU输出PWM→三极管/MOS推挽→雾化片谐振驱动 。
void mist_drive(uint8_t on){
if(on) pwm_set_duty(MIST_TIM, MIST_CH, 70); // 70%占空比
else pwm_set_duty(MIST_TIM, MIST_CH, 0);
}
睡眠档可降到30%~40%占空比减噪;连续PID场景就把pid_out映射到占空比。
五、安全保护:缺水优先于控湿
闭环再准,无水干烧一次雾化片就废。水位探针/干簧管进GPIO,任何控制输出前先判水位 :
void mist_drive(uint8_t on){
if(!water_present()){ // 无水强制断
pwm_set_duty(MIST_TIM, MIST_CH, 0);
buzzer_alarm();
return;
}
pwm_set_duty(MIST_TIM, MIST_CH, on?70:0);
}
附加兜底:
传感器连续N次读失败→停止加湿,报传感器故障
湿度长时间不升(比如10分钟没变化)→疑似水位/堵塞,停机
开机软启动,占空比从0渐升到目标,别瞬间满功率
出雾口回流导致传感器假高→传感器远离雾道,走风管采样更稳
六、任务节奏(裸机/RTOS)
2s -> SHT30读一次
2s -> IIR滤波+量程检查
2s -> 滞回判定
1s -> 水位/保护检查
中断 -> 按键改目标湿度
别把i2c读和控制在同一个紧循环里死等,HAL_delay类阻塞会卡主循环。
七、落坑清单
主循环每10ms读SHT30→I2C拥塞、传感器自热
不用CRC/校验→偶尔255或0照常控
单帧跳变直接采用→误关误开
回差设0.5%→继电器啸叫
继电器硬切雾化片→触点粘连
传感器放出雾口正上方→读数虚高
缺水不拦截→干烧裂片
PID位置式直出→开机满占空
读失败拿上一次值硬凑→越控越偏
八、落地顺序
I2C接SHT30,2s采一次
原始值做IIR+跳变丢弃
滞回控制输出on/off
PWM驱MOS而不是继电器
水位探针优先于湿度逻辑
读失败计数→故障停机
睡眠档降占空比
要恒湿±2%再换增量PID
加湿器闭环的本质不是算法多高级,是“采样别抖、判定别急、执行别硬切”。家用场景滞回控制够稳,PID是按需选项不是标配;传感器读数干净了,回差给够,雾化片用PWM软驱,基本不会出现“一直加湿到60%还不停”或“继电器咔咔响”。真正量产翻车大多不在控制逻辑,在缺水保护和传感器位置——这两处漏了,闭环再漂亮也白搭。





