Alexaで実現するスマートホーム監視システム
必要なパーツと用品
| パーツ | 数量 |
|---|---|
| Arduino UNO | 1 |
| Espressif ESP8266 ESP-01 | 1 |
| DHT11 温湿度センサー(4ピン) | 1 |
| ControlEverything.com 4チャネル リレーコントローラー(I2C) | 1 |
| IR障害物センサー | 1 |
| カメラ | 1 |
| ジャンパーワイヤー | 1 |
使用するアプリとオンラインサービス
- Arduino IDE
- Amazon Alexa Skills Kit
- Amazon Alexa echosim.io
- ThingSpeak API
- Backendless
- PushingBox
プロジェクト概要
はじめに
現代では職場で過ごす時間が自宅より長くなっています。そのため、仕事中でも自宅の状況を把握できるホームモニタリングシステムが必要です。さらに、「誰か」に自宅の状況を尋ねるだけで情報が得られたらもっと便利です。これはAmazon Alexaをアシスタントとして活用し、ユーザーが必要な自宅情報を取得できるようにすることで実現できます。
職場だけでなく、インターネット接続とAmazon Alexaデバイスがあれば、世界中のどこからでも自宅の状況を知ることができます。
本プロジェクトで実装する機能:
- 家電デバイス(扇風機、照明)の制御
- デバイスの状態通知
- 自宅の気象条件(温度・湿度)の通知
- 要求時に自宅内部のスナップショットをGmailで送信
- 以下の場合に通知を送信(写真付きも含む)
- 侵入者検知(写真付き)
- 火災検知
- 来客検知(写真付き)
ステップ1:ArduinoとESP8266のプログラミング
Arduino IDEを公式サイトからダウンロード:
https://www.arduino.cc/en/Main/Software
Arduino IDEを開き、ファイル → 環境設定 → 追加のボードマネージャーのURLに以下を入力:
https://arduino.esp8266.com/stable/package_esp8266com_index.json
ツール → ボード → ボードマネージャーで、最後にあるesp8266パッケージをインストール。
Arduinoのプログラミング: USBケーブルでArduinoをPCに接続し、ツール → ボードでArduino/Genuino UNOを選択。正しいCOMポートを選択(デバイスマネージャーで確認)。プログラムを書いてコンパイルし、エラーがなければアップロード。
ESP8266のプログラミング: 以下の図のように接続:
ArduinoからPCへUSB接続。ツール → ボード → Generic ESP8266 Moduleを選択し、正しいCOMポートを選択。プログラムを書いてコンパイル・アップロード。ArduinoのRSTをGNDに接続しておくこと(ArduinoがESP8266へのプログラマーとして動作)。
本プロジェクトでは、まずESP8266をプログラミングし、その後接続を外して「回路接続」図のように再接続し、最後にArduinoをプログラミングします。
ステップ2:ThingSpeak.comの設定
- ThingSpeak.comでアカウント作成
- チャネルを使用してデバイス制御情報を格納し、Arduino/Alexaへルーティング。温度・湿度値もチャネルに格納。情報のストレージとして機能。
- チャネル → マイチャネル → 新しいチャネルで作成。名前と説明を入力。
本プロジェクトでは8つのチャネルを使用(チャネル数を減らすことも可能だが、プログラミングが複雑になる)。ThingSpeakには更新間隔の制約があり、同一チャネルへの連続更新には10-15秒の間隔が必要。
8つのチャネルとその値・意味:
| チャネル名 | 値と意味 |
|---|---|
| 1) デバイス制御 | 0=照明ON, 1=照明OFF, 2=扇風機ON, 3=扇風機OFF |
| 2) 照明状態 | 0=照明OFF, 1=照明ON |
| 3) 扇風機状態 | 0=扇風機OFF, 1=扇風機ON |
| 4) 湿度 | 湿度値 |
| 5) 温度 | 温度値 |
| 6) 侵入者通知 | 1=侵入者アラート |
| 7) 火災通知 | 1=火災アラート |
| 8) 来客通知 | 1=来客アラート |
チャネルをクリックすると、API KeysタブでChannel IDとWrite API Keyが確認できます。Channel IDはチャネルから情報を読み取る際に、Write Keyは値を書き込む際に必要。
チャネル更新用HTTPリクエスト:
https://api.thingspeak.com/update?api_key=<write key>&field1=<value>
チャネル読み取り用HTTPリクエスト:
https://api.thingspeak.com/channels/<channel id>/field/field1/last.html
ステップ3:プログラム
プログラムは3部構成:
A) Arduino用プログラム
非常にシンプル。ESP8266からシリアルでデータを受信し、受信データに基づいてデバイスを制御。詳細はコード内のコメントを参照。
B) ESP8266用プログラム
以下の3つの処理を含みます:
- 温度・湿度をHTTPリクエストでThingSpeakに更新:
client.print(String("GET ") + "/update?key=&field1=" + humidity + " HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); // 湿度更新
client.print(String("GET ") + "/update?key=&field1=" + temperature + " HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n"); // 温度更新
<write key>はThingSpeakの湿度・温度チャネルそれぞれのWrite Keyに置き換え。hostはapi.thingspeak.com。
DHTライブラリ:https://github.com/adafruit/DHT-sensor-library
- ThingSpeakの対応チャネルから読み取り、取得値に基づいてデバイス制御:
client.print(String("GET ") + "/channels//field/field1/last.html HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
<channel id>はThingSpeakで見つかる対応チャネルのIDに置き換え。
- 高温時のアラートをPushingBox経由で送信:
String host1 = "api.pushingbox.com";
client.print(String("GET ") + "/pushingbox?devid= HTTP/1.1\r\n"
+ "Host: " + host1 + "\r\n" + "Connection: close\r\n\r\n");
<your dev id>はPushingBoxのデバイスIDに置き換え。
ステップ4:LinkIt Smart 7688 DuoとWebカメラの設定
このプロジェクトではWebカメラとLinkIt Smart 7688 Duoを使用して必要な写真を撮影し、ユーザーに送信します。ArduinoカメラモジュールやIPカメラでも代替可能。
- LinkIt Smart 7688のWiFiボタンを20秒長押ししてリセット。リセット後、ワイヤレスネットワーク接続にアクセスポイント名が表示されるので、PCで接続。
- ブラウザで
192.168.100.1を開き、ポータルにアクセス。パスワードを設定してログイン。
- ログイン後、NetworkタブでStationモードを選択(インターネットアクセスのため)。自分のWiFiネットワークに接続し、Configure and Restartを押下。
- 再起動後、ローカルIPアドレスが割り当てられる。IPツールやルーターポータルで確認(例:192.168.1.4)。
- ブラウザでローカルIPアドレスを開き、再ログイン。
- Webカメラからのストリーミングを有効化するには、MJPG Streamerを有効化。ポータル右上のOpenWrtをクリックし、再ログイン。
- ServiceタブでMJPG Streamerを有効化し、ポート番号を割り当て(例:4440)。Save and Apply。
- WebカメラをOTGケーブルでLinkIt Smart 7688 DuoのUSBホストに接続。
- ブラウザで
<ローカルIP>:<ポート>を開いてストリーム確認(例:192.168.1.4:4400)。
- スナップショット撮影:
<ローカルIP>:<ポート>?action=snapshot
- ポートフォワーディングでこの画像を外部からアクセス可能にする。ルーターポータルで設定(通常NATサービス配下)。ルーターごとに手順が異なるため「ポートフォワーディング ルーター名」で検索。
- ポートフォワーディング後、外部IP(Googleで「whats my ip」で確認)の
https://<外部IP>:<ポート>?action=snapshotでアクセス可能。このアドレスをPushingBoxに設定すれば、必要時に画像を添付してメール送信できる。
LinkIt Smart 7688 DuoにはSDカードスロットもあり、画像をローカル保存することも可能。
詳細:MediaTek公式ドキュメント
ステップ5:PushingBoxの設定
PushingBoxで各種アラート通知をGmail送信します。
- GoogleアカウントでPushingBoxにサインイン:https://www.pushingbox.com/
- My Services → Add a serviceで、Gmailを選択(写真添付が必要なため)。適切な設定と、アラート送信先のGmailアドレスを入力。
- My Scenariosで新しいシナリオを作成。名前を付け(例:ALERT)、先ほど作成したサービスを追加。
- メールの件名と本文を記述し、Webカメラのスクリーンショット撮影用URL(スナップショット用URL)を入力して写真添付を設定。アラート種類ごとに異なるシナリオを作成。
シナリオ実行用API:
https://api.pushingbox.com/pushingbox?devid=<device id>
ステップ6:BackendlessでAlexaスキルを作成
Backendlessはシンプルなドラッグ&ドロッププログラミングでAlexaスキル(やその他のプログラム)を作成でき、Backendless APIでアクセス可能。
- Backendlessでアカウント作成:https://backendless.com/
- ログイン後、Create appでアプリ名を入力。
- 左側アイコンバーのBusiness Logicアイコンをクリック → API SERVICES画面。
- 「+」アイコンで新しいサービス作成。CODELESSを選択、サービス名「AlexaService」でSAVE。
- メソッド作成プロンプトが表示。メソッド名「handleRequest」、REST操作「POST」、引数名「req」、タイプ「Any Object」で宣言。
- EDITでCodeless Logic Designerに切り替え。プレースホルダーブロックの「doSomething」を「sendAlexaResponse」に変更。この関数でAlexaが発話する内容を引数で渡せるようにする。SAVE。
- 紫ブロック右横の歯車アイコンで引数を2つ追加:「whatToSay」と「waitForResponse」。引数追加に伴いContext Blocksエリアに自動的にブロックが追加される。
- 関数ロジックを以下のように修正(歯車アイコンでオブジェクトプロパティ名を変更)。SAVEを忘れずに。
- カスタム関数完成後、handleRequestメソッドに戻り、左ツールバーのCustom FunctionsからsendAlexaResponseブロックをドラッグし、サービスメソッドのreturnコネクタに接続。
上記手順は公式サイトでも解説されています:
Part 1
Part 2
- BROWSERセクションのFunctions下にあるAdd Newノードをクリック。プレースホルダーの「doSomething」を「getIntentName」に変更。
- ブロックを修正して以下のように:
これでサンプル発話に基づいてインテント名を取得可能。API Services → アプリ名 → handle request(BROWSERセクション)に戻り、VariablesとLogicをSystemセクションから作成:
インテント名をリクエスト変数に格納し、インテントと比較。例えばリクエストが「introduction」なら、応答変数を「こんにちは!私は照明制御などができます...」に設定し、最後にAlexaが読み上げます。
リクエストが「LightsOn」ならThingSpeakチャネルを'0'に更新(HTTP GETリクエスト)し、同時にデバイス状態も更新(ON/OFFに応じて1/0)。同じことを「LightsOff」「FanOn」「FanOff」で繰り返し。
「Weather」なら温度・湿度チャネルから読み取り、結果を応答変数に格納。チャネルは値のみ返すため、テキストを追加して意味のある応答に:
「リビングルームのスナップショット」ならPushingBoxシナリオを実行:
「デバイス状態」ならThingSpeakの状態チャネルから情報読み取り:
「通知・アラート」ならアラートチャネル(火災、侵入者、来客)から読み取り:
取得値に基づき対応するアラートメッセージをrespond0変数に格納。通知がない場合はメッセージなし。
通知を読み上げた後、通知チャネルに'0'を書き戻し、同じ通知を繰り返さないようにする。最後にリクエストに基づきrespond0またはrespond変数を読み上げ。
重要: Backendlessでモデル作成後、Codeless画面右上のDeploy Modelをクリックしてデプロイしてください。
ステップ7:Amazon Developer ConsoleでAlexaスキルを設定
- Amazon Developer Consoleにサインイン:https://developer.amazon.com/
- Developer Console → ALEXAタブ → Alexa Skills Kit → Get Started
- カスタムスキルタイプを作成、名前と起動名(Invocation Name)を設定。
インテントとサンプル発話は以下のJSONで定義:
{
"languageModel": {
"intents": [
{"name": "AMAZON.CancelIntent", "samples": []},
{"name": "AMAZON.HelpIntent", "samples": []},
{"name": "AMAZON.StopIntent", "samples": []},
{"name": "Camera", "samples": ["リビングルームのスナップショットを送って", "家のスナップショットを送って", "リビングルームの写真を送って", "家の写真を送って"], "slots": []},
{"name": "FanOff", "samples": ["扇風機を消して", "扇風機をオフにして", "扇風機を切って"], "slots": []},
{"name": "FanOn", "samples": ["扇風機をつけて", "扇風機をオンにして", "扇風機を回して"], "slots": []},
{"name": "Introduction", "samples": ["何ができるの", "何ができますか", "私に何ができますか"], "slots": []},
{"name": "LightsOff", "samples": ["照明を消して", "ライトを消して", "照明をオフにして"], "slots": []},
{"name": "LightsOn", "samples": ["照明をつけて", "ライトをつけて", "照明をオンにして"], "slots": []},
{"name": "Notification", "samples": ["通知はある?", "通知を読み上げて", "更新はある?"], "slots": []},
{"name": "Status", "samples": ["デバイスの状態は?", "デバイスのステータスは?"], "slots": []},
{"name": "Weather", "samples": ["家の天気は?", "家の温度は?", "温度は?", "湿度は?"], "slots": []}
],
"invocationName": "my home"
}
}
- Configurationタブで、サービスエンドポイントタイプにHTTPSを選択、デフォルトURLにBackendlessのAPI URLを入力。
- デフォルトエンドポイントのSSL証明書で2番目のオプションを選択。テストシミュレーターでスキルテストも可能。
- テスト完了後、必要な公開情報を入力してスキルを公開。
ステップ8:最終セットアップと完成!
以下の図のように回路を接続:
注意: ESP8266は電流不足で誤動作することがあります。回路図には記載されていませんが、ESP8266を別の3.3V電源から給電することを推奨します。モバイルバッテリーを使用する場合、5Vから3.3Vへレギュレーターで降圧してください。
ESP8266とArduinoにプログラムをアップロード。電球への接続を例示していますが、同じ要領で扇風機や他のデバイスにも拡張可能です。
最後にAmazon Echoまたはechosim.ioでスキルをテスト。
重要: スキルは起動名(私の場合「my home」)で有効化する必要があります。起動名なしでは動作しない場合があります(動画で何度か実演)。
チュートリアルをお楽しみいただけましたか?
ありがとうございました!
コード
Smart_bell_ESP8266.ino(侵入者・来客アラート用ESP8266コード)
/*
* スマートベルとホームセキュリティの実装
*/
#include
#include
// 変数
int intruder = 0, guest = 2; // ピン0: 侵入者アラート, ピン2: 来客アラート
const char* ssid = "SSID"; // WiFi SSID
const char* password = "password"; // WiFiパスワード
const char* host = "api.thingspeak.com";
void setup() {
Serial.begin(115200);
delay(10);
Serial.println();
Serial.print("Connecting to "); Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500); Serial.print(".");
}
Serial.println(""); Serial.println("WiFi connected");
Serial.println("IP address: "); Serial.println(WiFi.localIP());
}
int value = 0;
int x, y = 0, z, k = 0;
void loop() {
delay(1000);
++value;
Serial.print("connecting to "); Serial.println(host);
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed"); return;
}
Serial.print("Requesting URL: ");
//===========================================================================
// 侵入者アラート
//===========================================================================
x = digitalRead(intruder);
if (x == 0) { y = 0; }
if (x == 1 && y == 0) {
Serial.println("Sending Alert Pls Wait.....");
delay(1000);
String host1 = "api.pushingbox.com";
client.print(String("GET ") + "/pushingbox?devid= HTTP/1.1\r\n"
+ "Host: " + host1 + "\r\n" + "Connection: close\r\n\r\n");
client.print(String("GET ") + "/update?api_key=&field1=1 HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
delay(1000); y = 1;
}
//===========================================================================
// 来客アラート
//===========================================================================
long t1;
z = digitalRead(guest);
if (z == 0 && (millis() >= t1 + 300000)) { // 5分待機でチャタリング防止
k = 0;
}
if (z == 1 && k == 0) { // ※元コードはx==1だが、z==1の間違いか?
t1 = millis();
Serial.println("Sending Alert Pls Wait.....");
delay(1000);
String host1 = "api.pushingbox.com";
client.print(String("GET ") + "/pushingbox?devid= HTTP/1.1\r\n"
+ "Host: " + host1 + "\r\n" + "Connection: close\r\n\r\n");
client.print(String("GET ") + "/update?api_key=&field1=1 HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
delay(1000); k = 1;
}
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop(); return;
}
}
}
Home_monitor_arduino.ino(家電制御用Arduinoコード)
// ホームオートメーション実装
#include
#include
// ピン定義
#define Light 2 // ピン2: 照明制御
#define fan 3 // ピン3: 扇風機制御
// 変数
char e;
int p = 0, q = 0; // 既にON/OFF済みのデバイスを二重操作しないためのフラグ
SoftwareSerial home_monitor(10, 11); // RX, TX
char control_data; // Bluetooth経由で送信される制御データ
void setup() {
home_monitor.begin(115200);
Serial.begin(9600);
pinMode(13, OUTPUT);
}
void loop() {
if (home_monitor.available()) {
control_data = home_monitor.read();
e = control_data;
Serial.print(control_data);
//===========================================================================
// デバイス制御
//===========================================================================
// チャネルの値に基づいて操作実行
//-------------------------------------------------------------
// 照明
//-------------------------------------------------------------
if (e == '0' && p == 0) { // 照明ON
digitalWrite(Light, LOW);
p = 1;
}
if (e == '1' && p == 1) { // 照明OFF
digitalWrite(Light, HIGH);
p = 0;
}
//-------------------------------------------------------------
// 扇風機
//-------------------------------------------------------------
if (e == '2' && q == 0) {
digitalWrite(fan, HIGH);
q = 1;
}
if (e == '3' && q == 1) {
digitalWrite(fan, LOW);
q = 0;
}
}
}
Home_Monitor_ESP8266.ino(温湿度更新・制御情報取得用ESP8266コード)
/*
* ThingSpeakに気象情報を更新し、制御情報を取得してArduinoにシリアル送信
*/
#include
#include
#include "DHT.h"
#define DHTTYPE DHT11
#define dht_dpin 2 // GPIO2をデータ入力に
DHT dht(dht_dpin, DHTTYPE);
// 変数
char c, e;
float temp, h;
String humidity, temperature, line;
int co = 0; // アラート送信制御
const char* ssid = "SSID";
const char* password = "password";
const char* host = "api.thingspeak.com";
void setup() {
dht.begin();
Serial.begin(115200);
delay(10);
Serial.println();
Serial.print("Connecting to "); Serial.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500); Serial.print(".");
}
Serial.println(""); Serial.println("WiFi connected");
Serial.println("IP address: "); Serial.println(WiFi.localIP());
}
int value = 0;
void loop() {
delay(1000);
++value;
Serial.print("connecting to "); Serial.println(host);
WiFiClient client;
const int httpPort = 80;
if (!client.connect(host, httpPort)) {
Serial.println("connection failed"); return;
}
Serial.print("Requesting URL: ");
//===========================================================================
// ミニ気象ステーション
//===========================================================================
h = dht.readHumidity();
temp = dht.readTemperature();
if (temp < 50.0) { co = 0; }
if (temp >= 50.0 && co == 0) { // 50度以上でアラート(coで多重送信防止)
Serial.println("Sending Alert please wait.......");
delay(1000);
String host1 = "api.pushingbox.com";
client.print(String("GET ") + "/pushingbox?devid= HTTP/1.1\r\n"
+ "Host: " + host1 + "\r\n" + "Connection: close\r\n\r\n");
delay(1000);
client.print(String("GET ") + "/update?key=&field1=1 HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
co = 1;
}
humidity = String(h);
temperature = String(temp);
Serial.println(h); Serial.println(temp);
client.print(String("GET ") + "/update?key=&field1=" + humidity + " HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
client.print(String("GET ") + "/update?key=&field1=" + temperature + " HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
//===========================================================================
// Arduinoへ制御情報をシリアル送信
//===========================================================================
client.print(String("GET ") + "/channels//field/field1/last.html HTTP/1.1\r\n"
+ "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 5000) {
Serial.println(">>> Client Timeout !");
client.stop(); return;
}
}
while (client.available()) {
line = client.readStringUntil('\r');
}
Serial.print(line); // Arduinoがシリアルで受信
}
回路図
製造プロセス
- LoRaベースのスマートシティ大気質監視システム
- スマートホーム連携向けマルチポジション温度センサーシステム
- K30 CO2センサで室内空気質をリアルタイム監視する
- スマート3Dセンサー: WalabotとAlexaで声によるランプ制御 – LattePanda活用
- Arduino Nanoで構築するスマートホーム植物自動水やりシステム
- Alexa対応Arduinoホームコントローラー:音声でスマートホーム自動化
- 学校向けスマート体温監視システム: 正確、リアルタイム、安全
- Arduino Nano製ウェザーステーション:気圧・温湿度・UV・時計表示付き
- Arduino MKR GSM 1400で作るスマートリストバンド:Hologram IoT × Azure × Power BIで患者見守り
- RFIDベースのホームセキュリティ: 低コスト・電池不要の監視ソリューション
- スマートホームテクノロジーが日常生活をどう変えるか