Help us understand the problem. What is going on with this article?

Wio LTE M1/NB1(BG96) で本当にパケ死せずにデータを受け取る(MQTT編)

More than 1 year has passed since last update.

これまでのあらすじ

Wio LTE M1/NB1(BG96) でパケ死せずにデータを受け取る - Qiita
「ボタンを押したらパトランプ点灯してブザーを鳴らす」を、すべてLTE-M回線で実現してみた。 - Qiita

の2部作で、SORACOM LTE-M Buttonを押したら、パトライトを点灯させる仕組みを作りました。

しかしながら、このときは、UDPで数秒おきにポーリングする仕組みとしましたが、それでもLTE-Mの通信料が高額(1MBあたり 500円超!)で、1ヶ月つけっぱなしにすると、2,3万円に到達する試算でした。

なので、さらなる通信量の削減を目指し、MQTTを使ってみることにしました。

スクリーンショット 2019-05-19 2.10.51.png

流れはこんな感じです。

  • 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の訳語?)→ 証明書 → 作成 を押します。
スクリーンショット 2019-05-19 2.32.32.png
1-Click 証明書の作成を押します。
スクリーンショット 2019-05-19 2.34.31.png

証明書と、プライベートキーが必要になります。ダウンロードしておきます。後ほどSORACOM Beamの設定で使います。

スクリーンショット 2019-05-18 15.34.47.png

次に、MQTTサーバにアクセスさせるためのポリシーを作ります。
安全性 → ポリシー → 作成を押します。

スクリーンショット 2019-05-18 22.32.34.png

適当な名前をつけます。
アクション、リソースは、とりあえずすべて許可にします。
(あとでちゃんと、権限を絞っておきましょう)

スクリーンショット 2019-05-18 22.33.28.png

証明書の設定にもどり、作成したポリシーをアタッチします。

スクリーンショット 2019-05-18 22.38.10.png

スクリーンショット 2019-05-18 22.38.15.png

最後に、接続先のサーバ名(エンドポイント)をメモしておきます。後ほどSORACOM Beamの設定で使います。

スクリーンショット 2019-05-18 22.56.37.png

SORACOM Beamの設定

ユーザコンソール上で、SIMグループを作成します。
グループの設定で、BEAMの設定 → MQTTエントリポイントを追加します。

スクリーンショット 2019-05-18 22.53.06.png

転送先の設定をします。ホスト名に、先ほどコピーしておいたAWSのエントリポイントをペーストします。
スクリーンショット 2019-05-18 22.57.25.png

証明書の利用をONにして、認証情報を追加します。
スクリーンショット 2019-05-18 22.57.30.png

先ほどダウンロードしたAWS-IoTの証明書と秘密鍵をテキストエディタで開いて、中身をコピペします。

スクリーンショット 2019-05-18 22.55.37.png

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の編集画面の下の、実行ロールの設定から、現在のロールを表示します。別の画面でロールが開きます。
スクリーンショット 2019-05-19 2.46.13.png
ポリシーのアタッチをクリックします。
スクリーンショット 2019-05-19 1.16.30.png
ポリシーの作成をクリックします。
スクリーンショット 2019-05-19 21.03.33.png
サービスをIoTにして、
スクリーンショット 2019-05-19 21.05.11.png
アクションをすべてにして、
スクリーンショット 2019-05-19 21.05.26.png
リソースをすべてにして、
スクリーンショット 2019-05-19 21.05.41.png
名前をつけて、ポリシーの作成を押します。
(もちろん、あとで適切な権限に絞っておきましょう)
スクリーンショット 2019-05-19 21.06.41.png

そうやって作ったポリシーを、アタッチして終了です。
スクリーンショット 2019-05-19 1.18.55.png

テスト

AWS-IoT コンソールのMQTTクライアント機能で、データがちゃんと飛ぶことを見てみます。
その際、ペイロード表示を、JSONフォーマットではなく、文字列表示に設定します。そうしないと、0 を送ったとき、空欄になってしまいます。
スクリーンショット 2019-05-21 22.55.45.png

SORACOM LTE-M Buttonを押してみたり、Lambdaのテスト機能を使って、データを送ってみます。
スクリーンショット 2019-05-21 22.56.58.png

Arduinoのコード

ライブラリのサンプルコードにちょっと付け加えただけです。

いずれかの方法で、MQTTのライブラリをダウンロードする必要があります。
下記をダウンロードして、Arduinoのライブラリフォルダに解凍しておくか、
GitHub - SeeedJP/pubsubclient: A client library for the Arduino Ethernet Shield that provides support for MQTT.

Arduinoのツール→ライブラリを管理 から、PubSubClientを探してインストールしておきます。
スクリーンショット 2019-05-19 21.26.02.png

#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秒にしておけばいいと思います)

AWS サービスの制限 - アマゾン ウェブ サービス
スクリーンショット 2019-05-19 21.30.50.png

PubSubClient.h
// MQTT_KEEPALIVE : keepAlive interval in Seconds
#ifndef MQTT_KEEPALIVE
#define MQTT_KEEPALIVE 900
#endif

試してみる

実際の通信量は

ここまで終わったので、実際に動かして見ます。

スクリーンショット 2019-05-19 2.48.12.png

電源を入れた直後は、5KBぐらいの通信が発生しますが、一度つないでしまえば、ボタンを押さなければ、1時間で1.2KBレベルまで減らすことができました。
つまり、0.6円 * 24時間 * 30日 = 432円 で運用できます。
ついにパケ死から逃れることができました!!
でも、まだ通常のLTEプランを使った方がきっと安いですね。。。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした