项目摘要与亮点
本项目面向本地化智能家居控制场景,围绕 `Thread`、`Matter` 与本地控制平台展开,构建了一条从无线接入、协议控制、家庭控制中心到触控交互终端的完整技术链路。项目采用 `ESP32-H2` 作为 `Thread RCP`,在 `Raspberry Pi 5` 上运行 `OTBR + Matter Server + Home Assistant`,以 `ESP32-C5` 作为 `Matter over Thread` RGB 灯终端,并以 `M5Stack Tab5` 作为本地可视化触控面板,实现了“本地组网、本地控制、本地显示”的智能家居原型系统。
项目已完成的核心成果如下:
- 成功设计PCB并制做ESP32-H2最小系统。
- 成功建立基于 `ESP32-H2 + Raspberry Pi 5` 的本地 `Thread Border Router`
- 成功实现 `Tab5` 通过本地 `MQTT` 对灯进行开关、亮度与颜色控制
- 成功将 `ESP32-C5` 板载 `WS2812` 作为 `Matter` 灯接入 `Home Assistant`
- 成功实现局域网内完整闭环控制,不依赖任何云端服务
本项目的重点不在于单一开发板功能演示,而在于完成一套具有标准协议基础、具备扩展能力并可在真实家庭网络中运行的本地智能家居系统验证。
相较于单点功能型作品,本项目的亮点主要体现在以下四个方面:
1. 完整性
项目不是单独点亮一个终端,而是完整打通了 `Thread Border Router -> Matter Controller -> Matter End Device -> 本地控制面板` 的整条链路。
2. 标准化
无线终端侧采用 `Matter over Thread` 标准协议,而非私有通信协议,具备较好的通用性和后续扩展基础。
3. 本地化
整个系统以本地部署为主,核心控制链路不依赖云端服务,具备较强的自治能力。
4. 工程化
终端层、网络层、控制层与交互层分工清晰,具备继续演化为自定义硬件和多设备系统的条件。
器件选型思路
- `ESP32-H2` 支持 `IEEE 802.15.4`,适合作为 `Thread RCP`
- `ESP32-C5` 能力适合承担 `Matter over Thread` 终端角色
- `Raspberry Pi 5` 算力和系统完整性适合承载 `HA`、`OTBR` 和 `Matter Server`
- `M5Stack Tab5` 集成屏幕、触摸、Wi-Fi,适合作为本地可视化控制终端
分层设计思路
项目将整个系统划分为三个层次:
- 终端层:`ESP32-C5 Matter Light`
- 控制与网络层:`OTBR + Matter Server + Home Assistant`
- 交互层:`Tab5 MQTT 控制面板`
采用该分层方案有以下优势:
- 终端设备侧走标准化协议,便于未来扩展更多 `Matter over Thread` 设备
- 控制能力集中收敛到 `Home Assistant`,统一管理自动化与实体状态
- 面板侧聚焦于交互体验,避免在 UI 终端上重复实现完整 Matter 控制栈
系统总体框图

- `Raspberry Pi 5` 负责承载本地控制与服务层
- `ESP32-H2` 负责 Thread 无线侧接入
- `ESP32-C5` 负责终端设备角色
- `Tab5` 负责本地触摸交互
数据流示意图

用户在 `Tab5` 上操作,控制命令通过 `MQTT` 进入 `HA`,再由 `Matter Server` 和 `OTBR` 传递到 `C5` 灯终端,设备状态最后再回到 `HA` 和 `Tab5`。
硬件连接示意

原理图和PCB设计介绍
原理图设计基于ESP32-H2芯片,在保留一定拓展能力的基础上,尽可能减少体积与复杂度,以匹配简单的RCP需求。

拓展引脚按微型开发板的方式设计在两边,以保留一定的可拓展性。


软件主要代码说明
树莓派 + ESP32-H2的物联网中枢及Boarder Router
配置docker compose:
services:
otbr:
image: openthread/border-router:latest
container_name: otbr
restart: unless-stopped
network_mode: host
privileged: true
devices:
- /dev/ttyAMA0:/dev/ttyACM0
- /dev/net/tun:/dev/net/tun
environment:
OT_RCP_DEVICE: spinel+hdlc+uart:///dev/ttyACM0?uart-baudrate=460800
OT_INFRA_IF: eth0
OT_THREAD_IF: wpan0
OT_LOG_LEVEL: "6"
volumes:
- ./runtime/otbr:/data
matter-server:
image: ghcr.io/home-assistant-libs/python-matter-server:stable
container_name: matter-server
restart: unless-stopped
network_mode: host
security_opt:
- apparmor:unconfined
volumes:
- /run/dbus:/run/dbus:ro
- ./runtime/matter-server:/data
command:
- --storage-path
- /data
- --paa-root-cert-dir
- /data/credentials
matter-thread-sync:
build:
context: .
dockerfile: sync.Dockerfile
container_name: matter-thread-sync
restart: unless-stopped
network_mode: host
depends_on:
- matter-server
environment:
HA_THREAD_DATASET_FILE: /ha-config/.storage/thread.datasets
MATTER_SERVER_INFO_URL: http://127.0.0.1:5580/info
MATTER_SERVER_WS_URL: ws://127.0.0.1:5580/ws
SYNC_INTERVAL_SECONDS: "15"
volumes:
- /docker/homeassistant/config:/ha-config:ro
部署完后,可以用下面方法来逐个检查,检查OTBR:
docker logs otbr --tail=100
docker exec otbr ot-ctl state
docker exec otbr ot-ctl dataset active -x
预期看到:
- `otbr-agent successfully started`
- `Radio Co-processor version: ... esp32h2`
- `ot-ctl state` 为 `leader`
检查 Matter Server:
docker logs matter-server --tail=100
curl -s http://127.0.0.1:5580/info
预期看到:
- `Matter Server` 正常启动
- `thread_credentials_set: true`
接下来需要在我们自制的ESP32-H2上刷入RCP固件,刷写代码我已经直接写成脚本,可以自动吧ESPIDF中的ot_rcp例程刷入:
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"
if [[ $# -lt 1 ]]; then
echo "用法: $0 <serial-port> [baud-rate]" >&2
exit 1
fi
PORT="$1"
BAUD_RATE="${2:-460800}"
if [[ ! -e "${PORT}" ]]; then
echo "串口不存在: ${PORT}" >&2
exit 1
fi
if [[ -n "${ESP_IDF_PATH:-}" ]]; then
IDF_PATH_CANDIDATE="${ESP_IDF_PATH}"
elif [[ -d "./tools/esp-idf-v5.5.1" ]]; then
IDF_PATH_CANDIDATE="./tools/esp-idf-v5.5.1"
else
echo "未找到 ESP-IDF。请先导出 ESP_IDF_PATH。" >&2
exit 1
fi
if [[ ! -f "${IDF_PATH_CANDIDATE}/export.sh" ]]; then
echo "ESP-IDF 路径无效: ${IDF_PATH_CANDIDATE}" >&2
exit 1
fi
EXAMPLE_DIR="${IDF_PATH_CANDIDATE}/examples/openthread/ot_rcp"
if [[ ! -d "${EXAMPLE_DIR}" ]]; then
echo "未找到 ot_rcp 示例: ${EXAMPLE_DIR}" >&2
exit 1
fi
BUILD_DIR="${PROJECT_ROOT}/runtime/build-h2-rcp"
mkdir -p "${BUILD_DIR}"
# shellcheck source=/dev/null
source "${IDF_PATH_CANDIDATE}/export.sh" >/dev/null
idf.py -C "${EXAMPLE_DIR}" -B "${BUILD_DIR}" set-target esp32h2
idf.py -C "${EXAMPLE_DIR}" -B "${BUILD_DIR}" build
idf.py -C "${EXAMPLE_DIR}" -B "${BUILD_DIR}" -p "${PORT}" -b "${BAUD_RATE}" flash
echo "RCP 固件已刷入 ${PORT}"
echo "如需调整 RCP 串口路径,请同步更新 compose.yaml 中 otbr 的 devices 配置。"
运行以下命令刷入:
./scripts/flash_h2_rcp.sh /dev/ttyUSB0
刷完后把ESP32-H2的UART0串口连接到树莓派上,一个Boarder Router和物联网核心就已经完成了。

一个大坑
HA中原生使用matter的方案是在HA OS下的,由于我的HA是部署在docker环境中,因此在这里做了个最小同步器来workaround,也就是matter-thread-sync容器,这个同步器只做一件事,读取 HA 的 thread.datasets,当外置 matter-server 丢失 Thread dataset 时自动重新注入。如果未来docker版的HA修复了这个问题,就不再需要matter-thread-sync这个服务。
TAB5人机交互控制器
程序优先使用现成的 `ArduinoHA` 库,不自己手搓 HA MQTT discovery。
但因为目标是“控制现有的 HA 灯实体 `light.test_product`”,而不是“把 Tab5 自己当一盏灯直接接入 HA”,所以仍然保留了一层非常小的自定义 topic 协议:
- `tab5/light/test_product/set`
- `tab5/light/test_product/state`
控制命令:
{"state":"ON"}
{"state":"OFF"}
{"state":"ON","brightness":180}
{"state":"ON","rgb":[255,140,48]}
{"request":"state"}
状态回传:
{"state":"ON","brightness":180,"rgb":[255,140,48]}
代码比较长就不贴在这里了,整个platformio项目可以从项目附件中看到,修改自己的服务器账号密码以及WIFI信息后刷入就可以。
基于ESP32-C5的终端设备
这部分代码也是基于ESP-IDF的官方库esp-matter-c5修改得到,具体代码就不再赘述,可以在项目附件中看到。只要修改app_config.h中的引脚后烧录就可以使用,编译与烧录命令如下:
source ./tools/esp-idf-v5.5.1/export.sh
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor
硬件功能演示

Tab5刷好程序后界面如上,可以用它来控制C5上的WS2812B灯珠。在下面的HA设备界面上也可以看到C5灯珠的被控状态。

所有用到的硬件如下:

详细的操作演示可以参考视频。
心得体会
借助这次活动的机会,不但深度体验了一把最新的设备ESP32-P4和ESP32-C5,还把MQTT, WIFI, THREAD, MATTER多个技术栈合并打通,收获满满!