# 配套 ESP32 固件

版本 `watcher-pet-sdk-0.3.2`，用于已验证的 WatcheRobot ESP32-S3、412×412 圆屏及现有 STM32 输入链路。它通过通用 WRU1 USB 帧承载 SDK 消息，提供 JPEG 显示会话和带呈现序号的实体输入。它不会在机器人上运行完整 Electron 桌宠。

## 应用广场关联

社区固件 ID 为 `local.watcher_desktop_pet_usb`，版本为 `0.3.2`；配套 App 0.1.2 将兼容范围锁定为 `==0.3.2`。发行元数据在 `marketplace.json`，由 `tools/package_firmware.py` 生成独立固件 ZIP。它不会把固件 ZIP 塞进 App 源码仓库，也不会上传、烧录或改写源二进制。

完成编译后，在应用目录打包到一个尚不存在的文件：

```powershell
& ./.venv/Scripts/python.exe tools/package_firmware.py --image firmware/build/watcher_pet_sdk.bin --output dist/firmware-0.3.2.zip
& ./.venv/Scripts/watcherobot.exe firmware check dist/firmware-0.3.2.zip --jsonl
```

ZIP 只包含 `firmware.json`、`flash_args.txt` 和 `application.bin`，仅声明 `0x10000` 这一处写入。完整第三方许可与修改说明来自 `DISTRIBUTION_NOTICES.txt`，随每个 ZIP 以 `flash_args.txt` 的 `# ` 注释行提供，SDK 解析刷写参数时会忽略注释。当前包格式不接受额外说明文件，不能直接向 ZIP 添加 LICENSE 或删除许可来缩小文件；打包工具会在完整说明超过 SDK 元数据限制时拒绝打包。

打包会限制镜像不超过已验证的 4 MiB 应用分区，并使用 SDK 的实际固件包检查器核对元数据、分段和摘要；同一输入得到相同 ZIP，不覆盖已有发行文件。检查成功只证明包结构与摘要，不替代硬件、分区及恢复条件核对。

本次候选包内的 application 镜像 SHA-256 为 `414ba6204bed64b59f3cfb3d9ab9d4511dc7a5529decf63b63a832960d9c531b`，与已完成实屏验收的固件相同。硬件修订尚未确认，`marketplace.json` 的 `unknown` 是未知状态，不能用来推定其他修订兼容。本地打包不是上传或审核通过。

## 编译

使用 **ESP-IDF 6.0.2**。硬件 SDK 是外部构建依赖，不随本 App 发布；从公开仓库获取固定快照：

```powershell
git clone --no-checkout https://github.com/orulink-ai/WatcheRobot-public.git vendor
git -C vendor checkout --detach 5dfd9caa0c820b143a8c178831041f1062aa4679
git -C vendor apply ../patches/vendor-changes.patch
idf.py build
```

上述命令在 `firmware` 目录执行。`patches/vendor-changes.patch` 为两处显示 DMA 配置修改及文件内修改声明；不能省略，否则不是本项目验证的源码组合。也可以通过 CMake 参数 `-DWATCHE_HW_SDK_ROOT=<完整目录>` 指向已应用同一补丁的固定快照。组件版本在 `main/idf_component.yml`、`dependencies.lock` 固定；首次构建需要下载公开组件。重新编译不保证二进制逐字节相同。

硬件 SDK、ESP-IDF 和组件保留各自授权。SDK 实际 LICENSE 与标准 Apache-2.0 文本有差异，本发行保留其原文，不替换或重新授权。许可原文允许源代码或目标代码再分发，须保留相应文本、版权及修改说明；本固件通过上述随包材料提供。JPEG 解码库限定用于 Espressif 产品，本镜像目标为 ESP32-S3。详细范围见 `DISTRIBUTION_NOTICES.txt` 和 [第三方说明](../THIRD_PARTY_NOTICES.md)。成功编译不代表能在其他硬件版本使用。

## 刷写边界

本项目没有一键通用刷写脚本。当前验证设备的 application 位于 `0x10000`，分区容量 `0x400000`；**这不能代替对目标设备实际分区表的检查**。

刷写前需确认芯片型号与设备身份，读取实际分区表，备份包含原应用及必要配置的 Flash，并用设备读回校验备份。备份可能包含设备凭据，只保存在私有目录。确认原应用镜像可恢复后，才可仅写入目标 application 分区，并校验镜像摘要及启动状态。

不要直接使用 IDF 输出的整套 `flash` 命令：本次集成只写 ESP32 application，不修改 bootloader、partition table、NVS、STM32 或 Himax。新镜像必须小于实际 application 分区。其他硬件或分区布局尚未验证。

恢复前先停止本应用并断开 SDK USB 连接，然后把刷写前保存的**同一台设备**应用镜像写回已核对的 application 分区，验证并重启。不要发布或复用含 NVS 的整机备份。

## 协议

USB UART：2,000,000 baud，8N1。主机在打开串口前关闭 DTR/RTS，避免串口握手线触发复位。主机连接参数必须与固件一致；0.3.1 固件固定为 1,000,000 baud，没有自动速率协商。回退至旧固件时，也要把主机改回对应速率。

0.3.2 沿用 0.3.1 的逐片 ACK、CRC 和 UART 有效字节读取，提升串口速率；与已通过实屏验收的 `0.3.2-baud2m-test` 相比，运行源码仅更新版本标识。2 Mbps 的连续呈现测试和人工操作确认见 [应用说明](../README.md)。

- WRU1：20 字节 little-endian `<4sBBHIII>`；magic、kind、flags、fragment index、frame id、payload length、CRC32。每片最多 1024 字节，逻辑帧最多 131072 字节。主机逐片等待传输 ACK；传输层不解析显示业务。
- 能力：`display.stream.v1`、`input.events.v1`、`input.display_context.v1`。
- `ctrl.display.open`：412×412、JPEG，返回显示会话、最大帧大小及租约。
- WSPK type 8：会话号和递增序号，JPEG 支持 206×206 或 412×412；前者由固件放大，后者用于文字菜单。
- `evt.display.presented`：真正呈现后返回 SDK 序号及设备序号，失败带错误原因。一次仅一帧在途。
- 输入带 `stream_id`、`display_sequence`、`age_ms` 和事件 ID，避免使用陈旧菜单位置执行操作。
- `ctrl.display.close`：仅关闭所拥有的会话。10 秒未呈现新帧则租约过期，清屏并结束输入上下文。

原生协议测试在 `tests/test_usb_wire.c`，覆盖 CRC、重复片、乱序、超时、边界和空帧。固件构建、Flash 校验、真实 SDK 连续呈现与人工实屏操作应分别验收。
