投稿日

SORACOM で完結!デバイスのファームウェア管理

こんにちは。ソリューションアーキテクトの渡邊(dai)です。

IoT機器のファームウェア管理、どのようにされていますか?
既に出荷済みのIoT機器の機能向上や不具合対応には、ファームウェアの更新で対応するケースもあります。IoT機器の特性上、広域で大量に使われる可能性があるため、あらかじめオンラインでのファームウェア管理の仕組みを検討しておく必要があります。

大型の機器や大規模なサービスを展開する場合には、ファームウェア管理の仕組みが不可欠であり、要件を満たすために自社で構築されることが多いのではないでしょうか。一方で、バッテリー駆動の小型の機器であったり、特定の業務に絞ったサービスにおいては、ファームウェア管理のためだけに新たにシステムを構築するのは大変です。

  • ファームウェアを配布するために配置するストレージの準備
  • 更新の有無を判定できる仕組みの検討
  • ダウンロードの条件や設定(URLなど)の制御

IoTの「つなぐ」を簡単にするサービスを提供している SORACOM では、IoT機器のファームウェア管理をより簡単にするためにお使いいただけるサービスも提供しています。

そこで本ブログでは、SORACOM を使ってIoT機器のファームウェア管理を実現する方法についてご紹介したいと思います。なお、ここで紹介するのはあくまで一例ですので、実際のシステムでは非機能要件と照らし合わせた上で、適宜ご参考にして頂ければ幸いです。

はじめに

はじめに、ファームウェアの管理には、クラウドとデバイスが協調して動作する、いわゆる双方向通信が求められます。そこで、ここでは双方向通信のパターンをおさらいしておきます。

https://www.slideshare.net/SORACOM/b2-soracom-technology-camp-2020

SORACOM ではIoT機器の双方向通信について3つのデザインパターンをまとめています。そのうち「IPアクセスパターン」「アプリケーションパターン」は、待ち受けのポートをIoT機器側で保持する必要があります。今回のユースケースではバッテリー駆動の機器を想定していますので、3つ目の「デバイスリードパターン」が最も適しています。それは、アップデートのトリガをデバイス側に持つことによって、不要な待機電力の消費を防ぎ、デバイスの状態不整合などでアップデートが失敗するリスクを減らすことができるからです。

なお、サービスによってはサーバ側から強制的にファームウェアアップデートを指示したい場合もあると思います。そういったケースでは「IPアクセスパターン」で直接指示を送ったり、「アプリケーションパターン」でSMS等のプッシュ通知を用いて起動する(キックするとも言う)構成を検討いただけると良いかと思います。

ユースケース1: デバイス全てに同じファームウェアを配布する

それでは最初のユースケースです。このケースではシンプルにデバイス側からファームウェアの更新チェックとダウンロードを行うケースを見てみたいと思います。

IoTデータ収集・蓄積サービスの中でファイルを扱える「SORACOM Harvest Files」では ETag に対応しているので、これは簡単に実現できます。あらかじめこちらを参考に SORACOM Harvest を有効にし、ユーザーコンソールより以下の画面からSORACOM Harvest Files上のファームウェアファイルを更新します。

SORACOM Harvest Files のアップロード画面

デバイス側では、特定のタイミングでファームウェアファイルの更新を確認し、更新されている場合にはダウンロード&アップデートを行う必要があります。

ここでは curl コマンドを使って新しいファイルがあるかを確認し、ファイルが更新されていた場合のみファームウェアをダウンロードしてみます。以下の例では、curl の --etag-save オプションを使って現在の ETag を current.etg へ保存し  --etag-compare オプションを使ってcurrent.etgの内容とサーバ側の ETag の比較を行い更新の有無を確認しています。

$ curl --etag-save current.etg --etag-compare current.etg -O http://harvest-files.soracom.io/firmware/firmware-1.0.1.tgz

このコマンドを実行した結果、新しいファームウェアがあればダウンロードが始まり、ファームウェアが保存されます。保存されたファイルが壊れていないか md5sum を使って current.etg と比較した上で、デバイスのアップデートを実施しましょう。

なお、新しいファームウェアがなければファイルは保存されず、上記コマンドはすぐに終了します。

データ保管の仕様について(詳しくはSORACOM Harvest Files の特徴から)
・データ保存期間:731 日間
・Harvest Files側での暗号化機能:なし。暗号化が必要な場合はクライアント側でご対応ください。
・ファイルサイズ上限:5 GiB

ETag の比較に使った--etag-compare は cURL 7.68.0 からの比較的新しい機能です。ご利用中の環境でこのオプションが無い場合は、代わりにIf-None-Match ヘッダーなどを活用ください。

ユースケース2:デバイス毎に異なるファームウェアを配布する

実際の運用では、例えば、特定の環境下で適用されるデバイスのみファームウェアを更新したい、不具合のワークアラウンドとして特定のデバイスでのみファームウェアを更新したい、といった個別のケースはよくあるのではないでしょうか。この場合はユースケース1とは異なる手法で実現できます。

ここではメタデータサービスを使ってこれを実現してみたいと思います。

メタデータサービスではSIMごとに任意の値を設定することが可能です。ここでは例えばデバイスのロットナンバーをあらかじめタグへ設定しておき、特定のロットナンバーのデバイスのみファームウェアアップデートを行う方法を考えてみたいと思います。

ファームウェアのアップデートを始める前に、あらかじめユーザーコンソールより各SIMのタグへロットナンバーを付与しておきます。

IoT SIM のタグを設定する
タグの設定例("lotNumber": "SORACOM101"

そして新しいファームウェアを配信する際は、アップデートの確認に必要なパラメータをユーザーデータを使って配信します。必要なパラメータとしては、ダウンロードを指定するURLになります。また、例えば先祖返りを防止するためにバージョン番号(version)を指定したり、誤ったダウンロードを防ぐためにロット番号(lotNumber)を指定したりして、ダウンロード時に検証するのも有効です。

メタデータサービス設定
メタデータサービスを設定する

ここではユーザーデータへ以下のようなJSONを設定します。新しいバージョンを 1.0.1、対象のロットナンバーを SORACOM101 として、ユーザーデータへ設定する例を記載しています。

{
  "firmware": {
    "version": "1.0.1",
    "lotNumber": "SORACOM101",
    "url": "http://harvest-files.soracom.io/firmware/firmware-1.0.1.tgz"
  }
}

そしてデバイス側では、ファームウェアの更新をする際に以下のような処理を行います。ここでは第一引数で与えられた version と 第二引数で与えられた lotNumber を、メタデータ情報 http://metadata.soracom.io/v1/userdata から取得した値と比較して判定を行っています。バージョンの比較には、sort -rV を使っています。チェックする際は、<script> 1.0.0 SORACOM101 のように実行することで、アップデートが必要かどうかを判定することができます。

#!/bin/bash

# check parameters
if [ $# != 2 ]; then
    echo "Usage: $0 version lotNumber"
    exit 0
fi

# download userdata
USERDATA=$(curl -s http://metadata.soracom.io/v1/userdata)
VERSION=$(echo "$USERDATA" | jq -r .firmware.version)
LOTNUMBER=$(echo "$USERDATA" | jq -r .firmware.lotNumber)

# return biggest version
function biggest() { echo $(echo "$@" | tr " " "\n" | sort -rV | head -n 1); }

if [ "$2" == "$LOTNUMBER" ] && [ "$1" != `biggest $1 $VERSION` ]; then
    echo "$1 is smaller than $VERSION"
    echo "DO SOMETHING HERE..."
fi

これで実際にデバイスごと(バージョンやロットナンバー)によって異なる条件でファームウェアの更新を行うことができそうです。

実装時に気を付けること

これまでご紹介した方法でファームウェアの更新確認や対象デバイスを絞り込む方法を見てきました。ここではさらに Dive Deep して、実際の運用時に発生しそうなポイントについて、より便利に使って頂ける内容をご紹介したいと思います。

通信速度

ユースケースを実際にやってみると、ファームウェアのダウンロードに非常に時間がかかる場合があります。これは、誤って大容量通信をしてしまうことを防止するために、IoT SIM の速度クラスがあらかじめ s1.standards1.slow に設定されていることがあるためです。そこでメタデータサービスを使って、デバイス側から速度クラスを変更する方法を見てみます。

メタデータサービスには、任意の値を保持する「タグ」以外にもシステムであらかじめ定義されたパラメータが存在します。そのうちspeedClassはユーザーコンソールでも変更可能な速度クラスを表しています。以下の例のように、ファームウェアダウンロードのときだけspeedClassをデバイスから変更することで、ダウンロードを高速に行うことが可能となります。

メタデータの書き込み例: 速度クラスを変更する

$ curl -s -X POST http://metadata.soracom.io/v1/subscriber/update_speed_class \
-H "Content-Type:application/json" \
-d '{"speedClass": "s1.4xfast"}'

なお、これらの機能はメタデータサービスの読み取り専用をOFFにする必要があります。また、speedClassの設定はダウンロードが終わったらもとの値に戻しておくことも忘れずに行いましょう。

ご契約のサブスクリプションによっては、通信速度によりご利用料金が異なる場合があるのでご注意ください。また、夜間などご利用料金がお得になるプランもありますので、合わせてご確認ください。

デバイス側での処理完了通知の仕組み

デバイス側でファームウェアアップデートを行った際、アップデートの処理が終わったことをユーザーコンソール側でも確認できると運用が捗るのではないでしょうか。

先の例ではデバイス側からメタデータを操作する例を見ましたが、「タグ」もまたデバイスから更新することが可能ですので、これを使って、デバイスが自律的に現在のバージョン情報を設定する例をご紹介します。

メタデータの書き込み例: IoT SIM のタグを追加 / 更新する

$ curl -s -X PUT http://metadata.soracom.io/v1/subscriber/tags \
-H "Content-Type:application/json" \
-d '[
  {
      "tagName":"version",
      "tagValue":"1.0.1"
  },
  {
      "tagName": "lotNumber",
      "tagValue": "SORACOM101"
  }]'

なお、こちらの方法でユーザーコンソールへデバイスのファームウェアバージョン一覧を表示することも可能です。一覧性が上がるのでおすすめです。また、デバイス出荷時に自動的にタグ付けをするにはこのあたりも参考にしてください。デバイスのライフサイクルや運用面までカバーされているので大変参考になると思います。

アクセスの集中負荷分散

ファームウェアの更新処理は、これまでご説明したとおりダウンロードを伴うためサーバに大きな負荷が伴います。特定の時刻や特定の状態(端末の起動時など)にこれらの処理を何も考えずに行うと、大量の機器からサーバへの同時アクセスが発生してしまい、サーバがパンクしたりネットワーク帯域を食いつぶしてしまったりします。

これらを避けるためには、ユースケース2でご説明したデバイス毎の分散も効果がありますが、さらにその中でも細かな条件を設定したり、時刻をランダムに分けるなどの工夫が必要です。

アップデート条件の更新

ユースケース2では、バージョンとロットナンバーをアップデート条件とする方法をご紹介しました。

しかしこの方法では、あらかじめデバイスへアップデート条件を判定するためのスクリプトを保存しておく必要があります。実際の運用をはじめてみると、事前に設定した単純な条件だけではアップデートの要否を決められず、アップデートの条件自体を変えたいケースも出てくると思います。そこで、もう一歩進んだやり方として、デバイスで実行されるロジックは極力シンプルにし、アップデートの条件も後からユーザーコンソールで設定できるようにしてみたいと思います。

メタデータサービスのユーザーデータへはスクリプトも設定することが可能です。そこで、デバイス側ではスクリプトのダウンロードと実行のみを実施し、アップデート条件はスクリプトとしてユーザーデータを使って配布する方法をご紹介します。

(デバイス側) ユーザーデータをダウンロードして実行

curl -s http://metadata.soracom.io/v1/userdata | bash

ユーザーデータへ配置するスクリプトでは、端末自身のバージョンやロットナンバーはあらかじめタグとしてSIM情報へ登録されているものとしています。先程はスクリプトの引数として貰っていたデバイス自身の情報を、今度は「タグ」から取得し CURRENT_VER, CURRENT_LOT へ格納した上で、同じようにユーザーデータと比較しています。

また、ユーザーデータへはこのスクリプト自体を設定することにしたので、ファームウェアのバージョンなどユースケース2で使用したJSONの属性情報は、SORACOM Harvest Files へ firmware-1.0.1.json として配置しています。

(ユーザーデータ側)ロットナンバーを比較しアップデートを実行

#!/bin/bash

# download tags
TAGDATA=$(curl -s http://metadata.soracom.io/v1/subscriber)
CURRENT_VER=$(echo "$TAGDATA" | jq -r .tags.version)
CURRENT_LOT=$(echo "$TAGDATA" | jq -r .tags.lotNumber)

# download userdata
USERDATA=$(curl -s http://harvest-files.soracom.io/firmware/firmware-1.0.1.json)
VERSION=$(echo "$USERDATA" | jq -r .firmware.version)
LOTNUMBER=$(echo "$USERDATA" | jq -r .firmware.lotNumber)

# return biggest version
function biggest() { echo $(echo "$@" | tr " " "\n" | sort -rV | head -n 1); }

if [ "$CURRENT_LOT" == "$LOTNUMBER" ] && [ "$CURRENT_VER" != `biggest $CURRENT_VER $VERSION` ]; then
    echo "$CURRENT_VER is smaller than $VERSION"
    echo "DO SOMETHING HERE..."
fi

さいごに

最後まで読んでいただき、有難う御座いました!

IoT機器のファームウェア管理にお困りの方が、SORACOMを使って運用の手間を削減するのに少しでもお役に立てればうれしいです。

今年もSORACOM Discovery の開催がいよいよ近くなってきました。今年は、7/6 ~ 7/7でオンライン開催されます!皆さまのご参加お待ちしております!

― ソラコム渡邊(dai)