更新记录
1.0.8(2024-07-23)
- 修复某些手机广播不成功问题
1.0.7(2024-07-05)
- 增加关闭服务接口
1.0.6(2023-10-22)
- android增加断开连接接口
平台兼容性
Android | Android CPU类型 | iOS |
---|---|---|
适用版本区间:4.4 - 14.0 | armeabi-v7a:未测试,arm64-v8a:未测试,x86:未测试 | 适用版本区间:9 - 17 |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
- 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
- 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
- 开发完毕后正式云打包
付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios
注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择
前言
功能
- 广播蓝牙数据,可以广播蓝牙名、serviceUuid、serviceDataUuid、serviceData、发射功率、厂家参数(ID和数据)、广播功率、是否可连接、持续时间
- 蓝牙数据通讯,增加删除service,characteristic读写、descriptor读写
- 支持十六进制、ASCII、iso、utf-8、utf-16等多种字符格式通讯
接入步骤
如不了解原生插件接入步骤的同学请参考: https://www.jianshu.com/p/830ccc503e29 或 https://blog.csdn.net/wenrisheng/article/details/124057700
介绍
蓝牙分为中心设备(Central、Master、主设备、客户端)和外围设备(Peripheral、Slave、从设备、服务端) 手机做为客户端可以连接多个蓝牙设备,所以手机又可以叫中心设备(Central),蓝牙设备叫外围设备(Peripheral)。 例如:通过手机蓝牙开启共享单车,手机称为中心设备,共享单车称为外围设备
本插件是外围设备(Peripheral、Slave、从设备、服务端)
中心设备测试包可以从商店下载蓝牙测试app或http://d.maps9.com/vce3下载 中心设备插件插件可使用https://ext.dcloud.net.cn/plugin?id=9710
-
常用的业务流程是: 判断蓝牙是否已经打开 -》 如果蓝牙打开则添加Service -〉 如果Service添加成功则开启广播
-
权限 android:
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
ios:
<key>NSBluetoothPeripheralUsageDescription</key>
<string>访问蓝牙需要您的授权</string>
ios如果需要后台使用蓝牙功能,在项目的"App其它常用设置"里的"后台运行能力"里增加bluetooth-peripheral
- 接口
var bluetoothslave = uni.requireNativePlugin("wrs-bluetoothslave");
- 打开蓝牙,仅支持Android
bluetoothslave.enableBluetooth();
- 关闭蓝牙,仅支持Android
bluetoothslave.disableBluetooth();
- 获取蓝牙打开关闭状态,仅支持Android,ios蓝牙状态的变更通过回调peripheralManagerDidUpdateState获取
var resp = bluetoothslave.isEnableBluetooth();
if (isString(resp)) {
resp = JSON.parse(resp);
}
var isEnable = resp.isEnable;
- 设置蓝牙名,仅支持Android(android、iOS都可以在广播时候设置广播的蓝牙名)
bluetoothslave.setName({name:"xxx"});
- 设置回调
// 设置蓝牙回调
bluetoothslave.setSlaveCallback((resp) => {
this.showMsg(JSON.stringify(resp));
var opt = resp.opt;
switch (opt) {
// 当客户端蓝牙连接状态改变时回调
case "onConnectionStateChange":
var status = resp.status;
var newState = resp
.newState; // 0: STATE_DISCONNECTED 1: STATE_CONNECTING 2: STATE_CONNECTED 3: STATE_DISCONNECTING
if (status == 0) { // 成功
if (newState == 2) { // 连接成功
} else if (newState == 0) { // 断开连接
} else if (newState == 1) { // 连接中
} else if (newState == 3) { // 断开中
}
} else { // 出错
}
break;
case "onServiceAdded":
var status = resp.status;
if (status == 0) { // 添加成功
} else { // 添加失败
}
break;
// 收到客户端特征值读取请求
case "onCharacteristicReadRequest":
// 响应客户端
var respResult = this.sendResponse(resp.device.address, resp.requestId, resp.offset,
resp.characteristic.value);
break;
// 收到客户端特征值写入数据,即:收到对方发来的蓝牙数据
case "onCharacteristicWriteRequest":
// 响应客户端,即:告诉对方蓝牙这边已经收到数据了
var respResult = this.sendResponse(resp.device.address, resp.requestId, resp.offset,
resp.value);
var _this = this;
// 根据收到的数据处理业务,处理完成后发送处理结果给对方蓝牙,即:回复客户端
setTimeout(function() {
_this.sendData(resp.device.address, resp.characteristic.uuid, "abcdef");
}, 3000);
break;
// 收到客户端描述读取请求
case "onDescriptorReadRequest":
// 响应客户端
var respResult = this.sendResponse(resp.device.address, resp.requestId, resp.offset,
resp.descriptor.value);
break;
// 收到描述写入数据
case "onDescriptorWriteRequest":
// 响应客户端
var respResult = this.sendResponse(resp.device.address, resp.requestId, resp.offset,
resp.descriptor.value);
break;
case "onExecuteWrite":
break;
// 一般这里判断sendData是否发送成功
case "onNotificationSent":
var status = resp.status;
if(status == 0) { // 成功
} else {
}
break;
case "onMtuChanged":
break;
case "onPhyUpdate":
break;
case "onPhyRead":
break;
// 当手机蓝牙状态改变时调用,监听蓝牙打开或关闭状态,仅支持iOS
case "peripheralManagerDidUpdateState":
// CBManagerStateUnknown = 0,
// CBManagerStateResetting = 1,
// CBManagerStateUnsupported = 2,
// CBManagerStateUnauthorized = 3,
// CBManagerStatePoweredOff = 4,
// CBManagerStatePoweredOn = 5,
var state = resp.state;
switch (state) {
// 蓝牙已经开启
case 5:
this.showMsg("蓝牙已经打开");
break;
// 蓝牙状态未知或关闭
default:
this.showMsg("蓝牙没有打开");
break;
}
break;
default:
break;
}
});
- 初始化蓝牙
var params = {};
switch (uni.getSystemInfoSync().platform) {
case 'ios':
params.CBCentralManagerOptionShowPowerAlertKey = true;
// params.CBCentralManagerOptionRestoreIdentifierKey = "xxx"; // CBCentralManagerOptionRestoreIdentifierKey需要开启后台模式:bluetooth-peripheral
break;
default:
break;
}
bluetoothslave.initBluetoothSlave();
- 开始广播
注意 ios仅支持params.name、params.advertiseData.serviceUuids这两个参数,iOS系统的蓝牙广播的仅开放了这两个参数 广播整体数据不能太大,否则广播不成功
var params = {};
// 是否可以连接
params.connectable = true;
// 蓝牙名,中心设备里有些手机扫描到的蓝牙名是有本地缓存的,如果发现扫描的名称没有改变,请多次重新刷新
params.name = this.name;
// 广播模式 1:ADVERTISE_MODE_BALANCED 2: ADVERTISE_MODE_LOW_LATENCY 3:ADVERTISE_MODE_LOW_POWER
params.advertiseMode = parseInt(this.advertiseMode);
// 发射功率级别 3:ADVERTISE_TX_POWER_HIGH 1: ADVERTISE_TX_POWER_LOW 2: ADVERTISE_TX_POWER_MEDIUM 0:ADVERTISE_TX_POWER_ULTRA_LOW
params.txPowerLevel = parseInt(this.txPowerLevel);
// 不得超过180*1000毫秒,值为0将禁用时间限制,不设置则为无限广播时长
params.timeout = parseInt(this.timeout);
// 广播数据
params.advertiseData = {
includeDeviceName: true, // 是否包含蓝牙名
includeTxPowerLevel: true, // 是否包含发射功率
manufacturerId: 0x06, // 厂家ID
manufacturerSpecificData: "020403", // 厂家数据,跟厂家ID成对传参
serviceUuids: [this.UUID_SERVICE, this.UUID_SERVICE2], // 服务UUID
// serviceDataUuid: "0000fff2-0000-1000-8000-00805f9b34fb", // 服务数据UUID
serviceData: "05FF", // 服务数据,跟服务数据UUID成对传参
};
// 扫描响应包,数据结构跟广播数据一样
// params.scanResponse = {};
// ios仅支持params.name、params.advertiseData.serviceUuids这两个参数
bluetoothslave.startAdvertising(params, (resp) => {
var flag = resp.flag;
if (flag) {
var opt = resp.opt;
if (opt == 'onStartSuccess') {
var str = "广播启动成功";
switch (uni.getSystemInfoSync().platform) {
case 'android':
if (resp.settingsInEffect.connectable) {
str = str + ",可连接"
} else {
str = str + ",不可连接"
}
if (resp.settingsInEffect.timeout == 0) {
str = str + ",持续广播"
} else {
str = str + ",广播时长 " + resp.settingsInEffect.timeout + " ms"
}
break;
case 'ios':
break
}
this.showMsg(str + JSON.stringify(resp));
} else if (opt == 'onStartFailure') {
// android errorCode:
// 1: ADVERTISE_FAILED_DATA_TOO_LARGE
// 2: ADVERTISE_FAILED_TOO_MANY_ADVERTISERS
// 3: ADVERTISE_FAILED_ALREADY_STARTED
// 4: ADVERTISE_FAILED_INTERNAL_ERROR
// 5: ADVERTISE_FAILED_FEATURE_UNSUPPORTED
var errorCode = opt.errorCode;
if (errorCode == 1) { // ADVERTISE_FAILED_DATA_TOO_LARGE
this.showMsg("广播失败 errorCode:" + errorCode + "数据报文超出31字节");
} else {
this.showMsg("广播失败 errorCode:" + errorCode);
}
}
} else {
var msg = resp.msg;
this.showMsg("广播失败 msg:" + msg);
}
});
- 停止广播
bluetoothslave.stopAdvertising((resp) => {});
// 同时关闭服务
bluetoothslave.close()
- 增加服务service, 蓝牙的数据通讯是通过service的characteristic、descriptors来进行的
// 权限
// android: 0x10: PERMISSION_WRITE 0x01: PERMISSION_READ
// ios:
// 0x01: CBAttributePermissionsReadable
// 0x02: CBAttributePermissionsWriteable
// 0x04: CBAttributePermissionsReadEncryptionRequired
// 0x08: CBAttributePermissionsWriteEncryptionRequired
var permissions = 0x01 | 0x10;
var value = null;
var descriptors = []; // 在ios里不要传null值,null值会被序列化成"<null>"字符串,如果没数据的话可以没有这个入参或传空数组
switch (uni.getSystemInfoSync().platform) {
case 'android':
// 0x10: PERMISSION_WRITE 0x01: PERMISSION_READ
permissions = 0x01 | 0x10;
value = "12335445";
descriptors = [{ // 描述
uuid: this.NotificationDescriptorUUID,
permissions: 0x01 |
0x10, // 仅对Android生效,0x01: PERMISSION_READ 0x10:PERMISSION_WRITE
value: "12335445"
}];
break;
case 'ios':
// 0x01: CBAttributePermissionsReadable
// 0x02: CBAttributePermissionsWriteable
// 0x04: CBAttributePermissionsReadEncryptionRequired
// 0x08: CBAttributePermissionsWriteEncryptionRequired
permissions = 0x01 | 0x02;
break
}
var params = {
uuid: this.UUID_SERVICE, // service的UUID
type: 0, // 0: SERVICE_TYPE_PRIMARY 1: SERVICE_TYPE_SECONDARY
characteristics: [{ // 读写特征
uuid: this.UUID_CHARACTERISTIC,
properties: 0x08 | 0x10 |
0x02, // 0x08: PROPERTY_WRITE 0x10: PROPERTY_NOTIFY 0x02: PROPERTY_READ
permissions: permissions,
value: value, // ios下,如果要给characteristic只能是只读状态下才能赋值
descriptors: descriptors
},
{ // 只读特征
uuid: this.UUID_CHARACTERISTIC2,
properties: 0x02, // 0x08: PROPERTY_WRITE 0x10: PROPERTY_NOTIFY 0x02: PROPERTY_READ
permissions: 0x01 ,// 0x10: PERMISSION_WRITE 0x01: PERMISSION_READ
value: "12335445"
}
]
};
bluetoothslave.addService(params);
- 删除某个服务
bluetoothslave.removeService({uuid: "xxx"});
- 删除所有服务
bluetoothslave.removeAllService({uuid: "xxx"});
- 发送响应数据,收到中心设备请求后,需要回复一个响应数据,比如中心设备发送过来一条数据,外围设备收到数据后需要发送一条响应数据告知中心设备数据已经收到
var respParams = {};
respParams.address = address; // 设备地址
respParams.requestId = requestId; // 请求ID
respParams.status = 0; // 响应状态,0: GATT_SUCCESS 257: GATT_FAILURE
respParams.offset = offset; // 偏移量,仅对Android生效
respParams.value = value; // 值,仅对Android生效
var resp = bluetoothslave.sendResponse(respParams);
if (isString(resp)) {
resp = JSON.parse(resp);
}
- 发送数据,比如收到中心设备获取天气信息的指令后,外围设备整理天气信息,然后通过此接口把天气信息发送给中心设备
// 在iOS上,需要中心设备订阅了特征值characteristic后,才能发送数据
// Android上数据是否发送成功,一般通过setSlaveCallback里的onNotificationSent里的state来判断
var sendParams = {};
sendParams.address = address; // 设备地址
sendParams.characteristicUuid = characteristicUuid; // 特征UUID
sendParams.value = value; // 值
sendParams.comfirm = false; // 是否需要确认,仅对Android生效
sendParams.dataType: 0, // 发送数据类型, -1: 不进行转换,以原始数据发送 0: 以十六进制发送,此时data数据为十六进制字符串或整型数组 1:以字符编码发送,此时data数据为字符串
sendParams.charsetName: "UTF_8", // 字符编码, 当dataType为1时生效,常用编码为:ISO_8859_1、US_ASCII、UTF_16、UTF_16BE、UTF_16LE、UTF_8
// sendParams.notify = false; // 一般都用不上,仅支持Android
// sendParams.notifyAll = false; // 一般都用不上,仅支持Android
var result = bluetoothslave.sendData(sendParams);
if(isString(result)) {
result = JSON.parse(result);
}
if(result.sendResult) {
}
- 释放资源
// 释放资源
bluetoothslave.releaseResource();
- 设置接收数据类型,设置后接收到的数据都会按照设置的字符数据类型进行转换
var params = {};
params.dataType = -1; // -1: 不转换数据 0: 以十六进制接收 1:以字符编码接收
params.charsetName = "UTF_8"; // 字符编码,当dataType为1时生效,常用编码为:ISO_8859_1、US_ASCII、UTF_16、UTF_16BE、UTF_16LE、UTF_8
bluetoothslave.setReceiveDataDataType(params);
- 断开连接(仅支持Android)
var params = {};
params.address = “xxx”
bluetoothslave.disConnectDevice(params);