LoRaWAN(ChirpStack)~
AoTは、ChirpStack v4 ネットワークサーバーを通じてLoRaWANデバイスと接続します。アップリンクはChirpStackのMQTTブローカー経由で届き、入力の測定値になります。ダウンリンクは、出力がChirpStack APIを通じてキューイングします。どちらのモジュールも標準で組み込まれています。
接続は 設定 → ChirpStack で一度だけ設定します。同じページから、ChirpStackにすでに登録済みのデバイスを選び、DevEUIやキーを打ち直すことなく、AoTの入力・出力として登録できます。
AoTが行わないこと~
AoTはLoRaWANネットワークサーバーではなく、ゲートウェイマネージャーでもありません。ChirpStackのクライアントです。
以下はすべてChirpStack側にとどまるものであり、AoTには対応する画面がありません。
- ゲートウェイの登録と、そのゲートウェイが動作するリージョン/チャンネルプラン
- テナント、アプリケーション、デバイスプロファイル、デバイス(DevEUI、AppKey、Join)の作成
- RX2データレート — ダウンリンクのペーシングで説明するとおり、AoTの送信レートはこれに合わせて調整されます
- デバイスプロファイルがClass Cを許可するかどうか
したがって、順序は常に次のとおりです。まずChirpStack側でデバイスをJoinさせてアップリンクを送らせ、それからAoTに取り込みます。一度もJoinしていないデバイスは、AoT側で何をしても表示されません。
AoTとChirpStackの接続~
1. APIキーを保存する~
ChirpStackのAPIキーは、ChirpStackのページには直接入力しません。設定 → APIキー管理 のキーストアに保存し、ChirpStackのページでは保存済みのキーのどれを使うかを選ぶだけです。まずそちらでキーを追加してください。
| 項目 | 備考 |
|---|---|
| 名前 | 自由に決められるラベルです — ChirpStackページのドロップダウンにはこの名前が表示されます |
| プロバイダー / メーカー | 任意です。ドロップダウンでは名前の後ろにかっこ書きで表示されます |
| APIキー | ChirpStackが発行したキーの値です。トークン本体だけを貼り付けてください |
トークンは Bearer という接頭辞を付けずに 貼り付けてください。付いていた場合は使用前に取り除かれますが、値をきれいな状態にしておくと他の場面での混乱を避けられます。
キーの権限によって、AoTが見えるものが決まります。管理者キーはテナントを列挙でき、したがってその配下のすべてのアプリケーションも見えます。テナント限定のキーはテナントを列挙できません。AoTはこれを検知し、テナントで絞り込まずにアプリケーションを一覧表示する方式にフォールバックするため、そのようなキーでも動作します — ただしそのデバイスは、テナント列が空欄のまま表示されます。
2. 接続情報を入力する~
設定 → ChirpStack を開きます。
| 項目 | 備考 |
|---|---|
| ChirpStack gRPC サーバー | ChirpStack APIの host:port です。ポートを省略すると :8080 が補われます |
| API トークン | 保存済みのAPIキーから1つを選びます。APIキーの管理 を押すとキーストアが開きます |
| MQTT ブローカーホスト | ChirpStackがイベントを発行するブローカーのホストです |
| MQTT ブローカーポート | 通常は 1883 です |
保存 を押します。更新 は何も変更せずにデバイス一覧を再読み込みします。
サーバー欄に付けたスキーム(http://、https://)や末尾のパスは無視されます — 使われるのはホストとポートだけです。ポートが8080でない場合は必ず明示してください。省略すると、クライアントが到達できないアドレスに解決してしまい、わかりやすい認証エラーや接続エラーの代わりに、紛らわしい「network unreachable」で失敗することがあります。
2つの異なる通信経路
gRPCサーバーのアドレスは、ダウンリンクとデバイス一覧の取得に使われます。MQTTブローカーは、アップリンクに使われます。この2つは別々のサービスで、同じホストでも別のポートになっていることが多いため、デバイス一覧が取得できたからといってアップリンクが届く保証にはならず、その逆も同様です。
gRPCクライアントライブラリが不足している場合、ページ上部に通知が表示され、デバイス一覧は空のままになります。これらはAoTのrequirementsに含まれているため、通常は手動で構築した環境でしか起こりません。
3. デバイス一覧を確認する~
接続を保存すると、そのキーが到達できるすべてのデバイスがテナント → アプリケーション → 名前の順で一覧表示され、テナント と アプリケーション のドロップダウンで絞り込めます。各行には 詳細 ボタンがあり、DevEUI、テナント、アプリケーション、最終確認日時、そのデバイスがすでにAoTの入力または出力として登録済みかどうかが表示されます。
デバイスが見つからなかった旨のメッセージは、キーがChirpStackには到達したものの何も見えなかったことを意味します — デバイスはここに表示される前に、ChirpStack側に存在している必要があります。一方、エラーメッセージが出た場合は、接続そのものかキー自体が失敗しています。
デバイスの登録~
デバイスオンボーディング セクションは、デバイス一覧が埋まった後にだけ表示されます。下部の表で必要なデバイスにチェックを入れ、何を作成するかを選びます。
入力(アップリンク) — Register Input をオンにすると、選択した各デバイスにつき1つのChirpStack MQTT入力が作成されます。Channels 欄には1行につき1つのJMESPath式を入力し、その行数がそのまま測定チャンネルの数になります。同じデバイスプロファイルを共有するデバイスは同じペイロード構造で送信するため、通常は同じ式のかたまりをまとめて全デバイスに適用できます。例:
出力(ダウンリンク) — Register Output をオンにすると、選択した各デバイスにつき1つのChirpStackダウンリンク出力が作成されます。あわせて、そのデバイスプロファイルに共通の値 — 16進数の On payload と Off payload(例: 010110 / 010210)、ファームウェアが待ち受ける FPort — を入力します。
その後 Register selected devices を押します。AoTは各デバイスについて、DevEUI・ブローカーアドレス・サーバーとキーがあらかじめ入力済みのモジュールを作成するため、デバイスごとに打ち直す必要はありません。ここで作成されるものはすべて通常の入力・出力であり、入力や出力のページを開けば、名前の変更・チャンネルの調整・ペイロードの変更ができます。
接続情報は参照ではなくコピーされる
登録処理は、現在のMQTTホストとポートを各入力にコピーし、現在のgRPCサーバーとAPIキーを各出力にコピーします。これらはスナップショットです。後でChirpStack設定ページのサーバーアドレスを変更したり、APIキーをローテーションしたりしても、登録済みの入力・出力は古い値を保持し続けるため、個別に更新する必要があります。設定から常に最新の値が読まれるのは、出力の確認リスナーが使うMQTTブローカーだけです。
同じデバイスを2回登録すると、最初のモジュールが更新されるのではなく、2つ目のモジュールが作成されます。すでに何が存在するかは、デバイスの詳細にある Registered の行で確認してください。
アップリンク: 測定値を読み取る~
オンボーディングで作成される入力は ChirpStack: MQTT (Payload JMESPath Expression) です。ChirpStack v4のアップリンクトピック application/+/device/+/event/up を購読し、各イベントのJSONに対してチャンネルごとに1つのJMESPath式を評価します。
| オプション | 備考 |
|---|---|
| MQTT Host / Port | ブローカーのアドレスです。登録時にChirpStackの設定から入力されます |
| MQTT Username / Password | 任意のブローカー認証情報です |
| Enable TLS / CA Certificate Path | 任意設定です。TLSには通常ポート8883を使います |
| Subscribe Topics | カンマ区切りです。既定ではChirpStack v4のアップリンクトピックです |
| Device EUIs | カンマ区切りのフィルターです。オンボーディングでは対象の1台のデバイスに設定されます |
| QoS, Keepalive, Client ID | 標準的なMQTT設定です |
チャンネルごとに設定するのは名前と JMESPath Expression だけで、イベントオブジェクト全体に対して評価されます。手がかりになる式の例:
| 式 | 値 |
|---|---|
object.battery_V | デコードされたフィールドです — ChirpStackのコーデックが object の下に出力する内容そのままです |
max_by(rxInfo,&rssi).rssi | このアップリンクを受信した中で最も良いゲートウェイのRSSI |
max_by(rxInfo,&snr).snr | 最も良いゲートウェイのSNR |
値の保存のされ方について、知っておくべき点は次のとおりです。
- 式は数値に変換できる値に解決される必要があります。結果が
nullの場合はそのチャンネルには何も保存されませんが、これはエラーではありません — あるアップリンクにそのフィールドが存在しないときの、正常な扱われ方です。 - コンパイルに失敗した式は、そのチャンネルを無効化し、起動時に一度だけログに記録されます。そのため、タイプミスはデータが黙って欠けるのではなく、ログに現れます。
- 測定値のタイムスタンプは、届いた時刻ではなく、アップリンク自身の
timeフィールドから取られます。 - Select Measurements to Enable でオフにされたチャンネルは、保存の直前に破棄されます。これはプッシュされた測定値にも適用されるため、チャンネルを無効にすれば実際に記録が止まります。
- この入力はリスナー方式です。Period はポーリング間隔ではなく、決まったスケジュールで何かを取得することはありません。
この入力の通信状況は、デバイスではなく ブローカーとの接続 に連動します。アップリンクが届けばデバイスが生きている証拠になりますが、届かないからといってその逆が証明されるわけではありません — センサーに単に報告することがないだけかもしれないからです。一定時間届かないデバイスをオフラインとして扱いたい場合は、測定値の経過時間をもとに機能で組み立ててください。
MQTT経由の方法ではカバーできない場合のために、他に2つの入力があります。
- ChirpStack: REST API (Payload JMESPath Expression) — 購読する代わりに、一定周期でChirpStackのREST APIをポーリングします。ブローカーにAoTから到達できない場合に使います。専用のAPIベースURL(RESTプロキシは通常ポート8090)とトークンが必要です。
- RAK3172 Valve Controller: Heartbeat (ChirpStack MQTT) — そのファームウェアのFPort 225ハートビートを、JMESPath式を使わずに直接デコードします — バッテリー、ノードクラス、ハートビート周期、バルブの状態、RSSI、SNR。
ダウンリンク: デバイスを制御する~
オンボーディングで作成される出力は On/Off: ChirpStack gRPC です。チャンネルをオン・オフにすると、そのデバイス宛のダウンリンクがキューイングされます。
| オプション | 備考 |
|---|---|
| ChirpStack gRPC Server / API Key | 登録時に設定ページからコピーされます |
| DevEUI | 対象のデバイスです |
| FPort | ファームウェアがコマンドを待ち受けるポートです |
| Payload Format | Hex Bytes または JSON Object です |
| On Payload / Off Payload | 送信するフレームです |
| Confirmed | LoRaWANの確認付きダウンリンクを要求します |
| コマンドタイムアウト(秒) | デバイスからの応答を待つ間、指令した状態を楽観的に保持しておく時間です。このモジュールでは既定で8が入力済みです |
| Enable Debug Logging | 接続とキューイングの通知をログに記録します。通常運用ではオフのままにしてください |
AoTはまずgRPCを試み、gRPCクライアントが使えない場合や呼び出しが失敗した場合はRESTのキューエンドポイントにフォールバックします。この際、gRPCサーバーがポート8080であればポート8090も試します。いずれの経路でも、フレームはそのデバイスのChirpStackキューに入ります。
キューに入ったことは、届いたことを意味しません。 Class Cのデバイスはいつでも到達できます。Class Aのデバイスは、次のアップリンクに続く受信ウィンドウでのみキュー内のダウンリンクを受け取るため、ハートビート間隔が長いとコマンドが長時間キューに留まることがあります。Class Cを許可するかどうかはChirpStack側のデバイスプロファイルのプロパティであり、AoTがそれをどう操作するかはクラススケジューリングを参照してください。
確認~
この出力は、フレームがキューに入っただけでデバイスが切り替わったとは見なしません。そのDevEUIのアップリンクトピックをMQTTブローカー上で購読し、ファームウェアからの確認応答とステータスフレームを監視します。その結果として:
- デバイスからの報告が届くまで、そのチャンネルは「オン」ではなく 保留中 の扱いです。時限コマンドの経過時間は、デバイスの応答があった時点から計測が始まるため、送信の遅延によって報告される点灯時間が水増しされることはありません。
- ChirpStack v4は確認応答のないダウンリンクを再送しないため、AoTはタイムアウトの範囲内で同じコマンドを自ら再送します。再送の間隔はコマンドタイムアウトの3分の1ですが、ペーシングの間隔より短くなることは決してありません — 物理的にそれより早く送信できない再送は、キューに積み上がって期限切れになるだけだからです。
- 確認が得られないままウィンドウが閉じると、そのコマンドは failed として報告され、チャンネルはコマンド実行前の状態に戻ります。デバイスが一度も確認していない状態を表示し続けることはありません。
- 確認できないまま諦めたチャンネルはオフラインとして扱われます。以降のコマンドは再送の連打なしに、探りとして 1回だけ 送信されるため、いなくなったデバイス1台がサイト全体のダウンリンク予算を食いつぶすことはありません。1回でも確認が取れれば、通常の状態に戻ります。
このリスナーが使うブローカーは、設定 → ChirpStack から常に最新の値が読まれます。そのため、入力側でアップリンクが届いているのに確認だけが一向に届かない場合、原因が通常このブローカーにあることはありません。
ダウンリンクのペーシング~
AoTのすべてのダウンリンク — 出力からのもの、クラススケジューラーからのもの、そしてすべての再送 — は、サイト全体で共有する1つのレートリミッターを通過します。負荷がかかると挙動がおかしくなるLoRaWANサイトについて理解すべきことの中で、これが最も重要な1点です。
なぜ必要か。 1つのサイトには通常ゲートウェイが1台しかなく、しかも半二重通信です — 送信している間は何も受信できません。Class Cのダウンリンクは RX2 で送出され、RX2データレートが低いと、たった1つの小さなフレームでも1秒を超える送信時間を占有します。コマンドが連続してキューに入ると、ゲートウェイはほぼ毎分のあいだ聞こえない状態になります — ちょうどそのとき、再送ロジックが待っているデバイスからの確認応答が返ってきているのです。確認応答の喪失がさらなる再送を引き起こし、それがさらに送信時間を増やし、さらに確認応答を失わせます。実際にこの崩壊を現場で測定したところ、5秒以内の確認応答率が約61%まで低下していました。
どう動くか。 サイト全体で、任意の2つのダウンリンクの間に最小の間隔が強制されます。送出の枠を得るために30秒を超えて待たされるはずの送信は、遅れて送るのではなく 破棄され、失敗として報告されます。半分待たされたバルブコマンドはどのみち古すぎますし、たまった分を一度に解放すれば、ペーシングがそもそも防ごうとしていた洪水そのものを再現してしまいます。
この間隔はRX2データレートと連動しており、両者は一緒に動かす必要があります。この間隔は設定項目ではなく組み込みの定数であるため、別のRX2データレートに合わせるにはコードの変更が必要で、設定の変更では済みません。
| ChirpStack RX2データレート | フレーム1件あたりのおおよそのエアタイム | 対応する間隔 |
|---|---|---|
rx2_dr = 0(SF12) | 約1.32秒 | 4.0秒 |
rx2_dr = 2(SF10) | 約0.37秒 | 1.5秒 — AoTの現在値 |
rx2_dr = 3(SF9) | 約0.19秒 | 0.8秒 |
避けるべき失敗パターン: ChirpStack側のRX2をSF12のままにしておきながら、AoTはSF10用にペーシングしている状態です。その場合、送信のたびに間隔のほとんどを占有してしまい、ゲートウェイはほぼ常に聞こえない状態になり、前述の崩壊が再び起こります。間隔を縮めずにRX2データレートだけを上げるのは単に慎重すぎるだけですが、データレートを上げずに間隔だけを縮めるのは危険です。
ペーシングによる2つの副作用が他の場面で見えることがありますが、どちらも不具合ではありません。
- 多数のLoRaWAN出力を有効化・再読み込みすると時間がかかります。起動時の状態フレームもペーシングの対象になるためです。デーモンがすぐに起動できるよう、AoTはこれらの処理をあえてバックグラウンドに回しています。
- 出力の設定を保存しても、停止・起動のフレームは送信されません。これは、バルブの名前を変えただけでダウンリンクを2回消費してバルブが動いてしまう、という事態を避けるためです。一方、出力を削除すると停止状態は適用されるため、デバイスは安全な状態で残されます。
クラススケジューリング~
Class Cはデバイスをいつでも到達可能にしますが、受信機を常時オンにするため電力を消費します。バッテリーやソーラーのノードでは、Class Cを四六時中動かすのはたいてい割に合いません。LoRaWANクラススケジューラー 機能は、その判断を行うサイト単位の唯一の権限を持ちます — 共有するChirpStackデバイスプロファイル上でClass Cのサポートを切り替え、それに合わせたハートビートモードをデバイスへ一斉に伝えます。これにより、デバイスごとに個別設定するのではなく、クラスとハートビート間隔が一貫して保たれます。
主な設定は次のとおりです。
| 設定 | 備考 |
|---|---|
| Control mode | AUTO は環境系の入力をスコア化し、MANUAL は毎日決まったClass Cの時間帯を使います |
| 手動でのClass C開始・終了 | MANUAL モードでの毎日の時間帯です |
| スコアのしきい値、最小滞留時間 | AUTO モードで、アクティブ状態に入る/抜けるタイミングと、最小滞留時間です |
| クラス / ハートビート周期 | アクティブ・レスト・冬季の各状態でのハートビート間隔です |
| 冬季開始・終了 | 強制的にレスト状態にする日付範囲です |
| バッテリー種別 | バッテリーゲートを有効にします — 電力に余裕のないノードはClass Cにされません |
| RESTポート | ChirpStackのRESTポートで、既定は8090です。サーバーとキーはChirpStackの設定から取得されます |
アクティブ状態を指定した分数だけ強制するボタンと、その強制を解除するボタンが手動で使えます。スケジューラー自体には測定値の設定はありません — テレメトリのチャンネルは、デバイスがそのスケジューラーに割り当てられたときに作成されます。
デバイスごとに設定する旧来の LoRaWAN Mode/Period Manager (RAK3172E) 機能もまだ存在し、動作します。ただし、サイト単位のスケジューラーがこれに取って代わります。デバイスごとに1つのマネージャーを置くより、多数のデバイスに1つのスケジューラーを使うほうを優先してください。
ノードを1つのデバイスとして追加する~
ノードがセンサーとアクチュエーターの両方を兼ねている場合、入力と出力を別々に登録すると、同じDevEUIを共有する2つのエントリを管理し続けることになります。Device ページには、1組の接続情報からその2つをまとめて作成する複合デバイスの種類が用意されています — ChirpStackのリレーノードなら AoT-C (Solar 12V Relay Node) です。DevEUI、MQTTブローカー、gRPCサーバーとキーを一度入力すれば、テレメトリ用の入力とダウンリンク用の出力が作成され、その後もそれらと連動し続けます。
その Class Scheduler 欄では、このDevEUIを管理させる既存のLoRaWANクラススケジューラーを選びます。スケジューラーを新規作成するわけではありません — 多数のデバイスが1つのスケジューラーを共有することがポイントだからです。自分でクラスを管理したい場合は、未設定のままにしておいてください。
トラブルシューティング~
このリストを順番に進めてください。各手順は、それより上の手順が通っていることを前提とします。
デバイス一覧が空、またはエラーが表示される場合。
- ChirpStack自身のWeb画面から、ChirpStack側にデバイスが存在することを確認します。
- gRPCサーバー欄を確認します — ホストとポートで、ポートが8080でない場合は明示してください。スキームとパスは無視されるため、それらが原因になることはありません。
- 選択したAPIキーの値が、他の何かではなくChirpStackのトークンであることを確認します。ドロップダウンにはキー名が表示されるため、保存済みの別のキーを誤って選びやすいので注意してください。
- 特定のテナントのデバイスだけが出てこない場合、そのキーはおそらく別のテナントに限定されています。管理者キーであればすべて見えます。
- ページにgRPCクライアントライブラリが不足していると表示される場合、環境が不完全です — 依存関係を参照してください。
ChirpStack側には届いているのに、AoTにアップリンクが届かない場合。
- デバイス一覧はgRPCを使い、アップリンクはMQTTを使います。MQTTブローカーのホストとポートを別途確認し、AoTからそのブローカーに到達できることも確かめてください(コンテナは、ブラウザで使っているのと同じホスト名を解決できないことがあります)。
- 入力の Device EUIs フィルターを確認します。アップリンクのDevEUIと一致しない場合は、仕様どおり黙って破棄されます。
- Subscribe Topics 欄が、依然としてChirpStack v4のアップリンクトピックと一致しているか確認します。
- 入力のログレベルをdebugに設定し、ログを観察します。アップリンクごとに、評価された式とその結果がログに記録されます。式の結果が
nullになる場合 — パスの間違いか、コーデックがデコードできていないことが原因で — 接続はしているのに何も保存されない入力の典型的な原因です。 - Select Measurements to Enable でそのチャンネルが有効になっていることを確認します。無効なチャンネルは、保存の直前に破棄されます。
- ChirpStackのコーデックが実際にペイロードをデコードしていることを確認します。イベントJSONの中で
objectが空であれば、object.配下のどんな式も値を生み出せません。
コマンドがデバイスに届かない場合。
- デバイスのクラスを確認します。Class Aでは、キューに入ったダウンリンクは次のアップリンクを待ちます。ハートビートが長い場合、それが何分にもなることがあります。
- ChirpStack側でそのデバイスのキューを確認します。フレームがキューに残っている場合、AoT側の処理は正しく動いており、問題は配送側にあります。AoT側で失敗しているのにキューが空の場合、キューイング自体が失敗しています — その出力のgRPCサーバーとキーを確認してください。これらは登録時にコピーされたものであり、古くなっている可能性があることを忘れないでください。
- 単発のコマンドは成功するのにまとめて送るとまとまって失敗する場合、これはペーシングによるものです。ChirpStackのRX2データレートがAoTのペーシング前提と一致しているか確認し、同時に切り替えるデバイスの数を減らしてください。
- 出力側は失敗と報告しているのに、実際にはデバイスが切り替わっている場合、確認応答が戻ってきていません。MQTTブローカーの設定と、ファームウェアの確認応答のポート・形式がこのモジュールの想定どおりであることを確認してください。
- 1つのチャンネルで失敗が繰り返されるとオフライン扱いになり、以降のコマンドは単発の探りとしてしか送られなくなります。これは意図された動作であり、1回でも確認が取れれば解除されます。
普段は問題なく動くのに、負荷がかかると劣化する場合。 ほとんどの場合、これはペーシングとRX2の相互作用によるものです。タイムアウトを変更する前にダウンリンクのペーシングを読んでください — 再送の回数を稼ごうとコマンドタイムアウトを延ばすと、飽和したリンクはかえって悪化します。
一般的なデバイス・デーモンの問題については、トラブルシューティングを参照してください。