ESP32-DevKitCとmatterでiOSの『ホーム』アプリ対応デバイスを作る

# Matter
iOSもMatterに対応したことでHomeKitに対応していなくてもMatter対応であればHomeアプリに対応できます。

https://developer.apple.com/jp/apple-home/matter/

ESP32のMatterライブラリがあるらしいのでマイコンESP32-DevKitCでMatterデバイスを作ってみます。
ボタンを押してLEDが点灯／消灯を切り替えます。iPhone側でタッブをしても切り替わります。

<iframe width="560" height="315" src="https://www.youtube.com/embed/im5_-XdhVe8?si=c5APwg4K5nkNSWHU" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

# ESP32-DevKitC-32E
https://akizukidenshi.com/catalog/g/gM-15673/

USBケーブルのドライバーはこちら

https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers?tab=downloads

ドライバをインストールしたらCOMポート番号をデバイスマネージャーで確認。

![dev_mgrスクリーンショット.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65554/5c9b739b-3c59-98a2-d470-e060740ede84.png)

COM5のようです。

# Arduino IDEをC++17対応にする
ESP32ボードのplatform.txtを探す。このファイルの場所はプラットフォームによって異なります。
Windowsの場合
C:\Users<USER>\AppData\Local\Arduino15\packages\esp32\hardware\esp32\2.0.11\platform.txt
platform.txtの中でstd=gnu++11を検索して-std=gnu++17に変更する。
Arduino IDEを再起動します。

# ESP32のMatterライブラリ

https://github.com/Yacubane/esp32-arduino-matter/releases

ここ↑からダウンロードします。メニューのスケッチ→ライブラリをインクルード→.zip形式のライブラリをインストール…を選んで、ダウンロードしたライブラリのzipを選択します。
メニューのツール→Partition SchemeをMinimal SPIFFSに設定します。最初この設定をしていなくてコンパイル後の書き込みでエラーになりました。

# 回路図
抵抗値にはあまり意味はないです。手持ちの抵抗を使ったので。
R1は270Ωぐらいの方がちょうど良い明るさかもしれません。R2とかは10kΩぐらいが普通かと。
マイコン側でプルアップされているIOだとR2は要らないかも。
マイコンのピン名は面倒なんで20番以降付けてないです。

![esp32_matter_light_回路図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65554/b1b60c5a-04cc-fdde-a236-62b4eaadcfdb.jpeg)

# ブレッドボード
マイコンの絵がただのICになってます。Windows版のfritzingのパーツのパーツエディタが起動しないバグがありまして。ICの絵をそのまま使っています。
Mac版のfritzingでは問題なさそうですけどね。

![esp32_matter_light_ブレッドボード.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/65554/e33b4d5c-1e05-e3ac-cb10-dbdb21746baf.jpeg)

# マイコンボードを選択
ボードマネージャーで`esp32`をインストール。
`esp32`の中の`ESP32-WROOM-DA Module`を選択。

# 接点バウンス対策ライブラリ
https://esp32io.com/tutorials/esp32-button-debounce

ライブラリマネージャーから、ezButtonライブラリをインストール。
本当は接点バウンス対策なんて簡単なんで自分で書いたら良いんですけどね。面倒なので。
自分で書きたい方は ファイル → スケッチ例 → 02.Digital → Debounce　を参考に。

# ソースコード
下のソースコードをマイコンに書込みます。上で調べたポートCOM5で通信速度は115200bps

```cpp:Light.ino
#include "Matter.h"
#include <app/server/OnboardingCodesUtil.h>
#include <credentials/examples/DeviceAttestationCredsExample.h>
#include <ezButton.h>

using namespace chip;
using namespace chip::app::Clusters;
using namespace esp_matter;
using namespace esp_matter::endpoint;

/**
 * This program presents example Matter light device with OnOff cluster by
 * controlling LED with Matter and toggle button.
 *
 * If your ESP does not have buildin LED, please connect it to LED_PIN
 *
 * You can toggle light by both:
 *  - Matter (via CHIPTool or other Matter controller)
 *  - toggle button (by default attached to GPIO0 - reset button, with debouncing) 
 */

// Please configure your PINs
const int LED_PIN = 25;
ezButton button(34);  // create ezButton object that attach to pin;

// Cluster and attribute ID used by Matter light device
const uint32_t CLUSTER_ID = OnOff::Id;
const uint32_t ATTRIBUTE_ID = OnOff::Attributes::OnOff::Id;

// Endpoint and attribute ref that will be assigned to Matter device
uint16_t light_endpoint_id = 0;
attribute_t *attribute_ref;

// There is possibility to listen for various device events, related for example to setup process
// Leaved as empty for simplicity
static void on_device_event(const ChipDeviceEvent *event, intptr_t arg) {}
static esp_err_t on_identification(identification::callback_type_t type, uint16_t endpoint_id,
                                   uint8_t effect_id, uint8_t effect_variant, void *priv_data) {
  return ESP_OK;
}

// Listener on attribute update requests.
// In this example, when update is requested, path (endpoint, cluster and attribute) is checked
// if it matches light attribute. If yes, LED changes state to new one.
static esp_err_t on_attribute_update(attribute::callback_type_t type, uint16_t endpoint_id, uint32_t cluster_id,
                                     uint32_t attribute_id, esp_matter_attr_val_t *val, void *priv_data) {
  if (type == attribute::PRE_UPDATE && endpoint_id == light_endpoint_id &&
      cluster_id == CLUSTER_ID && attribute_id == ATTRIBUTE_ID) {
    // We got an light on/off attribute update!
    bool new_state = val->val.b;
  }
  return ESP_OK;
}

void setup() {
  Serial.begin(115200);

  // Enable debug logging
  esp_log_level_set("*", ESP_LOG_DEBUG);

  // Setup Matter node
  node::config_t node_config;
  node_t *node = node::create(&node_config, on_attribute_update, on_identification);

  // Setup Light endpoint / cluster / attributes with default values
  on_off_light::config_t light_config;
  light_config.on_off.on_off = false;
  light_config.on_off.lighting.start_up_on_off = false;
  endpoint_t *endpoint = on_off_light::create(node, &light_config, ENDPOINT_FLAG_NONE, NULL);

  // Save on/off attribute reference. It will be used to read attribute value later.
  attribute_ref = attribute::get(cluster::get(endpoint, CLUSTER_ID), ATTRIBUTE_ID);

  // Save generated endpoint id
  light_endpoint_id = endpoint::get_id(endpoint);
  
  // Setup DAC (this is good place to also set custom commission data, passcodes etc.)
  esp_matter::set_custom_dac_provider(chip::Credentials::Examples::GetExampleDACProvider());

  // Start Matter device
  esp_matter::start(on_device_event);

  // Print codes needed to setup Matter device
  PrintOnboardingCodes(chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE));

  pinMode(LED_PIN, OUTPUT);
  button.setDebounceTime(100); // set debounce time to 100 milliseconds
}

// Reads light on/off attribute value
esp_matter_attr_val_t get_onoff_attribute_value() {
  esp_matter_attr_val_t onoff_value = esp_matter_invalid(NULL);
  attribute::get_val(attribute_ref, &onoff_value);
  return onoff_value;
}

// Sets light on/off attribute value
void set_onoff_attribute_value(esp_matter_attr_val_t* onoff_value) {
  attribute::update(light_endpoint_id, CLUSTER_ID, ATTRIBUTE_ID, onoff_value);
}

// When toggle light button is pressed (with debouncing),
// light attribute value is changed
void loop() {
  button.loop(); // MUST call the loop() function first
  // Read actual on/off value, invert it and set
  esp_matter_attr_val_t onoff_value = get_onoff_attribute_value();
  if(button.isPressed() && button.getState() == 0) {
    onoff_value.val.b = !onoff_value.val.b;
    set_onoff_attribute_value(&onoff_value);
  }
  if (onoff_value.val.b) {
    digitalWrite(LED_PIN, HIGH);  // turn the LED on (HIGH is the voltage level)
  } else {
    digitalWrite(LED_PIN, LOW);   // turn the LED off by making the voltage LOW
  }

}
```

LEDとボタンのGPIO番号は上の回路通りなら25と34ですが、変えたい方は下の箇所を変更です。

```cpp
// Please configure your PINs
const int LED_PIN = 25;
ezButton button(34);  // create ezButton object that attach to pin;
```


# QRコードを取得する
Homeにmatterデバイスとして登録するにはQRコードか11桁の数字が必要です。
Arduino IDEのシリアルモニタ(あるいはお好みのシリアルコンソール)でESPマイコンから流れてくる出力から下の箇所を探します。`***`な箇所は実際は英数字です。
`https://project-chip.github.io/connectedhomeip/qrcode.html?data=**%***.***`... の箇所をコピーし、WebブラウザのURLにペーストするとQRコードが表示されます。
QRコードを使わなくても11桁の数字のコードを入力する事でも登録できます。
`Manual pairing code: [************]`の***********の箇所は11桁の数字になっています。この数字を入力するのです。

```console
19:58:11.236 -> WIFI_EVENT_STA_START
19:58:11.236 -> I (1703) chip[SVR]: Copy/paste the below URL in a browser to see the QR Code:
19:58:11.236 -> I (1704) chip[DL]: Done driving station state, nothing else to do...
19:58:11.236 -> I (1716) chip[DL]: WIFI_EVENT_STA_CONNECTED
19:58:11.236 -> I (1720) chip[DL]: WiFi station state change: Connecting -> Connecting_Succeeded
19:58:11.236 -> I (1727) chip[SVR]: https://project-chip.github.io/connectedhomeip/qrcode.html?data=**%***.*******************
19:58:11.267 -> I (1728) chip[DL]: WiFi station state change: Connecting_Succeeded -> Connected
19:58:11.267 -> I (1744) chip[DL]: WiFi station interface connected
19:58:11.267 -> I (1749) chip[SVR]: I (1749) chip[ZCL]: WiFiDiagnosticsDelegate: OnConnectionStatusChanged
:
:
19:58:11.333 -> I (1800) chip[IM]: Resuming 2 subscriptions in 0 seconds
19:58:11.333 -> Manual pairing code: [************]
```

# Homeアプリにデバイス登録
Homeアプリ上部の＋をタップし、「アクセサリを追加」を選択。
カメラ画像が出るので上でWebブラウザに出したQRコードを映します。
あるいは、`Manual pairing code: [************]`の11桁の数字を入力します。
後はデバイス名とか、どの部屋かとか聞いてくるので適当に。