2.5. 详细时序图

2.5.1. 获取手环状态

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.status</color>()
B -> G : <color #118888>CougarResponse.status</color>(\n\t<color purple>localTimestampInSecond</color>,\n\t<color purple>softwareVersion</color>,\n\t<color purple>hardwareVersion</color>,\n\t<color purple>heartRateHasNewData</color>,\n\t<color purple>accHasNewData</color>,\n\t<color purple>temperatureHasNewData</color>,\n\t<color purple>beaconHasNewData</color>)

网关发送 CougarRequest.status() 可以获取手环的 时间戳,软件版本,硬件版本和是否有新数据。

重要

如果手环重启,那么存储的数据不会丢失,但是手环直接认为历史数据都是可以擦除的。如果剩余空间不够,会擦出旧数据。

2.5.2. 设置手环

设置里包含多个设置项。这些设置项可以独立设置,也可以组合在一起一次设置。但是`擦除`需要单独一条命令。如果设置里包含`擦除`,那么其他设置会被忽略。

如果有多条设置项一起下发,手环是按下列顺序处理。一个设置项出错就会返回错误,剩余设置项会被跳过。

  • 擦除(erase)

  • 新的时间戳(newTimestamp)

  • 广播设置(advSettings)

  • Beacon 扫描设置(beaconScanSettings)

  • 休眠设置(sleepSettings)

  • 加速度设置(accSettings)

下面对每个设置项依次介绍:

2.5.2.1. 新的时间戳(newTimestamp)

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.settings</color>(<color purple>newTimestamp</color>)
B -> G : <color #118888>CougarResponse.settings</color>(<color purple>errorCode</color>)

手环只关心以秒为单位的时间戳而非日期。初次上电或重启之后时间戳是0,且不会自增。获取到新的时间戳后,手环的时间戳才开始按秒自增,并采集记录传感器数据。每天网关也可以用此命令同步手环的时间。

重要

  1. 因为更小的数字在传输时可以节约字节数,所以可以用最近的某个时间点(比如2025/01/01 00:00:00)作为初始时间。

  2. 手环每天的时间漂移不到10秒。网关可以每天使用此命令更新一下手环的时间。手环会以每过2秒拨快(慢)1秒的速度慢慢更新到新时间,以防止记录的传感器数据的时间戳回退或大幅跳跃。

手环返回的 errorCode 范围和含义如下:

  • SettingErrorCode.SE_SUCCESS: 成功

2.5.2.2. 广播设置(advSettings)

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.settings.advSettings</color>(<color purple>miniReportThresholdInMinute</color>)
B -> G : <color #118888>CougarResponse.settings</color>(<color purple>errorCode</color>)

为了节省电量,手环BLE广播有以下两个规则:

  1. 广播是按照广播1秒,停止1s这个循环工作的。目前此行为不可修改,未来可能会做调整。

  2. 在每天的工作时段,如果网关从手环取走了所有的数据,广播会停止一段时间,然后再次开启。

此设置可以控制广播停止时长:最小报告时间间隔(miniReportThresholdInMinute),单位是分钟,从网关断开BLE连接开始计算。

手环返回的 errorCode 含义如下:

  • SettingErrorCode.SE_SUCCESS: 成功

  • SettingErrorCode.SE_INVALID_PARAMETER: 参数范围不合法。miniReportThresholdInMinute 合法范围是:[1, 60]。

2.5.2.3. Beacon 扫描设置(beaconScanSettings)

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.settings.beaconScanSettings</color>(\n\t<color purple>bleInterval</color>, \n\t<color purple>bleWindow</color>, \n\t<color purple>bleDuration</color>, \n\t<color purple>intervalInSecond</color>, \n\t<color purple>beaconName</color>)
B -> G : <color #118888>CougarResponse.settings</color>(<color purple>errorCode</color>)

Beacon 扫描设置控制的是手环扫描Beacon的行为。总共包含五个参数,其中前三个是底层BLE的参数需要和Beacon的广播匹配,以便能尽快扫描到Beacon以节省电量。

  • bleInterval:手环会以这个周期开始持续扫描。数值合法范围是[4,16384],单位是625us

  • bleWindow: 一个 bleInterval 周期内 BLE 持续扫描的时间。数值合法范围是[4,16384],单位是625us,必须小于等于bleInterval。

  • bleDuration:一次BLE扫描的持续时间。数值合法范围是[1,6000],单位10ms。

  • intervalInSecond:多久触发一次扫描Beacon。每次扫描都会按前3个参数设计的行为扫描Beacon,直到超时。数值合法范围是[1,60],单位1s。此时间必须大于 bleDuration 的时间。

  • beaconName:需要扫描的Beacon的名字。手环会记录所有以此命名的beacon的MAC。名字最长20字节,超过的话手环会解码失败,不会有任何response发送回去。

../_images/drawio-f33e7fca0477e2c3773b00d7cbee6b3a6973215e.png

几个扫描行为的参数的关系

建议的参数如下:

参数

数值

bleInterval

600

bleWindow

500

bleDuration

100

intervalInSecond

10

手环返回的 errorCode 含义如下:

  • SettingErrorCode.SE_SUCCESS: 成功

  • SettingErrorCode.SE_INVALID_PARAMETER: 参数范围不合法。

2.5.2.4. 休眠设置(sleepSettings)

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.settings.sleepSettings</color>(\n\t<color purple>append</color>, \n\t[<color purple>SleepCommand</color>])
B -> G : <color #118888>CougarResponse.settings</color>(<color purple>errorCode</color>)

休眠是为了让手环完全进入低功耗模式。休眠时间到时,手环会停止采集数据,停止BLE广播(如果手环有数据未上传,不会关闭BLE广播),停止扫描Beacon。

休眠设置有两个参数:

  1. append : 此设置里的休眠命令是追加还是替换之前的休眠命令。为 True,则追加。为 False,则替换。

  2. sleepCommands: 这是一个数组。表明一系列时间戳和对应的时间。比如:在1000秒休眠,在1500秒唤醒,在2000秒休眠,在2500秒唤醒。

SleepCommand 结构包含如下两个成员: #. timeStampInSecond: 事件的时间戳 #. sleep: True 为休眠,False 为唤醒。

手环返回的 errorCode 含义如下:

  • SettingErrorCode.SE_SUCCESS: 成功

  • SettingErrorCode.SE_INVALID_PARAMETER: 休眠设置里最后一个命令必须是唤醒。

2.5.2.5. 擦除(erase)

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.settings</color>(<color purple>erase</color>)
group 仅当 erase 是  EraseOptions.EO_PERSONAL 时
B -> G : <color #118888>CougarResponse.settings</color>(<color purple>errorCode</color>)
end

可以选择擦除

  • 用户数据(EraseOptions.EO_PERSONAL)

  • 设置(EraseOptions.EO_SETTINGS)

  • 全部(EraseOptions.EO_ALL)

擦除 设置 或者 全部 之后,手环不会回复response而是直接重启。状态回到初次上电。

手环返回的 errorCode 含义如下:

  • SettingErrorCode.SE_SUCCESS: 成功

2.5.2.6. 加速度设置(accSettings)

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.settings.accSettings</color>(<color purple>threshold</color>)
B -> G : <color #118888>CougarResponse.settings</color>(<color purple>errorCode</color>)

加速度设置可以设置加速度阈值(threshold)。阈值单位是1/512 g,即0.019140625 m/s2。 当x,y,z的合加速度绝对值大于此阈值时,x,y,z会被记录。

手环返回的 errorCode 含义如下:

  • SettingErrorCode.SE_SUCCESS: 成功

  • SettingErrorCode.SE_INVALID_PARAMETER: threshold必须小于2048。

2.5.3. 获取传感器记录的数据

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
...
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.report</color>(\n\t<color purple>startTimestamp</color>,\n\t<color purple>endTimestamp</color>,\n\t<color purple>reportType</color>)
G <- B : <color #118888>CougarResponse.report</color>(<color purple>index = 0</color>,<color purple>data = [...]</color> )
G <- B : <color #118888>CougarResponse.report</color>(<color purple>index = 1</color>,<color purple>data = [...]</color> )
... 连续发送 ...
G <- B : <color #118888>CougarResponse.report</color>(<color purple>index = n</color>,<color purple>data = [空]</color> )

Step 1 中网关必须填写 startTimestamp 和 endTimestamp 来指定获取哪个时间段的数据。

手环收到request后会准备数据。因为要发送的数据可能会超过MTU,所以数据会被拆分成N个包发送。网关收到的 CougarResponse.report 规则如下:

  1. index 从 0 开始,每发送一个包,index会增加1。

  2. data是被拆分的数据。如果网关收到了一个 data 的长度为0,则表明数据全部发送完毕,

  3. 不同传感器的 data 格式各不相同,里面包含若干个传感器数据单元。(例如,心跳的数据单元是:时间戳+心跳值)

  4. 一个传感器数据单元不会被拆分到两个 CougarResponse.report.data 里。

传感器数据单元里的格式是经过简单压缩编码的。首先,需要了解如下编码格式:Varints。

2.5.3.1. 数据的基本编码格式(Varints)

Varints 会把一个int编码成一个变长数组。数组里每一个字节都是一个基本单元。

一个基本单元由一个标识位(F)和7个数据位组成。如果MSB是1,代表后续还有基本单元。MSB是0,则这个最后一个单元。

../_images/drawio-1b45c6e5b02d6ade72c1ddacffb5e17e9fdc46aa.png

Varints的基本单元结构

下面是两个例子,分别展示如何解码 0000 0001 和 10010110 00000001

../_images/drawio-6ffd30ba05d413a520d998f0b0558c62587bf9dc.png

Varints解码示例

2.5.3.2. 负数问题(ZigZag)

如果一个int是负数,按照Varints规则会编出一个最长数据。所以对于一个可能是负数的待编码数据,使用 ZigZag 编码把负数转成正数,再用 Varints 编码。

ZigZag 编码规则是:正数n编码成 2*n,负数n编码成 2 * -n - 1。 如0编成0, 1 编成 2,-1 编成 1, -2 编成 3。

../_images/drawio-6ebe3aa7ebc7b08a1132564992b83c04b8f3d5c7.png

ZigZag编码

解码一个ZigZag数据使用如下代码:

1n = (n >> 1) ^ -(n & 1)

备注

负数经过 ZigZag 编码之后,再经过 Varints 编码就可以把编码之后的长度缩小很多。换来的代价是正数的编码比直接 Varints 编码可能会略长。

2.5.3.3. 心率数据的格式

每一个有效的 CougarResponse.report.data 里面都是一个 时间戳差值(dt)+心跳(h) 结构 的数组。示例如下:

../_images/drawio-ab56d0237b1abb333aa9899d017383ea26bc502f.png

心率数据格式

dt均经过Varints编码,心率值是一个字节(uint8_t)的原始数据。

dt0代表的时间差是指此心跳数据和 startTimestamp 之间的差值。dt1代表的时间差是指此心跳数据和前一个时间戳的差值。后面以此类推。

以下是示例。report.startTimestamp 是 6443500,得到的 3 个 report.data 数据分别是

index: 0

01510554055b05600560055f055e055e055d055a0559055905590559055805530550054f054d054c05530557055905590559055805580556055405550558055405500555055a055c065a0459055805550556055705580558055905590558055705570557055705560555055505530552054d054c054d

index: 1

a80250055505570554055005510656045a055d065f045f055f065f045f055f0560055f055d055905580558055b0555054f064d0450

index: 2

也就是说有效数据是两个数据包。解码出来的两个数据分别是(格式是 时间戳:数据):

 16443501:81
 26443506:84
 36443511:91
 46443516:96
 56443521:96
 66443526:95
 76443531:94
 86443536:94
 96443541:93
106443546:90
116443551:89
126443556:89
136443561:89
146443566:89
156443571:88
166443576:83
176443581:80
186443586:79
196443591:77
206443596:76
216443601:83
226443606:87
236443611:89
246443616:89
256443621:89
266443626:88
276443631:88
286443636:86
296443641:84
306443646:85
316443651:88
326443656:84
336443661:80
346443666:85
356443671:90
366443676:92
376443682:90
386443686:89
396443691:88
406443696:85
416443701:86
426443706:87
436443711:88
446443716:88
456443721:89
466443726:89
476443731:88
486443736:87
496443741:87
506443746:87
516443751:87
526443756:86
536443761:85
546443766:85
556443771:83
566443776:82
576443781:77
586443786:76
596443791:77
60
616443796:80
626443801:85
636443806:87
646443811:84
656443816:80
666443821:81
676443827:86
686443831:90
696443836:93
706443842:95
716443846:95
726443851:95
736443857:95
746443861:95
756443866:95
766443871:96
776443876:95
786443881:93
796443886:89
806443891:88
816443896:88
826443901:91
836443906:85
846443911:79
856443917:77
866443921:80

2.5.3.4. 加速度数据的格式

和心跳数据格式类似,加速度data里的格式是一个 时间戳差值(dt)+x+y+z 结构 的数组。示例如下:

../_images/drawio-4e3d8a00864ce745b1193c6bc72411d5c06d1d2a.png

加速度数据格式

dt的编码和含义同心率。x,y,z存储的是对应轴上的加速度值,单位是1/512 g,即0.019140625 m/s2。因为可能有正负,所以x,y,z都是经过ZigZag编码再Varints编码。

以下是示例。report.startTimestamp 是 6443500,得到的 2 个 report.data 数据分别是

index: 0

ae06f4039d109e0900b4038a01ba0b00ca09c1029417019201e101900901a7025eb10e003e25880901a105c0019d1e00d80627880501d206fa0cfa0600fa02f202aa12009b01e101f90901ff0a04ba0a00b206ee019a0a00910267e30d008e02b004fd1500930c008e0d00ea0aa40cc20f00e006cc01ef0901a208a404be1200bd09cf028d0600b80248d60800ad0db107f107027087019a0e008604b302e40700a306de03ad0300d2014dfc0701c107ff07810800d6048603ec0901f906c9028d050069a904b90900d304e006b81501990c06860d003ec5038908006822a809

index: 1

也就是说有效数据是一个数据包。解码出来的数据是(格式是 时间戳: x:%d, y:%d z:%d):

 16444314: x:250, y:-1039, z:591
 26444314: x:218, y:69, z:733
 36444314: x:613, y:-161, z:1482
 46444315: x:73, y:-113, z:584
 56444316: x:-148, y:47, z:-921
 66444316: x:31, y:-19, z:580
 76444317: x:-337, y:96, z:-1935
 86444317: x:428, y:-20, z:324
 96444318: x:425, y:829, z:445
106444318: x:189, y:185, z:1173
116444318: x:-78, y:-113, z:-637
126444319: x:-704, y:2, z:669
136444319: x:409, y:119, z:653
146444319: x:-137, y:-52, z:-882
156444319: x:135, y:280, z:-1407
166444319: x:-778, y:0, z:839
176444319: x:693, y:786, z:993
186444319: x:432, y:102, z:-632
196444320: x:529, y:274, z:1183
206444320: x:-607, y:-168, z:-391
216444320: x:156, y:36, z:555
226444320: x:-855, y:-473, z:-505
236444322: x:56, y:-68, z:909
246444322: x:259, y:-154, z:498
256444322: x:-402, y:239, z:-215
266444322: x:105, y:-39, z:510
276444323: x:-481, y:-512, z:-513
286444323: x:299, y:195, z:630
296444324: x:-445, y:-165, z:-327
306444324: x:-53, y:-277, z:-605
316444324: x:-298, y:432, z:1372
326444325: x:-781, y:3, z:835
336444325: x:31, y:-227, z:-517
346444325: x:52, y:17, z:596

2.5.3.5. Beacon扫描数据的格式

和心跳数据格式类似,Beacon data里的格式是一个 时间戳差值(dt)+MAC+rssi 结构 的数组。示例如下:

../_images/drawio-a4af304c4ddb6bb9e3b1de3878c0cc95fba9e6ad.png

Beacon数据格式

dt的编码和含义同心率。MAC和rssi是固定长度。 手环在非睡眠时间按照设定的参数扫描beacon,并记录下信号最强的前 3个 beacon 的 MAC 和 rssi 。

2.5.3.6. 体温数据的格式

体温的格式同心率。只是心率数据换成体温既可。体温是一个字节的有符号数。

以下是示例。report.startTimestamp 是 6440000,得到的 3 个 report.data 数据分别是

index: 0

250d2a0f280f2a0f280f2a0f280f2a0f280f2a0f280d2a0f280f2a0f280f2a0f280f290f290f290f2a0d280f290f290f290f290f290f290f290f290f290d290f290f290f290f290f290f290f290f290f290d290f290f290f290f290f290f290f290f290f290f290f290fab030d2810290f290f290f

index: 1

f0150f290f2a0f

index: 2

也就是说有效数据是两个数据包。解码出来的两个数据分别是(格式是 时间戳:数据):

 16440037:13
 26440079:15
 36440119:15
 46440161:15
 56440201:15
 66440243:15
 76440283:15
 86440325:15
 96440365:15
106440407:15
116440447:13
126440489:15
136440529:15
146440571:15
156440611:15
166440653:15
176440693:15
186440734:15
196440775:15
206440816:15
216440858:13
226440898:15
236440939:15
246440980:15
256441021:15
266441062:15
276441103:15
286441144:15
296441185:15
306441226:15
316441267:13
326441308:15
336441349:15
346441390:15
356441431:15
366441472:15
376441513:15
386441554:15
396441595:15
406441636:15
416441677:13
426441718:15
436441759:15
446441800:15
456441841:15
466441882:15
476441923:15
486441964:15
496442005:15
506442046:15
516442087:15
526442128:15
536442169:15
546442596:13
556442636:16
566442677:15
576442718:15
586442759:15
59
606442800:15
616442841:15
626442883:15

2.5.4. 获取Log数据

!pragma teoz true
participant 网关 as G #cyan
box 手环 #LightBlue
participant BLE as B
end box

autonumber
...
hnote across: 连接 BLE
G -> B : <color #118888>CougarRequest.report</color>(\n\t<color purple>0</color>,\n\t<color purple>0</color>,\n\t<color purple>reportType=RT_LOG</color>)
G <- B : <color #118888>CougarResponse.report</color>(<color purple>index = 0</color>,<color purple>data = [...]</color> )
G <- B : <color #118888>CougarResponse.report</color>(<color purple>index = 1</color>,<color purple>data = [...]</color> )
... 连续发送 ...
G <- B : <color #118888>CougarResponse.report</color>(<color purple>index = n</color>,<color purple>data = [空]</color> )

获取Log的命令同获取传感器数据的命令。startTimestamp 和 endTimestamp 的值会被忽略,直接设置成0即可。

每次手环收到命令之后,会把从上次发送的Log之后产生的新Log全部发送出去。每次手环重启之后会找到最早的一条log作为Log的发送起始点。

重要

Log 数据不是可打印的数据,而是经过压缩编码之后的二进制数据。需要用特殊工具解析。