Open Thread Boarder Routerの製作
IKEAのMatter対応デバイスをホームオートメーションシステムで利用し、スマートフォンなどから操作するには、通常はIKEA純正ハブであるDIRIGERAと、純正アプリIKEA Home smart(デバイス管理・操作アプリ)を使用します。
一方、Matterはホームオートメーション機器向けの共通規格であるため、Apple HomePod / Apple TV、Google Nest Hub、Amazon 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 TV、Google Nest Hub、Amazon 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変換)
動作環境
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コマンドが通りません。
ターミナル:ヘルプ
$ pio --help
platformio.iniで指定したデバイス専用のビルドファイルsdkconfig.esp32-c6-devkitc-1が自動生成されます。このファイルを直接編集しても構いませんが、
Platform -> Run Menuconfig
でビルド条件を変更・追加できます。
ターミナル:設定メニュー表示
$ 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のローカルアドレスが付与されます。
STAモード後ルータのIPを確認してルータ管理ページへアクセスできます。
http://192.168.xx.xx
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
- 画面右下 Add Integration ボタンをクリック
- リストから OpenThread Border Router を選択
- OTBRのURI http://192.168.xx.xx を入力
Thread
-
Settings > Devices & services
-
画面右下 Add Integration ボタンをクリック
-
リストから Thread を選択
-
画面指示に従って下さい(上記のOTBRによる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
Matterデバイス動作確認
2つのデバイスのみしか試していませんが、壁越しでのレスポンスも良く無反応なケースはありませんでした。5-6台前後のデバイスであれば、今回製作したOTBRで使用上特に問題は発生しないと考えます(電波強度ムラ・干渉などにより多少の反応遅れが発生するケースがあるかもしれません)。
Matterでは、リモコンスイッチのパターン認識を一回クリック、ニ回クリック、長押しーリリースと3パターン認識できるようになっています。
LED照明については、輝度、色合い、ON/OFFまでの移行時間、調光など多岐に渡る制御が可能です。
Home Assistantのオートメーションルールにより、上記デバイスの認識パターンを組合せ好みの制御が実現できます。
最後に
トラブルシュートや内容のアップデートについては下記フォーラムを参照願います。