SORACOM LTE-M Button powered by AWSが使えた場所を取得してみた

本記事は[SORACOM LTE-M Button powered by AWS Advent Calendar 2018](https://qiita.com/advent-calendar/2018/soracom)の10日目の記事です。

# モチベーション

## 無線の泣きどころ

SORACOM LTE-M Button powered by AWS（長いので以下「LTE-M Button」）に限らず、無線を使った機器につきまとう問題が「電波が届くかどうか」です。そもそもサービスエリアがまだ狭い（[KDDIのLTE-Mサービスエリア検索](http://www.kddi.com/business/mobile/m2m-solution/iot-lpwa/area)）という問題だけでなく、仮にサービスエリア内であっても地形や建物の影響により満足に通信できないというケースはざらにあります。都会のビルなどを訪れた際、奥まったところにある部屋でモバイルルータが使えず困った経験がある方も多くいらっしゃると思います。

## 実地踏査

なので、業務で無線機器を使おうとすると実際にその場所での通信環境を確認する「実地踏査」が必要不可欠です。SORACOMのLoRaWANサービスの例だと「[LoRa デバイス LT-100を使ってみた](https://qiita.com/ygotou/items/70f35f8069dd6a75caf5)」という記事がQiitaに掲載されていたりします。専用のGPS機器により、位置情報をデータとして送信するという単純かつエレガントな方法で目的を達成しています。

## ところが

残念なことにGPS付きのLTE-Mボタンというのはまだ存在しません。また電波ものの宿命として機種による性能の差が非常に大きく、ある機器では通信できても別の機器では無理、なんてことも普通に起こります。ですからGPS付きのLTE-M端末を別途用意して…というのも結果の信頼性に不安が残ってしまいます。省電力機器は往々にして電波の飛びが悪かったりして、いざ設置してみると圏外、では悔やんでも悔やみきれません。

## そこで

現行のLTE-Mボタンをそのまま使い、かつ電波が届いた場所を（できれば自動的に）記録したいというのが本記事を書いたモチベーションです。

---

# システム構成

## 基本的アイデア

本記事の基本的なアイデアは「餅は餅屋」、つまりGPS測位はスマホに任せようということです。

1. LTE-Mボタンが押される
2. AWS Lambdaファンクションがキックされる
3. LambdaからSNSを呼び出す
4. SNSがApple Push Notificationサービス(APNs)に通知する
5. APNsが端末にPuch通知を発信する
6. 端末がPush通知を受信し、位置情報を記録する
7. 端末が位置情報をデータベースに格納する

という流れで処理が進みます。制約として、LTE-MボタンとiPhoneが同じ場所にある必要がありますが、大抵の場合は自動的にこの制限が満たされていると思います。

## LTE-Mボタンまわりの処理

ここは大量の記事があふれている分野だと思いますので省略します。うまいこと設定してLambdaファンクションが呼び出されるようにしてください。

## Lambdaファンクション

Lambdaファンクションの機能は次の通りです。

1. LTE-Mボタンからデータを受信する
2. APNs用のデータ（PNsリモート通知ペイロード）を生成し、SNSに送る

APNsに送るメッセージのことを「リモート通知ペイロード」と呼びます。これは[Appleによって定義されている規格](https://developer.apple.com/jp/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CreatingtheNotificationPayload.html)で、形式としてはJSON Stringになります。

SNSにプッシュするメッセージには、このリモート通知ペイロードを適切な形で含んだJSON stringを生成する必要があります。ここで「JSON string」という単語が連続して登場しましたが、この意味をよく考えないと落とし穴に嵌まりますので注意が必要です。具体的にLambda Functionで示します。

```node.js
const AWS = require('aws-sdk');
const sns = new AWS.SNS({apiVersion: '2010-03-31'});

const region = <regions>;
const topicARN = <topic arn>;

exports.handler = (event, context, callback) => {
    const deviceId  = event.deviceInfo.deviceId;
    const clickType = event.deviceEvent.buttonClicked.clickType;
    const remainingLife = event.deviceInfo.remainingLife;
    const reportedTime = event.deviceEvent.buttonClicked.reportedTime;
    
    const messageDefault = 'default';
    const messageAPNS = {
        'aps': {
            'alert': <alert message>
        },
        'buttonInfo' : {
            'deviceId': deviceId,
            'clickType': clickType,
            'remainingLife': remainingLife,
            'reportedTime': reportedTime
        }
    }
    const message = {
        default: messageDefault,
        APNS: JSON.stringify(messageAPNS),
        APNS_SANDBOX: JSON.stringify(messageAPNS)
    }

    const params = {
        Message: JSON.stringify(message),
        MessageStructure: 'json',
        Subject: <subject>,
        TopicArn: topicARN
    };

    sns.publish(params, (err, data) => {
        if(err) console.log(err);
        else console.log(data);
    });

    console.log(params);
    callback();
};
```
まず、AWS　SNSに送信するSNSメッセージにはキー"Message"の値として各環境向けの通知内容をJSON.stringifyして渡す必要があります。で、APNsの場合はキー"APNS"あるいは"APNS_SANDBOX"の値としてJSON.stringifyされたAPNs通知ペイロードを格納することになります。

ここで肝心なのがこれらのキーの値がJSON Objectではなく JSON Stringであるという点です。まとめてオブジェクトを生成して最後に JSON.stringifyしてもAPNｓペイロードとは認識されず、default環境向けのメッセージが通知されてしまいます。

## それから

AWS SNSにメッセージが送られたら、あとはAPNsむけの設定を行うだけです。ここは検索すれば情報がたくさん出てくると思いますので本稿では省略します。

また、送られてきた通知を受けて位置情報を取得するiOSアプリも必要になります。これも本稿では触れないことにします。

-----

# 動作検証

##　電波が届くと

実はこの記事は長野で書いています。IoT ALGYANのイベント [1泊2日＋新幹線＋SensorTag付き！Azure IoTハンズオン@長野テレワーク体験無料ツアー](https://algyan.connpass.com/event/104321/)では２日目（１２月９日）の午前中に「おためしテレワーク体験」という時間があり、それを利用して長野市の施設「[長野市ものづくり支援センター](http://ufo-nagano.com)」の一角をお借りして記事を執筆しました。

![IMG_6966.jpg](https://qiita-image-store.s3.amazonaws.com/0/18746/5968a090-e59e-05e6-1715-9eb2da7ddd44.jpeg)

晴れた日にはこんな絶景が望める最高の環境です。この場所でLTE-M Buttonを押してしばらく待つと…

![IMG_7011.jpg](https://qiita-image-store.s3.amazonaws.com/0/18746/6923eee5-c7a0-4a01-1f33-a9940f8a93de.jpeg)

私のiPhoneにはこんな地図が表示されました。無事に通知が届いたのでブルーのフラッグに位置と標高が表示されています。黒塗り部分はLTE-MボタンのIDです。他にも、

![IMG_7012.jpg](https://qiita-image-store.s3.amazonaws.com/0/18746/ace354b5-77ff-72a1-2e32-ee93901ff5e0.jpeg)

長野駅前の[ホテルサンルート長野](https://www.sunroutenagano.jp)客室からでも電波が届きました。屋内でも環境が良ければちゃんと届くようですね。他にも長野市内のあちこちで試してみましたが、圏外になる場所は見つけられませんでした。

# 電波が届かないと

LTE-Mボタンの圏外では、いくらボタンを押しても通知が届きません（または、iPhoneが圏外の場合にも）。LTE-M機器そのものがGPS機能を持っている場合はそれを記録するのが難しいですが、今回のようにiPhoneを使っている場合は「この場所では通知が届かなかった」という事実そのものを直接記録することが可能です。残念ながら現状は人間の介在が必要で、LTE-Mボタンの赤ランプ（通信失敗）が転倒したらiPhone画面上の「届きません」ボタンを押すという作業が必要です。

例えば…

![IMG_6949.jpg](https://qiita-image-store.s3.amazonaws.com/0/18746/f054ad7d-6cb6-49a0-f4ec-0fe520e62254.jpeg)

北陸新幹線　軽井沢駅に停車中の新幹線車内では通知が届きませんでした。他にも、

![IMG_6950.jpg](https://qiita-image-store.s3.amazonaws.com/0/18746/50c480fe-c2d3-4347-2349-29dfee795cfa.jpeg)

これは電波の飛びだけが原因ではないかもしれませんが、高崎から安中榛名に向かう車中でも通知が届きません。LTE-Mは移動中の通信も可能というのが売りですが、さすがに新幹線では移動速度が速すぎるのか、それとも本当に圏外だったのかは不明です。

-----

# まとめ

* LTE-Mボタンを押すと、iPhoneに通知が飛ぶ仕掛けを作りました。
* iPhoneが通知を受信すると、現在位置と標高、ボタンIDを記録するアプリを作りました。
    * 通知が届かない場合は手動で位置情報を記録します

実際にボタンをもってあちこちうろうろしてみると、圏外になる場所は想像以上に少なかったです。都市部、屋外で使う分には十分可能性がありそうです。一方で、実は自宅の作業部屋、会社のオフィス、よく立ち寄るお客様が入居しているビルのロビーなど、屋内はかなりの割合で通知が届きません。おそらく、ユースケースの大半は屋内だと思いますので、現時点ではまだまだ十分な実地検証が必要なレベルだな、というのが率直な感想でした。

-----

それでは皆様よい新年 & LTE-Mボタンライフを！