以前、有志の方が作った、Arduino IDEで使えるMatterライブラリesp32-arduino-matterを使用して、ESP32でMatter対応プログラムを作りました。

今は、本家のespressif社が提供しているarduino-esp32で、Matter機能が提供されているようです。そこで、arduino-esp32を使って、Matter対応プログラムを作ってみました。
Espressifのライブラリ
以前の記事で使ったesp32-arduino-matterのページには、
このリポジトリはアーカイブされており、今後はメンテナンスされません! 現在はarduino-esp32がMatterをサポートしているので、そちらを使用してください。
と書かれていました。リンク先は、本家Espressif社の、Arduino IDEボードマネージャのリポジトリでした。
ということで、現在のArduinoのESP32ボードマネージャでは、Matter関係のサポートがされているようです。ボードマネージャの最新バージョンは3.3.3です。

ちなみにボードマネージャのインストール方法はこちらをご覧ください。
いつものように、ボートはESP32 Dev Moduleに設定してます。

サンプルをコンパイルする
このリポジトリの、libraries/Matter/examplesには、いろいろなMatterデバイスのサンプルがあります。現バージョンのESP32用ボードマネージャを使えば、これらが全て使えます。
この中から、一番簡単そうなOnOffライトを選んで試してみます。Arduinoのスケッチは、MatterOnOffLight.inoというファイルです。何も考えず、これをArduino IDEにコピペし、コンパイルを試みました。すると、ストレージ容量が足りないという意味のエラーが出ました。出来上がったプログラムが、現在使えるストレージ容量の131%になったようです。

試しに、Arduino IDEのメニューのTools -> Partition Schemeから、8Mにしてみました。

そうしたらコンパイルが通りました。とりあえず幸先良いです。

実は、市場にあるESP32ボードのほとんどのフラッシュメモリは4MBです。手元のESP32も4MBです。なのでこの設定では、ほぼ使えません。あとで調整します。
プログラムを解読する
このプログラムは、On/Offするだけの単純な照明器具のプログラムです。調光や調色機能はありません。その機能を確認するために、ESP32の青色LEDをライトに割り当ててます。また、人がライトをOn/Offする物理スイッチも用意されています。これをESP32のBOOT押しボタンに割り当ててます。動作すれば、HomeKitなどからのOn/Off操作により、青色LEDがOn/Offします。また、BOOTボタンを押すと、青色LEDがOn/Offすると同時に、HomeKit側のスイッチも状態変化します。
。 
プログラムを読んで理解したこと(間違っているかもしれませんが)を、日本語でコメント書き足してみます。ちなみに、青色LEDは2番ピンに、BOOTボタンは0番ピンに割り当てられてます。
#include <Matter.h>
#if !CONFIG_ENABLE_CHIPOBLE
//もしBLEでコミッションするならフラッシュメモリの無駄を防ぐためWiFiはインクルードしない
#include <WiFi.h>
#endif
#include <Preferences.h>
// BLEでコミッションするならCONFIG_ENABLE_CHIPOBLEを定義しておく
#if !CONFIG_ENABLE_CHIPOBLE
// BLEでコミッションしない場合は、手動でWiFi指定してWiFiでコミッションする
const char *ssid = "your-ssid"; // Change this to your WiFi SSID
const char *password = "your-password"; // Change this to your WiFi password
#endif
// このNodeのEndpointsのリスト ここではOn/Off Light Endpoint
MatterOnOffLight OnOffLight;
// 最後のOnOff状態をPreferencesを使って記憶
Preferences matterPref;
const char *onOffPrefKey = "OnOff";
// オンボードのLEDのピン番号。
const uint8_t ledPin = 2; // ESP32では2番が青色LEDなのでそれを書いておく
// bootボタンを入力に使う場合0
const uint8_t buttonPin = 0;
// Button control
uint32_t button_time_stamp = 0; // チャッタリング制御用
bool button_state = false; // ボタン状態 false = released | true = pressed
const uint32_t debouceTime = 250; // チャッタリング防止時間 (ms)
const uint32_t decommissioningTimeout = 5000; // 5秒長押しで設定モードへ
// ライトをOnOffする命令が来ると、このcallback関数が呼ばれる
bool setLightOnOff(bool state) {
Serial.printf("User Callback :: New Light State = %s\r\n", state ? "ON" : "OFF");
if (state) {
digitalWrite(ledPin, HIGH); //OnならLEDを点灯して
} else {
digitalWrite(ledPin, LOW); //OffならLEDを消灯
}
// store last OnOff state for when the Light is restarted / power goes off
matterPref.putBool(onOffPrefKey, state); //電源落とされても復帰するようバックアップ
// This callback must return the success state to Matter core
return true; //必ずtrueを返す
}
void setup() {
// ボタンとLEDを初期化
pinMode(buttonPin, INPUT_PULLUP);
pinMode(ledPin, OUTPUT);
Serial.begin(115200);
// CONFIG_ENABLE_CHIPOBLE is enabled when BLE is used to commission the Matter Network
// BLEでコミッションしないなら、手動でWiFi起動
#if !CONFIG_ENABLE_CHIPOBLE
// We start by connecting to a WiFi network
Serial.print("Connecting to ");
Serial.println(ssid);
// Manually connect to WiFi
WiFi.begin(ssid, password);
// Wait for connection
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\r\nWiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
delay(500);
#endif
// Matter EndPointを初期化。不揮発メモリ(?)から過去の状態を読んで設定
matterPref.begin("MatterPrefs", false);
bool lastOnOffState = matterPref.getBool(onOffPrefKey, true);
OnOffLight.begin(lastOnOffState);
OnOffLight.onChange(setLightOnOff);//変化した場合のcallback関数を設定
// Matter beginning - 全てのEndPointsが初期化できたら開始
Matter.begin();
// すでにコミッション済みのアクセサリの再起動かもしれない、その場合
if (Matter.isDeviceCommissioned()) {
Serial.println("Matter Node is commissioned and connected to the network. Ready for use.");
Serial.printf("Initial state: %s\r\n", OnOffLight.getOnOff() ? "ON" : "OFF");
OnOffLight.updateAccessory(); // configure the Light based on initial state
}
}
void loop() {
// コミッション済みかどうかをチェック(loop中にコミッション状態変化することもある)
if (!Matter.isDeviceCommissioned()) { // コミッション終わってなかったら
Serial.println(""); // ペアリング情報を流す
Serial.println("Matter Node is not commissioned yet.");
Serial.println("Initiate the device discovery in your Matter environment.");
Serial.println("Commission it to your Matter hub with the manual pairing code or QR code");
Serial.printf("Manual pairing code: %s\r\n", Matter.getManualPairingCode().c_str());
Serial.printf("QR code URL: %s\r\n", Matter.getOnboardingQRCodeUrl().c_str());
// waits for Matter Light Commissioning.
uint32_t timeCount = 0;
while (!Matter.isDeviceCommissioned()) { //コミッション終わるまで待ち、5秒ごとにメッセージ出す
delay(100);
if ((timeCount++ % 50) == 0) { // 50*100ms = 5 sec
Serial.println("Matter Node not commissioned yet. Waiting for commissioning.");
}
}
Serial.printf("Initial state: %s\r\n", OnOffLight.getOnOff() ? "ON" : "OFF");
OnOffLight.updateAccessory(); // configure the Light based on initial state
Serial.println("Matter Node is commissioned and connected to the network. Ready for use.");
}
// ボタンはライトの制御にも使う。ボタンが押されたかどうかチェック
if (digitalRead(buttonPin) == LOW && !button_state) { //前回HIGH今回LOWなら
// チャッタリング防止
button_time_stamp = millis(); // ボタン押下時刻を記録
button_state = true; // pressed.
}
// ボタン短押しでライトをOnOff(チャッタリング期間を過ぎて、ボタンHIGHなら、短押し)
uint32_t time_diff = millis() - button_time_stamp;
if (button_state && time_diff > debouceTime && digitalRead(buttonPin) == HIGH) {
button_state = false; // released
// Toggle button is released - toggle the light
Serial.println("User button released. Toggling Light!");
OnOffLight.toggle(); // Matter Controller also can see the change
}
// ボタンを5秒長押しなら、ライトを消して、つぎのloopでコミッションやり直しへ
if (button_state && time_diff > decommissioningTimeout) {
Serial.println("Decommissioning the Light Matter Accessory. It shall be commissioned again.");
OnOffLight.setOnOff(false); // turn the light off
Matter.decommission();
button_time_stamp = millis(); // avoid running decommissining again, reboot takes a second or so
}
}
プログラムのポイントをまとめると以下のようです。
- コミッション(最初にHomeKitに登録する作業)する時に、WiFiまたはBLEを使う
- BLE使う時はそこでWiFiの設定ができるけど、WiFi使う場合は、プログラムの中でSSID/Passwordを手動で設定しておく
- 今回使うESP32無印はBLEコミッションできないのでWiFi設定が必須
- Matter環境からOnOff指示が来る時のコールバック関数をonChange()メソッドで設定しておく
- そのコールバック関数でライトをOnOffする
- 物理ボタンの操作でもライトをOnOffする
- その場合は、toggle()メソッドでMatter環境に伝える
- 最初のloop()に入った時にコミッションできてなければ、できるまで永遠に待つ
- 動作していても物理ボタン5秒長押しされたらコミッションをやり直す
コミッションは、プログラムしなくてもやってくれるみたいですし、コールバックなどもわかりやすいと思いました。
メモリ不足を工夫で乗り切る
使ったボードは、技適マークが刻印されているESP32無印です。フラッシュメモリ容量は4MBです。先の手順で、8MBのパーティションスキームでコンパイルしたあと、ダウンロードしてみましたが、当然ですが動作しませんでした。
現在流通しているESP32は、ほとんどが4MBで、この上の8MB, 16MBの製品は見当たりませんでした。そこで、パーティションスキームで工夫することにします。ESP32のパーティションスキームについて、以下のサイトがとても参考になりました。

以下はこのページから引用させていただいた表です。4MBのESP32を、それぞれのパーティションスキームが、どのように分割するかが書かれてます。デフォルトでは、OTAでの使用を考慮して1,280kB x 2に分割し、他をファイル領域 (spiffs) にしているようです。
| filename | total | ota_0 | ota_1 | spiffs | 備考 |
|---|---|---|---|---|---|
| default.csv | 4MB | 1,280KB | 1,280KB | 1,408KB | 標準。BLEだと容量が足りない |
| huge_app.csv | 4MB | 3,072KB | 0KB | 896KB | OTAをなくしてspiffsを減らす |
| min_spiffs.csv | 4MB | 1,920KB | 1,920KB | 128KB | spiffsを減らす |
| no_fs.csv | 4MB | 1,984KB | 1,984KB | 0KB | spiffsを無くした |
| no_ota.csv | 4MB | 2,048KB | 0KB | 1,920KB | OTAをなくしてspiffsを増やす |
今回の利用では、ファイル領域は少なくても良いと思います。一方で、できればOTAは使いたいところです。ということで、spiffsを減らすmin_spiffsスキームを使うことにしました。プログラムを保存する領域は1,920KBです。デフォルトから50%増えるので、31%超過している現状を打破できるかと思いました。ということで、Arduino IDEのToolsメニューから、そのように設定しました。Flash Sizeは4MBで、Minimum SPIFFSを選んでます。

コミッションする
パーティションを調整したことで、プログラムが動作するようになりました。シリアルモニターには、以下のように、
- WiFiに接続できたこと、IPアドレスを得たこと
- まだコミッションされていないこと
- コミッションに必要なcode(数値)とQRコード(URL)
- コミッションできるまで待っていること
が表示されます。loop()の冒頭に書かれた数行を実施し、そのあとのループで止まっている状態です。
Connecting to your_ssid
...
WiFi connected
IP address:
192.168.0.146
E (4604) chip[DMG]: Endpoint 0, Cluster 0x0000_0031 not found in IncreaseClusterDataVersion!
Matter Node is not commissioned yet.
Initiate the device discovery in your Matter environment.
Commission it to your Matter hub with the manual pairing code or QR code
Manual pairing code: 34970112332
QR code URL: https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT:Y.K9042C00KA0648G00
Matter Node not commissioned yet. Waiting for commissioning.
Matter Node not commissioned yet. Waiting for commissioning.
Matter Node not commissioned yet. Waiting for commissioning.
このcodeの11桁の数字、もしくはURL先にあるQRコード、のいずれかを使ってHomeKitに追加します。下の写真は、
- iPhoneのホームから、画面右上の+ボタンを押し、
- 「アクセサリを追加」メニューを選んで、
- 上のメッセージにあるURL先の、QRコードを読み込ませている
ところです。

実はHomeKitの状態が良くないのか何度か挑戦して、ようやく追加できました。iPhoneを再起動したら、追加できました。
動作確認
iPhoneやMacのホームには、ESP32が以下のように照明デバイスとして表示されます。タップやクリックするとon/offします。


それと同期して、ESP32の青色ダイオードも点灯・消灯します。またESP32のBOOTボタンを押すと、この電球デバイスの状態がon/offと切り替わります。
操作している様子の動画を以下に示します。画面ボタンを操作した後、最初に物理ボタンを使った時に、画面ボタンへの反映に6秒程度の時間がかかってます。他のMatterデバイス(以前紹介したOnvisのプラグ)でも同程度の遅延が発生しているので、HomeKit環境側の問題かと思われます。それ以外の操作では、問題なく動作してます。
ESP32のMatter対応状況
githubのページに、ESP32モデルに対して、ライブラリが提供している機能が掲載されています。ESP32がハードウェアを搭載していても、非サポートの機能があります。
今回使用した無印とS2は、BLEを搭載してはいますが、BLEコミッションをサポートしていません。なので、先のサンプルプログラムのように、WiFiのSSID/パスワードをプログラムのコード内で設定して、そのあとWiFiを使ってコミッションする必要があります。
C5, C6も同様に、Threadを搭載してはいますが、Matter over Threadは、サポートしていません。ESP-IDFを使って頑張れば、C5, C6でもMatter over Threadが可能だそうです。

今後は、BLEコミッションと、Threadをぜひ試してみたいです。
まとめ
Espressif社のArduino IDE用ESP32ボードマネージャーが、Matter対応していました。おかげで、Arduino IDEを使ってMatterプログラムを作ることができました。ライブラリはわかりやすくて、シンプルです。使いやすいArduino IDEでプログラムできるので、素人DIYでもMatterデバイスが作れると思います。
一方で、HomeKit側の不調が原因と思われますが、コミッションに手間取りました。また、4MBモデルではメモリー容量が厳しいので、複雑なプログラムを作れるのか、さらにはOTAができるのかなど、今後確かめたいです。




コメント