これまでのあらすじ
Wio LTE M1/NB1(BG96) でパケ死せずにデータを受け取る - Qiita
「ボタンを押したらパトランプ点灯してブザーを鳴らす」を、すべてLTE-M回線で実現してみた。 - Qiita
の2部作で、SORACOM LTE-M Buttonを押したら、パトライトを点灯させる仕組みを作りました。
#あのボタン を押したことを Wio LTE M1/NB1で受信して、パトランプとブザーを鳴らしてみた。音量注意。#SORACOM #AWS pic.twitter.com/4UMEiCktyH
— Kosuke Koiwai (@kocko) April 28, 2019
しかしながら、このときは、UDPで数秒おきにポーリングする仕組みとしましたが、それでもLTE-Mの通信料が高額(1MBあたり 500円超!)で、1ヶ月つけっぱなしにすると、2,3万円に到達する試算でした。
なので、さらなる通信量の削減を目指し、MQTTを使ってみることにしました。
流れはこんな感じです。
- LTE-M Buttonを押す
- AWS 1-Clickを経由して、Lambdaを起動
- Lambdaで、AWS-IoTのMQTTサーバにデータをPublish
- MQTTサーバとWio-LTEとの間をSORACOM Beamでつなぐ
- Wio-LTEでパトライトを光らせる
AWS-IoTの設定
MQTTサーバは、AWS-IoTを使うことにします。
まず、IoT Coreに接続できるようにするため、証明書の設定をします。
AWS-IoTの設定画面から、安全性(Serucityの訳語?)→ 証明書 → 作成 を押します。
1-Click 証明書の作成を押します。
証明書と、プライベートキーが必要になります。ダウンロードしておきます。後ほどSORACOM Beamの設定で使います。
次に、MQTTサーバにアクセスさせるためのポリシーを作ります。
安全性 → ポリシー → 作成を押します。
適当な名前をつけます。
アクション、リソースは、とりあえずすべて許可にします。
(あとでちゃんと、権限を絞っておきましょう)
証明書の設定にもどり、作成したポリシーをアタッチします。
最後に、接続先のサーバ名(エンドポイント)をメモしておきます。後ほどSORACOM Beamの設定で使います。
SORACOM Beamの設定
ユーザコンソール上で、SIMグループを作成します。
グループの設定で、BEAMの設定 → MQTTエントリポイントを追加します。
転送先の設定をします。ホスト名に、先ほどコピーしておいたAWSのエントリポイントをペーストします。
先ほどダウンロードしたAWS-IoTの証明書と秘密鍵をテキストエディタで開いて、中身をコピペします。
Lambdaの編集
LTE-M Buttonを押したらLambdaを呼ぶ設定については、こちらの公式記事を参考にしてください。
SORACOM LTE-M Button powered by AWS をクリックしてSlackに通知する | Getting Started with SORACOM LTE-M Button | SORACOM Developers
設定が終わったら、Lambdaのコードを、下記のようにして、ボタンを押されたらMQTTサーバにpublishするようにします。
前回同様、なるべくデータ量を減らすため、MQTTにはJSONではなく、0, 1, 2 の数字1つだけを送ることにします。
エンドポイントには、先ほどSORACOM Beamで設定したものと同じものを設定します。
コード
const https = require('https');
var AWS = require("aws-sdk");
exports.handler = async(event) => {
console.log('Received event:', JSON.stringify(event, null, 2));
var putItem = {};
switch (event.deviceEvent.buttonClicked.clickType) {
case 'SINGLE':
buttonState = 1;
break;
case 'DOUBLE':
buttonState = 2;
break;
default:
buttonState = 0;
break;
}
var iotdata = new AWS.IotData({
endpoint: 'XXXXXXXXXXXXXX.iot.ap-northeast-1.amazonaws.com',
region: 'ap-northeast-1'
});
var params = {
topic: "IoTButtonStatus",
payload: ''+buttonState, // convert to String
qos: 0
};
await iotdata.publish(params).promise();
};
ロールの追加
AWS-IoTのMQTTサーバにLambdaから繋げるための権限設定をします。
Lambdaの編集画面の下の、実行ロールの設定から、現在のロールを表示します。別の画面でロールが開きます。
ポリシーのアタッチをクリックします。
ポリシーの作成をクリックします。
サービスをIoTにして、
アクションをすべてにして、
リソースをすべてにして、
名前をつけて、ポリシーの作成を押します。
(もちろん、あとで適切な権限に絞っておきましょう)
テスト
AWS-IoT コンソールのMQTTクライアント機能で、データがちゃんと飛ぶことを見てみます。
その際、ペイロード表示を、JSONフォーマットではなく、文字列表示に設定します。そうしないと、0
を送ったとき、空欄になってしまいます。
SORACOM LTE-M Buttonを押してみたり、Lambdaのテスト機能を使って、データを送ってみます。
Arduinoのコード
ライブラリのサンプルコードにちょっと付け加えただけです。
いずれかの方法で、MQTTのライブラリをダウンロードする必要があります。
下記をダウンロードして、Arduinoのライブラリフォルダに解凍しておくか、
GitHub - SeeedJP/pubsubclient: A client library for the Arduino Ethernet Shield that provides support for MQTT.
Arduinoのツール→ライブラリを管理 から、PubSubClientを探してインストールしておきます。
#include <WioCellLibforArduino.h>
#include <WioCellularClient.h>
#include <PubSubClient.h> // https://github.com/SeeedJP/pubsubclient
#include <stdio.h>
#define APN "soracom.io"
#define USERNAME "sora"
#define PASSWORD "sora"
#define MQTT_SERVER_HOST "beam.soracom.io"
#define MQTT_SERVER_PORT (1883)
#define ID "WioCell"
#define IN_TOPIC "IoTButtonStatus"
#define RED_PIN (WIO_D19)
#define YELLOW_PIN (WIO_D20)
#define BUZZER_PIN (WIO_D38)
WioCellular Wio;
WioCellularClient WioClient(&Wio);
PubSubClient MqttClient;
void callback(char* topic, byte* payload, unsigned int length) {
SerialUSB.print("Subscribe:");
for (int i = 0; i < (int)length; i++) SerialUSB.print((char)payload[i]);
SerialUSB.println("");
char v = int(payload[0]);
if (v == '0'){
digitalWrite(YELLOW_PIN, LOW);
digitalWrite(RED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}else if(v == '1'){
digitalWrite(YELLOW_PIN, HIGH);
digitalWrite(RED_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
}else if(v == '2'){
digitalWrite(YELLOW_PIN, LOW);
digitalWrite(RED_PIN, HIGH);
digitalWrite(BUZZER_PIN, HIGH);
}
}
void setup() {
delay(200);
pinMode(RED_PIN, OUTPUT);
pinMode(YELLOW_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
SerialUSB.begin(115200);
SerialUSB.println("");
SerialUSB.println("--- START ---------------------------------------------------");
SerialUSB.println("### I/O Initialize.");
Wio.Init();
SerialUSB.println("### Power supply ON.");
Wio.PowerSupplyCellular(true);
delay(500);
SerialUSB.println("### Turn on or reset.");
#ifdef ARDUINO_WIO_LTE_M1NB1_BG96
Wio.SetAccessTechnology(WioCellular::ACCESS_TECHNOLOGY_LTE_M1);
Wio.SetSelectNetwork(WioCellular::SELECT_NETWORK_MODE_MANUAL_IMSI);
#endif
if (!Wio.TurnOnOrReset()) {
SerialUSB.println("### ERROR! ###");
return;
}
SerialUSB.println("### Connecting to \"" APN "\".");
if (!Wio.Activate(APN, USERNAME, PASSWORD)) {
SerialUSB.println("### ERROR! ###");
return;
}
SerialUSB.println("### Connecting to MQTT server \"" MQTT_SERVER_HOST "\"");
MqttClient.setServer(MQTT_SERVER_HOST, MQTT_SERVER_PORT);
MqttClient.setCallback(callback);
MqttClient.setClient(WioClient);
if (!MqttClient.connect(ID)) {
SerialUSB.println("### ERROR! ###");
return;
}
MqttClient.subscribe(IN_TOPIC);
SerialUSB.println("### Setup completed.");
}
void loop() {
MqttClient.loop();
delay(500);
}
Keep Alive間隔を延ばす
PubSubClientのデフォルトKeepAlive間隔は15秒ですが、AWS-IoTは、30秒から1200秒までに対応しています。
ライブラリのReadmeによれば、PubSubClient.h
の中で、KeepAlive設定値を変えられるとのことだったので変えます。ちょっと余裕を持って900秒にしてみました。(デフォルトは1200秒とのことなので、普通に1200秒にしておけばいいと思います)
// MQTT_KEEPALIVE : keepAlive interval in Seconds
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 900
#endif
試してみる
UDPでポーリングしていたときと比べて、反応が早くなった。#soracom #wiolte #あのボタン pic.twitter.com/cNkuuFDVym
— Kosuke Koiwai (@kocko) May 21, 2019
実際の通信量は
ここまで終わったので、実際に動かして見ます。
電源を入れた直後は、5KBぐらいの通信が発生しますが、一度つないでしまえば、ボタンを押さなければ、1時間で1.2KBレベルまで減らすことができました。
つまり、0.6円 * 24時間 * 30日 = 432円 で運用できます。
ついにパケ死から逃れることができました!!
でも、まだ通常のLTEプランを使った方がきっと安いですね。。。