Arduinoでスマートホームを作る:模型の家、センサー、コード、アプリ
Arduinoのスマートホームプロジェクトをステップごとに作ります。模型の家の設計、PIRで点く照明、ガス・火災警報、RFIDのドア、リレーのランプ、ESP32でのスマホ操作、完全なコードまで紹介します。
Arduinoのスマートホームプロジェクトでは、模型の家の照明、ドア、窓、警報をセンサーで自動的に動くシステムにします。希望すれば、すべてをスマホから操作することもできます。学校のプロジェクトとして特に人気があるのには理由があります。動きを検知すると点く廊下の照明、ガス漏れで鳴る警報、暑くなると回るファン、カードで開くドアを組み合わせると、小さいながらも実際に動くスマートホームシステムになります。Arduino UnoかESP32、10個ほどのモジュール、そして週末があれば作れます。
このガイドでは、まず模型の家の設計から始めます。どのモジュールをどの部屋に置くか、予算をどこに使うか、電源をどう計画するか、ピンをどう割り当てるかです。次に各モジュールを1つずつ組んでテストし、すべてをdelay()を使わずに同時に動かす完全なArduinoスケッチを書きます。最後のセクションでは、ESP32のWebサーバーやBlynkでスマホから家を操作する方法、よくある間違いの直し方、学校の発表で審査員にアピールする方法を学びます。






スマートホームシステムはどう動く?
どのスマートホームも、同じ3つの要素のループで動いています。センサーは周囲を測ります。誰かが動いているか、暗いか、どれくらい暖かいか、キッチンにガスや炎はないか、ドアにどのカードがかざされたか。コントローラー、つまりArduinoやESP32は、この情報をルールと照らし合わせます。「暗くて動きがあれば照明を点ける」「温度が28 °Cを超えたらファンを回す」といったルールです。アクチュエーターは決定を実行します。リレーがランプとファンを切り替え、サーボがドアと窓を動かし、ブザーが鳴り、ディスプレイが状態を表示します。スマホでの操作は、このループに入力を1つ加えるだけです。この場合はルールの代わりに画面のタップが決定を下します。
| 部屋・場所 | モジュール | 役割 |
|---|---|---|
| 玄関ドア | RC522 RFID + SG90サーボ | 登録済みのカードを読むとドアを開け、5秒後に閉める |
| 廊下・リビング | HC-SR501 PIR + LDR + リレー | 暗い中で動きを検知するとランプを点け、動きがなくなって20秒後に消す |
| キッチン | MQ-2ガスセンサー + 炎センサー + ブザー | ガスか炎を検知するとピーピー鳴る警報を出す |
| 窓 | SG90サーボ | ガスを検知すると換気のために窓を開ける |
| 寝室 | DHT11 + リレー + ファン | 28 °Cでファンを回し、26 °Cで止める |
| 機械室 | Arduino、I2C LCD、電源 | 温度、湿度、ガスの値、状態メッセージを表示する |
模型の家を設計する
模型は電子回路に合わせて設計してください。あとから電子回路を押し込もうとしないことが大切です。2〜3部屋と玄関なら、50×40 cmほどの土台で十分です。壁には5 mmのスチレンボードや発泡塩ビ板(Forexなど)がカッターで簡単に切れて便利です。3〜4 mmのMDFはより頑丈ですが、のこぎりかレーザーカッターが必要です。縮尺は1:20から始めるとよいでしょう。2.6 mの天井は13 cmの壁に、90 cmのドアは幅4.5 cmになります。屋根は取り外せるようにするか、最初から付けないでおきます。審査員は部屋の中を見たがるからです。
- 機械室または二重床:Arduino、ブレッドボード、リレーモジュール、電源の入口は、家の裏側の区画か、高さ3〜4 cmの二重床の下にまとめます。透明なアクリル板でふたをすれば、回路を守りながら見せることもできます。
- 配線の通り道:各センサーの下に6〜8 mmの穴を開け、ケーブルを土台の下に通して機械室まで引き込みます。短い線をつなぎ合わせるより、最初から十分な長さのセンサー用ケーブルを選んでください。
- ドア:小さな蝶番かテープでドアを壁に固定し、ゼムクリップを曲げた針金でSG90のホーンとドアをつなぎます。RC522は外壁の内側に貼ります。リーダーは通常、5 mmの板越しでもカードを読めます。
- キッチン:実際の家では、都市ガス(メタン)は空気より軽いので検知器を天井近くに、LPG(プロパンガス)は空気より重いので床近くに付けます。模型なら天井近くで問題ありません。炎センサーはコンロに向けます。
- そのほかのセンサー:PIRは玄関への通路を横から見通せる角に、LDRはランプの光が届かない外壁か窓に、DHT11はランプやMQ-2の熱を持った本体から離して置きます。
部品リストと費用
| 部品 | 数量 | 用途 | 価格帯 |
|---|---|---|---|
| Arduino Uno R3(互換品でも可)またはESP32 DevKit | 1 | 家の頭脳 | 中 |
| ブレッドボードとジャンパーワイヤーのセット(オス-オス、オス-メス) | 1セット | 配線 | 低 |
| HC-SR501 PIR人感センサー | 1 | 廊下の照明 | 低 |
| LDRと10 kΩの抵抗 | 1 + 1 | 暗さの検知 | とても低い |
| MQ-2ガスセンサーモジュール | 1 | ガス・煙の警報 | 低 |
| 炎センサーモジュール(LM393) | 1 | 火災警報 | 低 |
| DHT11温湿度モジュール | 1 | ファンの制御 | 低 |
| RC522 RFIDモジュール(カードとキーホルダー付き) | 1 | カードで開くドア | 低 |
| SG90サーボモーター | 2 | ドアと窓 | 低 |
| 2チャンネル5 Vリレーモジュール | 1 | ランプとファン | 低 |
| アクティブブザー | 1 | 警報音 | とても低い |
| I2C 16x2 LCDディスプレイ | 1 | 状態の表示 | 低〜中 |
| 5 Vミニファンと5 V LEDランプまたはLEDテープ | 1 + 1 | リレーで動かす負荷 | 低 |
| 5 V 2〜3 AのACアダプターとDCジャックまたは端子台 | 1 | サーボ、リレー、センサーの電源 | 中 |
| スチレンボードまたは発泡塩ビ板、カッター、ホットボンド | – | 家の本体 | 中 |
価格は販売店や時期によってよく変わるので、金額は載せていません。表は、各部品が予算の中でどれくらいの比重を占めるかを示しています。センサーやモジュールは1つずつならどれも安く、予算の大半はボード、LCD、ACアダプター、模型の材料に使われます。Arduinoのスターターキットを持っていれば、ボード、ブレッドボード、配線、LDR、ブザー、そして多くの場合サーボとLCDもすでに揃っています。予算が厳しければ、LCDと2つ目のサーボを省けます。ディスプレイの代わりにシリアルモニタを使い、窓はあとの段階に回しましょう。DHT11の代わりにより正確なDHT22を使うと少し高くなりますが、発表での測定値の説得力が増します。
電源の計画:なぜ別の5 Vアダプターが必要?
スマートホームの模型で「ときどきリセットされる」「サーボが震える」「LCDに変な文字が出る」といった不具合は、ほぼすべて電源が原因です。パソコンのUSBポートが供給できるのは約500 mAで、ボードをUSBで動かしているときはUnoの5Vピンもこの上限の内側にあります。下の値を合計すると、2つのサーボが同時に動いたときに合計電流が簡単に1 Aを超えることがわかります。
| 部品 | 5 Vでのおおよその電流 |
|---|---|
| Arduino Uno | 45〜50 mA |
| MQ-2(ヒーターは常時オン) | 約150 mA |
| リレーモジュール(1チャンネルあたり、リレー動作時) | 70〜80 mA |
| SG90サーボ(動作中・負荷時) | 100〜250 mA / 最大0.5〜0.7 A |
| I2C LCD(バックライト点灯) | 20〜30 mA |
| RC522(3.3 Vピンから) | 13〜26 mA |
| アクティブブザー、DHT11、PIR、炎センサー | 合計30〜40 mA |
| 5 VミニファンとLEDランプ | モデルにより100〜400 mA |
ピンの計画:Arduino UnoとESP32
これだけのモジュールをつなぐとUnoのピンはほぼ使い切るので、最初から計画が必要です。RC522はハードウェアSPIを使うため、MOSI、MISO、SCKの線はD11、D12、D13に決まっています。LCDはI2CでA4とA5だけを使います。そのためパラレル接続のLCDではなく、I2Cモジュール付きのディスプレイを選びました。アナログピンは必要に応じてデジタルピンとしても使えるので、窓のサーボをA2につなげます。D0とD1は空けておいてください。このシリアル線は、コードを書き込むときやシリアルモニタでカード番号を読むときに使います。ボードのピンはピン配置ツールで確認できます。
| モジュールのピン | Arduino Uno | ESP32 |
|---|---|---|
| PIR OUT | D2 | GPIO 16 |
| 炎センサーDO | D3 | GPIO 17 |
| DHT11 DATA | D4 | GPIO 13 |
| アクティブブザー(+) | D5 | GPIO 25 |
| ドアのサーボ(信号) | D6 | GPIO 32 |
| リレーIN1(ランプ) | D7 | GPIO 26 |
| リレーIN2(ファン) | D8 | GPIO 27 |
| RC522 RST | D9 | GPIO 4 |
| RC522 SDA (SS) | D10 | GPIO 5 |
| RC522 MOSI / MISO / SCK | D11 / D12 / D13 | GPIO 23 / 19 / 18 |
| MQ-2 AO | A0 | GPIO 34(分圧回路を通す) |
| LDR分圧回路の中点 | A1 | GPIO 35(分圧回路は3V3から給電) |
| 窓のサーボ(信号) | A2 | GPIO 33 |
| I2C LCD SDA / SCL | A4 / A5 | GPIO 21 / 22 |
ステップごとの組み立て
- 模型の本体を作り、モジュールの位置とケーブルの穴を準備します。電子回路の取り付けは最後です。
- 電源ラインを作ります。5 Vアダプターをブレッドボードの電源レールにつなぎ、アダプターのGNDをArduinoのGNDにつないで、テスターで5 Vを確認します。
- I2C LCDをつなぎ、何か文字を表示します。ディスプレイのアドレスがわからない場合は、I2C LCDの記事にあるスキャナーのスケッチを使ってください。
- センサーを1つずつ追加します。DHT11、LDR、PIR、MQ-2、炎センサーの順です。それぞれ短いテスト用スケッチでシリアルモニタに表示し、きれいな空気の中、暗いとき、明るいときの値をメモします。
- アクチュエーターを追加します。まずリレーを負荷なしでテストし、カチッという音とLEDを確認します。次にサーボを外部電源でテストし、最後にRC522をつなぎます。カードを読み取って番号をメモします。
- メインのスケッチを書き込み、メモをもとにGAS_LIMIT、DARK_LIMIT、FAN_ONのしきい値を設定します。
- ホットボンドとラベルで配線を固定し、システムを30分ほど動かしたままにして、リセット、過熱、誤報がないか観察します。
PIRとLDRで自動点灯する照明
HC-SR501 PIRセンサーは人体が出す赤外線の変化を検知し、動きを見つけると出力をHIGHにします。PIRだけだと昼間もランプが点いてしまうので、ルールには2つの条件を設けます。動きがあり、かつ暗ければ点けるというものです。LDRは10 kΩの抵抗と分圧回路にし(5V → LDR → A1 → 10 kΩ → GND)、暗くなるほどA1の値が下がります。コードは最後に動きを検知した時刻を記録し、新しい動きがないまま20秒たつとランプを消します。誰かが歩き回っている間は、そのたびにタイマーがやり直しになります。センサーのジャンパーはH(リトリガー)に、Txのつまみは最短時間に設定し、時間の管理はコードに任せましょう。詳しくはPIR人感センサーとLDR光センサーの記事を参照してください。
ガス・火災警報
MQ-2の内部には、ヒーターで高温に保たれた酸化スズの層があります。LPG、メタン、ブタン、水素、アルコール、煙がこの層の抵抗を変えます。モジュールのAOピンは、ガスの濃度が上がるほど高い電圧を出します。センサーは電源を入れるたびに温まる必要があるので、コードは最初の60秒間ガスの警報を無視します。炎センサーは炎が出す760〜1100 nmの赤外線を検知します。多くのモジュールでは炎を見つけるとDO出力がLOWになり、基板上の2つ目のLEDが点きますが、必ず離れた位置でライターを使って自分のモジュールで確かめてください。ガスを検知すると警報が鳴り、換気のために窓が開きます。炎を検知したときは窓を閉めたままにします。新鮮な空気は火を大きくするからです。しきい値の設定やほかのMQセンサーについてはMQ-2ガスセンサーの記事で解説しています。
温度、湿度、自動で回るファン
DHT11は温度を0〜50 °Cの範囲で±2 °Cの精度で、湿度を20〜90 %の範囲で±5 %の精度で測ります。読み取りは最大で1秒に1回までなので、コードは2秒ごとに読みます。ファンには1つのしきい値ではなくヒステリシスを使います。ファンは28 °Cで回り始めますが、止まるのは温度が26 °Cまで下がったときです。しきい値が1つだと、温度が27〜28 °Cの間で揺れるたびにリレーがカチカチ鳴り続け、リレーもファンも傷みます。センサーが読み取りに失敗して「nan」を返した場合、コードはその測定を飛ばし、最後の有効な値を保ちます。より正確に測りたい場合はDHT22も同じコードで使えます。DHT11をDHT22に書き換えるだけです。詳しくはDHT11温湿度センサーの記事を参照してください。
サーボで動くRFIDカードのドア
RC522は13.56 MHzのMIFAREカードやキーホルダーを3〜5 cmの距離から読み取ります。カードにはそれぞれ固有の識別番号(UID)があり、コードは読み取ったUIDを登録済みカードのリストと比べます。一致すればサーボがドアを90度まで開け、短いビープ音が鳴り、LCDに「Hos geldin!」(トルコ語で「ようこそ!」)と表示されます。5秒後にドアは自動で閉まります。知らないカードには長いビープ音が鳴ります。自分のカードのUIDを調べるには、コードを書き込んでシリアルモニタを9600ボーで開き、カードをかざします。「UID: A1 B2 C3 D4」のような行が表示されます。このバイトを0xA1の形でCARDSリストに書き込みます。1桁で表示されたバイトには先頭に0を付けてください(5ではなく0x05)。UIDだけを確認する錠は学習には十分ですが、本当の意味でのセキュリティはありません。理由はRFID RC522の記事で説明しています。
窓とカーテン
窓には2つ目のSG90で十分です。蝶番付きの小さなアクリルの窓をサーボのホーンと針金でつなぎ、0度を閉、90度を開とします。attach()の前にwrite(0)を呼ぶと、電源投入時にサーボが90度へ跳ねて窓をバタンと開けるのを防げます。カーテンにはもっと大きな動きが必要です。布を細い棒に巻き付け、その棒を回します。これにはULN2003ドライバと28BYJ-48ステッピングモーターが静かで正確ですが、ピンが4本必要です。Unoにはもう空きがないので、カーテンはESP32版かArduino Megaに回すのが賢明です。どうしても1本で済ませたいならFS90R連続回転サーボも使えますが、自分の位置がわからないため、時間でカーテンを開け閉めします。雨センサーを追加して、雨が降ったら窓を自動で閉めるのもよい拡張です。
リレーで動かすランプとファン

Arduinoのピンが安全に供給できる電流は約20 mA(絶対最大定格は40 mA)だけですが、ファンやランプにはずっと多くの電流が必要です。そこで間にリレーモジュールを入れます。INピンの信号が電磁石を動かし、別の回路の接点を閉じます。負荷はCOMとNOの端子につないでください。そうすればArduinoが止まっている間や再起動中にランプが消えたままになります。こうしたモジュールの多くはアクティブLOWで、LOWを書き込むとリレーがオンになります。コードのRELAY_ONとRELAY_OFFの定数はそのためのものです。setup()ではピンを出力にする前にHIGHを書き込むので、Arduinoの起動時にリレーが一瞬でもカチッとオンになることはありません。ファンはオン・オフを繰り返しすぎないようにヒステリシスを使い、ランプはPIRのタイマーが切れるまで点いたままにします。
完全なスケッチ(Arduino Uno)
スマートホームのコードで最も大切なルールは、どのモジュールもほかのモジュールを待たないことです。delay(5000)でドアを5秒間開けたままにすると、その間はガス警報が働かず、PIRも読まれません。そのため各モジュールをそれぞれの関数にまとめ、loop()がそれらの関数を1秒間に何百回も順番に呼び出します。時間に関わる処理はすべてmillis()で測り、各モジュールの状態はlightOn、fanOn、doorOpen、alarmOnといった変数に保持します。設定はすべて定数としてコードの先頭にまとめてあるので、しきい値を変えるためにコードを探し回る必要はありません。この考え方の背景はmillis()とdelay()の違いの記事で解説しています。コードをコンパイルするには、ライブラリマネージャからMFRC522ライブラリ、AdafruitのDHT sensor library、Frank de Brabander氏のLiquidCrystal I2Cをインストールし、依存ライブラリのインストールを求められたらすべてインストールします。Servo、SPI、WireはArduino IDEに付属しています。
1#include <SPI.h>
2#include <MFRC522.h>
3#include <Servo.h>
4#include <DHT.h>
5#include <Wire.h>
6#include <LiquidCrystal_I2C.h>
7
8// Pinler / Pins
9const byte PIR_PIN = 2, FLAME_PIN = 3, DHT_PIN = 4, BUZZER_PIN = 5, DOOR_PIN = 6;
10const byte LAMP_RELAY = 7, FAN_RELAY = 8, RST_PIN = 9, SS_PIN = 10;
11const byte GAS_PIN = A0, LDR_PIN = A1, WINDOW_PIN = A2;
12
13// Ayarlar / Settings
14const byte RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
15const byte FLAME_ACTIVE = LOW; // alev görünce DO LOW olur / DO goes LOW on flame
16const int GAS_LIMIT = 400; // temiz havaya göre ayarla / tune in clean air
17const int DARK_LIMIT = 300; // bunun altı karanlık / below this is dark
18const float FAN_ON = 28.0, FAN_OFF = 26.0; // fan histerezisi (°C) / fan hysteresis (°C)
19const unsigned long LIGHT_MS = 20000; // son hareketten sonra ışık / light after last motion
20const unsigned long DOOR_MS = 5000; // kapı açık kalma süresi / door open time
21const unsigned long WARMUP_MS = 60000; // MQ-2 ve PIR ısınması / MQ-2 and PIR warm-up
22
23const byte CARDS[][4] = { // yetkili kart UID'leri / authorised card UIDs
24 {0xA1, 0xB2, 0xC3, 0xD4},
25 {0x12, 0x34, 0x56, 0x78}
26};
27
28MFRC522 rfid(SS_PIN, RST_PIN);
29Servo door, window;
30DHT dht(DHT_PIN, DHT11);
31LiquidCrystal_I2C lcd(0x27, 16, 2);
32
33float tempC = 0, hum = 0;
34int gas = 0;
35bool lightOn = false, fanOn = false, doorOpen = false, alarmOn = false;
36unsigned long lastMotion = 0, doorAt = 0, lastDht = 0, lastLcd = 0, beepUntil = 0;
37
38void setup() {
39 Serial.begin(9600);
40 digitalWrite(LAMP_RELAY, RELAY_OFF); pinMode(LAMP_RELAY, OUTPUT); // açılışta kapalı / off at boot
41 digitalWrite(FAN_RELAY, RELAY_OFF); pinMode(FAN_RELAY, OUTPUT);
42 pinMode(PIR_PIN, INPUT);
43 pinMode(FLAME_PIN, INPUT);
44 pinMode(BUZZER_PIN, OUTPUT);
45 door.write(0); door.attach(DOOR_PIN); // önce açı, sonra attach / angle first, then attach
46 window.write(0); window.attach(WINDOW_PIN);
47 dht.begin();
48 SPI.begin();
49 rfid.PCD_Init();
50 lcd.init();
51 lcd.backlight();
52 lcd.print(F("BlueGrays Ev"));
53}
54
55void loop() {
56 unsigned long now = millis();
57 handleLight(now);
58 handleClimate(now);
59 handleSafety(now);
60 handleDoor(now);
61 bool beep = (long)(beepUntil - now) > 0;
62 digitalWrite(BUZZER_PIN, beep || (alarmOn && (now / 250) % 2)); // alarmda kesik bip / pulsed beep on alarm
63 updateLcd(now);
64}
65
66// 1) Hareketle ışık / motion light
67void handleLight(unsigned long now) {
68 if (now < WARMUP_MS) return; // PIR ısınıyor / PIR warming up
69 if (digitalRead(PIR_PIN) == HIGH) {
70 lastMotion = now;
71 if (analogRead(LDR_PIN) < DARK_LIMIT) lightOn = true; // yalnız karanlıkta yak / only when dark
72 }
73 if (lightOn && now - lastMotion > LIGHT_MS) lightOn = false;
74 digitalWrite(LAMP_RELAY, lightOn ? RELAY_ON : RELAY_OFF);
75}
76
77// 2) Sıcaklık ve fan / temperature and fan
78void handleClimate(unsigned long now) {
79 if (now - lastDht < 2000) return; // DHT11'i sık okuma / don't read DHT11 too often
80 lastDht = now;
81 float t = dht.readTemperature();
82 float h = dht.readHumidity();
83 if (isnan(t) || isnan(h)) return; // okuma hatası / read error
84 tempC = t;
85 hum = h;
86 if (tempC >= FAN_ON) fanOn = true;
87 if (tempC <= FAN_OFF) fanOn = false;
88 digitalWrite(FAN_RELAY, fanOn ? RELAY_ON : RELAY_OFF);
89}
90
91// 3) Gaz ve yangın / gas and fire
92void handleSafety(unsigned long now) {
93 gas = analogRead(GAS_PIN);
94 bool gasAlarm = now > WARMUP_MS && gas > GAS_LIMIT;
95 bool fire = digitalRead(FLAME_PIN) == FLAME_ACTIVE;
96 alarmOn = gasAlarm || fire;
97 window.write(gasAlarm && !fire ? 90 : 0); // gazda aç, yangında kapalı tut / open on gas, shut on fire
98}
99
100// 4) RFID kapı / RFID door
101void handleDoor(unsigned long now) {
102 if (doorOpen && now - doorAt > DOOR_MS) {
103 door.write(0); // süre dolunca kapat / close when time is up
104 doorOpen = false;
105 }
106 if (!rfid.PICC_IsNewCardPresent() || !rfid.PICC_ReadCardSerial()) return;
107 Serial.print(F("UID:")); // yeni kartın UID'sini öğren / learn a new card's UID
108 for (byte i = 0; i < rfid.uid.size; i++) {
109 Serial.print(' ');
110 Serial.print(rfid.uid.uidByte[i], HEX);
111 }
112 Serial.println();
113 if (isAllowed(rfid.uid.uidByte)) {
114 door.write(90);
115 doorOpen = true;
116 doorAt = now;
117 beepUntil = now + 100; // kısa bip: hoş geldin / short beep: welcome
118 } else {
119 beepUntil = now + 800; // uzun bip: yetkisiz kart / long beep: unknown card
120 }
121 rfid.PICC_HaltA();
122}
123
124bool isAllowed(const byte *uid) {
125 for (byte i = 0; i < sizeof(CARDS) / 4; i++) {
126 if (memcmp(uid, CARDS[i], 4) == 0) return true;
127 }
128 return false;
129}
130
131// 5) Ekran / display
132void updateLcd(unsigned long now) {
133 if (now - lastLcd < 1000) return;
134 lastLcd = now;
135 lcd.setCursor(0, 0);
136 lcd.print(F("T:"));
137 lcd.print(tempC, 0);
138 lcd.print(F("C Nem:%"));
139 lcd.print(hum, 0);
140 lcd.print(F(" "));
141 lcd.setCursor(0, 1);
142 if (alarmOn) {
143 lcd.print(F("!! ALARM !! "));
144 } else if (doorOpen) {
145 lcd.print(F("Hos geldin! "));
146 } else {
147 lcd.print(F("Gaz:"));
148 lcd.print(gas);
149 lcd.print(F(" "));
150 }
151}| 関数 | 役割 | 実行頻度 |
|---|---|---|
| handleLight() | PIRとLDRに応じてランプのリレーをオンにし、動きがなくなって20秒後にオフにする | 毎回のloop(最初の60秒を除く) |
| handleClimate() | DHT11を読み、28/26 °Cのヒステリシスでファンを動かす | 2秒ごと |
| handleSafety() | MQ-2と炎センサーを読み、警報と窓を管理する | 毎回のloop |
| handleDoor() | カードを読み、UIDをシリアルモニタに表示し、ドアを開け閉めする | 毎回のloop |
| updateLcd() | 温度、湿度、ガスの値、または状態メッセージを表示する | 1秒に1回 |
| loop()内のブザーの行 | カードのビープ音と断続的な警報音を出す | 毎回のloop |
このコードをコンパイルすると、Unoのプログラムメモリの約38 %、RAMの約28 %を使います。メモリにはまだ余裕がありますが、ピンで空いているのはA3だけです。A3にはボタンや雨センサーをつなげます。それ以上を追加するにはArduino MegaかESP32に移る必要があります。固定の文字列をF()で囲むのが、RAMの使用量を抑える最も簡単な方法です。
スマホで操作する:ESP32のWebサーバー

UnoにはWi-Fiがありません。スマホからスマートホームを操作する最も簡単な方法は、頭脳をESP32に替えることです。下のコードはESP32を家のネットワークにつなぎ、小さなWebサーバーを起動します。スマホを同じWi-Fiネットワークにつなぎ、シリアルモニタに表示されたIPアドレスをブラウザで開くと、ランプ、ファン、ドア、窓の4つの大きなボタンと、現在の温度、湿度、ガスの値が表示されます。ページは10秒ごとに自動で更新されます。各ボタンは、どの機器を切り替えるかを伝える短いリクエストをESP32に送ります。ESP32は状態を変え、メインページにリダイレクトします。サーボ用にライブラリマネージャからESP32Servoライブラリをインストールしてください。標準のServoライブラリはESP32では動かないからです。
1#include <WiFi.h>
2#include <WebServer.h>
3#include <ESP32Servo.h>
4#include <DHT.h>
5
6const char* ssid = "AgAdi"; // Wi-Fi adın / your Wi-Fi name
7const char* password = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
8
9const int LAMP_RELAY = 26, FAN_RELAY = 27;
10const int DOOR_PIN = 32, WINDOW_PIN = 33;
11const int DHT_PIN = 13, GAS_PIN = 34; // GPIO 34: ADC1, bölücüyle / ADC1, through a divider
12const int RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
13
14WebServer server(80);
15Servo door, window;
16DHT dht(DHT_PIN, DHT11);
17
18bool lamp = false, fan = false, doorOpen = false, windowOpen = false;
19float tempC = NAN, hum = NAN;
20unsigned long lastDht = 0;
21
22void apply() { // durumu donanıma uygula / push the state to the hardware
23 digitalWrite(LAMP_RELAY, lamp ? RELAY_ON : RELAY_OFF);
24 digitalWrite(FAN_RELAY, fan ? RELAY_ON : RELAY_OFF);
25 door.write(doorOpen ? 90 : 0);
26 window.write(windowOpen ? 90 : 0);
27}
28
29String button(const char* id, const char* label, bool on) {
30 return String("<a class='b ") + (on ? "on" : "off") + "' href='/set?d=" + id +
31 "&v=" + (on ? "0" : "1") + "'>" + label + (on ? ": ON" : ": OFF") + "</a>";
32}
33
34void handleRoot() {
35 String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>"
36 "<meta name='viewport' content='width=device-width,initial-scale=1'>"
37 "<meta http-equiv='refresh' content='10'><title>BlueGrays Ev</title>"
38 "<style>body{font-family:sans-serif;text-align:center}"
39 ".b{display:block;margin:12px auto;padding:18px;max-width:320px;border-radius:10px;"
40 "color:#fff;text-decoration:none;font-size:20px}"
41 ".on{background:#2e7d32}.off{background:#607d8b}</style></head><body>"
42 "<h2>BlueGrays Akıllı Ev</h2>";
43 html += "<p>" + String(tempC, 1) + " °C | %" + String(hum, 0) +
44 " | Gaz/Gas: " + String(analogRead(GAS_PIN)) + "</p>";
45 html += button("lamp", "Lamba / Lamp", lamp);
46 html += button("fan", "Fan", fan);
47 html += button("door", "Kapı / Door", doorOpen);
48 html += button("window", "Pencere / Window", windowOpen);
49 html += "</body></html>";
50 server.send(200, "text/html", html);
51}
52
53void handleSet() { // /set?d=lamp&v=1
54 String d = server.arg("d");
55 bool v = server.arg("v") == "1";
56 if (d == "lamp") lamp = v;
57 else if (d == "fan") fan = v;
58 else if (d == "door") doorOpen = v;
59 else if (d == "window") windowOpen = v;
60 apply();
61 server.sendHeader("Location", "/"); // ana sayfaya dön / back to the main page
62 server.send(303);
63}
64
65void setup() {
66 Serial.begin(115200);
67 door.attach(DOOR_PIN, 500, 2400);
68 window.attach(WINDOW_PIN, 500, 2400);
69 pinMode(LAMP_RELAY, OUTPUT);
70 pinMode(FAN_RELAY, OUTPUT);
71 apply(); // her şey kapalı başlar / everything starts off
72 dht.begin();
73 WiFi.begin(ssid, password);
74 while (WiFi.status() != WL_CONNECTED) {
75 delay(500);
76 Serial.print('.');
77 }
78 Serial.println();
79 Serial.println(WiFi.localIP()); // bu adresi telefonda aç / open this address on your phone
80 server.on("/", handleRoot);
81 server.on("/set", handleSet);
82 server.begin();
83}
84
85void loop() {
86 server.handleClient();
87 if (millis() - lastDht > 2000) { // 2 sn'de bir ölç / measure every 2 s
88 lastDht = millis();
89 float t = dht.readTemperature();
90 float h = dht.readHumidity();
91 if (!isnan(t) && !isnan(h)) {
92 tempC = t;
93 hum = h;
94 }
95 }
96}このコードには遠隔操作する部分だけが入っています。UnoのスケッチからhandleLight()、handleSafety()、handleDoor()の関数を移すこともできます。ピン番号を表のESP32の列に合わせ、ServoではなくESP32Servoを使い、ESP32のADCは0〜4095の値を返すのでGAS_LIMITとDARK_LIMITのしきい値を測り直してください。このページにはパスワードがないので、家のネットワークにいる人なら誰でもドアを開けられます。WebServerライブラリのauthenticate()関数で簡単なパスワードを付けるのはよい習慣です。リアルタイムで更新されるパネルについてはESP32のWebSocketの記事を参照してください。
Blynkでどこからでも操作する
ESP32のWebサーバーには同じネットワークからしかアクセスできません。外出先からモバイルデータで操作したい場合は、Blynk IoTのようなクラウドプラットフォームを使うと簡単です。スマホのアプリでドラッグ&ドロップでインターフェースを作り、ESP32はBlynkのサーバーに接続します。Blynk.Cloudでテンプレートを作り、V0(整数、0〜1、ランプ)、V1(実数、温度)、V2(実数、湿度)の3つのデータストリームを追加し、デバイスを追加して、表示されたTemplate ID、Template Name、Auth Tokenをコードの先頭に貼り付けます。アプリではV0にSwitchを、V1とV2にそれぞれGaugeを割り当てるだけです。無料プランではデバイス数、データストリーム数、月あたりのメッセージ数が制限されているので、センサーのデータは1秒ごとではなく5秒ごとに送っています。設定の全手順はESP32でBlynk IoTを使うガイドにあります。
1#define BLYNK_TEMPLATE_ID "TMPLxxxxxxx" // Blynk konsolundan kopyala / copy from the Blynk console
2#define BLYNK_TEMPLATE_NAME "Akilli Ev"
3#define BLYNK_AUTH_TOKEN "TokenBuraya"
4
5#include <WiFi.h>
6#include <BlynkSimpleEsp32.h>
7#include <DHT.h>
8
9const int LAMP_RELAY = 26; // aktif LOW röle / active-LOW relay
10DHT dht(13, DHT11);
11BlynkTimer timer;
12
13BLYNK_WRITE(V0) { // uygulamadaki anahtar / switch widget in the app
14 digitalWrite(LAMP_RELAY, param.asInt() ? LOW : HIGH);
15}
16
17BLYNK_CONNECTED() {
18 Blynk.syncVirtual(V0); // bağlanınca son durumu al / restore the last state
19}
20
21void sendSensors() {
22 float t = dht.readTemperature();
23 float h = dht.readHumidity();
24 if (isnan(t) || isnan(h)) return;
25 Blynk.virtualWrite(V1, t); // sıcaklık / temperature
26 Blynk.virtualWrite(V2, h); // nem / humidity
27}
28
29void setup() {
30 pinMode(LAMP_RELAY, OUTPUT);
31 digitalWrite(LAMP_RELAY, HIGH); // açılışta kapalı / off at boot
32 dht.begin();
33 Blynk.begin(BLYNK_AUTH_TOKEN, "AgAdi", "Sifre123");
34 timer.setInterval(5000L, sendSensors); // 5 sn'de bir: mesaj kotasını korur / every 5 s saves quota
35}
36
37void loop() {
38 Blynk.run();
39 timer.run();
40}どの操作方法を選ぶべき?
| 方法 | 必要なもの | 家の外から | 難易度 | 向いている用途 |
|---|---|---|---|---|
| ESP32のWebサーバー | ESP32と家のWi-Fi | 不可、同じネットワークのみ | 中級 | 学校の発表、インターネットなしのデモ、すべて自分のコード |
| Blynk IoT | ESP32、インターネット、Blynkのアカウント | 可 | 簡単 | 既製のインターフェース、グラフ、通知 |
| Bluetooth(HC-05) | Uno、HC-05、Androidアプリ | 不可、約10 m | 簡単 | Unoのまま使いたい場合(iPhoneでは使えない) |
| Telegramボット | ESP32とインターネット | 可 | 中級 | 警報の通知と短いコマンド |
| MQTT + Home Assistant | ESP32とローカルサーバー(例:Raspberry Pi) | 可、設定が必要 | 上級 | 本格的なホームオートメーション |
学校の発表には、ESP32のWebサーバーが最も確実です。インターネットやクラウドに頼らず、会場にWi-Fiがなくても自分のネットワークで動きます。UnoのままBluetoothだけを追加したい場合は、App Inventorで自分のAndroidアプリを作れます。ただしUnoにはピンの空きがないので、HC-05をつなぐためにLCDか窓のサーボをあきらめる必要があります。ESP32のTelegramボットを使えば警報が鳴ったときにスマホにメッセージが届き、MQTTとHome Assistantを使えば家を実際の機器とつなげられます。
Raspberry Pi Picoでスマートホームを作る
同じシステムはRaspberry Pi PicoとMicroPythonでも作れます。Wi-Fi付きのPico Wなら、ESP32と同じようにスマホから操作できます(Pico WのWebサーバーを参照)。Picoは3.3 Vで動くので、ここでもESP32のルールが当てはまります。LDRの分圧回路は3V3から給電し、5 Vのアナログ出力は分圧回路で下げ、3.3 Vで駆動できるリレーモジュールを選びます。アナログ入力はGP26、GP27、GP28だけです。下のコードは、動きで点灯する照明モジュールのMicroPython版です。
1from machine import Pin, ADC
2import time
3
4pir = Pin(15, Pin.IN) # PIR OUT (3,3 V çıkış / 3.3 V output)
5ldr = ADC(26) # GP26: bölücü 3V3'ten / divider fed from 3V3
6relay = Pin(14, Pin.OUT, value=1) # aktif LOW röle, kapalı başla / active-LOW relay, start off
7
8LIGHT_MS = 20000 # son hareketten sonra ışık / light after last motion
9DARK = 20000 # bunun altı karanlık / below this is dark
10last = time.ticks_ms()
11on = False
12
13while True:
14 now = time.ticks_ms()
15 if pir.value():
16 last = now
17 if ldr.read_u16() < DARK: # yalnız karanlıkta yak / only when dark
18 on = True
19 if on and time.ticks_diff(now, last) > LIGHT_MS:
20 on = False
21 relay.value(0 if on else 1)
22 time.sleep_ms(50)学校の発表のためのヒント
- 問題から始める:「誰もいない家で点けっぱなしの照明、誰も気づかないガス漏れ、鍵をなくしがちなお年寄り」のように、実際の問題を1文で説明し、模型がそれにどう応えるかを見せます。
- ブロック図を用意する:左にセンサー、中央にArduino、右にアクチュエーターを置き、矢印にルール(「暗い + 動き → ランプ」)を書きます。審査員はコードを読みませんが、この図は覚えています。
- ライブデモの台本を作る:カードをかざしてドアが開くところを見せ、PIRの前で手を振り、消毒用アルコールを含ませたコットンをMQ-2に近づけ(アルコールにもセンサーは反応します)、指でDHT11を温めます。各デモは10〜20秒に収めます。
- 裸火は使わない:段ボールの模型の中でライターやろうそくに火を付けてはいけません。多くの炎センサーは、近づけたテレビのリモコンの赤外線にも反応します。事前に試して、デモではそれを使いましょう。
- しきい値を会場に合わせる:当日の会場が22 °Cなら、28 °Cを待っているとファンのデモが台無しになります。FAN_ONを室温より2〜3度高く設定し、指でセンサーを温めたときにファンが回るようにします。
- 測定して数字で示す:たとえば、PIRありとなしでランプが1日に何分点いているかを記録し、節約量をパーセントで示します。数字のある結果は、「動きます」と言うよりずっと説得力があります。
- 予備の計画を用意する:動いているシステムの動画を撮り、予備の配線、サーボ、アダプターを持参し、カード番号とWi-Fiの設定を紙に書いておきます。
- 審査員の質問に備える:「実際の家ではどう動きますか?」に対し、商用電源の安全、認証済みの検知器、RFIDのセキュリティ、停電時にドアがどうなるかについて短い答えを用意しておきます。
よくある間違いと解決法
| 症状 | 考えられる原因 | 解決法 |
|---|---|---|
| サーボが動くとArduinoがリセットされる、またはLCDが乱れる | サーボとリレーをArduinoの5Vピンから給電している | 別の5 V 2〜3 Aアダプター、共通GND、サーボの電源に470〜1000 µFのコンデンサ |
| リレーが逆に動き、電源投入時にオンになる | モジュールがアクティブLOW | RELAY_ONとRELAY_OFFの値を入れ替える |
| 電源を入れるたびに警報が鳴る | MQ-2が温まっている途中で、PIRが周囲を学習している | WARMUP_MSの待ち時間を残す。初めて使う前にMQ-2を長時間通電しておく |
| 昼間にランプが点く、または消えてすぐまた点く | DARK_LIMITが合っていない、またはLDRがランプの光を受けている | シリアルモニタの値からしきい値を決め、LDRを外壁に移す |
| カードが読まれない | RC522の配線が違う、またはモジュールを5 Vにつないでいる | SPIのピンを確認し、DumpInfoのスケッチ例でモジュールをテストする |
| DHT11がnanを返す | DATA線のピンが違う、または単体のセンサーにプルアップ抵抗がない | ピンを確認し、モジュールを使うか、DATAとVCCの間に10 kΩを入れる |
| サーボがときどき震える | 電源が弱い。DHT11を読む間、割り込みが一瞬止まる | 電源を強化する。必要なら動作が終わったサーボをdetach()する |
| ESP32でガスの値が常に0か4095 | ADC2のピンがWi-Fiとぶつかっている、または分圧回路がない | GPIO 34や35などのADC1のピンと分圧回路を使う |
| スマホでESP32のページが開かない | スマホがゲストネットワークかモバイルデータにつながっている、またはネットワークが5 GHz | 同じ2.4 GHzのネットワークにつなぎ、シリアルモニタでIPアドレスを確認する |
Arduinoのスマートホームプロジェクトは、センサーの読み取り、判断、モーターとリレーの駆動、電源の計画、無線での操作を1つの作品にまとめたもので、学ぶにも発表するにも最適なテーマです。まず1つのモジュールから始め、すべてのステップをテストし、システムを少しずつ大きくしていきましょう。BlueGraysアプリでは、スマート階段照明、PIRとLDRのスマート照明、炎・ガス警報、RFIDのドアロック、ESP32のWebサーバーのプロジェクトを、回路図とコード付きでステップごとに紹介しています。
よくある質問
Arduinoのスマートホームプロジェクトには何が必要?
基本の模型なら、Arduino UnoかESP32、PIR人感センサー、LDR、MQ-2ガスセンサー、炎センサー、DHT11、RC522 RFIDモジュール、SG90サーボ2個、2チャンネルリレーモジュール、ブザー、I2C LCDで十分です。これにブレッドボード、ジャンパーワイヤー、5 V 2〜3 AのACアダプター、模型の材料を加えます。モジュールの多くは安く、予算の大半はボード、ディスプレイ、アダプター、模型そのものに使われます。
スマートホームプロジェクトにはArduinoとESP32のどちらがいい?
スマホでの操作が不要ならArduino Unoのほうが簡単です。5 Vのモジュールとそのまま使え、ほとんどの例がUno向けに書かれています。スマホからのWi-Fi操作、通知、より多くのピンが必要ならESP32を選びます。ただしピンが3.3 Vで動くので、リレーモジュールや5 Vのアナログセンサーには余分な注意が必要です。
Arduinoのスマートホームをスマホから操作するには?
最も簡単なのは、ESP32を使ってWebサーバーを立てる方法です。同じWi-Fiネットワークにつないだスマホのブラウザで、ボタンを使ってランプ、ファン、ドアを操作できます。家の外からアクセスしたい場合はBlynk IoTのようなクラウドアプリを、Arduino Unoのまま使いたい場合はHC-05 BluetoothモジュールとAndroidアプリを使えます。
スマートホームの模型に最適な材料は?
最も手軽なのは5 mmのスチレンボードか発泡塩ビ板です。カッターで切れて、ホットボンドで接着できます。より頑丈な本体にしたいなら3〜4 mmのMDFを使えます。50×40 cmほどの土台、取り外せる屋根、電子回路用の独立した機械室があると、作業がずっと楽になります。
Arduinoで商用電源のランプをオン・オフできる?
はい、5 Vのリレーモジュールを使えばできます。リレーが商用電源の回路をArduinoから電気的に切り離します。ただし商用電源の電圧は命に関わります。接続はプラグを抜いた状態でだけ行い、回路は絶縁された閉じた箱に入れ、生徒のプロジェクトでは大人の監督なしに作業してはいけません。模型なら5 Vか12 Vのランプで十分です。
Arduinoのスマートホームは科学コンテストのテーマとして向いている?
スマートホームはとてもよくあるテーマなので、動く模型だけでは足りないことが多いです。プロジェクトを測定できる研究課題と結び付ければ、たとえば動きで点灯する照明がどれくらい省エネになるか、ガス警報がどれくらい速く反応するかなどにすれば、審査員の評価はずっと高くなります。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
DHT11で温度と湿度を測定
部屋の温度は何度で、空気は湿っているでしょうか、それとも乾燥しているでしょうか?DHT11センサーが温度と湿度を測定し、Arduinoが2行のLCDディスプレイに値を表示して、数秒ごとに更新します。センサーに異常があればエラーメッセージが表示されます。自分だけの小さな気象観測装置を作る第一歩です。

階段・廊下のスマート照明
階段や廊下に足を踏み入れると、進む方向に沿って照明が1段ずつ点灯します。両端に距離センサーがあり、どちらの前を通っても、その端から8個のLEDが0.5秒間隔で順番に点灯し、その後、同じ方向に順番に消灯します。夜の暗がりで足元を照らす、おしゃれなホームオートメーションのアイデアです。

ESP32スマート照明(PIR + LDR + リレー + OLED)
ESP32で廊下やトイレ、食品庫の照明をスマート化します!PIRモーションセンサー、LDR光センサー、リレーモジュールを使い、「暗い場所で動きを検知すると点灯する」定番の仕組みを作ります。周囲が明るければ動きを検知しても点灯しないため、日中の無駄な電力消費を抑えられます。照明は30秒間点灯し、動きが続くとタイマーがリセットされて点灯時間が延びます。SSD1306 OLEDには大きな文字で状態(ON / DAYTIME / WAITING)とカウントダウンを表示します。

モーションセンサーで作るドアベルとライト
玄関に誰かが近づくと、呼び鈴を押す前にわかります。モーションセンサーが人を検知するとLEDが点灯し、ブザーが高い音から低い音へ3音のチャイムを鳴らします。メロディーが終わるとライトが消え、センサーは再び待機します。音と光の両方で知らせるシンプルな玄関の見張り役です。

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

ESP32のRFIDドアロック(RC522 + サーボモーター + OLED + ブザー + LED)
ESP32、RC522 RFIDモジュール、SG90サーボモーター、SSD1306 OLEDディスプレイ、アクティブブザー、LED2個を使ってスマートドアロックを作ります。カードを読み取ると、UIDをコード内に登録した許可済みカードの一覧と照合します。許可されたカードなら、サーボモーターを回して解錠し、緑色のLEDが点灯して短い音が2回鳴ります。5秒後に自動で施錠します。許可されていないカードでは赤色のLEDが点灯し、短い間隔で3回音を鳴らして警告し、読み取ったUIDをOLEDに表示します。実際のIoTセキュリティの仕組みを実践的に学べます。

リモコンで220V電球を点灯・消灯
ソファに座ったまま、テレビのリモコンで家庭の電球を点灯・消灯します。Arduinoがリモコンから届く目に見えない光の信号を読み取り、電源ボタンを押すとリレーを動かして電球を点灯させ、消音ボタンを押すと消灯させます。家庭用電源を扱うため、配線は大人と一緒に、必ずプラグを抜いた状態で行ってください。

ESP32非同期Webサーバー(LCDにIPアドレスを表示)
このプロジェクトでは、ESP32を同じWi-Fiネットワーク上のスマートフォンやパソコンから操作できる非同期Webサーバーにします。ESPAsyncWebServerライブラリを使うと、従来のWebServer.hのように1つのリクエストで処理がブロックされず、複数のリクエストを同時に処理できます。ブラウザーでESP32のIPアドレスを開き、ON/OFFボタン(/onと/offのルート)を押すと、GPIO 5に接続したLEDを点灯・消灯できます。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

