用 GPT-Live-1 和 Codex 让我的 LED 显示屏活起来
为了给我室友的生日,我给他买了一个小 LED 显示屏,可以显示飞过我们窗户附近的航班。这是一份相当酷的礼物,但在看着飞机出现在屏幕上之后,我一直在想,这样的显示屏是否能做更多事情,而不只是追踪飞机。它能显示天气或我的日程表吗?当我准备出门时,我能不能直接问“下一班火车什么时候到?”,然后在显示屏上看到答案?
这很快变成了一个副项目,涉及树莓派、一块 128 × 64 HUB75 LED 面板、Codex,以及一个我最终开始称之为 Jack 的助手。对话由 GPT-Live-1 驱动,这是我们最近在 API 中推出的全双工语音模型。另一个模型负责协助研究,一个渲染器负责处理像素,尽管我们一开始绝对没有考虑得这么周全。这篇文章记录了这个过程是如何进行的。
Codex 如何帮助我让 HUB75 RGB LED 面板工作
起初,Codex 和我必须弄清楚如何在显示屏上显示任何有用的东西。我使用的是一块 128 × 64 HUB75 RGB 面板,但我对控制器、接线,或者如何向它发送除当前正在显示的内容之外的其他东西知之甚少。
我开始给 Codex 发送图片,并让它解释我看到的是什么。它帮助识别了重要的连接,理解了基于 ESP32 的控制器,并弄清楚了 HUB75 面板实际上是如何接收显示数据的。它甚至在图片上做了标记,向我展示哪些部分很重要,这很有用,因为如果靠我自己,我看到的基本上只是一块电路板和很多猜错的机会。
一旦我们了解了基本的硬件,Codex 就帮助我让控制器运行 WLED-MM,并确认面板已配置为正确的 128 × 64 分辨率。从那里,我们可以向它发送自定义帧,并开始试验在这么小的屏幕上什么看起来效果最好。Codex 负责研究和故障排除,而我负责物理连接,并在某些东西的行为不符合我们预期时反馈回去。
在我的 Mac 上构建语音控制的 LED 显示屏原型
第一个可用的版本比我们最终得到的要简单得多。一旦控制器运行了 WLED-MM,Codex 就帮助我构建了一个渲染器和一个自定义技能,我使用 ChatGPT 桌面应用中的语音功能告诉它要在显示屏上放什么。
这已经足以让显示屏感觉不同了。它可以显示天气、我的日程表、地铁到站信息、金融图表、消息、倒计时、照片、艺术作品或自定义动画场景,每当某个想法在这么小的屏幕上看起来不对劲时,我们就会不断调整渲染器。
语音交互仍然发生在 Codex 内部,自定义技能为它提供了一种更新墙面应该显示内容的方式。从那里,我 Mac 上的渲染器将布局转换为 128 × 64 RGB 帧,并通过本地网络使用 DDP 发送,而运行 WLED-MM 的 ESP32 控制器负责将这些帧显示到面板上。
Codex 的帮助不仅仅在于连接这些部分。128 × 64 的显示屏给你留下的空间不多,所以我们不断研究如何将文本、图像、天气、日程事件和地铁到站信息适配到屏幕上,而不至于让所有东西都变成一团无法阅读的乱麻。我会告诉 Codex 哪里看起来不对,它会调整渲染器或布局,我们会再次尝试,直到待机屏幕开始感觉像是我真正想一直留着的东西。
问题在于整个系统仍然依赖我的 Mac。渲染器在那里运行,语音交互从那里启动,如果我的笔记本进入休眠状态,或者我把它带到别处,这面墙就无法以我期望的方式发挥作用。理想情况下,我希望下班回到公寓后,直接让我的墙给我展示一些东西。
将语音助手和渲染器迁移到树莓派
我告诉 Codex 我想让笔记本退出这个环节,于是我们把需要持续运行的部分迁移到了树莓派上,将代码放入私有仓库,并确保我们有备份以及在出错时回滚的方法。在我插上树莓派后,Codex 负责让代码运行起来、检查日志并解决出现的任何问题,而我则继续描述我希望它实现的功能。
现在它上面运行着两个小型服务:
- 语音服务负责麦克风、PipeWire 音频、WebRTC 回声消除、唤醒与休眠行为、打断处理、GPT-Live-1 连接以及客户端委派。
- 渲染服务负责保持动画待机屏幕运行、刷新天气和 MTA 地铁到站信息、使用经过脱敏的日历数据、校验场景描述,并将帧流式传输到墙上。
在树莓派上连接了麦克风和扬声器后,我可以走进房间,说一句“Hey Jack”,然后直接提出请求,无需打开电脑。我们花了一些时间改进唤醒行为和它的响应方式,但让它脱离我的 Mac 才是让它感觉不那么像笔记本演示、而更像一个可以一直开着的东西的关键。
GPT-Live-1 与树莓派如何协同工作
我们最终的分工方式是:GPT-Live-1 是我对话的对象,而不是在墙上作画的部分。GPT-Live-1 保持对话开启,在说话时监听,允许我打断它,并决定某个请求是否需要对话本身之外的能力。
当出现这种情况时,树莓派会接收委派,并使用 GPT-5.6 Luna 以低推理力度发起客户端 Responses API 请求。Luna 可以搜索公共网络、查看我日历的只读连接、从 Wikimedia Commons 查找可复用的图片,并决定该请求需要什么样的场景。
有一次,我让 Jack 把一张照片放到墙上,它告诉我做不到。渲染器已经支持图片,但我没有给语音助手提供查找和发送图片的工具。Codex 帮我把它连接到 Wikimedia Commons,这样它就能寻找匹配的图片并检查其许可和署名信息。我们还调整了图片适配面板的方式:树莓派会将图片缩放至 128 × 64 像素以内,同时保持比例,这样它们就不会在显示屏上被拉伸。
随着我们不断添加功能,Codex 帮助确保各个部分不会纠缠在一起:树莓派上的代码决定 Luna 可以使用哪些工具,Luna 判断需要什么信息或场景,而渲染器在将场景转换为精确的 128 × 64 RGB 像素之前会先校验场景 JSON。随后,它通过 DLP 将这些帧发送到 WLED-MM 控制器,由后者驱动 HUB75 面板。
一个早期的 bug 出现在一次非常普通的对话中。我会问 Jack 纽约的天气,然后说“把它显示在墙上”,而它不知道“它”指的是什么。Codex 发现我们的应用只把最新的请求发送给了负责更新显示的模型。我们修改了交接方式,将最近的对话和之前的结果一并包含进去。
从“Hey Jack”到 LED 显示屏上的像素
如果我让 Jack 查点东西并显示在屏幕上,大致会发生这样的过程:
- Raspberry Pi 以 24 kHz PCM 音频采集我的声音,并进行本地音频处理。
- GPT-Live-1 接收音频流,理解我在问什么,并保持对话开放,这样我就可以打断或补充内容。
- 如果请求需要研究、日历信息、图片或墙面更新,GPT-Live-1 会创建一个客户端委派,Pi 通过 Responses API 将任务发送给 GPT-5.6 Luna。
- Luna 使用我为该请求暴露的工具,例如托管网络搜索、只读日历连接、可复用图片查找,或用于更新墙面的工具。
- 如果已有天气、日程、消息或倒计时场景适用,它就选择该场景;如果我要求的内容是我们未曾预料的,它就创建一个新场景。
- 本地渲染器验证该场景,将其转换为 128 × 64 RGB 帧,并通过 DDP 将其发送到墙面,同时 GPT-Live-1 告诉我它找到了什么。
我喜欢的是,我不必等一个僵化的语音助手回合结束才能说别的。我可以打断 Jack,澄清我的意思,而研究和渲染仍在后台进行。
总的来说,这是一个相当简单的业余项目。我之前没有 HUB75 面板、ESP32 控制器、Raspberry Pi 或语音模型的经验,在另一个时候,我可能一打开显示屏就会停下来。相反,Codex 帮助我边做边弄清楚,每当遇到不熟悉的东西,我都可以直接问。
如果你有这样的项目但不确定从哪里开始,就问问 Codex。你可能会惊讶于自己能走多远。