上周带孩子上课时,有个网友求助我一个蓝牙的问题,正巧孩子上课我有大把时间,于是便聊了起来,他使用的是nordic的平台,想要链接顽鹿和zwift这类的应用app来实现室内自行车等运动设备。
我曾经也想过这个应用,家里有一台闲置着,满是灰尘的椭圆机,只有一个简单的断码屏幕可以显示写数字,比较枯燥,一直想改装一下,无奈不得闲。
今天正巧,遇上了就尝试着了解一下,顺便帮网友也一起调试一下,不过他用的nordic平台我已经七八年不使用了,几乎回忆不起来了,因为蓝牙这个东西,每家的软件都不甚相同,跨行业的电控工程师上手还是很难的。为了方面,我先用最近玩的国产芯片,磐启微的PAN1070来实现。
之前自己做过模组,还做了一个底板,专门用来实现各种小应用。
顽鹿和zwift一样,是一个健身类的APP,模拟了实际场景,可以链接上我们室内的自行车等运动设备,通过蓝牙接受到踏频,功率和速度信息,然后就生成动画来模拟我们户外骑行。
同时,如果像上图这样,我们的室内自行车具备阻力调剂能力,软件还可以下发阻力信息,这样就能够把真实的路况信息模拟出来。对于自行车爱好者来说非常的有价值,雾霾天呀,大冬天呀都可以在家骑车了。
我不是自行车爱好者,我曾经骑摩拜单车十公里,蛋蛋都麻木的没有知觉了,所以还是把椭圆机模拟成自行车吧。
首先,蓝牙有个标准协议族FTMS,他规定了绝大部分的健康运动方面的硬件设备协议,这也让我们的硬件和手机等上位机软件具备了标准的通信能力。至于ANT+,我特别不喜欢,原因在于它不够开放,也玩不起。
我们先说广播,我们的蓝牙模块首先要广播一些必要信息,让APP能够发现我们的设备,进一步链接我们的设备。
上图是使用nrf connect扫描出来的一个广播包信息,近些年来我一直在帮一些公司招聘技术人员,面试蓝牙相关的候选人非常之多,不过,我发现他们绝大部分弄不懂蓝牙广播包的数据形式,我之前的蓝牙基础教程中也介绍过,这里不多言,放个链接:
第三篇 蓝牙的广播是如何做到多样化的?
这里解释下,蓝牙的广播包中,每一个段数据都是从LEN长度开始,后面跟着LEN个数据。其中紧跟LEN的就是广播类型,最后面的一坨是数据。
比如第二行,他就是说,后面12个数是我的,0x09就是说,我是名称,在后面的一串就是名称字符串了。
那么对于FTMS协议,我们需要再广播中展示出来,让顽鹿和zwift来识别。对应的也就是我上图中的最后一行,6个字节,TYPE类型为0x16。它的数据部分是FTMS的UUID(0x1826),再后面的指示运动设备支持的类型。具体要看蓝牙官方的文档
FTMS_v1.0.pdf
简单说,UUID后面的一个字节表示健身设备是否有效。
最后的16bit表示不同的健身设备
我上面的广播包开始弄错了,想要尝试的朋友可以按照上面的表格启用第4和5bit即可以模拟出划船机和室内自行车。
有了广播数据包,启动顽鹿APP就可以在设备列表中看到我们的设备了。
当APP这边链接好了以后,我们就可以按照APP的操作逻辑来进行数据上报了。
数据上报的指令都是Notify的,具体的可以参考官方文档,这里有个华为开发的网战,上面是中文的,看起来会省很多力气。
文档中心
这里面规定了我们蓝牙设备需要实现的服务,基本上包含两个服务:
DIS标准特征值支持范围
FTMS标准特征值支持范围
他们详细的列表可以打开我上面放的链接查看,这里不粘贴过来了。我们以Indoor bike为例来说明一下这服务的协议内容。
这里的UUID就是我们需要在0x1826服务下面生命的特征值。顽鹿APP在读取到0x1826下面的0x2AD2特征值时,就会订阅我们的Notify特征,此时我们就可以发送对应的Notify数据了。顺着这个链接点进去就能够看到具体协议内容。
蓝牙的很多协议,为了保证数据内容的灵活性,都采用了类似的方式,就是先放一个长度或者标志,来决定后面跟的数据是什么,这种架构极大的扩展了蓝牙协议的扩展性。
你看,indoor bike data中的前两个字节是flags,这里有16个bit,每一个bit代表着某一个数据是否上传。简单来说,加入Flags中有两个bit被置位了,那么后面我们应该跟上两个参数,参数的长度类型在标准文档里有定义。
表格中的每一个bit都做了介绍,尤其要注意第一个,它与众不同,置0时表示有对应的数据。不知到为了啥,我想肯定是为了提高什么效率之类的。
接下来看具体数据的类型。
这里面列出来每种数据类型格式和单位,后面的必选和可选忽略它,这只是华为这边自己要求的,实际蓝牙协议里面都是可选的。
让人不舒服的一点就是第一项,不用instantaneous speed,非得用more data,不知到为什么,可能是因为其他的健康设备协议中,第一个bit有可能代表多个数据,划船机就是这样。
如果你的自行车不支持阻力调节,其实到这里,我们就可以在软件中吃撑天涯了,但是如果想实现阻力控制,我们还需要实现一个特征值Fitness Machine Control Point。这里以后再说。
先来看效果。