零知派——STM32智能小车系列教程(五):DHT11温湿度、火焰传感器与风扇灭火模块原理及调试
引言:智能小车要实现的功能很多——避障、跟随、贴边、灭火、直线行驶,每一个模块单独调试起来都有自己的难点。如果一开始就把所有模块塞进一个工程里联调,出了问题很难分清到底是传感器硬件的问题、电平极性的问题,还是上层控制算法的问题
所以这一系列教程的思路是:先把每个功能模块单独拎出来,配一个不依赖其他硬件的最小测试程序,把模块本身调通调透,再回头看它在完整项目里是怎么被使用的
目录
一、引言
前面四篇讲的都是“运动控制”相关的模块——循迹让小车知道“路在哪”、超声波让小车知道“障碍在哪”、红外让小车知道“目标在哪”、编码器让小车知道“跑多快”。这一篇转向“环境感知与执行”——检测环境温度湿度、检测火焰、控制风扇灭火
灭火模式在整个项目中的定位与其他模式完全不同:
| 特性 | 其他模式(循迹/避障/跟随等) | 灭火模式 |
|---|---|---|
| 控制权 | 完全由代码自动控制 | 手动遥控为主,代码自动干预为辅 |
| 触发方式 | 进入模式即开始自动运行 | 由用户遥控驾驶,检测到火焰时自动接管 |
| 退出方式 | 切换模式退出 | 火焰熄灭3秒后自动归还控制权 |
| 用户交互 | 不需要 | 用户随时可用方向键控制移动 |
这种“用户驾驶 + 自动灭火”的混合设计思路——小车平时由用户遥控行驶,只有检测到火焰时才自动干预执行灭火动作,灭火完成后立即归还控制权——是嵌入式项目中“人机协同”的一个典型实例
这个系列教程拆成以下几篇:
| 篇号 | 主题 | 核心硬件 |
|---|---|---|
| 第一篇(已完成) | 五路循迹模块 | TCRT5000 + LM393/LM339 |
| 第二篇(已完成) | HC-SR04超声波+SG90舵机云台 | HC-SR04 + SG90 |
| 第三篇(已完成) | 红外双目跟随模块 | 红外避障对管 |
| 第四篇(已完成) | 红外对射式测速+直线行驶PID闭环 | 槛型红外计数模块+码盘+PID |
| 第五篇(本文) | DHT11温湿度、火焰传感器、风扇灭火 | DHT11+火焰传感器+风扇 |
| 第六篇(待更新) | 蓝牙/串口双通道通信协议 | HC-05/BT06 |
| 终篇(待更新) | 整车项目架构总览 | 全模块整合 |
二、模块硬件原理
本篇涉及三个独立硬件:DHT11负责测环境温湿度(本项目里主要作为状态展示,不直接参与灭火决策)、红外火焰传感器负责判断"有没有火",风扇负责执行"吹灭"这个物理动作
| 模块 | 核心元件 | 输出类型 |
|---|---|---|
| DHT11 | 电阻式湿敏元件+NTC温敏元件+8位单片机 | 单总线数字信号,40bit一次完整数据 |
| 红外火焰传感器 | 红外接收管(对760~1100nm波段敏感)+LM393比较器 | 数字开关量,检测到火焰输出低电平 |
| 风扇 | 直流电机+扇叶 | 单路开关控制(本项目未做调速) |
三者各自独立工作,靠代码逻辑串联成一套完整流程
2.1 DHT11单总线通信协议
2.1.1 完整通信时序
DHT11采用单总线(Single-bus)通信,一根数据线既传输命令也传输数据,靠严格的时序约定区分不同信号
起始信号
主机(开发板)接入数据线的I/O输出低电平,且低电平保持时间不能小于 18ms

响应信号
等待模块的响应信号到来。将数据线改为输入模式,如果接入到低电平,说明接收到模块的响应

数据传输
主机接收模块发送的40位数据,其中,位数据 ‘0’ 表示54us的低电平,27us的高电平;位数据 ‘1’ 表示54us的低电平,74us的高电平

两个格式的分辨主要是高电平的输出时长不同
结束信号
模块的数据线输出 40 位数据后,是以低电平结束,它会继续输出低电平 54 微秒后转为输入状态,主机需要转为输出状态,输出高电平释放总线

2.1.2 数据格式
完整的一次数据传输是40位,格式是8位湿度整数数据+8位湿度小数数据+8位温度整数数据+8位温度小数数据+8位校验位
| 0011 0101 | 0000 0000 | 0001 1000 | 0000 0100 | 0101 0001 |
|---|---|---|---|---|
| 湿度高8位 | 湿度低8位 | 温度高8位 | 温度低8位 | 校验位 |
校验和计算: 0011 0101 + 0000 0000 + 0001 1000 + 0000 0100 = 0101 0001,与接收的数据一致
数据时序图

高位先发送,对DHT11来说湿度小数字节和温度小数字节固定为0,校验位是前四个字节相加后取低8位
2.2 红外火焰传感器原理
2.2.1 火焰传感器工作原理
火焰燃烧时会释放特定波长的红外线,火焰传感器中的红外光电三极管接收到红外辐射后,其集电极电流随光强增加而增大,通过 LM393 比较器与预设阈值比较后输出数字电平
与循迹模块 TCRT5000 的红外检测有何不同:
| 对比项 | TCRT5000 循迹模块 | 火焰传感器 |
|---|---|---|
| 检测对象 | 反射回来的红外光 | 主动发射的红外光 |
| 检测原理 | 发射→反射→接收 | 接收环境中的红外辐射 |
| 检测距离 | 1~2.5cm | 0.3~3m |
| 检测角度 | 窄(约 15°) | 宽(约 60°) |
| 核心用途 | 近距离检测地面颜色 | 远距离检测火源 |
区别:TCRT5000 需要主动发射红外光然后接收反射光,而火焰传感器只接收不发射——它被动接收火焰燃烧产生的红外辐射。这个区别决定了火焰传感器的检测距离远(可达数米),但也会被阳光、热源等其他红外辐射源干扰
2.2.2 灵敏度调节
模块上那颗蓝色方形可调电位器(3296 型)用于调节火焰检测的阈值电压,从而改变检测灵敏度
| 调节方向 | 阈值变化 | 检测效果 |
|---|---|---|
| 顺时针旋转 | 参考电压降低 | 更灵敏(检测更远火焰,但易误触发) |
| 逆时针旋转 | 参考电压升高 | 更不灵敏(只检测强火焰,抗干扰强) |
实际调试方法
- 将火源放在传感器正前方约 30cm 处
- 慢慢旋转电位器,观察模块上的状态指示灯
- 找到指示灯在“有火源”和“无火源”之间刚好切换的临界点
- 再往灵敏方向回调一点(顺时针约 1/8 圈)
三、硬件接线说明
3.1 引脚定义
本项目三个模块的引脚定义(与pinsdefine.h一致):
#define FAN_PIN 22 // 风扇(装在车身左中部)
#define FLAME_PIN 23 // 火焰传感器(装在车尾左侧)
#define DHT11_PIN 27 // DHT11 单总线数据脚
3.2 接线方案表
| 模块 | 引脚 | 零知派迷你板引脚 | 说明 |
|---|---|---|---|
| DHT11 | VCC | 5V | 供电3~5.5V |
| DHT11 | GND | GND | 共地 |
| DHT11 | DATA | D27 | 单总线数据脚,模块内部已带上拉电阻 |
| 火焰传感器 | VCC | 3.3V或5V | — |
| 火焰传感器 | GND | GND | 共地 |
| 火焰传感器 | DO | D23 | 检测到火焰输出低电平 |
| 风扇模块 | VCC | 5V/独立电源 | 风扇启动瞬间电流较大,建议独立供电 |
| 风扇模块 | GND | GND | 共地 |
| 风扇模块 | IN | D22 | 高电平开启 |
3.3 连接示意图
风扇启动瞬间电流较大,建议单独供电或确保 5V 电源有足够的余量(≥1A),避免影响主控芯片工作

四、独立测试程序
在整合到整车项目之前,先用下面这个程序单独验证火焰传感器和风扇的功能:
/**************************************************************************************
* 文件: Fire_Extinguish_Standalone_Test.ino
* 作者:零知实验室(深圳市在芯间科技有限公司)
* 功能:火焰传感器 + 风扇灭火 独立测试程序
* 不依赖其他硬件,用于验证火焰检测灵敏度和风扇控制
*
* 引脚(与主项目 pinsdefine.h 完全一致):
* FLAME_PIN = D23
* FAN_PIN = D22
*
* 串口命令(波特率115200):
* f 手动开关风扇(切换状态)
* s 打印一次温湿度(DHT11)
* t 火焰检测灵敏度测试模式(连续监测)
* h 打印帮助信息
*
* 使用方法:
* 1. 烧录本程序到零知派迷你板
* 2. 打开串口监视器,波特率115200
* 3. 用打火机(不点燃)或手机闪光灯作为火源
* 4. 在不同距离测试火焰检测的灵敏度
**************************************************************************************/
#include <SimpleDHT.h>
#define FLAME_PIN 23
#define FAN_PIN 22
#define DHT11_PIN 27
SimpleDHT11 dht11(DHT11_PIN);
// ── 状态变量 ──────────────────────────────────────────────────────
bool fanState = false; // false=关, true=开
bool testMode = false; // false=命令模式, true=持续监测模式
unsigned long lastFlameDetectedMs = 0;
// ── 串口命令处理 ──────────────────────────────────────────────────
void printHelp()
{
Serial.println(F("========================================"));
Serial.println(F(" 火焰传感器 + 风扇 独立测试程序"));
Serial.println(F("========================================"));
Serial.println(F(" 命令:"));
Serial.println(F(" f 手动开关风扇"));
Serial.println(F(" s 读取并打印温湿度"));
Serial.println(F(" t 进入/退出持续监测模式"));
Serial.println(F(" h 打印本帮助信息"));
Serial.println(F(""));
Serial.println(F(" 当前状态:"));
Serial.print(F(" 火焰检测: "));
Serial.println(digitalRead(FLAME_PIN) == LOW ? "有火焰 (LOW)" : "无火焰 (HIGH)");
Serial.print(F(" 风扇状态: "));
Serial.println(fanState ? "ON" : "OFF");
Serial.println(F("========================================"));
}
void handleCommand(String cmd)
{
cmd.trim();
if (cmd.length() == 0) return;
if (cmd == "f") {
fanState = !fanState;
digitalWrite(FAN_PIN, fanState ? HIGH : LOW);
Serial.print(F("[FAN] "));
Serial.println(fanState ? "ON" : "OFF");
return;
}
if (cmd == "s") {
byte temp = 0, humi = 0;
int err = dht11.read(&temp, &humi, nullptr);
if (err != SimpleDHTErrSuccess) {
Serial.print(F("[DHT] 读取失败 err="));
Serial.println(err);
if (err == SimpleDHTErrStartLow) {
Serial.println(F("[DHT] 提示:DHT11 需要至少 1s 采样间隔"));
}
} else {
Serial.print(F("[DHT] 温度="));
Serial.print((int)temp);
Serial.print(F("°C 湿度="));
Serial.print((int)humi);
Serial.println(F("%RH"));
}
return;
}
if (cmd == "t") {
testMode = !testMode;
Serial.print(F("[TEST] "));
Serial.println(testMode ? "持续监测模式 ON" : "持续监测模式 OFF");
return;
}
if (cmd == "h") {
printHelp();
return;
}
Serial.print(F("[WARN] 未知命令: "));
Serial.println(cmd);
}
// ── 火焰检测封装 ──────────────────────────────────────────────────
bool flameDetected()
{
return (digitalRead(FLAME_PIN) == LOW);
}
// ── setup ─────────────────────────────────────────────────────────
void setup()
{
Serial.begin(115200);
delay(200);
pinMode(FLAME_PIN, INPUT_PULLUP);
pinMode(FAN_PIN, OUTPUT);
digitalWrite(FAN_PIN, LOW);
Serial.println(F("========================================"));
Serial.println(F(" 火焰传感器 + 风扇 独立测试程序"));
Serial.println(F(" 波特率:115200"));
Serial.println(F(" 命令:f(开关风扇) s(读温湿度) t(监测模式) h(帮助)"));
Serial.println(F("========================================"));
Serial.println(F("提示:火焰传感器检测到火焰时 DO 输出 LOW"));
Serial.println(F(""));
}
// ── loop ──────────────────────────────────────────────────────────
void loop()
{
// ── 串口命令 ──────────────────────────────────────────────────
while (Serial.available()) {
String cmd = Serial.readStringUntil('\n');
handleCommand(cmd);
}
// ── 持续监测模式 ─────────────────────────────────────────────
if (testMode) {
bool flame = flameDetected();
static bool lastFlame = false;
if (flame != lastFlame) {
lastFlame = flame;
Serial.print(F("[FLAME] "));
Serial.println(flame ? "检测到火焰!" : "无火焰");
}
// 检测到火焰时自动开启风扇(模拟灭火)
if (flame) {
if (!fanState) {
fanState = true;
digitalWrite(FAN_PIN, HIGH);
Serial.println(F("[AUTO] 火焰检测触发 → 风扇 ON"));
}
lastFlameDetectedMs = millis();
} else {
// 连续3秒无火焰则关闭风扇
if (fanState && (millis() - lastFlameDetectedMs > 3000)) {
fanState = false;
digitalWrite(FAN_PIN, LOW);
Serial.println(F("[AUTO] 连续3秒无火焰 → 风扇 OFF"));
}
}
delay(50);
} else {
// 命令模式下,每隔1秒打印一次火焰状态(非侵入式)
static unsigned long lastPrintMs = 0;
if (millis() - lastPrintMs >= 1000) {
lastPrintMs = millis();
Serial.print(F("[MONITOR] 火焰="));
Serial.print(flameDetected() ? "有" : "无");
Serial.print(F(" 风扇="));
Serial.println(fanState ? "ON" : "OFF");
}
delay(100);
}
}
4.1 调试流程
①打开串口监视器
显示帮助信息和当前状态,发送
t进入持续监测模式,串口实时打印火焰状态变化
②靠近火焰传感器

检测到火焰时自动开启风扇,移开光源等待 3 秒,风扇自动关闭
③发送 s 读取温湿度

打印当前温湿度数值,发送
f手动开关风扇,风扇 ON/OFF 切换
4.2 视频演示
火焰传感器+风扇灭火模块独立测试
输入h展示当前实时状态,用打火机在远、中、近三个距离依次靠近传感器,配合t模式下[FLAME]边沿触发提示和[AUTO]自动开关风扇的串口输出同框录制,重点展示"检测到火焰立刻响应、拿开后延迟3秒才关闭"这个防抖设计的实际效果。切到s命令读取温湿度,展示正常读数和"采样间隔不足"报错两种情况的对比
五、整车项目灭火模式实现
5.1 灭火模式状态机
整车项目中,FireBehavior 类实现了完整的灭火状态机
四种状态
enum FireState : uint8_t {
FIRE_IDLE, // 空闲态:遥控驾驶,后台监测火焰
FIRE_BACK, // 倒车靠近火源
FIRE_PAUSE, // 停车吹风
FIRE_FORWARD // 前进离开
};
状态转换图

5.2 关键设计细节
①全局火焰监测(独立于状态)
bool flame = g_env.flameDetected();
if (flame) _lastFlameMs = millis();
无论当前处于哪个子状态,每帧都会检查火焰检测状态,并更新 _lastFlameMs(最后一次检测到火焰的时间戳)。
② 灭火成功的判定条件
if (_st != FIRE_IDLE && (millis() - _lastFlameMs > NO_FLAME_MS)) {
// 连续3秒未检测到火焰 → 判定灭火成功
g_env.setFan(false);
g_motor.stop();
_st = FIRE_IDLE;
}
只有在非 IDLE 状态下才进行这个判断(IDLE 时本来就没有火焰,不需要判定),避免逻辑混乱。
③ 反复灭火机制
case FIRE_FORWARD:
if (millis() - _ts > FORWARD_MS) {
g_motor.stop();
if (flame) {
// 火焰还在 → 继续倒车吹风
g_motor.motorRun(-BACK_SPEED, -BACK_SPEED);
_st = FIRE_BACK;
_ts = millis();
} else {
_st = FIRE_IDLE;
}
}
break;
前进阶段结束后,如果还检测到火焰,会自动回到 FIRE_BACK 继续灭火,形成往复循环,直到火焰完全熄灭。
④ 手动驾驶控制权
sc_comms.cpp中的parseCmd()函数将MODE_FIRE加入了方向键白名单
if (g_mode == MODE_MANUAL || g_mode == MODE_STRAIGHT || g_mode == MODE_FIRE) {
switch (c) {
case '1': g_motor.goWithDirec(carForward); break;
case '2': g_motor.goWithDirec(carBack); break;
// ... 方向键处理
}
}
在 FIRE_IDLE 状态下,用户可以自由遥控驾驶小车。检测到火焰时,代码自动接管控制权执行灭火,灭火完成后自动归还控制权
5.3 参数调优
灭火模式的参数定义
static const long BACK_MS = 500; // 倒车靠近火源的时长
static const long PAUSE_MS = 800; // 停留吹风时长
static const long FORWARD_MS = 500; // 前进离开的时长
static const long NO_FLAME_MS = 3000; // 连续多久未检测到判定灭火成功
static const int BACK_SPEED = 60; // 倒车/前进速度
调参建议
| 参数 | 作用 | 调大效果 | 调小效果 |
|---|---|---|---|
BACK_MS |
倒车时长 | 风扇更靠近火源,但可能撞墙 | 离火源较远,效果可能差 |
PAUSE_MS |
吹风时长 | 灭火更彻底,但耗时更长 | 响应更快,但可能吹不灭 |
NO_FLAME_MS |
判定灭火的等待时间 | 更稳定,避免误判 | 响应更快,但可能误判 |
BACK_SPEED |
移动速度 | 动作更快,但可能吹动火焰 | 更平稳,但移动慢 |
六、常见问题 FAQ
Q1:灭火模式下小车不执行灭火动作,但指示灯亮了?
A:_st 状态标志没有被正确设置,火焰检测的极性判断错误。确认 EnvSensor::flameDetected() 返回值正确,检测到火焰返回 true。串口打印 _st 值,确认状态机是否正确进入 FIRE_BACK
Q2:灭火模式中,火焰熄灭后风扇不关闭?
A:NO_FLAME_MS = 3000 意味着需要连续 3 秒未检测到火焰才判定灭火成功。如果传感器偶尔误读到火焰,会重置计时器。确认火焰传感器没有误触发,适当降低 NO_FLAME_MS
灭火模式是整个项目中唯一一个“用户驾驶为主、代码自动干预为辅”的混合模式——小车平时由用户遥控行驶,只有检测到火焰时才会自动介入执行灭火动作,灭火完成后立即归还控制权。这种“人机协同”的设计思路在其他模式(循迹、避障、跟随)中是见不到的,是一个非常典型的“人机协同”嵌入式设计
更多推荐
所有评论(0)