tirtc-device-builder 0.8.0 → 0.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +13 -0
- package/README.md +19 -4
- package/package.json +1 -1
- package/skills/tirtc-esp32-builder/SKILL.md +19 -8
- package/skills/tirtc-esp32-builder/USAGE.md +14 -0
- package/skills/tirtc-esp32-builder/VERSION +1 -1
- package/skills/tirtc-esp32-builder/assets/lckfb-szpi-esp32s3-portable-prompt.md +4 -4
- package/skills/tirtc-esp32-builder/assets/report-template.md +4 -1
- package/skills/tirtc-esp32-builder/references/environment.md +8 -2
- package/skills/tirtc-esp32-builder/references/firmware-delivery.md +83 -0
- package/skills/tirtc-esp32-builder/references/porting-risks.md +29 -1
- package/skills/tirtc-esp32-builder/references/product-controls.md +76 -0
- package/skills/tirtc-esp32-builder/references/reporting.md +7 -3
- package/skills/tirtc-esp32-builder/references/workflow.md +11 -0
- package/skills/tirtc-esp32-builder/scripts/firmware_identity.py +180 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
This project follows Semantic Versioning.
|
|
4
4
|
|
|
5
|
+
## 0.8.1
|
|
6
|
+
|
|
7
|
+
- Separate rapid `idf.py flash` iteration from portable evidence-bundle export,
|
|
8
|
+
so active development keeps the normal `build/` tree and does not require a
|
|
9
|
+
new multi-BIN archive for every device test.
|
|
10
|
+
- Add deterministic ESP-IDF application-descriptor inspection for explicit
|
|
11
|
+
firmware versions, application-BIN SHA-256, and full ELF SHA-256 matching.
|
|
12
|
+
- Add product-control rules that distinguish MCU buttons, boot straps, reset
|
|
13
|
+
lines and PMIC/power-latch keys across baseboard and battery-carrier variants,
|
|
14
|
+
and route button gestures through the runtime's intent queue.
|
|
15
|
+
- Add trace-based diagnosis for repeated AI/H5 downlink audio across SDK
|
|
16
|
+
callbacks, bounded queues, playback, resampling, and codec/I2S writes.
|
|
17
|
+
|
|
5
18
|
## 0.8.0
|
|
6
19
|
|
|
7
20
|
- Add an evidence-backed board identity and curated registry workflow with exact,
|
package/README.md
CHANGED
|
@@ -549,7 +549,19 @@ python3 ~/.codex/skills/tirtc-esp32-builder/scripts/install_video_gate.py .
|
|
|
549
549
|
idf.py build
|
|
550
550
|
```
|
|
551
551
|
|
|
552
|
-
|
|
552
|
+
只安装实际请求能力的门禁。编译后先核对固件内嵌版本、应用 BIN
|
|
553
|
+
SHA-256 和完整 ELF SHA-256:
|
|
554
|
+
|
|
555
|
+
```bash
|
|
556
|
+
python3 ~/.codex/skills/tirtc-esp32-builder/scripts/firmware_identity.py \
|
|
557
|
+
build/<app>.bin --elf build/<app>.elf --expect-version <expected-version>
|
|
558
|
+
```
|
|
559
|
+
|
|
560
|
+
随后按
|
|
561
|
+
[`firmware-delivery.md`](skills/tirtc-esp32-builder/references/firmware-delivery.md)
|
|
562
|
+
选择快速真机迭代或可移植证据包;该文档也规定从 `build/` 记录迁移到
|
|
563
|
+
`artifacts/` 时如何保持 Hardware IR 和报告一致。编译成功但语义门禁缺失
|
|
564
|
+
或失败时只能记录 `COMPILE_PASS / CAPABILITY_BLOCKED`。
|
|
553
565
|
|
|
554
566
|
生成器会把 TiRTC SDK 复制到工程的 `third_party/tirtc/`。此后工程不再依赖 `tirtc-server-example`,但换机编译仍需准备兼容的 ESP-IDF 5.5.x 工具链。
|
|
555
567
|
|
|
@@ -599,7 +611,10 @@ cd /absolute/path/my-esp32-device
|
|
|
599
611
|
idf.py -p /dev/ttyACM0 flash monitor
|
|
600
612
|
```
|
|
601
613
|
|
|
602
|
-
|
|
614
|
+
普通开发烧录优先使用这一条命令,让 ESP-IDF 从当前构建目录解析
|
|
615
|
+
bootloader、分区表和应用镜像;无需手工维护多条 `esptool.py` 地址参数。
|
|
616
|
+
使用 `Ctrl+]` 退出 ESP-IDF Monitor。多个串口同时存在时,需要通过 USB
|
|
617
|
+
拔插、设备标识或芯片探测确认目标,不能默认烧录第一个端口。
|
|
603
618
|
|
|
604
619
|
### Wi-Fi 凭证和设备绑定
|
|
605
620
|
|
|
@@ -911,8 +926,8 @@ metadata 中的版本、标签、上游 commit 和期望 SHA-256 必须与本地
|
|
|
911
926
|
|
|
912
927
|
```bash
|
|
913
928
|
npm test
|
|
914
|
-
git tag -a v0.8.
|
|
915
|
-
git push origin v0.8.
|
|
929
|
+
git tag -a v0.8.1 -m "v0.8.1"
|
|
930
|
+
git push origin v0.8.1
|
|
916
931
|
```
|
|
917
932
|
|
|
918
933
|
不要重复发布已经存在的 npm 版本。版本变化同步更新 `package.json`、`.codex-plugin/plugin.json` 和发布说明。
|
package/package.json
CHANGED
|
@@ -98,18 +98,29 @@ H5/AI/call runtime behavior, read and install the applicable semantic contracts.
|
|
|
98
98
|
Keep concrete board values in Hardware IR, contracts and adapter files rather
|
|
99
99
|
than Skill defaults.
|
|
100
100
|
|
|
101
|
+
When the product request includes a physical button, touch input, reset, power
|
|
102
|
+
key, wake source or enclosure label, read
|
|
103
|
+
[product-controls.md](references/product-controls.md). Treat the exact physical
|
|
104
|
+
product as baseboard plus any carrier, PMIC and enclosure controls. Board input
|
|
105
|
+
code emits bounded intents to the state-owning runtime; it does not own session
|
|
106
|
+
lifecycle.
|
|
107
|
+
|
|
101
108
|
## 6. Build and assess
|
|
102
109
|
|
|
103
|
-
|
|
110
|
+
Read [firmware-delivery.md](references/firmware-delivery.md) and select
|
|
111
|
+
`development flash` or `evidence bundle` from the user's current goal. Run
|
|
112
|
+
focused tests, resolve managed components, validate the exact SDK
|
|
104
113
|
`manifest/build-contract.env`, install every applicable semantic gate, then run
|
|
105
114
|
the ordinary ESP-IDF build. Requested call/VoIP features must be represented by
|
|
106
115
|
the runtime/business contract and the unified arbiter implementation; an H5/AI-
|
|
107
116
|
only starter cannot reach `BUILD_VERIFIED` for those features.
|
|
108
117
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
118
|
+
Set an explicit firmware version, verify the application descriptor and full
|
|
119
|
+
ELF identity with `firmware_identity.py`, and record the application-BIN hash
|
|
120
|
+
with its exact label. Follow the selected delivery mode's artifact paths and
|
|
121
|
+
retention rules, record actual size and SHA-256 in Hardware IR, then run strict
|
|
122
|
+
build assessment with `--project` and that exact hash. Compiler success with a
|
|
123
|
+
missing feature gate is `COMPILE_PASS`, not product completion.
|
|
113
124
|
|
|
114
125
|
## 7. Flash, verify, and learn
|
|
115
126
|
|
|
@@ -129,9 +140,9 @@ board; attach artifact evidence; add regression tests for generic invariants;
|
|
|
129
140
|
then publish a new Skill/registry version. Never let an installed Skill mutate
|
|
130
141
|
itself from conversation history.
|
|
131
142
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
143
|
+
Complete the selected mode's retention or export checks from
|
|
144
|
+
`firmware-delivery.md`. Return the project, Hardware IR, identity match,
|
|
145
|
+
capability result, exact artifacts, flash command when applicable, and report.
|
|
135
146
|
|
|
136
147
|
## Security boundary
|
|
137
148
|
|
|
@@ -102,6 +102,20 @@ python3 <skill-dir>/scripts/hardware_ir.py assess --phase hil \
|
|
|
102
102
|
|
|
103
103
|
`init` 默认创建 schema v2;v1 仅用于兼容已有 H.264 IR。`BLOCKED` 表示资料已确认硬件/合同/资源或凭证策略不满足;`NEEDS_CONFIRMATION` 表示当前阶段仍有未知项或证据不足;`READY_TO_PORT` 表示可以生成并实现板级适配;`BUILD_VERIFIED` 表示精确 artifact 已通过源码、编译和 post-link 门禁;`HIL_VERIFIED` 还要求同一 SHA-256 的 L5/L6 运行证据。
|
|
104
104
|
|
|
105
|
+
## 固件身份与烧录方式
|
|
106
|
+
|
|
107
|
+
顶层 `CMakeLists.txt` 应在 `project()` 前设置明确的 `PROJECT_VER`。编译后
|
|
108
|
+
核对应用 BIN 中的版本和完整 ELF SHA-256:
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
python3 <skill-dir>/scripts/firmware_identity.py build/<app>.bin \
|
|
112
|
+
--elf build/<app>.elf --expect-version <expected-version>
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
按 [firmware-delivery.md](references/firmware-delivery.md) 选择快速真机迭代或
|
|
116
|
+
可移植证据包;模式转换时必须同步迁移 Hardware IR 与报告中的 artifact
|
|
117
|
+
路径。普通开发烧录命令为 `idf.py -p <exact-port> flash monitor`。
|
|
118
|
+
|
|
105
119
|
## 当前边界
|
|
106
120
|
|
|
107
121
|
ThingConnect 仓库提供 ESP32-S3 H5/AI 模板和生成器,但默认媒体适配器不包含特定开发板的摄像头、麦克风、选定视频路径、Wi-Fi 凭证方法和扬声器驱动。模板生成和编译成功只证明工程与协议骨架可用,不代表 Web 已经出图或 AI 音频已经通过实机验收。
|
|
@@ -1 +1 @@
|
|
|
1
|
-
0.8.
|
|
1
|
+
0.8.1
|
|
@@ -7,7 +7,7 @@ $tirtc-esp32-builder
|
|
|
7
7
|
前置条件(必须由开发者在启动本次 Codex 会话前完成,不属于本提示词内的操作):
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx --yes tirtc-device-builder@0.8.
|
|
10
|
+
npx --yes tirtc-device-builder@0.8.1 setup esp32 --install --force-skill
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
该命令只允许在当前用户目录安装固定版本的 Skill、managed ESP32 Device Kit、ESP-IDF 和工具链;禁止 sudo、系统级包变更和修改 shell profile。安装完成后,开发者必须关闭原 Codex 会话,再从本 clean-room 工作区启动一个新会话,然后粘贴本提示词。
|
|
@@ -15,11 +15,11 @@ npx --yes tirtc-device-builder@0.8.0 setup esp32 --install --force-skill
|
|
|
15
15
|
本轮第一步先只读运行:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
npx --yes tirtc-device-builder@0.8.
|
|
19
|
-
npx --yes tirtc-device-builder@0.8.
|
|
18
|
+
npx --yes tirtc-device-builder@0.8.1 --version
|
|
19
|
+
npx --yes tirtc-device-builder@0.8.1 setup esp32
|
|
20
20
|
```
|
|
21
21
|
|
|
22
|
-
必须根据命令的实际输出和本机文件确认:npm 包为 0.8.
|
|
22
|
+
必须根据命令的实际输出和本机文件确认:npm 包为 0.8.1、已安装 Skill 的 `VERSION` 为 0.8.1、所选 Device Kit 的 `manifest.json` 中 `kit_version` 为 1.1.1,并且 Doctor 对 `--expected-kit 1.1.1` 输出 `OVERALL: PASS`。Plugin manifest 不属于这种 npm 安装方式的运行时前置条件,不得把不可访问的 Plugin 版本当作阻塞项。如果版本不一致、Skill 是在当前会话启动后才安装,或环境检查未通过,停止并报告前置条件不成立;不要在当前会话中替换 Skill 后继续生成工程。
|
|
23
23
|
|
|
24
24
|
工作区与 clean-room 边界:
|
|
25
25
|
- 将启动 Codex 时的当前目录定义为 `WORKSPACE_ROOT`。
|
|
@@ -64,9 +64,12 @@
|
|
|
64
64
|
|
|
65
65
|
## Firmware and flash record
|
|
66
66
|
|
|
67
|
+
- Delivery mode (`development flash` or `evidence bundle`): `{{DELIVERY_MODE}}`
|
|
68
|
+
- Application project/version: `{{FIRMWARE_PROJECT_VERSION}}`
|
|
67
69
|
- Serial port/chip: `{{SERIAL_TARGET}}`
|
|
68
70
|
- Project-relative firmware artifacts: `{{FIRMWARE_ARTIFACTS}}`
|
|
69
|
-
-
|
|
71
|
+
- Application-BIN SHA-256: `{{FIRMWARE_BIN_SHA256}}`
|
|
72
|
+
- Descriptor/full ELF SHA-256: `{{FIRMWARE_ELF_SHA256}}`
|
|
70
73
|
- Flash command/result: `{{FLASH_RESULT}}`
|
|
71
74
|
|
|
72
75
|
## Artifact-bound runtime evidence
|
|
@@ -63,13 +63,19 @@ the packaged S3 archive as a compatible fallback.
|
|
|
63
63
|
|
|
64
64
|
SDK resolution is independent after generation: an explicit `--sdk-dir` wins, followed by `<project>/third_party/tirtc`, then the SDK packaged in the resolved Device Kit or legacy workspace. The generated project remains diagnosable after it is moved away from the Kit.
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
For the evidence-bundle mode defined in
|
|
67
|
+
[firmware-delivery.md](firmware-delivery.md), run:
|
|
67
68
|
|
|
68
69
|
```bash
|
|
69
70
|
python3 <skill-dir>/scripts/project_portability.py <generated-project> --export
|
|
70
71
|
```
|
|
71
72
|
|
|
72
|
-
|
|
73
|
+
Run it on a delivery copy and export source inputs only. CMake caches absolute
|
|
74
|
+
source, toolchain, and Python paths under `build/`; `managed_components/` may be
|
|
75
|
+
regenerated from the committed `dependencies.lock`. The bundled
|
|
76
|
+
`third_party/tirtc` SDK and its build contract must remain in the source package.
|
|
77
|
+
CMake must invoke shell gates through `bash <script>` so the build does not
|
|
78
|
+
depend on archive- or filesystem-specific executable bits.
|
|
73
79
|
|
|
74
80
|
The export check also requires Hardware IR, every requested-feature semantic
|
|
75
81
|
contract, `sdkconfig.defaults`, a referenced custom partition table, and each
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Firmware identity and delivery
|
|
2
|
+
|
|
3
|
+
Read this reference whenever the task builds, flashes, hands off, compares, or
|
|
4
|
+
retains firmware. Select the delivery mode from the user's immediate goal; do
|
|
5
|
+
not force a release bundle onto a quick device-test loop.
|
|
6
|
+
|
|
7
|
+
## Choose one mode explicitly
|
|
8
|
+
|
|
9
|
+
### Development flash
|
|
10
|
+
|
|
11
|
+
Use this mode for rapid build/flash/monitor iteration on one development
|
|
12
|
+
machine. Keep the normal ESP-IDF `build/` tree and use the ordinary command:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
idf.py -p <exact-port> flash monitor
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Do not create a new archive or make the user manually flash bootloader,
|
|
19
|
+
partition-table and application BIN files when `idf.py flash` can resolve the
|
|
20
|
+
build metadata. Before flashing, record the application version and hash of the
|
|
21
|
+
exact application BIN. A build assessment may bind to a project-relative file
|
|
22
|
+
under `build/`; that evidence remains valid only while that exact file exists.
|
|
23
|
+
|
|
24
|
+
If an experiment produces runtime evidence worth retaining, copy the unchanged
|
|
25
|
+
BIN and matching ELF into `artifacts/` before the next rebuild, record their
|
|
26
|
+
sizes and hashes, and update the report. Copying does not create a new firmware
|
|
27
|
+
identity; a rebuild does.
|
|
28
|
+
|
|
29
|
+
### Evidence bundle
|
|
30
|
+
|
|
31
|
+
Use this mode for a portable handoff, release candidate, rollback image,
|
|
32
|
+
retained HIL result, or source export. Keep project-relative copies of the exact
|
|
33
|
+
application BIN and ELF, plus bootloader and partition-table files when the
|
|
34
|
+
recipient needs manual flashing. Record byte sizes and SHA-256 values in
|
|
35
|
+
Hardware IR, run strict build assessment, then run
|
|
36
|
+
`project_portability.py --export` on the source-only delivery tree.
|
|
37
|
+
|
|
38
|
+
When promoting a development build into an evidence bundle, copy the unchanged
|
|
39
|
+
files first, then replace every retained `build/...` artifact path in Hardware
|
|
40
|
+
IR and the report with its new `artifacts/...` path. Remove transient build
|
|
41
|
+
records that are not being retained; do not merely add bundle records beside
|
|
42
|
+
stale paths. Rerun strict build assessment against the copied application-BIN
|
|
43
|
+
hash so the assessor reopens the new path. The source-only export is ready only
|
|
44
|
+
when every `build_evidence.artifacts[]` path exists outside `build/` and the
|
|
45
|
+
matching report identifies the same hashes.
|
|
46
|
+
|
|
47
|
+
Only this export workflow requires removal of the machine-bound `build/` tree.
|
|
48
|
+
Do not delete it during active iteration, and never delete a user-owned build
|
|
49
|
+
tree merely to make a report look portable.
|
|
50
|
+
|
|
51
|
+
## Make firmware self-identifying
|
|
52
|
+
|
|
53
|
+
Set an explicit, human-readable `PROJECT_VER` before `project()` in the top-level
|
|
54
|
+
ESP-IDF `CMakeLists.txt`. Keep it at 31 UTF-8 bytes or fewer so it fits the
|
|
55
|
+
`esp_app_desc_t.version` field with its terminator. Use a label that states what
|
|
56
|
+
the image is: for example `audio-repro.2`, `button-test.1`, `fix.3`, or a release
|
|
57
|
+
version. A build that deliberately preserves a known bug is a reproduction or
|
|
58
|
+
diagnostic image, not a fix.
|
|
59
|
+
|
|
60
|
+
At boot, log `project_name`, `version`, build date/time, IDF version, and the
|
|
61
|
+
full ELF SHA-256 from `esp_app_get_description()` or
|
|
62
|
+
`esp_app_get_elf_sha256()`. If the product has a console, expose the same fields
|
|
63
|
+
through `version` or `status`. This lets a tester distinguish a successful
|
|
64
|
+
flash of the wrong image from a runtime regression.
|
|
65
|
+
|
|
66
|
+
Verify the built application BIN before flash or handoff:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
python3 <skill-dir>/scripts/firmware_identity.py build/<app>.bin \
|
|
70
|
+
--elf build/<app>.elf --expect-version <expected-version>
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
The script reads the embedded ESP-IDF application descriptor, verifies the
|
|
74
|
+
explicit version, and confirms that the supplied ELF's full SHA-256 matches the
|
|
75
|
+
descriptor. It accepts an application BIN, not a merged whole-flash image.
|
|
76
|
+
|
|
77
|
+
## Name hashes precisely
|
|
78
|
+
|
|
79
|
+
The outer application-BIN SHA-256 and the descriptor's full ELF SHA-256 identify
|
|
80
|
+
different files. Record both with unambiguous labels; do not call either one
|
|
81
|
+
simply “firmware hash.” Bind Hardware IR artifacts and HIL observations to the
|
|
82
|
+
exact retained artifact SHA required by the assessor, and include the boot-log
|
|
83
|
+
version/full ELF hash so the physical device can be reconciled with that record.
|
|
@@ -62,9 +62,37 @@ Disable Wi-Fi power saving for the realtime baseline unless the product contract
|
|
|
62
62
|
|
|
63
63
|
Confirm SDK send return semantics and callback payload lifetimes from the selected SDK version. Keep SDK callbacks bounded and copy payloads before returning.
|
|
64
64
|
|
|
65
|
+
## Repeated or duplicated downlink audio
|
|
66
|
+
|
|
67
|
+
Do not label a build as a fix merely because it adds logs or makes the symptom
|
|
68
|
+
less frequent. First produce a self-identifying reproduction build, then count
|
|
69
|
+
the same frame across four boundaries: TiRTC receive callback, accepted queue
|
|
70
|
+
item, playback dequeue, and codec/I2S write. Include mode and connection
|
|
71
|
+
generation, stream/media type, payload length, bounded queue depth/drop counters,
|
|
72
|
+
and an available transport sequence or timestamp. Sample payload hashes only as
|
|
73
|
+
diagnostic evidence; do not blindly discard equal hashes because silence or
|
|
74
|
+
legitimately repeated encoded frames may be identical.
|
|
75
|
+
|
|
76
|
+
Carry one local trace ID from the accepted callback through queue and playback,
|
|
77
|
+
and record write offset, requested bytes, returned bytes, and cumulative bytes.
|
|
78
|
+
Multiple codec/I2S writes are valid when a frame is deliberately chunked or a
|
|
79
|
+
partial write advances the offset; a local duplication exists only when the
|
|
80
|
+
same byte/sample range is committed more than once or cumulative output exceeds
|
|
81
|
+
the frame's decoded contract. Repeated callbacks with the same available
|
|
82
|
+
transport identity point upstream or into SDK delivery. Unique downlink frames
|
|
83
|
+
heard repeatedly require checking resampling, DMA/I2S replay and acoustic
|
|
84
|
+
feedback separately. On stop/reconnect, reject stale generations and drain or
|
|
85
|
+
invalidate queued audio. Verify the correction with repeated AI sessions, rapid
|
|
86
|
+
stop/start, delayed callbacks and H5 recovery against the exact reproduction and
|
|
87
|
+
candidate-fix firmware identities.
|
|
88
|
+
|
|
65
89
|
## Artifact discipline
|
|
66
90
|
|
|
67
|
-
|
|
91
|
+
Follow [firmware-delivery.md](firmware-delivery.md). Record the explicit firmware
|
|
92
|
+
version, application-BIN SHA-256 and descriptor/full ELF SHA-256 for every build
|
|
93
|
+
used in HIL. Runtime evidence applies only to the exact artifact. Diagnose one
|
|
94
|
+
failing invariant, make the smallest correction, rerun that layer, and preserve
|
|
95
|
+
the comparison in the report.
|
|
68
96
|
|
|
69
97
|
Local intermediate snapshots may be ignored, but the Hardware IR, semantic
|
|
70
98
|
contracts, `dependencies.lock`, custom partition input, and the artifact named
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# Product controls, reset, and power
|
|
2
|
+
|
|
3
|
+
Read this reference when the product has buttons, touch inputs, reset controls,
|
|
4
|
+
power keys, wake sources, or enclosure labels. Concrete pins and electrical
|
|
5
|
+
behavior belong in the exact board/carrier evidence and adapter, never in this
|
|
6
|
+
Skill.
|
|
7
|
+
|
|
8
|
+
## Identify the physical product, not just the baseboard
|
|
9
|
+
|
|
10
|
+
A retail or battery-powered device may combine a compute board with a carrier,
|
|
11
|
+
PMIC, latch circuit, flex PCB, or enclosure controls that are absent from the
|
|
12
|
+
baseboard schematic. When a user's physical observation conflicts with the
|
|
13
|
+
available schematic, treat it as evidence of a missing or different variant.
|
|
14
|
+
Do not declare a working control nonexistent. Request or inspect the exact
|
|
15
|
+
carrier schematic, PCB markings, photographs, BSP definitions, or safe probe
|
|
16
|
+
results and keep the unresolved mapping explicit.
|
|
17
|
+
|
|
18
|
+
Classify each physical label before assigning software behavior:
|
|
19
|
+
|
|
20
|
+
- MCU-readable GPIO button or touch input;
|
|
21
|
+
- boot-strapping input;
|
|
22
|
+
- reset/enable line;
|
|
23
|
+
- PMIC or hardware power-latch control;
|
|
24
|
+
- I/O-expander input;
|
|
25
|
+
- indicator with no input function;
|
|
26
|
+
- unknown control on a missing board layer.
|
|
27
|
+
|
|
28
|
+
For an MCU-readable input, record the SoC GPIO, active level, external pull,
|
|
29
|
+
debounce requirement, shared owner, wake capability, and any strapping role.
|
|
30
|
+
Holding a strapping pin during reset can select a boot mode. A reset/enable line
|
|
31
|
+
normally resets the MCU and cannot also be treated as an application button.
|
|
32
|
+
A PMIC/latch power key and an MCU “enter deep sleep” action are different
|
|
33
|
+
product contracts; do not substitute one for the other without circuit evidence.
|
|
34
|
+
|
|
35
|
+
## Keep controls behind a narrow adapter
|
|
36
|
+
|
|
37
|
+
Use a board/product-controls component to own GPIO setup, polling or ISR work,
|
|
38
|
+
debounce, gestures, and boot-held suppression. It emits bounded product intents
|
|
39
|
+
such as `AI_TOGGLE_REQUESTED`; it does not call TiRTC, HTTP, MQTT, media, or
|
|
40
|
+
session lifecycle APIs from an ISR or polling callback.
|
|
41
|
+
|
|
42
|
+
Prefer one `AI_TOGGLE_REQUESTED` event whose start/stop decision is serialized by
|
|
43
|
+
the state-owning runtime. If the existing runtime exposes only separate start
|
|
44
|
+
and stop intents, the adapter may map a public state snapshot to one of them,
|
|
45
|
+
but the runtime must validate the intent again against its authoritative state.
|
|
46
|
+
Define whether rapid gestures are queued, coalesced, or rejected so two reads of
|
|
47
|
+
the same stale snapshot cannot silently violate the product behavior.
|
|
48
|
+
|
|
49
|
+
The runtime remains responsible for session generation, callback ordering,
|
|
50
|
+
media ownership, timeouts, cleanup, and late-event rejection. A toggle must
|
|
51
|
+
define behavior for waiting, connecting, active, stopping, H5-owned, and
|
|
52
|
+
error/recovery states instead of maintaining a second Boolean inside the button
|
|
53
|
+
driver.
|
|
54
|
+
|
|
55
|
+
## Deterministic button behavior
|
|
56
|
+
|
|
57
|
+
For a mechanical active-low or active-high input, require all of the following:
|
|
58
|
+
|
|
59
|
+
- a stable active interval before one press event;
|
|
60
|
+
- no repeat event until a stable release;
|
|
61
|
+
- a button already held during boot must be released before it can trigger;
|
|
62
|
+
- short/long-press thresholds have an explicit product meaning;
|
|
63
|
+
- queue overflow or rejected intents are logged and recover safely;
|
|
64
|
+
- polling tasks and ISRs stay bounded and never block on network/session work.
|
|
65
|
+
|
|
66
|
+
Test boot-held, bounce, rapid repeated presses, press during connection, press
|
|
67
|
+
during active audio, press during stop, delayed SDK callbacks, and recovery to
|
|
68
|
+
H5 or waiting state. For a battery product, separately test cold power-on,
|
|
69
|
+
software shutdown/deep sleep, wake, charging/USB behavior, and reset; a passing
|
|
70
|
+
AI toggle does not prove the power path.
|
|
71
|
+
|
|
72
|
+
Keep debounce and gesture state transitions separable from GPIO/RTOS plumbing
|
|
73
|
+
so host tests can prove boot-held suppression, one event per stable press,
|
|
74
|
+
release re-arming, and the selected rapid-gesture policy. Add a focused runtime
|
|
75
|
+
test for stale or duplicate control intents whenever the runtime performs the
|
|
76
|
+
authoritative state validation.
|
|
@@ -16,13 +16,17 @@ Use [the report template](../assets/report-template.md) and preserve separate `P
|
|
|
16
16
|
| L6 Sessions | AI, device-call and WeChat VoIP requested flows work; simultaneous capture/playback, AEC/double-talk, stop, timeout and H5 recovery are observed separately |
|
|
17
17
|
| L7 Stability | Requested weak-network, repeated-session, resource, and soak criteria pass |
|
|
18
18
|
|
|
19
|
-
Run and record the intake assessment before L0, the build assessment with `--project` and the exact artifact SHA-256 at L1, and the HIL assessment only when matching runtime evidence exists.
|
|
19
|
+
Run and record the intake assessment before L0, the build assessment with `--project` and the exact artifact SHA-256 at L1, and the HIL assessment only when matching runtime evidence exists. Follow [firmware-delivery.md](firmware-delivery.md) for artifact location, retention, and mode-transition rules. Record actual byte size and SHA-256 in `build_evidence.artifacts[]`; the assessor reopens the file and rejects stale metadata. Missing serial or browser access is a `SKIP` for the affected L2-L7 levels, not an L0/L1 failure.
|
|
20
20
|
|
|
21
|
-
Keep `COMPILE_PASS` separate from `BUILD_VERIFIED`. When the compiler succeeds but a required runtime, audio, or video semantic gate fails or is missing, record the compiler result and report the feature and project as blocked.
|
|
21
|
+
Keep `COMPILE_PASS` separate from `BUILD_VERIFIED`. When the compiler succeeds but a required runtime, audio, or video semantic gate fails or is missing, record the compiler result and report the feature and project as blocked. H5 image display is L5 evidence, never an inference from L1.
|
|
22
22
|
|
|
23
23
|
## Evidence
|
|
24
24
|
|
|
25
|
-
Record exact board revision, selected media, Wi-Fi, and binding profiles,
|
|
25
|
+
Record exact board revision, selected media, Wi-Fi, and binding profiles,
|
|
26
|
+
toolchain and SDK versions, source/adapter revisions, commands, return codes,
|
|
27
|
+
firmware version, application-BIN SHA-256, descriptor/full ELF SHA-256, serial
|
|
28
|
+
port/chip, sanitized log paths, browser or platform observations, and every
|
|
29
|
+
unavailable dependency. Never conflate the BIN and ELF hashes.
|
|
26
30
|
|
|
27
31
|
Runtime evidence belongs to one artifact. Label superseded artifacts and keep their observations as history; do not promote them to the current BIN/ELF. For HIL assessment, add the full SHA-256 to `runtime_evidence` and run `hardware_ir.py assess --phase hil --artifact-sha256 <sha> --strict`.
|
|
28
32
|
|
|
@@ -30,6 +30,9 @@ Use this branch when the user supplies a board model, vendor URL, schematic, BOM
|
|
|
30
30
|
2. Freeze the user-supplied product contract: selected video profile, audio/stream formats, duplex/AEC policy, supported Wi-Fi credential methods, selected onboarding method, transport staging, output path, and mutation boundary. Use `unknown` where the prompt lacks an answer.
|
|
31
31
|
3. Prefer official schematic/BOM and BSP facts. For a PDF schematic, inspect page labels and net names; prefer an exported netlist, pin CSV, or vendor board definition when available.
|
|
32
32
|
4. Cross-check critical pins, clocks, power enables, reset lines, sensor/codec variants, ESP-IDF version, resource ownership, and onboarding behavior across at least two independent artifacts when possible.
|
|
33
|
+
For buttons, power keys and enclosure labels, identify the full physical
|
|
34
|
+
product including carrier/PMIC layers; a working control missing from the
|
|
35
|
+
baseboard schematic is a variant or evidence gap, not proof that it is absent.
|
|
33
36
|
5. Create the Hardware IR v2. Use `null` for unknown facts and retain contradictory values as an explicit issue instead of selecting one silently. Store concrete board values in the IR/adapter rather than Skill files.
|
|
34
37
|
6. Validate and run the intake assessment. Classify unresolved facts by their next evidence source: source, implementation, build, HIL, or user input. Ask only for `user_blocked` facts that prevent a safe design. SoftAP is optional when another evidenced Wi-Fi credential method is available, keeps credentials outside source, and defines reprovisioning.
|
|
35
38
|
7. When hardware identity, wiring, product contracts, and an evidenced resource plan reach `READY_TO_PORT`, generate the starter and implement the board adapter. Generate a compile-safe adapter by default when remaining uncertainty is implementation-, build-, or HIL-resolvable. Stop at the IR/report only when missing user evidence or an incompatible dependency makes a safe implementation impossible.
|
|
@@ -79,6 +82,14 @@ connection handles, pending calls, monotonic deadlines, session generation, and
|
|
|
79
82
|
H5/AI/CALL/VOIP sequencing. SDK lifecycle changes such as disconnect run in a
|
|
80
83
|
worker/state-machine context, never directly inside an SDK callback.
|
|
81
84
|
|
|
85
|
+
A separate product-controls adapter owns physical input setup, debounce,
|
|
86
|
+
boot-held suppression and gestures. It queues bounded intents into the stable
|
|
87
|
+
runtime. It never manipulates TiRTC/session state directly or keeps a competing
|
|
88
|
+
session Boolean. Prefer an atomic runtime-owned toggle intent; when only explicit
|
|
89
|
+
start/stop intents exist, follow
|
|
90
|
+
[product-controls.md](product-controls.md) for snapshot mapping and authoritative
|
|
91
|
+
runtime revalidation.
|
|
92
|
+
|
|
82
93
|
## Verification loop
|
|
83
94
|
|
|
84
95
|
Use a bounded loop per layer: diagnose one failing invariant, make the smallest correction, and rerun that layer before moving forward. Change one high-risk variable per HIL comparison. Turn reusable invariants into tests or post-link gates. Stop and report when the remaining failure requires unavailable hardware, credentials, a new SDK binary, a public protocol change, or a user choice.
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Inspect the ESP-IDF application descriptor in an application BIN."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
import struct
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
ESP_IMAGE_MAGIC = 0xE9
|
|
16
|
+
ESP_APP_DESC_MAGIC = 0xABCD5432
|
|
17
|
+
IMAGE_HEADER_SIZE = 24
|
|
18
|
+
SEGMENT_HEADER_SIZE = 8
|
|
19
|
+
APP_DESC_OFFSET = IMAGE_HEADER_SIZE + SEGMENT_HEADER_SIZE
|
|
20
|
+
APP_DESC_REQUIRED_SIZE = 176
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def sha256_file(path: Path) -> str:
|
|
24
|
+
digest = hashlib.sha256()
|
|
25
|
+
with path.open("rb") as stream:
|
|
26
|
+
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
|
|
27
|
+
digest.update(chunk)
|
|
28
|
+
return digest.hexdigest()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def descriptor_string(data: bytes, offset: int, size: int, label: str) -> str:
|
|
32
|
+
raw = data[offset : offset + size]
|
|
33
|
+
value = raw.split(b"\0", 1)[0]
|
|
34
|
+
try:
|
|
35
|
+
decoded = value.decode("utf-8")
|
|
36
|
+
except UnicodeDecodeError as exc:
|
|
37
|
+
raise ValueError(f"application descriptor {label} is not UTF-8") from exc
|
|
38
|
+
if any(ord(character) < 0x20 or ord(character) == 0x7F for character in decoded):
|
|
39
|
+
raise ValueError(f"application descriptor {label} contains control bytes")
|
|
40
|
+
return decoded
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def inspect_firmware(
|
|
44
|
+
firmware_path: Path,
|
|
45
|
+
*,
|
|
46
|
+
elf_path: Path | None = None,
|
|
47
|
+
expected_version: str | None = None,
|
|
48
|
+
) -> dict[str, Any]:
|
|
49
|
+
firmware = firmware_path.expanduser().resolve()
|
|
50
|
+
try:
|
|
51
|
+
data = firmware.read_bytes()
|
|
52
|
+
except OSError as exc:
|
|
53
|
+
raise ValueError(f"cannot read application BIN {firmware}: {exc}") from exc
|
|
54
|
+
|
|
55
|
+
minimum_size = APP_DESC_OFFSET + APP_DESC_REQUIRED_SIZE
|
|
56
|
+
if len(data) < minimum_size:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"application BIN is truncated: need at least {minimum_size} bytes"
|
|
59
|
+
)
|
|
60
|
+
if data[0] != ESP_IMAGE_MAGIC:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"not an ESP application image: expected magic 0x{ESP_IMAGE_MAGIC:02x}"
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
segment_length = struct.unpack_from("<I", data, IMAGE_HEADER_SIZE + 4)[0]
|
|
66
|
+
if segment_length < APP_DESC_REQUIRED_SIZE:
|
|
67
|
+
raise ValueError("first ESP image segment is too small for esp_app_desc_t")
|
|
68
|
+
|
|
69
|
+
descriptor = data[APP_DESC_OFFSET : APP_DESC_OFFSET + APP_DESC_REQUIRED_SIZE]
|
|
70
|
+
descriptor_magic = struct.unpack_from("<I", descriptor, 0)[0]
|
|
71
|
+
if descriptor_magic != ESP_APP_DESC_MAGIC:
|
|
72
|
+
raise ValueError(
|
|
73
|
+
"first ESP image segment does not start with a valid esp_app_desc_t"
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
version = descriptor_string(descriptor, 16, 32, "version")
|
|
77
|
+
if not version:
|
|
78
|
+
raise ValueError("application descriptor version is empty")
|
|
79
|
+
if expected_version is not None and version != expected_version:
|
|
80
|
+
raise ValueError(
|
|
81
|
+
f"firmware version mismatch: expected {expected_version!r}, got {version!r}"
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
elf_digest = descriptor[144:176].hex()
|
|
85
|
+
result: dict[str, Any] = {
|
|
86
|
+
"firmware_path": str(firmware),
|
|
87
|
+
"size_bytes": len(data),
|
|
88
|
+
"bin_sha256": hashlib.sha256(data).hexdigest(),
|
|
89
|
+
"segment_count": data[1],
|
|
90
|
+
"project_name": descriptor_string(descriptor, 48, 32, "project_name"),
|
|
91
|
+
"version": version,
|
|
92
|
+
"build_time": descriptor_string(descriptor, 80, 16, "time"),
|
|
93
|
+
"build_date": descriptor_string(descriptor, 96, 16, "date"),
|
|
94
|
+
"idf_version": descriptor_string(descriptor, 112, 32, "idf_ver"),
|
|
95
|
+
"app_elf_sha256": elf_digest,
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if elf_path is not None:
|
|
99
|
+
elf = elf_path.expanduser().resolve()
|
|
100
|
+
try:
|
|
101
|
+
elf_size = elf.stat().st_size
|
|
102
|
+
actual_elf_digest = sha256_file(elf)
|
|
103
|
+
except OSError as exc:
|
|
104
|
+
raise ValueError(f"cannot read ELF {elf}: {exc}") from exc
|
|
105
|
+
if actual_elf_digest != elf_digest:
|
|
106
|
+
raise ValueError(
|
|
107
|
+
"ELF SHA-256 mismatch: application descriptor records "
|
|
108
|
+
f"{elf_digest}, file is {actual_elf_digest}"
|
|
109
|
+
)
|
|
110
|
+
result.update(
|
|
111
|
+
{
|
|
112
|
+
"elf_path": str(elf),
|
|
113
|
+
"elf_size_bytes": elf_size,
|
|
114
|
+
"elf_sha256": actual_elf_digest,
|
|
115
|
+
}
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return result
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def print_human(result: dict[str, Any]) -> None:
|
|
122
|
+
fields = (
|
|
123
|
+
("firmware", "firmware_path"),
|
|
124
|
+
("project", "project_name"),
|
|
125
|
+
("version", "version"),
|
|
126
|
+
("build", "build_date"),
|
|
127
|
+
("time", "build_time"),
|
|
128
|
+
("idf", "idf_version"),
|
|
129
|
+
("size_bytes", "size_bytes"),
|
|
130
|
+
("bin_sha256", "bin_sha256"),
|
|
131
|
+
("app_elf_sha256", "app_elf_sha256"),
|
|
132
|
+
("elf", "elf_path"),
|
|
133
|
+
("elf_size_bytes", "elf_size_bytes"),
|
|
134
|
+
("elf_sha256", "elf_sha256"),
|
|
135
|
+
)
|
|
136
|
+
for label, key in fields:
|
|
137
|
+
if key in result:
|
|
138
|
+
print(f"{label}: {result[key]}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
142
|
+
parser = argparse.ArgumentParser(
|
|
143
|
+
description=(
|
|
144
|
+
"inspect version and SHA-256 identity from an ESP-IDF application BIN"
|
|
145
|
+
)
|
|
146
|
+
)
|
|
147
|
+
parser.add_argument("firmware", type=Path, help="ESP-IDF application BIN")
|
|
148
|
+
parser.add_argument(
|
|
149
|
+
"--elf",
|
|
150
|
+
type=Path,
|
|
151
|
+
help="matching ELF; its SHA-256 must equal app_elf_sha256 in the BIN",
|
|
152
|
+
)
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
"--expect-version",
|
|
155
|
+
help="fail unless the application descriptor contains this exact version",
|
|
156
|
+
)
|
|
157
|
+
parser.add_argument("--json", action="store_true", help="emit JSON")
|
|
158
|
+
return parser
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def main(argv: list[str] | None = None) -> int:
|
|
162
|
+
args = build_parser().parse_args(argv)
|
|
163
|
+
try:
|
|
164
|
+
result = inspect_firmware(
|
|
165
|
+
args.firmware,
|
|
166
|
+
elf_path=args.elf,
|
|
167
|
+
expected_version=args.expect_version,
|
|
168
|
+
)
|
|
169
|
+
except ValueError as exc:
|
|
170
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
171
|
+
return 2
|
|
172
|
+
if args.json:
|
|
173
|
+
print(json.dumps(result, indent=2, sort_keys=True))
|
|
174
|
+
else:
|
|
175
|
+
print_human(result)
|
|
176
|
+
return 0
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
if __name__ == "__main__":
|
|
180
|
+
raise SystemExit(main())
|