蓝牙

蓝牙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;
}
Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐