evnchn-agentic/dmr39

GitHub: evnchn-agentic/dmr39

通过 ESP32-C3 截取并重放 IR 信号,为无网络、无 API 的老旧机顶盒搭建了一套支持网页遥控器和 HLS 直播流的远程控制系统。

Stars: 0 | Forks: 0

# DMR-39 Remote Bridge **逆向工程一台没有 API、没有网络连接、也没有说明书的机顶盒——只借助一名 AI 结对程序员、一张 HDMI 采集卡以及大量的焊锡。** ![HLS直播流和平板上的网页遥控器](https://static.pigsec.cn/wp-content/uploads/repos/cas/8f/8f315eabb14dcc0ea61d2aa1fe6b4b7bc05f857a46aafcb87f5556702f0de0ab.jpg) *带有完整41键遥控功能的电视直播流——全部通过单一 URL 提供。* ## 这是什么 这是一个通过网络远程查看和控制 SUPER DMR-39 数字电视机顶盒的完整系统。 ![SUPER DMR-39——一款2016年时期的香港 DVB-T2 接收器](https://static.pigsec.cn/wp-content/uploads/repos/cas/b7/b7bfb45efa5fa319abc00a69781362ada1b595d73088440da4cbde8062e335f2.png) DMR-39 是一款2016年时期的香港 DVB-T2 接收器(板级代码 `MSD7816_88969T_608_148mm_NP`,固件 `V1.01 2016.04.02`)。它没有 Ethernet,没有 Bluetooth,也没有任何官方文档协议。它的 USB 接口仅用于 DVR 录制和固件升级——这两者在这里都派不上用场。唯一可行的交互接口是一个 38kHz 的 IR 接收器和一个 5 针 I2C 前面板连接器。截至2026年3月,下个月它就满十岁了。 最终完成的系统包含: - **ESP32-C3** 直接采集 PCB 上的解调后 IR 信号——无需额外的 IR LED - **网页遥控器**,通过 NEC 协议映射了全部41个按键 - **HLS 直播流**,通过 VAAPI 硬件编码实现电视画面的传输并同步音频 - 中继服务器上的 **Nonce 身份验证**,防止未经授权的 IR 指令 - **单端口 Web UI**,将电视流和遥控功能合二为一,支持隧道转发 - 带有密码验证的 **OTA 固件更新**(设备处于密封外壳中) ``` ┌──────────────────────────────┐ Physical Remote ──>│ IR Receiver (demodulated) ──> │──> STB │ GPIO3 (sniff) GPIO0 (TX) │ │ ESP32-C3 │ │ WiFi Web Server :80 │ └──────────┬───────────────────┘ │ /ir?code=A25D ┌──────────┴───────────────────┐ │ Relay Server :8889 │ │ HLS Video + AAC Audio │ │ + IR Relay + Nonce Auth │ │ (VAAPI H.264 encode) │ └──────────┬───────────────────┘ │ Cloudflare Tunnel Browser ``` ## 研发历程 ### 阶段 0. 可行性研究(编码前) 在编写任何代码之前,第一步是弄清楚这台设备到底能不能被控制。我们打开了机顶盒外壳,拍下了 PCB 的照片,并将板子上的丝印(`MSD7816_88969T_608_148mm_NP`)发给 Claude 进行分析。 Claude 确认了主 SoC 是一颗 **MStar MSD7816**(常见于中国大陆及香港地区廉价机顶盒的 DVB-T2 解码芯片),前面板驱动芯片则是 **兼容 TM1650 的芯片**(该板子使用了一颗 **HW650EP** 仿制芯片)。TM1650 的数据手册揭示了一个简单的 I2C 协议:SoC 写入显示段位并读取按键扫描的响应。从理论上讲,只要用 ESP8266 伪装成前面板——拦截显示数据以读取频道号,并注入按键扫描响应来模拟按键按下即可。 可行性结论是:**理论可行,但此前从无人记录过成功的先例。** 当时世界上并不存在开源的 TM1650 slave 实现。I2C 的时序要求非常苛刻。要知道它到底能不能工作,唯一的办法就是亲自上手尝试。 ### 阶段 1. I2C Slave 模拟——27种策略的严酷考验
AI agent 当时操作的硬件环境(点击展开) ![通宵调试期间的接线状态](https://static.pigsec.cn/wp-content/uploads/repos/cas/d6/d603919005fd4f059444159afb142984bbc23991852b31f33cfb5583efd34a5d.jpg) *这造型绝对赢不了什么选美比赛。这就是拆掉外壳的 DMR-39 机顶盒,ESP32-C3 通过杜邦线焊接在 PCB 走线上悬空挂着,旁边是 USB HDMI 采集卡,以及共用同一个架子、乱七八糟的各种 homelab 设备。这就是 AI agent(通过 HDMI 采集卡)在通宵调试期间一直盯着看的东西。*
第一种方案是拆下前面板,通过 I2C 冒充其 HW650EP 显示/扫描芯片(TM1650 仿制芯片)。只需用 ESP8266 解码显示写入(以读取频道号)并注入按键扫描响应(模拟按键)。 然而,随后发生的是嵌入式开发中最折磨人的一场调试马拉松。在好几个熬通宵的会话中——期间 Claude Code 还在墨水屏上发布进度更新,而用户则在睡觉——我们在 ESP8266 上尝试了 **27 种截然不同的 GPIO 注入策略**: - 与 HW650EP 的 22ms 扫描周期进行相位同步的脉冲注入 - 基于 ISR 的 I2C slave 模拟,前后迭代了 **11 个版本**(V1-V11) - 在 160MHz 频率下进行直接的寄存器级 GPIO 操控 - 双 ISR(一个作用于 SDA 负责 START/STOP,另一个作用于 CLK 负责位采样) - 以每秒 120 万次迭代的频率进行轮询,并辅以三重采样噪声过滤器 这台机顶盒的 I2C 总线运行在约 250kHz——远远快于预期的 100kHz——并且杜邦线上存在严重的电信号振铃现象。ESP8266 的中断延迟(3-5 微秒)导致它总是漏掉 START 状态条件。读取到的地址字节是 0x9F 和 0xFF——分析表明,这与在错误的时钟相位进行采样是一致的,而不是接线错误。 **V4 版本达到了最高水准**:成功解码了 39 次真实的 I2C 交易,并读到了正确的显示地址(0x6E)。但是对 START/STOP 状态的误判导致后续的读取数据全部损坏。在转而使用 ESP32-C3 以获取硬件 I2C 支持时,我们发现了一个关键线索:**对所有的 I2C 地址进行 ACK 会导致机顶盒进入降级模式**(显示“----”)。这台机顶盒正是利用 NACK 响应来识别它的前面板。切换到选择性 ACK 策略——仅在真正的 TM1650 地址范围内进行响应——立刻就让屏幕上出现了真实的频道号。 ### 阶段 2. 256个值的暴力穷举扫描 既然证明了按键注入的可行性,接下来的问题就是:在 256 个可能的字节值中,究竟哪些对应着遥控器上的实际按键?无论是 TM1650 的数据手册还是机顶盒的固件,都没有提供查找表。 解决的办法是全自动暴力穷举。ESP32-C3 循环遍历所有的 256 个值,将每一个值注入到按键扫描响应中,同时通过**双重反馈通道**监控反应: - I2C 显示写入(当机顶盒更新其前面板时即可被检测到) - HDMI 画面帧大小的变化(当 OSD 出现时,USB 采集卡捕获到的 JPEG 文件体积会突然增大) 在机顶盒电源上装一个**机械定时器**,提供自动的重启循环——通电一分钟,断电一分钟——每个周期能提供大约 50 秒可用的扫描时间。固件会在重启之间记录断点位置,并自动恢复进度。RapidOCR 被用于解析 HDMI 截图,从而检测屏幕上的菜单文字。 经历了六次完整的通断电循环后,大部分值没有任何反应。随后,当测试到 **0xE0** 时,HDMI 的画面帧大小激增了 33%——屏幕上出现了一个音量 OSD。这是第一个,也是最终唯一一个通过 I2C 注入发现的按键代码。经历了 256 个值的测试最终只找到一个能用的按键,此时该如何抉择已不言而喻。 ### 阶段 3. “说实话,我觉得 I2C 太难了” 用户打出了这句改变一切的信息。他们已经在机顶盒的 PCB 上找到了两根解调后的 IR 线路: ``` GPIO3 ← Front panel IR receiver (demodulated RX, active LOW) GPIO0 → STB IR input trace (demodulated TX) GPIO1 ← STB VCC rail (power state detection) ``` 不需要再操心 38kHz 的载波问题——导线上传输的就是干干净净的数字脉冲。曾经在 I2C 上折腾了好几周都没搞定的事,只需一个晚上就能通过这个方法完成。 ### 阶段 4. 三种信号采集方案 **尝试 1 — 手写 ISR**:在 GPIO3 上附加中断,记录 `micros()` 时间戳,计算时间间隔。然而 NEC 数据帧总是支离破碎——WiFi 协议栈的中断抢占了这个 ISR,导致原本应为 32 位的数据只捕获到了 20 位,引导码后面跟着一堆乱码。 **尝试 2 — 带间隔检测的改进版 ISR**:提高了帧间间隔的阈值(15ms → 50ms → 100ms),去除了防抖代码,加入了双缓冲。但数据帧依然破碎。用户一语道破了复杂性的症结:*"别人都是怎么解码 NEC 信号的?我们是不是可以直接用现成、久经考验的代码,而不用自己手写呢?"* **尝试 3 — IRremoteESP8266 + RMT 硬件外设**:ESP32-C3 的 RMT 外设可以在硬件 FIFO 中以微秒级的精度捕获信号边沿,完全不受软件中断抖动的影响。每一次按键都能瞬间完成干净的 NEC 解码。 但是,使用这个库带来了一个新问题:RMT 外设占用了 GPIO3,导致之前基于 ISR 的实时透传功能失效。最终的解决方案是**自动转发模式**:先通过该库捕获完整的 NEC 数据帧(在信号结束后约 15ms),然后利用直接的寄存器写入操作,将原始时序重放到 GPIO0 上。对于用户来说,20-30ms 的延迟几乎无法察觉,而且原来的实体遥控器可以像以前一样完美工作。 ### 阶段 5. 映射全部41个按键 在自动转发模式开启的情况下,原来的实体遥控器依然可以正常使用,同时每一次按键都会被系统记录下来。一个通过 USB 串口运行的交互式 CLI (`ircli.py`) 提供了完整的映射工作流,并结合 HDMI 截图功能,以便直观地确认每个按键的效果。 用户按分组系统性地挨个按下了每一个键: - 导航键:MENU, EXIT, UP/DOWN/LEFT/RIGHT/OK - 功能键:EPG, INFO, TTX, AUDIO, PVR, SUB - 数字键盘:0-9 - 播放控制键:REW, FF, PREV, NEXT, PLAY, PAUSE, STOP, REPLAY - 彩色键:RED, GREEN, YELLOW, BLUE - 系统键:POWER, MUTE, FAV, TV, BACK, SEEK 最后一次“全盘扫描”(从上到下,从左到右)确认了没有遗漏任何按键,并发现了一些双功能别名:LEFT/RIGHT 同时兼任 VOL-/VOL+,UP/DOWN 兼任 CH+/CH-。 最终成果:[`ir_codes.json`](ir_codes.json) —— 包含全部 41 个按键的完整 NEC 码映射表。 ### 阶段 6. MSB/LSB 比特序 Bug 网页遥控器看起来很完美。ESP32-C3 接收到 `/ir?code=A25D`,构建出 NEC 数据帧,并极其精准地用微秒级时序将其 bit-bang 发送出去——然而机顶盒完全无视了它。示波器上的波形看起来毫无破绽:引导码正确,位数正确,脉冲宽度也正确。 根本原因在于:**IRremoteESP8266 库存储 NEC 数值时采用 MSB 优先(最高有效位优先)**,但是手写的 `sendNEC()` 在遍历比特位时却采用了 LSB 优先(最低有效位优先)。这导致虽然生成了一个格式合法的 NEC 数据帧,但里面的代码却截然不同——这是一个机顶盒从未见过的代码。修复这个 Bug 只需要改动一行代码: ``` // Before (broken): for (int i = 0; i < 32; i++) // After (working): for (int i = 31; i >= 0; i--) { ``` 当这个 Bug 被发现并修复时,用户其实正在洗澡。等他回来时,发现整条链路已经彻底打通,并且通过了 HDMI 采集验证:MENU 打开了设置,EXIT 关闭了它,CH+ 切换到了 TVB Plus 频道。 ### 阶段 7. 流媒体基础设施 当用户产生“想随时随地查看并控制这台电视”的想法时,这个项目就不再仅仅是“一个遥控器”了。 **流媒体服务器的演进历程:** 1. **MJPEG 快照** —— 简单地通过 USB HDMI 采集卡获取 `/snapshot` 2. **MJPEG + 独立的 MP3** —— 视频和音频无法同步(严重的画面声音不同步问题) 3. **基于 VAAPI 的 HLS** —— 单个 ffmpeg 进程,在 Intel HD 4000 显卡上(i965 驱动)进行硬件 H.264 编码,并封装 AAC 音频,切片为 1 秒的 segments 在 i7-3632QM (Ivy Bridge) 上的 CPU 性能消耗基准测试: | 组件 | CPU 占用率 | 备注 | |-----------|-------|-------| | MJPEG 软件解码 | 46% | Ivy Bridge 架构没有硬件 MJPEG 解码支持(直到 Haswell 架构才加入) | | VAAPI H.264 硬件编码 | 5% | 交由 Intel HD 4000 GPU 处理 | | AAC 软件编码 | 20% | 任何消费级平台上都不存在硬件音频编码器 | | **总计** | **65%** | 占用了一 8 线程 CPU 的其中 1 个核心 | 硬件升级分析表明,如果换用 **Intel N5095** (Jasper Lake 架构),通过 VAAPI 进行 MJPEG 硬件解码可以将总占用率降至约 11%;而如果使用 **NVIDIA Quadro K620** (Maxwell GM107 架构),配合 NVDEC MJPEG 和 NVENC H.264,可以将所有的视频处理任务完全卸载到 GPU 上。 ### 阶段 8. 安全性(与过度工程化) 第一版安全方案实现了一个双层 HMAC-SHA256 机制:中继服务器向浏览器下发基于会话的 nonce,再加上中继服务器到 ESP32-C3 之间使用的单调递增序列号和截断的 HMAC 签名。ESP32 通过 mbedtls 进行验证,并拒绝重放序列号的请求。 用户看了一眼就说道:*"这看起来非常不靠谱。我们能不能干脆不要这套方案?"* 他说得对。在设备重启时,这套 HMAC 机制非常脆弱,而且它为一个仅限于局域网使用的设备增加了额外的复杂性,甚至是在解决一个根本不存在的威胁(谁会专门跑到别人的家庭网络里发送恶意的 IR 指令呢?)。整个 HMAC 层随后从 ESP32 固件中被剥离。留下来的方案虽然更简单,但已经足够: - **浏览器 → 中继服务器**:使用基于会话的 nonce token(通过 `secrets.token_urlsafe` 生成,TTL 有效期为 1 小时),在页面加载时签发,并在每次请求 `/ir` 时进行验证。这可以防止来自其他标签页或网站的恶意跨站请求。 - **中继服务器 → ESP32-C3**:直接使用 HTTP,不设认证。ESP32 接受局域网内任何人发送的 `/ir?code=XXXX`。对于家庭网络设备来说,这是可以接受的风险。 **经验教训:安全措施应该与实际的威胁模型相匹配,而不是迎合工程师的热情。** 在局域网 IR 发射器上加 HMAC 纯属为了解决一个伪需求而生造出来的方案。 ### 阶段 9. 项目整合与代码审查 在迭代开发期间,累积产生了三个独立的项目目录: - `dmr-39/` —— 仅包含参考图片 - `dmr39-bridge/` —— 已废弃的 ESP8266 I2C 代码以及 Python 流媒体服务器 - `dmr39-c3/` —— 正在使用的 ESP32-C3 IR 固件 我们将这些内容整合进了一个单一的 `dmr39/` 代码仓库。ESP8266 的代码作为历史记录被保留在 `archive/esp8266-bridge/` 目录下。同时初始化了 git 仓库。 随后进行了一次代码审查——由 AI 检查它自己编写的代码——并发现了多个问题: | 严重程度 | 问题 | 修复方案 | |----------|-------|-----| | 高危 | HLS 文件服务中的路径遍历漏洞(可通过 `%2F`/`%2e%2e` 绕过) | 引入 `os.path.realpath()` + 路径前缀检查 | | 高危 | OTA 密码被硬编码在 `platformio.ini` 中 | 外部化提取到 `$DMR39_OTA_PASS` 环境变量中 | | 高危 | IR 指令参数缺少输入验证 | 在 ESP32 端增加 `isHex()` 校验,在中继端使用 `re.fullmatch` | | 中危 | IR 解码内存分配失败时出现空指针解引用 | 增加了 nullptr 防御检查 | | 中危 | ESP32 缺少 WiFi 自动重连机制 | 在 30 秒的存活检查循环中加入了 `WiFi.reconnect()` | | 中危 | urllib 响应对象泄漏 | 使用 `with` 上下文管理器进行包装 | | 中危 | 错误信息中泄漏了 ESP32 内部 IP 地址 | 统一返回通用的 `502 relay error` | | 低危 | HLS 清理竞态条件(在写入 segment 时删除了文件) | 添加 `try/except FileNotFoundError` 处理 | OTA 密码也从 git 历史记录中被彻底抹除(在早期的草稿中它曾被写在 `platformio.ini` 里)。同时确认 WiFi 凭证从未被提交过——仓库中只追踪跟踪了 `.example` 模板文件。 ### 阶段 10. 部署到 Homelab(然后被封锁) 项目需要将文档部署到 homelab 的文档服务器上,该服务器运行在 IP 为 `192.168.50.1` 的 ASUS RT-AX82U 路由器上,由 lighttpd 提供服务,开放端口为 `8080`。部署需要通过 3131 端口 SSH 连接到该路由器。 随后发生了一出充满戏剧性的小乌龙: 1. AI 尝试执行 `sshpass -p '***' ssh evnchn@192.168.50.1 -p 3131` —— 结果是 **Permission denied**(权限被拒绝) 2. 为什么?因为 SSH 客户端在 `~/.ssh/` 目录下找到了本地密钥,并优先尝试使用这些密钥,从而忽略了密码 3. 而每一次失败的密钥尝试,都被路由器计为一次暴力破解尝试 4. 在经过足够多次的失败尝试后,路由器**直接将该 IP 彻底封锁** 5. 于是 3131 端口的反馈从“connection refused”(连接被拒绝)变成了“connection timed out”(连接超时)——完全被锁定 修复这个问题需要两步:首先使用 `-o PubkeyAuthentication=no` 强制仅使用密码验证;其次,由于 `.156` 这台机器已经被拉黑,必须通过 `.180`(那台 N5095 主机)进行跳转。此外,这台路由器上并没有安装 `sftp-server`,因此传输文件不能使用 `scp`,只能通过 `cat file | ssh cat > remotefile` 这种方式来代替。 AI 因为过于“热心”帮忙,结果把自己锁在了自己的基础设施门外。后来还是用户解释了这种锁定机制,并建议了使用跳板机。理清操作套路后,文档终于被成功部署。 ### 阶段 11. 移动端 UI 与“不像遥控器的遥控器” 用户在手机上测试了网页遥控器,并反馈了两个问题: 1. 在竖屏模式下,遥控器显得**太宽了**——完全不像真正握着遥控器的感觉 2. Safari 的动态地址栏导致页面发生溢出(这是经典的 `100vh` 问题) 于是,ESP32 独立遥控器的宽度从 `max-width:340px` 缩减到了 `240px`,按键和间距也等比例缩小。HLS 页面的移动端布局被限制在居中的 `max-width:280px` 范围内,不再铺满整个手机屏幕宽度。两个页面都采用了 `100dvh`(并带有针对 Safari 动态视口的 `100vh` 回退机制)。 ESP32-C3 通过开发机进行了空中升级(OTA)——这是当初搭建的 OTA 系统第一次真正投入使用。`.180` 上的中继服务器也通过 SSH 进行了更新并重启,全程丝毫没有影响到该机器上运行的另外 20 个 Docker 容器。 ### 阶段 12. 产品图(或:不用 Photoshop 也能完成抠图) README 需要几张产品图,但我们手上有的仅是几张手机随拍的照片:机顶盒被放在乱糟糟的架子上,下面是深色的木地板,背景是米色的墙壁。没有 Photoshop,系统自带的 Python 也不允许不使用 `--break-system-packages` 参数安装 `numpy`,而且 Claude Code 毕竟是一个代码工具——并不是图像编辑器。 接下来我们构建了一条出人意料的复杂抠图流水线,整个过程完全依赖 Pillow 库和 API 调用: 1. **从图像边缘进行泛洪填充**(仅使用 Pillow):从图像边界向内遍历,将任何亮度高于阈值的像素全部替换为白色。这一步清理了大约 80% 的背景,但对于那些被机顶盒深色外壳所包围的深色地板区域则无能为力。 2. **手动多边形涂抹**(使用 Pillow `ImageDraw`):估算出机顶盒的左边缘作为一条线,将其左侧的所有内容涂成白色。这一步需要进行多次的“涂抹、观察、调整坐标、重试”的迭代——AI 无法一次性精确地看清像素级别的边界。 3. **Gemini 3 Flash 边界多边形生成**(通过 OpenRouter API 调用):要求 `google/gemini-3.1-flash-image-preview` 模型返回机顶盒的轮廓,形式为归一化坐标的多边形顶点。模型返回了一个拥有 7 个顶点的多边形,完美地将设备主体与地板分隔开来——这正是泛洪填充算法无法触及的区域。 4. **Gemini 多边形与泛洪填充强强联合**:多边形蒙版消除了深色地板,而泛洪填充则搞定了多边形遗漏的明亮墙壁部分。这两种技术完美地弥补了彼此的盲区。 在此期间,我们还尝试了使用 **Gemini Flash 图像生成**功能来实现全 AI 自动抠图——模型确实返回了一张背景干净的全白底图片,但这张图明显是重新生成的,而不是在原图基础上编辑的:分辨率变低了,上面的文字标签略有不同,甚至连透视关系也发生了改变。虽然这可以作为一个很好的参考比对,但作为需要高度还原真实模样的产品图来说就不合适了。 ## 文件列表 | 路径 | 描述 | |------|-------------| | `src/main.cpp` | ESP32-C3 固件:包含 IR 捕获、NEC TX、Web 服务器以及 OTA 更新 | | `src/wifi_credentials.h.example` | WiFi 和 OTA 凭证的模板文件(真实文件已被 gitignore 忽略) | | `ir_codes.json` | 包含全部 41 个按键的完整 NEC 码映射表 | | `platformio.ini` | PlatformIO 配置文件:包含 USB 烧录和 OTA 升级环境 | | `server/mjpeg_stream.py` | HLS 流媒体服务器 + IR 中继 + nonce 身份验证 | | `tools/` | 包含 IR 扫描、OCR 穷举测试以及调试用的脚本 | | `archive/esp8266-bridge/` | 最初版本的 ESP8266 I2C 桥接代码(已废弃,仅作记录) | | `archive/images/` | 机顶盒和遥控器的参考图片 | ## 为什么这个项目值得放入作品集 这个项目的意义不仅在于“我做了一个 IR 遥控器”——它是一个关于 **代理式硬件逆向工程**的案例研究,也是一堂关于“如何让 AI agent 在物理世界中真正发挥作用”的实践课。 ### HDMI 采集卡是最关键的一环 Kahneman 在《思考,快与慢》一书中指出,专家只有在拥有 **清晰、即时反馈** 的环境中,才能培养出可靠的直觉。如果没有反馈,再丰富的经验也无法提升准确度——只会让人变成一个充满自信但错得离谱的人。 同样的原则也适用于与硬件打交道的 AI agent。那张价值 15 美元的 USB HDMI 采集卡是整个项目中最为关键的基础设施。它赋予了 AI agent **观察物理世界的眼睛**:能够截取电视屏幕画面,通过对比 JPEG 文件大小来检测 OSD 变化,运行 OCR 读取屏幕上的文字,以及在发送 IR 指令后直观地验证频道是否真正发生了切换。 如果没有采集卡,每一个假设都需要用户手动去查看并反馈结果。agent 就会像盲飞一样——无法区分“IR 指令被忽略了”还是“IR 指令起作用了,只是切换到了一个无关紧要的频道”。这样的反馈回路会从几秒钟被拉长到好几分钟,要想实现彻夜的自主运行更是天方夜谭。 **给硬件代理任务的启示:优先投资于反馈基础设施。** 无论是采集卡、USB 串口日志记录器,还是电流传感器——只要能给 AI agent 提供关于物理系统的闭环观测能力,就值得投入。agent 之所以能够在一夜之间试错 27 种失败的策略,前提正是它能够在不需要人类介入的情况下,自行评估每一次尝试的结果。 ### AI 审查自己写的代码 代码审查阶段值得一提。AI 在几小时前刚写完的代码中,找出了真实的漏洞——路径遍历、缺失的输入验证、硬编码的密钥。不过,它也设计了一套过度工程化的 HMAC 安全方案,而被用户正确地指出根本没有必要。 这种模式——先快速构建,然后再严苛审查——往往比一开始就试图写出完美的代码效果更好;尤其是在这种伴随着每次新发现而不断改变需求的探索性硬件项目中。 ### 当 AI 自己阻碍了自己 SSH 被锁事件就是一个前车之鉴。AI 尝试了最显而易见的方法(使用密码进行 SSH 登录),遭遇失败后,如果任由它继续尝试各种变体——好在用户识别出了这种失败的演变模式(暴力破解导致的封锁),并给出了解决方案。硬件环境拥有软件工程师极少遇到的 **带状态的失败模式**:你得到的不仅仅是一个错误提示,而是被直接 *锁在门外*,而且盲目试只会让情况变得更糟。 ### 数据背后的故事 在最终放弃该方案之前,我们一共尝试了 **27 种失败的 I2C 策略**。通过自动化电源通断进行的 256 个值暴力穷举扫描,加上基于 OCR 的 HDMI 画面监控,最终只找到了一个能用的按键。正是在那个时候,我们迎来了项目的转折点。 **AI 彻夜自主工作**,将状态发布到墨水屏上,不断刷新固件,捕获并分析 HDMI 截图,关联分析 I2C 总线数据——所有这一切都在与真实硬件的持续闭环中完成。当用户离开去洗澡时,它独立调试并修复了一个让整个开发过程卡壳了一个小时的比特顺序 Bug。 **每一次方向调整都保留了之前的成果**:为调试 I2C 而搭建的 HDMI 采集流水线,成为了随后 IR 测试的视觉质量保证系统,接着又演变成了最终的直播流媒体基础设施。之前的 I2C 总线分析结论启发了选择性 ACK 策略。机械定时器控制通断电的技巧也被复用到了自动化的扫描测试中。 我们的对话记录跨越了 20,000 多行、体积高达 63MB 以上,贯穿了多个工作阶段。它记录了当工程师是一个语言模型时,真正的逆向工程究竟是什么样子:不断提出错误的假设并将其验证抛弃,遭遇并设法绕过硬件的物理限制,简化过度设计的方案,甚至还包括 AI 偶尔把自己锁在路由器门外的滑稽时刻。 ## 未来扩展计划 请查阅 [STRETCH-GOALS.md](archive/esp8266-bridge/docs/STRETCH-GOALS.md#future-expansion) 了解计划开发的功能:实时字幕 OCR、将 IR 接收器作为家庭自动化的触发器,以及基于网页的 IR 学习模式。 ## 编译与构建 ``` # ESP32-C3 固件(USB,首次) cp src/wifi_credentials.h.example src/wifi_credentials.h # edit with your creds pio run -e esp32c3 -t upload # ESP32-C3 固件(OTA,后续更新) export DMR39_OTA_PASS= pio run -e ota -t upload # 流媒体服务器(在带有 HDMI 采集卡的机器上) python3 server/mjpeg_stream.py # 或者启用为 systemd 服务: systemctl --user enable --now hdmi-capture ``` ## 许可证 MIT
标签:ESP32, 云资产清单, 嵌入式开发, 物联网, 硬件黑客, 视频流媒体, 逆向工具, 逆向工程