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

## 目次
- Wio LTEを受信用デバイスとして使いたい
- SORACOM Air plan-KM1 はパケ死する
- Lambdaの作成
- API Gatewayの作成
- SORACOM Beamの設定
- Wio LTEのコード
- 実際の通信料は

## Wio LTEを受信用デバイスとして使いたい
以前、[SORACOM LTE\-M ButtonからLINEにメッセージを送る \- Qiita](https://qiita.com/kkoiwai/items/a44f38676c92402d10c6) という記事を書きまして、その後も日々このボタンをポケットに入れ、機会あるたびに人にデモしていたのですが、ボタンを押したらLINEメッセージが届くだけでは、「すごい」と言われても、「それで？」「だから？」という気持ちが透けて見えてきました。

そのため、LTE-Mボタンを押したら、パトランプが光るようなデモができたらと考えました。そして、どうせなら、受信側も、LTE-M回線を経由して、Wio LTEを使えたら良いのではと考えました。

LTE-Mボタンを押したら、その情報を何らかのサーバを経由してWio-LTEにおくり、パトランプを点灯させるという簡単な仕組みです。
いろいろ調べて、今回は、個人が簡単に安価でできる範囲で実現するため、Wio-LTEから数秒おきにサーバに状態を問い合わせる方式とすることにしました。

<img width="847" alt="スクリーンショット 2019-04-21 22.21.46.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/343264f1-6fed-1743-d40a-79f05c976a44.png">

## SORACOM Air plan-KM1 はパケ死する

元々IoT用の Lte-M1 という通信方式は、大量に撒いたデバイスから、1日1回とか、1週間に1回、それも数十バイトといった頻度・データ量の通信を想定しているため、月額利用料は安いものの、通信料は一昔前のパケット代レベル（ **1MBあたり 500円超！** )になっています。
[日本向け Air SIM のご利用料金](https://soracom.jp/services/air/cellular/price/#plan-km1)
<img width="799" alt="スクリーンショット 2019-04-21 19.52.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/04f2aa22-425a-a6b3-bd3a-60e2d7cbf8d0.png">

そのため、例えば、数秒おきに、HTTPSで外部サーバに接続に行くなんてことをすると、 **たった1時間で数千円の請求** になりかねません。
参考：[HTTP\(S\)とMQTT\(S\)の消費電流とデータサイズを計測する \- Qiita](https://qiita.com/kopanitsa/items/5333b3f6126cfe3d7495#%E9%80%9A%E4%BF%A1%E3%83%87%E3%83%BC%E3%82%BF%E3%82%B5%E3%82%A4%E3%82%BA%E3%81%A9%E3%81%86%E3%81%A0%E3%81%A3%E3%81%9F%E3%81%8B)

なので、今回は、便利なSORACOM Beamの、UDPを使ってデータを受信することとします。
[SORACOM Beam : Beamを使用したデータの送信 \| 開発者ガイド \| SORACOM Developers](https://dev.soracom.io/jp/beam/send-data-using-beam/#udp2http) によれば、APIのレスポンスのBody部分が、そのままデバイスに返信されます。
<img width="809" alt="スクリーンショット 2019-04-21 22.24.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/421bfa2d-ad8e-3dde-b6cf-789e421a4691.png">
SORACOM Beamを設定するには、まず通信先のAPIが必要になるため、AWSのLambdaとAPI Gatewayを利用して、APIを作成します。


## Lambdaの作成

将来的には直近のボタンの状態を取得して返しますが、現時点では、単純に1を返すことにします。

- ボタンが1回押された：1
- ボタンがダブルクリックされた：2
- ボタンが長押しされた（リセット）：0

```js
exports.handler = async(event) => {
    return ("1\n");
};
```

あとで気づいたのですが、最後に改行```\n```を付け加えないと、Beam側でデータを受け取ってくれない様です。特にドキュメントには書いてなかったのですが・・・。

## API Gatewayの作成

LambdaをSORACOM Beamから呼べるように、API Gatewayを作成します。
普通にPOSTメソッドを先ほどのLambdaに設定すればいいのですが、1点だけ、「Lambda プロキシ統合の使用」のチェックは入れません。

## SORACOM Beamの設定

[SIM グループ \- SORACOM ユーザーコンソール](https://console.soracom.io/#/groups?coverage_type=jp) で、SIMグループの作成し、SORACOM Beamの設定で、「UDPToHTTP」を設定します。
その際、「ステータスコードを省略」をONにしておくと、さらにバイト数を節約できます。

<img width="592" alt="スクリーンショット 2019-04-21 22.30.43.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/fe7cc936-f551-7f79-1bdb-b23205aba202.png">

## Wio LTEのコード(都度接続版)
公式のサンプルコードにある、「soracom/soracom-harvest」を編集して、SORACOM Beamに空データを送るコードを書きました。5秒おきに接続する様になっています。
[Wiki/home\-ja\.md at master · SeeedJP/Wiki · GitHub](https://github.com/SeeedJP/Wiki/blob/master/Wio_cell_lib_for_Arduino/home-ja.md) 

```c
#include <WioCellLibforArduino.h>

#define INTERVAL        (5000)
#define RECEIVE_TIMEOUT (10000)

WioCellular Wio;

void setup() {
  delay(200);

  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 \"soracom.io\".");
  if (!Wio.Activate("soracom.io", "sora", "sora")) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  SerialUSB.println("### Setup completed.");
}

void loop() {
  char data[100];
  
  sprintf(data, "{}");

  SerialUSB.println("### Open.");
  int connectId;
  //beam.soracom.io:23080
  connectId = Wio.SocketOpen("beam.soracom.io", 23080, WIO_UDP);
  if (connectId < 0) {
    SerialUSB.println("### ERROR! ###");
    goto err;
  }

  SerialUSB.println("### Send.");
  SerialUSB.print("Send:");
  SerialUSB.print(data);
  SerialUSB.println("");
  if (!Wio.SocketSend(connectId, data)) {
    SerialUSB.println("### ERROR! ###");
    goto err_close;
  }
  
  SerialUSB.println("### Receive.");
  int length;
  length = Wio.SocketReceive(connectId, data, sizeof (data), RECEIVE_TIMEOUT);
  if (length < 0) {
    SerialUSB.println("### ERROR! ###");
    goto err_close;
  }
  if (length == 0) {
    SerialUSB.println("### RECEIVE TIMEOUT! ###");
    goto err_close;
  }
  SerialUSB.print("Receive:");
  SerialUSB.print(data);
  SerialUSB.println("");

err_close:
  SerialUSB.println("### Close.");
  if (!Wio.SocketClose(connectId)) {
    SerialUSB.println("### ERROR! ###");
    goto err;
  }

err:
  delay(INTERVAL);
}

```

プログラムを書き込んで実行すると、下記のようなログがシリアルモニタに出力されました。

```
--- START ---------------------------------------------------
### I/O Initialize.
### Power supply ON.
### Turn on or reset.
### Connecting to "soracom.io".
### Setup completed.
### Open.
### Send.
Send:{}
### Receive.
Receive:1

### Close.
### Open.
### Send.
Send:{}
### Receive.
Receive:1
```

## 実際の通信料は

実際に起動して数分放置させ、180回弱ほど通信した後、[SIM 管理 \- SORACOM ユーザーコンソール](https://console.soracom.io/#/?coverage_type=jp) から通信料履歴タブを見ると、21.9KBほどの通信が発生していました。
1回の通信で、約120byteほどのデータを消費しているようです。
これでも、5秒おきに通信するとなると、 120byte * 12回/分 * 60分 * 24時間 * 30日 = 40MB = 3万円/月 ぐらいの通信料になるので、素直に plan-K か plan-D を使うことをおすすめします。

<img width="872" alt="スクリーンショット 2019-04-21 19.31.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/8b6ae836-b3d4-adf5-f2d4-0a995a555721.png">


## Wio LTEのコード(接続しっぱなし版)
先ほどのコードでは、Socket通信を毎回オープン・クローズしていたので、もしかしたらオーバーヘッドがあるかもしれないと思い、接続しっぱなしのバージョンも試してみました。
コードはこちら。

```c
#include <WioCellLibforArduino.h>

#define INTERVAL        (10000)
#define RECEIVE_TIMEOUT (10000)

#define RED_PIN      (WIO_D20)
#define YELLOW_PIN      (WIO_D19)

WioCellular Wio;
bool socketStatus;

void setup() {
  delay(200);

  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 \"soracom.io\".");
  if (!Wio.Activate("soracom.io", "sora", "sora")) {
    SerialUSB.println("### ERROR! ###");
    return;
  }

  socketStatus = false;
  
  SerialUSB.println("### Setup completed.");
}

void loop() {
  char data[100];
  int v;
  int connectId;
  sprintf(data, "0");
  
  if(!socketStatus){
    SerialUSB.println("### Open.");
    //beam.soracom.io:23080
    connectId = Wio.SocketOpen("beam.soracom.io", 23080, WIO_UDP);
    if (connectId < 0) {
      SerialUSB.println("### ERROR! ###");
      goto err;
    }
    socketStatus = true;
  }

  SerialUSB.println("### Send.");
  SerialUSB.print("Send:");
  SerialUSB.print(data);
  SerialUSB.println("");
  if (!Wio.SocketSend(connectId, data)) {
    SerialUSB.println("### ERROR! ###");
    goto err_close;
  }
  
  SerialUSB.println("### Receive.");
  int length;
  length = Wio.SocketReceive(connectId, data, sizeof (data), RECEIVE_TIMEOUT);
  if (length < 0) {
    SerialUSB.println("### ERROR! ###");
    goto err_close;
  }
  if (length == 0) {
    SerialUSB.println("### RECEIVE TIMEOUT! ###");
    goto err_close;
  }
  SerialUSB.print("Receive:");
  SerialUSB.print(data);
  SerialUSB.println("");
  
  goto err;

err_close:
  SerialUSB.println("### Close.");
  if (!Wio.SocketClose(connectId)) {
    SerialUSB.println("### ERROR! ###");
    goto err;
  }
  socketStatus = false;

err:
  delay(INTERVAL);
}
```

65回ほど通信させて、8.1KB。1回の通信で約120バイト。
残念ながら、消費するバイト数は同じくらいでした。もしかしたらもう少し放置したら違いが出るかも？
<img width="879" alt="スクリーンショット 2019-04-27 15.55.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135431/6c2e1e95-2c4f-af2b-13dd-cf9b7da1489b.png">

## 続き

完成したものは、こちら。
[「ボタンを押したらパトランプ点灯してブザーを鳴らす」を、すべてLTE\-M回線で実現してみた。 \- Qiita](https://qiita.com/kkoiwai/items/e05f11f26fa3b28d42ba)

<blockquote class="twitter-tweet" data-lang="en"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/%E3%81%82%E3%81%AE%E3%83%9C%E3%82%BF%E3%83%B3?src=hash&amp;ref_src=twsrc%5Etfw">#あのボタン</a> を押したことを Wio LTE M1/NB1で受信して、パトランプとブザーを鳴らしてみた。音量注意。<a href="https://twitter.com/hashtag/SORACOM?src=hash&amp;ref_src=twsrc%5Etfw">#SORACOM</a> <a href="https://twitter.com/hashtag/AWS?src=hash&amp;ref_src=twsrc%5Etfw">#AWS</a> <a href="https://t.co/4UMEiCktyH">pic.twitter.com/4UMEiCktyH</a></p>&mdash; Kosuke Koiwai (@kocko) <a href="https://twitter.com/kocko/status/1122421384976539648?ref_src=twsrc%5Etfw">April 28, 2019</a></blockquote>