WebアプリでIoTに挑戦してみた ~Webアプリ編~

この記事は [DENSO アドベントカレンダー 2020](https://qiita.com/advent-calendar/2020/denso) の 14 日目の記事です。

バイクツーリングの記録を収集・可視化する仕組みを個人開発で構築し、社内の LT 大会で発表しました[[記事]](https://speakerdeck.com/ktr03rtk/webapuridesumahoiotnitiao-zhan-sitemita)。Web アプリによるスマホ IoT でどこまで実現できるか知りたいというのがきっかけでしたが、いざ取り掛かると技術的な制約で妥協した点もあり、個人で使用する範囲では実用に耐えるが、業務で使うことは難しいとわかりました。この記事では IoT ユースケースのために工夫した点、妥協した点、調査して参考になった情報について記載します。私と同じ様に Web アプリでの IoT を考えている方の参考となれば幸いです。

## 欲しかったもの

- ツーリングでナビアプリをフォアグラウンド実行中に、バックグラウンドでデータ収集・アップロードする
- 山中などのオフライン環境でも起動・計測でき、ネットワークが復旧したら自動でデータアップロードする
- Web アプリ

## できたもの

- フォアグラウンドで内蔵 GPS と Bluetooth センサからのデータ収集・アップロードをする
- PWA(Progressive Web Apps)の技術を使い、オフラインで起動・動作できる
- 計測データはキューに詰められ、確実にアップロードできたデータのみ削除されるが、長時間のデータ蓄積はできない
- Android OS の Chrome でのみ動作する Web アプリ

## 構成

全体構成は下記のとおりです。今回は標題通り Web アプリをメインに扱います。

![architecture.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/928808/58110f96-5db7-c72d-98d0-85dac42f7194.png)

## デモ

ホーム画面上のアイコンから Web アプリを起動し、付近の Bluetooth センサをスキャン・接続してデータを取得・アップロードします。

![10output.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/928808/1d1ed6f9-3a2e-8e46-fc67-2cfc5e1bd9f7.gif)

こちらは AWS のマネージドサービスで構築した可視化のデモです。高度に比例して色付けされたマーカーが地図上に表示されます。

![kibana_demo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/928808/3eb6f8a9-cb3c-e91f-9c8e-92427ab70d7c.gif)

## Web アプリのコード

コードは[こちら](https://github.com/ktr03rtk/iot-web-app)で公開しています。

# センサデータの取得

## Web API

Web アプリでセンサデータ取得するには、まずブラウザが API 対応していることが大前提です。[MDN](https://developer.mozilla.org/ja/docs/Web/API)の情報を参考に Geolocation, Bluetooth と Screen Wake Lock の API を選定しました。

- Geolocation（内蔵 GPS、位置情報）
- Bluetooth （外部センサ、温湿度）
- Screen Wake Lock （スクリーンロック禁止）

[対応表](https://developer.mozilla.org/ja/docs/Web/API/Web_Bluetooth_API#Browser_compatibility)に記載の通り、Bluetooth は iPhone で動作しません。iPhone で Chrome を使用しても動作しません。iPhone の Chrome は safari と同じ webkit を使用しているためのようです。

Screen Wake Lock は Geolocation と Bluetooth の処理をバックグラウンド化する試み（後述）で失敗したため、妥協案として使用します。フォアグラウンドで計測開始した後、ユーザが操作しないと画面ロックして計測終了してしまうため、画面ロックを禁止しています。この API も iPhone は非対応のため、位置情報を自動計測するだけの用途でも iPhone は使えないということになります。

## Bluetooth センサ

Linking デバイスを使用しました。下記のような多彩な用途で計測やデバイス操作ができ価格もお手頃です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/928808/985aac8f-cdcb-cc5c-23df-33ad3417289f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/928808/8420735a-ea36-3f53-688d-cb115e68ec8d.png)

今回使用したのは[Tukeru TH](https://store.braveridge.com/products/detail/34)です。こちらは Bluetooth LE(low energy)規格になります。通常の Bluetooth 規格に対し、省電力のため IoT 用途で使われることが多いようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/928808/6de88a01-f3e0-e87e-eedf-d0ba990a1aba.png)

また、node.js 向けのライブラリもあるためラズパイなどでの個人開発も気軽に始められます。今回こちらのライブラリ[[futomi/node-linking]](https://github.com/futomi/node-linking)を参考に、取得したバイナリデータの物理値変換を実装しました。

Bluetooth LE を扱うのが初めての場合、[こちらの記事](https://qiita.com/ynakajima/items/261e53c8b4ecad7ec0df)がおすすめです。Central 機から Peripheral 機へのデバイス探索 → サービス探索 → キャラクター探索と階層構造を理解しやすい形で説明されています。

# PWA(Progressive Web Apps)

ネットワークに繋げない山中でも起動できること、ナビアプリのバックグラウンドで動作すること、計測したデータはストレージに保管し、ネットワーク接続時に自動アップロードできることを要件とし、実現技術として PWA 化に取り組みました。
※データ収集のための API（Geolocation, Bluetooth）使用にあたって、PWA 化は必須ではありません。

PWA(Progressive Web Apps) は ネイティブアプリライクな使い勝手をもたらす Web アプリです。

- ホーム画面のアイコンから呼び出す
- オフラインで動作する
- プッシュ通知を受け取る<br>
  などなど

ここでは技術の詳細については説明しません。開発時に調べたこと、妥協した点、注意が必要な点について記載します。

## PWA の学習

学習にあたっては Google が公開している[トレーニングコンテンツ](https://developers.google.com/web/ilt/pwa)が参考になりました。使われている技術はモバイルだけでなく PC 環境での UX 改善にも効果があり一読の価値があります。
※[こちら](https://github.com/google-developer-training/pwa-training-labs)の方が新しいようです

## manifest とアイコンイメージの準備

netlify が公開している[Web App Manifest Generator](https://manifest-gen.netlify.app/)サービスを利用すると、manifest.json ファイルと各種画面サイズ用にリサイズしたイメージを生成することができます。

## iPhone での PWA 化

iPhone でも PWA を利用できますが、追加の対応が必要です。Google が[対応方法](https://github.com/GoogleChromeLabs/pwacompat)を公開しています。具体的には以下の文を index.html に追加するだけです。

```html
<link rel="manifest" href="manifest.webmanifest" />
<script async src="https://unpkg.com/pwacompat" crossorigin="anonymous"></script>
```

私が公開したコードでは下記となってます。

```html
<link rel="manifest" href="manifest.json" />
<script async src="https://unpkg.com/pwacompat" crossorigin="anonymous"></script>
```

## Service Worker のスコープ

Service Worker として登録されるファイルの場所は注意が必要です。サブディレクトリに配置すると包含関係にないファイル（images ディレクトリ配下の png イメージなど）に対するリクエストがスコープ外となります。こちらの記事[[ServiceWorker のスコープとページコントロールについて]](https://qiita.com/nhiroki/items/eb16b802101153352bba)を参考にしました。

## Service Worker のライフサイクル

初回ダウンロード時に Service Worker を登録した後に、処理が正常に動作しませんでした。controller について理解が不足していたのですが、こちらの記事[[中級者向け Service Worker Tutorial]](https://blog.jxck.io/entries/2016-04-24/service-worker-tutorial.html)の動画説明が参考になりました。Google のトレーニングコンテンツでは触れられていない内容について、ライブコーディングと図でわかりやすく説明されています。

## キャッシュ戦略

コンテンツのキャッシュについてはきめ細かな制御が可能です。例えば、キャッシュがないときはリクエストする、キャッシュ有無に関わらず毎回最新情報をリクエストする、最初はキャッシュした情報を表示しリクエストのレスポンスが得られたら直ちに差し替える、などです。[The Offline Cookbook](https://web.dev/offline-cookbook/)で多様なユースケースと合わせて実装例が公開されています。

現状の私のコードでは Web サイトに一度アクセスしてキャッシュ登録すると、ユーザがキャッシュを消去しない限りコンテンツを更新することができません。上記のキャッシュ戦略を実装することでユーザの明示的な操作なくコンテンツ更新ができます。

## バックグラウンド動作

Service Worker 上で処理を書くとバックグラウンド処理させることができるのですが、前述の通り Geolocation と Bluetooth は Service Worker 上から呼び出すことができません。このためフォアグラウンド上で実行する必要があり、ナビアプリのバックグラウンドで実行するという要件を妥協せざるを得ませんでした。これらの API 呼び出しを Service Worker に許可しようという議論はなされていますが、プライバシーとセキュリティ上の懸念があるようです。[[参照 1]](https://github.com/w3c/ServiceWorker/issues/745)[[参照 2]](https://github.com/WebBluetoothCG/web-bluetooth/issues/422)

## オフライン対応

上記のキャッシュ登録でオフライン起動、計測が可能ですが、データアップロードには対応できていません。現在公開しているコードでは計測データをキューに詰め、アップロード完了したものから削除しているのですが、オフライン状態が長時間続くとデータ量が多すぎるためかアプリがフリーズしてしまうということがありました。こちらは IndexedDB を使ってデータを一時保管し、Web Background Synchronization を使ってオンライ復帰時の自動アップロードで完全なオフライン対応が実現できそうです。

## 開発者ツール

PWA 開発中はキャシュコンテンツを何度も削除することになります。[Tools for PWA Developers](https://developers.google.com/web/ilt/pwa/tools-for-pwa-developers)が参考になります。

## Android remote デバッグ

Android 端末上の Chrome で developer tool を使ってデバックすることが可能です[[参照]](https://developers.google.com/web/tools/chrome-devtools/remote-debugging?hl=ja)。Port forwarding を使えば PC 上の localhost:8080 などに Android からアクセスすることができ、 Bluetooth の動作確認が容易になります。

## 電力消費

計測のために常時フォアグラウンド処理となるため、電力消費対策が必要です。バッドノウハウですが起動中は手動で明るさを最小限にしたのと、どれだけ効果があったかはわかりませんが、デザインをダークモード調にしました。これにより 1 日は連続計測が可能となりました。

# まとめ

Web アプリでスマホ IoT に取り組んでみました。iPhone 非対応、バックグラウンド非対応、といった制約があり業務での活用は難しいことがわかりました。個人ユースとして考えると、Bluetooth デバイスと繋ぐことができるので、デバイスの選定次第では様々なことができそうです。また今回使用した PWA の技術は、 UX 改善手段としてモバイルに限らず適応できるので Web アプリ開発者としては学ぶ価値があるかと思います。