nRF Connect SDKは、選択されたMatterデバイスタイプの機能を実装する複数のMatterサンプルを提供しています。ただし、実用上の理由から、利用可能なすべてのMatterデバイスタイプをサンプルとして提示することはできません。
この演習では、nRF Connect SDKのMatterテンプレートサンプルを基盤として使用し、nRF Connect SDKでサンプルとして提供されていないデバイスタイプを使用する独自のMatterデバイスを作成します。目標は、Matter On/Offプラグデバイスを作成することです。
Matterテンプレートサンプルは、エンドポイント0に関連する機能のみを実装しており、特定のアプリケーションユースケースに固有の実装は含まれていません。
0. コース用のGitHubリポジトリを取得してください。
このコース用のGitHubリポジトリをクローンしてください。
一部のオペレーティングシステム(Windows)では、アプリケーションパスが長すぎるとビルドシステムが失敗する可能性があるため、パスが長い場所にリポジトリを保存しないでください。
1. この演習の基本コードを取得してください。
VS CodeのnRF Connect拡張機能のWELCOMEパネルで、既存のアプリケーションを開くを選択し、前の手順でクローンしたコース用のGitHubリポジトリに移動して、l3/l3_e1 ディレクトリを開いてください。アプリケーションはAPPLICATIONSパネルに表示されます。
このアプリケーションは、Matterテンプレートサンプルのコピーであり、この演習のコードスニペットを入力する場所の指示が追加されています。
1.2 srcディレクトリに存在するファイルの内容を確認してください。レッスン2で説明したように、MatterサンプルはnRF Connect Matter APIを使用します。具体的には:
app_task.cpp ファイルには、Matterサーバーとボードコンポーネントを初期化し、Matterイベントのデフォルトハンドラーを設定し、メインループを開始するメソッドが含まれています。この基本実装により、デバイスをMatterファブリックにコミッショニングし、次の要素を使用してNordic DKと対話できます:template.zap default_zapディレクトリにあるファイルには、エンドポイント0(ルートエンドポイント)に必要なクラスターの必須構成のみが含まれています。zap-generated default_zapディレクトリにあるディレクトリには、template.zapファイルに基づいて生成されたソースファイルとヘッダーファイルが含まれています。これらのファイルはクラスターを構成し、それらのデフォルトハンドラーを提供します。
2. ZAPツールを使用してOn/Offプラグエンドポイントを追加する
MatterはZAPツールとPythonスクリプトを使用して、.zapファイルからコードを生成します。正しいZAPツールバージョンを設定してスクリプトを実行することは複雑になる可能性があります。これを簡素化するために、nRF Connect SDKは、これらの操作を簡単な呼び出しにラップするカスタムWestコマンドを提供しています。詳細については、ZAPツールwestコマンドのドキュメントを参照してください。
2.1 ターミナルウィンドウでnRF Connectツールチェーンを起動してください。
Visual Studio CodeのAPPLICATIONSウィンドウで、アプリケーションl3_e1を右クリックし、「Start New Terminal」を選択してください。

使用しているnRF Connect SDKバージョンを選択してください:

対応するツールチェーンバージョンを選択してください:

サンプルディレクトリでターミナルが開きます。
2.2 次のコマンドを使用してZAPツールを実行してください:
west zap-guiTerminal commandこのコマンドは、適切なZAPツールバージョンを自動的にダウンロードして実行します。インストールが完了すると、ZAPツールのMatter Cluster Configuratorウィンドウが表示されます。

ウィンドウの左側のサイドパネルにエンドポイントのリストが表示されます。現在、インデックス0のエンドポイントが1つだけあり、これがルートエンドポイントです。
2.3 ZAPツールで、+ ADD ENDPOINTをクリックしてください。
2.4 Create New Endpointメニューで、on/offプラグデバイスタイプを表す新しいエンドポイントを作成してください:

CREATEボタンを選択して選択を確定すると、新しいエンドポイントがサイドパネルに表示されます。
2.5 Matter仕様で要求されるすべてのクラスターが有効になっていることを確認しましょう。
デフォルトでは、Matterデバイスタイプを選択すると、必要なすべてのクラスター、属性、コマンドが有効になります。
すべての必須要素が有効になっており、不要なオプション要素が無効になっていることを確認するために、常にMatter仕様に対して構成を検証してください。ツールは通常これを自動的に処理しますが、古くなる可能性があります。
ウィンドウの最初の行Generalをクリックして展開してください。

構成を確認することもできます。たとえば、On/Offクラスターの詳細を確認するには、その横にある歯車アイコンをクリックしてください。デフォルト構成ではすべての属性が有効になっていますが、実際にはすべての属性が必須というわけではありません。Featuresタブに移動すると、Lighting機能が有効になっていますが、私たちのユースケースは照明に関連していません。

ツールはlighting機能が必須であると主張していますが、Matter仕様では、OffOnly機能が無効になっている場合はオプションであり、そうでない場合はサポートされないと記載されています。この機能を無効にして、選択を確定してください。アプリケーションの警告は無視してください。

その結果、大部分の属性といくつかのコマンドが無効になります。

2.6 上部バーのFile->Saveをクリックして構成を保存し、アプリケーションを終了してください。
2.7 VS Codeターミナルで次のコマンドを実行して、.zapファイルに基づいてコードを生成してください:
west zap-generateTerminal commandこの時点で、新しいエンドポイントと必要なすべてのクラスターがアプリケーションに追加されました。
このコマンドを実行すると、ビルドログにアラームベルが表示されます。これは、ツールが誤って必須であると主張しているlighting機能を無効にしたために発生するため、無視できます。
3. アプリケーションコードでOn/Offプラグ機能を処理する
Matterデータモデルが更新され、Matterコントローラーによって適切に制御できるようになりましたが、アプリケーションはOn/Offプラグを制御する必要があるため、属性の変更の結果としていくつかのアクションを実行する必要があります。実際のデバイスでは、スイッチをオンまたはオフにしてコンセントへの電力を有効にすることができますが、サンプルアプリケーションでは、DKのLED1を使用してコンセントの状態をエミュレートできます。コンセントには、コンセントの状態を手動で強制できる物理ボタンもある可能性があり、DKのBUTTON 1を使用してこの機能をエミュレートできます。
3.1 OnOff状態属性の変更を処理してください。
データモデルでOnOff状態属性値が変更されたという情報を取得する必要があります。たとえば、コントローラーの操作の結果として変更された場合です。その後、LED1の状態をそれに応じて変更できます。
そのためには、MatterPostAttributeChangeCallbackメソッドを実装する必要があります。これは、属性の値が変更されたときにMatterデータモデルによって呼び出されるコールバックです。
変更されたすべてのコードは、src ディレクトリのapp_task.cppファイルに適用されます。
MatterPostAttributeChangeCallbackメソッドのドキュメントは、modules/lib/matter/src/app/util/generic-callbacks.hファイルで読むことができます。
3.1.1 他のusing namespace行の下に次の2行を追加して、より短いコード行を使用してクラスターのメソッドと値にアクセスできるようにしてください:
using namespace ::chip::app::Clusters;
using namespace ::chip::app::Clusters::OnOff;C++3.1.2 ファイルの下部に、MatterPostAttributeChangeCallbackメソッドが実装されています。
次の行を追加して、attributePath引数からクラスターIDと属性IDに関する情報を取得してください:
ClusterId clusterId = attributePath.mClusterId;
AttributeId attributeId = attributePath.mAttributeId;C++3.1.3 同じ関数で、変更された属性が関心のある属性であるかどうかを検証するチェックを追加してください。MatterPostAttributeChangeCallbackメソッドは任意の属性変更時に呼び出されるためです。OnOff属性の状態に応じて、DKのLED1の状態を変更してください。さらに、デバッグ用のログ行を追加しましょう。
次のコードスニペットを追加してください:
if (clusterId == OnOff::Id && attributeId == OnOff::Attributes::OnOff::Id) {
LOG_INF("Cluster OnOff: attribute OnOff set to %" PRIu8 "", *value);
Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).Set(*value);
}C++LED1を使用する必要がありますが、コードはNrf::DeviceLeds::LED2を参照していることに気付くかもしれません。これは、Nordic DKがシリーズによって異なるインデックスを使用するためです。一部は0から始まり、他は1から始まります。コード内のマクロは常に1から始まるインデックスが付けられているため、オフセットが発生する可能性があります。
3.2 手動状態変更に反応してLED状態を変更するボタン処理を追加してください。
変更されたすべてのコードは、app_task.cppファイルに適用されます。
3.2.1 ButtonEventHandler()関数で、コールバックが汎用的であり、ライブラリがすべてのボタンに対してそれを呼び出すため、コールバックがBUTTON1の変更に反応して呼び出されているかどうかを確認してください。ButtonEventHandler()関数は割り込みハンドラーから直接呼び出されるため、その中で長い操作を実行せず、たとえばPostTask()メソッドを使用してアプリケーションキューにスケジュールすることをお勧めします。
次のコードスニペットを追加してください:
if ((DK_BTN2_MSK & hasChanged) & state) {
Nrf::PostTask([] {
Nrf::GetBoard()
.GetLED(Nrf::DeviceLeds::LED2)
.Set(!Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).GetState());
/* STEP 3.3.3 - Update the attribute state using the Set() method */
});
}C++LEDと同様に、DKのBUTTON1はコード内で対応するBTN2マクロを使用します。
3.2.2 AppTask::Init()メソッドで呼び出されるNrf::GetBoard().Init()メソッドにカスタムハンドラーを渡してください。
アプリケーションで新しいボタンを処理するには、AppTask::Init()メソッドで呼び出されるNrf::GetBoard().Init(メソッドにカスタムボタンハンドラーを渡す必要があります。ハンドラーAPIのドキュメントについては、nrf/samples/matter/common/src/board/board.hファイルを参照してください。ハンドラーはbutton_handler_t型である必要があり、これはnrf/include/dk_buttons_and_leds.hファイルで宣言されています。
次のコードスニペットを追加してください:
if (!Nrf::GetBoard().Init(ButtonEventHandler)) {
LOG_ERR("User interface initialization failed.");
return CHIP_ERROR_INCORRECT_STATE;
}C++3.3 ボタン押下に反応してMatterデータモデルを更新してください。
これまでの実装では、LED1のローカル状態を更新していますが、この情報はMatterデータモデルに渡されないため、属性状態が物理デバイスの状態と矛盾する可能性があります。
3.3.1 他の#include行の下に次の行を追加してください:
#include <app-common/zap-generated/attributes/Accessors.h>C++3.3.2 Matterデータモデルと対話するメソッドにOnOffPlugエンドポイントのIDを伝播するために使用されるヘルパーconst値を定義してください。
constexpr EndpointId kOnOffPlugEndpointId = 1;C++3.3.3 Accessor.hファイルのSet()メソッドを使用してOnOff属性状態を更新してください。
アプリケーションスレッドからMatterスタックとデータモデルコンテキストを変更することは、未定義の動作につながる可能性があるため許可されていません。代わりに、Matterスレッドを使用してこれを行うことができます。
これはSystemLayer().ScheduleLambda()メソッドを使用して実行できます。
ButtonEventHandler()関数に次のコードスニペットを追加してください。
SystemLayer().ScheduleLambda([] {
Protocols::InteractionModel::Status status = Clusters::OnOff::Attributes::OnOff::Set(
kOnOffPlugEndpointId, Nrf::GetBoard().GetLED(Nrf::DeviceLeds::LED2).GetState());
if (status != Protocols::InteractionModel::Status::Success) {
LOG_ERR("Updating on/off cluster failed: %x", to_underlying(status));
}
});
C++4. アプリケーションをビルドしてDKにフラッシュする
レッスン2の演習1と同様に、アプリケーションをビルドしてDKにフラッシュしてください。
5. CHIPツールを使用してOnOffPlugデバイスをコミッショニングする
レッスン2の演習1と同様に、CHIPツールを使用してOnOffPlugデバイスをコミッショニングしてください。
5.1 Threadボーダールーターがまだ実行されていることを確認してください。実行されていない場合:
5.1.1 新しいコマンドラインターミナルを開き、次のコマンドを実行してOpenThreadボーダールーターを実行してください:
/dev/ttyACM0をThreadコプロセッサーが使用しているシリアルポート番号に置き換えてください。
sudo docker run -it --rm --privileged --name otbr --network otbr -p 8080:80 \
--sysctl "net.ipv6.conf.all.disable_ipv6=0 net.ipv4.conf.all.forwarding=1 net.ipv6.conf.all.forwarding=1" \
--volume /dev/ttyACM0:/dev/radio nrfconnect/otbr:fbde28a --radio-url spinel+hdlc+uart:///dev/radio?uart-baudrate=1000000Terminal command「The container name “/otbr” is already in use by container…」のようなエラーが発生した場合は、次のコマンドを実行してください
sudo docker kill otbrsudo ip -6 route del "fd11:22::/64" dev otbr0 via "fd11:db8:1::2"sudo ip link set dev otbr0 downsudo docker network rm otbr5.1.2 Webブラウザでhttp://localhost:8080/アドレスを開き、OpenThreadボーダールーターのグラフィカルユーザーインターフェースにアクセスしてください。
5.1.3 サイドパネルからFormタブに移動し、挿入されたすべてのデータが次の画像と同じであることを確認してください。次に、FORMボタンを押して、OpenThreadボーダールーターにThreadネットワークを形成してThreadリーダーになるよう要求してください。

5.1.4 新しいコマンドラインターミナルを開き、Docker内で実行されているThreadノードのステータスを確認してください:
sudo docker exec -it otbr sh -c "sudo ot-ctl state"Terminal command出力は次のようになります:
leader
DoneTerminal5.2 CHIPツールがまだ実行されていることを確認してください。実行されていない場合:
5.2.1 新しいコマンドラインターミナルを開き、次のコマンドを使用して前の演習で取得したダウンロード済みバイナリファイルを実行してください:
PCの場合:
./chip-tool_x64 interactive start
Raspberry Piの場合:
./chip-tool_arm64 interactive start
5.3 デバイスをネットワークにコミッショニングしてください。
5.3.1 Bluetooth LE経由のMatter広告が実行されていることを確認してください。
デバイスのシリアルポートに次のログが表示されます:
I: 730208 [DL]CHIPoBLE advertising started
I: 730212 [DL]NFC Tag emulation startedTerminalBluetooth LE経由のMatter広告は、Matterテンプレートサンプルでは自動的に開始されますが、1時間後にタイムアウトすることに注意してください。広告がタイムアウトした場合は、MatterデバイスのBUTTON0を押して再度開始してください。
5.3.2 CHIPツールアプリケーションを実行しているターミナルウィンドウに戻ってください。
次のコマンドを実行してコミッショニングプロセスを開始し、<thread dataset>引数に、レッスン2の演習1で取得してコンピューターに保存したThreadデータセットを入力してください。<your_selected_node_id>を、他の演習で使用されていないランダムなノードID(例:2)に置き換えてください。この同じ番号は、CHIPツール経由でデバイスにコマンドを送信するときに使用されます。
pairing ble-thread <your_selected_node_id> hex:<thread dataset> 20202021 3840Terminal commandその結果、MatterドアロックデバイスとCHIPツールアプリケーションは、コミッショニングフローを示す多くの詳細なメッセージをログに出力し始めます。これらは、ペアリングに問題がある場合に特に役立ち、問題のトラブルシューティングを可能にします。
5.1 CHIPツールがまだ実行されていることを確認してください。実行されていない場合:
5.1.1 新しいコマンドラインターミナルを開き、次のコマンドを使用して前の演習で取得したダウンロード済みバイナリファイルを実行してください:
PCの場合:
./chip-tool_x64 interactive start
Raspberry Piの場合:
./chip-tool_arm64 interactive start
5.2 デバイスをネットワークにコミッショニングしてください。
5.2.1 MatterデバイスのBUTTON0を押して、Bluetooth LE経由のMatterアドバタイジングを開始してください。
デバイスのシリアルポートに次のログが表示されます:
I: 730208 [DL]CHIPoBLE advertising started
I: 730212 [DL]NFC Tag emulation startedTerminal5.2.2 CHIPツールアプリケーションを実行しているターミナルウィンドウに戻ってください。
次のコマンドを実行し、<wifi_ssid>と<wifi_password>引数にWi-Fiネットワークデータを入力してください。
<your_selected_node_id>を、他の演習で使用されていないランダムなノードID(例:2)に置き換えてください。この同じ番号は、CHIPツール経由でデバイスにコマンドを送信するときに使用されます。
pairing ble-wifi <your_selected_node_id> <wifi_ssid> <wifi_password> 20202021 3840Terminal commandその結果、MatterドアロックデバイスとCHIPツールアプリケーションは、コミッショニングフローを示す多くの詳細なメッセージをログに出力し始めます。これらは、ペアリングに問題がある場合に特に役立ち、問題のトラブルシューティングを可能にします。
6. OnOffPlugの状態を制御する
6.1 CHIPツールのターミナルで次のコマンドを実行して、OnOffクラスターで使用可能なコマンドを確認しましょう:
onoffTerminal command次の出力に、使用可能なコマンドのリストが表示されます:
onoff command_name [param1 param2 ...]
+-------------------------------------------------------------------------------------+
| Commands: |
+-------------------------------------------------------------------------------------+
| * command-by-id |
| * off |
| * on |
| * toggle |
| * off-with-effect |
| * on-with-recall-global-scene |
| * on-with-timed-off |
| * read-by-id |
| - Read attributes from this cluster; allows wildcard endpoint and attribute. |
| * read |
| * write-by-id |
| * force-write |
| * write |
| * subscribe-by-id |
| - Subscribe to attributes from this cluster; allows wildcard endpoint and attri...|
| * subscribe |
| * read-event-by-id |
| - Read events from this cluster; allows wildcard endpoint and event. |
| * subscribe-event-by-id |
| - Subscribe to events from this cluster; allows wildcard endpoint and event. |
+-------------------------------------------------------------------------------------+Terminal6.2 CHIPツールのターミナルで次のコマンドを実行して、OnOffクラスターで読み取り可能な属性を確認してください:
onoff readTerminal command次の出力に、使用可能な属性のリストが表示されます:
onoff read attribute-name [param1 param2 ...]
+-------------------------------------------------------------------------------------+
| Attributes: |
+-------------------------------------------------------------------------------------+
| * on-off |
| * global-scene-control |
| * on-time |
| * off-wait-time |
| * start-up-on-off |
| * generated-command-list |
| * accepted-command-list |
| * attribute-list |
| * feature-map |
| * cluster-revision |
+-------------------------------------------------------------------------------------+Terminal6.3 CHIPToolのターミナルで次のコマンドを実行して、OnOff属性の状態を読み取ってください:
onoff read on-off <your_selected_node_id> 1Terminal commandデフォルトではOnOffPlugの状態がオフ(LED1がオフ)であるため、CHIPツールターミナルに次のような出力が表示されます:
[1770350882.137] [23287:23289] [TOO] Endpoint: 1 Cluster: 0x0000_0006 Attribute 0x0000_0000 DataVersion: 4111306799
[1770350882.137] [23287:23289] [TOO] OnOff: FALSETerminal6.4 CHIPツールターミナルで次のコマンドを実行して、OnOffPlugをオンにしてください:
onoff on <your_selected_node_id> 1Terminal commandLED1の状態がオンに変わったことが確認できます。また、Matterデバイスのログに次のような出力が表示されます:
I: 267590 [ZCL]Toggle ep1 on/off from state 0 to 1
I: Cluster OnOff: attribute OnOff set to 1Terminal6.5 CHIPツールのターミナルで次のコマンドを実行して、OnOff属性の状態を再度読み取り、変更されたことを確認してください:
onoff read on-off <your_selected_node_id> 1Terminal command今回は、OnOffPlugの状態がオン(LED1がオン)であるため、CHIPツールターミナルに次のような出力が表示されます:
[1770351060.542] [23287:23289] [TOO] Endpoint: 1 Cluster: 0x0000_0006 Attribute 0x0000_0000 DataVersion: 4111306804
[1770351060.542] [23287:23289] [TOO] OnOff: TRUETerminal6.6 BUTTON1をクリックして、LED1の状態がオフに変わったことを確認してください。
6.7 CHIPツールのターミナルで次のコマンドを実行して、OnOff属性の状態を再度読み取り、変更されたことを確認してください:
onoff read on-off <your_selected_node_id> 1Terminal command今回は、OnOffPlugの状態がオフ(LED1がオフ)であるため、CHIPツールターミナルに次のような出力が表示されます:
[1770351094.016] [23287:23289] [TOO] Endpoint: 1 Cluster: 0x0000_0006 Attribute 0x0000_0000 DataVersion: 4111306806
[1770351094.016] [23287:23289] [TOO] OnOff: FALSETerminalOnOff属性をサブスクライブすることで、コントローラーから手動で状態を読み取る必要がなくなり、状態が変更された場合に自動的に通知を受け取ることができます。
まず、次のコマンドを実行してコマンドの使用方法を学習してください:
onoff subscribeTerminal command次の出力に、使用可能な属性のリストが表示されます:
Usage:
onoff subscribe attribute-name [param1 param2 ...]
+-------------------------------------------------------------------------------------+
| Attributes: |
+-------------------------------------------------------------------------------------+
| * on-off |
| * global-scene-control |
| * on-time |
| * off-wait-time |
| * start-up-on-off |
| * generated-command-list |
| * accepted-command-list |
| * attribute-list |
| * feature-map |
| * cluster-revision |
+-------------------------------------------------------------------------------------+Terminal次のコマンドを実行して、サブスクリプションパラメータを学習してください:
onoff subscribe on-offTerminal command次の出力に、必要な引数のリストが表示されます:
Usage:
onoff subscribe on-off min-interval max-interval destination-id endpoint-idsTerminal6.8 次のコマンドを実行して、OnOff属性値をサブスクライブしてください:
onoff subscribe on-off 0 30 <your_selected_node_id> 1Terminal commandCHIPツールターミナルに次のような出力が表示されます:
[1770351174.845] [23287:23289] [DMG] Subscription established in 217ms with SubscriptionID = 0xd60a035c MinInterval = 0s MaxInterval = 30s Peer = 01:0000000000000001Terminal6.9 BUTTON1をクリックして、OnOff状態に関する通知が受信されることを確認してください。BUTTON1をクリックするたびに、CHIPツールターミナルに次のような出力が表示されます:
[1770351187.923] [23287:23289] [TOO] Endpoint: 1 Cluster: 0x0000_0006 Attribute 0x0000_0000 DataVersion: 4111306807
[1770351187.923] [23287:23289] [TOO] OnOff: FALSETerminal