蓝牙指令解析
本篇从如下三个方面介绍通过data如何与蓝牙进行通讯。
- 字节高低位及运算
- 蓝牙特征的属性及作用
- CRC16校验常用算法及实现
字节高低位及运算
1byte的范围为0-255,两个16进制的字符表示的范围(15*16+15)为0~255,故一字节等于两个16进制的字符.
字节高低位的概念
蓝牙通信规约中,一般会说明一个信息占用多少个字节,同时会说明高位在前还是低位在前。
一般,字节的表示都是高位在前,低位在后。
如:一个16位的字符,高位和低位各占8位,前8位为高位的字节,后8位为低位的字节;
32位的字符高位和低位各占16字节,前16位为高位的字节,后16位为低位的字节。
大小端模式
- 大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中
- 小端模式:是指数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低。
字节高低位的运算
- 字节高低位的运算主要使用的是位运算。根据字节所在的位置,运算相应的字节数得到具体的int值。
例:1字节的运算,分为高低各4位。
- 1字节等于2个16进制的字符:
0xDA
- 高字节在前:
- 第一个16进制字符表示高4位。D的大小:
(D << 4) & 0xFF
,int类型。移动到高位再计算大小 - 第二个16进制字符表示低4位。A的大小:
A & 0xFF
,int类型。
- 低字节在前:
- 第一个16进制字符表示低4位。D的大小:
D & 0xFF
,int类型。 - 第二个16进制字符表示高4位。A的大小:
(A << 4) & 0xFF
,int类型。移动到高位再计算大小
- 有时候一个数据区间
>1 byte
,需要计算这部分的大小,即各个位置上的byte值相加(64位CPU):
如:4byte值的计算:0x4A5D434F
- 第一位的值:
4F & 0xFF
- 第二位的值:
(43 << 8 ) & 0xFF
- 第三位的值:
(5D << 16) & 0xFF
- 第四位的值:
(4A << 32) & 0xFF
- 将各位置上的值相加,即为此数据区间的值。
蓝牙特征属性
基本蓝牙的使用就不介绍了,这里说下蓝牙服务中特征的属性,不同的特征属性对蓝牙的通信模式影响很大。
iOS中的蓝牙特征属性值如下:
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
CBCharacteristicPropertyWrite = 0x08,
CBCharacteristicPropertyNotify = 0x10,
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(10_9, 6_0) = 0x200
};
我们调试蓝牙的时候,特别需要注意的属性为这几个,它们关系到特征的作用,进而影响功能的实现。
// 读特征,读取蓝牙传输的数据
CBCharacteristicPropertyRead,
// 写特征,数据写入蓝牙后蓝牙不回复是否写入成功
CBCharacteristicPropertyWriteWithoutResponse,
// 写特征,数据写入蓝牙后蓝牙会回复是否写入成功,调用相关的代理方法获取
CBCharacteristicPropertyWrite,
// 订阅特征,蓝牙会主动回传消息。订阅有订阅成功失败的代理,以及获取订阅消息的代理方法
CBCharacteristicPropertyNotify,
可以写数据的特征的不同属性,直接影响写数据方法的调用。
typedef NS_ENUM(NSInteger, CBCharacteristicWriteType) {
CBCharacteristicWriteWithResponse = 0,
CBCharacteristicWriteWithoutResponse,
};
// 给外设写数据的方法,type要根据特征的属性来设置
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;
CRC校验
CRC校验:循环冗余检查(CRC)是一种数据传输检错功能,对数据进行多项式计算,并将得到的结果附在帧的后面,接收设备也执行类似的算法,以保证数据传输的正确性和完整性。
百科详解
在蓝牙指令传输中,通常会在数据的末尾进行CRC校验,和公司硬件端采用同样的算法即可。
蓝牙指令解析举例说明
简单的蓝牙通讯协议
简单的通讯协议说明如下,基本都大同小异,高字节在前,低字节在后:
类型 | 字节大小 | 说明 |
同步头 | 4字节 | 同步头一般是固定的4个字节 |
报文长度 | 2字节 | 除了同步头之外其他数据的大小,每次传输时都要进行计算 |
命令字 | 2字节 | 此例中为0x00 0x00 |
功能码 | 2字节 | 此例中为0x00 0x00 |
数据正文 | 动态字节长度 | 计算出报文长度后减去固定字节区域后的数据 |
校验CRC16 | 2字节 | 报文长度开始至动态部分结束,及不含同步头和校验码的数据进行校验后的值 |
这个报文结构比较简单,实际中可根据需要自由添加不同区间的功能码及字节数。
解析协议
- 订阅特征后,接收到蓝牙数据
4a4a4a4a000c000000004a5d6b3c1234
- 根据上面的协议可知:各区域字节代表的含义。
- 计算报文长度:
(00 << 8) & 0xFF + 0c & 0xFF = 12
; - 此数据类型为
NSData
,通过NSData
的subdataWithRange:
进行数据的截取。 - 数据解析过程中需要用到各种转换。基本的转换为
data和16进制字符串
互相转换。