本文共 12801 字,大约阅读时间需要 42 分钟。
由于业务需要,基于 开发的App要跟BLE蓝牙设备通信。 http://blog.csdn.net/withings/article/details/71378562
在上搜索 Native BLE蓝牙组件,只找到三个组件:
-
:文档清晰明了,简单,基本每个月都有更新,遇到问题提交issue作者也能及时回复。
-
:阅读起来有点难度,更新频率较慢。
-
:由 BLE改写而成,不是很适用于React Native。
综上分析,我选择react-native-ble-manager组件,组件的安装、配置看即可。
修改源码发送16进制数据给蓝牙
通信的基础是发送正确的数据格式,react-native-ble-manager组件默认发送到蓝牙的数据格式是10进制的string
,而我们的蓝牙设备一般接收的是16进制的string
。带着这个疑问,我提了一个issue给作者,问题是:然而作者却没有给我一个满意的解决办法。所以,我尝试着修改组件源码来适配实际的项目需求。
本源码修改方法基于react-native-ble-manager组件3.2.1版本更改,高于或者低于这个版本的修改方法也都一样。
android源码修改
修改的源文件只有两个:BleManager.
和Peripheral.java
文件,这两个文件都在react-native-ble-manager\\src\main\java\it\innove
目录下
BleManager.java文件
/** 16进制字符串转换为2进制byte字节数组 */public static byte[] hexStringToBytes(String hexString) { if (hexString == null || hexString.equals("")) { return null; } hexString = hexString.toUpperCase(); int length = hexString.length() / 2; char[] hexChars = hexString.toCharArray(); byte[] d = new byte[length]; for (int i = 0; i < length; i++) { int pos = i * 2; d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1])); } return d;}public static byte charToByte(char c) { return (byte) "0123456789ABCDEF".indexOf(c);} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
- 修改
writeWithoutResponse
方法内的代码:
修改前: byte[] decoded = Base64.decode(message.getBytes(), Base64.DEFAULT);修改后:byte[] decoded = hexStringToBytes(message); 1 2 3 4 5 1 2 3 4 5
Peripheral.java 文件
/** 2进制byte数组转换成16进制字符串 */public static String bytes2HexString(byte[] b) { String ret = ""; for (int i = 0; i < b.length; i++) { String hex = Integer.toHexString(b[ i ] & 0xFF); if (hex.length() == 1) { hex = '0' + hex; } ret += hex.toUpperCase(); } return ret;}/** 16进制字符串转换成16进制byte数组 */public static byte[] strToHexByteArray(String str){ byte[] hexByte = new byte[str.length()/2]; for(int i = 0,j = 0; i < str.length(); i = i + 2,j++){ hexByte[j] = (byte)Integer.parseInt(str.substring(i,i+2), 16); } return hexByte;} 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
修改前:characteristic.setValue(data);修改后:byte [] value = strToHexByteArray(bytes2HexString(data));characteristic.setValue(value); 1 2 3 4 5 6 1 2 3 4 5 6
修改前:characteristic.setValue(data);修改后:byte [] value = strToHexByteArray(bytes2HexString(data));characteristic.setValue(value); 1 2 3 4 5 6 1 2 3 4 5 6
数据通信
实现蓝牙数据通信需要初始化、搜索、连接、通知、写数据
初始化
BleManager.start({ showAlert: false}) .then(() => { console.log('Module initialized'); });//检查蓝牙打开状态,初始化蓝牙后检查当前蓝牙有没有打开BleManager.checkState(); 1 2 3 4 5 6 7 1 2 3 4 5 6 7
添加相应的监听器
//蓝牙状态改变监听NativeAppEventEmitter.addListener('BleManagerDidUpdateState', (args) => { console.log('BleManagerDidUpdateStatea:', args); if(args.state == 'on' ){ //蓝牙已打开 }}); 1 2 3 4 5 6 7 1 2 3 4 5 6 7
搜索
第一次使用react-native-ble-manager
这个组件时,发现搜索不到附近的蓝牙设备(手机,电脑),于是就向作者提交了一个issue,问题是:,然后才明白该组件只能搜索到标准的BLE蓝牙设备。
蓝牙4.0标准包含两个蓝牙标准,准确的说,是一个,它包含传统蓝牙部分(也有称之为经典蓝牙Classic Bluetooth)和低功耗蓝牙部分(Bluetooth Low Energy)。
经典蓝牙可以用数据量比较大的传输,如:图像、视频、音乐等。低功耗蓝牙的数据传输用于实时性要求比较高但数据速率比较低的产品,如穿戴设备、遥控类的,鼠标,键盘,遥控鼠标(Air Mouse),还有传感设备的数据发送,如心跳带,血压计,温度传感器等等、其应用的行业和方向也比较广泛。
详情可查看:
所以,即使是蓝牙4.0的设备,例如手机蓝牙、电脑蓝牙,使用该组件也搜索不到蓝牙,要看它是不是低功耗标准的蓝牙。
在android平台下,可以直接搜索到蓝牙设备的Mac地址,而需要通过广播0x18才能获取得到蓝牙的Mac地址(需要修改蓝牙固件将Mac地址加入到广播中,普通蓝牙设备一般没有)。
//扫描可用设备BleManager.scan([], 5, true) .then(() => { console.log('Scan started'); });//停止扫描BleManager.stopScan() .then(() => { console.log('Scan stopped'); }) .catch((err)=>{ console.log('Scan stopped fail',err); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 1 2 3 4 5 6 7 8 9 10 11 12 13 14
添加相应的监听器
//搜索到一个新设备监听NativeAppEventEmitter.addListener('BleManagerDiscoverPeripheral', (data) => { console.log('BleManagerDiscoverPeripheral:', data); let id; //蓝牙连接id let macAddress; //蓝牙Mac地址 if(Platform.OS == 'android'){ macAddress = data.id; id = macAddress; }else{ //ios通过广播0x18获取蓝牙Mac地址,如果广播携带有Mac地址 macAddress = getMacAddressFromIOS(data); id = data.id; } });/** ios系统从蓝牙广播信息中获取蓝牙MAC地址 */getMacAddressFromIOS(data){ let macAddressInAdvertising = data.advertising.kCBAdvDataManufacturerMacAddress; macAddressInAdvertising = macAddressInAdvertising.replace("<","").replace(">","").replace(" ",""); if(macAddressInAdvertising != undefined && macAddressInAdvertising != null && macAddressInAdvertising != '') { macAddressInAdvertising = swapEndianWithColon(macAddressInAdvertising); } return macAddressInAdvertising;}/*** ios从广播中获取的mac地址进行大小端格式互换,并加上冒号:* @param str 010000CAEA80* @returns { string} 80:EA:CA:00:00:01*/swapEndianWithColon(str){ let format = ''; let len = str.length; for(let j = 2; j <= len; j = j + 2){ format += str.substring(len-j, len-(j-2)); if(j != len) { format += ":"; } } return format.toUpperCase();}//搜索结束监听NativeAppEventEmitter.addListener('BleManagerStopScan', () => { console.log('BleManagerStopScan:','Scanning is stopped'); //搜索结束后,获取搜索到的蓝牙设备 BleManager.getDiscoveredPeripherals([]) .then((peripheralsArray) => { console.log('Discovered peripherals: ', peripheralsArray); });}); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
连接
android使用Mac地址与蓝牙连接,ios使用UUID与蓝牙连接。
//连接蓝牙BleManager.connect(id) .then((peripheralInfo) => { console.log('Connected peripheralInfo: ', peripheralInfo); }) .catch((error) => { console.log('Connected error:',error); });//断开蓝牙连接BleManager.disconnect(id) .then( () => { console.log('Disconnected'); }) .catch( (error) => { console.log('Disconnected error:',error); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
添加相应的监听器
//蓝牙设备已连接监听NativeAppEventEmitter.addListener('BleManagerConnectPeripheral', (args) => { log('BleManagerConnectPeripheral:', args);});//蓝牙设备已断开连接监听NativeAppEventEmitter.addListener('BleManagerDisconnectPeripheral', (args) => { console.log('BleManagerDisconnectPeripheral:', args);}); 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
蓝牙连接后会显示该设备的具体信息,android平台下连接成功后返回的数据如下:
{ characteristics: [ { properties: { Read: 'Read' }, characteristic: '2a00', service: '1800' }, { properties: { Read: 'Read' }, characteristic: '2a01', service: '1800' }, { properties: { Write: 'Write', Read: 'Read' }, characteristic: '2a02', service: '1800' }, { properties: { Read: 'Read' }, characteristic: '2a04', service: '1800' }, { descriptors: [ { value: null, uuid: '2902' } ], properties: { Indicate: 'Indicate', Read: 'Read' }, characteristic: '2a05', service: '1801' }, { descriptors: [ { value: null, uuid: '2902' }, { value: null, uuid: '2901' } ], properties: { Notify: 'Notify' }, characteristic: '0783b03e-8535-b5a0-7140-a304d2495cb8', service: '0783b03e-8535-b5a0-7140-a304d2495cb7' }, { descriptors: [ { value: null, uuid: '2902' }, { value: null, uuid: '2901' } ], properties: { WriteWithoutResponse: 'WriteWithoutResponse' }, characteristic: '0783b03e-8535-b5a0-7140-a304d2495cba', service: '0783b03e-8535-b5a0-7140-a304d2495cb7' }, { descriptors: [ { value: null, uuid: '2902' }, { value: null, uuid: '2901' } ], properties: { Notify: 'Notify', WriteWithoutResponse: 'WriteWithoutResponse', Read: 'Read' }, characteristic: '0783b03e-8535-b5a0-7140-a304d2495cb9', service: '0783b03e-8535-b5a0-7140-a304d2495cb7' } ], services: [ { uuid: '1800' }, { uuid: '1801' }, { uuid: '0783b03e-8535-b5a0-7140-a304d2495cb7' } ], rssi: -46, advertising:{ data: 'AgEGEQe3XEnSBKNAcaC1NYU+sIMHCQlQRVAtUEVOLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=',CDVType: 'ArrayBuffer' }, id: '00:CD:FF:00:22:2D', name: 'PEP-PEN-01' } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
通知
蓝牙连接后还不能进行通信,要使蓝牙和手机互相通信,需要开启通知服务。
//打开通知BleManager.startNotification(peripheralId, serviceUUID, characteristicUUID) .then(() => { console.log('Notification started'); }) .catch((error) => { console.log('Notification error:',error); });//关闭通知BleManager.stopNotification(peripheralId, serviceUUID, characteristicUUID) .then(() => { console.log('stopNotification success!'); }) .catch((error) => { console.log('stopNotification error:',error); }); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
peripheralId
,配对的设备id,android下peripheralId
为Mac地址,ios下peripheralId
为UUID;
假设蓝牙连接后返回的数据为bluetoothInfo
,bluetoothInfo
下的characteristics字段值是一个特征数组,每一项代表一个特征通道,找到properties属性为{ Notify: 'Notify' }
的那一项
{ descriptors: [ { value: null, uuid: '2902' }, { value: null, uuid: '2901' } ], properties: { Notify: 'Notify' }, characteristic: '0783b03e-8535-b5a0-7140-a304d2495cb8', service: '0783b03e-8535-b5a0-7140-a304d2495cb7' } 1 2 3 4 1 2 3 4
该项代表通知的特征通道,service字段值即为serviceUUID
服务UUID,characteristic字段值即为characteristicUUID
特征值UUID
let peripheralId, serviceUUID, characteristicUUID;peripheralId = bluetoothInfo.id;for(let item of bluetoothInfo.characteristics){ if(JSON.stringify(item.properties) == JSON.stringify({ Notify: 'Notify' })){ serviceUUID = item.service; characteristicUUID = item.characteristic; break; } } 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
写数据
写数据有两个方法,write
和writeWithoutResponse
,由于写数据使用的properties属性为writeWithoutResponse
的特征通道,所以这里使用writeWithoutResponse
方法来发送数据到蓝牙
BleManager.writeWithoutResponse(peripheralId, serviceUUID, writeCharacteristicUUID, data) .then(() => { console.log('Write success: ',data); }) .catch((error) => { console.log('Write failed: ',data); }); 1 2 3 4 5 6 7 1 2 3 4 5 6 7
peripheralId
,配对的设备id,android下peripheralId
为Mac地址,ios下peripheralId
为UUID;data
为发送给蓝牙的数据;
假设蓝牙连接后返回的数据为bluetoothInfo
,bluetoothInfo
下的characteristics字段值是一个特征数组,每一项代表一个特征通道,找到properties属性为{ WriteWithoutResponse: 'WriteWithoutResponse' }
的那一项
{ descriptors: [ { value: null, uuid: '2902' }, { value: null, uuid: '2901' } ], properties: { WriteWithoutResponse: 'WriteWithoutResponse' }, characteristic: '0783b03e-8535-b5a0-7140-a304d2495cba', service: '0783b03e-8535-b5a0-7140-a304d2495cb7' } 1 2 3 4 1 2 3 4
该项代表写数据到蓝牙的特征通道,service字段值即为serviceUUID
服务UUID,characteristic字段值即为writeCharacteristicUUID
写数据特征值UUID;
let peripheralId, serviceUUID, writeCharacteristicUUID;peripheralId = bluetoothInfo.id;for(let item of bluetoothInfo.characteristics){ if(JSON.stringify(item.properties) == JSON.stringify({ WriteWithoutResponse: 'WriteWithoutResponse' })){ serviceUUID = item.service; writeCharacteristicUUID = item.characteristic; break; } } 1 2 3 4 5 6 7 8 9 1 2 3 4 5 6 7 8 9
添加相应的监听器
//通知接收到蓝牙发送过来的新数据NativeAppEventEmitter.addListener('BleManagerDidUpdateValueForCharacteristic', (data) => { //ios接收到的是小写的16进制,android接收的是大写的16进制,统一转化为大写16进制 let value = data.value.toUpperCase(); console.log('BluetoothUpdateValue', value);}); 1 2 3 4 5 6 1 2 3 4 5 6
demo
截图