スマートホーム向け汎用トリガーを作る(ESP32使用):接点多入力版

📅
前回汎用トリガーの接点1入力版を紹介しました。
ところで使用しているESP32モジュールボード『DOIT ESP32 DEVKIT V1』はIOピンがたくさんあるので(今回使用している30ピン版モジュールボードでも24個のIOピンを装備)、1入力しか使わないのは損ですよね😅。
そこで複数入力に対応できるようにプログラム(スケッチ)を書き換えました。

今回はスマートホーム向け汎用トリガー(接点多入力版)の紹介です。
今回紹介するのは2入力版ですが、3入力以上の多入力にも容易に変更出来るようにしています。


■ESP32ボードを用いてスマートホーム向け汎用トリガー(接点多入力版)を作る

以下は『汎用トリガー(多入力版)の作成と、IFTTTでの動作確認(ルームライトの点灯・消灯まで』です。

使用するESP32ボードは前回と同じ『DOIT ESP32 DEVKIT V1』です。
(PCとボードの接続方法、プログラム(スケッチ)の書き込み方法はこちら『ESP32モジュールボードを使ってみる - その1:『PCセットアップ(Lチカまで)』編』を参照ください。)

●汎用トリガー(接点多入力版)の作成

1. 回路 - 汎用トリガー(接点多入力版)

汎用性を出すために接点入力部分については、
  • 入力接点の極性対応(Lアクディブだけでなく、Hアクディブへの対応)
  • 入力電圧幅を増加
の2項目に対応できるようにして、これまでの準備編の回路から大幅に変更しています(1入力版とは基本回路は同じです)。
回路図(汎用トリガー(接点多入力版))

実装例(汎用トリガー(接点多入力版):動作確認用ブレッドボード)


1-1. 接点入力(入力ポート)

ESP32ボードのIOポート22(D22)とIOポート21(D21)をセンサーの接点の入力ポートとして使用します。

センサーの出力は以下の2種類のケースが考えられます。
  • センサーが検知した時の出力がLOW電圧(Lアクティブ)
  • センサーが検知した時の出力がHigh電圧(Hアクティブ)
Lアクティブの方が一般的な出力だと思いますが、ここで作成するトリガーは汎用性を重視していますので、Hアクティブ出力のセンサーにも対応できるようにしました。
(対応可能なセンサーの出力仕様(電圧等)は上記回路図、または下記仕様書(データシート)を参照ください。)


1-2. LED表示(出力ポート)

ESP32ボードのIOポート23(D23)とLED表示用の出力ポートとして使用します。

ポートとGNDの間にLEDと電流制限抵抗(220Ω)を接続します。
Arduinoの出力は3.3V、LED(緑)のVFは2V程度なので、LEDに流れる電流は(3.3V ー 2V) ÷ 220Ω = 6mAとなります(LEDの明るさを変えるにはこの抵抗値を調整してください)。


- 【参考】ボードの電源

ボードに元からついているmicro USBコネクタ(プログラム書き込み・シリアル通信用)を使います。
スケッチ(プログラム)書き込んだ後はこのmicro USBコネクタにUSB電源(USB充電器等)を接続して動作させることができます(動作確認済み。但し、この場合PCとは繋がっていないので当然ですがシリアル通信は機能しません)。


2. プログラム(スケッチ) - 汎用トリガー(接点多入力版)

参照元のプログラム(スケッチ)は経過時間のオーバーフローに対応していないので、その部分を追加しています(オーバーフローまでに約50日間かかるのでまだ動作確認はできていません😅)。
また多入力に対応するために、サブルーチン化、変数の配列化など大幅に変更しています。

プログラム(スケッチ)の動き:
  • LED表示:
    • 起動後、Wi-Fiに接続するまではLEDを点滅。接続が完了したらLEDは消灯。
    • 接点信号を検出したらLEDは点灯
  • シリアル通信:
    • 起動後、Wi-Fiに接続が完了したらその旨をPCに送信
    • 接点信号を検出したらその旨をPCに送信
  • Webhooks:
    • 接点信号を検出したら、Webhooksに任意のテキスト(プログラムで設定可能)を発行。
(以下のサンプルプログラム中の” WiFi_ssid ”と” Password ”は接続するWi-FiネットワークのSSIDとパスワードに書き換えてください。
そして、" Private_key "はWebhooksのプライベートキーに書き換えてください(Webhooksについてとプライベートキーの取得方法は下で紹介しています)。
またWebhooksに発行するテキスト(イベント名)は任意に設定可能です。)

このプログラム(スケッチ)は2入力版ですが、以下の4箇所を書き換えることによって容易に3入力以上に変更可能です。
※① 入力チャンネル数
※② Webhooksで発行するテキスト(イベント名)
※③ 入力ポートの設定
※④ 入力のノイズ排除時間の判定基準時間

今回はプログラム(スケッチ)内のコメントも接点1入力版から大幅に更新しています。

ーーーサンプルプラグラム(汎用トリガー(接点多入力版))ーーー
 (" // ... "と" /* ... */ "の部分(黄色)はコメントです。)
/**
 *
 * ー スマートホーム向け汎用トリガー(接点多入力版) ー
 *
 *
 *  このスケッチを使用するためには
 *    ・Wi-FiのSSIDおよびパスワード
 *    ・Webhooksのプライベートキー
 *  の設定が必要です。
 *
 *
 *  引用元によると、このスケッチのWi-Fi接続およびスイッチ入力認識は
 *  Arduino IDEのサンプルを使っているそうです
 *  ★引用元:
 *    "CONNECT MAKER TO ANYTHING - IFTTT"
 *    https://bigjungle.net/blog/2015/6/26/connect-maker-to-anything-ifttt
 *
 *
 * 入力チャンネル数を増やすにはプログラム中の以下の4箇所(※印)を変更する
 *   ※① 入力チャンネル数
 *   ※② Webhooksで発行するテキスト(イベント名)
 *   ※③ 入力ポートの設定
 *   ※④ 入力のノイズ排除時間の判定基準時間
 *
 */

#include <WiFi.h>  // ヘッダファイル"WiFi.h"はArduino IDEに標準であるようです(別途インストール不要)

// Wi-Fi接続先の設定
const char *ssid     = "WiFi_ssid";   // WiFi_ssidには接続するWi-FiのSSIDを設定
const char *password = "Password";    // Passwordには接続するWiFiのパスワードを設

// Webhooks(IFTTT)の設定
const char *host = "maker.ifttt.com";      // WebhooksのホストURL
const char *privateKey = "Private_key";  // Private_keyにはWebhooksのあなたのプライベートキーを設定

// 入力チャンネル数
// ※① 入力チャンネル数を変更するにはこの数字を変える
const int nInput = 2;  

// Webhooksに発行するテキスト(イベント名)
// ※② 入力チャンネル数を変更するには、このテキストをチャンネル数用意する
const char *event_name[nInput] = {  
                  "input0_sensed",    // 入力チャンネル0のテキスト
                  "input1_sensed"     // 入力チャンネル1のテキスト
};

// ハードウェアの設定
// ※③ 入力チャンネル数を変更するには、この入力ポートをチャンネル数用意する
const int buttonPin[nInput] = {22, 21};     // 入力(接点)を割り当てるポート
const int ledPin = 23;                      // LEDを割り当てるポート

// 変数の定義
int buttonState[nInput];             // 入力ポートの今回状態
int lastButtonState[nInput];         // 入力ポートの前回状態


// 以下はタイマーとして使う変数で、代入するmillis()と同じ型の"unsigned long"で定義
unsigned long lastDebounceTime[nInput];  // 入力ポートの状態が切り替わってからのタイマー

// ※④ 入力チャンネル数を変更するには、この判定基準時間をチャンネル数用意する
unsigned long debounceDelay[nInput] = {50,50};    // 入力ポートの切り替わりがノイズか否か? の判定基準時間(50m秒)

/**
 * セットアップルーチン setup()
 *   ー スケッチ実行時の最初に1回だけ実行 ー
 */
void setup() {
  
  // IOポートのモード設定
  for(int i=0; i < nInput; i++){
    pinMode(buttonPin[i], INPUT);    // 入力ポートの設定
  };
  pinMode(ledPin, OUTPUT);           // 出力ポート(LED)の設定
  
  // シリアル通信のデータ転送レートの設定
  Serial.begin(115200);
  delay(10);
  
  // ボタンの前回状態(lastButtonState)の初期化
  for(int i=0; i < nInput; i++){
    lastButtonState[i] = LOW;
  };
  
  // タイマー(lastDebounceTime)の初期化(入力チャンネル毎)
  for(int i=0; i < nInput; i++){
    lastDebounceTime[i] = 0;
  };
  
  // Wi-Fi接続の開始
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  
  // Wi-Fi接続確立中はLEDが点滅
  int led = HIGH;  
  while (WiFi.status() != WL_CONNECTED) {
    delay(200);
    digitalWrite(ledPin, led);
    led = !led;
  }
  
  // Wi-Fi接続が確立したらLEDは消灯
  digitalWrite(ledPin, LOW);
  
 // Wi-Fi接続が確立したらシリアル通信でIPアドレスを送信
  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
}


/**
 * メインルーチン loop()
 *   ー スケッチ実行時にsetup()実行後、繰り返し実行 ー
 */
void loop() {
  for(int i=0; i < nInput; i++){
    sense_button(i);             // 入力チャンネル毎に入力の有無を確認
  }; 
}


/**
 * サブルーチン sense_button()
 *     ー 入力の有無を確認 ー
 */
void sense_button(const int buttonNumber) {
   
  // スイッチの現状の読み込み
  int reading = digitalRead(buttonPin[buttonNumber]);
  
  // millis()がオーバーフローしたらタイマーをリセットします
  if (lastDebounceTime[buttonNumber] > millis()) {
    // タイマーのリセット
    lastDebounceTime[buttonNumber] = millis();
  }
  
  // 以下はスイッチの現状(reading)を監視して、信号がノイズか本当の
  // 押下かの判定
  
  // スイッチの現状(reading)が前回状態(lastButtonState)からすぐに
  // 変わってしまったらノイズと見なしてタイマーをリセットします
  if (reading != lastButtonState[buttonNumber]) {
    // タイマーのリセット
    lastDebounceTime[buttonNumber] = millis();
  }
  
  // スイッチの現状(reading)が判定基準時間以上前回状態(lastButtonState)
  // 変化がない場合はノイズではないと見なします
  if ((millis() - lastDebounceTime[buttonNumber]) > debounceDelay[buttonNumber]) {
    
    // 入力チャンネル毎のボード入力の状態をシリアル通信で送信
    if (reading != buttonState[buttonNumber]) {
      Serial.print("Input");
      Serial.print(buttonNumber);
      Serial.print(" now ");
      Serial.println(HIGH == reading ? "HIGH" : "LOW");
      buttonState[buttonNumber] = reading;
      
      // スイッチの今回状態(buttonState)がLOWの場合はスイッチ
      // 押下とみなしてWebhooksを発行する
      if (buttonState[buttonNumber] == LOW) {
        send_event(buttonNumber);
      }
    }
  }
  
  // スイッチの前回状態(lastButtonState)に現状(reading)を保存する
  lastButtonState[buttonNumber] = reading;
}


/**
 * サブルーチン send_event()
 *     ー Webhooksにテキスト(イベント名)を発行 ー
 */
void send_event(const int buttonNumber) {
  
  // Webhooksにテキスト発行中はLEDを点灯する
  digitalWrite(ledPin, HIGH);
  
  // Webhooksにテキスト発行中はシリアル通信で接続したホストを送信
  Serial.print("Connecting to ");
  Serial.println(host);
  
  // TCP接続を行うためにWiFiClientを使用する
  WiFiClient client;
  const int httpPort = 80;
  
  // 接続が失敗した場合にはその旨をシリアル通信で送信する
  if (!client.connect(host, httpPort)) {
    Serial.println("Connection failed");
    return;
  }
  
  // Webhooks発行用のURLを作成する
  String url = "/trigger/";
  url += event_name[buttonNumber];
  url += "/with/key/";
  url += privateKey;
  
  Serial.print("Requesting URL: ");
  Serial.println(url);
  
  // 指定したURLにHTTP GETリクエストを送信する
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
               "Host: " + host + "\r\n" + 
               "Connection: close\r\n\r\n");
  
  // サーバーからの全ての返信データをシリアル通信で送信する
  // サーバーからの全ての返信データを受信したら接続を終了(切断)する
  while(client.connected()) {
    if(client.available()) {
      String line = client.readStringUntil('\r');
      Serial.print(line);
    } else {
      // サーバーからのデータがまだあるかも知れないので少し待つ
      delay(50);
    };
  }
  
  // すべてが完了したらその旨をシリアル通信で送信する
  Serial.println();
  Serial.println("closing connection");
  
  client.stop();
  
  // シリアル通信が完了したらLEDを消灯する
  digitalWrite(ledPin, LOW);
}
ーーーサンプルプラグラム [おわり]ーーー

【参照サイト】CONNECT MAKER TO ANYTHING - IFTTT

※ このプログラムはヘッダファイル『 WiFi.h 』を使用していますが、この『 WiFi.h 』はArduino IDEに標準で用意されているようです。別途用意する必要はありませんでした。


- 【参考】Webhooksについて(プライベートキーの取得)

Webhooksはアプリではありません。IFTTTのトリガー用にIFTTTが提供しているサービスのひとつです。IFTTTに登録されていないアプリ・サービスをIFTTTのトリガーにするためには必要不可欠なサービスです。

上記のプログラム(スケッチ)でこのサービスを利用するにはプライベートキーの取得が必要になります。以下に紹介します(事前にIFTTTの登録が必要です)。

IFTTTのPC版ページにあるWebhooksのページを開き『Documentation』を選択します。

開いた画面の『Your key is:』に記載されているのがあなたのプライベートキーです。この部分を保存し、上記のプログラム(スケッチ)で使用します。

以上です。


3. 動作確認用のIFTTTアプレット

3-1. 接点0検出時のアプレット

このアプレットは『Webhooksに発行されるテキスト(イベント名)"input0_sensed"受け取ったら、Nature Remo経由でルームライトをONにする』という内容です。

トリガー(IF)にWebhooksを、アクション(THEN)にNature Remoを設定します。

トリガー(IF)としてはWebhooksを選択し、『Event Name』には上記プログラム(スケッチ)でWebhooksに発行されるテキスト(イベント名)『input0_sensed』を入力します。

アクション(THEN)としてはNature Remoで"ライトを点灯する"(我が家の場合『ライト - 全灯』)を選択します。

以上で、接点1検出時のアプレットは完了です。


3-2. 接点1検出時のアプレット

このアプレットは『Webhooksに発行されるテキスト(イベント名)"input1_sensed"受け取ったら、Nature Remo経由でルームライトをOFFにする』という内容です。

トリガー(IF)にWebhooksを、アクション(THEN)にNature Remoを設定します。

トリガー(IF)としてはWebhooksを選択し、『Event Name』には上記プログラム(スケッチ)でWebhooksに発行されるテキスト(イベント名)『input1_sensed』を入力します。

アクション(THEN)としてはNature Remoで"ライトを消灯する"(我が家の場合『ライト - 消灯』)を選択します。

以上でIFTTTアプレットの設定は完了で、全ての準備が完了です。


4. 動作確認 - 汎用トリガー(接点多入力版)

以下の動作確認はESP32ボードをPCとUSB接続した状態で行います(シリアル通信を確認するためです)。


4-1. Wi-Fi接続確立時

Wi-Fi接続確立中はLEDが点滅し、確率後は以下のメッセージがシリアル通信でPCに送信されます。


4-2. 接点信号検出時

- 接点0検出時
接点0の信号検出時は、①LEDが点灯し、②以下のメッセージがシリアル通信でPCに送信され、③Webhooksにテキスト(今回の場合は"input0_sensed")が発行されます。

発行されたWebhooksのテキスト(今回の場合は"input0_sensed")に応じて④IFTTTのアプレットが実行されます(今回の場合、上記のアプレットの通りにNature Remo経由でライトが点灯します。)

- 接点1検出時
接点1の信号検出時は、①LEDが点灯し、②以下のメッセージがシリアル通信でPCに送信され、③Webhooksにテキスト(今回の場合は"input1_sensed")が発行されます。

発行されたWebhooksのテキスト(今回の場合は"input1_sensed")に応じて④IFTTTのアプレットが実行されます(今回の場合、上記のアプレットの通りにNature Remo経由でライトが消灯します。)


●汎用トリガー(接点多入力版)の仕様書(データシート)

今回作成した汎用トリガーの仕様書を作ってみました。
汎用トリガー(接点多入力版)仕様書(データシート)


【おまけ情報】
上記回路図および仕様書において、Hアクティブの時のOFF電圧(LOW電圧)を0.2V以下と定義していますが、これは使用しているデジタルトランジスタのデータを杓子定規に引用しているだけで、現実的には1V程度以下でOFFすると思います。


『汎用トリガー(接点多入力版)』の説明は以上になります。
苦手😅なコーディングはこれにて再び封印します😋。

今後はこの汎用トリガーにセンサー等を付けて実際に使ってみますね。


●参照サイト

CONNECT MAKER TO ANYTHING - IFTTT(プログラム(スケッチ))

Arduino 日本語リファレンス




3 件のコメント:
  1. はじめまして。『汎用トリガー(接点多入力版)』の記事、私も似た様なものを作ってみようと思っていたので興味深く拝見しました。大変恐縮ですが、もし可能であれば、『汎用トリガー(接点多入力版)』の回路図の解像度が高いものをご提供願えないでしょうか。部品を集めて作ってみたいと考えております。ご面倒をお掛けしますが宜しくお願いします。

    返信削除
    返信
    1. コメントありがとうございます。

      回路図をクリックしていただくと解像度の高い回路図が表示されるようにしているのですがご確認いただけましたでしょうか。
      (一応、文字もなんとか読める程度の解像度にしたつもりなのですが…。)

      お手数おかけしますが今一度ご確認いただけますでしょうか。

      削除
    2. いかがでしょうか。まだ画像が荒いでしょうか?

      削除