开始前适用开发板:ESP32-P4 + ESP32-C5 · 开发框架:ESP-IDF
适用开发板:P4C5 · 工程
04.application.ksdiy_smart_panel· targetesp32p4
前置:Wi-Fi Station · MIPI 屏
学习时长:约 45 分钟 · 难度:★★★★★
P4C5 独有:Korvo 无此例程
学习目标
完成本节后,你将能够:
- 理解面向 Home Assistant 的嵌入式客户端架构
- 掌握 WebSocket + REST API 双通道与 HA 通信的方式
- 了解 128 实体注册表 + 观察者模式的状态管理
- 学会复杂 LVGL 9 多页面 UI 的设计(仪表盘/灯光/环境/摄像头/语音/设置)
- 理解 A/B OTA 双分区的固件升级机制
1. 这个例子在干嘛
一个面向 Home Assistant 的 本地化智能家居中控屏——竖屏 480×800 MIPI 显示仪表盘、灯光控制、环境监控、MIPI 摄像头预览、离线语音指令、设置与 OTA。所有操作通过局域网 HA API 完成,不依赖云端。
生活类比:你家墙上的智能面板,但底层代码你都能看到和修改。
┌─────────────────────────────────────┐
│ P4C5 中控屏 480×800 │
│ ┌───────────────────────────────┐ │
│ │ 仪表盘:时钟 + 设备汇总 │ │
│ │ 4 场景快捷 + 5 导航入口 │ │
│ ├───────────────────────────────┤ │
│ │ 灯光 │ 环境 │ 监控 │语音│设置 │ │
│ └───────────────────────────────┘ │
│ ↕ WebSocket + REST │
│ Home Assistant :8123 │
│ (局域网内的智能家居服务器) │
└─────────────────────────────────────┘
2. 编译烧录
cd P4_C5_4.3_Firmware\04.application.ksdiy_smart_panel
idf.py set-target esp32p4
idf.py menuconfig
idf.py build flash monitor
menuconfig 关键配置
| 菜单 | 选项 | 默认值 | 说明 |
|---|---|---|---|
| KSDIY 智能家居中控屏 | ESP_WIFI_SSID | myssid | 路由器 SSID |
ESP_WIFI_PASSWORD | mypassword | 密码 | |
ESP_MAXIMUM_RETRY | 10 | Wi-Fi 重连次数 | |
KSDIY_PANEL_ENABLE_VOICE | y | 启用离线语音 | |
KSDIY_PANEL_ENABLE_CAMERA | y | 启用 MIPI CSI 摄像头 | |
KSDIY_PANEL_DEFAULT_HA_URL | http://homeassistant.local:8123 | HA 地址 | |
KSDIY_PANEL_DEFAULT_OTA_URL | — | OTA 固件 URL | |
KSDIY_PANEL_HA_RECONNECT_MIN/MAX_MS | 3000/60000 | HA 重连退避 | |
| ESP-SR | WakeNet | wn9_hilexin | 「嗨,乐鑫」 |
| MultiNet | mn7_cn_quant | 中文命令 |
3. 首次使用
3.1 HA 准备
- 局域网内运行 Home Assistant
- HA → 用户 → 长期访问令牌 → 创建并复制
- 记下 HA 地址(如
http://192.168.1.100:8123)
3.2 设备配置
- 烧录后设备尝试连 Wi-Fi
- 连接失败 → 进入 屏上配网(扫描 AP、输入密码)
- 连上 Wi-Fi 后进入设置页 → 填入 HA URL 和 Token
- 保存后 WebSocket 连接 HA → 仪表盘实时显示设备状态
4. 运行现象
| 阶段 | 屏幕 | 串口 |
|---|---|---|
| 启动 | LVGL UI 加载 | NVS/SPIFFS/LVGL init |
| 配网中 | AP 列表+密码输入 | WiFi scan/connect |
| Wi-Fi 就绪 | 状态栏 WiFi 图标亮 | GOT_IP |
| HA 连接 | 状态栏 HA 绿色 | WS: auth_ok |
| 设备同步 | 仪表盘显示灯光/传感器数 | REST: loaded N entities |
| 灯光控制 | 滑条/开关响应 | POST: light.turn_on |
| 语音唤醒 | — | WakeNet: detected |
5. 与 Home Assistant 的三通道通信
这是本例程最核心的架构设计。
5.1 架构总览
┌─────────── Home Assistant (局域网 :8123) ───────────┐
│ │
│ WebSocket: subscribe "state_changed" ← 增量推送 │
│ REST GET: /api/states ← 首次全量 │
│ REST POST: /api/services/<domain>/<service> ← 控制 │
│ │
└─────────────────────────────────────────────────────┘
│ │ ▲
ha_ws_supervisor ha_rest_bootstrap ha_rest_worker
│ │ ▲
└──────────► ha_model (128 实体 + 8 观察者) ◄── UI 页面
5.2 WebSocket 读路径
1. 连接 ws://host:8123/api/websocket
2. 收到 auth_required → 发送 {"type":"auth","access_token":"..."}
3. auth_ok → subscribe_events "state_changed"
4. 收到 event → 解析 new_state → ha_model_upsert()
5. 断线 → 指数退避重连(3s ~ 60s)
5.3 REST 控制(写路径)
ha_client_call_service("light", "turn_on", "light.ke_ting", {"brightness":200})
→ 入队(深度 8 的异步队列)
→ ha_rest_worker 消费
→ POST /api/services/light/turn_on
Authorization: Bearer <token>
Content-Type: application/json
{"entity_id":"light.ke_ting","brightness":200}
5.4 便捷 API
| 函数 | 用途 |
|---|---|
ha_client_toggle(entity_id) | 切换开关 |
ha_client_turn(entity_id, on/off) | 明确开/关 |
ha_client_set_brightness(entity_id, 0~255) | 设置亮度 |
ha_client_activate_scene(entity_id) | 激活场景 |
6. UI 页面架构
6.1 页面导航模型
主屏 (page_home)
├─ [0] page_lights 灯光控制(最多 24 路)
├─ [1] page_climate 环境监控(温湿度 + 空调)
├─ [2] page_camera MIPI CSI 摄像头预览
├─ [3] page_voice 语音状态 + 命令列表
└─ [4] page_settings HA 配置 / 亮度 / OTA
每次进入子页面 create() 重建 → 离开 destroy() 释放,返回主屏 refresh()。
6.2 主仪表盘
- 大时钟(Montserrat 48 + 中文字体,1s 定时器)
- 设备汇总:灯光/空调/传感器数量
- 场景快捷按钮(4 个:回家/离开/观影/就寝)
- 5 路导航卡片
6.3 灯光页
- 遍历
HA_DOMAIN_LIGHT,每行:名称 + Switch + 亮度 Slider - 操作 →
ha_client_turn()/ha_client_set_brightness() - WS 推送 → 实时同步开关与滑条
6.4 监控页
- 懒加载:进入才初始化摄像头
- SC2336 640×480 RGB565,esp_timer 33ms 刷新(~30fps)
- 离开页面自动停止释放资源
7. 离线语音控制
「嗨乐鑫」→ 播放 /spiffs/wozai.wav
→ MultiNet 6 秒窗口 → 识别命令
→ voice_bindings_lookup(command_id) → HA 服务调用
→ 播放 haode.wav(成功)/ shibai.wav(失败)
默认 12 条语音绑定
| ID | 短语 | HA 动作 |
|---|---|---|
| 0~1 | 打开/关闭客厅灯 | light.ke_ting |
| 2~3 | 打开/关闭卧室灯 | light.wo_shi |
| 4~5 | 打开/关闭全部灯 | light.all |
| 6~9 | 回家/离开/观影/就寝 | scene.* |
| 10~11 | 打开/关闭空调 | climate.ke_ting |
需按实际 HA entity_id 修改
voice_bindings.c中的映射表,同时在 ESP-SR menuconfig 中配置对应拼音命令词。
8. 常见问题与排查
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 无法连 HA | URL 格式不对 | 不要漏 http:// 和端口号 |
| 无法连 HA | Token 过期/错误 | HA 重新生成长期令牌 |
状态栏 HA! 红色 | 鉴权失败 | Token 复制完整(含前缀) |
状态栏 HA… 黄色 | 正在连接/重连 | 等待退避重连 |
状态栏 HA-- 灰色 | 未配置 | 进设置页填 URL+Token |
| 设备状态不更新 | entity_id 不对 | HA 开发者工具确认 ID |
| 灯控无效 | HA 中设备离线 | 检查 HA 设备状态 |
| 摄像头无图 | FPC 排线 | 检查 CSI 连接 |
| 语音无反应 | ESP-SR 未配置命令词 | menuconfig → ESP-SR 配置拼音 |
| 中文方块 | 字体未生成 | 按 font/GENERATE_FONT.md 生成 |
9. 动手改造建议
- 新增设备页面:窗帘(cover 域)、风扇(fan 域)
- 场景编辑:UI 上可选场景而非硬编码
- 语音绑定 UI:设置页中可视化配置命令词→entity 映射
- 可视对讲:MIPI CSI + MJPEG 推流 + HA 集成
- 中文 TTS:接入 ESP-SR TTS 或 HTTP 语音合成
10. 知识延伸
- WebSocket vs REST 轮询:WS 推送实时变化,REST 只用于首次引导和控制命令。这种混合架构是智能家居客户端的最佳实践
- 观察者模式:
ha_model_subscribe()最多 8 个监听者,每个 UI 页面注册一个。状态变化时自动回调刷新 UI - A/B OTA:两个固件分区交替写入,失败可回滚——
CONFIG_APP_ROLLBACK_ENABLE=y - ESP-SR 双任务:feed 在 Core0(不阻塞网络),detect 在 Core1(不阻塞 UI)
11. 下一步
| 方向 | 推荐例程 | 说明 |
|---|---|---|
| 天气时钟 | Weather Clock | 另一个产品级应用 |
| OTA 基础 | OTA | 理解 OTA 流程 |
| MQTT | MQTT | 另一种 IoT 通信方式 |
| Wi-Fi 基础 | Wi-Fi Station | C5 联网流程 |
酷世DIY · Kevincoooool