mBaaSお役立ちブログ

ESP-WROOM-02を使用して自作IoTデバイスで他人のすねを叩く(myThings × mBaaS の作例)

こんにちは、石川大樹と申します。デイリーポータルZ(以下DPZ)というサイトの編集者です。その傍ら、頭の悪い電子工作をたしなんだり、「技術力の低い人限定ロボコン(通称:ヘボコン)」の主催なども行っております。

このたび「myThingsアプリ」に、ニフティクラウド mobile backend(以下mBaaS)の「mobile backendチャンネル」が加わったということで、DPZのほうに告知記事を書かせていただきました。

シリーズ「上司にダメージを与えるIoT」(1) スネ叩きマシン
http://portal.nifty.com/kiji/170228198910_1.htm

記事中で制作したのは、上司にダメージを与えるIoTデバイス「すね叩きマシン」。本編では技術的な部分に触れませんでしたので、この場を借りて解説させていただきます。私もIoTですねを折りたい!という方はあまりいないと思いますが、myThings × mBaaS の作例のひとつとして、ご笑覧いただければ幸いです。


↑これです

本体を作る

まずはすね叩きマシンの本体を作っていきましょう。(いきなりmyThingsもmBaaSも出てこない話で恐縮です)

DPZで工作をする場合、タイトな執筆スケジュールの中での制作になるため、「いかに早く作るか」に主眼を置いています。撮影中だけ動けばいいので、耐久性は重視しません。

この場合、使うべき材料は、こういったものです。

・塩ビパイプ
・ダンボール
・太さ1~数センチの角材
・薄い木の板

加工性が最優先、あと軽くて、安いこと。
今回はサイズが大きくて、かつ棒で構成すると作りやすそうだったので、塩ビパイプを選びました。25~40mm径くらいのが使いやすいでしょう。

これらが材料です。

塩ビパイプはジョイントを使って組み立てます。ジョイント―パイプ間は本来の用途であれば接着剤で止めるのですが、差し込んだだけでもそれなりに止まるので、僕は負荷のかかる箇所以外はそのままにしています。あとで運ぶとき分解できて便利です。
同じ理由で、負荷のかかる箇所であっても接着剤を使わず、穴を開けてボルトで止めることが多いです。

設計図等はないのでフィーリングで組み立てていきます。

支柱ができました。今回はこれにシーソーをつけて、シーソーの先端ですねを叩くことにしました。つまり、

こういうことです。(これは完成後)

電子工作で人を攻撃するときのポイントは、重力を活用することです。アクチュエータ(モーターとかソレノイドとか)で人を痛めつけるほどのトルクを得るのは困難ですが、重力を使えば簡単です。

支柱にサーボモーターをつけました。これでストッパーを動かします。
スタンバイ時はシーソーを持ち上げてストッパーをかけておき、スイッチONでサーボが動きストッパーを外す、という仕組みです。

シーソーをつけ、土台に すのこ も付けました(これがないと作動時の反動でマシンが倒れます)。

ストッパー部分のアップです。スタンバイ時の状態。どう動くのか気になる方は、この記事の最後、動作確認のところで動いている様子を見られます。

というわけで、本体はこれで完成です。やったね!

回路制作

いよいよ電子工作をしましょう。今回のメインとなるパーツは下記です。

タイトルにあるESP-WROOM-02は、Wifi機能つきのマイコンモジュールです。Arduinoの開発環境が使えるのですが、USB-シリアル変換等の部品を足さないとパソコンにつながらなかったりして、ちょっと面倒です。そんなESP-WROOM-02に足りない部品をあらかじめ追加して、Arduino感覚で使えるようにしたのがESPr Developerです。通称エスパーとか呼ばれています。Arduinoに比べるとIOピンが少ないのが弱点といえば弱点でしょうか。

親指で持っている青いのがESPrです。

上の基板にはサーボにつなぐコネクタとかテスト用スイッチ等のこまごましたものがついています。ブレッドボードで組むと運搬や保管がしにくいので、僕はいつもユニバーサル基板に実装するようにしています。回路としてはこんな感じです。(ふだん回路図をあんまり描かないので、変だったらすみません)

※図中にESP-WROOM-02がいますが、これをESPr Developerに読み替えてください。回路図では電源をVCCとGNDにつないでいますが、実際にはUSB端子に入れました。ただ、このあとVCCに5Vを入れても問題なく動くことがわかったので、図のとおりの回路でも問題なかったかも。

動作テスト用スイッチを、IO12で入力しています。プルアップ抵抗をつけました。
IO13はサーボ制御用のパルスを出力します。

ハードウェアとしてはこれでひとまず完成です。
無線制御だと入出力周りがすっきりして楽ですね。

 

myThings、mBaaS、IDCFの設定

myThingsにもmBaasにも触れずに記事が半分終わってしまいました。いよいよ登場です。
今回のシステムの動作フローはこのようになっています。

実現したい機能は「ゲームでn回以上コンティニューが行われると、すね叩きが作動する」です。

コンティニュー回数はmBaaSのデータストア上に保持しています。これは誰かがコンティニューするとインクリメントされます。

myThings上にてmBaaSデータストアの「更新」をトリガに設定すると、myThingsは約15分に1回更新状態をチェックして、更新があればその値をアクション側に送信してくれます。アクション側ですが、myThingsを使ってデバイスと連携するにはIDCFチャンネルサーバを使うといいようなので、今回はひとつ用意してもらいました。

IDCFチャンネルサーバがアクションを起こすと、トリガとなったmBaaSデータストアの値をMQTTメッセージとして送信してくれます。すね叩きマシンではそのメッセージをESPrで受け取り、保持していた前回値と比較、閾値以上増加していれば、すねを叩きます。

具体的な連携方法ですが、mBaaSとmyThingsについては、こちらをご覧ください
myThingsとIDCFの間の連携については、このページの下の方にチュートリアルがありました。今回はSTEP2の「サーバの認証」のページまでできていればOKです。

結局、今回はこんな組み合わせを作成しました。

なお、myThingsでは今回と逆の組み合わせ、つまりIDCFをトリガにしてmBaaSをアクションに設定することもできます。
これを使えば、たとえば今回の場合、マシンが動くたびにmBaaSデータストアのコンティニュー回数を0にリセットすることも可能です。そうすればESPr側で前回値を持っておく必要がなくなりますね。

プログラムを書き込む

最後に、ESPrにプログラムを書き込みましょう。今回、プログラムすべきことはざっくり

・Wifiの接続
・前回のコンティニュー数をEEPROMから取得
・メッセージの待機
・MQTTメッセージを受信したらコンティニュー回数の差を計算
・差が閾値を越えていたらサーボを動かす
・最新のコンティニュー回数をEEPROMに記録

です。

※EEPROMというのはESPrに(Arduinoにもあります)内蔵されている不揮発性メモリで、電源を落としても内容が維持されます。今回は前回値との比較をすね叩きの動作条件にしているため、内部メモリのみだとデバイスを再起動したタイミングで前回値がクリアされ、次回必ずすね叩きが発動してしまうことになります。なのでバックアップとしてEEPROMを使用しています。

書き込みの前に、ArduinoIDEへのボード設定の追加、および今回使用するライブラリの追加が必要です。

ボードの設定追加方法は販売元公式のマニュアルが用意されています。
ライブラリはMQTTメッセージの送受信を行うためのPubSubClient、それからMQTTメッセージの中身がJSONで送られてくるのでJsonライブラリも必要です。Jsonはメニューの Sketch>Include Library>Manage Libraries… から「Json」で検索すればインストールできます。PubSubClientですが、ライブラリマネージャから入れられるものはうまく動きませんでした。Imroy/pubsubclientを使用しましょう。(参考ページ

実際に使用したコードは下記のとおりです。ライブラリのサンプルコード、および上記参考ページのコードを参考にしました。

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Servo.h>
#include <EEPROM.h>
#include <ArduinoJson.h>

#define FIRE_THRESHOLD 5     //発動閾値
#define PINNO_DEBUG_SWITCH 12 //デバッグ用スイッチのIOピン
#define PINNO_SERVO 13        //サーボのIOピン
#define SERVO_POS_INITIAL 0   //サーボの初期位置
#define SERVO_POS_FIRED 150   //発動後のサーボの位置

Servo servo;

//Wifi接続設定
const char *ssid =  “********”; //SSID
const char *pass =  “********”; //PASS
//IDCF関連設定
IPAddress server(000, 000, 000, 000); //IDCFサーバのIPアドレス
const char* action_1_uuid = “************”; //action1のUUID
const char* action_1_token = “***********”; //action1のtoken

int numberOfContinues;  //総コンティニュー回数

//MQTTメッセージ受信用コールバック関数(メッセージ受信すると呼ばれる)
void callback(const MQTT::Publish& pub) {
Serial.println();
Serial.print(” => “);
Serial.println(pub.payload_string());

//JSONをパースしてペイロード値(中身は総コンティニュー回数)の取得
DynamicJsonBuffer jsonBuffer;
JsonObject& root = jsonBuffer.parseObject(pub.payload_string());

if (!root.success())
{
Serial.println(“parseObject() failed”);
return;
}

int payload = root[String(“data”)][String(“payload”)];
Serial.print(“payload: “);
Serial.println(payload);

//最新の総コンティニュー回数をEEPROMに書いておく
EEPROM.write(0, (payload >> 0) & 0xFF);
EEPROM.write(1, (payload >> 8) & 0xFF);
EEPROM.commit();

//発動させるかどうかの判定
int numberOfRecentContinues = payload – numberOfContinues;
Serial.print(“Number of recent continues: “);
Serial.println(numberOfRecentContinues);

if (numberOfRecentContinues >= FIRE_THRESHOLD)
{
//発動
servo.write(SERVO_POS_FIRED);
delay(5000);
//servo.write(SERVO_POS_INITIAL);
//※手で戻すので自動では戻さない
}

numberOfContinues = payload;

}

WiFiClient wclient;
PubSubClient client(wclient, server);

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

pinMode(PINNO_DEBUG_SWITCH, INPUT);
servo.attach(PINNO_SERVO);
servo.write(SERVO_POS_INITIAL);

//前回までのコンティニュー回数をEEPROMから読み込む
EEPROM.begin(100);
numberOfContinues = ((EEPROM.read(0) << 0) & 0xFF) + ((EEPROM.read(1) << 8) & 0xFF00);
Serial.print(“Accumulated numer of continues from EEPROM: “);
Serial.println(numberOfContinues);
}

void loop() {
//テスト用スイッチの処理
if (digitalRead(PINNO_DEBUG_SWITCH) == LOW){ //プルアップ抵抗入れたのでスイッチ押すとLOW
servo.write(SERVO_POS_FIRED);
delay(5000);
//servo.write(SERVO_POS_INITIAL);
//※手で戻すので自動では戻さない
}

//Wifiつながってなかったらつなぐ
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 (WiFi.status() == WL_CONNECTED) {
//MQTTつながってなかったらつなぐ
if (!client.connected()) {

MQTT::Connect mqttConnect(“arduinoClient”);
mqttConnect.set_auth(action_1_uuid, action_1_token);

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

//WifiもMQTTもつながってる
if (client.connected()){
client.loop();
}
}
}

プログラムの書き込みができない場合は、Reset Methodをnodemcuに設定しているか確認しましょう。

 

動作確認

書き込みが終わったら実際に動かしてみます。まずテスト用ボタンを押してストッパーの動作チェック。

いいですね!
オンラインでの動作チェックは、mBaaSのデータストア上の値を手動で変更し、myThingsアプリ上で使用中の組み合わせを呼び出し「手動実行」することで確認できます。

 

おわりに

以上、すね叩きマシンの技術解説でした。実際の使用風景はDPZの記事のほうでご覧ください。

シリーズ「上司にダメージを与えるIoT」(1) スネ叩きマシン
http://portal.nifty.com/kiji/170228198910_1.htm

できることならみなさまは僕に倣わず、この記事で得た知見を平和利用していただければ幸いです。よろしくおねがいいたします。