由于Covid,我今年一直遇到我平常的锻炼。为了帮助解决这个问题,我决定抓住机会并订购桌面运动自行车以获得更多的有氧运动。我买的自行车是体面的:良好的紧张,不会移动,非常安静,并具有用于跟踪锻炼的蓝牙连接。
如果你'重新考虑在桌子上获得其中一个,我会自动推荐它。如果您的办公桌正常大小,您将跪在地上。但我的办公桌旨在与键盘托盘一起使用,我讨厌键盘托盘。所以相反,我的键盘位于桌子的顶部,我的椅子被提升,我使用了脚凳。这意味着我的办公桌实际上是兜售的完美高度。
蓝牙连接对我来说很重要;能够看到和记录速度和距离是一个大动力。为了连接,无法置于手机应用程序。它不是一个好的应用程序。它感觉不受影响并获得了Mediocre评论。当然,它还需要注册并尝试在课程订阅时追随您。我可以忽略这一切。但是,坐在我的工作站上,我不想每次想要锻炼我的手机并经过5个步骤。我也不想让我的手机保持在我的桌子上长时间。最后,我非常不希望在这个应用程序中锁定的所有锻炼数据。
我的灵感来自这篇文章:用10美元的覆盆子pi毫不含量为2,000美元的自行车。我想我的桌子自行车可能以类似的方式工作,如果我可以从我的桌面连接到它,我可以写自己的应用程序。
我通过亚马逊的USB加密狗解决了第一次并发症。第二个并发症会在我去的时候解决。
第一个任务是谷歌尽可能多的信息。这导致了一个名为NRF Connect的软件。此Android软件显示有关所有蓝牙设备的信息,允许您连接到它们,向您展示所有可用的配置文件和日志数据。有了这个,我了解了自行车使用一个名为Nordic Uart的芯片。这基本上是一端的串行连接,另一端是蓝牙。这是一个很棒的新闻 - 它应该意味着自行车只是流字节。
但是,与NRF连接到它并立即将其连接到自行车上的任何数据。它已连接,我订阅了变化,但没有发送任何数据。这需要更多的工作。
所以下一步是运行应用程序,并记录所有蓝牙流量以查看正在发生的事情。更多Googling向我发送了本文,描述了在我的手机上启用蓝牙日志并下载以进行分析。我在手机上启用了屏幕录制,启用蓝牙记录,在自行车上进行了几个运行,然后下载了日志。
使用Wireshark并将其与我录制的视频进行比较,我看看应用程序如何与自行车通信。该应用程序将发送命令数据包,并且自行车将以一个或多个数据包响应。当锻炼启动时,该应用程序将连续发送单个命令,并且自行车将以类似于查找数据的数据包响应。
使用日志和Wireshark,我确定该应用程序发送6个不同的命令。因此,下一步是创建一个可以连接到自行车的简单控制台应用程序,发送命令并接收结果。我需要查看我是否可以在桌面上使用自己的软件复制应用程序的活动。
此冒险的选择平台是Windows上的.NET 5.0。我需要访问Windows中蓝牙API的Windows运行时。我发现这很容易。
App发送我将调用Connect命令的第一个命令。必须在任何其他命令之前发送此命令。发送此命令后,必须至少将命令发送一秒的时间,或者自行车将关闭连接。
应用程序发送我调用hold命令的第二个命令。由于您必须定期发送命令,一旦连接,发送此命令似乎会似乎保持连接打开。每次发送此命令时,骑自行车将响应2个相同数据的数据包。
hold command:f9 d1 05 02 00 00 00 00 00 d1 response#1:f9 e1 10 07 00 00 00 00 00 00 00 02 00 03 37 00 00 2ARESPONSE#2:F9 E2 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 eB
第3和第4命令我只需调用INFO1和INFO2。这些命令由App每次连接发送一次,它们始终返回相同的信息。这可以是模型或校准信息。
INFO1命令:F9 D3 0D 01 00 00 2C 00 00 3C 00 A0 00 00 00 00 E2 00 00 00Response#1:F9 E3 01 00 ddResponse#2:F9 E3 0C 00 00 00 00 00 00 00 00 00 00 00 00 e8Info2 Command:F9 D4 0F 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
此时我们可以看到模式出现。每个命令和每个响应都以字节F9开始。下一个字节包含命令或响应类型;高分性是用于命令和e的d。下一个字节是不包括此标题的数据包的长度。后面的一个字节似乎是某种校验和。
下一个最有趣的命令。我调用这些开始锻炼并继续锻炼命令。启动锻炼时发送第一个命令,然后将应用程序连续发送第二个命令以从自行车上对数据进行示例。
开始CMD:F9 D5 0D 01 00 00 00 00 00 00 00 00 00 00 00 00 DC 00 00 00Response#1:F9 E5 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 efResponse#2:F9 E6 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 efResponse#3:F9 E7 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 F0继续CMD:F9 D5 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0003 00 07 00 00 00 00 00 0001八0007 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00>
此时,我可以开始组装和解释数据。我所做的第一件事就是将所有锻炼结果包组合在一起,并延迟标题。然后我抓住了10分钟收集数据。
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EF 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 01 F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EF 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 02 00 00 00 00 00 00 00 00 00 00 00 00 00 01 F1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EF 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 03 00 00 00 01 00 00 00 BC 00 00 66 00 00 01 15 00 00 00 00 07 00 00 00 00 00 00 00 00 00 19 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 03 00 01 00 01 00 00 00 BB 00 00 65 00 00 01 14 00 00 00 00 07 00 00 00 00 00 00 00 00 00 19 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 04 00 01 00 02 00 00 00 BE 00 00 67 00 00 01 1B 00 00 00 00 07 00 00 00 00 00 00 00 00 00 25 1B 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 05 00 01 00 03 00 00 00 C1 00 00 69 00 00 01 22 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 04 00 00 00 C3 00 00 6 A 00 00 01 28 00 00 00 00 07 00 00 00 00 00 00 00 00 00 32 28 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 07 00 02 00 06 00 00 00 C1 00 00 69 00 00 01 28 00 00 00 00 07 00 00 00 00 00 00 00 00 00 36 2C 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 07 00 03 00 06 00 00 00 C1 00 00 69 00 00 01 29 00 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0003 00 00 00 3B 31 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0A 00 04 00 09 00 00 00 B0 00 00 5F 00 00 01 15 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3C 32 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0B 00 04 00 0A 00 00 00 B5 00 00 63 00 00 01 20 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3D 33 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 25 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 05 00 00 00 00 01 25 00 00 0001 00 00 00 00 00 00 00 3D 33 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0C 00 05 00 0C 00 00 00 BB 00 00 66 00 00 01 2D 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3E 34 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0D 00 06 00 0D 00 00 00 BF 00 00 68 00 00 01 36 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3F 35 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6 00 00 00 00 00 00 00 00 00 00 F0 0F 00 06 00 0F 00 00 00 BF 00 00 68 00 00 01 3A 00 00 00 00 07 00 00 00 00 00 00 00 00 00 41 37 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0F 00 07 00 0F 00 00 00 BE 00 00 67 00 00 01 39 00 00 00 00 07 00 00 00 00 00 00 00 00 00 41 37 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0 ...
有些值连续增加,而其他值与我兜售的速度直接相关。当没有兜售时,后一个值也为零。
基于这些观察,我将代码将数据分解为9个不同的字节和Word字段。在锻炼期间再次运行我的控制台应用程序,将这些值收集到CSV中,然后将其扔进Excel进行分析。通过观察它们,大多数值变得非常明显。我最慢的部分是弄清楚自行车发送Imperial测量而不是指标。 9个字段为:
目前的第二个锻炼。此值超过0到59遍历。如果比每秒更快地样本,则该值将保持多个样本相同。
锻炼时间在几秒钟内。这从零开始,只要我兜售,就继续增加。当我停止兜售时,这个值将保持不变。
未知(一个字节)。似乎与锻炼速度有关,但如果没有移动它每秒增加一个并定期重置为值,然后保持计数。
2其他未知的字节值。两者似乎是与整个锻炼的速度相关的平均值,但到底是它代表我不知道的。
通过这些观察,我决定只需要3个命令:connect命令,start workout命令和continue workout命令。我还需要了解命令;我只需要以此顺序发送这些字节。
最后一步重新安排我完成了一致API的工作,然后为用户界面创建WPF应用程序。 这个部分是' t太难; 这是MVVM的完美应用类型。 我还使用实体框架核心来非常快速地放在一起的SQLite存储以进行锻炼信息。 在锻炼结束时,值切换到锻炼的平均值。 自动检测自行车上的蓝牙激活(通过兜售开启)。 这开始锻炼并弹出窗口。 兜售停止时暂停锻炼。 暂停时的锻炼时间将闪烁。 一分钟暂停锻炼后自动结束。 窗口可以始终设置为顶部(右键单击上下文菜单的窗口)。 将锻炼信息和所有样本从自行车上保存到SQLite数据库。 该应用程序很棒,是我希望的一切。 现在它只是一个看到我可以兜售有多远的问题。