dzerik/nivona-ble-emulator
GitHub: dzerik/nivona-ble-emulator
ESP32 BLE 外设模拟器,无需真实咖啡机即可离线开发与调试 Home Assistant 集成及进行 BLE 协议研究。
Stars: 1 | Forks: 0
# Nivona BLE Emulator
[](https://github.com/dzerik/nivona-ble-emulator/actions/workflows/build.yml)
**版本:** 见 [`VERSION`](VERSION) · **更新日志:**
[`CHANGELOG.md`](CHANGELOG.md) · **Tags:** `emu-v`
一个 BLE 外设模拟器,可在 ESP32 上模拟 Nivona NICR/NIVO 咖啡机,用于在没有真机的情况下离线开发 Home Assistant 的 `melitta_barista` 集成。同时也可配合官方 Nivona Android 应用进行协议逆向工程。
实现了完整的 Eugster BLE 协议(service `AD00`)——包括 HU 握手、RC4 帧加密、所有已记录的 `H*` 命令、按系列区分的配方布局,以及模拟冲泡周期的有限状态机。
## 硬件
可在构建时使用 `-DBOARD=` 进行选择(默认为 `xiao_c6`):
| BOARD | Chip | Flash | 备注 |
| ----- | ---- | ----- | ----- |
| `xiao_c6` (默认) | ESP32-C6 | 4 MB | Seeed XIAO ESP32-C6。GPIO3/14 上的 RF 开关(PCB 天线 / 外部 U.FL)。 |
| `xiao_s3` | ESP32-S3 | 8 MB | Seeed XIAO ESP32-S3 / S3 Plus (OPI PSRAM)。 |
| `waveshare_c6_lcd_1_47` | ESP32-C6 | 8 MB | Waveshare ESP32-C6-Touch-LCD-1.47 — 增加了 **触摸屏前面板 UI**(JD9853 LCD + AXS5106L 触摸,LVGL);见[触摸屏 UI](#touchscreen-ui)。 |
开发板配置文件位于 `boards//`(每个都提供了 `board.c`、`sdkconfig.board` 和 `partitions.csv`)。通用配置位于 `sdkconfig.defaults` 中。显示/触摸驱动和触摸屏 UI 仅在开发板配置中设置 `CONFIG_NIVONA_BOARD_HAS_DISPLAY=y` 时才会编译,以确保无头开发板保持精简。
## 功能
| 层级 | 实现状态 |
| -------------- | --------------------- |
| Advertising | 随机静态地址 (F1:…),制造商数据 `0x0D` + 客户过滤器 |
| DIS (`180A`) | 制造商 / 型号 / 序列号 / 硬件 / 固件 / 软件版本号 |
| GATT `AD00` | AD01 (控制), AD02 (通知), AD03 (写入), AD04/5 (存根), AD06 (名称) |
| Security | Just Works 配对 + 绑定 (NVS 持久化) |
| Framing | `S + cmd + [kp] + payload + cs + E`, RC4, MTU 分块, 按命令大小门控 |
| HU 握手 | 完整的双轮验证 + 会话密钥协商 |
| Commands | HX (状态), HV, HL, HI, HS, HR/HW, HA/HB, HE (冲泡), HZ, HY, HD, HN, Hp, HC/HJ (存根) |
| FSM | process / sub_process / info / manipulation / progress |
| 冲泡周期 | READY → PRODUCT,进度 0→100%,异步主动推送 HX |
| Storage | 保存在 NVS 中的数字和字母数字寄存器 |
| 系列切换 | CLI `family` 命令可选择 600/700/79x/900/900-light/1030/1040/8000 |
| 触摸屏 UI | 仅限 Waveshare 开发板 — 在 172×320 LVGL 显示屏上实现实时前面板(状态、冲泡、取消、确认、加水/豆、系列选择) |
## 前置条件
- **ESP-IDF 5.4+** — 已使用 v5.4.1 RISC-V 工具链测试
- 主机对开发板的串口访问权限(原生 USB Serial/JTAG)
- 开发板可连接的 WiFi 网络(用于 OTA 和 telnet)
## 设置
```
# 1. 设置 WiFi 凭据(一次性操作,永不提交)
cp main/wifi_secrets.h.template main/wifi_secrets.h
$EDITOR main/wifi_secrets.h # fill WIFI_SSID / WIFI_PASS
# 2. 构建并烧录
. $IDF_PATH/export.sh
idf.py -DBOARD=xiao_c6 build # default; or xiao_s3 / waveshare_c6_lcd_1_47
idf.py -DBOARD=xiao_c6 -p /dev/ttyACM0 flash monitor
# 切换 board(target/flash 变更):idf.py -DBOARD= fullclean
```
首次通过 USB 刷写后,所有后续更新均可通过空中网络 (OTA) 进行:
```
curl -X POST --data-binary @build/nivona_emulator.bin \
http://nivona-emu.local/ota
```
## 运行时端点
| 端点 | 用途 |
| ------------------------------- | ----------------------------------------------------------- |
| `GET http:///` | 固件版本、IDF 版本、编译时间 |
| `GET http:///diag` | 所有诊断计数器的 JSON (连接、帧、HU、HX 等) |
| `POST http:///ota` | 刷写新的 `nivona_emulator.bin` — 主体为原始二进制文件 |
| `POST http:///reboot` | 重启设备 |
| `telnet 23` | 交互式 CLI — 请参阅下方的命令 |
mDNS:开发板会将自身宣告为 `MDNS_HOSTNAME.local`(默认为 `nivona-emu.local`),因此你可以用它来代替 IP。
## CLI 命令(telnet 或 USB 控制台)
```
help list registered commands
status show FSM state (process, sub_process, manip, progress)
diag show diagnostic counters
brew start a brew cycle with the given process value
cancel cancel active brew
trigger set a manipulation (water_empty / beans_empty /
tray_full / clean / descale / none)
dump dump persisted register store
family switch emulated family — 600 / 700 / 79x / 900 /
900-light / 1030 / 1040 / 8000. Takes effect on reboot.
pair enter pairing mode (wipes bonds, restarts advertising)
forget wipe stored BLE bonds
reboot reboot the device
```
## 触摸屏 UI
在 `waveshare_c6_lcd_1_47` 开发板上,1.47″ 172×320 触摸屏即为机器的前面板——它是与 CLI 和 BLE 操作同步的另一个前端。它使用 LVGL 基于 Espressif 的 `esp_lcd_jd9853`(面板)和 `esp_lcd_touch_axs5106`(触摸)驱动程序(两者均在 `components/` 下进行第三方托管)构建,并且仅在开发板配置设置 `CONFIG_NIVONA_BOARD_HAS_DISPLAY=y` 时才会编译。
- **主页** — 型号 + BLE 连接指示点;实时状态磁贴(READY / 带有进度的 BREWING / 提示横幅);冲泡、取消、确认、加水/豆和系列的磁贴;耗材状态栏。
- **冲泡选择器** — 当前系列的配方;点击开始冲泡。
- **加水/豆** — 水 / 咖啡豆 / 清空残渣盘。
- **系列选择器** — 切换模拟的系列(重启后生效)。
状态从 FSM 中以约 7 Hz 的频率轮询;按钮调用与 telnet/USB CLI 相同的内部 API(`nivona_brew_start`, `nivona_maint_handle_confirm`, `nivona_consumable_set`, `nivona_family_set`)。
## 测试
`tests/test_emulator.py` 中的 Python 测试涵盖了协议辅助程序、HTTP 诊断以及完整的 BLE 往返流程。在已连接的开发板上运行:
```
pip install bleak pytest pytest-asyncio requests
EMU_IP=192.168.1.29 EMU_MAC=F1:32:04:33:52:DA python3 tests/test_emulator.py
# 或
python3 -m pytest tests/ -v -s
```
## 架构
```
boards// per-board profile: board.c (board_hal impl) + sdkconfig.board + partitions.csv
components/
├── esp_lcd_jd9853/ vendored JD9853 LCD panel driver (Espressif, Apache-2.0)
├── esp_lcd_touch_axs5106/ vendored AXS5106L touch driver (Espressif, Apache-2.0)
└── nivona_ui/ LVGL touchscreen front panel (display boards only)
main/
├── main.c application entry: lifecycle, board_early_init(), SM config
├── nivona_ble.c/h advertising, GAP events, scan response
├── nivona_gatt.c/h GATT service AD00 with 6 characteristics
├── nivona_dis.c/h Device Information Service (180A)
├── nivona_frame.c/h framing (S/E, checksum, RC4, per-cmd size gating)
├── nivona_crypto.c/h RC4 + HU verifier + HU lookup table
├── nivona_dispatch.c/h command router with HU / HX / HV / HL / HI / HR / HW / …
├── nivona_fsm.c/h process state machine (thread-safe)
├── nivona_store.c/h numerical + alphanumeric register store with NVS
├── nivona_brew.c/h async brew cycle task, unsolicited HX pushes
├── nivona_cli.c/h esp_console REPL (USB Serial/JTAG)
├── nivona_wifi.c/h WiFi STA + mDNS
├── nivona_ota.c/h HTTP server (status / diag / ota / reboot)
└── nivona_telnet.c/h TCP:23 bridge to esp_console + log mirroring
```
## 协议参考
- 上游逆向工程(实地笔记 + 参考客户端):
`https://github.com/mpapierski/esp-coffee-bridge` — 特别是 `docs/NIVONA.md`
- HA 集成 — 协议常量的真实来源:
`../custom_components/melitta_barista/protocol.py`
`../custom_components/melitta_barista/brands/nivona/`(包,按系列拆分为 `_family_.py`;加密在 `_crypto.py` 中,寄存器基类在 `_registers.py` 中,序列号前缀到系列的映射在 `_prefixes.py` 中)
## 安全说明
- `main/wifi_secrets.h` 已被 `.gitignore` 忽略。切勿提交它。
- 该模拟器接受任何配对请求(Just Works)。请勿将其暴露在不受信任的网络中。
- OTA 没有身份验证。请将端口 80 保留在受信任的 LAN 中。
## 许可证
MIT — 见 [`LICENSE`](LICENSE)。与父级 `melitta-barista-ha` 集成相匹配。
## 法律与商标
这是一个研究和互操作性工具。它与 Nivona Apparate GmbH 或 Eugster / Frismag AG **没有**任何附属、认可或赞助关系。“Nivona”、“NICR”和“NIVO”是其各自所有者的商标,此处仅出于描述性目的使用 — 有关完整的立场声明、欧盟/美国/俄罗斯的法律依据以及处理任何合法关切的合作渠道,请参阅 [`LEGAL.md`](LEGAL.md)。
## 安全
漏洞报告请通过 GitHub Security Advisories 提交,请勿作为公开 issue 提出 — 详见 [`SECURITY.md`](SECURITY.md)。
## 贡献
有关构建快速入门、分支/提交规范和 PR 检查清单,请参阅 [`CONTRIBUTING.md`](CONTRIBUTING.md)。
标签:ESP32, 协议逆向, 客户端加密, 嵌入式开发, 智能家居, 物联网, 蓝牙低功耗(BLE), 逆向工具