RaspberryPiでMatter対応スマートライトを作った

# はじめに

[Matter公式github](https://github.com/project-chip)のコードを使ってスマートライトを作ってみました.
結果としてAlexaからのみですが普段使いできそうなものができました.

* Google Homeでは追加時に"Not a Matter-certified device"と表示され登録できなかった. 
  どうやら自前の証明書を発行した上で許可しないといけないっぽい
  https://developers.home.google.com/matter/integration/create?hl=ja
https://developers.home.google.com/matter/build/certificates?hl=ja
* Siriは環境が無いので未検証です

# ポイント
できるだけシンプルかつ実用的に作るため以下のようにしています

1. Matter over Wi-Fiを使用する
    Threadを使わないので外部モジュール不要
2. 給電制御可能なUSBハブでライトのON/OFFを制御する
    市販のUSBライトが使用可能
 
# 使用した物

1. Raspberry Pi 3 Model B
    Ubuntu Server 22.04 LTS (64bit)が動作するもの (おそらく3B以降であればどれでも動く)
1. microSDカード 32GB
   16GBではギリギリかも
1. BUFFALO 電源連動節電機能付き 4ポートセルフパワーハブ BSH4AE12BK
    libusbからUSBポートの給電ON/OFF制御ができるUSBハブ
1. ダイソー テープライト（３０ＬＥＤｓ、１ｍ、ホワイト）
   USBに挿すだけで点灯するシンプルなLEDストリップ
1. Amazon Echo
    Matter対応のもの

# デバイス構築

## OSのインストール

1. Raspberry Pi ImagerなどでUbuntu 22.04 LTSをmicroSDカードに書き込む
1. 起動してユーザやNWなどを設定

## wpa_supplicantの設定

アプリからのデバイス追加の際に使用するWi-Fiを設定するので、設定内容を保存できるようにwpa_supplicantの設定を変更します

1. serviceの修正
/etc/systemd/system/dbus-fi.w1.wpa_supplicant1.serviceのExecStart=の行を以下のように変更
```text
ExecStart=/sbin/wpa_supplicant -u -s -i wlan0 -c /etc/wpa_supplicant/wpa_supplicant.conf
```
1. confの修正
/etc/wpa_supplicant/wpa_supplicant.conf の内容を以下のように変更
```text
ctrl_interface=DIR=/run/wpa_supplicant
update_config=1
```

## 必要なライブラリのインストール

```
sudo apt-get install -y git gcc g++ python pkg-config libssl-dev libdbus-1-dev libglib2.0-dev ninja-build python3-venv python3-dev unzip pi-bluetooth avahi-utils
# USBハブの給電制御用
sudo apt-get install -y libusb-dev
```

## swapの追加
アプリのビルドにメモリを使用するのでswapを追加しておく
2GB以上のメモリがあればスキップしてOK(たぶん)
```bash
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
sudo chmod 0600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
```

## コードのcloneと環境のセットアップ

```bash
git clone https://github.com/project-chip/connectedhomeip.git --branch v1.0.0
cd connectedhomeip
./scripts/checkout_submodules.py --shallow --platform linux
./scripts/build/gn_bootstrap.sh
source scripts/activate
```

## サンプルアプリの変更

examples/lighting-app/linux以下のLinux用サンプルアプリをUSBハブの電源供給をON/OFFするように変更
 ```diff
diff --git a/examples/lighting-app/linux/BUILD.gn b/examples/lighting-app/linux/BUILD.gn
index 721f408437..edc31c6c39 100644
--- a/examples/lighting-app/linux/BUILD.gn
+++ b/examples/lighting-app/linux/BUILD.gn
@@ -94,6 +94,7 @@ executable("chip-lighting-app") {
     cflags = [ "-Wconversion" ]
   }

+  libs = [ "usb" ]
   output_dir = root_out_dir
 }

diff --git a/examples/lighting-app/linux/include/CHIPProjectAppConfig.h b/examples/lighting-app/linux/include/CHIPProjectAppConfig.h
index e919666bb9..73a6984fdf 100644
--- a/examples/lighting-app/linux/include/CHIPProjectAppConfig.h
+++ b/examples/lighting-app/linux/include/CHIPProjectAppConfig.h
@@ -40,7 +40,13 @@
 #define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DEVICE_TYPE 1

 #define CHIP_DEVICE_CONFIG_DEVICE_TYPE 257 // 0x0101 = 257 = Dimmable Bulb
+//#define CHIP_DEVICE_CONFIG_DEVICE_TYPE 256 // 0x0100 = 256 = Light Bulb

 #define CHIP_DEVICE_CONFIG_ENABLE_COMMISSIONABLE_DEVICE_NAME 1

 #define CHIP_DEVICE_CONFIG_DEVICE_NAME "Test Bulb"
+
+#define FATCONFDIR "/var/tmp"
+#define SYSCONFDIR "/var/tmp"
+#define LOCALSTATEDIR "/var/tmp"
+#define CHIP_CONFIG_KVS_PATH "/var/tmp/chip_kvs"
diff --git a/examples/lighting-app/linux/main.cpp b/examples/lighting-app/linux/main.cpp
index 7813c59979..42acfd8ab8 100644
--- a/examples/lighting-app/linux/main.cpp
+++ b/examples/lighting-app/linux/main.cpp
@@ -25,6 +25,13 @@
 #include <app/clusters/network-commissioning/network-commissioning.h>
 #include <lib/support/logging/CHIPLogging.h>
 #include <platform/Linux/NetworkCommissioningDriver.h>
+#include <usb.h>
+
+#define USB_PORT_FEAT_POWER 8
+#define CTRL_TIMEOUT 1000
+#define USB_RT_PORT (USB_TYPE_CLASS|USB_RECIP_OTHER)
+#define HUB_ID_VENDOR 0x05e3
+#define HUB_ID_PRODUCT 0x0608

 using namespace chip;
 using namespace chip::app;
@@ -73,6 +80,55 @@ void ApplicationInit()
 #endif
 }

+void UsbPowerCtrl(int id_vendor, int id_product, bool pwr)
+{
+    struct usb_bus *busses;
+    struct usb_bus *bus;
+    busses = usb_get_busses();
+    for (bus = busses; bus; bus = bus->next)
+    {
+        struct usb_device *dev;
+        for (dev = bus->devices; dev; dev = dev->next)
+        {
+            usb_dev_handle *udh = NULL;
+            if (dev->descriptor.idVendor == id_vendor
+                && dev->descriptor.idProduct == id_product)
+            {
+                udh = usb_open(dev);
+                int request = pwr ? USB_REQ_SET_FEATURE : USB_REQ_CLEAR_FEATURE;
+                int feature = USB_PORT_FEAT_POWER;
+                for( int i = 1; i <= 4; i++)
+                {
+                    usb_control_msg(udh, USB_RT_PORT, request, feature, i, NULL, 0, CTRL_TIMEOUT);
+                }
+                usb_close(udh);
+            }
+        }
+    }
+}
+
+void ActionInitiated(LightingManager::Action_t aAction)
+{
+    if (aAction == LightingManager::ON_ACTION)
+    {
+       UsbPowerCtrl(HUB_ID_VENDOR, HUB_ID_PRODUCT, true);
+    }
+    else if (aAction == LightingManager::OFF_ACTION)
+    {
+       UsbPowerCtrl(HUB_ID_VENDOR, HUB_ID_PRODUCT, false);
+    }
+}
+
+void ActionCompleted(LightingManager::Action_t aAction)
+{
+    if (aAction == LightingManager::ON_ACTION)
+    {
+    }
+    else if (aAction == LightingManager::OFF_ACTION)
+    {
+    }
+}
+
 int main(int argc, char * argv[])
 {
     if (ChipLinuxAppInit(argc, argv) != 0)
@@ -80,7 +136,12 @@ int main(int argc, char * argv[])
         return -1;
     }

+    usb_init();
+    usb_find_busses();
+    usb_find_devices();
+    UsbPowerCtrl(HUB_ID_VENDOR, HUB_ID_PRODUCT, false);
     LightingMgr().Init();
+    LightingMgr().SetCallbacks(ActionInitiated, ActionCompleted);
     ChipLinuxAppMainLoop();

     return 0;
```

  * 簡単な説明
    * CHIPProjectAppConfig.h
         * (CHIP_DEVICE_CONFIG_DEVICE_TYPE: Dimmable Bulbから変更したかったが効果なしだった)
         * #define 〜DIR, ~PATH: 生成ファイルの場所を再起動しても消えない/var/tmpに変更
    * main.cpp
        * #define HUB_ID_VENDOR, HUB_ID_PRODUCT: 該当USBハブを特定するための製品固有値
        * UsbPowerCtrl(): 下記参考記載のhub-ctrl.cを参考にしたidVendor/IdProductを元に電源供給ON/OFFする関数
       (おそらくハブの実装起因で)USBポート毎のON/OFFがうまく動作しないので全ポート(1~4)を一括操作
        * LightingMgr().SetCallbacks(ActionInitiated, ActionCompleted)
       コントローラーからのON/OFF指示に対するCallbackにUsbPowerCtrl()を呼ぶ関数を登録

## サンプルアプリのビルド

```bash
cd examples/lighting-app/linux
gn gen out/debug
ninja -C out/debug
```

## サンプルアプリの自動起動の設定

```bash
# サンプルアプリのコピー
sudo mkdir /opt/lighting-app
sudo cp out/debug/chip-lighting-app /opt/lighting-app/
# systemdのサービスファイルの用意
cat <<EOF > chip-lighting-app.service
[Unit]
Description=CHIP lighting app
After=network.target

[Service]
Type=Simple
ExecStart=/opt/lighting-app/chip-lighting-app --wifi
Restart=on-failure

[Install]
WantedBy=default.target
EOF
sudo cp chip-lighting-app.service /etc/systemd/system
sudo systemctl daemon-reload
sudo systemctl enable chip-lighting-app.service
```

## 起動

事前にUSBハブ、テープライトを接続しておく
```bash
sudo systemctl start chip-lighting-app.service
```
(サンプルアプリが起動できればテープライトが消灯する)

## Alexaアプリから作成したデバイスを登録

Alexaアプリのデバイス追加(+ボタン)からウィザードにしたがってデバイスを追加する
* QRコードはサンプルデフォルトの以下を使用する
    https://project-chip.github.io/connectedhomeip/qrcode.html?data=MT%3A-24J042C00KA0648G00
* 接続するWi-FiはEchoと同じNWを指定する

## デバイスの操作

アプリ上から(他のデバイスと同じように)ON/OFFが可能
※アプリ上は照度やカラーも操作できるがサンプルアプリもハードウェアも対応してないので動作しない
※登録直後の数回はON/OFF操作が効かないことがある

## その他

* 一度設定すれば電源を切ってもOKだが操作可能になるまでかなり時間がかかる
   (コントローラー側がデバイスを認識し直すまでにラグがある...?)
* サンプルアプリを初期化したい場合は/var/tmp/chip_*を削除する
* Callbackに好きな処理書けば良いのでアイデア次第で何でもできそう...!

# 感想

Matterのおかげでデバイスだけの自作でAlexaから使えるスマートライトができました.
スマートデバイス作成にはデバイス管理のサーバ,Alexaスキル等のplatform連系アプリ, デバイスと管理サーバ連系まわりなどが必要だったのでMatterの登場によって非常に敷居が下がったんではないでしょうか.

# 追記

chi-lighting-appがかなり標準出力を吐くのでログまわりを抑制しないとディスクを使い切ります(ました).
以下の様に jounalログの最大サイズを制限し、rsyslog側へのログ転送も停止すると良いです.
 /etc/systemd/journald.conf
  ```ini
[Journal]
SystemMaxUse=50M
ForwardToSyslog=no
```
変更後はOS/journaldの再起動で設定を反映させます.

# 参考

https://github.com/project-chip/connectedhomeip

https://community.arm.com/arm-community-blogs/b/internet-of-things-blog/posts/matter-development-ble-wifi-arm-virtual-hardware

http://www.gniibe.org/development/ac-power-control-by-USB-hub/index.html