逆向工程项目。它始于 Bowflex 跑步机 22,但最终被推广到 Nautilus Inc.(Nautilus、Bowflex、Schwinn)销售的任何由 Android 驱动的设备。
这主要适用于 Treadmill 22 和 Treadmill 56。
电机控制板由 Electronics Way Industry 制造。
根据 Nautilus Inc. 提供的 服务手册(archive.org 的备份):
特别关注这一部分:
我们可以识别出连接电机控制器的“通信电缆”是一个 5 针电缆。只有一个 5 针连接器。 我已经用相应的颜色标注了电缆(数据和开关是光隔离的):
电缆颜色 | 标签 |
---|---|
红色 | GND |
白色 | RXD |
黑色 | TXD |
黄色 | +12 |
绿色 | SW |
该板未直接连接到 Android 控制台。
唯一的 5 针连接器来自 Molex。谷歌搜索“小 Molex 连接器”让我看到了他们所说的 Molex Micro-Fit 3.0 Single Row (5-Pin)
的图片,用于连接电机控制器板:
通过 jadx-gui
查看 NautilusLauncher.apk
,我可以看到他们使用 230400 波特率的串行通信将 Android 平板电脑与他们的“通用控制台”连接(使用 /dev/ttyS4
)。这不是我们在这里分析的内容。那指的是 Android 与“通用控制台”之间的通信。我们正在调查“按钮面板控制器”和“电机控制板”之间的通信,从而排除了三个板作为潜在的故障点。
尝试将 ESP32 或基于 CH340 的串行桥直接连接到跑步机底座和 Bowflex 控制板之间的电缆导致跑步机底座无法正确初始化,之后我获取了一个逻辑分析仪以进一步调查。
在最近几周,几乎在我开始这个项目三年后,几个人联系我询问进展,确认了我最初的假设,即跑步机系统很糟糕,机器开始故障只是时间问题。看起来是时候使用我的逻辑分析仪了,它直到现在才被用上,此前它只是积灰。
将逻辑分析仪连接到 TXD 和 RXD 线路(当然还有 GND),我立即能够在不干扰通信的情况下拦截双方的消息。我猜我最初不能使用 ESP32 是由于阻抗问题。经过几分钟的试验和错误,我得出了以下串行配置:
- 2400 波特率
- 每帧 8 位
- 1 个停止位
- 无奇偶校验位
- 最低有效位先发送
- TXD:信号反转
- RXD:信号不反转
使用这些设置,我可以清晰地看到定义明确的消息。
引导过程中拦截 UART 消息
我立即注意到的一些事情:
0x68
开头0x73
开头0x43
结尾在此基础上,开始解读消息并理解双方之间传递的信息,进行有控制的锻炼程序更改。
通过对特定速度进行有控制的更改,可以观察到以下发送到电机控制板的值:
屏幕上的速度 | 发送的消息 |
---|---|
0.0 公里/小时(等待或暂停) | 0x68 0x08 0x80 0x50 0x00 0x0A 0x00 0x00 0xE2 0x43 |
2.0 公里/小时 | 0x68 0x08 0x80 0x50 0x14 0x0A 0x00 0x00 0xF6 0x43 |
3.0 公里/小时 | 0x68 0x08 0x80 0x50 0x1D 0x0A 0x00 0x00 0xFF 0x43 |
5.0 公里/小时 | 0x68 0x08 0x80 0x50 0x31 0x0A 0x00 0x00 0x13 0x43 |
可以观察到,第 5 字节和第 9 字节发生了变化。第 5 字节似乎是十六进制的速度,第 9 字节似乎是校验和。
将第 5 字节的值转换为十进制:
屏幕上的速度 | 十六进制 | 十进制 |
---|---|---|
0.0 公里/小时(等待或暂停) | 0x00 | 0 |
2.0 公里/小时 | 0x14 | 20 |
3.0 公里/小时 | 0x1D | 29 |
5.0 公里/小时 | 0x31 | 49 |
回忆起几年前反编译 Android 系统的部分内容,我记得在配置机器为公制系统时,Bowflex 应用程序内部执行了从公制到英制的转换,以与“UCB”通信。电机控制板似乎使用公制系统,显然,在从公制转换到英制然后再转换回公制(电机控制板所期望的)时,精度有所损失,因为一切都是以 1 位小数的精度处理。Nautilus,难道这样做这么难吗?
考虑到这一点,如果应用缩放因子 10,正好与发送到电机控制板的值匹配。因此,公式如下:
十进制值 = 速度(公里/小时) × 10
按照与速度相同的过程,可以观察到以下发送到电机控制板的值:
屏幕上的坡度 | 发送的消息 |
---|---|
-5° | 0x68 0x08 0x80 0x50 0x1D 0x00 0x00 0x00 0xF5 0x43 |
0° | 0x68 0x08 0x80 0x50 0x1D 0x32 0x00 0x00 0x27 0x43 |
9° | 0x68 0x08 0x80 0x50 0x1D 0x8C 0x00 0x00 0x81 0x43 |
在这种情况下,第 6 字节似乎是十六进制的坡度,并且确认第 9 字节是校验和。
将第 6 字节的值转换为十进制:
屏幕上的坡度 | 十六进制 | 十进制 |
---|---|---|
-5° | 0x00 | 0 |
0° | 0x32 | 50 |
9° | 0x8C | 140 |
完全匹配发送到电机控制板的值的公式是:
十进制值 = (角度 + 5) × 10
这似乎是微控制器中一种简单且标准的校验和,将消息的所有字节相加并在达到 256 时发生溢出。一个简单的表示方式如下:
uint8_t calculateChecksum(uint8_t *msg) {
return msg[1] + msg[2] + msg[3] + msg[4] + msg[5] + msg[6] + msg[7];
}
通过使用 uint8_t
作为返回类型,溢出会自然发生。可以使用 for
循环来求和并返回 sum % 256
,但对于没有任何实际好处的微控制器来说,这会更慢。
通过这些,按钮面板的功能可以被复制,从而可以通过微控制器控制跑步机。
— 待续 —
©2022-2025 Sebastian Barrenechea. 保留所有权利.
构建于 Astro v5.6.1.