BLE低功耗蓝牙的广播内容

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BLE低功耗蓝牙的广播内容相关的知识,希望对你有一定的参考价值。

参考技术A

BLE的设备可以发出广播信号,其信号占用的通道是37,38,39通道

设备可以选择广播在其中的任何一个,两个或全部三个通道

BLE4.0-BLE4.2支持的最大广播payload包长是31字节;BLE5.0增加了扩展模式,以数据通道发送额外的数据,使得广播支持的最大payload包长是254字节。

除了广播的31字节以外,还支持在扫描回复内容中包含最多31字节的数据。
这两者的区别是:广播的内容会持续定期发送,不管有没有接收者;而扫描回复内容只有在主机发送扫描命令后,设备才会在扫描回复中携带此内容
以下是一个典型的广播-扫描-回复过程。

广播内容和扫描回复内容的格式定义是通用的。都是长度+数据的方式,其中数据又分成类型+实际数据,如下图

格式定义字段(Flags)是一个字节的配置字段,是唯一一个必须包含的字段,用于指示设备是否可以被搜到、是否支持传统蓝牙,是否支持可被连接等。

UUID是一种唯一代号,一般每个service会对应一个UUID,一个设备可以有多个UUID。

一个完整的UUID是128bit,也可以以32bit或16bit缩写的方式表示。
通过此字段,可以选择将全部或部分UUID广播出来。

名称字段(Local Name)决定了设备在手机中搜索出来的名字

厂商自定义字段(Manufacturer Specific Data)是我们可以自己定义其内容的字段。其中也预定义了一些厂商,在 Bluetooth官方网站 可以查到,这些都是交了会员费的成员。
如果我们自己用,也可以完全自定义内容

其他字段可以参考蓝牙标准,或者 其他网站 说明。

注意:广播和扫描字段内容均不能超过31字节,并且要特别注意每个字段中的Length要与Data匹配,否则可能会造成某些手机能搜到广播某些搜不到的奇怪现象。

CC264x中,广播内容和扫描回复内容要分别设置。
定义两个数组缓冲

使用GapAdv_loadByHandle()函数加载缓冲内容

之后调用GapAdv_enable()打开广播即可

如果在广播已经开启后,要修改广播内容,有两种方法
方法一:使用GapAdv_prepareLoadByHandle()和GapAdv_loadByHandle()方法

方法二:使用GapAdv_prepareLoadByBuffer() 和 GapAdv_LoadByBuffer()方法

注意:对于某些SDK(例如CC13x2_26x2 SDK 3.20.00.68, 3.40.00.02),有 已知BUG ,方法二只能用于更新广播,而不能用于更新扫描数据。

https://blog.csdn.net/slimmm/article/details/100583655
https://blog.csdn.net/zzfenglin/article/details/56064808
https://www.novelbits.io/bluetooth-low-energy-advertisements-part-1/
https://www.novelbits.io/bluetooth-low-energy-advertisements-part-2/
https://stackoverflow.com/questions/40250621/does-the-ble-spec-allow-for-manufacturer-ad-type-in-both-advertising-data-and-sc
https://stackoverflow.com/questions/33535404/whats-the-maximum-length-of-a-ble-manufacturer-specific-data-ad
https://e2e.ti.com/support/wireless-connectivity/bluetooth-group/bluetooth/f/bluetooth-forum/845470/cc2642r-updating-scan-response-data-via-gapadv_loadbybuffer

原创Android 5.0 BLE低功耗蓝牙从设备应用

如果各位觉得有用,转载+个出处。

现如今安卓的低功耗蓝牙应用十分普遍了,智能手环、手表遍地都是,基本都是利用BLE通信来交互数据。BLE基本在安卓、IOS两大终端设备上都有很好支持,所以有很好发展前景。

现市面上各种手环、手表的智能设备中基本都充当"从设备"这样的角色,基本由智能设备完成蓝牙广播,由手机进行连接,然后交互数据。

根据上述方式的应用在安卓4.3、IOS 7.0的版本上就得到了支持,应用也比较广泛,园里应该有很多相关实现,大家可以自己找找,如果不愿意找,抽空再写一篇。

今天主要是为了说在安卓5.0时升级了广播相关API,园里也有一些说明,但之所以还写这篇是因为数据交换的提及很少。

既然将手机要做广播了,那么实质手机就变成手环、手表的角色,一个从设备了。

如果你愿意,可以拿另一台手机做个主设备,这样他们就可以交流了。

好了,我们进入代码正题吧...

 

首先应用权限设置。在AndroidManifest.xml中还是要加入BLE控制权限,不然异常一定与你为伍。

1     <uses-permission android:name="android.permission.BLUETOOTH" />
2     <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

 

 接着我们上套路了,判断手机是否支持BLE以及是否支持BLE从设备。

 1   if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
 2             showToast("该设备不支持蓝牙低功耗通讯");
 3             this.finish();
 4             return;
 5         }
 6 
 7         bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
 8 
 9         bluetoothAdapter = bluetoothManager.getAdapter();
10 
11         if (bluetoothAdapter == null) {
12             showToast("该设备不支持蓝牙低功耗通讯");
13             this.finish();
14             return;
15         }
16 
17         bluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
18         if (bluetoothLeAdvertiser == null) {
19             showToast("该设备不支持蓝牙低功耗从设备通讯");
20             this.finish();
21             return;
22         }

我建议你先拿你调试设备试试,大多数同学走到这里都绝望了。你问我为啥?你试试就知道了。

 

如果你一脸what???的话,那恭喜你,你的调试设备是被选中的孩子,让我们继续乘风破浪吧。顺便在评论里告诉我下你用啥设备哦。

这时候我们开启广播的旋风吧。

 1  //广播设置
 2         AdvertiseSettings.Builder settingBuilder = new AdvertiseSettings.Builder();
 3         settingBuilder.setConnectable(true);
 4         settingBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
 5         settingBuilder.setTimeout(0); //我填过别的,但是不能广播。后来我就坚定的0了
 6         settingBuilder.setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH);
 7         AdvertiseSettings settings = settingBuilder.build();
 8 
 9 
10         //广播参数
11         AdvertiseData.Builder dataBuilder = new AdvertiseData.Builder();
12         bluetoothAdapter.setName("H8-BlePpl"); //你想叫啥名字,你愿意就好
13         dataBuilder.setIncludeDeviceName(true);
14         dataBuilder.setIncludeTxPowerLevel(true);
15 
16         dataBuilder.addServiceUuid(ParcelUuid.fromString(Const.UUID_SERVICE)); //可自定义UUID,看看官方有没有定义哦
17         AdvertiseData data = dataBuilder.build();
18 
19         bluetoothLeAdvertiser.startAdvertising(settings, data, advertiseCallback);

然后你的小手机就开始广播了,说大家来连我啊连我啊。别总搜地址,貌似地址动态会变的,还是用名儿吧毕竟你起了啊。

我之前傻傻的查了手机蓝牙MAC地址,后来发现不是广播的那个...

广播回调我干了点新增服务的活儿,不干你拿啥通信来。

 1  private AdvertiseCallback advertiseCallback = new AdvertiseCallback() {
 2         @Override
 3         public void onStartSuccess(AdvertiseSettings settingsInEffect) {
 4             super.onStartSuccess(settingsInEffect);
 5             runOnUiThread(new Runnable() {
 6                 @Override
 7                 public void run() {
 8                     showInfo("1.1 AdvertiseCallback-onStartSuccess");
 9                 }
10             });
11 
12 
13             bluetoothGattServer = bluetoothManager.openGattServer(getApplicationContext(),
14                     bluetoothGattServerCallback);
15 
16             BluetoothGattService service = new BluetoothGattService(UUID.fromString(Const.UUID_SERVICE),
17                     BluetoothGattService.SERVICE_TYPE_PRIMARY);
18 
19             UUID UUID_CHARREAD = UUID.fromString(Const.UUID_CHARACTERISTIC);
20 
21             //特征值读写设置
22             BluetoothGattCharacteristic characteristicWrite = new BluetoothGattCharacteristic(UUID_CHARREAD,
23                     BluetoothGattCharacteristic.PROPERTY_WRITE |
24                             BluetoothGattCharacteristic.PROPERTY_READ |
25                             BluetoothGattCharacteristic.PROPERTY_NOTIFY,
26                     BluetoothGattCharacteristic.PERMISSION_WRITE);
27 
28             UUID UUID_DESCRIPTOR = UUID.fromString(Const.UUID_CHARACTERISTIC_CONFIG);
29 
30             BluetoothGattDescriptor descriptor = new BluetoothGattDescriptor(UUID_DESCRIPTOR, BluetoothGattCharacteristic.PERMISSION_WRITE);
31             characteristicWrite.addDescriptor(descriptor);
32             service.addCharacteristic(characteristicWrite);
33 
34             bluetoothGattServer.addService(service);
35 
36 
37             runOnUiThread(new Runnable() {
38                 @Override
39                 public void run() {
40                     showInfo("1.2. Service Builded ok");
41                 }
42             });
43 
44         }};

当你收到广播成功回调后,来吧,特征值啊~~反正要通信呐~~

被你发现了我偷懒读写特征值用了一个,其实你愿意用两个就用两个吧。

我用的NOTIFICATION方式做主设备的读取,你也可用INDICATION方式做。

服务建立完成后,也会收到通知。BLE嘛~~都是异步回调~~我是习惯了!

  1   private BluetoothGattServerCallback bluetoothGattServerCallback = new BluetoothGattServerCallback() {
  2         @Override
  3         public void onServiceAdded(int status, BluetoothGattService service) {
  4             super.onServiceAdded(status, service);
  5 
  6             final String info = service.getUuid().toString();
  7             runOnUiThread(new Runnable() {
  8                 @Override
  9                 public void run() {
 10                     showInfo("1.3 BluetoothGattServerCallback-onServiceAdded " + info);
 11                 }
 12             });
 13 
 14 
 15         }
 16 
 17         @Override
 18         public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
 19             super.onConnectionStateChange(device, status, newState);
 20             final String info = device.getAddress() + "|" + status + "->" + newState;
 21 
 22             runOnUiThread(new Runnable() {
 23                 @Override
 24                 public void run() {
 25                     showInfo("1.4 onConnectionStateChange " + info);
 26                 }
 27             });
 28         }
 29 
 30         @Override
 31         public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
 32                                                 BluetoothGattCharacteristic characteristic) {
 33             super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
 34 
 35 
 36             final String deviceInfo = "Address:" + device.getAddress();
 37             final String info = "Request:" + requestId + "|Offset:" + offset + "|characteristic:" + characteristic.getUuid() + "|Value:" +
 38                     Util.bytes2HexString(characteristic.getValue());
 39 
 40             runOnUiThread(new Runnable() {
 41                 @Override
 42                 public void run() {
 43 
 44                     showInfo("=============================================");
 45                     showInfo("设备信息 " + deviceInfo);
 46                     showInfo("数据信息 " + info);
 47                     showInfo("=========onCharacteristicReadRequest=========");
 48 
 49                 }
 50             });
 51 
 52             bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, characteristic.getValue());
 53 
 54         }
 55 
 56         @Override
 57         public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId, BluetoothGattCharacteristic characteristic, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
 58             super.onCharacteristicWriteRequest(device, requestId, characteristic,
 59                     preparedWrite, responseNeeded, offset, value);
 60 
 61             final String deviceInfo = "Name:" + device.getAddress() + "|Address:" + device.getAddress();
 62             final String info = "Request:" + requestId + "|Offset:" + offset + "|characteristic:" + characteristic.getUuid() + "|Value:" + Util.bytes2HexString(value);
 63 
 64 
 65             bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
 66            //TODO:你做数据处理
 67 
 68 
 69             runOnUiThread(new Runnable() {
 70                 @Override
 71                 public void run() {
 72 
 73                     showInfo("=============================================");
 74                     showInfo("设备信息 " + deviceInfo);
 75                     showInfo("数据信息 " + info);
 76                     showInfo("=========onCharacteristicWriteRequest=========");
 77 
 78                 }
 79             });
 80 
 81 
 82         }
 83 
 84 
 85         @Override
 86         public void onNotificationSent(BluetoothDevice device, int status) {
 87             super.onNotificationSent(device, status);
 88 
 89             final String info = "Address:" + device.getAddress() + "|status:" + status;
 90 
 91             runOnUiThread(new Runnable() {
 92                 @Override
 93                 public void run() {
 94                     showInfo("onNotificationSent " + info);
 95                 }
 96             });
 97         }
 98 
 99 
100         @Override
101         public void onDescriptorWriteRequest(BluetoothDevice device, int requestId, BluetoothGattDescriptor descriptor, boolean preparedWrite, boolean responseNeeded, int offset, byte[] value) {
102             final String deviceInfo = "Name:" + device.getAddress() + "|Address:" + device.getAddress();
103             final String info = "Request:" + requestId + "|Offset:" + offset + "|characteristic:" + descriptor.getUuid() + "|Value:" + Util.bytes2HexString(value);
104 
105             runOnUiThread(new Runnable() {
106                 @Override
107                 public void run() {
108 
109                     showInfo("=============================================");
110                     showInfo("设备信息 " + deviceInfo);
111                     showInfo("数据信息 " + info);
112                     showInfo("=========onDescriptorWriteRequest=========");
113 
114                 }
115             });
116 
117 
118             // 告诉连接设备做好了
119             bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, value);
120         }
121 
122         @Override
123         public void onDescriptorReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattDescriptor descriptor) {
124 
125             super.onDescriptorReadRequest(device, requestId, offset, descriptor);
126 
127             final String deviceInfo = "Name:" + device.getAddress() + "|Address:" + device.getAddress();
128             final String info = "Request:" + requestId + "|Offset:" + offset + "|characteristic:" + descriptor.getUuid();
129 
130 
131             runOnUiThread(new Runnable() {
132                 @Override
133                 public void run() {
134 
135                     showInfo("=============================================");
136                     showInfo("设备信息 " + deviceInfo);
137                     showInfo("数据信息 " + info);
138                     showInfo("=========onDescriptorReadRequest=========");
139 
140                 }
141             });
142 
143             // 告诉连接设备做好了
144             bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, offset, null);
145 
146 
147         }
148 
149     };

基本上从设备就做完了。

调试的时候你用主设备查服务列表可能查不到你的UUID_SERVICE,但是别慌,你getServcie(UUID_SERVICE)试试,说不定就柳暗花明了。

 

还有就是我拿的华为Honor 8做的调试机子,其他还有啥型号请各位分享下。姑娘我跪谢啦~~

以上是关于BLE低功耗蓝牙的广播内容的主要内容,如果未能解决你的问题,请参考以下文章

低功耗蓝牙BLE传统广播总结—看这篇就够了

一分钟读懂低功耗蓝牙(BLE)广播数据包

解密:Ble低功耗蓝牙和蓝牙mesh网络之间的关系

如何使用android原生BLE蓝牙进行操作?

总结:低功耗蓝牙常见的4种工作模式

Android ble (蓝牙低功耗) 中的坑和技巧