解码细气管

2021-01-09 18:10:43

TL; DR-我已经解码(大部分)Peloton自行车用于与其主机平板电脑通信的协议,并构建了一个设备PeloMon,该设备在骑行过程中获取该数据,而不会干扰Peloton软件,并通过Bluetooth LE进行广播到您想要的任何设备-手表,Zwift,Wahoo等。坚持不懈地查找逻辑分析仪的迹线,硬件图,通过蓝牙进行的固化以及一些不错的接口。

我们家里有一个细气管球。最初,我很怀疑,但是后来我找到了喜欢的教练,和朋友一起骑很有趣。在关闭相机的会议期间,确实需要边缘边缘全天变焦会议参加。

但是,作为我这个人,我很好奇这个运行皮肤浅薄的Android的主机如何从实际的自行车中收集统计数据。我也很生气,尽管Peloton软件会愉快地从HR传感器读取数据,并将您的乘车数据上传到Strava,但它无法将有关乘车的数据广播到Localfitness设备-这意味着尽管我可以跟踪我正在Garmin手表上进行``室内自行车''骑行的事实,由于没有自行车数据,因此手表会行驶0英里。

然后,几个月前,我遇到了一篇博客文章,互联网上的某人能够使用Raspberry Pi将他们的(折叠的)Flywheelbike挂接到Zwift上,以解码自行车传感器数据并通过蓝牙进行广播-最终我受到启发试一下这个项目。如果您愿意,我们应该如何称呼Peloton播放状态统计信息的设备? “ PeloMon”怎么样?

任何物理层的工作都将需要实际的硬件。如果您想继续,我将提供指向该项目涉及的所有硬件的链接。我从亚马逊和阿达菲特的混合物中买来的;请注意,亚马逊链接是会员链接,因此如果您最终在那购买,我会获得一个小的推荐奖金。

平板电脑中只有两根电缆:一个在12V电压下来自DC的桶式插孔(来自电源砖)和一个TRRS连接器—想想“立体声辅助电缆”。因为我只想观察HU和自行车之间的流量,而不是打断它,所以我使用了耳机分配器电缆,通过一个额外的插孔将自行车的电缆分开,并连接了从该额外插孔到分支的辅助电缆。这样的连接可能会破坏某些高速通信协议,但我的猜测是:a)不会有太多流量需要任何花哨的东西,b)3.5mm TRRS上运行速度不会很快,并且c)该接口应该是设计具有相当宽的容差,可以在家庭环境中保持稳定。 (扰流板警报:所有这些都是对的。)

第一步是尽可能确定插孔的引脚排列。自行车处于活动状态时,用数字多用表(DMM)探测导线,表明环2接地(相对于电源地为0V),针尖或环1与地面之间的电压为-5.5-6V。 Sleeve似乎也击败了地面,但它与Ring2可能并不完全相同(稍后会详细介绍)。相对于地面的负电压是一个非常有力的信号,我们将看到RS-232信号。

(如果您想加载PulseView跟踪信息以查看自己的数据,可以从peloton_decoding / resistance-stepped-10s.srin的PeloMon GitHub存储库中找到一个测试过程中的会话文件。)

正常情况下,我会整理分组并在Arduino上一起破解一些东西,但事实证明逻辑分析仪已经变得非常便宜,所以我花了$ 12.50买了一个,以为我会给ita whirl-结果是这是一个使信号解码变得更容易的好主意。在完全放弃接地隔离和LA的电压容限(应该能够处理负电压)的前提下,我将其连接起来以查看得到的结果。这些cheapo LA模仿Saleae Logicanalyzer,并在开源PulseView软件中正常工作。

连接LA,尖端为D0,环1为D4,套筒为D5,环2为GND

这些电线上显然有串行信令!一方似乎每隔100毫秒就在Tip上发送一次请求,另一方大约在毫秒后在Ring1上做出响应。从数字万用表(DMM)来看,从GND信号线到接地线应该是Ring2还是在Sleeve上,但是从LAtraces看来,如果将Ring2用作GND,响应线上的毛刺就更少了,因此我们将这样做。窄脉冲可能是单个位,因此以高速率(几百kHz)采样可以让我们将比特率测量为19200bps:

PulseView还允许您将协议解码器添加到特定线路。我把UART解码器放在D0上作为“ RX”,将D4放在“ TX”上,看到了数据,但是有很多低级串行协议错误。 UART反转在线路上输出的信号,并期望另一端有另一个UART进行反转。由于没有第二个UART,因此必须在PulseView中打开“ Invert RX”和“ Invert TX”选项,然后才能看到干净的数据!我还用其他串行选项(数据,奇偶校验和停止位)进行了实验,但是对8N1的第一次猜测却是正确的。

跟随数据流,我们发现乘车期间提示线上的数据流始终由三个不同的四字节数据包之一组成,每100毫秒重复循环一次,在Ring1上对这些请求的响应更长,其长度取决于提示似乎向我们显示了从主机到自行车的信号,而Ring1似乎是自行车在响应主机的自行车,其中包含有关当前速度,阻力或功率输出等变量的信息。

因此,我们为Peloton通信制定了物理层协议:5.5V的RS-232和19200bps 8N1编码,在Tip上使用HU-to-bike通信,在Ring1上使用Bike-to-HU通信,在Ring2上使用GND,并在地上进行通信。就像在袖子上一样。

为了研究主机和自行车之间的编码,我使用了逻辑分析仪来捕获骑行过程中的通信轨迹。 (Shoutoutto Ben Alldis和他的音效部骑乘系列!)数据转储和脚本可在该项目的GitHub存储库中找到,网址为https://github.com/ihaque/pelomon/,位于peloton_decoding子目录中。)

在骑行过程中,HU大约每100毫秒(100.66-100.8毫秒)向自行车发送一个请求,自行车从HU的请求结束后开始响应大约300us(即0.3毫秒)。 HU轮流发送三个不同的请求数据包,每个请求类型的响应长度与自行车不同:

第一个字节是标头-似乎是HU的F5和自行车的F1。

HU发送的第二个字节似乎是请求类型,并反映在自行车响应的第二个字节中。

自行车响应的第三个字节是完整数据包的长度减去5-可能表示跟随它的数据有效载荷的长度。

串行数据包通常带有一个校验和,这里来自HU或自行车的倒数第二个字节似乎是:它是它前面所有字节的总和(模256)。

现在我们可以对每个响应字节进行快速绘图以查找模式。

部分行程中常见请求数据包的Peloton响应数据的按字节绘制

似乎每个字节的范围从0x30到0x39-恰好是十进制数字0到9的ASCII范围。此外,每个数据包中的较早字节似乎比后面的字节变化更快,这表明最低有效数字优先。 Peloton bikeis将其响应编码为小端ASCII数字。现在我们知道编码方式,就可以很容易地按响应类型转储这些值的图表了:

看一下这些图表,很明显,我们获得了蛇elo三连击的骑行数据(踏频,力量和抵抗力),以及一些奇怪的信息。与Peloton行驶统计数据相比,请求类型0x41似乎可以直接返回以rpm为单位的节奏,而请求类型0x44可以返回以瓦特为单位的功率的10倍(即,以分瓦为单位的当前功率输出)。然而,虽然请求类型0x4a的形状看起来像电阻图,但数字却很疯狂,范围从650到几乎900。

这里要做的显而易见的事情是收集更多的数据。我使用逻辑分析仪记录了一次测试骑行的信号,在此过程中,我按顺序将电阻步进了10个点(从0开始,到10、20,…,90、100,然后又下降到95、85,…,5, 0),并保持每个电阻水平稳定至少10秒钟,然后再移动到下一个。由于HU每100ms会向自行车发送一次信息,并且只有每隔三条信息会询问电阻值,因此在0至100的每个电阻值下,应该以5的步长为我们提供大约30个(10s / 300ms)样本,并且级别之间有足够的余量。实际上,这就是我们所看到的(将稳定水平分组并在旋钮移动时过滤掉样本):

然后,将原始电阻与已知电阻作图非常容易...

该曲线非常非线性。一方面,这是意料之中的:我们对力的感知是非线性的,指数在1.4到1.7之间,这种关系被称为史蒂文斯定律。但是这里的曲线看起来比这更复杂。特别要注意的是,虽然从15到100的曲线相当平滑,但曲线下方有一些扭结,指示电阻和原始电阻之间的线性关系低于15%。曲线的顶端分辨率也很小(我在实验中也注意到了;令人惊讶的是,很难将阻力固定在95%的确切百分比上)。

怎么办呢?一种选择是简单地将回归曲线拟合给定值。但是有关Peloton支持站点的一篇文章说,对自行车进行了单独校准以抵抗出厂阻力。这表明也许在自行车本身上存储了一个映射,这些映射将这些原始传感器值映射到Peloton软件报告的(标准化)电阻百分比……但是我们没有看到任何可能与此对应的值。

校准信息必须存储在自行车中,而不是主机中。我怎么知道?当我们移动时,我们的推动者砸坏了我们的自行车的屏幕,而佩洛顿(Peloton)向美国派遣了我更换的自行车,使我能够连接起来,而不必大惊小怪。虽然Peloton HQ可能知道我们的自行车的校准并将其闪现到我们的主机中,但这似乎不太可能浪费支持时间-将其存储在自行车上并由HU查询会更加合理。如果它不是在骑行过程中来的,也许是HU从冷启动启动并在HU的正常运行时间被缓存时出现的?

有了这个想法,逻辑分析仪再一次突破了漏洞,这次在主机启动时记录了流量。瞧瞧-新的数据包类型!来回显示在下表中,其中一些值由于某些原因而被删除,我将在下面简要说明:

细管启动顺序。请注意,由于它们未提供新信息,因此我从每个消息的末尾删除了校验和和F6字节。 UU-ZZ是已编辑字节。

我们看到三种新的数据包“类别”,它们仅在启动时出现,并对我们以前观察到的模式进行了一些更改。

来自HU的第一个数据包以0xFE开头(以前,我们仅看到HU请求以0xF5开头),而自行车的响应在其请求类型字段中具有0xFE,而其他数据包本来具有来自HU的第二个字节。响应看起来像ASCII码,如果像其他类似的响应那样使用little-endian的话,则对应于015,但说实话,我不知道这是什么意思-它可能是协议版本请求(0.1.5?)或类似的一部分开始时的握手协议。

第二个请求/响应看起来更正常-HU的请求以0xF5开头,请求类型为0xFB,而自行车的响应类型为0xFB。但是,一次返回的值不是ASCII数字。在Peloton软件中挖掘一点信息表明这对正在检索自行车ID。给定响应字节“ UU VV WW XX YY ZZ”以十六进制表示,我的自行车ID为TuuVVPLWWXXYYZZ,其中uu表示十进制。向几个朋友询问他们的自行车ID并与他们每次购买Pelotons的时间相符,这表明uuVV可能是制造该自行车的年月(例如1909将是2019年9月,并且会显示为UU = 0x13,VV = 0x09;在这种情况下,UU和VV都可能需要用十进制表示(有人在10月,11月或12月生产了Peloton来确认吗?)字节WW,XX,YY,ZZ直接渲染到ID字符串中;所有我问的朋友那里只有十进制数字,所以可能不是所有十六进制空间都被实际使用了。我不知道开头的01是什么-也许它标识“自行车版本1”?

第三类请求/响应实际上来自一个31个顺序请求的块,看起来像是读取一个内存块或一个查找表。 HU发送F7 00,F7 01,…,F7 1E(注意:这些请求以F7而不是F5开头),并且自行车以再次显示为低端ASCII码的形式响应(但响应类型为字节型,但具有F7,而不是00/01 /…/ 1E字节)。自行车返回的值(当解释为小尾数ASCII时)随HU请求的假设“地址”增加而单调增加。这似乎是电阻校准数据的真正强力候选者。

如果我们做了最简单的事情,然后将这些值线性映射到0-100%的范围内(即F7 00的值对应于0%,F7 1E的值对应于100%,并且所有其他值之间线性间隔)该怎么办)?

实验获得的电阻值(蓝色)与启动序列的值(假设映射到线性电阻范围)一起绘制

值(几乎)完美对齐!除了我的实验每5%采样一次,而引导时传输的查找表显然每3.5%采样一次,以及一个电阻为10%的实验数据点似乎略有偏离之后,这些曲线相互精确地跟踪。这样看来,Peloton自行车在启动时会传送一个阻力校准查询表。在行驶过程中,它仅传输原始电阻传感器值(可能是电阻旋钮上的旋转编码器的输出,或者可能是电磁制动器上的线性传感器的输出),并且主机使用查找表对归一化的电阻进行插值以进行显示。

至此,我们已经掌握了Peloton HU和Bike在启动时以及在骑行期间传达您的步调(曲轴转速/ rpm),输出(功率)和阻力(从0归一化)时相互发送的消息的几乎完整的目录。 -100%),以及包含自行车ID的一些额外数据。这是一个简要表:

相对于GND,线上的串行通信以-5.5-6V的电压反转。数据协议为RS-232 @ 19200bps 8N1。

所有消息字节按在网络上发送的顺序写为十六进制。

CS代表校验和字节,计算为消息中所有先前字节的总和256。例如,给定一条消息01 A0 33 CS F6,则预期校验和为beD1,最后一条消息为01 A0 23 D1 F6。

HU发送的所有消息的长度为4。自行车响应的长度是可变的,并且包含“有效载荷长度”字节作为第四个字节。有效载荷标记在方括号中,以便于查看。

请求的第二个字节标识要返回的电阻校准值(甚至31个采样,包括0-100%)。数据有效载荷是低字节ASCII码,对应于请求的标准化电阻的原始电阻值。 HU在启动时发送31个数据包,其中xx的范围从00到1E。请注意,自行车响应中未返回xx。

当前原始电阻值,为小尾数ASCII。必须使用启动时发送的电阻校准表进行解码,以转换为标准化的0-100%电阻。

上面的协议信息足以满足我的目标:节奏,速度和蓝牙功率,可与其他健身追踪器配对。但是底层协议上还有一些悬而未决的问题:

对BIKE ID查询的响应(01)的第一个有效载荷字节是什么意思?

自行车还会响应其他消息吗? F5 41/44 / 4A / FB区域内部似乎有足够的地址空间。如果您从电阻表中请求超过1E的值会怎样?法律上还有什么?

与此相关的是:必须有一个协议可以在自行车的控制器硬件中设置值(例如,在工厂进行电阻校准)。哪些消息会写到自行车上,而不仅仅是阅读?它们是否有不同的格式,因为当前的HU消息格式似乎没有足够的空间容纳有效载荷(除非writecommand将地址和命令打包到第一个字节中,而有效载荷打包到第二个字节中,反之亦然)

HU仅每300ms轮询一次自行车的踏频,输出和阻力(三个信号以100ms的间隔循环轮询)。 自行车响应非常快-从HU请求结束起不到1毫秒。 自行车读取和集成其传感器的潜在速率是多少?对于功率变化非常快的事物获得更好的时间分辨率是否可行? (还有一个问题,即Peloton软件如何从仅报告节奏,力量和阻力的系统中提高速度,但我们将在以后的文章中解决!) 如果您可以对这些悬而未决的问题有任何了解,或者到目前为止尚未找到有关这些细节的任何问题,请在Twitter上给我写一行,并将其标记为#pelomon!