duramson/ritto-esp
GitHub: duramson/ritto-esp
该项目通过逆向工程分析 Ritto TwinBus 对讲机信号,利用 ESP32-C6 和 ESPHome 将其改造为完全集成 Home Assistant 的智能设备,实现远程开门与智能通知。
Stars: 0 | Forks: 0
# Ritto 7630 TwinBus 智能对讲机
借助 Seeed XIAO ESP32-C6 和 ESPHome,将 [Ritto 7630](https://www.ritto.de/) TwinBus 公寓对讲机改造为
完全集成的 Home Assistant 设备。
我租住的公寓墙上正好有一台这种老式的 TwinBus 对讲机。它
只能做一件事:响铃,而且如果我碰巧站在它旁边,我可以
按下按钮打开街门。但我想要更多功能。我希望我的
手机能告诉我谁在门外,我希望能在任何地方为访客开门,
我希望我不在家时快递能被直接放在走廊里,我还希望
在双手提满东西回来时,无需费力找钥匙就能进门。
已经有一些将 TwinBus 智能化的项目(Shelly、Wemos、
从总线取电为主板供电,参见[来源](#sources-and-inspiration)),但
每一个都有我不想要的妥协:要么无法真正区分两种铃声,要么
依赖不可靠的总线电压来供电。所以我给它接上了示波器,
研究出它的信号机制,然后打造了自己的版本。代码是最简单的部分。
有趣的工作,也是本文的主要内容,是逆向工程。

## 我希望它能实现的功能
- 区分**公寓铃声**(大楼内部有人在公寓门外按铃)和
**街门铃声**(有人在主入口按铃)的区别。
- 当任何铃声响起时,发送**手机通知**。
- 从手机上、在任何地方**打开街门**。
- **快递模式**:当我不在家且有快递员按门铃时,自动开门,
以便快递员能将包裹留在走廊里。
- 为我自己和少数信任的人提供**一键“让我进门”**功能。
## 起点
Ritto 7630 是 TwinBus 系统的室内话机。所有设备(两个门禁站
和所有公寓)共享一根双绞线总线,同时传输电力
和信号。主板会发出两种不同的音调,一种用于
街门,另一种用于公寓门,并且它有一个按钮,能瞬间
为街门站点的开门器通电。

为了使其智能化,我需要从主板上获取三样东西:“街门铃响了”的干净信号、“公寓铃响了”的干净信号,以及一种
由我自己触发开门器的方法。这些都没有作为整洁的接口暴露出来,所以我
必须在主板上找到它们。
## 逆向工程总线
我将主板背面候选的信号线接地,然后在示波器上观察它们,同时分别按下响铃。

结果发现有两条线是关键。在示波器上,通道 1(蓝色)是振铃线,
通道 2(黄色)是公寓线。
**街门。** 振铃线通常保持接地,并在一段固定时间内(大约 500 毫秒)跃升至约 5V 的干净数字高电平,然后
回落。公寓线完全没有反应。

**公寓门,短按。** 此时,平时保持在约 5V 高电平的
公寓线,被拉至接地,持续时间与按下按钮的时间完全一致。
墙内的按钮只是一个机械触点,按下时会将这条线短路到地。
振铃线也会发出 500 毫秒的脉冲,但
不是在同一瞬间。

**公寓门,长按。** 我特意测量了短按和长按,以
弄清楚振铃脉冲到底与什么相关联。两张快照都是在振铃脉冲(通道 1)上触发的,因此它在每张图中都位于屏幕中心。请注意,公寓线的**下降沿**在两者中落在相同位置,而其上升沿(我松开的瞬间)则随着我按住按钮的时间长短而移动。

因此,振铃总是在**按下开始后的固定延迟**触发(在 500 毫秒/格的网格上略超过三个分度,大约为 1.6 秒),而按下的持续时间
根本不重要。
整个事件的触发器是公寓线的下降沿。这就是为什么固件只对该下降沿做出反应,以及为什么一个固定的阻塞窗口就足以
掩盖随后的振铃回声。
这就是塑造整个设计的关键所在:振铃线会在**两种**
铃声下都产生脉冲,因此单靠它本身无法将它们区分开来。区分的关键在于公寓线。如果它降为接地,响铃就是公寓门铃;如果它保持高电平,响铃就是街门铃。

这与其他人在这块主板上映射的结果相符(参见
[quhfan.de](https://quhfan.de/post/ritto-twinbus-7630-smart-home-assistant/) 和
[deh0511 TwinBus 引脚分配](https://deh0511.de/twinbus/))。相关的板载
接线端子有:
| # | 端子 | 行为表现 |
| --- | --- | --- |
| 1 | GND | 地线 |
| 2 | KLINGEL (振铃) | 任何响铃时变为 ~5V |
| 3 | +24V | 总线电压 |
| 4 | ETAGE (公寓) | 平时保持在 ~5V,公寓响铃时降为接地 |
| 5 | 门禁开启 | 拉至接地时开启街门 |
| 6 | GND | 地线 |
示波器也回答了如何安全连接的实际问题。
信号传输在约 5V 下进行,而总线整体在 +24V 下运行。我直接从电路走线上读取 ~5V 电平(捕获的峰值在 4.8V 左右),这就是我在后文中据此计算输入电阻值的依据。
## 固件如何利用这一切
理解了信号之后,逻辑就很简单了:
- **ETAGE(端子 4)→ GPIO D9。** 当它降为接地时,说明发生了公寓响铃。
- **KLINGEL(端子 2)→ GPIO D10。** 每次响铃都会产生脉冲。
- 因为 KLINGEL 在公寓响铃时也会产生脉冲,所以固件**每当 ETAGE 触发时,就会将 KLINGEL 线屏蔽几秒钟**。这样,单纯的 KLINGEL 脉冲就表示“街门”,而 ETAGE 的下降则表示“公寓门”。
- **开门器(端子 5)→ GPIO D8。** 一个短脉冲将其拉至接地并
开启街门。
这个阻塞标志是 [`esphome/ritto-intercom.yaml`](esphome/ritto-intercom.yaml) 的核心。
Home Assistant 只会看到两个干净的 binary sensor 和一个“开门”按钮;
原始的总线输入和继电器完全保留在节点内部。
## 电路与构建
我不想将 ESP32 直接连接到 24V 总线上,因此对讲机和 XIAO 之间的每条线路都通过一个**光耦**进行电气隔离:输入端(KLINGEL、ETAGE)有两个,输出端(开门器)有一个。XIAO 通过 USB-C 供电,完全避开了不可靠的总线电压。
### 接线
| Ritto 端子 | 作用 | 经过 | XIAO 引脚 |
| --- | --- | --- | --- |
| 2 KLINGEL | 振铃,任何响铃 | 输入光耦 | D10 |
| 4 ETAGE | 公寓线 | 输入光耦 | D9 |
| 5 门禁开启 | 开启街门 | 输出光耦 | D8 |
| 1 / 6 GND | 地线 | — | GND |
在构建照片上,接线端子的标签(从左至右)分别为 TÜR
(开门器)、KLINGEL、ETAGE 和 Ritto GND。XIAO 连接到了 D8、D9、D10 和
GND。


### 确定电阻值
每个光耦 LED 都需要一个串联电阻。对于常见的 PC817(LED 正向
压降约 1.2V,舒适的正向电流约 5mA):
- **输入端(KLINGEL、ETAGE):** 由约 5V 的信号线驱动,因此
R = (5 − 1.2) / 0.005 ≈ **760Ω**,标准的 680Ω 就可以很好地工作。
- **输出端(开门器):** 由 XIAO 的 3.3V GPIO 驱动,因此
R = (3.3 − 1.2) / 0.0095 ≈ **220Ω**,也就是原理图中的值。
### 物料清单
| 部件 | 备注 |
| --- | --- |
| Seeed XIAO ESP32-C6 | 控制器 |
| 3 × PC817 光耦(或类似元件) | 2 个输入,1 个输出 |
| 2 × ~680Ω 电阻 | 输入光耦 LED |
| 1 × 220Ω 电阻 | 输出光耦 LED |
| 洞洞板 | 任何小型原型板均可 |
| 4 位接线端子 | 总线连接 |
| USB-C 数据线 + 5V 电源 | 供电 |
| 杜邦线 | |
我使用了一块洞洞板,因为我手头正好有。正规的 PCB 甚至
小型面包板也能达到同样的效果。

## 刷入固件
完整的配置文件是 [`esphome/ritto-intercom.yaml`](esphome/ritto-intercom.yaml)。
Wi-Fi 凭据保存在外部,因此请先复制示例密钥文件:
```
cp esphome/secrets.yaml.example esphome/secrets.yaml
# 编辑 esphome/secrets.yaml
esphome run esphome/ritto-intercom.yaml # build, flash over USB (OTA afterwards)
esphome logs esphome/ritto-intercom.yaml # stream logs
```
该配置会在启动时将 XIAO 切换到外部天线;如果您使用的是
内部天线,请移除该代码块。
## 实际体验
节点接入后,Home Assistant 就会拥有两个 binary sensor
(`...doorbell_apartment`、`...doorbell_main`)和一个 `...open_door` 按钮。示
例自动化程序位于 [`home-assistant/`](home-assistant/) 中,已翻译为
英文,并将密钥替换为了占位符。这正是本项目真正发挥价值的地方。
**了解谁在按门铃。** 每次响铃都会发送一条 Telegram 消息。街门消息包含
一个内联的“开门”按钮,因此我可以直接从通知中
为访客开门,而无需打开任何应用程序。一个小型的重试循环使其能够
有效应对 Telegram 超时问题,这是早期导致通知遗漏的
唯一烦人原因。
**快递模式。** 这是我最常用的功能。当我不在家且
等待快递时,我会向机器人发送 `/postmode_on`。下一次街门铃响时,
门会在三秒后自动打开,快递员会将包裹留在走廊里。我完全不需要在场,甚至不需要做出反应。`/postmode_off` 会使其恢
复正常,`/postmode` 会报告当前状态。它在 Home Assistant 上本地运行,
因此不依赖 Telegram 的网络往返。
**让自己进门。** 满载而归时还要掏钥匙正是那种值得
通过自动化消除的日常小烦恼。每个受信任的人都有自己独一无二的 webhook URL 来开启大门。在我的手机上只需轻
点一下:iOS 主屏幕上的快捷指令(或门旁的 NFC 标签),或者 Android 上
的 HTTP 请求自动化。走上前,轻点,门就开了。
### 设置个人开门按钮
1. 在 `home-assistant/automations/webhook-access.yaml` 中,给每个人分配一个长随机
`webhook_id`。
2. webhook URL 为 `https:///api/webhook/` (POST)。
3. **iOS:** 快捷指令 → 新建快捷指令 → 指向该 URL 的“获取 URL 内容”,
方法为 POST。将其添加到主屏幕,或通过 NFC 标签触发。
4. **Android:** 任何支持 HTTP 请求的自动化应用(HTTP Shortcuts、Tasker 等)
调用相同的 URL。
请将其置于 HTTPS 保护之下,并将这些 webhook ID 当作您前门
的密码一样对待,因为它们的作用正是如此。
## 来源与灵感
- [deh0511.de TwinBus](https://deh0511.de/twinbus/) — 总线与端子引脚分配
- [quhfan.de: Ritto TwinBus 7630 in Home Assistant](https://quhfan.de/post/ritto-twinbus-7630-smart-home-assistant/) — 带编号的主板图例
- [nicht-trivial.de: Ritto zu MQTT](https://www.nicht-trivial.de/index.php/2018/02/14/ritto-zu-mqtt/)
- [XIAO ESP32-C6 天线切换](https://wiki.seeedstudio.com/xiao_esp32c6_getting_started/)
## 许可证
[MIT](LICENSE)
标签:ESP32, ESPHome, Home Assistant, 云资产清单, 智能家居, 物联网, 硬件破解, 逆向工程, 门禁系统