前の演習で構成したクラスターは、.xml ファイルに記述されています。.xml ファイルを手動で作成して新しいクラスターを作成することも可能ですが、これは便利な方法ではありません。
Nordic Semiconductorは、Matterデータモデルに従ってMatterクラスター、クラスター拡張、およびデバイスタイプを作成および編集するために使用できる、Matter Cluster Editorと呼ばれる専用ツールを提供しています。このツールはグラフィカルユーザーインターフェースを提供し、nRF Connect for Desktopのアプリケーションの1つとして利用できるため、簡単にインストールできます。
この演習の目標は、Random Number Generator(乱数生成器)と呼ばれる新しいデバイスタイプと、この機能を実装するクラスターを作成することです。新しいデバイスタイプは、このレッスンの演習1で作成したOnOff Plugサンプルに追加されます。
0. この演習のベースコードを入手します。
Visual Studio Codeの「WELCOME」パネルで「Open an existing application」を選択し、コースのGitHubリポジトリに移動して、l3/l3_e2 ディレクトリを開きます。アプリケーションは「APPLICATIONS」パネルの下に表示されます。
この演習のベースコードは前の演習のソリューションであり、この演習のコードスニペットをどこに入力するかについての指示が追加されています。
1. Matter Cluster Editorを開く
1.1 nRF Connect for Desktopを起動し、[Apps]タブに移動します。
1.2 Matter Cluster Editorアプリをインストールして開きます。

次のウィンドウが表示されます。

2. Random Number Generatorクラスターを追加する
2.1 「CLUSTER」タブに移動し、すべてのフィールドに次の情報を入力します。

「Code」などの許可される値の説明については、フィールド名にカーソルを合わせると、ツールのヒントの説明が横に表示されます。

選択した「Domain」はクラスターの動作に大きな影響を与えませんが、共通の目的に基づいてクラスターをグループ化できるため、ZAPツールなどを使用してクラスターリストをナビゲートするのが簡単になります。何らかのパラメータを測定するためのクラスターを作成する場合、適切なドメインは「Measurement & Sensing」になります。「RandomNumberGenerator」の場合、適切なドメインが定義されていないため、他の特定のグループに属さないクラスターをグループ化する「General」ドメインを選択するのが最善のオプションです。
「Code」は32ビットの値で、上位16ビットがメーカーコード、下位16ビットがクラスターIDです。独自のコードを登録しているメーカーであればそれを使用できますが、この演習ではテストコード範囲のコード「0xFFF1」を使用しました。カスタムクラスターIDは0xFC00〜0xFFFEの範囲の値である必要があるため、例としてこの演習では「0xFC01」という値を使用しました。
2.2 「COMMANDS」タブに移動し、「Add Command」ボタンをクリックします。
フィールドに次の情報を入力して、「Generate」コマンドを作成します。
「Code」は32ビットの値で、上位16ビットがメーカーコード、下位16ビットがコマンドIDを表します。この演習のクラスターでは、テスト用に予約されているID「0xFFF1」を使用しています。コマンドIDは0x0000〜0x00FFの間の値である必要があるため、この演習ではランダムに「0x0000」という値を選択できます。
Nordic DKはサーバーロールを実行しており、このコマンドを送信するのは外部クライアントデバイス(具体的にはMatterコントローラー)であるため、コマンドの「Source」は「client」に設定する必要があります。

次に、「Arguments」ボタンをクリックし、表示されるプラス記号をクリックしてコマンド引数を追加します。

フィールドに次の情報を入力して、生成される数値の最小値と最大値を設定する2つの引数を作成します。
まず、最小値の詳細を入力します。
次に、プラス記号をクリックして別の引数を追加し、最大値の詳細を入力します。

「Arguments」ビューで「Save」ボタンをクリックし、次に「Command」ビューで再度「Save」をクリックします。
次の図に示すように、コマンドが「COMMANDS」ビューに表示されます。

2.3 「ATTRIBUTES」タブに移動し、「Add Attribute」ボタンをクリックします。
フィールドに次の情報を入力して、「GeneratedNumber」属性を作成します。

「Code」は32ビットの値で、上位16ビットがメーカーコード、下位16ビットが属性IDです。クラスターの場合と同様に、この演習ではテストコード範囲のコード「0xFFF1」を使用しました。コマンドIDは0x0000〜0x4FFFの範囲の値である必要があるため、例としてこの演習では「0x0000」という値を使用しました。
その属性のコードと「Generate」コマンドのコードが同じであることに気づくかもしれません。これは偶然であり、要件ではありません。この演習では、必要な範囲から利用可能な最初の値を使用したためです。
Nordicデバイスはサーバーロールを実行し、属性の状態を保持するため、属性の「Side」は「server」に設定する必要があります。
「Type」には16ビットサイズの符号付き整数が選択され、「Min」および「Max」の値は16ビット符号付き整数の定義された制限として設定されました。
「Save」ボタンをクリックします。次の図に示すように、属性が「ATTRIBUTES」ビューに表示されます。

2.4 「EVENTS」タブに移動し、「Add Event」ボタンをクリックします。
フィールドに次の情報を入力して、「NumberGenerated」イベントを作成します。

「Code」は32ビットの値で、上位16ビットがメーカーコード、下位16ビットがイベントIDです。クラスターの場合と同様に、この演習ではテストコード範囲のコード「0xFFF1」を使用しました。コマンドIDは0x0000〜0x00FFの範囲の値である必要があるため、例としてこの演習では「0x0000」という値を使用しました。
Nordicデバイスはサーバーロールを実行し、属性の状態を保持するため、属性の「Side」は「server」に設定する必要があります。
「Priority」はリストから利用可能な任意の値に設定できます。例として「info」プライオリティが選択されました。
「Save」ボタンをクリックします。次の図に示すように、イベントが「EVENTS」ビューに表示されます。

2.5 「DEVICE TYPE」タブに移動し、フィールドに次の情報を入力して「Random number generator」デバイスタイプを作成します。

選択した「Domain」は、新しいクラスターを作成する場合と同様に、デバイスタイプの動作に大きな影響を与えません。このデバイスタイプの場合、適切なドメインが定義されていないため、「RandomNumberGenerator」クラスターと同様に、「General」ドメインが選択されました。
新しいデバイスタイプはいかなるユーティリティ機能も表さず、ダイナミックエンドポイントで動作することを意図していないため、「Class」は「Simple」に設定する必要があります。
新しいデバイスタイプはノード全体の機能ではなく、単一のエンドポイントで表される単一の機能に適用されるため、「Scope」は「Endpoint」に設定する必要があります。
「Profile ID」はMatterスタックのリビジョン(Matter 1.5)を表すため、「0x105」に設定する必要があります。
「Device ID」は32ビットの値で、上位16ビットがメーカーコード、下位16ビットがデバイスIDです。クラスターの場合と同様に、この演習ではテストコード範囲のコード「0xFFF1」を使用しました。デバイスIDは0x0000〜0x4FFFの範囲の値である必要があるため、例としてこの演習では「0x0001」という値を使用しました。
現時点では、新しいデバイスタイプはいかなるクラスターも有効にする必要はありません。
この要件を追加するには、「Add Cluster assignment to device type」ボタンをクリックします。
「Cluster」フィールドに必要な「RandomNumberGenerator」クラスターを入力し、「Server」フィールドの横にあるスイッチを有効にして、クラスターがサーバーの属性とコマンドを使用することを示します。次に「Save」をクリックします。

2.6 左側のバーにある「Save to file」ボタンをクリックします。
サンプルのディレクトリを基準とした src/default_zap ディレクトリ(今回の場合は C:\ncs\matt-fund\l3\l3_e2\src\default_zap)に移動し、ファイル名を RandomNumberGenerator.xml にします。次に「Save」をクリックします。

3. ZAPツールを使用してRandom Number Generatorデバイスタイプを追加する
3.1 Visual Studio Codeの「APPLICATIONS」パネルで、アプリケーションを右クリックして「Start New Terminal」を選択し、nRF Connect SDKのバージョンと対応するツールチェーンのバージョンを選択します。これにより、サンプルディレクトリでターミナルが開きます。
新しいデバイスタイプとクラスターを記述した RandomNumberGenerator.xml ファイルを含む次のコマンドを使用して、ZAPツールを実行します。
west zap-gui -j src/default_zap/zcl.json --clusters ./src/default_zap/RandomNumberGenerator.xmlTerminal commandZAPツールのウィンドウが表示されます。
3.2 「+ ADD ENDPOINT」ボタンをクリックし、「Device」の下で「Random number generator」を検索して選択すると、残りのフィールドが自動的に入力されます。

次に「Create」を選択して、「Random number generator」デバイスタイプを有効にします。

3.3 右上の検索ウィンドウで「RandomNumberGenerator」クラスターを検索し、「RandomNumberGenerator」クラスターがサーバーロールで有効になっていることを確認します。

3.4 歯車アイコンをクリックし、すべての属性、コマンド、およびイベントが有効になっていることを確認します。

3.5 上部バーの「File」->「Save」をクリックして構成を保存し、アプリケーションを終了します。
3.6 ターミナルで次のコマンドを呼び出して、.zapファイルに基づいてコードを生成します。
west zap-generate --fullTerminal commandこの時点で、新しいエンドポイントと必要なすべてのクラスターがアプリケーションに追加されました。
--full オプションを指定して west zap-generate コマンドを呼び出すと、Matterデータモデル全体が再生成されるため、数十秒かかる場合があります。プロセスが終了するまでお待ちください。
4. アプリケーションコードでRandomNumberGenerator機能を処理する
4.1 ビルドシステムが、指定されたMatterクラスターのみを含むデフォルトのファイルではなく、カスタムクラスターを含む生成されたファイルを指すようにします。これを行うには、サンプルのディレクトリにある CMakeLists.txt ファイルを変更します。次のコードスニペットを CMakeLists.txt ファイルに追加します。
include(${ZEPHYR_NRF_MODULE_DIR}/samples/matter/common/cmake/zap_helpers.cmake)
ncs_get_zap_parent_dir(ZAP_PARENT_DIR)
#Override zap-generated directory.
get_filename_component(CHIP_APP_ZAP_DIR ${ZAP_PARENT_DIR}/zap-generated REALPATH CACHE)CMake4.2 次に、カスタムクラスターをMatterデータモデル構成に渡すために、次の行を追加します。
ncs_configure_data_model(
EXTERNAL_CLUSTERS "RANDOM_NUMBER_GENERATOR" # Add EXTERNAL_CLUSTERS flag
)CMake4.4 「RandomNumberGenerator」クラスターのInitコールバックを定義します。
この実装は、たとえ空であっても、コードで処理されるすべてのクラスターに対して提供される必要があります。
app_task.cpp ファイルの最後の一番下に、次のコードを追加します。
void MatterRandomNumberGeneratorPluginServerInitCallback() {
}C++4.5 データモデルのコールバック宣言を含むヘッダーファイルを app_task.cpp ファイルにインクルードします。
#include <app-common/zap-generated/callback.h>C++4.5 マクロを短く使用できるように、他の同様の行の近くにカスタムクラスターの「using namespace」エントリを追加します。
using namespace ::chip::app::Clusters::RandomNumberGenerator;C++4.6 次に、「RandomNumberGenerator」クラスターから「Generate」コマンドを受信したときにデータモデルによって自動的に呼び出されるコールバックの実装を定義する必要があります。関数のシェルは、この演習のベースコードですでに次のように定義されています。
bool emberAfRandomNumberGeneratorClusterGenerateCallback(chip::app::CommandHandler *commandObj, const chip::app::ConcreteCommandPath &commandPath,
const RandomNumberGenerator::Commands::Generate::DecodableType &commandData) {}C++4.7 乱数生成の最小値と最大値である、コマンドに含まれる引数を取得します。
次の2行を追加します。
int16_t minValue = commandData.minValue;
int16_t maxValue = commandData.maxValue;C++4.8 乱数を生成するためのメソッドを含むヘッダーファイルをインクルードします。
#include <zephyr/random/random.h>C++4.9 指定された範囲で乱数を生成し、デバッグ目的でデバイスログに出力するコードを追加します。
LOG_INF("Generating random number between %d and %d", minValue, maxValue);
int16_t randomNumber = sys_rand16_get() % (maxValue - minValue + 1) + minValue;
LOG_INF("Random number generated: %d", randomNumber);C++4.10 生成された数値をGeneratedNumber属性の値として設定し、デフォルトのレスポンスコマンドにステータスを追加して、コマンドが成功したかどうかをクライアントデバイスに通知します。
Protocols::InteractionModel::Status status = RandomNumberGenerator::Attributes::GeneratedNumber::Set(commandPath.mEndpointId, randomNumber);
commandObj->AddStatus(commandPath, status);
if (status == Protocols::InteractionModel::Status::Success) {
return true;
}
return false;C++4.11 GenerateNumber属性値の変更の処理を追加します。
これは、演習1で使用した MatterPostAttributeChangeCallback メソッドで行うことができます。既存のコードを変更して、変更された値を取得し、生成された数値が実際にMatterデータモデルに設定されたことを確認するためのログを出力する「else if」条件を追加します。
else if (clusterId == RandomNumberGenerator::Id && attributeId == RandomNumberGenerator::Attributes::GeneratedNumber::Id) {
int16_t generatedNumber = 0;
memcpy(&generatedNumber, value, sizeof(int16_t));
LOG_INF("Cluster RandomNumberGenerator: attribute GeneratedNumber set to %d", generatedNumber);
}C++5. アプリケーションをビルドしてDKに書き込む
アプリケーションをビルドしてDKに書き込みます。
6. CHIPツールを使用してMatterデバイスをコミッショニングする
前の演習と同様に、CHIPツールを使用してMatterデバイスをコミッショニングします。
6.1 Threadボーダールーターがまだ動作していることを確認します。動作していない場合:
6.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 otbr6.1.2 Webブラウザで http://localhost:8080/ アドレスを開き、OpenThreadボーダールーターのグラフィカルユーザーインターフェースにアクセスします。
6.1.3 サイドパネルから「Form」タブに移動し、入力されたすべてのデータが次の図と同じであることを確認します。次に、「FORM」ボタンを押して、OpenThreadボーダールーターにThreadネットワークを形成し、Threadリーダーになるよう要求します。

6.1.4 新しいコマンドラインターミナルを開き、Docker内で実行されているThreadノードのステータスを確認します。
sudo docker exec -it otbr sh -c "sudo ot-ctl state"Terminal command出力は次のようになります。
leader
DoneTerminal6.2 CHIPツールがまだ動作していることを確認します。動作していない場合:
6.2.1 新しいコマンドラインターミナルを開き、前の演習で入手したダウンロード済みのバイナリファイルを次のコマンドを使用して実行します。
PCの場合:
./chip-tool_x64 interactive start
Raspberry Piの場合:
./chip-tool_arm64 interactive start
6.3 デバイスをネットワークにコミッショニングします。
6.3.1 Bluetooth LE経由のMatterアドバタイズが実行されていることを確認します。
デバイスのシリアルポートに次のログが表示されるはずです。
I: 730208 [DL]CHIPoBLE advertising started
I: 730212 [DL]NFC Tag emulation startedTerminalBluetooth LE経由のMatterアドバタイズはMatter Templateサンプルに対して自動的に開始されますが、1時間後にタイムアウトすることに注意してください。アドバタイズがタイムアウトした場合は、MatterデバイスのBUTTON0を押して再度開始してください。
6.3.2 CHIPツールアプリケーションを実行しているターミナルウィンドウに戻ります。
次のコマンドを実行してコミッショニングプロセスを開始し、「<thread dataset>」引数に、レッスン2の演習1で取得してコンピュータに保存したThreadデータセットを入力します。<your_selected_node_id> を、他の演習で使用されていないランダムなノードID(例:3)に置き換えます。CHIPツールを介してデバイスにコマンドを送信するときも、これと同じ番号が使用されます。
pairing ble-thread <your_selected_node_id> hex:<thread dataset> 20202021 3840Terminal commandその結果、MatterデバイスとCHIPツールアプリケーションは、コミッショニングフローを示す多くの詳細なメッセージをログに出力し始めます。これらは、ペアリングの問題が発生した場合に特に役立ち、問題のトラブルシューティングを可能にします。
6.1 CHIPツールがまだ動作していることを確認します。動作していない場合:
6.1.1 新しいコマンドラインターミナルを開き、前の演習で入手したダウンロード済みのバイナリファイルを次のコマンドを使用して実行します。
PCの場合:
./chip-tool_x64 interactive start
Raspberry Piの場合:
./chip-tool_arm64 interactive start
6.2 デバイスをネットワークにコミッショニングします。
6.2.1 MatterデバイスのBUTTON0を押して、Bluetooth LE経由のMatterアドバタイズを開始します。
デバイスのシリアルポートに次のログが表示されるはずです。
I: 730208 [DL]CHIPoBLE advertising started
I: 730212 [DL]NFC Tag emulation startedTerminal6.2.2 CHIPツールアプリケーションを実行しているターミナルウィンドウに戻ります。
次のコマンドを実行し、<wifi_ssid> と <wifi_password> 引数にWi-Fiネットワークデータを入力します。
<your_selected_node_id> を、他の演習で使用されていないランダムなノードID(例:3)に置き換えます。CHIPツールを介してデバイスにコマンドを送信するときも、これと同じ番号が使用されます。
pairing ble-wifi <your_selected_node_id> <wifi_ssid> <wifi_password> 20202021 3840Terminal commandその結果、MatterデバイスとCHIPツールアプリケーションは、コミッショニングフローを示す多くの詳細なメッセージをログに出力し始めます。これらは、ペアリングの問題が発生した場合に特に役立ち、問題のトラブルシューティングを可能にします。
7. Random number generatorデバイスの機能をテストする
7.1 「RandomNumberGenerator」クラスターから「GeneratedNumber」属性の値を取得します。
CHIPツールは、名前によるカスタムクラスターおよび属性の制御をサポートしていません。ただし、クラスター作成時に割り当てられたコードを使用して行うことができます。たとえば、RandomNumberGeneratorクラスターの場合、コードは0xFFF1FC01であり、GeneratedNumber属性の場合、コードは0xFFF10000です。必要な構文を確認するには、次のコマンドを使用します。
any read-by-idTerminal command次に、属性の値を読み取るには、CHIPツールのターミナルで次のコマンドを使用します。
any read-by-id 0xFFF1FC01 0xFFF10000 <your_selected_node_id> 2Terminal commandCHIPツールのターミナルに同様の出力が表示されます。
[1770662965.655] [78890:78892] [DMG] Data = 0 (signed),
[1770662965.655] [78890:78892] [DMG] },
[1770662965.655] [78890:78892] [DMG]
[1770662965.655] [78890:78892] [DMG] },
[1770662965.655] [78890:78892] [DMG]
[1770662965.655] [78890:78892] [DMG] ],
[1770662965.655] [78890:78892] [DMG]
[1770662965.655] [78890:78892] [DMG] SuppressResponse = true,
[1770662965.655] [78890:78892] [DMG] InteractionModelRevision = 12
[1770662965.655] [78890:78892] [DMG] }
[1770662965.655] [78890:78892] [TOO] Endpoint: 2 Cluster: 0xFFF1_FC01 Attribute 0xFFF1_0000 DataVersion: 4218421684
[1770662965.655] [78890:78892] [TOO] Don't know how to log attribute valueTerminal「Data = 0 (signed)」フィールドには属性の値が表示されます。デフォルトでは0に設定されています。
7.2 「RandomNumberGenerator」クラスターの「Generate」コマンドを使用して、新しい数値の生成を要求します。
属性やクラスターと同様に、コマンドはそのコード(0xFFF10000)を使用して送信できます。さらに、コマンド引数は、そのインデックスと、データ型を表す専用の文字を伴う値を使用して渡すことができます。必要な構文を確認するには、次のコマンドを使用します。
any command-by-idTerminal command次に、次のコマンドを呼び出して、「MinValue」を-10に、「MaxValue」を10に設定して「Generate」コマンドを送信します。
any command-by-id 0xFFF1FC01 0xFFF10000 '{"0x0": "s:-10", "0x1": "s:10"}' <your_selected_node_id> 2Terminal commandMatterデバイスのログに同様の出力が表示されます。
I: Generating random number between -10 and 10
I: Random number generated: -5
I: Cluster RandomNumberGenerator: attribute GeneratedNumber set to -5Terminal7.3 次のコマンドを使用して「GeneratedNumber」属性の値を再度取得し、変更されたことを確認します。
any read-by-id 0xFFF1FC01 0xFFF10000 <your_selected_node_id> 2Terminal commandすると、CHIPツールのターミナルに次の出力が表示されます。
[1770664264.909] [91192:91194] [DMG] Data = -5 (signed),
[1770664264.909] [91192:91194] [DMG] },
[1770664264.909] [91192:91194] [DMG]
[1770664264.909] [91192:91194] [DMG] },
[1770664264.909] [91192:91194] [DMG]
[1770664264.909] [91192:91194] [DMG] ],
[1770664264.909] [91192:91194] [DMG]
[1770664264.909] [91192:91194] [DMG] SuppressResponse = true,
[1770664264.909] [91192:91194] [DMG] InteractionModelRevision = 12
[1770664264.909] [91192:91194] [DMG] }
[1770664264.910] [91192:91194] [TOO] Endpoint: 2 Cluster: 0xFFF1_FC01 Attribute 0xFFF1_0000 DataVersion: 3312041728
[1770664264.910] [91192:91194] [TOO] Don't know how to log attribute valueTerminal「Data = -5 (signed)」には属性の値が表示されます。この値は変更されており、Matterデバイスのログに表示されている値と一致しています。
7.4 手順7.2と7.3を数回繰り返し、ランダムな値が生成されることを確認します。また、MinValueとMaxValueを変更して、使用される値の範囲を調整してみることもできます。