uni-app 蓝牙设备
- 蓝牙
- 蓝牙接口API
- 接口API使用示例
- utils.js
蓝牙
蓝牙4.0包括:经典蓝牙、高速蓝牙、低功耗蓝牙(BLE)
BLE信道有0~39个信道,每个信道的带宽2Mhz;第一个信道2400Mhz 第二个信道2420Mhz; 蓝牙频段2.4G,
BLE广播信道有3个(37,38,39信道)用于发送广播,主机发现BLE设备;
0~36信道工作信道,可以调频工作;
BLE特性:高可靠性、快速连接、低成本低功耗、传输距离、高安全性
BLE的5种状态: 待机、 广播、 监听/扫描、 初始化、 连接
广播设备不需要连接就可以发送数据;
GATT协议分Server和Client两个角色:
发起连接的是主机(Client角色),接收连接的是从机(Server角色)
蓝牙接口API
接口API使用示例
deviceId(设备ID):蓝牙设备的MAC地址,每台设备是唯一的;
serviceId(服务ID):每台设备有多个服务ID;
characteristicId(特征ID):每个服务ID有多个特征ID;
// 通过deviceId获取serviceId:
uni.getBLEDeviceServices({
deviceId: '11:22:33:44:55:99',
success: (res)=>{
console.log('BLE设备服务', res.services)
}
})
// 通过deviceId和serviceId获取characteristicId
uni.getBLEDeviceCharacteristics({
deviceId: '11:22:33:44:55:99',
serviceId: '0000AE30-0000-1000-8000-00805F9B34FB',
success: function(res){
//console.log('获取设备属性成功' ,res.characteristics);
console.log('获取设备属性成功' ,res);
},
fail: function(res){
console.log('获取设备属性失败:', res)
}
})
// 小程序写数据到BLE设备
uni.writeBLECharacteristicValue({
deviceId: '11:22:33:44:55:99',
serviceId: '0000AE30-0000-1000-8000-00805F9B34FB',
characteristicId: '0000AE01-0000-1000-8000-00805F9B34FB',
value: buffer,
success: function(res){
console.log('BLE写数据成功:', res)
},
fail: function(res){
console.log('BLE写数据失败:', res)
},
complete: function(res){
console.log('BLE写数据完成:', res)
}
})
// 小程序订阅BLE设备 特征值,notify或indicate
uni.notifyBLECharacteristicValueChange({
deviceId: '11:22:33:44:55:99',
serviceId: '0000AE30-0000-1000-8000-00805F9B34FB',
characteristicId: '0000AE02-0000-1000-8000-00805F9B34FB',
state:true,
success: (res)=>{
console.log("notify success.");
uni.onBLECharacteristicValueChange(function(res){
console.log('收到通知:');
console.log(`characteristic ${res.characteristicId} has changed, now is ${res.value}`)
})
},
fail: (res)=>{
console.log('notify fail.'+res);
},
complete: (res)=>{
console.log('notify complete.'+res);
}
})
<template>
<view class="content">
<!-- image class="logo" src="/static/logo.png"></image -->
<view class="text-area">
<text class="title">{{title}}</text>
</view>
<button size="default" type="default" @click="ble_device_open()">打开蓝牙</button>
<button size="default" type="default" @click="ble_device_close()">关闭蓝牙</button>
<button size="default" type="default" @click="ble_device_scan_start()">搜索设备</button>
<button size="default" type="default" @click="ble_device_scan_stop()">停止搜索</button>
<button size="default" type="default" @click="ble_get_adapter_state()">适配器状态</button>
<button size="default" type="default" @click="ble_device_connect()">连接设备</button>
<button size="default" type="default" @click="ble_device_get_serviceId()">获取服务ID</button>
<button size="default" type="default" @click="ble_device_get_characteristics()">获取特征</button>
<button size="default" type="default" @click="ble_device_write_data()">发送数据</button>
<input class="uni-input" @input="input_ssid" placeholder="ssid" />
<input class="uni-input" @input="input_passwd" placeholder="passwd" />
</view>
</template>
<script>
import { ab2hex, strToHexCharCode, hexCharCodeToStr } from '@/utils/utils.js';
export default {
data() {
return {
bluetooth:[],
title: 'Hello',
macAddress: 'FC:C4:85:EE:94:0C',// 云耳样机:'F3:0F:2B:20:A1:1B', //芯岸样机:'FC:C4:85:EE:94:0C',
services:[],
_serviceId: '0000AF30-0000-1000-8000-00805F9B34FB',
_characteristicId: '0000AE01-0000-1000-8000-00805F9B34FB',
ssid:'',
passwd:''
}
},
onLoad() {
},
methods: {
ble_device_open: function() {
uni.openBluetoothAdapter({
success: (res) => {
console.log('蓝牙打开成功:' + res)
uni.onBluetoothAdapterStateChange(function(res){
console.log('蓝牙适配器状态改变:', res.available, res.discovering)
})
},
fail: function(res) {
console.log('蓝牙打开失败')
},
complete: function(res) {
console.log('蓝牙打开完成')
}
})
},
ble_device_close: function() {
uni.closeBluetoothAdapter({
success: function() {
console.log('蓝牙关闭成功')
},
fail: function() {
console.log()
},
complete: function() {
}
})
},
ble_device_scan_start: function(res) {
uni.startBluetoothDevicesDiscovery({
success: (res) => {
console.log(res)
//this.onBluetoothDeviceFound();
uni.onBluetoothDeviceFound((devices)=>{
console.log('new device list has founded')
console.log('name:', devices.devices[0].name)
console.log('localName:', devices.devices[0].localName)
console.log('deviceID:', devices.devices[0].deviceId)
console.log('advertisServiceUUIDs:', devices.devices[0].advertisServiceUUIDs)
console.log('RSSI:', devices.devices[0].RSSI)
//console.log('advertisData:', ab2hex(devices.devices[0].advertisData))
console.log('serivceData:', devices.devices[0].serviceData)
if (devices.devices[0].name == 'EEQ-T069') {
console.log("找到设备:"+devices.devices[0].name + ' MAC:' + devices.devices[0].deviceId);
this.macAddress = devices.devices[0].deviceId;
this.ble_device_scan_stop()
this.bluetooth = devices.devices;
}
uni.getBluetoothDevices({
success: (res) => {
console.log("获取到蓝牙设备:"+res+ res.devices[0].localName)
this.bluetooth = res.devices;
}
})
}),
setTimeout(()=>{
uni.stopBluetoothDevicesDiscovery({
success:function(){
console.log('自动停止搜索')
}
})
}, 5000)
},
fail: function() {
}
})
},
ble_device_scan_stop: function() {
uni.stopBluetoothDevicesDiscovery({
success: function(res){
console.log('停止搜索设备成功: ' + res)
},
fail: function(res){
console.log('停止搜索设备失败: ' + res)
},
complete:function(res){
console.log('停止搜索设备完成: '+ res)
}
})
},
onBluetoothDeviceFound() {
console.log("监听查询设备");
uni.onBluetoothDeviceFound(function(){
this.getBluetoothDevices();
})
},
getBluetoothDevices() {
uni.getBluetoothDevices({
success: (res)=>{
console.log("获取到蓝牙设备:"+res)
this.bluetooth = res.devices;
},
})
},
ble_get_adapter_state: function() {
uni.getBluetoothAdapterState({
success: function(res) {
console.log("adapter state:" + res)
}
})
},
ble_device_connect: function() {
uni.createBLEConnection({
deviceId: this.macAddress, //'11:22:33:44:55:99',
serviceId : this._serviceId,
timeout:5000,
success: (res)=>{
console.log('BLE连接成功', res)
uni.showToast({
title:'设备连接成功',
duration:1500,
}),
uni.notifyBLECharacteristicValueChange({
deviceId: this.macAddress,
serviceId: '0000AE30-0000-1000-8000-00805F9B34FB', //this._serviceId,
characteristicId: '0000AE02-0000-1000-8000-00805F9B34FB', //this._characteristicId,
state:true,
success: (res)=>{
console.log("notify success.");
uni.onBLECharacteristicValueChange(function(res){
console.log('收到通知:');
console.log(`characteristic ${res.characteristicId} has changed, now is ${res.value}`)
var data = ab2hex(res.value);
var arr1 = new Int8Array(res.value);
console.log(ab2hex(res.value));
console.log(data);
console.log(data.length);
console.log(arr1.length);
console.log(arr1[0]);
console.log(arr1[1]);
if(arr1[0] == 0x12) {
uni.showToast({
title: '小程序已连接'
})
} else if (arr1[0] == 0x34) {
uni.showToast({
title: '配网成功'
})
}
})
},
fail: (res)=>{
console.log('notify fail.'+res);
},
complete: (res)=>{
console.log('notify complete.'+res);
}
})
}
})
},
ble_device_get_serviceId: function(){
uni.getBLEDeviceServices({
deviceId: this.macAddress, //'11:22:33:44:55:99',
success: (res)=>{
console.log('BLE设备服务', res.services)
this.services = res.services
}
})
},
ble_device_get_characteristics: function(){
// 0: {uuid: "0000AE30-0000-1000-8000-00805F9B34FB", isPrimary: true}
// 1: {uuid: "0000AE3A-0000-1000-8000-00805F9B34FB", isPrimary: true}
// 2: {uuid: "00001800-0000-1000-8000-00805F9B34FB", isPrimary: true}
// length: 3
for(var i=0; i<this.services.length; i++) {
console.log('uuid:'+this.services[i].uuid);
console.log('isPrimary:'+this.services[i].isPrimary);
uni.getBLEDeviceCharacteristics({
deviceId: this.macAddress, //'11:22:33:44:55:99',
serviceId: this.services[i].uuid, //'0000AE30-0000-1000-8000-00805F9B34FB',
success: function(res){
//console.log('获取设备属性成功' ,res.characteristics);
console.log('获取设备属性成功' ,res);
},
fail: function(res){
console.log('获取设备属性失败:', res)
},
});
}
},
ble_device_write_data: function() {
//var msg = 'WIFI:T:WPA;S:robot-dev;P:666888999;;' //'testwifi:0123456789'; //74657374776966693a3838383838383838
var msg = 'WIFI:T:WPA;'+'S:'+this.ssid+';P:'+this.passwd+';;'
let buffer = new ArrayBuffer(msg.length);
let dataView = new DataView(buffer);
console.log('msg:', + msg.toString());
for (var i=0; i<msg.length; i++) {
dataView.setUint8(i, msg[i].charCodeAt()); // 字符转UNICODE
}
// for (var i=0; i<msg.length; i++) {
// console.log(msg[i].charCodeAt());
// }
// String.fromCharCode(126);
// 设置BLE MTU
uni.setBLEMTU({
deviceId: this.macAddress, //'11:22:33:44:55:99',
mtu:256,
success: function() {
console.log('设置BLE MTU 成功');
}
})
uni.readBLECharacteristicValue({
deviceId: this.macAddress, //'11:22:33:44:55:99',
serviceId: this._serviceId,
characteristicId: '0000AE01-0000-1000-8000-00805F9B34FB',
}),
uni.writeBLECharacteristicValue({
deviceId: this.macAddress, //'11:22:33:44:55:99',
serviceId: '0000AE30-0000-1000-8000-00805F9B34FB',
characteristicId: '0000AE01-0000-1000-8000-00805F9B34FB', // '0000AE10-0000-1000-8000-00805F9B34FB',
value: buffer,
success: function(res){
console.log('BLE写数据成功:', res)
},
fail: function(res){
console.log('BLE写数据失败:', res)
},
complete: function(res){
console.log('BLE写数据完成:', res)
}
})
},
input_ssid: function(e) {
console.info('SSID:'+ e.target.value);
this.ssid = e.target.value;
},
input_passwd: function(e) {
console.info('PSWD:'+e.target.value);
this.passwd = e.target.value;
}
}
}
</script>
<style>
.content {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.logo {
height: 200rpx;
width: 200rpx;
margin-top: 200rpx;
margin-left: auto;
margin-right: auto;
margin-bottom: 50rpx;
}
.text-area {
display: flex;
justify-content: center;
}
.title {
font-size: 36rpx;
color: #8f8f94;
}
</style>
utils.js
// ArrayBuffer转16进度字符串示例
export function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join('')
}
// 字符串转16进制
export function strToHexCharCode(str) {
if(str === "")
return "";
var hexCharCode = [];
hexCharCode.push("0x");
for(var i = 0; i < str.length; i++) {
hexCharCode.push((str.charCodeAt(i)).toString(16));
}
return hexCharCode.join("");
}
// 16进制转字符串
export function hexCharCodeToStr(hexCharCodeStr) {
var trimedStr = hexCharCodeStr.trim();
var rawStr = trimedStr.substr(0,2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr;
var len = rawStr.length;
if(len % 2 !== 0) {
alert("Illegal Format ASCII Code!");
return "";
}
var curCharCode;
var resultStr = [];
for(var i = 0; i < len;i = i + 2) {
curCharCode = parseInt(rawStr.substr(i, 2), 16); // ASCII Code Value
resultStr.push(String.fromCharCode(curCharCode));
}
return resultStr.join("");
}
// ArrayBuffer转为字符串,参数为ArrayBuffer对象
export function ab2str(buf) {
return String.fromCharCode.apply(null, new Uint16Array(buf));
}
// 字符串转为ArrayBuffer对象,参数为字符串
export function str2ab(str) {
var buf = new ArrayBuffer(str.length*2); // 每个字符占用2个字节
var bufView = new Uint16Array(buf);
for (var i=0, strLen=str.length; i<strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
// 获取字符串的utf8字节流
export function getUTF8Bytes(str) {
var bytes = [];
var len = str.length;
for (var i = 0; i < len; ++ i) {
var code = str.charCodeAt(i);
if (code >= 0x10000 && code <= 0x10ffff) {
bytes.push((code >> 18) | 0xf0); // 第一个字节
bytes.push(((code >> 12) & 0x3f) | 0x80);
bytes.push(((code >> 6) & 0x3f) | 0x80);
bytes.push((code & 0x3f) | 0x80);
} else if (code >= 0x800 && code <= 0xffff) {
bytes.push((code >> 12) | 0xe0);
bytes.push(((code >> 6) & 0x3f) | 0x80);
bytes.push((code & 0x3f) | 0x80);
} else if (code >= 0x80 && code <= 0x7ff) {
bytes.push((code >> 6) | 0xc0);
bytes.push((code & 0x3f) | 0x80);
} else {
bytes.push(code);
}
}
return bytes;
}
// 将字节流转换成16进制字符串
export function hexString(bytes) {
var arr = bytes.map(function (code) {
return (code).toString(16).toUpperCase();
});
return arr.join(' ');
}
export function utf8(str) {
return hexString(getUTF8Bytes(str));
}
// ArrayBuffer转为字符串,参数为ArrayBuffer对象
function arraybuffer2String(arr) {
var ints = new Uint8Array(arr);
var str = '', _arr = ints;
for (var i = 0; i < _arr.length; i++) {
var one = _arr[i].toString(2),
v = one.match(/^1+?(?=0)/);
if (v && one.length == 8) {
var bytesLength = v[0].length;
var store = _arr[i].toString(2).slice(7 - bytesLength);
for (var st = 1; st < bytesLength; st++) {
store += _arr[st + i].toString(2).slice(2);
}
str += String.fromCharCode(parseInt(store, 2));
i += bytesLength - 1;
} else {
str += String.fromCharCode(_arr[i]);
}
}
return str;
}
// 字符串转为ArrayBuffer对象,参数为字符串
function string2Arraybuffer(str) {
var bytes = new Array();
var len, c;
len = str.length;
for (var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if (c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if (c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if (c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
var array = new Int8Array(bytes.length);
for (var i in bytes) {
array[i] = bytes[i];
}
return array.buffer;
}