# LoRa + MQTT + OpenRemote によるIoTシステムの構築

**URL:** <https://forum.ficusonline.com/t/topic/568>\
**Category:** Automation\
**Tags:** lora, openremote, mqtt, automation\
**Created:** [2026 年 9 月 24 日午前 2:23 UTC](https://forum.ficusonline.com/t/topic/568 "2026-09-24T02:23:50Z")\
**Posts on this page:** 1\
**Showing post:** 14

<div class="post-metadata">

**Author:** ![tk-fuse](https://forum.ficusonline.com/user_avatar/forum.ficusonline.com/tk-fuse/32/255_2.png) [@tk-fuse](https://forum.ficusonline.com/u/tk-fuse)\
**Post date:** [2026 年 10 月 29 日午前 7:46 UTC](https://forum.ficusonline.com/t/topic/568/14 "2026-10-29T07:46:40Z")

</div>

## SipHashとnonceを導入（アクチュエータノード）

### 通信仕様の変更

- UARTの下りフレーム(ESP32→STM32)を、7バイトから19バイトに変更しました。形式は `CC 33 target(2) type value nonce(4) tag(8) CRC8` です。CRC8の対象は `target` から `tag` までです。
- LoRaの `CmdPacket` を、11バイトから19バイトに変更。`tag[8]` を追加し、`cmdSeq` 欄にはActuatorが発行したnonceを入れます。
- LoRaの `StatusPacket` を、15バイトから19バイトに変更。`nonce`(4バイト)を追加しました。
- 署名の対象は、`magic0` から `cmdValue` までの10バイトです(リトルエンディアン)。アルゴリズムは SipHash-2-4 で、タグは8バイトです。

### 新規ファイル

- `siphash24.h` を、`esp32_gateway/src/` と `stm32_dx-lr30/src/` の両方に追加(ヘッダのみの実装です)。
- `stm32_dx-lr30/src/mesh_secret.h` を追加。STM32側の鍵 `MESH_KEY[16]` を置くファイルで、コミットしません。`.gitignore` に追記。
- `gen_mesh_key.py` を追加。乱数で鍵を作り、`mesh_secret.h` を書き出し、ESP32に入れる32文字のhexを表示します。

### STM32:`mesh_protocol.h`

- `siphash24.h` と `stddef.h` をinclude。
- `CmdPacket` に `tag[8]` を追加。
- `StatusPacket` に `nonce` を追加。
- `meshCmdVerify()` を追加。タグを、定数時間で比較して検証します。
- `meshCmdJudge()` を追加。結果は `MCV_REJECT`、`MCV_APPLY`、`MCV_RESEND_STATUS` のいずれかです。
- `meshNonceMix()` を追加。前回のnonce、カウンタ、`micros()`、RSSI、ADCを、鍵つきでハッシュしてnonceを作ります。`0xFFFFFFFF` は予約値で、直前と同じ値も避けます。

### STM32:`mesh_main.cpp`

**Actuator**

- `actNonce` と、関連の変数を追加。
- `actRotateNonce()` を追加。`actSendStatus()` は、`sp.nonce` を設定して送信します。
- `loopActuator()` のコマンド受信を、`meshCmdJudge()` の結果で分岐するように変更。
  - タグが不正なら破棄。
  - nonceが一致すれば、リレーを適用します。
  - nonceが古い(使用済みの再送や、再生されたもの)場合は、再適用せずに、ステータスだけを返します。返信は500 msに1回までに制限。

- 次の3つのタイミングで、nonceを更新。
  - コマンドの適用後(結果のステータスと同時)
  - 60秒ごとの定期ステータスの直前
  - 起動時の最初のステータスの直前

**Gateway**

- UARTの下りの受信を、16バイトの本体を貯める方式に書き換え。
- `PendingCmd` に、nonceとタグを持たせました。
- `sendCommandToActuator()` を、`PendingCmd` を受け取る形に変更。

**その他**

- `mesh_secret.h` をinclude。
- `CMD_CONFIRM_TIMEOUT_MS` を、400から600に変更。

### ESP32:`main.cpp`

- `StatusPacket` の構造体に、`nonce` を追加。
- nonceの管理表を追加(8スロット、ミューテックスで保護)。
  - ステータスを受け取るたびに、Actuatorごとの最新nonceを覚えます(`nonce_on_status`)。
  - コマンドは、nonceがあればすぐに署名して送り、なければ保留します(最新の1件だけ、最長70秒)。
  - 送信後に、3秒たっても新しいステータスが来なければ、コマンドが届かなかったとみなし、同じnonceを再利用します。

- `cmd_sign()` と `send_signed_cmd()` を追加。19バイトのUARTフレームを組み立てます。
- MQTTの `cmd/relay` の処理を、`nonce_submit_cmd()` の呼び出しに置き換え。
- 鍵の読み込み(`cmd_auth_load_key()`)を、`gw_config_load()` の直後で行います。未設定、または不正なら、リレーコマンドを無効にして、ログに案内を出力。

### ESP32:ポータルと設定(`key-portal.patch`)

 ![Screenshot from 2026-10-29 08-17-22](https://forum.ficusonline.com/uploads/default/original/2X/1/1f91025d1baf183e81f0f74a88fc548ae46d5ec5.png)

- `gw_prov.h` の `GwConfig` に、`mesh_key_hex[33]` を追加。
- `gw_prov.cpp`:NVSのキー `meshkey` に、読み書きします。
- ポータルに「Relay command key」の欄(パスワード欄)を追加。空欄なら保存済みの値を維持し、32文字のhex以外は拒否。
- 保存時のログには、鍵そのものは出さず、`set` か `NOT set` だけを出力。

### 動作として得られるもの

- ESP32が署名。Actuatorが、タグとnonceを検証。
- 盗聴したコマンドを再送しても、nonceが更新されているため、適用されません。
- 署名済みで、届かなかったコマンドは、最長60秒で失効。

---

_[View the full topic](https://forum.ficusonline.com/t/topic/568)._
