> 文章列表 > bthome协议分析及esp32上的实现

bthome协议分析及esp32上的实现

bthome协议分析及esp32上的实现

前言

最近自己搞了些智能家居的小节点,但由于wifi入网方式功耗太高,于是关注起了蓝牙

bthome是一种灵活的低功耗BLE数据格式协议,用于广播传感器数据,此协议支持数据加密,目前最新为v2版本。在home assistant中也支持协议,这意味着自己DIY的传感器采集节点,通过BLE广播数据时只要遵守此协议,该节点就能被HA探测到并解析传感器数据。

协议官方说明见https://bthome.io/format/,但我在阅读过程中由于我个人阅读理解能力有限,感觉其文档术语混乱,结构混乱,所以根据自己理解写下这篇文章

协议分析

此协议只是对蓝牙数据包中PDU中数据格式的规定,蓝牙Link Layer层数据包格式如下
bthome协议分析及esp32上的实现

bthome对payload中的数据格式作出了规定。

bthome协议分析

bthome协议支持对传感器数据加密,根据是否加密与否,其数据结构也稍有不同,本文将其简称为非加密结构和加密结构

1. 非加密结构

bthome协议分析及esp32上的实现

  • head

    头部长度3字节,对于bthome来说是固定的:0x02 0x01 0x06

  • local name

    长度为type+name长度,type分为Shortened local name (0x08)、Complete local name (0x09)

    name部分为字符的ASCII码

  • len

    service data长度计算为service flag+UUID+device info+BThome data,也就是len位之后所有数据的长度

  • service flag

    此部分名称官方没有明确的表述,我暂且称其为service flag,对于此协议固定为0x16

  • UUID

    对于此协议固定为0xfcd2,但由于UUID需要按字节反向读取,所以在数据包中需要写入0xd2 0xfc

  • device info

    长度为1字节,其各位含义如下,对于非加密数据包为0x40

    • bit 0: “Encryption flag”,0表示传感器数据没有加密,反之加密
    • bit 1-4: “Reserved for future use”
    • bit 5-7: “BTHome Version”,目前协议版本是V2所以其二进制为010
  • sensor data

    此部分是存放传感器数据,也是启用加密时的加密对象,此部分定义参考https://bthome.io/format/

2. 加密结构

加密结构前面部分与非加密结构相同,因此本小节只描述加密相关结构

bthome协议分析及esp32上的实现

bthome协议对数据的加密采用AES-CCM,AES-CCM在加密数据的同时输出数据摘要(tag、digest),摘要可用于校验数据完整性。被加密部分为sensor data,加密完成后需要在数据包中写入加密时的counter、tag以供对端解密数据。

下面以mbedtls中的加密api来进行讲解

在进行加密前需要调用mbedtls_ccm_setkey设置加密密匙

int mbedtls_ccm_setkey	(	mbedtls_ccm_context * 	ctx,mbedtls_cipher_id_t 	cipher,const unsigned char * 	key,  //密匙数据指针unsigned int 	keybits       //密匙数据长度(bit长度)
)	

再调用mbedtls中的mbedtls_ccm_encrypt_and_tag函数来进行加密,其定义和解释如下

int mbedtls_ccm_encrypt_and_tag	(	mbedtls_ccm_context * 	ctx,size_t 	length,              //被加密数据长度const unsigned char * 	iv,  //nonce数据指针size_t 	iv_len,              //nonce数据长度const unsigned char * 	add, //额外数据size_t 	add_len,             //额外数据长度const unsigned char * 	input,//被加密数据指针unsigned char * 	output,   //存放输出密文的数据指针unsigned char * 	tag,      //存放输出tag(digest)的数据指针size_t 	tag_len               //指定输出tag的长度,必须为4, 6, 8, 10, 14 或 16
)	

对于nonce的结构为 [mac addr] + [UUID] + [device info] + [counter] 总长度为13字节

  • mac addr长度为6字节
  • UUID长度为2字节
  • device info长度1字节
  • counter长度4字节,初始值可以自己设置,在每次发送包后对其加一

对于bthome协议,tag长度需要为4字节。

所以在加密中输入为nonce、密匙、counter、明文,输出为密文、tag(digest)。将密文、counter、tag加密到数据包中即可组成完整的数据包。

下面是对一个bthome数据包的解析,以供参考

02 01 06  [HEAD]
04 09 65 6e 66 [Complete name size: 0x4] 
12 [service data len 0x12]
|	16  [service flag]
|	d2 fc [UUID ]
|	41 [device info]
|     2f 0a fa 54 37 e3  [sensor data]|
|	00 11 22 33  [counter]
|	b8 ed 87 a7  [tag]

下面是我在开源项目基础上完善了bthome协议在esp32上的实现,并使用此协议采集传感器数据的demo

https://github.com/junchao98/bthome-weather-station