Help us understand the problem. What is going on with this article?

Arduino×Discordでお手軽IoTを実現してみた話

More than 1 year has passed since last update.

ドアセンサーを作ってみる

 自室のドアが開閉した際にスマホ/PCで通知を受け取れたら面白いのでは、と思い作ってみました。

今回使うあれそれ

【必須】
・Arduino(要Wi-Fiモジュール)
・Discordアカウント
・リードスイッチ

【任意】
・HTTPサーバーを用意できる((根気||知識)&&サーバー機)



 ログの保存/閲覧のためにわざわざ何かこしらえるのは面倒なので、既存のチャットツールへBotに投稿させその履歴をログとします。SlackやLINEなんかもBotがあるらしいんですが今回はDiscordで実装します。

 HTTPサーバーはArduinoとDiscordの仲介を担います。Arduinoから直接Discordへ投げてもいいんですが、後々の拡張性を考えて間に自前サーバーを挟んでます。

簡略図

2019-11-14_15h17_53.png

実装

Arduino

 今回はWi-FiモジュールとArduino互換機が合体した「ESP-12E」を使いました。ESP8266が搭載されています。
 画像ではUNOになってますがそのあたりは心の目で見てください。

2019-11-14_21h36_28.png
 スイッチがアナログピンに繋がっているのは完全に気まぐれです。特にこれといった理由はありません。それからESP-12Eはボード上に書かれた番号とGPIOの番号が一致しないので使う場合はそのあたり気を付けましょう。

ESP.ino
#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楽しいです。



最終的にはこんな感じで取り付けます。
2019-11-14_23h01_04.png

Discord

 こちらの記事を参考にウェブフック用のURLを取得してください。POSTリクエストでJSON投げるだけでBotがテキストチャンネルにメッセージを投稿してくれるようになります。
 DiscordにWebhookでいろいろ投稿する

 
 

HTTPサーバー

 C#で書きました。上記で取得したURLを使います。postToDiscord()メソッド内にコピペしましょう。

Program.cs
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);
        }
    }
}

完成

こんな感じで動いてくれます。
【プロトタイプ】



【完成品】

うまく動かないときのあれこれ

開発環境やプログラムが管理者権限で動いていない

 サーバープログラム起動時にエラーが発生します。

サーバー機のファイアウォールが邪魔している

 サーバーが稼働しているのにリクエストがはじかれる場合はFWの設定を疑いましょう。

ローカルIPが固定されていない

 動いてたのに再起動したら動かなくなったぞ、とかいう場合はたぶんIPの不一致が原因です。

おまけ

 今回VS2019で初めて実行ファイルを生成しようとしたんですがVS2010とかと違って勝手に生えてこないんですね。泣きそうになりながら.exeを探してました。メニューの[ビルド]から発行とやらを選択する必要があるようで。いやぁ知らんかった。

おまけ2

今回のきったねえ配線で最後を飾りたいと思います。お疲れ様でした。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away