Matter でオレオレ Remo nano Dash ボタンを作ろう!

ブログ祭5日目

ファームウェアエンジニアの中林 (id:tomo-wait-for-it-yuki) です。 これは第2回 Nature Engineering Blog 祭5日目のエントリです。 4日目はバックエンドエンジニアの桒山さんのMatter における TLV エンコーディングでした。

みなさん Matter 楽しんでますか? 興味はあるけど手軽に買えるプロダクトがない、そう思っているあなた! なんと3,980円で遊べる Nature Remo nano という Matter デバイスが発売されているのをご存知でしょうか? これは Matter を完全に理解するために何個か買って遊び倒すしかないですね!

Remo nano では Remo で赤外線操作可能な家電を、Matter コントローラーから操作することができます。 そうなると、自分専用の Matter コントローラーを作って便利に使いたい!となりますよね!? 本エントリではボタンを押したら、Matter 通信を介して Remo nano に特定の赤外線信号を出力させる Remo nano Dash ボタンの作り方を紹介します。

Remo nano Dash ボタン!

消灯が遅いのは照明の仕様です。

サポートしているプラットフォームは以下の通りです。

  • ATOM Lite
  • M5Stamp C3 Mate
  • ESP32-DevKitC
  • ESP32C3-DevKitM
  • ESP32S3-DevKitC

エントリ内では M5Stamp C3 Mate をターゲットデバイスとして解説します*1

Matter の概要や Nature Remo nano 開発の裏話が聞けるかもしれないイベント『Nature Matter Kaigi』も開催します。 こちらも奮ってご参加ください!

nature.connpass.com

システム概要

まず作るものの概要を把握しておきましょう。

Matter デバイスとして Remo nano は Matter コントローラーからリクエストを受けて、赤外線信号を出力します。 この Matter コントローラーは Amazon AlexaApple Home Pod のようなスマートスピーカーであることが多いでしょう。

nano 動作の仕組み

ただ、「特定操作だけをより簡易なインターフェースで実行できるようにしたい!」と考えるのは至って自然な流れでしょう。 そこで、通常はスマートスピーカーが行っている Matter コントローラーとしての機能を自作してみましょう。 今回は手軽に作れるように M5Stamp C3 Mate *2 を使うことにしました。 汎用的な Matter コントローラーのように高機能にすることはできませんが、特定操作を簡易に実行するには十分です。

オレオレ Matter Dash ボタンを作る!

最終的に M5Stamp C3 Mate のボタンを押すごとに、照明のオン / オフがトグルするものを作ります。 このシステムを構築してうれしいポイントは、M5Stamp C3 Mate で作ったオレオレ Matter コントローラーが Remo nano 以外と組み合わせても (せいぜい少しの修正で) 動作することです。 今回 Remo nano と組み合わて使う手順を紹介しますが、OnOff クラスターを搭載している Matter デバイスであれば、せいぜいハードコーディングしている Endpoint ID を修正するだけで動かすことができるでしょう。 OnOff クラスターや Endpoint ID についてはのちほど説明しますので、現時点では Remo nano 専用システムを構築しなくて済む、という共通規格の利点が活かせる、ということが重要です。

ビルド方法 & 使い方

ビルドしたらすぐに動かせるものを用意してありますので、まずビルド方法と使い方を紹介します。 コードや Matter については追って解説します。

ビルドを確認している環境は Ubuntu 20.04 です。

基本的な手順は Espressif の Matter SDK 用ドキュメントをベースとしています。 MacOS / Windows でも下記ドキュメント通り環境構築をすればビルドできるはずです。 Windows は WSL でビルドしてください (Matter SDK のビルドサポートが全体的にそういう感じです) 。

docs.espressif.com

以降、適当な作業ディレクトリで作業することを想定しています。

ESP-IDF のインストール

ESP-IDF v4.4.4 でビルドできることを確認しています。

git clone --recursive https://github.com/espressif/esp-idf.git -b v4.4.4
cd esp-idf
./install.sh
source export.sh
cd ..

esp-matter & connectedhomeip

Linux と ESP32 の組み合わせで Matter をビルドできる環境を構築します。

git clone --depth 1 https://github.com/espressif/esp-matter.git -b release/v1.0
cd esp-matter
git submodule update --init --depth 1
cd ./connectedhomeip/connectedhomeip
./scripts/checkout_submodules.py --platform esp32 linux --shallow
cd ../..
./install.sh
source export.sh
cd ..

※いろいろダウンロードして10GBくらいストレージが消費されます。ご注意ください。

再度ビルド環境を整えるとき

ESP-IDF と esp-matter との環境変数を設定します。

# esp-idf
source esp-idf/export.sh
# esp-matter
source esp-matter/export.sh

remo-nano-dash-button ビルド & Flash

M5Stamp C3 Mate をターゲットにビルドします。 Matter SDK のビルドが非常に重いので、少し時間がかかります。 Nature 開発者コミュニティで雑談をして過ごしましょう。

git clone https://github.com/natureglobal/remo-nano-dash-button.git
cd remo-nano-dash-button
export ESP_MATTER_DEVICE_PATH=$(pwd)/device/m5stampc3
idf.py set-target esp32c3
idf.py build

サポートしているプラットフォームそれぞれでのビルド方法は次の通りです。

プラットフォーム 設定
ATOM Lite export ESP_MATTER_DEVICE_PATH=$(pwd)/device/atomlite
M5Stamp C3 Mate export ESP_MATTER_DEVICE_PATH=$(pwd)/device/m5stampc3 && idf.py set-target esp32c3
ESP32-DevKitC 何もせずそのまま idf.py build する
ESP32C3-DevKitM idf.py set-target esp32c3
ESP32S3-DevKitC idf.py set-target esp32s3

無事ビルドできたら、USB ケーブルを接続して Flash に書き込みましょう。

idf.py flash

無事 Flash への書き込みが完了したら idf.py monitor で M5Stamp C3 Mate を起動します。 初回だけ、シリアルコンソールから設定をします。

idf.py monitor

起動ログが流れた後、一度エンターキーを押すとプロンプト > が出力されます。 この状態でコマンドを入力していきます。

ESP-ROM:esp32c3-api1-20210207
Build:Feb  7 2021
rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:1
load:0x3fcd5810,len:0x16d0
load:0x403cc710,len:0x944
load:0x403ce710,len:0x2dfc
entry 0x403cc710
I (30) boot: ESP-IDF v4.4.4 2nd stage bootloader
I (30) boot: compile time 13:46:34
I (30) boot: chip revision: v0.3
I (34) boot.esp32c3: SPI Speed      : 80MHz
I (39) boot.esp32c3: SPI Mode       : DIO
I (43) boot.esp32c3: SPI Flash Size : 4MB
I (48) boot: Enabling RNG early entropy source...
I (54) boot: Partition Table:
I (57) boot: ## Label            Usage          Type ST Offset   Length
I (65) boot:  0 sec_cert         unknown          3f 06 0000d000 00003000
I (72) boot:  1 nvs              WiFi data        01 02 00010000 00006000
I (79) boot:  2 otadata          OTA data         01 00 00016000 00002000
I (87) boot:  3 phy_init         RF data          01 01 00018000 00001000
I (94) boot:  4 ota_0            OTA app          00 10 00020000 001e0000
I (102) boot:  5 ota_1            OTA app          00 11 00200000 001e0000
I (109) boot:  6 paa_cert         Unknown data     01 82 003e0000 00020000
I (117) boot: End of partition table

...

> 

Remo nano Dash ボタン使い方

まずは Nature Remo nano を普通にセットアップします。 続いて照明を1つ登録しておきましょう。 これを Remo nano Dash ボタンから ON / OFF します。

実際に照明の ON / OFF 操作ができるものを登録すると良いでしょう。 このあとの感動が倍増しますよ。

Remo アプリの設定画面から、対象の Remo nano を選んで、「Matterを介して操作できる家電」が次のようになっていれば OK です!

Matter を介して照明を操作できる状態に

これで Remo nano 側の準備は完了です。 おっと!このあと Matter のセットアップをするために Remo nano の裏に印字されている11桁の番号 (MPC) が必要なのでメモっておきましょう。

お次は Remo nano Dash ボタン側です。 ESP32 を起動し、コンソールから WiFi の設定をします。 Remo nano と同じネットワークに接続するのをお忘れなきよう!

> matter esp wifi connect <ssid> <password>

さあみなさん、心の準備は良いですか? とうとう Matter として Remo nano をセットアップ (コミッショニング) しますよ!

Remo アプリの設定画面から対象の Remo nano を選んで少し下の方に + Matter連携をする というボタンがあります。 これをタップして、次の画面でペアリングを始めるボタンをタップすると、 Remo nano がコミッショニング可能状態になります。 タップして、 Remo nano のステータスランプが黄色点滅していることを確認して下さい。

Matter連携をする

Remo nano 裏に貼られている11桁の MPC を入力します。 onnetwork の次の 1 はノード ID で、コミッショナーがデバイスを識別するための番号です。 1 にしてコミッショニングしてください。

> matter esp pairing-code onnetwork 1 <Remo nano 裏にある11桁の MPC>

次のようなログが流れれば無事コミッショニング完了です!

> matter esp pairing-code onnetwork 1 02511727867
I (80503) pairing_code: pairing_code_dispatch
I (80503) pairing_code: pairing-code onnetwork
Done
> I (81273) pairing_command: Discovered Device: FDEE:AD6B:9A31:4785:424C:CAFF:FEBD:C5C:5540
I (81273) chip[EM]: <<< [E:2528i M:155865666] (U) Msg TX to 0:0000000000000000 [0000] --- Type 0000:20 (SecureChannel:PBKDFParamRequest)
I (81293) chip[IN]: (U) Sending msg 155865666 to IP address 'UDP:[FDEE:AD6B:9A31:4785:424C:CAFF:FEBD:C5C]:5540'
I (81303) chip[CTL]: Setting attestation nonce to random value
I (81313) chip[CTL]: Setting CSR nonce to random value
I (81313) chip[CTL]: Commission called for node ID 0x0000000000000001

// 中略。たくさんログが出ます

E (105073) chip[EM]: OnMessageReceived failed, err = 70
I (105343) chip[EM]: >>> [E:2539i M:91064013 (Ack:164732082)] (S) Msg RX from 1:0000000000000001 [4E7E] --- Type 0001:09 (IM:InvokeCommandResponse)
I (105353) chip[DMG]: Received Command Response Data, Endpoint=0 Cluster=0x0000_0030 Command=0x0000_0005
I (105363) chip[CTL]: Received CommissioningComplete response, errorCode=0
I (105373) chip[CTL]: Successfully finished commissioning step 'SendComplete'
I (105373) chip[CTL]: Commissioning stage next step: 'SendComplete' -> 'Cleanup'
I (105383) chip[CTL]: Performing next commissioning step 'Cleanup'
I (105393) chip[SC]: SecureSession[0x3fff4ae0]: Moving from state 'kActive' --> 'kPendingEviction'
I (105403) chip[CTL]: Successfully finished commissioning step 'Cleanup'
I (105413) pairing_command: Device commissioning completed with success - getting OperationalDeviceProxy
I (105423) chip[CTL]: Found an existing secure session to [1:0000000000000001]!
I (105423) pairing_command: OnDeviceConnectedFn
I (105423) pairing_command:  ----- AutoCommissioner -- Commissionee vendorId=0x138A productId=0x1392

おめでとうございます!

さて、この状態で M5Stamp C3 Mate のボタンを押すと…エントリ冒頭のように照明の ON / OFF ができるようになります🎉 コンソール上のログは次のようになります。 なぜか最後に chip[EM]: OnMessageReceived failed, err = 70 と出力されますが、こちらは気にしなくて大丈夫です。

I (1224987) app_main: Toggle button pressed
I (1224997) chip[CTL]: Found an existing secure session to [1:0000000000000001]!
I (1224997) chip[EM]: <<< [E:36855i M:144865575] (S) Msg TX to 1:0000000000000001 [583A] --- Type 0001:08 (IM:InvokeCommandRequest)
I (1225007) chip[IN]: (S) Sending msg 144865575 on secure session with LSID: 56036
I (1225377) chip[IN]: (S) Sending msg 144865575 on secure session with LSID: 56036
I (1225387) chip[EM]: >>> [E:36855i M:240689493 (Ack:144865575)] (S) Msg RX from 1:0000000000000001 [583A] --- Type 0001:09 (IM:InvokeCommandResponse)
I (1225397) chip[DMG]: Received Command Response Status for Endpoint=2 Cluster=0x0000_0006 Command=0x0000_0002 Status=0x0
I (1225407) esp_matter_client: Send command success
I (1225417) chip[EM]: <<< [E:36855i M:144865576 (Ack:240689493)] (S) Msg TX to 1:0000000000000001 [583A] --- Type 0000:10 (SecureChannel:StandaloneAck)
I (1225427) chip[IN]: (S) Sending msg 144865576 on secure session with LSID: 56036
I (1225457) chip[EM]: >>> [E:36855i M:240689494 (Ack:144865575)] (S) Msg RX from 1:0000000000000001 [583A] --- Type 0000:10 (SecureChannel:StandaloneAck)
E (1225467) chip[EM]: OnMessageReceived failed, err = 70

次回以降は、 Remo nano をリセットしたり、 M5Stamp C3 Mate のデータを消さない限りは、デバイスを再起動した後もコミッショニングせずに使えます。

もしコミッショニングがうまくいかなかった場合は、+ Matter連携をする からやり直してみてください。 Matter コミッショニング可能状態は最大で15分しか持続しない (これも Matter の spec です) ため、時間切れになった可能性があります。

それでもうまく行かない場合は、Nature 開発者コミュニティにてご質問ください。

discord.gg

もし、コミッショニングを最初からやり直したい場合は、M5Stamp C3 Mate のボタンを5秒長押しして下さい。 Matter のコミッショニング情報などがリセットされます。

pairing-code onnetwork の補足

onnetwork でのコミッショニングは、ネットワーク内で DNS-SD を使ってコミッショニング可能なデバイスを探して、コミッショニングします。 そのため、コミッショナーとデバイスとは UDP マルチキャスト通信が届く同じネットワーク内に居る必要があります。

matter esp pairing-code onnetwork 1 <Remo nano 裏にある11桁の MPC>

onnetwork 以外だと BLE 経由で Wi-Fi の設定をしてからコミッショニングする BLE コミッショニングなどがあります。

ノード ID はコミッショナーからデバイスを一意に識別できれば良い (重複していなければ良い) ので、任意の数値を割り振ることができますが、今回のコードでは、 M5Stamp C3 Mate のボタンを押したときにノード ID が 1 のデバイスに対して、コマンドリクエストを送るようにハードコーディングしているため、 1 にしています。

耳寄りな話

今回作った Remo nano Dash ボタンは OnOff クラスターであればトグル操作することができます。 Remo アプリでは、照明とテレビがデフォルトで OnOff クラスターとして動作するようになっています。

それでは他の赤外線操作家電を Remo nano Dash ボタンから操作することはできないのでしょうか? 実は良い方法があります! 学習リモコン機能を使いましょう。

Remo アプリの「新しい家電を追加」から「その他」を選びます (エアコン、テレビ、照明以外ならなんでも良いですが) 。 ここで Remo nano Dash ボタンから操作したい赤外線信号を Remo nano に向かって送信します。 Remo nano が赤外線信号を受信したらアイコンを「ON」で登録します (デフォルトで ON になっています) 。 続いて、登録した家電に、同様に OFF 用の赤外線信号を、アイコン「OFF」で登録します。

これで、準備完了です。 実際に ON / OFF するかは関係ありません。 とにかく Remo nano Dash ボタンを押すごとに、こういう状態になって欲しい、という信号を学習させてください。

コンソール操作の補足

ボタンで ON / OFF のトグルをできるようにしましたが、コンソールからその他の操作を試すこともできます。 コマンドを途中まで入力するとヘルプが出力されます。

> matter esp
I (832207) esp_matter_console:  diagnostics: Diagnostic commands. Usage matter esp diagnostics <diagnostic_command>.
I (832207) esp_matter_console:  wifi: Wi-Fi commands. Usage: matter esp wifi <wifi_command>.
I (832217) esp_matter_console:  help: Print help
I (832237) esp_matter_console:  controller: Controller commands. Usage: matter esp controller [command_name]
I (832247) esp_matter_console:  pairing-code: Pairing with code commands. Usage: matter esp pairing-code [command_name]
Done

Matter と関係あるのは controller コマンドです。 read-attrwrite-attrアトリビュートの読み書きをしたり、invoke-cmd でコマンドリクエストを送ることができます。

> matter esp controller
I (944387) esp_matter_console:  help: print this page
I (944387) esp_matter_console:  pairing: Pairing a node.
        Usage: controller pairing onnetwork [nodeid] [pincode] Or
        controller pairing ble-wifi [nodeid] [pincode] [discriminator] [ssid] [password] Or
        controller pairing ble-thread [nodeid] [pincode] [discriminator] [dataset]
I (944417) esp_matter_console:  group-settings: Managing the groups and keysets of the controller.
        Usage: controller group-settings <sub-commands>
I (944427) esp_matter_console:  invoke-cmd: Send command to the nodes.
        Usage: controller invoke-cmd [node-id|group-id] [endpoint-id] [cluster-id] [command-id] [payload]
        Notes: group-id should start with prefix '0xFFFFFFFFFFFF', endpoint-id will be ignored if the fist parameter is group-id.
I (944457) esp_matter_console:  read-attr: Read attributes of the nodes.
        Usage: controller read-attr [node-id] [endpoint-id] [cluster-id] [attr-id]
I (944467) esp_matter_console:  write-attr: Write attributes of the nodes.
        Usage: controller write-attr [node-id|group-id] [endpoint-id] [cluster-id] [attr-id] [attr-value]
I (944477) esp_matter_console:  read-event: Read events of the nodes.
        Usage: controller read-event [node-id] [endpoint-id] [cluster-id] [event-id]
I (944497) esp_matter_console:  subs-attr: Subscribe attributes of the nodes.
        Usage: controller subs-attr [node-id] [endpoint-id] [cluster-id] [attr-id] [min-interval] [max-interval]
I (944517) esp_matter_console:  subs-event: Subscribe events of the nodes.
        Usage: controller subs-attr [node-id] [endpoint-id] [cluster-id] [event-id] [min-interval] [max-interval]
I (944527) esp_matter_console:  shutdown-subs: Shutdown subscription.
        Usage: controller shutdown-subs [node-id] [subscription-id]

ぶっちゃけ CLI ツールとして使うなら、田井さんの書いてくれた chip-tool を使う方が100倍便利です。

engineering.nature.global

が、ESP32 でプログラムに落とし込みたいときは、各コマンドで何をやっているのか実装を確認すると良いでしょう。

コードの解説

それではここから楽しいコードの解説、と参りましょう! とは言っても、今回自分で書いているコードはかなり少ないです。

Matter SDK (connectedhomeip) 自体が、(素直に作れば) 初期化とコールバックを埋めれば Matter 対応デバイス作れるようなものになっています。 コントローラー部分は Espressif の Matter SDK (esp-matter) である程度実装されています。今回作ったのは

  • MPC (Manual Pairing Code) で Matter セットアップ (コミッショニング) する
  • ボタンを押されたら特定の Matter リクエストを出す

の2つくらいです。

ディレクトリ構成

ビルド前の remo-nano-dash-button ディレクトリは次のようになっています。 それほどファイルもディレクトリも多くはないですね?

$ tree -L 1
.
├── CMakeLists.txt
├── components
├── device
├── main
├── paa_cert
├── partitions.csv
├── README.md
└── sdkconfig.defaults

main

アプリケーションコード部分です。自分でコードを書いているのはほぼここだけです。

main/pairing_code.cpp

Remo nano の裏側についている11桁のコード (MPC) でコミッショニングできるコマンドを追加しています。

コマンドの追加は esp-matter と connectedhomeip 側で shell 実装のための API があるので、それに従ってやるだけです。 ヘルプ表示用のハンドラと処理用のハンドラを登録してあげる素直な作りですね。

esp_err_t pairing_code_register_commands()
{
    // Subcommands for root command: `pairing-code <subcommand>`
    const static command_t pairing_code_sub_commands[] = {
        {
            .name = "help",
            .description = "print this page",
            .handler = pairing_code_help_handler,
        },
        {
            .name = "onnetwork",
            .description = "Pairing a node.\n"
                           "\tUsage: pairing-code onnetwork [nodeid] [MPC]",
            .handler = pairing_code_onnetwork_handler,
        },
    };

    const static command_t pairing_code_command = {
        .name = "pairing-code",
        .description = "Pairing with code commands. Usage: matter esp pairing-code [command_name]",
        .handler = pairing_code_dispatch,
    };
    // Register the pairing-code commands
    pairing_code_console.register_commands(pairing_code_sub_commands, sizeof(pairing_code_sub_commands) / sizeof(command_t));
    return add_commands(&pairing_code_command, 1);
}

ポイントは、処理用のハンドラである pairing_code_onnetwork_handler() の中身ですね。 argv[1] は自分で入力した11桁の MPC の文字列です。 これを chip::ManualSetupPayloadParser に与えると、あら不思議! setUpPINCode なる PIN コードが得られます。

static esp_err_t pairing_code_onnetwork_handler(int argc, char **argv)
{
    ESP_LOGI(TAG, "pairing-code onnetwork");
    if (argc != 2) {
        return ESP_ERR_INVALID_ARG;
    }

    const uint64_t nodeId = string_to_uint64(argv[0]);
    chip::SetupPayload payload;
    chip::ManualSetupPayloadParser(argv[1]).populatePayload(payload);
    const uint32_t pincode = payload.setUpPINCode;

    return controller::pairing_on_network(nodeId, pincode);
}

Matter のコミッショニングでは各デバイス固有の PIN コード*3が必要です。 この PIN コードは MPC を規定の方法 (Matter spec に記載されています) でデコードすると得ることができます。 逆に言うと、MPC しかない状態では、上述のような MPC から PIN コードを取り出すプログラムを書くか、chip-tool の専用コマンドを使わないと PIN コードがわかりません。

esp-matter では PIN コードを直接入力するコミッショニングコマンドしか提供しておらず、 Remo nano Dash ボタンを作るにあたって、各環境で MPC をデコードして下さい、ではあまりに不便でしたので、 pairing-code コマンドを用意するに至りました。

ちなみに MPC ではなく QR コードがついている Matter 製品もありますが、QR コードは MPC より情報量が多いだけで、PIN コードへデコードしてコミッショニングする点は同じです。

app_main.cpp

プログラムの main 関数です。 esp-matter は良くも悪くもイージーに使えるようになっています (その代わりシンプルさを失っているので融通が効きません) 。 NVS を初期化したら、esp_matter::start() を呼び出すと、 Matter プロトコルスタックが動き始めます。 esp_matter::console から始まる行はコンソールのコマンドを登録しているだけなので気にしなくて大丈夫です。

extern "C" void app_main()
{
    esp_err_t err = ESP_OK;
    /* Initialize the ESP NVS layer */
    nvs_flash_init();
    app_driver_button_init();

    /* Matter start */
    err = esp_matter::start(app_event_cb);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Matter start failed: %d", err);
    }

    esp_matter::console::diagnostics_register_commands();
    esp_matter::console::wifi_register_commands();
    esp_matter::console::init();

    esp_matter::lock::chip_stack_lock(portMAX_DELAY);
    esp_matter::commissioner::init(5580);
    esp_matter::lock::chip_stack_unlock();

    esp_matter::console::controller_register_commands();
    esp_matter::console::pairing_code_register_commands();
}

コミッショナーとしての初期化は次の3行でやっています。 Matter プロトコルスタックは、単一タスクのイベントループ内で逐次的にプロトコルの処理をしています。 今、esp_matter::commissioner::init() を呼び出しているのは main task のコンテキストであって、Matter プロトコルスタックのコンテキストではないため、Matter プロトコルスタックのイベントループを一時停止させるためにロックを取得しています。

    esp_matter::lock::chip_stack_lock(portMAX_DELAY);
    esp_matter::commissioner::init(5580);
    esp_matter::lock::chip_stack_unlock();

続いて、ボタンドライバーでボタンが押されたイベントを検出したら、Remo nano にコマンドリクエストを送る部分です。 ボタンドライバーは device hal を使って初期化し、ボタンが押されたら app_driver_button_toggle_cb() がコールバックで呼ばれるようにしています。

static app_driver_handle_t app_driver_button_init()
{
    /* Initialize button */
    button_config_t config = button_driver_get_config();
    button_handle_t handle = iot_button_create(&config);
    iot_button_register_cb(handle, BUTTON_PRESS_DOWN, app_driver_button_toggle_cb, NULL);
    return (app_driver_handle_t)handle;
}

それでは肝心の app_driver_button_toggle_cb() の中身を見てみましょう。 コードにインラインでコメントします。

static void app_driver_button_toggle_cb(void*, void*)
{
    using namespace chip::app::Clusters;
    ESP_LOGI(TAG, "Toggle button pressed");

    // このコールバックは esp-timer コールバックのコンテキストで呼ばれるので、
    // Matter プロトコルスタックのロックを取得します。
    // C++ のコンストラクタ / デストラクタを使ったロックガード
    // `chip::DeviceLayer::StackLock` があるので便利に使います。
    chip::DeviceLayer::StackLock lock;

    // OnOff クラスターの Toggle コマンドを作ります
    controller::command_data_t *command_data = (controller::command_data_t *)esp_matter_mem_calloc(1, sizeof(controller::command_data_t));
    command_data->cluster_id = OnOff::Id;
    command_data->command_id = OnOff::Commands::Toggle::Id;
    command_data->command_data_count = 0;

    // Node ID = 1, Endpoint ID = 2 に向けてコマンドリクエストを送ります
    controller::cluster_command *cmd = chip::Platform::New<controller::cluster_command>(1, 2, command_data);
    cmd->send_command();
}

OnOff クラスターはその名の通り、On / Off 状態があるクラスターです。 Toggle コマンドは、現在の状態が On なら Off に、Off なら On に、状態を変えるコマンドです。 照明であれば、Toggle コマンドを実行するたびに、消灯 -> 点灯、を繰り返すことになります。

sdkconfig.defaults

おなじみ ESP-IDF のプロジェクト設定ファイルです。 コントローラーとしての設定部分は次の通りです。

# Enable Controller and commissioner
CONFIG_ESP_MATTER_CONTROLLER_ENABLE=y
CONFIG_ESP_MATTER_COMMISSIONER_ENABLE=y
CONFIG_SPIFFS_ATTESTATION_TRUST_STORE=y

コントローラー / コミッショナーの機能を有効にして、SPI Flash ファイルシステムにデバイス認証用の PAA を格納するようにしています。 CONFIG_SPIFFS_ATTESTATION_TRUST_STORE=y にしておくと main/CMakeLists.txtpaa_cert ディレクトリ下にある PAA を SPI Flash に格納してくれるようになっています。

spiffs_create_partition_image(paa_cert ${CMAKE_SOURCE_DIR}/paa_cert FLASH_IN_PROJECT)

公開しているリポジトリpaa_cert ディレクトリにはテスト用の PAA と Remo nano の認証に必要な PAA とを格納してあります。

Matter のデバイス認証については、3日目の井田さんのエントリに詳しく書いてありますので、そちらもご参照ください。

engineering.nature.global

device/m5stampc3

esp-matter device hal の M5Stamp C3 Mate 実装です。 特に大したことはしていなくて、M5Stamp C3 Mate だとボタンが GPIO03 に割り当てられているので、その設定をしています。

#define LED_GPIO_PIN GPIO_NUM_2
#define LED_CHANNEL 0 /* LEDC_CHANNEL_0 */
#define BUTTON_GPIO_PIN GPIO_NUM_3

esp-matter と connectedhomeip

余談ですが、esp-matterconnectedhomeip とでは全く API が異なり使用感が違います。 esp-matter の方はほぼ namespace 付きの C 言語です。 一方 connectedhomeip は普通に C++API になっています。

esp-matterconnectedhomeip のラッパーなのですが、本来 connectedhomeip でオーバーライド可能な API のうち、かなりの部分を esp-matter 内で実装を固定して、アプリケーション側でオーバーライドできないようにしています。 その結果、とっつきやすい感じにはなっているのですが、ちょっと esp-matterで用意されたレールから外れようとすると、esp-matter` ごと修正することになりがちです…。なんてこったい。

C++ っぽく使いたいという方は esp-matter で感覚を掴んでから connectedhomeip を直接使うことをオススメします。 自分で書かなければいけないコードは増えてしまいますが、自由度は大きいです。 Matter として動作するのに必要な ESP32 向けのコードは connectedhomeip 側のプラットフォーム依存部分として提供されているので、そのレベルで苦労することはありません。

今後

esp-matter の特にコントローラー部分はまだまだ実装中の印象が強いです。 コントール可能なクラスターも限られています。 Remo nano で使えるのに esp-matter で未実装なクラスターとしては、エアコン (Thermostat) が挙げられます。

あと、本当は M5Stack CoreS3 あたりを使ってグラフィカルなオレオレコントローラーを作りたかったのですが、時間が足りず、Dash ボタンになりました。 ぜひ今後はより複雑なオレオレコントローラーを作っていきたいですね。

明日はバックエンドエンジニアの北原さんです。お楽しみに!

Nature エンジニアコミュニティ

エンジニアコミュニティはじめました。Matter を使った Nature Remo nano の活用方法から Nature Remo could API の疑問など、Nature のエンジニアがお答えします! Discord への参加はこちらから。

discord.gg

Nature Remo nano 好評発売中!

Matter 対応の Nature Remo nano が定価3,980円で発売中!

*1:ATOM Lite サポートは冒頭動画の見た目を良くするために後から作ったので…

*2:スイッチサイエンス: https://akizukidenshi.com/catalog/g/gM-11819/https://www.switch-science.com/products/7474

*3:規格上は passcode ですが、実装との整合性のため、本エントリ内では PIN コードと記載します