ドアセンサーを作ってみる
自室のドアが開閉した際にスマホ/PCで通知を受け取れたら面白いのでは、と思い作ってみました。
今回使うあれそれ
【必須】
・Arduino(要Wi-Fiモジュール)
・Discordアカウント
・リードスイッチ
【任意】
・HTTPサーバーを用意できる((根気||知識)&&サーバー機)
ログの保存/閲覧のためにわざわざ何かこしらえるのは面倒なので、既存のチャットツールへBotに投稿させその履歴をログとします。SlackやLINEなんかもBotがあるらしいんですが今回はDiscordで実装します。
HTTPサーバーはArduinoとDiscordの仲介を担います。Arduinoから直接Discordへ投げてもいいんですが、後々の拡張性を考えて間に自前サーバーを挟んでます。
簡略図
実装
Arduino
今回はWi-FiモジュールとArduino互換機が合体した「ESP-12E」を使いました。ESP8266が搭載されています。
画像ではUNOになってますがそのあたりは心の目で見てください。
スイッチがアナログピンに繋がっているのは完全に気まぐれです。特にこれといった理由はありません。それからESP-12Eはボード上に書かれた番号とGPIOの番号が一致しないので使う場合はそのあたり気を付けましょう。
#include <ESP8266WiFi.h>
#include <Switches.h>
bool isOpen = true;
// Wi-Fi(5GHzには非対応)
const char* ssid = "SSID";
const char* password = "パスワード";
// Server
const char* host = "192.168.0.16";//サーバー機のローカルIP
const int httpPort = 80;
const char* path = "/Hoge";
WiFiClient client;
//インジケータLED(フルカラー)用のピンを指定
int pinR=5;
int pinG=4;
void setup() {
pinMode(pinR, OUTPUT);//D1ピン
pinMode(pinG, OUTPUT);//D2ピン
Serial.begin(115200);
WIFIconnecting();
}
void WIFIconnecting() {
// WIFI_AP, WIFI_STA, WIFI_AP_STA or WIFI_OFF
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFiに接続しました。");
Serial.print( "IPアドレス: ");
Serial.println( WiFi.localIP());
Serial.print( "ゲートウェイ: ");
Serial.println(WiFi.gatewayIP());
Serial.print( "サブネットマスク: ");
Serial.println(WiFi.subnetMask());
}
void loop() {
int val = analogRead(0);
bool current = val == 1024 ? false : true;
if (isOpen != current) {
Serial.println(!isOpen);
isOpen = !isOpen;
post();
}
switch (isOpen) {//インジケータ用LEDの点灯。カソードコモン/アノードコモンで適宜書き換え
case true:
digitalWrite(pinR, LOW);
digitalWrite(pinG, HIGH);
Serial.println("OPEN");
break;
case false:
digitalWrite(pinR, HIGH);
digitalWrite(pinG, LOW);
Serial.println("CLOSE");
}
delay(200);
}
void post() {
path = isOpen ? "/open" : "/close";
if (true) {
Serial.println("送信中");
if (!client.connect(host, httpPort)) {
Serial.println("サーバーへのアクセスに失敗しました。");
return;
}
int httpcode = client.print(String("POST ") + path + " HTTP/1.1\r\n" +
"Host: " + host + "\r\n" +
"Content-Length:" + "0" + "\r\n" +
"Connection: close\r\n\r\n" +
"piyo");
Serial.println(httpcode);
unsigned long timeout = millis();
while (client.available() == 0) {
if (millis() - timeout > 10000) {
Serial.println(">>> タイムアウトしました。");
return;
}
}
Serial.println("送信成功");
// while (client.available()) {
// //char c = client.read();
// //Serial.print("OK");
// }
Serial.println("");
}
}
ググって出て来たコードを切り貼りして書き足したものです。
フルカラーLED楽しいです。
インジケータつけました pic.twitter.com/vxiABrwisl
— うなせん王国@パンソロ補佐 (@unasenohkoku) November 13, 2019
最終的にはこんな感じで取り付けます。
Discord
こちらの記事を参考にウェブフック用のURLを取得してください。POSTリクエストでJSON投げるだけでBotがテキストチャンネルにメッセージを投稿してくれるようになります。
DiscordにWebhookでいろいろ投稿する
HTTPサーバー
C#で書きました。上記で取得したURLを使います。postToDiscord()メソッド内にコピペしましょう。
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Text;
class Program
{
static async System.Threading.Tasks.Task Main(string[] args)
{
try
{
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://192.168.0.16:80/");
listener.Start();
while (true)
{
HttpListenerContext context = listener.GetContext();
HttpListenerRequest request = context.Request;
HttpListenerResponse res = context.Response;
Console.WriteLine(request.RawUrl);
await postToDiscord(request.RawUrl);
res.StatusCode = 200;
byte[] content = Encoding.UTF8.GetBytes("HELLO");
res.OutputStream.Write(content, 0, content.Length);
res.Close();
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex);
}
}
static async System.Threading.Tasks.Task postToDiscord(string str)
{
// POSTする対象のURL
string url = "ウェブフック用URL";
// POSTメソッドで渡すパラメータ
var json = "{ \"content\" : \"hoge\"}";
switch (str) {
case "/open": json = "{ \"content\" : \"OPEN!!\"}";
break;
case "/close": json = "{ \"content\" : \"CLOSE...\"}";
break;
default: json = "{ \"content\" : \"UNKNOWN_MESSAGE\"}";
break;
}
using (var client = new HttpClient())
{
var content = new StringContent(json, Encoding.UTF8, "application/json");
var response = await client.PostAsync(url, content);
}
}
}
完成
こんな感じで動いてくれます。
【プロトタイプ】
ドアセンサー+ロガーのプロトタイプが完成しました pic.twitter.com/WkzDbU9Jdv
— うなせん王国@パンソロ補佐 (@unasenohkoku) November 13, 2019
【完成品】
— うなせん王国@パンソロ補佐 (@unasenohkoku) November 13, 2019
うまく動かないときのあれこれ
開発環境やプログラムが管理者権限で動いていない
サーバープログラム起動時にエラーが発生します。
サーバー機のファイアウォールが邪魔している
サーバーが稼働しているのにリクエストがはじかれる場合はFWの設定を疑いましょう。
ローカルIPが固定されていない
動いてたのに再起動したら動かなくなったぞ、とかいう場合はたぶんIPの不一致が原因です。
おまけ
今回VS2019で初めて実行ファイルを生成しようとしたんですがVS2010とかと違って勝手に生えてこないんですね。泣きそうになりながら.exeを探してました。メニューの[ビルド]から発行とやらを選択する必要があるようで。いやぁ知らんかった。
おまけ2
今回のきったねえ配線で最後を飾りたいと思います。お疲れ様でした。
#〆のグルーガン pic.twitter.com/DVvBFJLHJC
— うなせん王国@パンソロ補佐 (@unasenohkoku) November 13, 2019