Chapter 17
デバイス実装の実際 — プラットフォーム・Factory Data・省電力・TLV
この章がなぜ必要なのか——「動く」と「製品になる」の間に大きな溝がある.
評価ボードでサンプルが動いた。だが製品にするには、 メモリは足りるのか、電池は何年持つのか、量産で鍵をどう入れるのか—— ハードウェアの現実と向き合う必要がある。
この章は、その現実的な制約を整理する。 併せて、Matter の全ペイロードを支える TLV も扱う。
この章で使う既出の用語(定義は各リンク先). Matter(01 章 3 節)、アプリケーション層(01 章 3 節)、セキュリティ(01 章 2 節)、ネットワーク(01 章 3 節)、Client(02 章 5 節)、mDNS(03 章 4 節)、Wi-Fi(04 章 9 節)、BLE(05 章 1 節)、必要(05 章 5 節)、Exchange(06 章 5 節)、SAI(06 章 3 節)、SII(06 章 3 節)、SRP(06 章 2 節)、再送(06 章 3 節)、UniqueID(08 章 1 節)、イベント(08 章 8 節)、Read(09 章 1 節)、Write(09 章 1 節)、Critical(10 章 5 節)、MaxInterval(10 章 1 節)、Manage(11 章 8 節)、改竄(11 章 1 節)、署名(11 章 2 節)、証明書(11 章 5 節)、DAC(12 章 11 節)、トレーサビリティ(12 章 7 節)、委託製造(12 章 7 節)、ZAP(16 章 1 節)
1. SoC の選定
必要なリソースの目安
| 項目 | 目安 |
|---|---|
| Flash | 1 MB 以上(OTA の A/B 領域を取るなら 2 MB 以上) |
| RAM | 128 KB 以上(クラスタ数と Fabric 数で変動) |
| 不揮発(設定用) | 数十 KB(Fabric 5 個で 5〜10 KB、14 章) |
| 暗号アクセラレータ | ほぼ必須(P-256、AES) |
| TRNG | 必須(11 章) |
| セキュアストレージ | DAC 秘密鍵の保護(12 章) |
OTA が Flash 要求を倍にする. 更新中に失敗しても元に戻れるようにするには、 新旧 2 つのイメージを同時に保持する必要がある(18 章)。 外部フラッシュを追加する設計も一般的である。
「1 MB あれば足りる」で設計を始めると、OTA で詰む。 最初から 2 MB 以上、または外部フラッシュを前提にする。
主なプラットフォーム
| ベンダー | 代表的な SoC | 対応 |
|---|---|---|
| Espressif | ESP32-C3 / C6 / H2 / S3 | Wi-Fi、Thread(C6/H2)、BLE |
| Nordic | nRF52840 / nRF5340 / nRF54 | Thread、BLE |
| Silicon Labs | EFR32MG24 / MG26 | Thread、BLE |
| Texas Instruments | CC13xx / CC26xx | Thread、BLE |
| NXP | K32W / RW61x | Thread、Wi-Fi、BLE |
| Realtek / Telink / Bouffalo / Infineon など | 各種 | 各種 |
選定の観点:
- Matter の公式サポートがあるか(
src/platform/にあるか、ベンダー SDK があるか)- Factory Data のツールが提供されているか(12 章)
- セキュアエレメント/セキュアストレージの有無
- 供給の安定性と長期供給の保証
- 認証実績(そのチップで認証を通した製品があるか)
最後の項目は軽視されがちだが極めて重要である。 前例のないチップで認証に挑むのはリスクが高い。
2. Factory Data
出荷時に個体ごとに書き込むデータ(12 章の再掲と実装)。
| 項目 | 個体ごと |
|---|---|
| DAC 証明書 + 秘密鍵 | ○ |
| PAI 証明書、CD | ×(製品共通) |
| Discriminator | ○ |
| SPAKE2+ の salt / iterations / 検証子 | ○ |
| VID / PID | × |
| シリアル番号、UniqueID | ○ |
| Rotating Device ID の鍵 | ○ |
| 製造日、ハードウェアバージョン | 一部 ○ |
実装の形
方式 A: 専用のフラッシュパーティション
├── 起動時に読み込む
└── ベンダーのツールで書き込む(esp-matter の mfg_tool など)
方式 B: セキュアエレメント
├── DAC 秘密鍵は SE の中。署名は SE に依頼する
└── 証明書は SE 内または通常フラッシュ
方式 C: 開発時のみ — ビルドに埋め込む
└── 製品では使ってはいけない// SDK は DeviceAttestationCredentialsProvider を差し替えられる
class MyDacProvider : public Credentials::DeviceAttestationCredentialsProvider {
CHIP_ERROR SignWithDeviceAttestationKey(const ByteSpan & message,
MutableByteSpan & out) override {
return SecureElementSign(message, out); // ← SE に署名を依頼
}
// GetDeviceAttestationCert, GetProductAttestationIntermediateCert, ...
};秘密鍵をメモリに読み出さずに署名できることが、SE の価値である. 「鍵をフラッシュから読んで、RAM 上で署名する」実装では、 RAM ダンプで鍵が漏れうる。
セキュアエレメントを使うなら、この Provider の差し替えが実装作業になる。
量産ラインでの書き込み
| 課題 | 検討事項 |
|---|---|
| 書き込み時間 | 1 個あたり数秒 × 生産数 |
| 並列化 | 複数台同時に書き込めるか |
| トレーサビリティ | シリアル ↔ 証明書の対応記録 |
| 不良品の扱い | 発行済み証明書の失効 |
| 委託製造 | EMS に何を渡すか。鍵を渡さない方式を選べるか |
| セキュリティ | 書き込み装置とネットワークの保護 |
ここは製造技術部門との協業になる. ソフト屋だけで決められない。開発の早い段階で製造側と握ること。
3. 省電力 — ICD(Intermittently Connected Device)
電池機器は常時受信できない(04 章の SED)。 これを Matter のアプリケーション層で扱う仕組みが ICD である。
SIT と LIT
| 種別 | Idle 時間 | 用途 |
|---|---|---|
| SIT(Short Idle Time) | 15 秒以下 | 下り方向の即応性が要る機器(ドアロックなど) |
| LIT(Long Idle Time) | 15 秒超(数分〜時間) | センサーなど、上り方向が主の機器 |
ICD Management クラスタ (0x0046)
| 属性 | 内容 |
|---|---|
IdleModeDuration | アイドル状態の長さ |
ActiveModeDuration | 起きている時間 |
ActiveModeThreshold | 通信後、これだけは起きている |
RegisteredClients | Check-In を送る相手 |
ICDCounter | Check-In のカウンタ |
UserActiveModeTriggerHint | ユーザーが機器を起こす方法(ボタンを押す等) |
Check-In プロトコル(LIT 向け)
LIT 機器は普段完全に寝ている
↓
定期的に起きて、登録されたクライアントに Check-In メッセージを送る
↓
クライアントが「用がある」と応答すれば、機器はしばらく起きている
↓
用がなければまた寝るこれで「数時間に 1 回しか起きない機器」でも制御できる. ただし応答は当然遅い。
UserActiveModeTriggerHintで「ボタンを押すと起きます」と伝え、 アプリが「機器のボタンを押してください」と案内するという UX になる。
電池寿命の見積り
| パラメータ | 典型値 |
|---|---|
| \(I_{\text{sleep}}\) | 1〜5 µA |
| \(I_{\text{active}}\) | 5〜10 mA(受信時) |
| \(t_{\text{active}}\) | 数 ms(Poll 1 回) |
| 電池容量(CR2032) | 約 220 mAh |
| 電池容量(単三 ×2) | 約 2000 mAh |
| 有効率 | 0.7〜0.8(自己放電、低温、電圧降下) |
試算例(CR2032、ポーリング 5 秒、Active 6 ms、5 mA、Sleep 2 µA):
平均電流 \(\approx 2\ \mu A + \frac{0.006}{5}\times 5\ \text{mA} = 2 + 6 = 8\ \mu A\)
寿命 \(\approx \frac{220\ \text{mAh} \times 0.75}{0.008\ \text{mA}} \approx 20{,}600\) 時間 \(\approx\) 2.3 年
ポーリングを 1 秒にすると平均電流は約 32 µA になり、寿命は約 7 か月まで縮む。 ポーリング間隔が寿命を支配していることが分かる。
この見積りは必ず実測で検証すること。 電流計での実測なしに製品を出してはいけない。 通信の頻度、再送、OTA、起動時の突入電流など、計算に入らない要素が多い。
省電力の実装ポイント
| 項目 | 内容 |
|---|---|
| サブスクリプションの MaxInterval | 長くする(10 章)。機器が主導権を持てる |
| SII / SAI の広告 | 正しく出す。出さないと再送で電池が減る(06 章) |
| 不要な通信を減らす | 定期報告の頻度を最小限に |
| 起動時間 | CASE Resumption で署名検証を省く(14 章) |
| 送信電力 | 必要以上に上げない |
| センサーの読み取り | 必要なときだけ |
4. TLV — Matter のシリアライズ形式
すべてのペイロードは TLV(Tag-Length-Value)でエンコードされる。
構造
┌──────────────┬─────────┬────────┬───────┐
│ Control Byte │ Tag │ Length │ Value │
└──────────────┴─────────┴────────┴───────┘
1 byte 0-8 byte 0-8 byte 可変Control Byte の上位 3 bit が Tag の形式、下位 5 bit が要素の型を示す。
Tag の形式
| 形式 | Tag のサイズ | 用途 |
|---|---|---|
| Anonymous | 0 | 配列の要素など |
| Context-specific | 1 byte | 構造体のフィールド(最も多い) |
| Common Profile (2/4 byte) | 2 or 4 | — |
| Implicit Profile | 2 or 4 | — |
| Fully Qualified (6/8 byte) | 6 or 8 | 完全修飾(クラスタ ID を含む) |
主な型
| 型 | 内容 |
|---|---|
| Signed Integer | 1, 2, 4, 8 byte |
| Unsigned Integer | 1, 2, 4, 8 byte |
| Boolean | True / False(値は Control Byte に埋め込まれる) |
| Float / Double | 4 / 8 byte |
| UTF-8 String | 長さ 1, 2, 4, 8 byte |
| Octet String | 同上 |
| Null | 値なし |
| Structure | 入れ子。End of Container で閉じる |
| Array | 同じ型の並び |
| List | 異なる型を含みうる並び |
| End of Container | 構造の終端 |
設計の要点:
- 整数は必要な最小サイズで符号化される。値 5 は 1 バイト。
- Boolean は値のバイトを持たない(Control Byte だけ)。
- 知らないタグは読み飛ばせるので、仕様の拡張に強い。
- JSON と違い、スキーマなしで構造がパースできる。
エンコード例
{ 0: true, 1: 42 } という構造体(Context tag 0 と 1):
15 ← Structure 開始(Anonymous tag)
28 00 ← Context tag 0, Boolean False … (True なら 29 00)
24 01 2A ← Context tag 1, UInt8, 値 0x2A = 42
18 ← End of Container実際のバイト列は仕様書の Appendix A に多数の例がある. 独自にパーサを書くなら、まずそこの例で検証すること。
通常は SDK の
TLVReader/TLVWriterを使うので、 手でエンコードすることはほぼない。 ただしログを読むときや、パケットキャプチャを解析するときに TLV が読めると圧倒的に速い(21 章)。
5. メモリ設計
RAM の主な消費要素
| 要素 | 目安 |
|---|---|
| Matter スタック本体 | 数十 KB |
| セッション(CASE) | 1 セッション数百 byte × 同時数 |
| Exchange | 数十 byte × 同時数 |
| サブスクリプション | パス数に比例 |
| 属性ストレージ(RAM 方式) | クラスタ数に比例 |
| イベントバッファ | 設計次第 |
| ネットワークスタック(Thread / Wi-Fi) | 数十 KB |
| TLS/暗号のワーク領域 | 数 KB |
削減の手立て
| 手段 | 効果 |
|---|---|
| 不要なクラスタを削る(ZAP) | 属性ストレージとコードサイズ |
SupportedFabrics を減らす | 不揮発とセッション |
| サブスクリプション上限を減らす | RAM |
| イベントバッファを小さく | RAM(ただし Critical の保持と両立させる) |
| 診断クラスタを削る | コードサイズ |
| ログレベルを下げる | コードサイズ(文字列が消える) |
リリースビルドでログ文字列を削るのは効果が大きい. デバッグログの文字列だけで数十 KB になることがある。 ただしフィールドでの障害解析ができなくなるので、 「エラーコードだけ残す」といった折衷が現実的である。
6. 起動時間
ユーザーは電源投入後すぐ使えることを期待する。
| フェーズ | 時間の目安 |
|---|---|
| ブート | 数十 ms〜 |
| Factory Data の読み込み | 数 ms |
| ネットワーク参加(Thread) | 数百 ms〜数秒 |
| ネットワーク参加(Wi-Fi) | 1〜数秒 |
| mDNS / SRP 登録 | 数百 ms |
| CASE 確立(初回) | 数百 ms〜1 秒 |
| CASE 確立(Resumption) | 数十 ms |
停電からの復帰が実際の評価ポイントになる. 家中の Matter 機器が一斉に起動し、一斉に mDNS を出し、 一斉に CASE を張ろうとする。 ネットワークが輻輳して、復帰に数分かかることがある。
対策として、起動時にランダムな遅延を入れる実装が有効である。
7. 落とし穴
| 落とし穴 | 対処 |
|---|---|
| Flash 容量の見積り不足(OTA) | 最初から 2 面分を確保 |
| 電池寿命の机上計算のみ | 必ず実測する |
| ポーリング間隔の設定ミス | 寿命を支配する。慎重に決める |
| TRNG を使っていない | 暗号が無意味になる |
| DAC 秘密鍵を平文保存 | SE か暗号化領域 |
| Factory Data の設計が遅い | 製造部門と早期に協議 |
| SII/SAI を広告しない | 再送で電池が減る |
| 起動時の一斉アクセス | ランダム遅延を入れる |
| セキュアブート未設定 | 改竄ファームが動く |
| デバッグポート開放のまま出荷 | 鍵が抜かれる |
8. まとめ
- SoC は Flash 2 MB 以上(OTA 込み)、RAM 128 KB 以上、暗号アクセラレータ、TRNG を目安に選ぶ。認証実績のあるチップが安全。
- Factory Data(DAC・鍵・Discriminator・SPAKE2+ 検証子)の書き込み方式は、 開発初期に製造部門と決める。
- 電池機器は ICD(SIT / LIT)。ポーリング間隔が寿命を支配する。 試算はするが、必ず実測で検証する。
- TLV が全ペイロードの形式。通常は SDK 任せだが、 読めるとデバッグが劇的に速くなる。
- メモリは Fabric 数・サブスクリプション数・クラスタ数に比例する。 リリースビルドではログ文字列の削減が効く。
- 停電復帰の一斉起動を考慮し、ランダム遅延を入れる。