26_微信小程序蓝牙-蓝牙通信篇

注: 示例代码使用uniapp编写,下面通过uni.调用的API侧重于微信侧,等同于wx.,

上一篇文章: 25_微信小程序蓝牙-蓝牙连接篇 中已经完成了蓝牙设备的连接与断开。

本文续接上一篇文章,实现终端设备(手机)与低功耗蓝牙设备间的通信,包括读取和写入数据。

一.通用流程

要实现终端设备与低功耗蓝牙蓝牙设备的通信,首先需要获取低功耗蓝牙设备所有可用的服务,找到每一个服务对应的所有特征,从这些特征中找到对应类型的特征,当然,我们也没有必要找到所有的特征,只需找到第一个满足条件的特征即可。

  • 读取数据找到indicate、notify或read的特征,indicate和notify用于实时读取。
  • 写入数据找到write的特征。

最后通过找到的特征实现读或者写操作。

注: ios端,断开设备连接,下次重新连接后,前一次连接查找到的特征失效

二.读取数据
  • 获取设备所有可用的服务

    调用uni.getBLEDeviceServices,传入已连接设备的设备id即可。

    注:ios平台 && wxwork(企业微信端),需待蓝牙连接成功后,延迟1秒后调用uni.getBLEDeviceServices

    <!-- index.vue -->
    <template>
    	<view class="index-page">
    		<view class="bluetooth-status">蓝牙状态: {{bluetoothStatus}}</view>
    		<scroll-view scroll-y class="device-list">
    			<view v-for="(item, index) in devices" class="device-item">
    				<view class="device-info-box">
    					<view class="device-info">设备名称: {{item.localName || item.name || item.deviceId}}</view>
    					<view class="device-info">设备ID: {{item.deviceId}}</view>
    				</view>
    				<view class="btn-change-connect" @click="toChangeConnect($event, index)">{{item.connected ? "断开连接":"连接"}}</view>
    			</view>
    		</scroll-view>
    		<view class="index__test-btn" @click="toReadBluetoothData($event)">读取数据</view>
    	</view>
    </template>
    
    <script>
    	export default {
    		...
    		computed: {
    			connectedIds() {
    				let devices = this.devices || []
    				let connectedDevices = devices.filter((item) => item.connected)
    				return connectedDevices.map((item) => item.deviceId)
    			}
    		},
    		methods: {
    			...
    			toReadBluetoothData: function(event) {
    				if(this.connectedIds.length <= 0) {
    					uni.showToast({
    						title: "请先连接蓝牙设备"
    					})
    					return
    				}
    				
    				//这里默认取第一个连接上的设备id,如果连接了多个设备,请自行适配
    				let deviceId = this.connectedIds[0]
    				new Promise((resolve, reject) => {
    				  let timer = setTimeout(() => {
    				    clearTimeout(timer)
    				    resolve()
    				  }, 1000)
    				}).then(() => {
    					return uni.getBLEDeviceServices({
    						deviceId: deviceId
    					})
    				}).then((res) => {
    					let services = (res || {}).services || []
    					console.log("toReadBluetoothData", services)
    				})
    			}
    		}
    	}
    </script>
    
    <style scoped>
    	...
    	.index__test-btn {
    		height: 88rpx;
    		border-radius: 12rpx;
    		box-sizing: border-box;
    		background: chocolate;
    		color: white;
    		font-size: 28rpx;
    		display: flex;
    		flex-direction: row;
    		align-items: center;
    		justify-content: center;
    		margin: 20rpx 20rpx constant(safe-area-inset-bottom);
    		margin: 20rpx 20rpx env(safe-area-inset-bottom);
    	}
    </style>
    
  • 调用getBLEDeviceCharacteristics传入deviceId和serviceId获取对应的特征列表,找到第一个indicate、notify或read的特征

    <script>
    	export default {
    		data() {
    			return {
    				bluetoothStatus: "BLUETOOTH_AUTHOR_PENDING",
    				devices: [],
    				characteristicMap: {}
    			}
    		},
    		...
    		methods: {
    			...
          /**
    			 * 找到第一个满足type条件的特征
    			 * @param {Array<any>} services - 所有可用的蓝牙服务
    			 * @param {string} deviceId - 设备id
    			 * @param {number} searchIndex - 从第几个服务开始查找
    			 * @param {string} type - 查找的特征类型,
    			 * type取值: read、write、notify、indicate、writeNoResponse、writeDefault,默认: read
    			 */
    			seekFirstCharacteristic: function(services = [], deviceId = "", searchIndex = 0, type = "indicate") {
    				if(searchIndex < services.length) {
    					let serviceId = services[searchIndex].uuid
    					let characteristicKey = `${deviceId}-${serviceId}-${type}`
    					
    					if(this.characteristicMap[characteristicKey]) {
    						return Promise.resolve(this.characteristicMap[characteristicKey])
    					}
    					
    					return uni.getBLEDeviceCharacteristics({
    						deviceId: deviceId,
    						serviceId: serviceId
    					}).then((res) => {
    						let characteristics = (res || {}).characteristics || []
    						let targetCharacteristics = undefined
    						for (let i = 0; i < characteristics.length  && !targetCharacteristics; ++i) {
    							let properties = (characteristics[i] || {}).properties || {}
    							if (properties[type]) {
    								targetCharacteristics = {
    									serviceId: serviceId,
    									deviceId: deviceId,
    									type: type,
    									characteristicId: characteristics[i].uuid
    								}
    							}
    						}
    						if(targetCharacteristics) {
    							this.characteristicMap[characteristicKey] = targetCharacteristics
    							return Promise.resolve(this.characteristicMap[characteristicKey])
    						} else {
    							searchIndex += 1
    							return this.seekFirstCharacteristic(services, deviceId, searchIndex, type)
    						}
    					}).catch(() => {
    						searchIndex += 1
    						return this.seekFirstCharacteristic(services, deviceId, searchIndex, type)
    					})
    				} else {
    					return Promise.reject({
    						"errMsg": `seekFirstCharacteristic:fail not found ${type} characteristic`,
    						"errno": -2
    					})
    				}
    			},
    			toReadBluetoothData: function(event) {
    				if(this.connectedIds.length <= 0) {
    					uni.showToast({
    						title: "请先连接蓝牙设备"
    					})
    					return
    				}
    				
    				//这里默认取第一个连接上的设备id,如果连接了多个设备,请自行适配
    				let deviceId = this.connectedIds[0]
    				new Promise((resolve, reject) => {
    				  let timer = setTimeout(() => {
    				    clearTimeout(timer)
    				    resolve()
    				  }, 1000)
    				}).then(() => {
    					return uni.getBLEDeviceServices({
    						deviceId: deviceId
    					})
    				}).then((res) => {
    					let services = (res || {}).services || []
    					return this.seekFirstCharacteristic(services, deviceId, 0, "notify")
    				}).then((res) => {
    					console.log("firstNotifyCharacteristic", res)
    				})
    			}
    		}
    	}
    </script>
    
  • 同样可以把seekFirstCharacteristic封装到BluetoothManager中

    ...
    
    function seekFirstCharacteristic(services = [], deviceId = "", searchIndex = 0, type = "read") {
    	if(searchIndex < services.length) {
    		let serviceId = services[searchIndex].uuid
    		let characteristicKey = `${deviceId}-${serviceId}-${type}`
    		
    		if(this.characteristicMap[characteristicKey]) {
    			return Promise.resolve(this.characteristicMap[characteristicKey])
    		}
    		
    		return uni.getBLEDeviceCharacteristics({
    			deviceId: deviceId,
    			serviceId: serviceId
    		}).then((res) => {
    			let characteristics = (res || {}).characteristics || []
    			let targetCharacteristics = undefined
    			for (let i = 0; i < characteristics.length  && !targetCharacteristics; ++i) {
    				let properties = (characteristics[i] || {}).properties || {}
    				if (properties[type]) {
    					targetCharacteristics = {
    						serviceId: serviceId,
    						deviceId: deviceId,
    						type: type,
    						characteristicId: characteristics[i].uuid
    					}
    				}
    			}
    			if(targetCharacteristics) {
    				this.characteristicMap[characteristicKey] = targetCharacteristics
    				return Promise.resolve(this.characteristicMap[characteristicKey])
    			} else {
    				searchIndex += 1
    				return this.getFirstCharacteristic(services, deviceId, searchIndex, type)
    			}
    		}).catch(() => {
    			searchIndex += 1
    			return this.getFirstCharacteristic(services, deviceId, searchIndex, type)
    		})
    	} else {
    		return Promise.reject({
    			"errMsg": `seekFirstCharacteristic:fail not found ${type} characteristic`,
    			"errno": -2
    		})
    	}
    }
    
    class BluetoothManagerInstance implements BluetoothManager {
      ...
      
      private characteristicMap: Object = {};
    
    	private onBLEConnectionStateChange: (result: any) => void = (result) => {
    		let connected = result.connected || false
    		let deviceId = result.deviceId || ""
    		
    		let existsIndex = this.connectedDeviceIds.findIndex((connectedId) => connectedId == deviceId)
    		if(connected) {
    			if(existsIndex == -1) {
    				this.connectedDeviceIds.push(deviceId)
    			}
    		} else {
    			//设备断开连接后,清除当前设备的特征值
    			for(let key in this.characteristicMap) {
    				if(key.indexOf(deviceId) != -1) {
    					this.characteristicMap[key] = undefined
    				}
    			}
    			
    			if(existsIndex != -1) {
    				this.connectedDeviceIds.splice(existsIndex, 1)
    			}
    		}
    		
    		getBluetoothDevices.call(this)
    	}
    
    	/**
    	 * 找到第一个满足type条件的特征
    	 * @param {Array<any>} services - 所有可用的蓝牙服务
    	 * @param {string} deviceId - 设备id
    	 * @param {number} searchIndex - 从第几个服务开始查找
    	 * @param {string} type - 查找的特征类型,
    	 * type取值: read、write、notify、indicate、writeNoResponse、writeDefault,默认: read
    	 */
    	getFirstCharacteristic(services = [], deviceId = "", searchIndex = 0, type = "read") {
    		return seekFirstCharacteristic.call(this, services, deviceId, searchIndex, type)
    	}
    
    	...
    }
    
    <script>
    	export default {
    		...
    		methods: {
    			...
    			toReadBluetoothData: function(event) {
    				if(this.connectedIds.length <= 0) {
    					uni.showToast({
    						title: "请先连接蓝牙设备"
    					})
    					return
    				}
    				
    				//这里默认取第一个连接上的设备id,如果连接了多个设备,请自行适配
    				let deviceId = this.connectedIds[0]
    				new Promise((resolve, reject) => {
    				  let timer = setTimeout(() => {
    				    clearTimeout(timer)
    				    resolve()
    				  }, 1000)
    				}).then(() => {
    					return uni.getBLEDeviceServices({
    						deviceId: deviceId
    					})
    				}).then((res) => {
    					let services = (res || {}).services || []
    					let bluetoothManager = getApp().globalData.bluetoothManager
    					return bluetoothManager.getFirstCharacteristic(services, deviceId, 0, "notify")
    				}).then((res) => {
    					console.log("firstNotifyCharacteristic", res)
    				})
    			}
    		}
    	}
    </script>
    
  • 读取蓝牙设备发送的数据

    • 如果是notify或者indicate,首先调用uni.onBLECharacteristicValueChange注册设备的特征值变化的事件回调,然后调用notifyBLECharacteristicValueChange启用监听,然后在设备的特征值变化的事件回调方法中,就可以获取到蓝牙设备发送的数据了。
    • 如果是read,则需要手动调用uni.readBLECharacteristicValue读取蓝牙设备发送的数据
    • 蓝牙设备发送的数据是一个字节数组,我们将字节数组,转换为16进制后,即为传输数据的ascii码数组,在将这个ascii码数组转换为字符串数组,即得到传输的数据了

    注: indicate操作成功后需要设备主动更新特征的 value,才会触发uni.onBLECharacteristicValueChange回调

    注: 安卓平台上,在uni.notifyBLECharacteristicValueChange接口调用成功后立即调用 uni.writeBLECharacteristicValue接口,在部分机型上会发生 10008 系统错误

    以notify为例:

    <script>
    	export default {
    		...
    		onLoad() {
    			uni.onBLECharacteristicValueChange(this.characteristicValueChange)
    			getApp().globalData.bluetoothManager.on("statusChange", this.onBluetoothStatusChanged)
    			getApp().globalData.bluetoothManager.on("deviceFound", this.onDeviceFound)
    		},
    		onUnload() {
    			getApp().globalData.bluetoothManager.off("statusChange", this.onBluetoothStatusChanged)
    			getApp().globalData.bluetoothManager.off("deviceFound", this.onDeviceFound)
    			uni.offBLECharacteristicValueChange(this.characteristicValueChange)
    		},
    		...
    		methods: {
    			...
          ab2hex(buffer) {
    				let hexArr = Array.prototype.map.call(
    					new Uint8Array(buffer),
    					function(bit) {
    						return ('00' + bit.toString(16)).slice(-2)
    					}
    				)
    				
    				let validate = true
    				for(let i=0; i<hexArr.length; i++) {
    					if(!this.valueMap[hexArr[i]]) {
    						validate = false
    						break
    					}
    				}
    				
    				return validate ? hexArr:[];
    			},
          
    			characteristicValueChange: function(result) {
            let buffer = (result || {}).value || []
    				let asciiArr = this.ab2hex(buffer)
            //ascii码数组转字符串
            ...
    			},
            
    			toReadBluetoothData: function(event) {
    				if(this.connectedIds.length <= 0) {
    					uni.showToast({
    						title: "请先连接蓝牙设备"
    					})
    					return
    				}
    				
    				//这里默认取第一个连接上的设备id,如果连接了多个设备,请自行适配
    				uni.showLoading()
    				let deviceId = this.connectedIds[0]
    				new Promise((resolve, reject) => {
    				  let timer = setTimeout(() => {
    				    clearTimeout(timer)
    				    resolve()
    				  }, 1000)
    				}).then(() => {
    					return uni.getBLEDeviceServices({
    						deviceId: deviceId
    					})
    				}).then((res) => {
    					let services = (res || {}).services || []
    					let bluetoothManager = getApp().globalData.bluetoothManager
    					return bluetoothManager.getFirstCharacteristic(services, deviceId, 0, "notify")
    				}).then((res) => {
    					return uni.notifyBLECharacteristicValueChange({
    						deviceId: res.deviceId,
    						serviceId: res.serviceId,
    						characteristicId: res.characteristicId,
    						state: true
    					})
    				}).finally(() => {
    					uni.hideLoading()
    				})
    			}
    		}
    	}
    </script>
    
三.写入数据

以蓝牙打印机为例,向设备发送tspl指令,关于tspl指令可以参考这篇文章: 打印机TSPL指令集

由于蓝牙之间传输的数据都是ArrayBuffer,所以需要把需要发送的数据预处理成ArrayBuffer。

获取设备所有可用的服务

调用uni.getBLEDeviceCharacteristics传入deviceId和serviceId获取对应的特征列表,找到第一个write的特征

最后调用uni.writeBLECharacteristicValue将ArrayBuffer写入

注: 小程序不会对写入数据包大小做限制,但系统与蓝牙设备会限制蓝牙 4.0 单次传输的数据大小,超过最大字节数后会发生写入错误,建议每次写入不超过 20 字节

注: 若单次写入数据过长,iOS 上存在系统不会有任何回调的情况(包括错误回调)

因此,我们需要将数据以20字节的大小分包写入。

  • 编写tspl指令

    <script>
      export default {
        ...
    		methods: {
          ...
          getTSPLCommand: function() {
    				let tsplCommand = 	`SIZE 60 mm,40 mm\r\n`
    				tsplCommand += 		`DIRECTION 0,0\r\n`
    				tsplCommand += 		`REFERENCE 0,0\r\n`
    				tsplCommand += 		`OFFSET 0 mm\r\n`
    				tsplCommand += 		`DENSITY 15\r\n`
    				tsplCommand += 		`AUTODETECT\r\n`
    				tsplCommand += 		`CLS\r\n`
    				
    				tsplCommand += 		`TEXT 0,16,"TSS24.BF2",0,1,1,"左上"\r\n`
    				tsplCommand += 		`TEXT 384,16,"TSS24.BF2",0,1,1,"右上"\r\n`
    				tsplCommand += 		`TEXT 0,280,"TSS24.BF2",0,1,1,"左下"\r\n`
    				tsplCommand += 		`TEXT 384,280,"TSS24.BF2",0,1,1,"右下"\r\n`
    				tsplCommand += 		`TEXT 192,136,"TSS24.BF2",0,1,1,"中间"\r\n`
    				
    				tsplCommand += 		`PRINT 1,1\r\n`
    				
    				return tsplCommand
    			},
          ...
        }
    	}
    </script>
    
  • 将编写好的tspl指令转成GBK编码的ArrayBuffer,并按照指定字节的大小分包,默认按20字节分

    注: javascript内置的TextEncoder和TextDecoder在微信小程序环境下被阉割了
    所以,转换GBK编码的ArrayBuffer,可以使用如下两个库:
    gbk.js,大小约50kb左右,适合在小程序中使用
    text-encoding,大小约600kb左右,小程序中慎用,否则很容易超包

    <script>
      import { encode } from "@/bluetooth/gbk.js"
      export default {
        ...
    		methods: {
          ...
        	getArrayBufferList: function(tsplCommand = "", subpackage = 20) {
    				const string2ArrayBuffer = (data) => {
    					var data = encode(data)
    					var byteArr = new Array();
    					for (var i = 0; i < data.length; i++) {
    						var ch = data.charAt(i);
    						if (ch == '%') {
    							var num = data.charAt(i + 1) + data.charAt(i + 2);
    							num = parseInt(num, 16);
    							num = num | (-1 << 8);
    							byteArr.push(num);
    							i += 2;
    						} else {
    							byteArr.push(ch.charCodeAt());
    						}
    					}
    					return Uint8Array.from(byteArr).buffer;
    				}
    				
    				const splitBuffer = (buffer, len) => {
    					let byteLength = buffer.byteLength;
    					let result = []
    					for(let i = 0; i < byteLength; i += len) {
    						result.push(buffer.slice(i, i + len))
    					}
    					return result;
    				}
    				
    				let buffer = string2ArrayBuffer(tsplCommand)
    				
    				return splitBuffer(buffer, subpackage)
    			},
          ...
        }
    	}
    </script>
    
  • 写入数据

    <script>
    	import { encode } from "@/bluetooth/gbk.js"
      
      export default {
        ...
    		methods: {
          ...
        	toWriteBluetoothData: function() {
    				if(this.connectedIds.length <= 0) {
    					uni.showToast({
    						title: "请先连接蓝牙设备"
    					})
    					return
    				}
    				
    				let sendloop = this.getArrayBufferList(this.getTSPLCommand())
    				
    				//这里默认取第一个连接上的设备id,如果连接了多个设备,请自行适配
    				uni.showLoading()
    				let deviceId = this.connectedIds[0]
    				new Promise((resolve, reject) => {
    				  let timer = setTimeout(() => {
    				    clearTimeout(timer)
    				    resolve()
    				  }, 1000)
    				}).then(() => {
    					return uni.getBLEDeviceServices({
    						deviceId: deviceId
    					})
    				}).then((res) => {
    					let services = (res || {}).services || []
    					let bluetoothManager = getApp().globalData.bluetoothManager
    					return bluetoothManager.getFirstCharacteristic(services, deviceId, 0, "write")
    				}).then((characteristic) => {
    					return this.writeCharacteristicValue(characteristic, sendloop)
    				}).finally(() => {
    					uni.hideLoading()
    				})
    			}
          ...
        }
    	}
    </script>
    
  • 整个写入蓝牙数据的过程,同样可以封装到BluetoothManager中

    import { encode } from "./gbk.js"
    
    ...
    
    function getArrayBufferList(content = "", subpackage = 20) {
    	const string2ArrayBuffer = (data) => {
    		var data = encode(data)
    		var byteArr = new Array();
    		for (var i = 0; i < data.length; i++) {
    			var ch = data.charAt(i);
    			if (ch == '%') {
    				var num = data.charAt(i + 1) + data.charAt(i + 2);
    				num = parseInt(num, 16);
    				num = num | (-1 << 8);
    				byteArr.push(num);
    				i += 2;
    			} else {
    				byteArr.push(ch.charCodeAt());
    			}
    		}
    		return Uint8Array.from(byteArr).buffer;
    	}
    	
    	const splitBuffer = (buffer, len) => {
    		let byteLength = buffer.byteLength;
    		let result = []
    		for(let i = 0; i < byteLength; i += len) {
    			result.push(buffer.slice(i, i + len))
    		}
    		return result;
    	}
    	
    	let buffer = string2ArrayBuffer(content)
    	
    	return splitBuffer(buffer, subpackage)
    }
    
    function realWriteCharacteristicValue(characteristic, sendloop) {
    	const writeSingle = (index = 0) => {
    		if (index >= sendloop.length) {
    			return Promise.resolve()
    		}
    		
    		let buffer = sendloop[index];
    		
    		return uni.writeBLECharacteristicValue({
    			deviceId: characteristic.deviceId,
    			serviceId: characteristic.serviceId,
    			characteristicId: characteristic.characteristicId,
    			value: buffer
    		}).then(() => {
    			index += 1
    			return writeSingle(index)
    		})
    	}
    	
    	return writeSingle()
    }
    
    class BluetoothManagerInstance implements BluetoothManager {
      ...
      
      /**
    	 * 向蓝牙设备发送数据
    	 * @param {string} deviceId - 设备id
    	 * @param {string} content - 数据内容
    	 * @param {number} subpackage - 分包大小
    	 */
    	writeCharacteristicValue(deviceId: string = "", content: string = "", subpackage: number = 20) {
    		if(!deviceId) {
    			return Promise.reject({
    				"errMsg": `请先连接蓝牙设备`,
    				"errno": -2
    			})
    		}
    		
    		if(!content) {
    			return Promise.reject({
    				"errMsg": `数据不能为空`,
    				"errno": -2
    			})
    		}
    		
    		let sendloop = getArrayBufferList.call(this, content, 20)
    		
    		return new Promise((resolve, reject) => {
    		  let timer = setTimeout(() => {
    			clearTimeout(timer)
    			resolve()
    		  }, 1000)
    		}).then(() => {
    			return uni.getBLEDeviceServices({
    				deviceId: deviceId
    			})
    		}).then((res) => {
    			let services = (res || {}).services || []
    			return this.getFirstCharacteristic(services, deviceId, 0, "write")
    		}).then((characteristic) => {
    			return realWriteCharacteristicValue.call(this, characteristic, sendloop)
    		})
    	}
      
      ...
    }
    
  • 最后来一张打印的效果图

    在这里插入图片描述

四.完整代码
  • gbk.js可以从这里下载

  • bluetooth-manager.ts

    import { encode } from "./gbk.js"
    
    /**
     * 蓝牙权限待申请
     */
    const BLUETOOTH_AUTHOR_PENDING:string = "BLUETOOTH_AUTHOR_PENDING"
    /**
     * 蓝牙权限被拒绝
     */
    const BLUETOOTH_AUTHOR_REJECT:string = "BLUETOOTH_AUTHOR_REJECT"
    /**
     * BluetoothAdapter不可用
     */
    const BLUETOOTH_ADAPTER_UNAVAILABLE:string = "BLUETOOTH_ADAPTER_UNAVAILABLE"
    /**
     * 蓝牙适配器可用
     */
    const BLUETOOTH_ADAPTER_AVAILABLE:string = "BLUETOOTH_ADAPTER_AVAILABLE"
    
    export interface BluetoothManager {
    	on(type: string, callback: (status: string) => void): void;
    	off(type: string, callback: (status: string) => void): void;
    	destroy(): void;
    }
    
    function dispatchDeviceFound(devices: Array<any>) {
    	this.devicesCallbacks.forEach((func) => {
    		func && func(devices)
    	})
    }
    
    function dispatchBluetoothStatus(status: string): void {
    	this.statusCallbacks.forEach((func) => {
    		func && func(status)
    	})
    }
    
    function startScanBluetoothDevice(): Promise<any> {
    	return uni.startBluetoothDevicesDiscovery({
    		allowDuplicatesKey: false,
    		interval: 1000
    	}).catch((err) => {
    		return Promise.reject()
    	}).then(() => {
    		//添加搜索到新设备的事件的监听函数
    		uni.offBluetoothDeviceFound(this.onBluetoothDeviceFound)
    		uni.onBluetoothDeviceFound(this.onBluetoothDeviceFound)
    		return Promise.resolve()
    	})
    }
    
    function stopScanBluetoothDevice():Promise<any> {
    	//移除搜索到新设备的事件的监听函数
    	uni.offBluetoothDeviceFound(this.onBluetoothDeviceFound)
    	return uni.stopBluetoothDevicesDiscovery().catch(() => undefined)
    }
    
    function getBluetoothDevices(): void {
    	uni.getBluetoothDevices().then((res) => {
    		let repeatDevices = (res || {}).devices || []
    		
    		//去重
    		let devices = []
    		repeatDevices.forEach((item) => {
    			let deviceId = item.deviceId
    			let existsIndex = devices.findIndex((existsItem) => existsItem.deviceId == deviceId)
    			if(existsIndex == -1) {
    				devices.push(item)
    			}
    		})
    		
    		//排序
    		devices.sort((item1, item2) => {
    			let name1 = (item1.localName || item1.name).toLowerCase()
    			let name2 = (item2.localName || item2.name).toLowerCase()
    			return name1.localeCompare(name2)
    		})
    		
    		//判断连接状态
    		devices.forEach((item) => {
    			let deviceId = item.deviceId
    			let existsIndex = this.connectedDeviceIds.findIndex((connectedId) => connectedId == deviceId)
    			item.connected = existsIndex != -1
    		})
    		
    		dispatchDeviceFound.call(this, devices)
    	})
    }
    
    function initializeBluetooth(): Promise<any> {
    	this.status = BLUETOOTH_AUTHOR_PENDING
    	dispatchBluetoothStatus.call(this, this.status)
    	
    	const scope:string = "scope.bluetooth"
    	const openBluetoothAdapter = ():Promise<any> => {
    		return uni.openBluetoothAdapter().catch((err) => {
    			this.status = BLUETOOTH_ADAPTER_UNAVAILABLE
    			dispatchBluetoothStatus.call(this, this.status)
    			return Promise.reject()
    		}).then((res) => {
    			this.status = BLUETOOTH_ADAPTER_AVAILABLE
    			dispatchBluetoothStatus.call(this, this.status)
    			return Promise.resolve()
    		}).finally(() => {
    			//关闭蓝牙适配器状态监听
    			uni.offBluetoothAdapterStateChange(this.adapterStateChange)
    			//开启蓝牙适配器状态监听
    			uni.onBluetoothAdapterStateChange(this.adapterStateChange)
    		})
    	}
    	
    	const openSetting = () => {
    		return uni.showModal({
    			content: "您需要在设置中开启\"蓝牙\"选项,才可以使用蓝牙哦!",
    			cancelText: "任性不去",
    			confirmText: "去设置",
    		}).then((res) => {
    			if (res.confirm) {
    				return new Promise((resolve, reject) => {
    					this.openSettingResolves.push(resolve)
    					uni.openSetting().catch((error) => {
    						reject({
    							"errMsg": "openSetting:fail system error",
    							"errno": -2
    						})
    					})
    				}).then((res) => {
    					let authSetting = res.authSetting || {}
    					if (authSetting[scope]) {
    						return Promise.resolve()
    					} else {
    						return openSetting()
    					}
    				})
    			} else {
    				return Promise.reject({
    					"errMsg": "authorize:fail auth deny",
    					"errno": 103
    				})
    			}
    		})
    	}
    	
    	const getSetting = () => {
    		return uni.getSetting().catch((error) => {
    			return Promise.reject({
    				"errMsg": "authorize:fail system error",
    				"errno": -2
    			})
    		}).then((res) => {
    			let authSetting = res.authSetting || {}
    			let keys = Object.keys(authSetting)
    			
    			if (keys.indexOf(scope) != -1) {
    				if(!authSetting[scope]) {
    					return openSetting()
    				} else {
    					return Promise.resolve() 
    				}
    			} else {
    				return Promise.reject({
    					"errMsg": "authorize:fail auth canceled",
    					"errno": -3
    				})
    			}
    		})
    	}
    	
    	return uni.authorize({scope: scope}).catch((err) => {
    		return getSetting()
    	}).catch((err) => {
    		this.status = BLUETOOTH_AUTHOR_REJECT
    		dispatchBluetoothStatus.call(this, this.status)
    		return Promise.reject()
    	}).then((res) => {
    		return uni.closeBluetoothAdapter().catch((err) => Promise.resolve())
    	}).then((res) => {
    		return openBluetoothAdapter()
    	}).then((res) => {
    		return stopScanBluetoothDevice.call(this)
    	}).then((res) => {
    		return startScanBluetoothDevice.call(this)
    	}).then((res) => {
    		//关闭蓝牙连接状态监听
    		uni.offBLEConnectionStateChange(this.onBLEConnectionStateChange)
    		//开启蓝牙连接状态监听
    		uni.onBLEConnectionStateChange(this.onBLEConnectionStateChange)
    	})
    }
    
    function seekFirstCharacteristic(services = [], deviceId = "", searchIndex = 0, type = "read") {
    	if(searchIndex < services.length) {
    		let serviceId = services[searchIndex].uuid
    		let characteristicKey = `${deviceId}-${serviceId}-${type}`
    		
    		if(this.characteristicMap[characteristicKey]) {
    			return Promise.resolve(this.characteristicMap[characteristicKey])
    		}
    		
    		return uni.getBLEDeviceCharacteristics({
    			deviceId: deviceId,
    			serviceId: serviceId
    		}).then((res) => {
    			let characteristics = (res || {}).characteristics || []
    			let targetCharacteristics = undefined
    			for (let i = 0; i < characteristics.length  && !targetCharacteristics; ++i) {
    				let properties = (characteristics[i] || {}).properties || {}
    				if (properties[type]) {
    					targetCharacteristics = {
    						serviceId: serviceId,
    						deviceId: deviceId,
    						type: type,
    						characteristicId: characteristics[i].uuid
    					}
    				}
    			}
    			if(targetCharacteristics) {
    				this.characteristicMap[characteristicKey] = targetCharacteristics
    				return Promise.resolve(this.characteristicMap[characteristicKey])
    			} else {
    				searchIndex += 1
    				return this.getFirstCharacteristic(services, deviceId, searchIndex, type)
    			}
    		}).catch(() => {
    			searchIndex += 1
    			return this.getFirstCharacteristic(services, deviceId, searchIndex, type)
    		})
    	} else {
    		return Promise.reject({
    			"errMsg": `seekFirstCharacteristic:fail not found ${type} characteristic`,
    			"errno": -2
    		})
    	}
    }
    
    function getArrayBufferList(content = "", subpackage = 20) {
    	const string2ArrayBuffer = (data) => {
    		var data = encode(data)
    		var byteArr = new Array();
    		for (var i = 0; i < data.length; i++) {
    			var ch = data.charAt(i);
    			if (ch == '%') {
    				var num = data.charAt(i + 1) + data.charAt(i + 2);
    				num = parseInt(num, 16);
    				num = num | (-1 << 8);
    				byteArr.push(num);
    				i += 2;
    			} else {
    				byteArr.push(ch.charCodeAt());
    			}
    		}
    		return Uint8Array.from(byteArr).buffer;
    	}
    	
    	const splitBuffer = (buffer, len) => {
    		let byteLength = buffer.byteLength;
    		let result = []
    		for(let i = 0; i < byteLength; i += len) {
    			result.push(buffer.slice(i, i + len))
    		}
    		return result;
    	}
    	
    	let buffer = string2ArrayBuffer(content)
    	
    	return splitBuffer(buffer, subpackage)
    }
    
    function realWriteCharacteristicValue(characteristic, sendloop) {
    	const writeSingle = (index = 0) => {
    		if (index >= sendloop.length) {
    			return Promise.resolve()
    		}
    		
    		let buffer = sendloop[index];
    		
    		return uni.writeBLECharacteristicValue({
    			deviceId: characteristic.deviceId,
    			serviceId: characteristic.serviceId,
    			characteristicId: characteristic.characteristicId,
    			value: buffer
    		}).then(() => {
    			index += 1
    			return writeSingle(index)
    		})
    	}
    	
    	return writeSingle()
    }
    
    class BluetoothManagerInstance implements BluetoothManager {
    	
    	private status:string;
    	private statusCallbacks: Array<(status: string) => void> = [];
    	private openSettingResolves: Array<(result:any) => void> = [];
    	private devicesCallbacks: Array<(devices: Array<any>) => void> = [];
    	
    	private onAppShow: (options: any) => void = (options): void => {
    		uni.getSetting().then((res) => {
    			if(this.openSettingResolves.length > 0) {
    				this.openSettingResolves.forEach((resolveFunc) => resolveFunc(res))
    				this.openSettingResolves = []
    			} else {
    				let authSetting = res.authSetting || {}
    				let keys = Object.keys(authSetting)
    				
    				if(keys.indexOf("scope.bluetooth") != -1) {
    					if (!authSetting["scope.bluetooth"]) {
    						if(this.status != BLUETOOTH_AUTHOR_PENDING) {
    							this.status = BLUETOOTH_AUTHOR_REJECT
    							dispatchBluetoothStatus.call(this, this.status)
    						}
    					} else {
    						if(this.status == BLUETOOTH_AUTHOR_REJECT) {
    							initializeBluetooth.call(this);
    						}
    					}
    				}
    			}
    			return Promise.resolve()
    		}).catch((err) => {
    			return Promise.resolve()
    		})
    	}
    	
    	private adapterStateChange: (result: any) => void = (res) => {
    		let available = (res || {}).available || false
    		let discovering = (res || {}).discovering || false
    		
    		let bluetoothStatus = BLUETOOTH_ADAPTER_UNAVAILABLE
    		if(available) {
    			bluetoothStatus = BLUETOOTH_ADAPTER_AVAILABLE
    		}
    		if(this.status != bluetoothStatus) {
    			//蓝牙开关状态改变
    			if(bluetoothStatus == BLUETOOTH_ADAPTER_AVAILABLE) {
    				//搜索设备
    				startScanBluetoothDevice.call(this)
    			} else {
    				//停止搜索设备
    				stopScanBluetoothDevice.call(this)
    			}
    		}
    		
    		this.status = bluetoothStatus
    	}
    	
    	private onBluetoothDeviceFound: (result: any) => void = (result) => {
    		getBluetoothDevices.call(this)
    	}
    	
    	private connectedDeviceIds: Array<string> = [];
    	
    	private onBLEConnectionStateChange: (result: any) => void = (result) => {
    		let connected = result.connected || false
    		let deviceId = result.deviceId || ""
    		
    		let existsIndex = this.connectedDeviceIds.findIndex((connectedId) => connectedId == deviceId)
    		if(connected) {
    			if(existsIndex == -1) {
    				this.connectedDeviceIds.push(deviceId)
    			}
    		} else {
          //设备断开连接后,清除当前设备的特征值
    			for(let key in this.characteristicMap) {
    				if(key.indexOf(deviceId) != -1) {
    					this.characteristicMap[key] = undefined
    				}
    			}
          
    			if(existsIndex != -1) {
    				this.connectedDeviceIds.splice(existsIndex, 1)
    			}
    		}
    		
    		getBluetoothDevices.call(this)
    	}
    	
    	private characteristicMap: Object = {};
    	
    	constructor() {
    		uni.onAppShow(this.onAppShow);
    		initializeBluetooth.call(this);
    	}
    	
    	on(type: string, callback: (result: any) => void): void {
    		if(type == "statusChange") {
    			this.statusCallbacks.push(callback)
    		} else if(type == "deviceFound") {
    			this.devicesCallbacks.push(callback)
    		}
    	}
    	
    	off(type: string, callback: (result: any) => void): void {
    		if(type == "statusChange") {
    			let callbackIndex = this.statusCallbacks.findIndex((item) => item == callback)
    			if(callbackIndex == -1) {
    				return
    			}
    			this.statusCallbacks.splice(callbackIndex, 1)
    		} else if(type == "deviceFound") {
    			let callbackIndex = this.devicesCallbacks.findIndex((item) => item == callback)
    			if(callbackIndex == -1) {
    				return
    			}
    			this.devicesCallbacks.splice(callbackIndex, 1)
    		}
    	}
    	
    	/**
    	 * 找到第一个满足type条件的特征
    	 * @param {Array<any>} services - 所有可用的蓝牙服务
    	 * @param {string} deviceId - 设备id
    	 * @param {number} searchIndex - 从第几个服务开始查找
    	 * @param {string} type - 查找的特征类型,
    	 * type取值: read、write、notify、indicate、writeNoResponse、writeDefault,默认: read
    	 */
    	getFirstCharacteristic(services = [], deviceId = "", searchIndex = 0, type = "read") {
    		return seekFirstCharacteristic.call(this, services, deviceId, searchIndex, type)
    	}
    	
    	/**
    	 * 向蓝牙设备发送数据
    	 * @param {string} deviceId - 设备id
    	 * @param {string} content - 数据内容
    	 * @param {number} subpackage - 分包大小
    	 */
    	writeCharacteristicValue(deviceId: string = "", content: string = "", subpackage: number = 20) {
    		if(!deviceId) {
    			return Promise.reject({
    				"errMsg": `请先连接蓝牙设备`,
    				"errno": -2
    			})
    		}
    		
    		if(!content) {
    			return Promise.reject({
    				"errMsg": `数据不能为空`,
    				"errno": -2
    			})
    		}
    		
    		let sendloop = getArrayBufferList.call(this, content, 20)
    		
    		return new Promise((resolve, reject) => {
    		  let timer = setTimeout(() => {
    			clearTimeout(timer)
    			resolve()
    		  }, 1000)
    		}).then(() => {
    			return uni.getBLEDeviceServices({
    				deviceId: deviceId
    			})
    		}).then((res) => {
    			let services = (res || {}).services || []
    			return this.getFirstCharacteristic(services, deviceId, 0, "write")
    		}).then((characteristic) => {
    			return realWriteCharacteristicValue.call(this, characteristic, sendloop)
    		})
    	}
    	
    	destroy(): void {
    		this.status = BLUETOOTH_AUTHOR_PENDING
    		
    		//关闭蓝牙连接状态监听
    		uni.offBLEConnectionStateChange(this.onBLEConnectionStateChange)
    		
    		stopScanBluetoothDevice.call(this)
    		
    		//关闭蓝牙适配器状态监听
    		uni.offBluetoothAdapterStateChange(this.adapterStateChange)
    		
    		uni.closeBluetoothAdapter().catch(() => undefined)
    		this.statusCallbacks = []
    		this.devicesCallbacks = []
    		this.connectedDeviceIds = []
    		
    		uni.offAppShow(this.onAppShow)
    	}
    	
    }
    
    export const createBluetoothManager = ():BluetoothManager => {
    	return new BluetoothManagerInstance();
    }
    
  • index.vue

    <template>
    	<view class="index-page">
    		<view class="bluetooth-status">蓝牙状态: {{bluetoothStatus}}</view>
    		<scroll-view scroll-y class="device-list">
    			<view v-for="(item, index) in devices" class="device-item">
    				<view class="device-info-box">
    					<view class="device-info">设备名称: {{item.localName || item.name || item.deviceId}}</view>
    					<view class="device-info">设备ID: {{item.deviceId}}</view>
    				</view>
    				<view class="btn-change-connect" @click="toChangeConnect($event, index)">{{item.connected ? "断开连接":"连接"}}</view>
    			</view>
    		</scroll-view>
    		<view class="index__test-btn" @click="toWriteBluetoothData($event)">写入数据</view>
    	</view>
    </template>
    
    <script>
    	export default {
    		data() {
    			return {
    				bluetoothStatus: "BLUETOOTH_AUTHOR_PENDING",
    				devices: []
    			}
    		},
    		onLoad() {
    			getApp().globalData.bluetoothManager.on("statusChange", this.onBluetoothStatusChanged)
    			getApp().globalData.bluetoothManager.on("deviceFound", this.onDeviceFound)
    		},
    		onUnload() {
    			getApp().globalData.bluetoothManager.off("statusChange", this.onBluetoothStatusChanged)
    			getApp().globalData.bluetoothManager.off("deviceFound", this.onDeviceFound)
    		},
    		computed: {
    			connectedIds() {
    				let devices = this.devices || []
    				let connectedDevices = devices.filter((item) => item.connected)
    				return connectedDevices.map((item) => item.deviceId)
    			}
    		},
    		methods: {
    			onBluetoothStatusChanged: function(status) {
    				this.bluetoothStatus = status
    			},
    			
    			onDeviceFound: function(devices) {
    				this.devices = devices
    			},
    			
    			toChangeConnect: function(event, index) {
    				let item = this.devices[index]
    				
    				uni.showLoading()
    				let changeTask = undefined
    				if(!item.connected) {
    					changeTask = uni.createBLEConnection({
    						deviceId: item.deviceId
    					})
    				} else {
    					changeTask = uni.closeBLEConnection({
    						deviceId: item.deviceId
    					})
    				}
    				changeTask.finally(() => {
    					uni.hideLoading()
    				})
    			},
    			
    			getTSPLCommand: function() {
    				let tsplCommand = 	`SIZE 60 mm,40 mm\r\n`
    				tsplCommand += 		`DIRECTION 0,0\r\n`
    				tsplCommand += 		`REFERENCE 0,0\r\n`
    				tsplCommand += 		`OFFSET 0 mm\r\n`
    				tsplCommand += 		`DENSITY 15\r\n`
    				tsplCommand += 		`AUTODETECT\r\n`
    				tsplCommand += 		`CLS\r\n`
    				
    				tsplCommand += 		`TEXT 0,16,"TSS24.BF2",0,1,1,"左上"\r\n`
    				tsplCommand += 		`TEXT 384,16,"TSS24.BF2",0,1,1,"右上"\r\n`
    				tsplCommand += 		`TEXT 0,280,"TSS24.BF2",0,1,1,"左下"\r\n`
    				tsplCommand += 		`TEXT 384,280,"TSS24.BF2",0,1,1,"右下"\r\n`
    				tsplCommand += 		`TEXT 192,136,"TSS24.BF2",0,1,1,"中间"\r\n`
    				
    				tsplCommand += 		`PRINT 1,1\r\n`
    				
    				return tsplCommand
    			},
    			
    			toWriteBluetoothData: function() {
    				//这里默认取第一个连接上的设备id,如果连接了多个设备,请自行适配
    				uni.showLoading()
    				let deviceId = this.connectedIds[0]
    				
    				let bluetoothManager = getApp().globalData.bluetoothManager
    				let content = this.getTSPLCommand()
    				bluetoothManager.writeCharacteristicValue(deviceId, content).finally(() => {
    					uni.hideLoading()
    				})
    			}
    		}
    	}
    </script>
    
    <style scoped>
    	.index-page {
    		width: 100%;
    		height: 100%;
    		display: flex;
    		flex-direction: column;
    		align-items: stretch;
    		justify-content: center;
    	}
    	
    	.bluetooth-status {
    		font-size: 24rpx;
    		padding: 20rpx 0;
    		margin: 0 20rpx;
    		box-sizing: border-box;
    		flex-shrink: 0;
    		border-bottom: thin solid #f2f2f2;
    	}
    	
    	.device-list {
    		flex: 1;
    		overflow: hidden;
    	}
    	
    	.device-item {
    		padding: 20rpx 0;
    		margin: 0 20rpx;
    		box-sizing: border-box;
    		background: white;
    		border-bottom: thin solid #f2f2f2;
    		display: flex;
    		flex-direction: row;
    		align-items: center;
    		justify-content: center;
    	}
    	
    	.device-info {
    		font-size: 28rpx;
    		margin-top: 20rpx;
    	}
    	
    	.device-info:first-child {
    		margin-top: 0;
    	}
    	
    	.device-info-box {
    		flex: 1;
    		overflow: hidden;
    		display: flex;
    		flex-direction: column;
    		align-items: stretch;
    		justify-content: center;
    	}
    	
    	.btn-change-connect {
    		padding: 20rpx 60rpx;
    		box-sizing: border-box;
    		background: #2BA3FF;
    		color: white;
    		font-size: 28rpx;
    		border-radius: 10rpx;
    	}
    	
    	.index__test-btn {
    		height: 88rpx;
    		border-radius: 12rpx;
    		box-sizing: border-box;
    		background: chocolate;
    		color: white;
    		font-size: 28rpx;
    		display: flex;
    		flex-direction: row;
    		align-items: center;
    		justify-content: center;
    		margin: 20rpx 20rpx constant(safe-area-inset-bottom);
    		margin: 20rpx 20rpx env(safe-area-inset-bottom);
    	}
    </style>
    
五.补充:将读取到蓝牙数据的ArrayBuffer解析成字符串
  • gbk.js

    注: re的值太长,请自行下载gbk.js,然后从下载好的文件中复制过来

    var U2Ghash;
    var G2Uhash;
    
    function data() {
      //re的值太长,请自行下载gbk.js,然后从下载好的文件中复制过来
      var re = ''
        .replace(/#(\d+)\$/g, function(a, b) {
          return Array(+b + 3).join('#');
        })
        .replace(/#/g, '####')
        .replace(/(\w\w):([\w#]+)(?:,|$)/g, function(a, hd, dt) {
          return dt.replace(/../g, function(a) {
            if (a != '##') {
              return hd + a;
            } else {
              return a;
            }
          });
        });
      U2Ghash = {},
      G2Uhash = {};
      return re;
    }
    
    function initUsChash(data) {
      var k = 0;
      data = data.match(/..../g);
      for (var i = 0x81; i <= 0xfe; i++) {
        for (var j = 0x40; j <= 0xFE; j++) {
          U2Ghash[data[k++]] = ('%' + i.toString(16) + '%' + j.toString(16))
            .toUpperCase();
        }
      }
      for (var key in U2Ghash) {
        G2Uhash[U2Ghash[key]] = key;
      }
    }
    
    function isAscii(unicode) {
      return ((unicode == 0x20AC) || (unicode <= 0x007F && unicode >= 0x0000));
    }
    
    export const encode = function(str) {
      if (U2Ghash == undefined) {
        initUsChash(data());
      }
      return str.replace(/./g, function(a) {
        var code = a.charCodeAt(0);
        if (isAscii(code)) {
          return encodeURIComponent(a);
        } else {
          var key = code.toString(16);
          if (key.length != 4) key = ('000' + key).match(/....$/)[0];
          return U2Ghash[key] || a;
        }
      });
    }
    
    export const decode = function(str) {
      if (G2Uhash == undefined) {
    	initUsChash(data());
      }
    
      return str.replace(/%[\w]{2}/g, function(a) {
    	let unicode = parseInt(a.replace(/%/g, ""), 16);
    	if(isAscii(unicode)) {
    	  return decodeURIComponent(a);
    	}
    	return a
      }).replace(/%[0-9A-F]{2}%[0-9A-F]{2}/g, function(a) {
        if (a in G2Uhash) {
          return String.fromCharCode('0x' + G2Uhash[a]);
        } else {
          return a;
        }
      })
    }
    
  • 在收到蓝牙数据的回调中解析数据

    <script>	
    	import { decode } from "@/bluetooth/gbk.js"
    	export default {
    		...
    		onLoad() {
    			getApp().globalData.bluetoothManager.on("statusChange", this.onBluetoothStatusChanged)
    			getApp().globalData.bluetoothManager.on("deviceFound", this.onDeviceFound)
    			uni.onBLECharacteristicValueChange(this.characteristicValueChange)
    		},
    		onUnload() {
    			uni.offBLECharacteristicValueChange(this.characteristicValueChange)
    			getApp().globalData.bluetoothManager.off("statusChange", this.onBluetoothStatusChanged)
    			getApp().globalData.bluetoothManager.off("deviceFound", this.onDeviceFound)
    		},
    		...
    		methods: {
    			...
    			
    			characteristicValueChange: function(event) {
    				let value = event.value
    				let uint8Value = new Uint8Array(value)
    				let result = Array.prototype.map.call(uint8Value, (unicode) => {
    					if((unicode == 0x20AC) || (unicode <= 0x007F && unicode >= 0x0000)) {
    						return encodeURIComponent(String.fromCharCode(unicode))
    					} else {
    						return "%" + ("00" + unicode.toString(16)).slice(-2).toUpperCase()
    					}
    				}).join("")
    				console.log("收到蓝牙发送的数据:", decode(result))
    			},
    		
    			toWriteBluetoothData: function() {
    				// //这里默认取第一个连接上的设备id,如果连接了多个设备,请自行适配
    				// uni.showLoading()
    				// let deviceId = this.connectedIds[0]
    
    				// let bluetoothManager = getApp().globalData.bluetoothManager
    				// let content = this.getTSPLCommand()
    
    				// bluetoothManager.writeCharacteristicValue(deviceId, content).finally(() => {
    				// 	uni.hideLoading()
    				// })
    				
    				
    				uni.showLoading()
    				let deviceId = this.connectedIds[0]
    				return new Promise((resolve, reject) => {
    					let timer = setTimeout(() => {
    						clearTimeout(timer)
    						resolve()
    					}, 1000)
    				}).then(() => {
    					return uni.getBLEDeviceServices({
    						deviceId: deviceId
    					})
    				}).then((res) => {
    					let services = (res || {}).services || []
    					let bluetoothManager = getApp().globalData.bluetoothManager
    					return bluetoothManager.getFirstCharacteristic(services, deviceId, 0, "notify")
    				}).then((res) => {
    					return uni.notifyBLECharacteristicValueChange({
    						deviceId: res.deviceId,
    						serviceId: res.serviceId,
    						characteristicId: res.characteristicId,
    						state: true
    					})
    				}).then((res) => {
    					let bluetoothManager = getApp().globalData.bluetoothManager
              //向打印机发送这个指令,后打印机会通过"蓝牙数据回调方法"回复我们设备的型号
    					let content = `~!T`
    					return bluetoothManager.writeCharacteristicValue(deviceId, content)
    				}).finally(() => {
    					uni.hideLoading()
    				})
    			}
    		}
    	}
    </script>
    

    在这里插入图片描述

微信小程序-蓝牙篇完结。

更多推荐