蓝牙指令解析

本篇从如下三个方面介绍通过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,通过NSDatasubdataWithRange:进行数据的截取。
  • 数据解析过程中需要用到各种转换。基本的转换为data和16进制字符串互相转换。