地球の反対側にあるESP8266を複数台連携させる


概要

IoTはモノとモノがつながる世界!

ということで「インターネット経由でデバイスがデバイスを動かす」を試してみます。具体的にはWiFi接続された2つのESP8266を用いて、IoTプラットフォームThingSpeak を介し、片方がもう一方を動作させる方法を紹介します。インターネット経由なので、各々Wi-Fi接続されていれば、一方が地球の反対にあっても同じ動作をします。

これは磁気スイッチの開閉を検知して、離れた場所でLチカ(開くとON)するシンプルな例ですが、入出力は様々なものに置き換えて応用できます。
デバイスの遠隔操作というと難しそうなイメージがあるかもしれませんが、今回はThingSpeakのTalkBackという機能を利用し、各デバイスではコマンドの「書き換え」「読み取り」のみの簡単なコーディングで連携できます。

もくじ

1. 準備
2. ThingSpeakのTalkBack設定
3. 磁気スイッチON/OFFデータを収集する(コマンドの書き換え)
4. TalkBackのコマンドに連動してLチカさせる(コマンドの読み取り)
5. まとめ

1. 準備

■ハードウェア

●ESPr® One ×2 https://www.switch-science.com/catalog/2620/
●リードスイッチ(磁石付セット) http://akizukidenshi.com/catalog/g/gP-04025/
●その他(LED,抵抗,ジャンパワイヤ、USBmicroケーブル等)

■開発環境

Arduino IDE (作成時は1.6.5を利用)

■ThingSpeakのアカウント、Channel作成

IoT プラットフォーム ThingSpeak
https://thingspeak.com/

ThingSpeakは無料で使い始めることができるIoTプラットフォームで、「データ収集と可視化」「解析」「アクション」の機能を持っています。

ThingSpeakの基本的な使い方は、こちらでわかりやすくまとめられています。
データを簡単に保存&グラフ化できるThingSpeakが便利!
http://iwathi3.hatenablog.com/entry/Data-to-Graph-ThingSpeak

データロギングに使うChannel IDWrite API Keyをメモっておきます。

2. ThingSpeakのTalkBack設定

TalkBackの作成

ThingSpeakの[Apps][TalkBack]をクリックします。

つづいて[New TalkBack]をクリックします。

次にTalkBackの名前と[Add a new Command]をクリックし、Command Stringに「SW_OFF」と入力します。これは事前にCommand IDを取得するのが目的なので、このCommand Stringはデバイスから書き換えて利用します。入力が終わったら[Save TalkBack]を押します。

TalkBack ID / API Key / Command IDの確認

表示されている①TalkBack ID(TalkBackの)API KeyCommand IDを控えておきます。

3. 磁気スイッチON/OFFデータを収集する(コマンドの書き込み)

データ収集デバイスのコーディング

準備ができたら、デバイス連携を行います。まずはデータ収集とコマンドの書き換えから。
一つ目(データ収集側)のデバイスで行うのは下記です

ESPr® Oneの入力PinはIO2を利用し、磁気スイッチの他方のリードはGNDに接続します。
Pin2をInputモードにし、その状態に応じて「SW_ON」もしくは「SW_OFF」のコマンドをThingSpeakのTalkBackに送ります。磁石が離れているとPin2はHighになります。 下記のサンプルではputTalkBack()でコマンドの書き換え、writefield()でChannelへのデータロギングを行っています。

コマンドの書き換えは、Talkbackのページにアップデート方法が書かれているので、この通りにESP8266からコマンドを送ります。

サンプルコード

#include <ESP8266WiFi.h>

char thingSpeakAddress[] = "api.thingspeak.com"; 
unsigned long myChannelNumber = ******; // Channel ID
String myWriteAPIKey = "****************"; // Channel Write API Key
String talkBackID = "*****"; // TalkBack ID
String talkBackAPIKey = "****************"; // TalkBack API Key
String COMMAND_ID ="*******"; // TalkBackで作成したCommand ID
String url = "/talkbacks/"+talkBackID+"/commands/"+COMMAND_ID;
const char* host = "api.thingspeak.com";
String magsw ="0";

char ssid[] = "******";   //  無線LANのSSID 
char pass[] = "******";   // 無線LANのpassword

int status = WL_IDLE_STATUS;
WiFiClient  client;

void setup() {
  Serial.begin(115200);
  WiFi.begin(ssid, pass); // WiFi接続
  pinMode(2, INPUT); // IO2を入力Pinに設定
}

void loop() {
// スイッチが離れ、IO2がHighになったら
// Channel Fieldへの書き込みとTalkBackのコマンドの書き換えを行う
  if (digitalRead(2) == HIGH){ 
    String magsw = "1";
    String commandstr = "SW_ON";
    writefield(magsw); // Channelへのデータ書き込み
    putTalkBack(commandstr); // TalkBackコマンドの変更
  }
  else{
    String magsw = "0";
    String commandstr = "SW_OFF";
    writefield(magsw);
    putTalkBack(commandstr);
  }

  delay(100); // 無料版使用の場合はChannelへの書き込みは15秒に1回に変更。delay(15000)
}

  void putTalkBack(String commandstr) 
 {
    if (client.connect(thingSpeakAddress, 80))
  { 
    Serial.println("Connected to ThingSpeak.");
    Serial.println("talkback post: " + commandstr);
    String tsData = "api_key="+ talkBackAPIKey +"&command_string="+commandstr;
    // talkbackコマンド書き換えのためにhttpコマンド送信
    client.print(String("PUT ") + url + "&headers=false" + " HTTP/1.1\r\n" 
                                      + "Host: " + host + "\r\n"
                                      + "Connection: close\r\n"
                                      + "Content-Type: application/x-www-form-urlencoded\r\n"
                                      + "Content-Length: "+tsData.length()+"\n\n"+tsData+"\n\n");
    client.stop();
    delay(100);
  }
  else
  {
     Serial.println("Connection failed.");
  }
 }

  void writefield(String magsw) 
 {
    if (client.connect(thingSpeakAddress, 80))
  { 
    Serial.println("Connected to ThingSpeak for write field.");
    Serial.println("write data: " + magsw);
    String SWData = "api_key="+ myWriteAPIKey +"&field1="+ magsw;
    // Channel fieldへのデータ書き込みのためにhttpコマンド送信
    client.print(String("POST /update HTTP/1.1\r\n") 
                                      + "Host: " + host + "\r\n"
                                      + "Connection: close\r\n"
                                      + "Content-Type: application/x-www-form-urlencoded\r\n"
                                      + "Content-Length: "+SWData.length()+"\n\n"+SWData+"\r\n\r\n");
    client.stop();
  }
  else
  {
   Serial.println("Connection failed.");
  }
 }

4. TalkBackのコマンドに連動してLチカさせる(コマンドの読み取り)

Lチカするデバイスのコーディング

3ではスイッチの変化でTalkBackのコマンドが定義される状態になりました。
続いてもうひとつの(コマンドに連動しアクションする)デバイスで用意する機能は下記です。

TalkBackのコマンドは下記のようにGET コマンドで読み取ることができます。
ここでも必要なのは①TalkBack ID(TalkBackの)API KeyCommand ID

取得ができたら、コマンドに応じて出力Pinの「HIGH」「LOW」を切り替えるだけです。

サンプルコード

#include <ESP8266WiFi.h>

const char* ssid     = "***"; // 無線LAN SSID
const char* password = "***"; // 無線LAN password
const char* host = "api.thingspeak.com";
String TalkBackID ="*****"; //Talkback ID
String TalkBackAPIKey ="****************"; // Talkback API key
String Command_ID ="*******"; // Talkbackで作成したコマンドID
// TalkBackコマンド取得のためのURL
String url = "/talkbacks/"+TalkBackID+"/commands/"+Command_ID+"?api_key="+TalkBackAPIKey;

// Use WiFiClient class to create TCP connections
WiFiClient client;
const int ledPin =  13; //Pinの指定IO13

void setup() {
  Serial.begin(115200);
  delay(100);

  // WiFiに接続
  Serial.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  Serial.println("");
  Serial.println("WiFi connected");  
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  pinMode(ledPin, OUTPUT);
}

void loop() {

  Serial.print("connecting to ");
  Serial.println(host);

  if (client.connect(host, 80)) {
    Serial.println("Tcp Connection eastablished with thingSpeak API");
  }
  else {
    Serial.println("connection failed");
    return;
  }
  // 接続が確認されたらTalkBackの現在のコマンドの状態を取得する
  client.print(String("GET ") + url + "&headers=false" + " HTTP/1.1\r\n" + "Host: " + host + "\r\n" + "Connection: close\r\n\r\n");

  delay(100); 
  // メッセージの読み取り
  String messageBody = "";
  while (client.available()) {
    String line = client.readStringUntil('\n');
    if (line.length() == 1) { 
      messageBody = client.readStringUntil('\n');
      break;
    }
  }
  // コマンドのON/OFFに応じて出力を変更する
  Serial.print("Found a message, the message is: ");
  if(messageBody == "SW_ON"){
    Serial.print(messageBody);
    digitalWrite(ledPin, HIGH);
  }
  if(messageBody == "SW_OFF"){
    Serial.print(messageBody);
    digitalWrite(ledPin, LOW);
  }

  client.stop();

  Serial.println();
  Serial.println("closing connection");
  Serial.println();
  delay(100);

}

5. まとめ

ThingSpeakのTalkBackを用いて
・HTTPコマンドを投げる
・Lチカ(Pin I/Oの制御)
の基本的な機能で複数のESP8266を連携することができました。
コマンドで管理することで、動かす側、動かされる側それぞれ分けて接続を考えればよく、
Channel Fieldへの結果のロギングも合わせてデバックも非常に簡単になります。
デバイスを3個4個と拡張していくこともできます。