はじめに

鶴渕一成と申します。IoTについていろいろ勉強しています。収穫があったら随時ここに寄稿したいと考えています。
さて、WiFi付き、切手大、センサーハブ ESP-WROOM-02 はとても安価です。モジュール自体は600円位、各端子に接続しやすいように基板にはんだ付けされたもので1000円位とお父さんのお小遣い程度で買えてしまいます。
センサーハブが安ければ、そして小さければ、それを使ってIoTが爆発的に普及するかもしれません。
このセンサーハブを使いこなせるようにしておけば、技術者として引く手あまたなはず!?


ESP-WROOM-02からデータをインターネットに送信するという方法はいくつかネット上に載っていますが、受信する方法はあまりネット上に載っていません。
なので、この記事は多分重宝されるはずです。
受信方法を習得できれば、ブラウザを使って自宅のエアコンをオンオフ出来たりも夢じゃない!!

今回作るもの

ブラウザからコマンドを送って、ESP-WROOM-02に接続したLEDを、点灯させたり、消灯させたりする仕組みを作ります。

前提

以下は、この記事を読むに当たっての前提です。
最低限ここまでは出来ているという前提で話を進めていきます。
前提を満たすための参考リンクだけは出来るだけ載せておきます。
この辺を詳しく書いてほしいという要望があれば言ってください。

ESP-WROOM-02のプログラムの準備

ESP-WROOM-02のプログラムは、Arduinoで書きます。他にもpythonとか使えるらしいのですが、個人的にライブラリが揃っているのでArduinoを愛用しています。
Arduinoはc++をベースにした言語らしいので、必要な場合はC++を勉強するといいはずです。

ソースコードを1行ずつ解説すると長くなってしまうので、ポイントだけを説明し最後にソースコード全体を載せようかと思います。
ソースコードはサンプルや、JSON関連はサンプル参考にしました。

ポイント1 Bluemixとの接続周り

Bluemixとの接続と、Subscribeの指定は、95~108行目で以下のように行っています。
Bluemixと接続してなければ接続するまで接続操作を繰り返し、接続したらSubscribeの設定を行います。
18~29行目に格納された接続用の値を駆使して、接続操作を行います。

  if (!!!client.connected()) {
    Serial.print("Reconnecting client to ");
    Serial.println(server);
    while (!client.connect(clientId, authMethod, token)) {
      Serial.print(".");
      delay(500);
    }
    if (client.subscribe(subtopic)) {
      Serial.println("Subscribe ok");
    } else {
      Serial.println("Subscribe failed");
    }
    Serial.println();
  }

ポイント2 受信時の動きを関数で記述

Subscribeでデータを受け取った時の処理を関数で記述し、データを受け取った時に起動させる関数をsetCallbackファンクションで指定します。

ソースでは34行目でcallbackという関数を宣言しています。なおこの例では関数の中で受け取ったJsonデータをパースし、要素「cmd」の値を取り出しています。
さらに要素「cmd」の値が「on」の場合、LEDの接続されたピンをHIGH、すなわちLEDを点灯させます。

void callback(char* topic, byte* payload, unsigned int length) {
....
}

89行目で、callbackという名前の関数をデータを受け取った時に起動させる関数として指定しています。

  client.setCallback(callback);

ポイント3 受信処理コマンドの記述

これは重要です。受信処理コマンドを忘れるとセンサーハブがデータを受け取ってくれません。loopファンクションを使います。
私はサンプルソースを見てもこれに気づかずデータが受信できず数日間ハマりました。
ソースの110行目を参考にしてください。

client.loop();

ソース全体

c:LED_on_off.ino

/*
   This source code refer to the recipe of IBM developerWorks, https://developer.ibm.com/recipes/tutorials/connect-an-esp8266-with-the-arduino-sdk-to-the-ibm-iot-foundation/ .
*/
//Subscribeの cmd の値が on ならLEDをオンする。

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <string.h>

//LEDは ピン16
const int ledpin = 16;

//-------- Customise these values -----------
const char* ssid = "xxxxx";
const char* password = "xxxxx";

#define ORG "xxxxxx"      // 組織ID
#define DEVICE_TYPE "ESP8266"  // デバイス・タイプ
#define DEVICE_ID "ESP8266_001"      // デバイスID
#define TOKEN "xxxxxxxxxxxxxxx"              // 認証トークン
//-------- Customise the above values --------

char server[] = ORG ".messaging.internetofthings.ibmcloud.com";
//char topic[] = "iot-2/evt/status/fmt/json";
char subtopic[] = "iot-2/cmd/cid/fmt/json";
char authMethod[] = "use-token-auth";
char token[] = TOKEN;
char clientId[] = "d:" ORG ":" DEVICE_TYPE ":" DEVICE_ID;

WiFiClient wifiClient;
PubSubClient client(wifiClient);

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.println("Subscribed Message Receaved");

  char message[200];
  Serial.print("Message arrived [");
  Serial.print(topic);
  Serial.print("] ");
  for (int i = 0; i < length; i++) {
    Serial.print((char)payload[i]);
    message[i] = (char)payload[i];
  }
  Serial.println();
  Serial.println(message);

// JSON parse

  StaticJsonBuffer<200> jsonBuffer;
  JsonObject& root = jsonBuffer.parseObject(message);
  if (!root.success())
  {
    Serial.println("parseObject() failed");
    return;
  }
  const char* cmd = root["cmd"];
  Serial.println(cmd);
  if(strcmp(cmd, "on") == 0) {
      digitalWrite(ledpin, HIGH);
  } 
  else {
      digitalWrite(ledpin, LOW);
  }
}

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println();

  Serial.print("Connecting to ");
  Serial.print(ssid);
  WiFi.begin(ssid, password);

  // initialize digital pin 16 as an output.
  pinMode(ledpin, OUTPUT);

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

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

  client.setServer(server, 1883);
  client.setCallback(callback);
}

void loop() {
  // put your main code here, to run repeatedly:

  if (!!!client.connected()) {
    Serial.print("Reconnecting client to ");
    Serial.println(server);
    while (!client.connect(clientId, authMethod, token)) {
      Serial.print(".");
      delay(500);
    }
    if (client.subscribe(subtopic)) {
      Serial.println("Subscribe ok");
    } else {
      Serial.println("Subscribe failed");
    }
    Serial.println();
  }

  client.loop();
  delay(10000);
}

Bluemix側

全体

Node-Redフローエディタで全体は以下の通りです。これ以降各ボックスの詳細を掲載します。
tajima_iot_NR_ALL20160616.png

http_input([get]/test)

メソッドはGETを選びます。URLはここでは/testとしておきます。

ボックス ボックスの詳細
tajima_iot_box_httpin.png tajima_iot_NR_httpin20160616.png

http_output(http)

このボックスは詳細設定の必要はありません。

ボックス ボックスの詳細
tajima_iot_box_httpout.png tajima_iot_NR_httpout20160616.png

switch

payload.onoffの値(ブラウザから渡された要素「onoff」の値)が「on」だったら上から1番目のルート、「off」だったら上から2番目のルートに進むよう設定しています。

ボックス ボックスの詳細
tajima_iot_box_sw.png tajima_iot_NR_switch20160616.png

Function1(test command on)

ここでは、ESP-WROOM-02へ送信するデータを作っています。要素 msg.payload.cmd の値にon、要素 msg.deviceIdの値にAccess-techinica_001をセットしています。cmsgというJSONデータを作っているんですが、どうもこのボックスを出るときにcmsgというJSONデータはmsgというデータに名前を変えるみたい。事実、次のボックスではmsgという名前のJSONデータに要素も値も引き継がれています。

ボックス ボックスの詳細
tajima_iot_box_cmd1.png IoT_fanc01.PNG

Function2(test command off)

ここでも、ESP-WROOM-02へ送信するデータを作っています。要素 msg.payload.cmd の値にoff、要素 msg.deviceIdの値にAccess-techinica_001をセットしています。

ボックス ボックスの詳細
tajima_iot_box_cmd2.png IoT_fanc02.PNG

IoTout(IBM IoT App Out)

ESP-WROOM-02にデータを送り出す設定です。Device TypeとDevice idの2つの値で、どのセンサーにデータを送り出すかを識別しています。この2つの値は、センサーの方に書き込むソースのDevice TypeとDevice idの値と合わせてください。
この記事でのソースでは、

要素
Device Type ESP8266
Device id ESP8266_001

と設定してあります。
(ソースの19・20行目参照)

ボックス ボックスの詳細
tajima_iot_box_IoT.png IoT_out.PNG

いざ実行

ブラウザのURL欄に
http://[経路URL]/test?[要素名]=[値]
例)http://tajima-iot.eu-gb.mybluemix.net/test?onoff=on
と打ち込みます。
IoT_url02.PNG

「/test」というのは、Node-REDのhttp_inputボックスのURL欄に指定したものです。
また、[経路URL]はダッシュボードの「経路」に示されたURLです。またはNode-REDフローエディタのURLだと思ってください。
IoT_url01.PNG

http://[経路URL]/test?onoff=on とブラウザのURL欄に入力すると
P_20160619_193916.jpg

http://[経路URL]/test?onoff=off とブラウザのURL欄に入力すると
P_20160619_193919.jpg

このようにLEDが点灯したり消灯したりすれば成功です。??