Arduinoで炎センサーを使う:配線、コード、火災警報
Arduinoで炎センサーを使うガイド。配線、DOとAOの出力の違い、感度、検知距離と角度、ブザーの火災警報、MQ-2との併用、誤報の防ぎ方、ESP32とPicoのコードを解説します。
炎センサーは、ライター、ろうそく、燃える紙が出す赤外線を検知する安価なモジュールで、わずか数行のコードでArduinoと一緒に使えます。Arduinoで炎センサーを使うのにライブラリは要りません。モジュールのDOピンは炎を見ると状態が変わり、4ピン版のAOピンは炎の強さに応じて変わる電圧を出します。火災警報、消火ロボット、キッチンの安全システム、学校のプロジェクトで最も人気のあるセンサーの1つです。
このガイドでは、炎センサーの中身と、760〜1100 nmの光にしか反応しない理由、デジタル出力とアナログ出力の違い、感度調整用のポテンショメーターの正しい合わせ方、検知距離と角度を学びます。続いて、ブザーとLEDで火災警報を作り、センサーをMQ-2ガスセンサーと組み合わせ、日光やリモコンなどによる誤報をソフトウェアで減らし、ESP32でスマホにTelegramの通知を送り、同じ回路をRaspberry Pi PicoとMicroPythonで動かします。

炎センサーとは?どう動くのか
モジュールの先端にある黒いLEDのような部品は、実は赤外線フォトトランジスタ(基板によってはフォトダイオード)で、よくある例はYG1006です。黒いエポキシの本体が可視光の大部分をさえぎり、およそ760〜1100 nmの近赤外線だけを通します。感度のピークは940 nm付近です。上限はシリコン自体によるものです。バンドギャップが1.12 eVのシリコンは、波長が約1100 nmより長い光子を吸収できません。炎の中の高温のすすの粒子もこの帯域で放射するので、炎が近づくほどフォトトランジスタを流れる電流が増えます。基板上の分圧回路がその電流を電圧に変え、LM393コンパレーターがそれをポテンショメーターで設定したしきい値と比べて、DOピンの状態を決めます。
| 項目 | 値 |
|---|---|
| 検知する波長 | 760〜1100 nm(感度のピークは約940 nm) |
| 検知角度 | 約60°(軸の左右に約30°ずつ) |
| 検知距離 | ライターの炎で一般に最大80 cm。大きな炎ならもっと遠く |
| 電源電圧 | 3.3〜5 V |
| コンパレーター | LM393。しきい値は多回転ポテンショメーターで設定 |
| 出力 | DO(デジタル、全モデル)、AO(アナログ、4ピンモデル) |
| DOの論理 | よくある青いモジュール:炎 = LOWでDOのLEDが点く。KY-026:通常は炎 = HIGH |
| 応答時間 | 1ミリ秒よりはるかに短い。フォトトランジスタはマイクロ秒単位で反応する |
| 受光部の動作温度 | 約−25〜+85 °C |
同じ仕事をする炎センサーの基板はいくつかあります。上の写真のような3ピンのモジュールにはデジタル出力しかなく、4ピンのモジュールにはアナログ出力が加わります。センサーキットでおなじみのKY-026も同じ仕組みですが、DO出力は通常反転しています。消火ロボットには、5個の受光部を扇形に並べた多チャンネルの基板が使われます。炎センサーはパッシブ型、つまり自分では光を出しません。この点で、発光部と受光部の両方を持つTCRT5000赤外線センサーとは異なります。
| モデル | ピン | 特徴 | 選ぶ場面 |
|---|---|---|---|
| 3ピンモジュール | VCC、GND、DO | 炎のあり・なしだけ | シンプルな火災警報、最初の実験 |
| 4ピンモジュール | VCC、GND、DO、AO | しきい値の出力 + 炎の強さ | 段階的な警報、誤報のフィルタリング |
| KY-026 | A0、G、+、D0 | 4ピンモジュールと同じ。DOは通常、炎でHIGH | センサーキットに付属するプロジェクト |
| 5チャンネルモジュール | VCC、GND、受光部ごとに独立した出力 | 約120°の広い視野 | 炎の方向を探すロボット |
デジタル出力(DO)とアナログ出力(AO)の違い
DOピンはあり・なしの答えです。赤外線がポテンショメーターで設定したしきい値を超えると状態が変わります。よくある青い基板では、このピンは普段HIGHで、炎を見るとLOWになり、DOのLEDが点きます。KY-026のように逆の基板もあります。ですから、まず自分のモジュールをライターでテストし、シリアルモニタに何が表示されるか確認してください。AOピンは生の信号を出します。多くのモジュールでは、炎が近づくほどこの値が下がります。炎のない部屋で900〜1020だったanalogReadの値が、ライターを近づけると数百まで下がることがあります。ポテンショメーターはDOのしきい値を変えるだけで、AOには影響しません。
| 項目 | DO(デジタル) | AO(アナログ) |
|---|---|---|
| 読み取る値 | HIGHまたはLOW | Arduino Unoで0〜1023 |
| しきい値 | 基板上のポテンショメーター | コードで設定する |
| 得られる情報 | 炎があるかないか | 炎の強さ、おおよその距離、揺らぎ |
| 使うピン | 任意のデジタルピン | アナログピン(ADC) |
| 向いている用途 | シンプルな警報、割り込みでの即時反応 | 段階的な警報、誤報のフィルタリング、方向の検出 |
感度調整用ポテンショメーターの合わせ方
- モジュールに電源を入れ、センサーを実際に使う場所に、監視する方向へ向けて置きます。この調整は必ず実際の環境で行ってください。
- 炎がない状態で、小さなドライバーで青いポテンショメーターを回し、DOのLEDが点灯と消灯の間で切り替わる位置を探します。
- その位置から、LEDが消えたままになる方向へさらに半回転から1回転回します。こうすれば、周囲の明るさの小さな変化で警報が鳴りません。
- 検知したい最も遠い距離からセンサーにライターを向け、LEDが点くか確認します。点かなければ、設定を少し敏感にして再度試します。
- センサーが日の当たる窓や白熱電球に向いていないか確かめ、数時間後に違う明るさの下で設定をもう一度確認します。
検知距離と角度
メーカーが示す80 cmのような値は標準的なライターの炎を基準にしたもので、実際の検知距離は炎の大きさ、ポテンショメーターの設定、周囲の明るさで決まります。ここでは単純な物理法則が当てはまります。小さな光源からの光の強さは距離の2乗に反比例して弱まります。距離が2倍になると、センサーに届く赤外線は4分の1になります。逆にいえば、4倍の光を出す炎なら2倍の距離から、100倍強い炎なら10倍の距離から検知できます。そのため、ろうそくの炎はライターより短い距離でしか検知できませんが、燃えている紙の山なら数メートル先からでも検知できます。
| 距離 | センサーに届く光(20 cm = 100%) |
|---|---|
| 20 cm | 100% |
| 40 cm | 25% |
| 60 cm | 11% |
| 80 cm | 6% |
| 1 m | 4% |
検知角度は約60度なので、センサーは軸の左右それぞれ約30度の円錐の中にある炎を見ます。炎がその円錐の端に近づくと、信号はすぐに弱まります。受光部の足は長いので、そっと曲げて好きな方向に向けられますが、足が本体に入る根元を何度も曲げると折れてしまいます。視野を狭めるには、受光部につや消しの黒いチューブをかぶせます。内径6 mm、長さ2 cmのチューブなら角度がおよそ30度に狭まり、横から入る日光やランプの光をさえぎれます。視野は監視したい範囲をちょうど覆う広さにしてください。それより広くしても、誤報の原因が増えるだけです。
炎センサーの配線:Arduino Uno、ESP32、Pico
| 接続 | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| モジュールのVCC | 5V | 3V3 | 3V3(OUT)、36番ピン |
| モジュールのGND | GND | GND | GND、38番ピン |
| モジュールのDO | D2 | GPIO 27 | GP15、20番ピン |
| モジュールのAO(4ピン) | A0 | GPIO 34(ADC1) | GP26 / ADC0、31番ピン |
| アクティブブザー(+) | D8 | GPIO 25 | GP14、19番ピン |
| 赤のLED(220 Ω付き) | D7 | GPIO 26 | GP13、17番ピン |
ステップごとに:Arduinoで炎センサーの火災警報を作る
- モジュールのVCCピンをArduinoの5Vピンに、GNDをGNDに、DOをD2につなぎます。
- アクティブブザーの長い(+)足をD8に、短い足をGNDにつなぎます。
- 赤のLEDの長い足を220 Ωの抵抗を通してD7に、短い足をGNDにつなぎます。別のLEDを使う場合は、LED抵抗計算ツールで抵抗値を求められます。
- 下のコードを書き込み、シリアルモニタを9600ボーで開いて、20〜30 cm離れたところからセンサーにライターを短時間向けます。
- ブザーが鳴りLEDが点けば、回路は動いています。炎がないのに警報が鳴り、炎があると静かになる場合は、コードのFLAME_ACTIVEをHIGHに変えてください。
1// Alev sensörü ile basit yangın alarmı / Simple fire alarm with a flame sensor
2const int FLAME_DO = 2; // modülün DO pini / module DO pin
3const int BUZZER = 8; // aktif buzzer / active buzzer
4const int LED = 7; // kırmızı LED (220 Ω ile) / red LED (with 220 Ω)
5const int FLAME_ACTIVE = LOW; // çoğu modülde alev = LOW, KY-026'da HIGH / most modules: flame = LOW, KY-026: HIGH
6
7void setup() {
8 Serial.begin(9600);
9 pinMode(FLAME_DO, INPUT);
10 pinMode(BUZZER, OUTPUT);
11 pinMode(LED, OUTPUT);
12}
13
14void loop() {
15 bool flame = digitalRead(FLAME_DO) == FLAME_ACTIVE;
16
17 if (flame) {
18 Serial.println(F("ALEV ALGILANDI! / FLAME DETECTED!"));
19 digitalWrite(LED, HIGH);
20 digitalWrite(BUZZER, HIGH); // kesik kesik alarm sesi / beeping alarm
21 delay(150);
22 digitalWrite(BUZZER, LOW);
23 delay(150);
24 } else {
25 digitalWrite(LED, LOW);
26 digitalWrite(BUZZER, LOW);
27 delay(50);
28 }
29}コードは毎回DOピンを読み、その結果をFLAME_ACTIVE定数と比べます。この定数のおかげで、炎でLOWになる青いモジュールでも、HIGHになるKY-026でも同じコードが使えます。炎が見えている間、LEDは点いたままで、ブザーは150 msごとにオン・オフして、注意を引くピーピーという音を出します。アクティブブザーは発振回路を内蔵しているので、HIGHとLOWだけで鳴ります。パッシブブザーの場合は、ブザーとは?で説明しているように、digitalWriteの代わりにtone()を使ってください。DOピンにD2を選んだのにはもう1つ理由があります。UnoではD2は割り込みに対応した2本のピンの1つなので、attachInterruptを使えばloopの残りを待たずに炎を捉えられます。
アナログでの読み取り:炎の強さとおおよその距離
4ピンのモジュールなら、AO出力を使ってずっと多くのことができます。生の値は環境ごとに違うので、固定のしきい値ではなく次の方法を使います。起動時に炎がない状態で基準値を測り、新しい値がそこからどれだけ離れたかを見ます。下のコードは16回の読み取りを平均してノイズを減らし、そのずれを炎の強さとして計算し、炎が近いか遠いかをおおまかに知らせます。炎でAOの値が上がるモジュールなら、DROPS_WITH_FLAMEをfalseにするだけです。
1// AO ile alev gücü ve yaklaşık mesafe / Flame strength and rough distance via AO
2const int FLAME_AO = A0; // 4 pinli modülün AO pini / AO pin of the 4-pin module
3const bool DROPS_WITH_FLAME = true; // çoğu modülde alev yaklaşınca değer düşer / on most modules the value falls as the flame approaches
4int baseline; // alevsiz ortam değeri / flame-free ambient value
5
6int readAverage() {
7 long sum = 0;
8 for (int i = 0; i < 16; i++) { // 16 ölçümün ortalaması / average of 16 readings
9 sum += analogRead(FLAME_AO);
10 delay(2);
11 }
12 return sum / 16;
13}
14
15void setup() {
16 Serial.begin(9600);
17 delay(1000); // açılışta ortamda alev olmamalı / no flame at start-up
18 baseline = readAverage();
19 Serial.print(F("Taban / Baseline: "));
20 Serial.println(baseline);
21}
22
23void loop() {
24 int value = readAverage();
25 int strength = DROPS_WITH_FLAME ? baseline - value : value - baseline; // alev gücü / flame strength
26 if (strength < 0) strength = 0;
27
28 Serial.print(F("AO: "));
29 Serial.print(value);
30 Serial.print(F(" Guc / Strength: "));
31 Serial.print(strength);
32
33 if (strength > 400) {
34 Serial.println(F(" -> YAKIN ALEV / CLOSE FLAME"));
35 } else if (strength > 100) {
36 Serial.println(F(" -> UZAK ALEV / DISTANT FLAME"));
37 } else {
38 Serial.println(F(" -> alev yok / no flame"));
39 }
40 delay(250);
41}コードの100と400というしきい値は出発点にすぎません。自分のセンサーに合わせるには、コードを書き込んでシリアルモニタを開き、ライターを10、30、60 cmの位置にかざしたときの炎の強さをメモします。逆2乗の法則により、近くでは値がとても速く、遠くではゆっくり変わるのがわかります。そのため、アナログ値から正確な距離をセンチメートルで求めることはできません。同じ値が、近くの小さな炎からも、遠くの大きな炎からも出るからです。ADCが生の値を作る仕組みはアナログ・デジタル変換で、シリアルモニタの細かな使い方はArduinoのシリアルモニタで学べます。
日光とそのほかの誤報の原因
炎センサーは火を見ているのではなく、特定の帯域の赤外線を見ています。その帯域の光を出すものなら、何でもセンサーを反応させられます。どの光源が問題になるかはウィーンの法則で説明できます。熱い物体が最も強く放射する波長は約2898 / Tマイクロメートルです(Tはケルビンでの温度)。太陽と白熱電球はまさにセンサーの帯域で強く放射しますが、人体は温度が低すぎるので、センサーにはまったく見えません。そのため動きを検知するには、ずっと長い波長を見るPIRセンサーが必要です。
| 光源 | おおよその温度 | ピーク波長 | 炎センサーへの影響 |
|---|---|---|---|
| 太陽 | 5800 K | 0.5 µm | 非常に強い。直射日光やガラスの反射でセンサーが目くらましになることがある |
| 白熱電球とハロゲン電球 | 2700〜3000 K | 約1 µm | 強い。光の多くがセンサーの帯域にちょうど入る |
| ろうそくとライターの炎(すすの粒子) | 約1500 K | 約1.9 µm | 検知したい対象。放射の一部が帯域に入る |
| 赤熱したヒーターの発熱体、コンロ | 約1100 K | 約2.6 µm | 近距離では反応させることがある |
| テレビのリモコン | - | 0.94 µm(狭い帯域) | 短いパルスで反応させる |
| LEDと蛍光灯 | - | 可視光の帯域 | 赤外線はほとんどなく、通常は問題ない |
| 人体 | 310 K | 約9.3 µm | センサーには見えない |
誤報の大半はハードウェアで防げます。センサーを窓、日の当たる壁、白熱電球に向けないように置き、できれば下向きに部屋の内側へ向け、黒いチューブで視野を狭め、実際の周囲の明るさの下でポテンショメーターを合わせます。強い日光は誤報を起こすだけでなく、センサーを飽和させて目くらましにすることもある点を覚えておいてください。日光の下でAOの値がすでに最小まで下がっていると、本物の炎が現れても変化する余地が残っていません。業務用の赤外線炎検知器は、高温の二酸化炭素が放射し、日光の大部分が大気に吸収される4.3 µmの帯域を見ること、複数の波長を比べること、炎の揺らぎを解析することでこれを解決しています。このモジュールでは最初の2つはできませんが、揺らぎの解析と時間による確認はソフトウェアでまねできます。
ソフトウェアで誤報を減らす:時間による確認と揺らぎの解析
本物の炎は決して一定には燃えません。空気の流れや上昇する熱いガスによって、明るさがおよそ1秒に1〜20回揺らぎます。一方、日光や白熱電球の光は短い時間ではほぼ一定で、リモコンのパルスは数ミリ秒しか続きません。下のコードはこの違いを3つのルールで利用します。炎の強さが基準値をはっきり上回っていること、直近1秒間に値が決まった量以上揺らいでいること、そしてその両方の条件が少なくとも2秒間途切れずに成り立っていることです。さらに、各読み取りは10 msの間に取った20個のサンプルの平均です。50 Hzの商用電源では、ランプは1秒に100回わずかにちらつきます。ちょうど1回のちらつきの周期である10 msで平均すると、このリップルが打ち消され、ランプを揺らぐ炎と間違えなくなります(日本でも東日本は50 Hzですが、西日本の60 Hz地域ではちらつきは1秒に120回、周期は約8.3 msです)。
1// Süre doğrulamalı ve titreşim analizli alev alarmı / Flame alarm with time confirmation and flicker analysis
2const int FLAME_AO = A0;
3const int BUZZER = 8;
4const int LED = 7;
5const int STRENGTH_LIMIT = 150; // tabandan en az bu kadar sapma / minimum deviation from the baseline
6const int FLICKER_LIMIT = 20; // 1 sn içindeki en az dalgalanma / minimum swing within 1 s
7const unsigned long CONFIRM_MS = 2000; // koşul bu kadar kesintisiz sürmeli / condition must hold this long
8
9float baseline;
10int winMin = 1023, winMax = 0;
11bool flickering = false;
12unsigned long windowStart = 0;
13unsigned long candidateSince = 0;
14
15// 10 ms boyunca 20 ölçüm: 100 Hz lamba titreşimini siler / 20 samples over 10 ms: cancels 100 Hz lamp flicker
16int readFiltered() {
17 long sum = 0;
18 for (int i = 0; i < 20; i++) {
19 sum += analogRead(FLAME_AO); // yaklaşık 0,11 ms sürer / takes about 0.11 ms
20 delayMicroseconds(390);
21 }
22 return sum / 20;
23}
24
25void setup() {
26 Serial.begin(9600);
27 pinMode(BUZZER, OUTPUT);
28 pinMode(LED, OUTPUT);
29 baseline = readFiltered(); // açılışta ortamda alev olmamalı / no flame at start-up
30}
31
32void loop() {
33 int value = readFiltered();
34 int strength = (int)baseline - value; // AO alevle artıyorsa: value - baseline / if AO rises with flame: value - baseline
35 unsigned long now = millis();
36
37 if (value < winMin) winMin = value;
38 if (value > winMax) winMax = value;
39
40 if (now - windowStart >= 1000) { // her saniye pencereyi değerlendir / evaluate the window every second
41 int swing = winMax - winMin;
42 flickering = swing > FLICKER_LIMIT; // alev titrer, güneş sabittir / flames flicker, sunlight is steady
43 if (strength < STRENGTH_LIMIT / 2) {
44 baseline += (value - baseline) * 0.05; // ortam ışığını yavaşça izle / slowly follow ambient light
45 }
46 Serial.print(F("Guc / Strength: "));
47 Serial.print(strength);
48 Serial.print(F(" Dalgalanma / Swing: "));
49 Serial.println(swing);
50 winMin = 1023;
51 winMax = 0;
52 windowStart = now;
53 }
54
55 bool candidate = strength > STRENGTH_LIMIT && flickering;
56 if (!candidate) candidateSince = now; // koşul bozulunca sayaç sıfırlanır / timer resets when the condition breaks
57 bool alarm = now - candidateSince >= CONFIRM_MS;
58
59 digitalWrite(LED, alarm ? HIGH : LOW);
60 digitalWrite(BUZZER, (alarm && (now / 200) % 2) ? HIGH : LOW); // kesik siren / beeping siren
61}コードはdelay()ではなくmillis()で時間を管理しているので、警報が鳴っている間も測定を続けます。この考え方の詳細はmillisとdelayの違いにあります。基準値も固定ではありません。炎がないとき、1秒に1回、最新の値に向かって5パーセントずつ動き、1日の中で変わる周囲の明るさにゆっくり追従します。よいFLICKER_LIMITを見つけるには、シリアルモニタで一定の光源の1秒間の揺れ幅を、次にろうそくの炎の揺れ幅を観察し、その間にしきい値を設定します。この方法は完璧ではありません。風に揺れる葉の間から差す日光も揺らぐからです。移動平均やメディアンフィルターなどほかの手法はセンサーデータのフィルタリングで紹介しています。
MQ-2ガスセンサーと一緒に使う
炎センサーとMQ-2ガスセンサーは、互いの弱点を補い合います。炎センサーはとても速いのですが、視野の中の炎しか見えず、日光にだまされることもあります。MQ-2は煙や可燃性ガスを検知し、角の向こうの火事や、炎の出ていないくすぶりにも気づけますが、煙が届くまで時間がかかり、電源を入れるたびに温まる必要があります。両方を使えば、片方のセンサーからの信号は警告、両方同時の信号は火災として扱えます。


| 炎センサー | MQ-2 | 判断 | 反応 |
|---|---|---|---|
| なし | 正常 | 正常 | LEDとブザーはオフ |
| あり | 正常 | 警告:日光、ランプ、または火災の始まりの可能性 | LEDが点滅、2秒ごとに短いビープ音 |
| なし | 高い | 警告:煙、ガス漏れ、または視野の外の火災 | LEDが点滅、2秒ごとに短いビープ音 |
| あり | 高い | 火災 | LEDが点灯したまま、速いビープ音のサイレン |
1// Alev sensörü + MQ-2 ile iki kademeli yangın alarmı / Two-level fire alarm with a flame sensor + MQ-2
2const int FLAME_DO = 2;
3const int MQ2_AO = A1; // MQ-2 analog çıkışı / MQ-2 analog output
4const int BUZZER = 8;
5const int LED = 7;
6const int FLAME_ACTIVE = LOW; // KY-026 kullanıyorsan HIGH / HIGH if you use a KY-026
7const int SMOKE_LIMIT = 400; // temiz hava değerinin belirgin üstü / well above the clean-air value
8const unsigned long WARMUP_MS = 60000; // MQ-2 ön ısıtma süresi / MQ-2 warm-up time
9const unsigned long HOLD_MS = 300; // DO'daki kısa kesintileri yok say / ignore short gaps on DO
10
11unsigned long lastFlame = 0;
12unsigned long lastPrint = 0;
13
14void setup() {
15 Serial.begin(9600);
16 pinMode(FLAME_DO, INPUT);
17 pinMode(BUZZER, OUTPUT);
18 pinMode(LED, OUTPUT);
19}
20
21void loop() {
22 unsigned long now = millis();
23 if (digitalRead(FLAME_DO) == FLAME_ACTIVE) lastFlame = now;
24 bool flame = lastFlame > 0 && now - lastFlame < HOLD_MS;
25
26 int gas = analogRead(MQ2_AO);
27 bool smoke = now > WARMUP_MS && gas > SMOKE_LIMIT; // ısınana kadar dumanı yok say / ignore smoke until warmed up
28
29 int level = 0; // 0 normal, 1 uyarı / warning, 2 yangın / fire
30 if (flame && smoke) level = 2;
31 else if (flame || smoke) level = 1;
32
33 if (level == 2) {
34 digitalWrite(LED, HIGH);
35 digitalWrite(BUZZER, (now / 150) % 2 ? HIGH : LOW); // hızlı siren / fast siren
36 } else if (level == 1) {
37 digitalWrite(LED, (now / 500) % 2 ? HIGH : LOW); // yanıp sönen LED / blinking LED
38 digitalWrite(BUZZER, (now % 2000) < 100 ? HIGH : LOW); // 2 sn'de bir kısa bip / short beep every 2 s
39 } else {
40 digitalWrite(LED, LOW);
41 digitalWrite(BUZZER, LOW);
42 }
43
44 if (now - lastPrint >= 1000) {
45 lastPrint = now;
46 Serial.print(F("Alev / Flame: "));
47 Serial.print(flame ? 1 : 0);
48 Serial.print(F(" MQ-2: "));
49 Serial.print(gas);
50 Serial.print(F(" Seviye / Level: "));
51 Serial.println(level);
52 }
53}MQ-2のAO出力をA1につなぎ、モジュールを5Vから給電します。炎センサーはD2のままで構いません。SMOKE_LIMITは、MQ-2のガイドで説明しているように、きれいな空気の中でセンサーを数分動かしたあとに読める値よりはっきり高く設定します。冷えたMQ-2は高い値から始まるので、最初の60秒間は煙の条件を無視します。炎の信号の300 msの保持時間(HOLD_MS)は、揺らぐ炎のせいでDO出力が短くオン・オフする動きを、1つの連続した検知にまとめます。この組み合わせをESP32に移す場合は注意してください。MQ-2は5 Vが必要で、AO出力が5 Vに達することがあるので、出力に分圧回路を入れる必要があります。一方、炎センサーは3V3でそのまま動きます。
ESP32でスマホに火災の通知を送る(Telegram)
部屋にいる人だけでなく、外出中の自分にも警報を知らせたいなら、ESP32を使えます。最も簡単なのはTelegramのボットです。無料で、自分のサーバーは要らず、メッセージは通知としてすぐスマホに届きます。下のコードは、炎が1.5秒間見えたらブザーを鳴らしてTelegramのメッセージを送り、同じ火災でスマホがメッセージであふれないよう、メッセージの間隔を最低1分空けます。ボットの準備とコマンドでの双方向の操作はESP32のTelegramボットで詳しく解説しています。
- Telegramで@BotFatherとチャットし、/newbotコマンドを送り、ボットに名前を付けて、表示されたトークンをコピーします。
- @userinfobotのようなIDボットを開いてStart(開始)をタップすると、自分のチャットIDが返ってきます。
- Telegramで新しく作ったボットを探してStartをタップします。これを飛ばすと、ボットはメッセージを送れません。
- Arduino IDEで、ライブラリマネージャからUniversalTelegramBotとArduinoJsonのライブラリをインストールします。
- コードのWi-Fiの名前、パスワード、トークン、チャットIDを自分のものに置き換え、ボードにESP32を選んでコードを書き込みます。
1// ESP32 alev alarmı + Telegram bildirimi / ESP32 flame alarm + Telegram notification
2#include <WiFi.h>
3#include <WiFiClientSecure.h>
4#include <UniversalTelegramBot.h> // ArduinoJson kütüphanesi de gerekir / also needs the ArduinoJson library
5
6const char* WIFI_SSID = "WIFI_ADI"; // Wi-Fi ağının adı / your Wi-Fi name
7const char* WIFI_PASS = "WIFI_SIFRESI"; // Wi-Fi şifren / your Wi-Fi password
8#define BOT_TOKEN "1234567890:ABCdefGhIJKlmNoPQRstuVWXyz" // BotFather'dan / from BotFather
9#define CHAT_ID "123456789" // senin chat ID'n / your chat ID
10
11const int FLAME_DO = 27; // modülün DO pini / module DO pin
12const int BUZZER = 25; // aktif buzzer / active buzzer
13const int LED = 26; // kırmızı LED (220 Ω ile) / red LED (with 220 Ω)
14const int FLAME_ACTIVE = LOW;
15const unsigned long HOLD_MS = 300; // DO'daki kısa kesintileri yok say / ignore short gaps on DO
16const unsigned long CONFIRM_MS = 1500; // alev bu kadar sürmeli / flame must last this long
17const unsigned long COOLDOWN_MS = 60000; // iki mesaj arası en az 1 dk / at least 1 min between messages
18
19WiFiClientSecure client;
20UniversalTelegramBot bot(BOT_TOKEN, client);
21
22unsigned long lastFlame = 0;
23unsigned long flameStart = 0;
24unsigned long lastMessage = 0;
25bool messageSent = false;
26
27void setup() {
28 Serial.begin(115200);
29 pinMode(FLAME_DO, INPUT);
30 pinMode(BUZZER, OUTPUT);
31 pinMode(LED, OUTPUT);
32
33 WiFi.mode(WIFI_STA);
34 WiFi.begin(WIFI_SSID, WIFI_PASS);
35 client.setCACert(TELEGRAM_CERTIFICATE_ROOT); // api.telegram.org kök sertifikası / root certificate
36 unsigned long start = millis();
37 while (WiFi.status() != WL_CONNECTED && millis() - start < 15000) { // en fazla 15 sn bekle / wait up to 15 s
38 delay(500);
39 Serial.print(".");
40 }
41 if (WiFi.status() == WL_CONNECTED) {
42 Serial.println(WiFi.localIP());
43 bot.sendMessage(CHAT_ID, "Yangin alarmi devrede / Fire alarm is online", "");
44 }
45}
46
47void loop() {
48 unsigned long now = millis();
49 if (digitalRead(FLAME_DO) == FLAME_ACTIVE) lastFlame = now;
50 bool flame = lastFlame > 0 && now - lastFlame < HOLD_MS;
51 if (!flame) flameStart = now; // alev yoksa sayaç sıfırlanır / timer resets without flame
52 bool alarm = now - flameStart >= CONFIRM_MS;
53
54 digitalWrite(LED, alarm ? HIGH : LOW);
55 digitalWrite(BUZZER, (alarm && (now / 200) % 2) ? HIGH : LOW); // kesik siren / beeping siren
56
57 if (alarm && (!messageSent || now - lastMessage >= COOLDOWN_MS)) {
58 if (WiFi.status() == WL_CONNECTED) {
59 bool ok = bot.sendMessage(CHAT_ID, "ALEV ALGILANDI! / FLAME DETECTED!", "");
60 Serial.println(ok ? "Mesaj gitti / Message sent" : "Gonderilemedi / Send failed");
61 } else {
62 WiFi.reconnect(); // bağlantı koptuysa yeniden dene / reconnect if the link dropped
63 }
64 lastMessage = now; // başarısızsa 1 dk sonra yeniden dener / retries after 1 min if it failed
65 messageSent = true;
66 }
67 delay(10);
68}sendMessage()の呼び出しは安全なHTTPS接続を開くので、1〜2秒かかることがあります。その間ブザーのパターンが一瞬止まりますが、正常です。起動時、コードは最大15秒間Wi-Fiへの接続を試みます。接続できなければ、その場の警報は動かし続け、送れなかったメッセージは1分後に再送を試みます。インターネットがなくてもブザーは鳴り続けます。ESP32でAO出力も読みたい場合は、必ずADC1のピン(GPIO 32〜39)につないでください。Wi-Fiがオンの間はADC2のピンを使えないからです。表でGPIO 34を選んだのはそのためです。ボードの安全なピンはピン配置ツールで確認できます。5 Vのアクティブブザーが3.3 Vのピンで弱々しく鳴る場合は、トランジスタを使って5 Vで駆動してください。Wi-Fiのない物置や別荘なら、SIM800L GSMモジュールでSMSを送る同じ警報を作れます。
Raspberry Pi PicoとMicroPythonで炎センサーを使う
Raspberry Pi Picoでも同じ考え方が使えます。モジュールを3V3(OUT)ピンから給電し、DOをGP15に、AOがあればGP26につなぎます。モジュールのDOの線には専用のプルアップ抵抗があるので、Picoの内部プルアップは不要です。PicoのADCは12ビットですが、MicroPythonのread_u16()は結果を0〜65535に拡大するので、Arduinoの1023に相当するのはここでは約65535です。コードは、DOの短い途切れを許す300 msの保持時間と、1.5秒の確認を使います。
1# Raspberry Pi Pico alev alarmı / Raspberry Pi Pico flame alarm
2from machine import Pin, ADC
3import time
4
5flame_do = Pin(15, Pin.IN) # modülün DO pini / module DO pin
6flame_ao = ADC(26) # GP26 = ADC0, 4 pinli modülde AO / AO on the 4-pin module
7buzzer = Pin(14, Pin.OUT) # aktif buzzer / active buzzer
8led = Pin(13, Pin.OUT) # kırmızı LED (220 Ω ile) / red LED (with 220 Ω)
9
10FLAME_ACTIVE = 0 # çoğu modülde alev = 0, KY-026'da 1 / most modules: flame = 0, KY-026: 1
11HOLD_MS = 300 # DO'daki kısa kesintileri yok say / ignore short gaps on DO
12CONFIRM_MS = 1500 # alev bu kadar sürmeli / flame must last this long
13
14last_flame = None
15flame_start = time.ticks_ms()
16last_print = time.ticks_ms()
17
18while True:
19 now = time.ticks_ms()
20 if flame_do.value() == FLAME_ACTIVE:
21 last_flame = now
22 flame = last_flame is not None and time.ticks_diff(now, last_flame) < HOLD_MS
23 if not flame:
24 flame_start = now # alev yoksa sayaç sıfırlanır / timer resets without flame
25 alarm = time.ticks_diff(now, flame_start) >= CONFIRM_MS
26
27 led.value(1 if alarm else 0)
28 buzzer.value(1 if alarm and (now // 200) % 2 else 0) # kesik siren / beeping siren
29
30 if time.ticks_diff(now, last_print) >= 500: # yarım saniyede bir yaz / print every half second
31 last_print = now
32 print(flame_ao.read_u16(), "ALARM" if alarm else "-")
33 time.sleep_ms(10)Thonnyでコードを動かすと、ShellウィンドウにAOの値と警報の状態が表示されます。Viewメニューから「Plotter」(プロッター)を開けば、値をグラフで追えます。Picoに電源を入れたときにプログラムを自動で起動させるには、ファイルをmain.pyとしてPicoに保存します。時間の計算にtime.ticks_diff()を使うのは重要です。ticks_ms()のカウンターはある値を超えると一周して戻るので、単純な引き算ではその瞬間に間違った結果になるからです。
炎センサーのプロジェクトアイデア
- キッチンの火災警報:コンロに向けた炎センサーとMQ-2で、上の2段階のロジックを使った警報を作ります。
- コンロの消し忘れリマインダー:炎が30分以上続けて見えたら、ブザーを鳴らしてスマホにメッセージを送ります。
- 消火ロボット:5チャンネルのセンサーで炎の方向を探し、L298NとDCモーターでロボットをそちらへ走らせ、ミニウォーターポンプでろうそくの火を消します。
- 炎の方向スキャナー:1個のセンサーをサーボモーターに載せて0〜180度を振り、信号が最も強い角度を表示します。
- スマートホームの安全パネル:炎、ガス、人感センサーを1つの基板にまとめます。詳しくはArduinoのスマートホームプロジェクトを参照してください。
- 作業場の見張り:はんだごてステーションの近くに思わぬ炎が出たら、リレーで警告灯を点けます。
- ろうそく消しコンテスト:学校のロボット競技会のように、迷路の中のろうそくをできるだけ速く見つけて消すロボットを作ります。始めるのに必要なのは炎センサーだけです。
よくある間違いと解決法
| 症状 | 考えられる原因 | 解決法 |
|---|---|---|
| DOのLEDが常に点いていて、警報が止まらない | しきい値が敏感すぎる、またはセンサーが太陽や白熱電球に向いている | 炎がない状態でLEDが消えるまでポテンショメーターを回し、センサーを光源からそらす |
| ライターを見せても何も起きない | しきい値が高すぎる、または炎が60度の視野の外か遠すぎる | ポテンショメーターを敏感な方向へ数回転回し、炎をセンサーの真正面に近づける |
| 炎がないと警報が鳴り、炎があると止まる | モジュールのDO出力がアクティブHIGH(例:KY-026) | コードのFLAME_ACTIVEをHIGHに変える |
| 炎の前でDOのLEDがちらつく | コンパレーターがしきい値ぎりぎりで、炎の揺らぎが出力を切り替えている | コードで保持時間(HOLD_MS)を使うか、しきい値を少し敏感にする |
| アナログ値がまったく変わらない | 3ピンのモジュールにはAOがない、またはDOピンをアナログ入力につないでいる | 4ピンのモジュールを使い、AOピンをアナログ入力につなぐ |
| リモコンを押すと警報が鳴る | テレビのリモコンの940 nmの赤外線LED | 少なくとも1〜2秒の時間による確認のあとでだけ警報を出す |
| 昼間の窓際で誤報が出る、または炎がまったく見えない | 日光がセンサーを反応させるか飽和させている | センサーを太陽から離して置き、黒いチューブを使い、揺らぎの解析を追加する |
| ESP32でWi-Fiが始まるとAOの値がおかしくなる | AOをADC2のピンにつないでいる | AOをGPIO 32〜39のADC1のピンにつなぐ |
| ESP32やPicoでブザーの音がとても弱い | 5 Vのアクティブブザーを3.3 Vのピンで駆動している | 3.3 Vのブザーを使うか、トランジスタを通して5 Vでブザーを駆動する |
炎センサーは安価ですが、赤外線の物理、コンパレーター回路、誤報との闘い方を一度に教えてくれる学びの多い部品です。シンプルなDOの警報から始め、アナログでの読み取り、揺らぎの解析、MQ-2でシステムを少しずつ強化しましょう。より本格的なセキュリティシステムについてはArduinoの警報システムを見てください。BlueGraysアプリには、炎センサー、炎・ガスセンサーの警報、ESP32のMQ-2煙検知器、Telegram通知付きのESP32警報システムのプロジェクトが、ステップごとの回路図とすぐ使えるコード付きで揃っています。
よくある質問
炎センサーはどのくらいの距離まで炎を検知できる?
多くのモジュールは標準的なライターの炎を約80 cmまで検知します。ろうそくの炎では距離が短くなります。光の強さは距離の2乗に反比例して弱まるので、4倍強い炎なら2倍の距離から見え、紙や木の大きな火なら数メートル先からでも検知できます。ポテンショメーターの設定と周囲の明るさも検知距離に直接影響します。
炎センサーは日光の影響を受ける?
はい。センサーが反応する760〜1100 nmの帯域では日光がとても強く、直射日光やガラスの反射は誤報を起こし、センサーを目くらましにして本物の炎を見逃させることもあります。センサーを太陽から離して置き、黒いチューブで視野を狭め、ソフトウェアで時間による確認と揺らぎの解析を使ってください。
炎センサーは炎を検知するとLOWとHIGHのどちらを出力する?
よくある青いLM393モジュールでは、DOピンは普段HIGHで、炎を見るとLOWになり、DOのLEDが点きます。KY-026のような一部の基板では、炎を検知すると出力がHIGHになります。自分のモジュールをライターでテストし、シリアルモニタに表示された値に合わせてコードの比較を設定してください。
炎センサーは3.3 Vで動く?
はい、これらのモジュールは3.3〜5 Vで動きます。ESP32やRaspberry Pi Picoでは、モジュールを3V3ピンから給電すればDOとAOの出力が3.3 V以下に保たれるので、そのままピンにつなげます。モジュールを5 Vで給電する場合、これらのボードでは出力に分圧回路が必要です。
炎センサーの感度はどう調整する?
炎がない状態で、基板の青い多回転ポテンショメーターをDOのLEDがちょうど消える位置まで回し、さらに鈍感な方向へ半回転回します。次に、検知したい最も遠い距離からライターでテストします。ポテンショメーターが変えるのはDOのしきい値だけで、AOのしきい値はコードで自分で設定します。
炎センサーは火災検知器の代わりになる?
いいえ。ホビー用の炎センサーは日光、白熱電球、リモコンにだまされることがあり、視野の外の火災や炎の出ていないくすぶりは見えず、どの規格の試験も受けていません。命を守るには、EN 14604に適合した煙式の警報器(日本では住宅用火災警報器)を使い、自作の回路は補助の警報システムとして扱ってください。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
ブザーの使い方
呼び鈴やアラームがどうやって音を出すのか、気になったことはありませんか?Arduinoが小さなブザーを短い間隔で鳴らし、音を出す、止める、を繰り返します。電気で音が生まれる仕組みを学び、メロディーを演奏するプロジェクトへの第一歩を踏み出せます。

LDRで光の強さを測定
部屋の明るさを数値で見てみませんか?光に反応する小さな部品、LDRで周囲の光を測定し、Arduinoが0.5秒ごとにその値をコンピューターの画面に表示します。手で覆うと数値が変わる様子を確認できます。自動で点灯する常夜灯などのプロジェクトへの第一歩です。

炎センサーの使い方
キッチンや作業場で、人より先に火に気づく小さな見張り役です。炎センサーは火が放つ目に見えない光を検出します。異常がないときは緑色のLEDが点灯し、炎を検出すると赤色のLEDが点灯してブザーが鳴ります。このシンプルな回路で火災感知器の基本的な仕組みを学べますが、実際の感知器の代わりにはなりません。

炎・ガスセンサーで警報
ガスが漏れたり炎が出たりすると、すぐに知らせる小さな見張り役です。ガスセンサーは空気中のガスを、炎センサーは火の光を監視します。異常がないときは緑色のLEDが点灯し、どちらかが危険を検出すると、赤色のLEDの点滅とブザーの断続音で警報を出します。危険がなくなるまで警報は続きます。

ガスセンサーの使い方
キッチンのガス漏れに人より先に気づく小さな見張り役です。ガスセンサーは空気中のガスを常に監視します。異常がないときは緑色のLEDが点灯し、ガス濃度が一定の値を超えると赤色のLEDが点灯してブザーが鳴ります。家庭用ガス警報器の仕組みを実際に確かめられます。

ESP32 MQ-2ガス・煙検知アラーム
このプロジェクトでは、MQ-2ガスセンサーモジュールで可燃性ガス(LPG、メタン、プロパン)と煙を検知し、パッシブブザーでサイレンのような警報音を鳴らします。センサーのしきい値判定用デジタル出力(DO)をESP32のGPIO 4ピンに接続します。ガスや煙がしきい値を超えるとDOがLOWになり、アラームが作動します。感度はモジュール上のポテンショメーターで調整します。パッシブブザーをtone()で駆動し、2つの周波数を交互に切り替えてサイレン音を作ります。

ESP32動体検知アラーム(PIR + ブザー + LED + Telegram)
PIRセンサーが動きを検知するとブザーとLEDを作動させ、同時にTelegramで持ち主に即座に通知するスマート防犯アラームです。アラームが繰り返される頻度を抑えるため、30秒のクールダウン(待ち時間)を設けています。この仕組みはPIRの検知が正しいかを確認したり、誤検知をなくしたりするものではありません。家の防犯、ガレージの監視、倉庫や作業部屋などに使えます。ESP32-CAMなら写真付きの通知を追加でき、通常のESP32 DevKit V1ではテキストで通知します。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

