ESP8266を使ってmyThings連携して今日の降水確率をLEDの色で表現してみる


myThingsとは

myThingsはヤフーから出しているアプリで、IFTTTのように、「もし〜だったら〜する」といった組み合わせを設定することで毎日を便利にしてくれます。

myThingsは、「もし〜だったら〜する」の〜の部分に、ヤフー天気やヤフオク、Twitter、Facebook、SlackなどといったWebサービスやWithingsやFitbit、iRemoconといったIoTデバイスと連携することができます。

これらWebサービス/IoTデバイスでの行動をトリガーとして、さらに別のWebサービス/IoTデバイス上でアクションを起こすことができます。
例えばこんなことができます。

  • もし明日の東京都港区の天気が雨の予報だったらSlackに「傘を忘れないで」と投稿する
  • Fitbitで消費したカロリーを計算してケーキ何個分か教えてくれる
  • Twitterで「ももクロ」を含むツイートがあったらBOCCOに喋らせる

自分もmyThingsで毎日朝起きるくらいの時間に今日の天気予報が雨だったら「今日は雨です」とiPhoneに通知させてたりするのですが、朝起きた時って通知いっぱいたまってて重要な情報だけ見るの難しいし、そもそも朝は忙しくて通知なんて見る暇ないんですよね。
だから雨降るってこと知らないで傘持って行かずに帰りずぶ濡れなんてこともある訳です。

いつでも自然に「今日雨降るんだ」とか「今日電車遅れてるんだ」とか必要な情報だけを簡単に知れたらいいなと常日頃から思ってるんです。

簡単に知れるというのはやっぱり視覚からの情報がいいんですけど、スマホではない別のモノからの視覚情報で子供からお年寄りまで理解できたら嬉しいですよね。
今日の雨降りそうかどうかくらい1画素で表現できるじゃないですか。
フルカラーLEDひとつで表現できるじゃないですか。
myThingsは自作のIoTデバイスとも連携することができるじゃないですか。

そう、自作のIoTデバイスやスマホのセンサーで取得した情報をmyThingsに流してトリガーに設定することもできるし、Webサービス上の行動を自作のIoTデバイスにフィードバックしてLED光らせたりモーター動かしたりなんかもできちゃうんです。

やってみたくなりますよね。

というわけで、今回は「Webサービス上の情報をLEDの色で表現するデバイス」を作って、
試しに「今日の降水確率をLEDの色で表現」してみようと思います。

事前準備

IDCFにMQTTブローカーを構築する

自作のIoTデバイスとmyThingsを連携させるためにはIDCFクラウドと契約して仮想サーバを建てて、
その上にMQTTブローカーを構築する必要があります。
手順は公式ページに詳しく載っているのでを見ながら順番にやっていけば簡単に構築できます。
公式ページを見ながらmyThingsアプリでIDCFチャンネルを認証するところまでやります。

ESP8266を使う環境を整える

ESP8266とは

ESP8266は最近巷で話題の技適取得済みで安価なWi-Fiモジュールです。
GPIO、I2C、PWM、10ビットADコンバータなど多彩なインターフェースを持っている上、
秋月で1つ550円という破格で販売されています。
店によっては300円とかで売ってるところもあるみたいです。
さらにこのモジュール、大きめのメモリとかCPUを積んでるので普通にこれ単体でなんでもできてしまうんです。

ESP8266焼き機を作る

ESP8266はIOピンなどがあるだけでプログラムを焼くためのUSB端子などは付いてません。
USBシリアル変換機を使って自分でPCからESP8266にプログラムを焼くための焼き機を作る必要があります。
汚いですがこんな回路図で作りました。

ESP8266は焼き込み前にフラッシュダウンロードモード(書き込みモード)にする必要があります。
書き込みモードにするときは

  • IO15をLOW
  • IO2をHIGH
  • IO0をLOW
  • RSTをLOW にします。

タクトスイッチ1を押すとIO2がGNDに落ち、同時にタクトスイッチ2を押すことでリセットがかかり、
フラッシュダウンロードモードとなるようにしました。
さらに、ESP8266の各IOピンにジャンパーピンをさせるように二重でピンソケットをつけてプロトタイピングを行いやすくしました。

ESP8266にフルカラーLEDを繋ぐ

今回使ったのは家に転がってたこちらのLEDを使いました。
こいつをESP8266のIOピンに接続します。

プログラミング

ArduinoIDEからESP8266に焼きこめるようにする

ここを参考にさせていただきました。
この通りやればできるはずですが、一応自分の実行環境の設定をメモしておきます。

  • マイコンボード:Generic ESP8266 Module
  • Upload Using:Serial
  • CPU Frequency:80MHz
  • Flash Frequency:40MHz
  • Flash Mode:DIO
  • Upload Speed:11520
  • Flash Size:512K(64K SPIFFS)
  • Reset Method:ck

プログラムを書く

やってることはざっくりこんな感じです。

  • Wi-Fiに接続する
  • IDCFに建てたMQTTブローカーをSubscribeする
  • メッセージを取得したらJSONパースして降水確率を取得する
  • 降水確率に応じて光らせる色を変える
    • 0〜30%:青
    • 30〜70%:緑
    • 70%以上:赤

こちらの記事を参考にさせて頂きました。
設定用のファイルとメインのコードで分けてます。
(ファイルを分けるのがどうやるかわからなかったのですが、ArduinoSDKの右上の下三角形から追加できました。)

config.h

// MQTTのクライアントID(ユニークにするためにスケッチをコンパイルした日付と時刻を使用)
const char *mqtt_client_id = "dkawashi" __DATE__ __TIME__;

// Wi-FiアクセスポイントのSSIDとパスワード
const char *ssid =  "<SSID>";
const char *pass =  "<password>";

// IDCF Cloudに関する設定
const char* action_1_uuid = "<action-1のUUID>";
const char* action_1_token = "<action-1のtoken>";
IPAddress server(210, , , ); // IDCFで立てたサーバのIP(カンマ区切り)

main.ino

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

#include "config.h"

#define BUFFER_SIZE 200

// メッセージを受け取ったらシリアルにプリント
void callback(const MQTT::Publish& pub) {
  //Serial.print(pub.topic());
  //Serial.print(" => ");
  if (pub.has_stream()) {
    // ペイロードのサイズが大きい場合にはローカルに用意したバッファに分割して読み取り
    // 読み取った単位ごとにシリアルにプリント
    /*
    uint8_t buf[BUFFER_SIZE];
    int read;
    while (read = pub.payload_stream()->read(buf, BUFFER_SIZE)) {
      Serial.write(buf, read);
    }
    pub.payload_stream()->stop();
    Serial.println();
    */
  } else {
    // ペイロードのサイズが小さい場合にはそのままシリアルにプリント
    StaticJsonBuffer<200> jsonBuffer;
    String str = pub.payload_string();
    Serial.println(str);
    char buf[BUFFER_SIZE];
    str.toCharArray(buf, BUFFER_SIZE);
    JsonObject& root = jsonBuffer.parseObject(buf);
    int rain = root["data"]["payload"];
    Serial.println(rain);
    if (rain > 70) {
      digitalWrite(12, LOW);
      digitalWrite(13, HIGH);
      digitalWrite(15, HIGH);
    } else if (rain > 30) {
      digitalWrite(12, HIGH);
      digitalWrite(13, LOW);
      digitalWrite(15, HIGH);
    } else {
      digitalWrite(12, HIGH);
      digitalWrite(13, HIGH);
      digitalWrite(15, LOW);
    }
  }
}

WiFiClient wclient;
PubSubClient client(wclient, server);

void setup() {
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(15, OUTPUT);
  digitalWrite(12, HIGH);
  digitalWrite(13, HIGH);
  digitalWrite(15, HIGH);
  Serial.begin(115200);
  delay(10);
  Serial.println();
  Serial.println();
}

void loop() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.print("Connecting to ");
    Serial.print(ssid);
    Serial.println("...");
    WiFi.begin(ssid, pass);

    if (WiFi.waitForConnectResult() != WL_CONNECTED) {
      return;
    }
    Serial.println("WiFi connected");
  }

  if (!client.connected()) {
    // アクション1のUUIDとトークンをユーザ名およびパスワードとしてサーバに接続
    MQTT::Connect mqttConnect(mqtt_client_id);
    mqttConnect.set_auth(action_1_uuid, action_1_token);

    if (client.connect(mqttConnect)) {
      client.set_callback(callback);
      client.subscribe(action_1_uuid);
    }
  }

  if (client.connected()) {
    client.loop();
  }
}

myThingsアプリで組み合わせを登録

あとはmyThingsアプリで組み合わせを登録するだけです。
トリガーに天気・災害を選んで今日の天気予報を選択します。
今回は東京都港区の天気を設定しました。
アクションにはIDCFを選択します。
アクションの設定方法についてもIDCF公式ページに詳細が載っているのでそちらで確認しましょう。

実行タイミングは設定しませんでした。時間指定しない場合は15分間隔で実行してくれます。

いったんこれで設定を保存します。
15分待つか、再度設定画面に行って下部の手動実行をタップすると即座に実行することができます。

動作確認・完成

実際に動いてるところです。

赤くなっているのできっと今日は雨ですね。