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


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

![SW_talkback.gif](https://qiita-image-store.s3.amazonaws.com/0/114533/abe2b818-0f06-10f3-d273-860dc084b80e.gif)



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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/ef5f21c2-c020-b7c8-0cff-5b93029fa3fd.png)


# もくじ
##### 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 ID**と**Write API Key**をメモっておきます。


# 2.	ThingSpeakのTalkBack設定

##TalkBackの作成

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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/a9c9dbe2-643c-a0dd-dcd6-1aa0884263c3.png)


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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/5e40c043-cb3d-daf2-c93d-1ad312e668a9.png)
 

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

 ![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/0c7fd001-9b01-3ce7-5a46-5818d63652b8.png)



##TalkBack ID / API Key / Command IDの確認

表示されている①**TalkBack ID** ②**(TalkBackの)API Key** ③**Command ID**を控えておきます。
 
![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/6df23780-a833-1eec-99d1-7efa3cae8409.png)


# 3.	磁気スイッチON/OFFデータを収集する(コマンドの書き込み)
##データ収集デバイスのコーディング
準備ができたら、デバイス連携を行います。まずはデータ収集とコマンドの書き換えから。
一つ目(データ収集側)のデバイスで行うのは下記です

![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/f0da5cd6-cd20-c104-d22c-fe5c906297fd.png)


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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/8d158a39-ec8a-4bee-458b-4012e0a29a09.png)

 
コマンドの書き換えは、Talkbackのページにアップデート方法が書かれているので、この通りにESP8266からコマンドを送ります。
 
![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/6f46a54d-042f-ebed-c6ce-4601f883c51b.png)


サンプルコード

```
#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のコマンドが定義される状態になりました。
続いてもうひとつの(コマンドに連動しアクションする）デバイスで用意する機能は下記です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/5e8a1dd5-c980-fa0b-7ae3-94c0629068e5.png)


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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/84aa5978-d0af-63dc-dbd2-7bd24ad926ad.png)


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

![image.png](https://qiita-image-store.s3.amazonaws.com/0/114533/cf4fc59a-2abe-f8c8-d236-45f08e86e840.png)


サンプルコード

```
#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個と拡張していくこともできます。