[2020/05/26追記 SORACOM UG Tokyo Online # 15 のLT内容に合わせてアップデート]
COVID-19の影響でリモートワークな人です。
今回は、
あのボタンを押したらM5Stackのディスプレイに今何してるか表示させる
っていうのをやってみました。
きっかけ
きっかけはソラコム小熊さんが投稿されてた
M5stack で作る ON AIR サインで手に入れる平穏な在宅勤務環境
です。
ボタン使ってできるんじゃないかなーって思って、誰か(ソラコム某エバンジェリスト)が作るんじゃないかなーとちょっとボールを投げたら、
見事に投げ返されたので、まあ、面白いからやってみぜよと。
まあ、事実としては、居間でテレワークしている人がいるので、会議中に居間行きたくないので、それがわかるようにも使えそうだしってこともありまして。。。
技術力の問題で時間かかってしまいました。。。
できました。
構成
実際動作映像
ボタンと文字の紐付けはこんな感じ。
シングルクリック : ON MEETING (ミーティング中的な)
ダブルクリック : OFF MEETING (ミーティング終わったよ)
長押し: END OF WORK (仕事終わったよ)
色は適当に選んでいるので、明暗がおかしくなっていてすみません。
-
シングルクリック
シングルクリック pic.twitter.com/7o9BHyCBq3
— Kenichiro Wada (@Keni_W) April 16, 2020 -
ダブルクリック(シングルクリックしたあとの状態から)
ダブルクリック pic.twitter.com/FoMl4PgS6G
— Kenichiro Wada (@Keni_W) April 16, 2020 -
長押し(ダブルクリックしたあとの状態から)
長押し pic.twitter.com/lMebp89CKC
— Kenichiro Wada (@Keni_W) April 16, 2020
必要なもの
- M5Stack Basic 3G 拡張ボード セット : ¥12800 + Tax
- 特定地域向け IoT SIM (plan-D) : ¥852
- IoTボタン
-
AWS IoT 1-Click デバイスに載っているもの。
- AWS IoT Enterprise Buttonって販売終了してたんですね。日本国内で、新規で買う場合は、LTE-M Button powered by AWS 一択になるのかな?
-
AWS IoT 1-Click デバイスに載っているもの。
- SORACOMアカウント
- AWSアカウント
- Arduinoに対応したIDEを搭載したパソコン
M5Stack Basic 3G 拡張ボード セット、去年のソラコムさんのイベントのクイズで当ててて、マジよかったです。
後述しますが、あって便利だったもの
- Wifiルーター
- 特定地域向け IoT SIM (plan-D)もう1枚
実装とか
- コードは別途Githubにあげます。
- M5Stackおよび3G 拡張ボードのセットアップは参考文献にあります。
Lambda
コードのベースは、
SORAZINE 技術組 Vol.2019Fの「IoTを活用した遠隔操作の概要と実践」の章に記載されていたLambdaのコードです。
Node.jsで書こうかと思ったんですが、ちょっとスピード勝負でってことで、流用しました(言い訳)。
久々のPython。作業用のMacのPythonが2.7で焦ったのは内緒。
2020/05/26コード変更 SORACOM Funk対応にアップデート
import json
# メッセージ
CLICK_TYPE_MESSAGE = {
'SINGLE': 'ON MEETING',
'DOUBLE': 'OFF MEETING',
'LONG':'END OF WORK'
}
# LCDのカラー
CLICK_TYPE_COLOR = {
'SINGLE': '0xF800',
'DOUBLE': '0x000F',
'LONG': '0xFD20'
}
# ボタンのクリックのタイプ
CLICK_TYPE_INT = {
'SINGLE': 1,
'DOUBLE': 2,
'LONG':3
}
import boto3
iot = boto3.client('iot-data')
def lambda_handler(event, context):
print(json.dumps(event, ensure_ascii=False))
if 'deviceEvent' in event:
click_type = event['deviceEvent']['buttonClicked']['clickType'] # LTE-M Button powered by AWS
else:
click_type = event['clickTypeName'] # LTE-M Button for Enterprise/LTE-M Button Plus
try:
message = CLICK_TYPE_MESSAGE[click_type]
color = CLICK_TYPE_COLOR[click_type]
status = CLICK_TYPE_INT[click_type]
except KeyError:
return
body = {
'message': message,
'color': color,
'status':status
}
payload = json.dumps(body, ensure_ascii=False)
# AWS IoT CoreのMQTT BrokerへPublish
iot.publish(topic='iotbutton/status', payload=payload, qos=0)
response = {
'statusCode': 202,
'body': json.dumps(event, ensure_ascii=False)
}
return response
一応こだわった点は、
別にいっぱい配るとかそういうわけではないですが、
できる限りクラウド側(Lambda)に処理は任せようって思ったので、Payload増えちゃうけど、
メッセージ、表示色はLambda側で定義してます。定義しているだけですけど。
色とかメッセージ変えたければ、Lambda更新して完了にはなるので、変更は容易かと。
Arduino繋いで、IDE立ち上げて、修正して、デプロイして、動作確認。。。よりは良いかなと。
AWS IoT Core With SORACOM Beam
完全にここを参考してます(それでもうまく繋がらなくて苦戦したけれども)。
SORACOM Beam を使用して AWS IoT と接続する(コンソール版)
M5Stack
こちらのコードのベースは、
M5stack で作る ON AIR サインで手に入れる平穏な在宅勤務環境
に記載されていたソースコードです。
#include <M5Stack.h>
#define TINY_GSM_MODEM_UBLOX
#include <TinyGsmClient.h>
#include "Adafruit_MQTT.h"
#include "Adafruit_MQTT_Client.h"
#include <ArduinoJson.h>
// 接続先 ここではsoracom beamのエンドポイントを指定してます。
#define MQTT_SERVER "beam.soracom.io"
#define MQTT_SERVERPORT 1883
#define READ_TIMEOUT 5000
// MQTTのトピック
const char TOPIC[] PROGMEM = "iotbutton/status";
TinyGsm modem(Serial2); /* 3G board modem */
TinyGsmClient ctx(modem);
Adafruit_MQTT_Client mqtt(&ctx, MQTT_SERVER, MQTT_SERVERPORT);
Adafruit_MQTT_Subscribe buttonStatusIndicator = Adafruit_MQTT_Subscribe(&mqtt, TOPIC);
void setup() {
Serial.begin(115200);
M5.begin();
M5.Lcd.clear(BLACK);
M5.Lcd.setTextColor(WHITE);
M5.Lcd.setTextSize(2);
M5.Lcd.println(F("M5Stack + 3G Module"));
M5.Lcd.print(F("modem.restart()"));
Serial2.begin(115200, SERIAL_8N1, 16, 17);
modem.restart();
M5.Lcd.println(F("done"));
M5.Lcd.print(F("getModemInfo:"));
String modemInfo = modem.getModemInfo();
M5.Lcd.println(modemInfo);
M5.Lcd.print(F("waitForNetwork()"));
while (!modem.waitForNetwork()) M5.Lcd.print(".");
M5.Lcd.println(F("Ok"));
M5.Lcd.clear(BLACK);
M5.Lcd.setCursor(0,0);
M5.Lcd.print(F("gprsConnect(soracom.io)"));
modem.gprsConnect("soracom.io", "sora", "sora");
M5.Lcd.println(F("done"));
M5.Lcd.clear(BLACK);
M5.Lcd.setCursor(0,0);
M5.Lcd.print(F("isNetworkConnected()"));
while (!modem.isNetworkConnected()) M5.Lcd.print(".");
M5.Lcd.println(F("Ok"));
M5.Lcd.clear(BLACK);
M5.Lcd.setCursor(0,0);
M5.Lcd.print(F("My IP addr: "));
IPAddress ipaddr = modem.localIP();
M5.Lcd.println(ipaddr);
mqtt.subscribe(&buttonStatusIndicator);
M5.Lcd.clear(BLACK);
M5.Lcd.setCursor(0,0);
// スリープからの復帰をAボタンに割り当て
M5.setWakeupButton(BUTTON_A_PIN);
M5.Lcd.print(F("Startup complete!"));
delay(2000);
}
void loop() {
M5.update();
if (M5.BtnA.wasPressed()) {
M5.Lcd.setCursor(0,0);
M5.Lcd.printf("Good Morning!");
delay(10000);
M5.Lcd.clear(BLACK);
M5.Lcd.setCursor(0,0);
}
// MQTT接続
connectMqtt();
Adafruit_MQTT_Subscribe *subscription;
while ((subscription = mqtt.readSubscription(READ_TIMEOUT))) {
if (subscription == &buttonStatusIndicator) {
String lastread = String((char*)buttonStatusIndicator.lastread);
lastread.trim();
StaticJsonDocument<200> jsonDoc;
DeserializationError err = deserializeJson(jsonDoc, lastread);
if (err) {
Serial.print(F("deserializeJson() failed: "));
return;
}
// JSONデータを割りあて
const char* message = jsonDoc["message"];
unsigned int color = strToHex(jsonDoc["color"]);
unsigned int statusType = jsonDoc["status"];
// statusTypeってないですが、念の為
if (statusType == 0) {
continue;
}
if (statusType == 1) {
// シングルクリックの場合
M5.Lcd.setBrightness(200);
showMessage(color, message);
} else if (statusType == 2) {
// ダブルクリックの場合
M5.Lcd.setBrightness(200);
showMessage(color, message);
} else if (statusType == 3) {
// 長押しの場合
showMessage(color, message);
delay(30000);
// 表示消す
M5.Lcd.clear(BLACK);
M5.Lcd.setBrightness(0);
// Deep Sleepモードに移行
setDeepSleep();
}
}
}
}
// メッセージ表示部
void showMessage(uint16_t bgColor, const char* message) {
M5.Lcd.fillScreen(bgColor);
M5.Lcd.setCursor(30, 100);
M5.Lcd.setTextSize(4);
M5.Lcd.println(F(message));
}
// 色指定の文字列を16真数に変換
uint16_t strToHex(const char* str) {
return (uint16_t) strtoull(str, 0, 16);
}
// Deep Sleepモード設定
void setDeepSleep() {
M5.Power.begin();
if (!M5.Power.canControl()) {
//can't control.
return;
}
// 12時間Deep Sleep
M5.Power.deepSleep(SLEEP_HR(12));
}
// MQTT接続部
void connectMqtt() {
int8_t ret;
if (mqtt.connected()) {
return;
}
while ((ret = mqtt.connect()) != 0) {
M5.Lcd.println(F(mqtt.connectErrorString(ret)));
M5.Lcd.println(F("Retrying MQTT connection in 5 seconds ..."));
mqtt.disconnect();
delay(5000);
}
}
最初Subscriberのところ、完全に理解間違ってて、別のClient(Arduino Client for MQTT)使わなきゃだめかーと思っていたのですが、
よくよく考えてみると、紹介されていた(adafruit/Adafruit_MQTT_Library - GitHub)を使えば問題ないってことに気づく始末。
・・・が、
SORACOM Beamには繋がるようになったけど、メッセージは取れない。
っていう状況になり、MacにMosquitto入れて、AWS IoT CoreやSORACOM BeamにPub/Subしてみたりして、
証明書再設定して、ようやくメッセージ取得に成功。
取れてしまえば、あとは、文字位置とかの調整でした。
一旦作り終えて、やったーって思っていたのですが、これ、定期的にメッセージがないか通信が発生しているわけで、
使う想定では、長押ししたら業務終了なので、そのあとは、まあ通信しなくてもいいかなと思いました。
電源落としできればよかったんですが、USB給電中は停止できないようだったので、
長押ししたあとは、deepSleep()呼び出してます。時間等の指定が必要だったので、とりあえず12時間は寝てるようにしてます(同じぐらい寝てたい。。。)。
でも、強制的に叩き起こさないとダメなこともあるので、ボタンA(一番左側)を押すと、目覚める(再起動)するにようにしてます。
この記事書いてて思ったのですが、
30秒でDeepSleepするようにしていますが、
1時間ぐらい変わらなかったら、Deep Sleepするようにした方がいいのかな?
って思っているので、別途アップデート予定。
あと、文字長に合わせて、文字サイズ変えられるように(今は4固定)したほうが良さげかな。
あと、起動したら、普通に仕事中って出してもいいのかもとかも。。。
動画の続きを。。。
-
DeepSleep時のアクション
DeepSleepしているときは、ボタンを押しても、通信してないので、当然表示は切り替わりません。
ボタンA(一番左側)を押すと、目覚める(再起動)ようにしてます。(動画の最後の方)
DeepSleep時のアクション pic.twitter.com/F0uuNeAlj6
— Kenichiro Wada (@Keni_W) April 16, 2020 -
再起動後(上の続き)
再起動して、通信可能な状態になると、ちゃんと切り替わります。
再起動後 pic.twitter.com/3EYd6dUwip
— Kenichiro Wada (@Keni_W) April 16, 2020
寝てる間は通信しないだろうというのは、あくまで想像だったので、
該当のM5Stackの3GモジュールにセットしてあるAir SIMの通信状況を見てると、
DeepSleep中(2020/4/15 10時ぐらいにDeepSleepしているので、22時ぐらいまで)は
通信してないということわかりました。
まとめ
よかったところ
- 実はボタン使ったリモートワーク改善自体は2つ目で、M5Stackも使ったので、実装、検証含めて楽しかった。
- ほんとIoTって総合格闘技だわと思いました。
- クラウド側(Lambda)は大幅に手を抜いたけど、
- 上述の通り、SORACOM Beamへはアクセスできているけど、AWS IoT Coreからメッセージ取れなかったことがあって、切り分けとかできたこと。
- Macから繋がないとダメで、どーするってなった時に、ちょうどWifiルーターを中古で手に入れたので、それにSORACOM Airさして、切り替えた上で、SORACOM Beamのエンドポイントリクエストして、取れるか試せた。
- Wifiルーターは持ってって損はないって実感。イ◯◯スさんありがとう。
- 書いてて思ったけど、SORACOM Endorse使えた?
- Macから繋がないとダメで、どーするってなった時に、ちょうどWifiルーターを中古で手に入れたので、それにSORACOM Airさして、切り替えた上で、SORACOM Beamのエンドポイントリクエストして、取れるか試せた。
- SORACOM BeamというかSORACOMプラットフォーム側で、証明書情報を保持できるので、クライアント(M5Stackには絶対持たせられる気がしない)にその辺の情報を持たなくていいのは、ほんと楽ですね。 Beam、これがサービス開始当初からあったと思うと、やっぱりすごいな。
- ついでにSORACOM PONG(正式名称ではないけどw デバイスからのPING要求にECHO REPLYを返すエンドポイントを用意しました。)試せたので、良かった。
ダメだったところ
- まだまだArduinoの理解不足は否めない(写経中心だったので)
- AWS IoT Coreも同じ、まだまだ理解不足。もっと触ってみます。
- 通勤時間だった時間(約1時間)を使うと言いつつ、1時間以上やってたことはまあまああり。
で使ってるの?
使ってません。ちゃんちゃんw
会議室とか、Web会議、オンラインセミナーに使えるかもーとか思ってはいますが。。。
2020/05/26 更新
- 2020/05/26 SORACOMUG Tokyo Online #15 でこの件でLTしたので、その資料をば。。。
- 発表内で行ったFunk対応を反映。Funk対応した場合の構成図は以下になります。
- Funkの設定は、公式のGetting Started「SORACOM Funk を利用して AWS Lambda を呼び出し Slack へ通知する」または 「SORACOM Funkを使ってみた。」(手前味噌)をご覧ください。
- LambdaとM5Stack(Arduino)のソースコードを以下で公開してます。
Kenichiro-Wada/iotbutton-to-m5stack
Special Thanks
- ソラコム小熊さん
- ソラコム松下さん
- オルターブース木村さん
参考資料
ベース
AWS IoT Core関係
- SORACOM Beam を使用して AWS IoT と接続する(コンソール版)
- AWS IoT とは
-
MosquittoのPub/Sub通信におけるQoS毎の通信フロー
- QoSってなんやねんって調べていた時に見つけたサイト。会社の同僚の方のサイトでしたw
Arduino関係
- プロトタイプ向けマイコンモジュール M5Stack と 3G 拡張ボードをセットアップする
- m5stack/m5-docs - GitHub
- API Reference - ArduinoJson
- Convert string as hex to hexadecimal
MacでPub/Sub
その他
-
初めての「技術ブログ」書き方のご紹介
- 初めてじゃないけどw、ちょっと見直してみたw