这个模块上的火焰传感器完全感觉不到热。它其实是一颗红外光电二极管,看的是 760–1100nm 的光;板上那颗蓝色电位器设定一个参考电压,而 LM393 比较器会在光电二极管的读数越过这个参考的那一瞬间,把 DO 引脚从 HIGH 拉到 LOW。让蜂鸣器叫起来的就是这一次跳变——所以所谓「调灵敏度」,实际上是在挑一个阈值,而这个阈值是可以量出来的。因为它认的是直视视线里的光,这是一个 RBT 课堂演示装置,不是家用烟雾探测器的替代品。
这个火焰传感器模块怎么知道有火?
FLAME4P 火焰传感器模块是一块青蓝色的小板,一端伸出一颗黑色的子弹头:一颗 5mm 红外光电二极管,响应波段是 760nm 到 1100nm,视角大约 60 度。火焰在这个波段里确实辐射得很强——但很多别的东西也一样,而这个事实几乎能解释这个模块的每一种行为。
落在光电二极管上的红外光会让它更导通,于是它旁边那个分压点的电压往下掉——这个电压原原本本地送到 AO 引脚上。标着 103(10k)的蓝色电位器则是另一个完全独立的分压器,做出一个由你自己选定的固定参考电压。中间那颗八只脚的黑色芯片是 TI 的 LM393 双路比较器,它只干一件事:比较这两个电压,宣布哪一个更高。
干净的时候 AO 坐在高位,红外一强它就往下掉——火焰越近,analogRead() 读到的数字越小。DO 平时是 HIGH,检测到火焰时降到 LOW,DO-LED 同时点亮。这两件事你都可以自己验证,不必相信任何人。LM393 在反相输入高于同相输入时把输出拉低。在这块板上,AO 喂的是同相输入,电位器喂的是反相输入,所以火焰把 AO 压到参考电压以下时,反相输入就成了更高的那一个——输出于是被拉到 LOW。桌面上看到的现象和比较器的规则,给出的是同一个答案。
LM393 的输出还是集电极开路的:里面只有一只能把 DO 拉到 GND 的晶体管,没有任何东西能把它推回高电平。没有上拉电阻,这只脚就是悬空的,它的 HIGH 什么都不代表。模块板上已经带了这颗电阻,所以 pinMode(pin, INPUT) 就够了;INPUT_PULLUP 只是再并上第二颗上拉电阻而已。
把这块板弄懂一次,你等于拿到了一大串模块的钥匙:同样的青色板 + 蓝色电位器 + LM393 + VCC/GND/DO/AO 排列,正是我们的土壤湿度模块、声音模块、红外避障传感器和 LDR 光敏模块。同一颗红外光电二极管也在循线小车里读地面的反射——一样的物理,不一样的活。
材料清单
选配:9V 电源适配器
| Item | Price | Qty | |
|---|---|---|---|
Power Supply Adapter DC Universal AC to DC Converter PSU 5V2A 5V3A 9V2A 12V2A - P.S. ADAPTOR (9V2A)PSA0902 | RM13.95 |
让警报器不靠笔记本电脑也能自己站着工作。9V 正好落在 Uno 建议输入范围的中间;5V 的适配器则不够板上稳压器工作。

为什么这是课堂里的警报器,而不是家里的防护?
这一类火焰传感器需要直视的视线,而这个限制正是从它的工作原理直接来的:它检测的是光,不是火。所以它会漏掉没有明火、只在阴燃的火情,它完全看不见烟,它也对家具后面或隔壁房间的火完全失明。真正保护一个家的,是通过认证的烟雾探测器,不是这个项目。如果你想测的是颗粒物和空气质量,那该看我们的空气质量与烟霾传感器指南。
同一个限制也解释了每一次误报。穿过窗户的阳光、白炽灯或卤素灯、CCTV 摄像头上的红外补光灯,甚至电视遥控器打出的红外脉冲——统统落在同一段 760–1100nm 里。规律很好记:能骗过这个传感器的是会发光的东西,而不只是热的东西。
用真火测试要按实验室的常规纪律来。一支小蜡烛或打火机,放在一臂距离外,有老师或成年人在旁看着,桌面清空、附近不要有易燃物;不要用大火焰,也不要让它无人看管。除此之外,整个作品都是低压直流——唯一跟市电有关的就是那个选配的墙插适配器,一个双重绝缘的封闭成品,你只需要把它插进插座,不拆开、也不改装。

火焰传感器、蜂鸣器和 Uno 要怎么接?
FLAME4P 模块出厂只有一种接口:一排 2.54mm 的 4-pin 公排针,标着 VCC、GND、DO、AO。而 Arduino Uno 朝上的那一面全是母座。公针当然插得进母座——问题在别处:模块那四只脚是紧挨着的,可它需要的四个信号(5V、GND、D2 和 A0)却分散在 Uno 三个不同的排座上。所以这个模块没办法直接插在板子上,每一个信号都得有自己的一条线。SFM-20B 蜂鸣器则是另一种麻烦:它的两条线末端是裸露的镀锡铜丝,没有任何连接器——直接戳进母座,它会散开,然后在演示到一半时掉出来。
170 孔迷你面包板一次把两个问题都解决了,因为里面的弹簧夹片本来就是设计来夹住公针以及裸线头的。注意:这个 170 孔的尺寸没有 + 和 − 电源轨。它自己的孔数就是证明——两组 × 17 列 × 5 孔 = 刚好 170,也就是说每一个孔都已经属于接线区,一个都不剩给电源轨。这个作品全部用列来接。板上也没有印列号,所以下面表格里的「第 1 列」,指的是你自己挑的四个相邻列中的第一列。
这就带出面包板最有用的那一点:同一列里的五个孔,是同一个电气节点。正因如此,蜂鸣器的黑线可以插进跟模块 GND 脚同一列——它们在板子内部已经连通了,所以只需要占用 Uno 的一个 GND 脚。模块的排针是从板子的一端伸出来的,不是从正面,所以插上去之后模块是竖着站起来的——而那颗黑色子弹头长在另一端,于是它朝上望着天花板,而不是横着扫过桌面。把它的两只脚轻轻掰一下,或者把模块整个斜过来,让那颗头对准你真正想覆盖的区域;然后在真实距离上用 DO-LED 确认一次。
| 从 | 面包板列 | 接到 Uno | 用什么线 |
|---|---|---|---|
| FLAME4P — VCC | 第 1 列 | 5V | M-M 杜邦线 |
| FLAME4P — GND | 第 2 列(共用) | GND | M-M 杜邦线 |
| FLAME4P — DO | 第 3 列 | D2 | M-M 杜邦线 |
| FLAME4P — AO | 第 4 列 | A0 | M-M 杜邦线 |
| 蜂鸣器 — 红线(+) | 另外一列 | D8 | M-M 杜邦线 |
| 蜂鸣器 — 黑线(−) | 第 2 列,跟模块 GND 同一列 | — | 不用再加线 |
总共只要五根公对公杜邦线,因为最后一行是面包板替你连的,不是线连的。这颗蜂鸣器分正负:红线接被驱动的那只脚,黑线接 GND;接反了它就只是不响。
这颗蜂鸣器不需要三极管,账是这样算的。SFM-20B 的额定电压是 12V DC,在这个额定值下的最大电流是 12mA(工作范围 3–24V,固定音调 3300±500Hz),所以用 5V 驱动时它吃得更少。Uno 这一侧,ATmega328P 数据手册把每只 I/O 引脚的直流电流绝对最大值定在 40.0mA,而它的输出电平是按 20mA 保证的——VCC 5V、拉出 20mA 时,引脚仍然保证至少有 4.2V。不到 12mA,稳稳地待在这两个数字下面。
老实说另一半:这颗蜂鸣器的主要响度指标是在那个 12V 额定值下给的,所以在 5V 下它会轻一些——够桌面演示或课堂用,但不是警笛。想要它全力尖叫?让蜂鸣器跑 12V,再用一颗小三极管去开关它。这个活别用继电器——那是机械开关,一秒响好几声的节奏会啃掉它的触点寿命。

那颗蓝色电位器要怎么才算校准对了?
FLAME4P 上的 10k 电位器设定的是一个阈值,所以校准它不是「拧到能用为止」——它是一次测量。这也正是下面这份 sketch 要把 analogRead(A0) 打到 Serial Monitor 的原因:那个数字给你两个边界标记。
- 用 9600 baud 打开 Serial Monitor,桌面干净、没有火。记下稳定下来的 AO 读数——这就是你的「干净」数。
- 把火焰拿到你真正想覆盖的距离上,比如 20 厘米。记下新的 AO 读数;它会更低。
- 慢慢拧电位器,直到 DO 在这两个数字之间翻转状态。DO-LED 会同时亮,所以这个跳变点不看屏幕也看得见。方向不用背:桌面干净时轻轻拧一点,看哪个方向会把 DO-LED 点亮——那个方向就是更灵敏的一边。
这也解释了为什么把电位器拧到底从来都不管用。一头会把参考电压放在你的干净读数之上,于是 DO 一直是 LOW,警报响个不停;另一头把它放在你的火焰读数之下,于是 DO 永远不动。有用的阈值,永远落在刚才那两次测量之间。
想亲眼看看拧螺丝时 DO-LED 真的在跳?这段模块演示录的正是同一个硬件:
这个火灾警报器的 Arduino 代码长什么样?
SFM-20B 是一颗有源蜂鸣器——振荡电路在它自己肚子里,所以 digitalWrite(HIGH) 让它响、digitalWrite(LOW) 让它安静。tone() 不会改变它的音调,它也放不了旋律;断续的响声来自你的代码,不是来自蜂鸣器。
还有一个设计决定值得解释:这份 sketch 要求连续五次读数都确认有火,才让它叫。采样每 20 毫秒一次,警报只在第五个样本上才启动,所以火光必须连续维持 80 到 100 毫秒——80ms 是第一个样本到第五个样本之间的间隔,剩下的取决于采样相位。相机闪光灯只有几毫秒,遥控器发的是断断续续的短脉冲——两者都填不满这个窗口,而真正的火焰轻轻松松就填满了。至于十分之一秒,也不会耽误任何要紧的事。
// 火灾警报器:火焰传感器模块(FLAME4P)+ SFM-20B 有源蜂鸣器。
// 这个模块的 DO 是 active-LOW:平时 HIGH,检测到火焰时落到 LOW。
// AO 则是反过来走的 - 没火时最高,红外一强就往下掉。Serial 会把两个
// 都打出来,这样你就能用自己这一颗的真实数字,去校准那颗蓝色电位器。
const int PIN_DO = 2; // 模块的数字输出(板上已经带了上拉电阻)
const int PIN_AO = A0; // 光电二极管分压点的原始电压
const int PIN_BUZZ = 8; // 蜂鸣器的红线
// 每 20 ms 采样一次,连续 5 个样本确认才算火:
// 火光必须连续维持 80-100 ms。相机闪光灯和电视遥控器的短脉冲
// 都填不满这个窗口;真正的火焰可以。
const unsigned long SAMPLE_MS = 20;
const int CONFIRM_HITS = 5;
// 断续的响声比一直尖叫更容易听清楚,而这颗有源蜂鸣器只认 HIGH 和
// LOW - 所以节奏得由代码来做。
const unsigned long BEEP_ON_MS = 400;
const unsigned long BEEP_OFF_MS = 200;
// 校准读数每秒打 4 次就好,这样 Serial Monitor 还看得过来。
const unsigned long PRINT_MS = 250;
unsigned long lastSample = 0;
unsigned long lastPrint = 0;
unsigned long beepMark = 0;
int hits = 0; // 已经连续确认了几个样本有火
bool alarming = false;
bool beepHigh = false;
void setup() {
Serial.begin(9600);
// 是 INPUT,不是 INPUT_PULLUP。LM393 的输出是集电极开路 - 它只能
// 把电平拉到 LOW - 而模块自己已经带了上拉电阻,所以 Uno 的内部
// 上拉在这里没有活可干。
pinMode(PIN_DO, INPUT);
pinMode(PIN_BUZZ, OUTPUT);
digitalWrite(PIN_BUZZ, LOW);
Serial.println(F("Flame sensor ready. AO high = no flame. DO LOW = flame detected."));
}
void loop() {
unsigned long now = millis();
if (now - lastSample >= SAMPLE_MS) {
lastSample = now;
bool flameNow = (digitalRead(PIN_DO) == LOW);
if (flameNow) {
if (hits < CONFIRM_HITS) hits++;
} else {
hits = 0;
}
bool wantAlarm = (hits >= CONFIRM_HITS);
if (wantAlarm && !alarming) {
alarming = true;
beepHigh = true;
beepMark = now;
digitalWrite(PIN_BUZZ, HIGH);
Serial.println(F("FLAME DETECTED - alarm on"));
} else if (!wantAlarm && alarming) {
alarming = false;
beepHigh = false;
digitalWrite(PIN_BUZZ, LOW);
Serial.println(F("Clear - alarm off"));
}
}
// 断续的响声节奏,不用 delay(),好让传感器继续被读。
if (alarming) {
unsigned long span = beepHigh ? BEEP_ON_MS : BEEP_OFF_MS;
if (now - beepMark >= span) {
beepMark = now;
beepHigh = !beepHigh;
digitalWrite(PIN_BUZZ, beepHigh ? HIGH : LOW);
}
}
// 校准行:先是 AO,然后是由电位器决定的 DO 状态。
if (now - lastPrint >= PRINT_MS) {
lastPrint = now;
Serial.print(F("AO="));
Serial.print(analogRead(PIN_AO));
Serial.print(F(" DO="));
Serial.println(digitalRead(PIN_DO) == LOW ? F("LOW (flame)") : F("HIGH (clear)"));
}
}
注意 loop() 里一个 delay() 都没有。响声的节奏是由 millis() 驱动的,所以哪怕蜂鸣器正在叫,传感器仍然每 20ms 被读一次——否则每一声响的那段时间里,警报器对整个世界都是聋的。
也留意一下它的不对称:要五个样本才启动警报,但只要一个干净的样本就足够把它关掉。蜂鸣器叫得断断续续、忽响忽停,说明你的阈值贴你的火焰读数贴得太近了。想让它一旦响起来就一直响到人为关掉?在它启动之后就把 alarming 钉在 true,只留 Uno 的 reset 按钮去清掉它。
我们从真实客户身上看到的常见错误
这个火焰传感器模块最常见的一个,是把 DO 读反了:代码认为 HIGH 才是有火,结果做出来的警报器一直在响,只有把火拿给它看的时候才安静。第二名:指望一颗有源蜂鸣器唱歌,试着用 tone(),然后以为蜂鸣器坏了。
接下来是把电位器拧到底的那种——一头是永远响,另一头是永远聋——而它真正的位置,是在两个量出来的 AO 读数之间。这个错误常常还配着另一个:测试台就摆在被太阳直射的窗边,然后得出「传感器坏了」的结论,而它其实完全在照着设计工作。
还有两个跟硬件有关。蜂鸣器的镀锡裸线被直接戳进 Uno 的母座,然后散开,演示到一半掉出来——这就是材料清单里放着面包板的原因。至于供电:选 9V 的适配器,不要 5V。Uno 的 barrel jack 喂的是板载稳压器,而稳压器只能把电压往下降——所以 Arduino 建议这个插孔用 7–12V,而 9V 正好落在中间。
关于 Arduino 火焰传感器的常见问题
为什么没有火,我的蜂鸣器也一直在响?
几乎每次都是 DO 读反了。这个模块是 active-LOW 的,所以你的判断必须是 digitalRead(pin) == LOW。如果代码本来就写对了,那么 DO-LED 一直亮就说明电位器的参考电压坐在你的干净 AO 读数之上——往回拧,直到那颗 LED 熄灭。
这个传感器可以代替家里的烟雾探测器吗?
不能,而且这跟元件质量无关。它认的是直视视线里的光,所以没有明火的阴燃、烟,以及墙后的火,全都会被漏掉。把它当作 RBT 演示装置或学校电子专题项目来用,保护家的事交给通过认证的烟雾探测器。
为什么一开灯或者一拉开窗帘,警报就响了?
那颗光电二极管响应的是 760–1100nm,而阳光、白炽灯、卤素灯以及 CCTV 摄像头的红外补光灯,在这一段里都辐射得很强。把传感器挪开直射的阳光,然后在它实际要工作的位置重新校准一次。
SFM-20B 蜂鸣器能用 tone() 放旋律吗?
不能。它是有源蜂鸣器,振荡器内建在 3300±500Hz 上,所以只有一个音。digitalWrite() 给 HIGH 它就响、给 LOW 它就停;断续的节奏是在代码里安排这只脚的时间做出来的。
用 Arduino 引脚直接驱动这颗蜂鸣器,需要三极管吗?
不需要。SFM-20B 的额定电压是 12V,在该额定值下的最大电流是 12mA,所以用 5V 驱动时它吃得更少——舒舒服服地待在 ATmega328P 数据手册给每只 I/O 引脚定的 40.0mA 绝对最大值之下。只有当你把蜂鸣器提到 12V 时,才需要三极管。
最后更新:2026年8月。有问题?WhatsApp 联系我们。



Arduino Uno Compatible SMD UNO R3 with Type B Cable - ATMEGA328P with CH340G-Microcontroller Project
Infrared Flame Sensor Module Fire Detector Sensor Module For Arduino IOT Application
SFM-20B Active Buzzer DC 3-24V Piezoelectric Long Continous Beep
MB102 Breadboard 170 400 830 Holes Breadboard Donut Board Arduino Prototype Multi Color - MINI BREADBOARD 170 HOLES (WHITE)
40pcs Dupont Wire 10cm 20cm 30cm for Breadboard DIY Experiment Jumper Wire Breadboard wire - DUPONT WIRE M-M 30CM
Power Supply Adapter DC Universal AC to DC Converter PSU 5V2A 5V3A 9V2A 12V2A - P.S. ADAPTOR (9V2A)