FICUSONLINE F9E
IKEA Matterデバイスに対応した単体のESP32-C6によるOpen Thread Boarder Routerの製作
Open Thread Border Router (OTBR) を用意すれば、Home AssistantをはじめとするMatter対応のホームオートメーションシステムで、メーカーを問わずThread対応Matterデバイスを登録・管理・操作することができます。そのためのOTBRを単体のESP32-C6で製作します。
Takanobu FuseAdministrator

2 months ago

Hardware

Open Thread Boarder Routerの製作

Thread Network & OTBR

IKEAのMatter対応デバイスをホームオートメーションシステムで利用し、スマートフォンなどから操作するには、通常はIKEA純正ハブであるDIRIGERAと、純正アプリIKEA Home smart(デバイス管理・操作アプリ)を使用します。

一方、Matterはホームオートメーション機器向けの共通規格であるため、Apple HomePod / Apple TVGoogle Nest HubAmazon Echoなど、Matterに対応した他社製ハブにもIKEAのMatter対応デバイスを直接登録できます。その場合でもスマートフォン用アプリからデバイスの管理・操作が可能です。(ただしIKEA Home smartアプリは利用できません)。

Matterでは、通信プロトコルとしてThreadを採用しています。ThreadはIPv6をベースとした低消費電力のメッシュネットワークであり、通常のWi-Fiネットワークとは独立して動作するため、Wi-Fi機器からThreadデバイスへ直接アクセスすることはできません

そのため、Threadネットワークと家庭内のIPネットワーク(Wi-FiやEthernet)との橋渡しを行う OpenThread Border Router(OTBR) が必要になります。

IKEA純正のDIRIGERAをはじめ、Apple HomePod / Apple TVGoogle Nest HubAmazon EchoなどのMatter対応ハブには、OTBR機能があらかじめ搭載されています。そのため、これらのハブを介してもThread対応のMatterデバイスを検出し、管理・操作することができます。

言い換えれば、OTBRを用意すれば、Home AssistantをはじめとするMatter対応のホームオートメーションシステムで、メーカーを問わずThread対応Matterデバイスを登録・管理・操作することができます。

そのためのOTBRをESP32で製作します。ベースとしたコードは、Espressif SystemsESP32が提供しているOTBRサンプルコードですが、これはWifiとThreadネットワークをそれぞれ独立させて機能するように2つのESP32による構成を前提条件としているため、これを単独のESP32-C6のみで動作するようにし実用上問題ないかどうか検証します。

注) ESP32-C6はWi-Fi(2.4GHz)とThread(802.15.4, 2.4GHz)が同じ2.4GHz帯のRFを共有しているため、同時使用時は干渉を避けるためコエグジスタンス(共存)制御が行われます。このためスループットが下がる場合があります。

注) Wi-FiとThreadを独立して運用するため、ホスト(Wi-Fi担当)にESP32系チップRCP(Radio Co-Processor、Thread担当)にESP32-H2をUART接続した構成がメーカ推奨です。

開発環境

VS-Code + PlatformIP

ESP32-C6: esp32-c6-devkitc-1(PCとの接続はUSB-TypeC <-> Serial変換)

ESP32-C6-DevKitC-1

動作環境

Home Assistant コンテナ、Matter Serverコンテナ、Nginxコンテナ

サンプルコードのダウンロード

ベースとなるサンプルコードをダウンロード

$ git clone https://github.com/espressif/esp-thread-br.git

コード編集

ESP32-C6単体で動作させるため下記のファイルを追加・編集します。

platformio.ini: 新規作成

PlatformIOの設定ファイルをプロジェクトフォルダとなるesp-thread-br/examples/basic_thread_border_routerに作成します。

esp-thread-br/examples/basic_thread_border_router/platformio.ini

[platformio]
src_dir = main

[env:esp32-c6-devkitc-1]
platform = https://github.com/platformio/platform-espressif32.git
board = esp32-c6-devkitc-1
framework = espidf

board_upload.flash_size = 16MB
board_upload.maximum_size = 16777216

; ポートとリセットの設定
upload_port = /dev/ttyACM0
monitor_port = /dev/ttyACM0
monitor_speed = 115200

upload_flags =
    --before=default_reset
    --after=hard_reset

extra_scripts = pre:build_spiffs.py

board_build.partitions = partitions.csv

board_build.embed_files =
    ../../components/esp_ot_br_server/favicon.ico
board_build.embed_txtfiles =
    server_certs/ca_cert.pem
    ../../components/esp_ot_br_server/frontend/wifi_configuration.html

esp-thread-br/examples/basic_thread_border_router/main/esp_ot_br.c

@@ -46,7 +46,7 @@
 #endif
 #if CONFIG_OPENTHREAD_BR_START_WEB
     esp_vfs_spiffs_conf_t web_server_conf = {
-        .base_path = "/spiffs", .partition_label = "web_storage", .max_files = 10, .format_if_mount_failed = false};
+        .base_path = "/spiffs", .partition_label = "web_storage", .max_files = 10, .format_if_mount_failed = true};
     ESP_RETURN_ON_ERROR(esp_vfs_spiffs_register(&web_server_conf), TAG, "Failed to mount web storage");
 #endif
     return ESP_OK;

@@ -60,7 +60,7 @@
     // * border router
     size_t max_eventfd = 3;

-#if CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
+#if CONFIG_OPENTHREAD_RADIO_NATIVE || CONFIG_OPENTHREAD_RADIO_SPINEL_SPI
     // * SpiSpinelInterface (The Spi Spinel Interface needs an eventfd.)
     max_eventfd++;
 #endif

esp-thread-br/examples/basic_thread_border_router/main/esp_ot_config.h

@@ -18,7 +18,12 @@

 #define RCP_FIRMWARE_DIR "/spiffs/ot_rcp"

-#if CONFIG_OPENTHREAD_RADIO_SPINEL_UART
+#if CONFIG_OPENTHREAD_RADIO_NATIVE
+#define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG()              \
+    {                                                      \
+        .radio_mode = RADIO_MODE_NATIVE,                   \
+    }
+#elif CONFIG_OPENTHREAD_RADIO_SPINEL_UART
 #define ESP_OPENTHREAD_DEFAULT_RADIO_CONFIG()              \
     {                                                      \
         .radio_mode = RADIO_MODE_UART_RCP,                 \
@@ -47,9 +52,9 @@
             .dma_channel = 2,                              \
             .spi_interface =                               \
                 {                                          \
-                    .mosi_io_num = CONFIG_PIN_TO_RCP_MOSI, \
-                    .miso_io_num = CONFIG_PIN_TO_RCP_MISO, \
-                    .sclk_io_num = CONFIG_PIN_TO_RCP_SCLK, \
+                    .mosi_io_num = 11, \
+                    .miso_io_num = 12, \
+                    .sclk_io_num = 13, \
                     .quadwp_io_num = -1,                   \
                     .quadhd_io_num = -1,                   \
                 },                                         \
@@ -59,7 +64,7 @@
                     .input_delay_ns = 100,                 \
                     .mode = 0,                             \
                     .clock_speed_hz = 2500 * 1000,         \
-                    .spics_io_num = CONFIG_PIN_TO_RCP_CS,  \
+                    .spics_io_num = 10,  \
                     .queue_size = 5,                       \
                 },                                         \
             .intr_pin = CONFIG_PIN_TO_RCP_BOOT,            \

esp-thread-br/examples/basic_thread_border_router/partitions.csv

@@ -1,9 +1,8 @@
-# Name,   Type, SubType, Offset,  Size, Flags
-# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap
-nvs,        data, nvs,      , 0x6000,
-otadata,    data, ota,      , 0x2000,
-phy_init,   data, phy,      , 0x1000,
-ota_0,      app,  ota_0,    , 2M,
-ota_1,      app,  ota_1,    , 2M,
-web_storage,data, spiffs,   , 200K,
-rcp_fw,     data, spiffs,   , 640K,
+# Name,       Type, SubType, Offset,   Size,    Flags
+nvs,          data, nvs,     0x9000,   0x10000,
+otadata,      data, ota,     0x19000,  0x2000,
+phy_init,     data, phy,     0x1b000,  0x1000,
+ota_0,        app,  ota_0,   0x20000,  4M,
+ota_1,        app,  ota_1,   0x420000, 4M,
+web_storage,  data, spiffs,  0x820000, 2M,
+rcp_fw,       data, spiffs,  0xa20000, 2M,

esp-thread-br/examples/basic_thread_border_router/sdkconfig.defaults

@@ -1,10 +1,10 @@
 # ESP32-S3 is the host SoC on ESP-Thread-Border-Router board
-CONFIG_IDF_TARGET="esp32s3"
+CONFIG_IDF_TARGET="esp32-c6-devkitc-1"

 #
 # Serial flasher config
 #
-CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
+CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
 # end of Serial flasher config

 #
@@ -46,14 +46,14 @@
 CONFIG_OPENTHREAD_BORDER_ROUTER=y
 CONFIG_OPENTHREAD_CLI_OTA=y
 CONFIG_OPENTHREAD_RCP_COMMAND=y
-CONFIG_OPENTHREAD_RADIO_SPINEL_UART=y
+###CONFIG_OPENTHREAD_RADIO_SPINEL_UART=y
 CONFIG_OPENTHREAD_TASK_SIZE=8192
 # end of OpenThread

 #
 # OpenThread Border Router Example
 #
-CONFIG_AUTO_UPDATE_RCP=y
+###CONFIG_AUTO_UPDATE_RCP=y
 # end of OpenThread Border Router Example

 #
@@ -98,16 +98,16 @@
 #
 # Ethernet
 # 
-CONFIG_EXAMPLE_USE_W5500=y
-CONFIG_EXAMPLE_ETH_SPI_HOST=2
-CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO=21
-CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO=45
-CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO=38
-CONFIG_EXAMPLE_ETH_SPI_CS_GPIO=41
-CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ=36
-CONFIG_EXAMPLE_ETH_SPI_INT_GPIO=39
-CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=40
-CONFIG_EXAMPLE_ETH_PHY_ADDR=1
+# CONFIG_EXAMPLE_USE_W5500=y
+# CONFIG_EXAMPLE_ETH_SPI_HOST=2
+# CONFIG_EXAMPLE_ETH_SPI_SCLK_GPIO=21
+# CONFIG_EXAMPLE_ETH_SPI_MOSI_GPIO=45
+# CONFIG_EXAMPLE_ETH_SPI_MISO_GPIO=38
+# CONFIG_EXAMPLE_ETH_SPI_CS_GPIO=41
+# CONFIG_EXAMPLE_ETH_SPI_CLOCK_MHZ=36
+# CONFIG_EXAMPLE_ETH_SPI_INT_GPIO=39
+# CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=40
+# CONFIG_EXAMPLE_ETH_PHY_ADDR=1
 # end of Ethernet

 #

esp_projects/esp-thread-br/examples/basic_thread_border_router/sdkconfig.defaults

@@ -124,6 +124,9 @@

         // Stop SoftAP mode
         esp_br_wifi_config_stop();
+        
+        esp_wifi_stop();
+        esp_wifi_deinit();
 #else
         // Standard Wi-Fi connection mode - get from Kconfig
         strncpy(wifi_ssid, CONFIG_EXAMPLE_WIFI_SSID, sizeof(wifi_ssid) - 1);

ビルド条件設定

PlatfornIOの各コマンドボタンは、画面左側のPlatformIOアイコンをクリックすると一覧が表示されます。ターミナルで実行する場合は、必ず画面下部のターミナルアイコンをクリックして下さい。画面上部のTerminal->New Terminalではpioコマンドが通りません。

VS-Code: PlatformIO

ターミナル:ヘルプ

$ pio --help

platformio.iniで指定したデバイス専用のビルドファイルsdkconfig.esp32-c6-devkitc-1が自動生成されます。このファイルを直接編集しても構いませんが、

Platform -> Run Menuconfig

でビルド条件を変更・追加できます。

PlatformIO Menu Config

ターミナル:設定メニュー表示

$ pio run -t menuconfig

以下項目の該当箇所のチェックを確認

(Top) → ESP Thread Border Router Example

    Border router board type (Standalone dev kits)  --->
    Board Configuration  --->
    Border router RCP target (ESP32-C6)  --->
[*] Enable the automatic start mode in Thread Border Router. (default value)
[*] Enable the web server in Thread Border Router. (default value)
[*] Enable SoftAP Wi-Fi configuration mode (default value)
(Top) → Component config → ESP-STDIO

    Channel for console output (Default: UART0)  --->
    Channel for console secondary output (USB_SERIAL_JTAG PORT)  --->
(Top) → Serial flasher config

[ ] Disable download stub (default value)
    Flash SPI mode (DIO)  --->
    Flash Sampling Mode (STR Mode)  --->
    Flash SPI speed (80 MHz)  --->
    Flash size (16 MB)  --->
[*] Detect flash size when flashing bootloader (default value)
    Before flashing (Reset to bootloader)  --->
    After flashing (Reset after flashing)  --->
(Top) → Config for OpenThread Examples

[*] Enable the automatic start mode of Thread network.
    External Console Commands  --->
(Top) → Component config → Wi-Fi

[ ] WiFi AMPDU TX
[ ] WiFi AMPDU RX

(20) Minimum active time (default value)
(Top) → Component config → OpenThread → OpenThread → Thread Core Features

[*] Enable timing optimization
(Top) → Component config → OpenThread → OpenThread → Thread Core Features → OpenThread Stack Parameters

(20) The size of max MLE children entries

(8) Maximum backoffs times before declaring a channel access failure.

ビルド&アップロード

General->Buildでビルドがスタートします。

ターミナル:ビルド

$ pio run 

General -> Upload で書込みスタート

ターミナル:アップロード

$ pio run --target upload

Wifi設定やThreadネットワークの設定をするためのウェブUIが用意されていますが、別途以下コマンドでバイナリイメージを生成・フラッシュへの書込みをする必要があります。

SPIFFSイメージの生成

$ python ~/.platformio/packages/framework-espidf/components/spiffs/spiffsgen.py 0x200000 \
../../components/esp_ot_br_server/frontend \
.pio/build/esp32-c6-devkitc-1/web_storage.bin

spiffsgen.py はESP-IDFに付属するツールで、指定したディレクトリの中身をSPIFFS(SPI Flash File System)形式のイメージファイルにまとめます。

  • 0x200000: 生成するSPIFFSイメージのサイズ(バイト単位の16進数)。この場合は 2,097,152 バイト = 2MB を指定しています。パーティションテーブルpartitions.csvで定義したSPIFFS用パーティションのサイズと一致させる必要があります。
  • ../../components/esp_ot_br_server/frontend: イメージ化する元のディレクトリ。Web UI用のHTML/CSS/JSファイルなどが入っています。
  • .pio/build/esp32-c6-devkitc-1/web_storage.bin: 出力先のバイナリファイル名。ビルド後にこのファイルとしてSPIFFSイメージが生成されます。

フラッシュへの書き込み

$ python ~/.platformio/packages/tool-esptoolpy/esptool.py --chip esp32c6 -p /dev/ttyACM0 \
-b 460800 write_flash 0x820000 .pio/build/esp32-c6-devkitc-1/web_storage.bin

esptool.py はEspressif製チップにシリアル経由でファームウェアやデータを書き込むための標準ツールです。

  • --chip esp32c6: 対象チップがESP32-C6であることを指定。
  • -p /dev/ttyACM0: 書き込みに使うシリアルポート(Linuxのデバイスファイル)。
  • -b 460800: 通信ボーレート(460800bps)。高速書き込みのための設定です。
  • write_flash 0x820000 ...web_storage.bin: 0x820000というフラッシュ上のアドレスに、先ほど生成したweb_storage.binを書き込みます。このアドレスはパーティションテーブルpartitions.csvで定義されたSPIFFS(またはstorage)パーティションの開始オフセットと一致している必要があります。

各種コマンド操作

ビルド後、シリアル通信で動作確認が出来ます。General -> Monitor

ターミナル:デバイスモニター

$ pio device monitor
esp32c6> ot wifi state
esp32c6> ot state
esp32c6> ot help

OTBRの起動

書込み後、OTBRはアクセスポイントモードで起動します。PCやモバイルで、このアクセスポイントへ接続しブラウザで以下のURLへアクセスして下さい。自宅LANのSSIDとパスワード入力し “Save Wi-Fi Configuration” をクリックすると自動でステーションモードに切り替わり、LANのローカルアドレスが付与されます。

http://192.168.4.1

OTBR AP Mode

STAモード後ルータのIPを確認してルータ管理ページへアクセスできます。
http://192.168.xx.xx

OTBR STA Mode

Home Assistantへ登録

事前に、Settings —> Device & Services—> Integrations で Open Thread Matter Router Thread, Matter Server の登録を行って下さい。

Matter Serverは予めHome Assistantとは別コンテナで稼働させています。

https://github.com/matter-js/matterjs-server

Open Thread Matter Router

  • Settings > Devices & services

HA Device & Services

HA Add Integrations

  • リストから OpenThread Border Router を選択

Select OTBR

OTBR URL

Thread

  • Settings > Devices & services

  • 画面右下  Add Integration ボタンをクリック

  • リストから Thread を選択

  • 画面指示に従って下さい(上記のOTBRによるThreadネットワークが選択されます)

Thread

Matter Server

  • Settings > Devices & services

  • 画面右下  Add Integration ボタンをクリック

  • リストから Matter -> Matter を選択

  • MatterサーバのURL: ws://localhost:5580/ws を入力

Matterデバイスの登録

Matterデバイスの登録は、Home Assistantのモバイルアプリ経由でのみ可能です。

注) 携帯のWiFiとESP32-C6によるOTBRは、同じ2.4GHz帯のSSIDである必要があります。

IKEA Matter対象デバイス

  • KAJPLATSLED bulb E17 810 lumen, smart white spectrum/globe/opal white
  • BILRESA Remote control, white smart/dual button

IKEA Matter Devices

Companion -> Trouble Shootings

Trouble Shottings -> Thread Sync

Add Matter Device 001

Add Matter Device 002

Add Matter Device load QR Code

Add Matter Device Start

Registered Matter Devices

Matterデバイス動作確認

2つのデバイスのみしか試していませんが、壁越しでのレスポンスも良く無反応なケースはありませんでした。5-6台前後のデバイスであれば、今回製作したOTBRで使用上特に問題は発生しないと考えます(電波強度ムラ・干渉などにより多少の反応遅れが発生するケースがあるかもしれません)。

Matterでは、リモコンスイッチのパターン認識を一回クリック、ニ回クリック、長押しーリリースと3パターン認識できるようになっています。

IKEA BILRESA

LED照明については、輝度、色合い、ON/OFFまでの移行時間、調光など多岐に渡る制御が可能です。

IKEA KAJPLATS

IKEA KAJPLATS Home Assistant Control

Home Assistantのオートメーションルールにより、上記デバイスの認識パターンを組合せ好みの制御が実現できます。

最後に

トラブルシュートや内容のアップデートについては下記フォーラムを参照願います。

https://forum.ficusonline.com/t/topic/562