uni-app 蓝牙设备链接

封装设备属性和方法

const devicesConfig ={
	config:{
		// 存储节点变量名
		deviceUseScanGun: "device_use_scan_gun",//是否使用扫码枪
		deviceScanGun : 'device_scan_gun',//扫码枪设备
		devicePrint : 'device_print',//打印机设备
	},
	
	initDevice: function(type) {
		console.log('初始化蓝牙..............')
		return new Promise((resolve, reject) => {
			uni.openBluetoothAdapter({
				success(res) {
					let device = uni.getStorageSync(type)
					if(!device || !device.connected){
						uni.showToast({
							title: '请链接蓝牙设备',
							icon: 'error'
						})
						return
					}
					console.log('初始化蓝牙成功..............')
					resolve(device)
				},
				fail(err) {
					uni.showToast({
						title: '初始化蓝牙失败',
						icon: 'error'
					})
					reject("初始化蓝牙失败")
				}
			})
		})
		
	},
	
	// 开启监听扫码枪
	onSacnGunValue: function(backFunc){
		devicesConfig.initDevice(devicesConfig.config.deviceScanGun).then(scanGun => {
			console.log("开启监听----设备id:" + scanGun.deviceId + ",服务id:" + scanGun.serviceId +
				",特征值id:" + scanGun.characteristicId)
			uni.notifyBLECharacteristicValueChange({
				deviceId: scanGun.deviceId, // 设备id
				serviceId: scanGun.serviceId, // 监听指定的服务
				characteristicId: scanGun.characteristicId, // 监听对应的特征值
				success(res) {
					uni.onBLECharacteristicValueChange(res => {
						let resHex = devicesConfig.ab2hex(res.value)
						let result = devicesConfig.hexCharCodeToStr(resHex)
						console.log(result)
						backFunc(result)
					})
				},
				fail(err) {
					console.error(err)
					uni.showToast({
						title: '请配置扫码枪',
						icon: 'error'
					})
				}
			})
		})
	},
	
	// ArrayBuffer转16进度字符串示例
	ab2hex: function(buffer){
		const hexArr = Array.prototype.map.call(
			new Uint8Array(buffer),
			function(bit) {
				return ('00' + bit.toString(16)).slice(-2)
			}
		)
		return hexArr.join('')
	},
	// 将16进制的内容转成我们看得懂的字符串内容
	hexCharCodeToStr: function(hexCharCodeStr) {
		let trimedStr = hexCharCodeStr.trim()
		let rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr
		let len = rawStr.length
		if (len % 2 !== 0) {
			alert("存在非法字符!")
			return ""
		}
		let curCharCode
		let resultStr = []
		for (let i = 0; i < len; i = i + 2) {
			curCharCode = parseInt(rawStr.substr(i, 2), 16)
			resultStr.push(String.fromCharCode(curCharCode))
		}
		return resultStr.join("").replace("\r",'')
	},
	
	//发送数据打印
	senBlData(deviceId, serviceId, characteristicId,uint8Array) {
		console.log('************deviceId = [' + deviceId + ']  serviceId = [' + serviceId + '] characteristics=[' +characteristicId+ "]")
		var uint8Buf = Array.from(uint8Array);
		function split_array(datas,size){
			var result = {};
			var j = 0
			for (var i = 0; i < datas.length; i += size) {
				result[j] = datas.slice(i, i + size)
				j++
			}
			console.log(result)
			return result
		}
		var sendloop = split_array(uint8Buf, 20);
		// console.log(sendloop.length)
		function realWriteData(sendloop, i) {
			var data = sendloop[i]
			if(typeof(data) == "undefined"){
				return
			}
			console.log("第【" + i + "】次写数据"+data)
			var buffer = new ArrayBuffer(data.length)
			var dataView = new DataView(buffer)
			for (var j = 0; j < data.length; j++) {
				dataView.setUint8(j, data[j]);
			}
			uni.writeBLECharacteristicValue({
				deviceId,
				serviceId,
				characteristicId,
				value: buffer,
				success(res) {
					realWriteData(sendloop, i + 1);
				}
			})
		}
	   var i = 0;
		realWriteData(sendloop, i);
	},
	
}

export default devicesConfig


设备链接

<template>
  <view class="help-container" style="height: 100%">
    <view title="蓝牙链接说明" class="list-title" style="height: 35%;">
      <view class="text-title">
        <view class="iconfont icon-help">扫码枪蓝牙链接说明</view>
		<button v-if="scanSetting.useScanGun" style="float:right;top:-10px;" class="mini-btn" type="primary" size="mini" @click="setScan">关闭扫码枪</button>
		<button v-if="!scanSetting.useScanGun" style="float:right;top:-10px;" class="mini-btn" type="default" size="mini" @click="setScan">启用扫码枪</button>
      </view>
      <view class="childList">
        <view  class="question" hover-class="hover">
          <view class="text-item">1:手机开启蓝牙</view>
          <view class="line" ></view>
        </view>
		<view  class="question" hover-class="hover">
		  <view class="text-item">2:点击下方“搜索蓝牙设备”按钮搜索设备</view>
		  <view class="line" ></view>
		</view>
		<view  class="question" hover-class="hover">
		  <view class="text-item">3:如果没有搜索到您需要的设备,请将您的设备调至蓝牙BLE模式</view>
		  <view >
		    <image src="@/static/images/scan_ble.png" style="height: 100px;" />
		  </view>
		  <view class="line" ></view>
		</view>
		<view  class="question" hover-class="hover">
		  <view class="text-item">4:点击搜索到的蓝牙设备进行链接</view>
		  <view class="line" ></view>
		</view>
      </view>
    </view>
	<view title="搜索蓝牙设备" class="list-title" style="height: 30%;">
	  <view class="text-title">
		<button type="primary" :disabled="searchDisabled" @click="initBlue" size="mini">
			<uni-icons type="search" size="10" color="F000" style="margin-right: 5px;"></uni-icons>
			搜索设备
		</button>
		<button type="primary" :disabled="!searchDisabled" @click="stopDiscovery()" size="mini" style="margin-left: 5px;">
			<uni-icons custom-prefix="iconfont" type="icon-stop" size="10" color="F000" style="margin-right: 5px;"></uni-icons>
			停止搜索
		</button>
		<button type="primary" :disabled="searchDisabled" @click="toDeviceTest" size="mini" style="margin-left: 5px;">
			<uni-icons type="checkbox" size="10" color="F000" style="margin-right: 5px;"></uni-icons>
			蓝牙测试
		</button>
	  </view>
	  <scroll-view scroll-y="true" class="scroll-Y childList" style="height: 150px;">
	    <view v-for="(item,zindex) in blueDeviceList" @click="connect(item)" :key="zindex" class="question" hover-class="hover">
	      <view class="text-item">{{ item.name }}</view>
	      <view class="line" v-if="zindex !== blueDeviceList.length - 1"></view>
	    </view>
	  </scroll-view>
	</view>
	<view title="已链接设备" class="list-title" style="height: 30%;">
	  <view class="text-title">
		<view class="iconfont icon-list"></view>已链接设备
	  </view>
	  <scroll-view scroll-y="true" class="scroll-Y childList" style="height: 150px;">
	    <view v-for="(item,zindex) in connectedBlueDeviceList" @click="cancelConnect(item)" :key="zindex" class="question" hover-class="hover">
	      <view class="text-item">{{ item.name }}</view>
	      <view class="line" v-if="zindex !== connectedBlueDeviceList.length - 1"></view>
	    </view>
	  </scroll-view>
	</view>
  </view>
</template>
<script>
import devicesConfig from '@/utils/devicesConfig'
	export default {
		data() {
			return {
				searchDisabled: false,
				// 搜索到的蓝牙设备列表
				blueDeviceList: [],
				// 搜索到的蓝牙设备列表
				connectedBlueDeviceList: [],
				// 蓝牙设备的id
				deviceId : "",
				// 硬件提供的服务id,开发中需要问硬件佬获取该id
				serviceId : '0000FFF0-0000-1000-8000-00805F9B34FB',
				characteristicId : '0000FFF1-0000-1000-8000-00805F9B34FB',
				// 监听到的内容
				message : '',
				messageHex : '', // 十六进制
				scanSetting:{
					useScanGun: uni.getStorageSync(devicesConfig.config.deviceUseScanGun)?uni.getStorageSync(devicesConfig.config.deviceUseScanGun):false,
				},
				testScanRes : "111",
				deviceType: ["扫码枪","打印机"],
				connectDeviceTypeIndex:0
			}
		},
		
		onLoad(option){
			this.getBlue()
			
		},
		onBackPress(){
			if(this.searchDisabled){
				this.stopDiscovery()
			}
			
		},
		methods: {
			toDeviceTest(){
				this.$tab.navigateTo("/pages/deviceTest")
			},
			setScan(event) {
			    this.scanSetting.useScanGun = !this.scanSetting.useScanGun
				uni.setStorageSync(devicesConfig.config.deviceUseScanGun, this.scanSetting.useScanGun)
				uni.showToast({
					title: '已'+(this.scanSetting.useScanGun?"启用":"关闭")+'扫码枪',
					icon: 'error'
				})
			},
			// 【1】初始化蓝牙
			initBlue() {
				let that = this
				that.searchDisabled = true
				console.log('初始化蓝牙..............')
				uni.openBluetoothAdapter({
					success(res) {
						console.log('初始化蓝牙成功')
						console.log(res)
						uni.onBLEConnectionStateChange(function (res) {
						  // 该方法回调中可以用于处理连接意外断开等异常情况
						  if(!res.connected){
							  let scan_d = uni.getStorageSync(devicesConfig.config.deviceScanGun)
							  let print_d = uni.getStorageSync(devicesConfig.config.devicePrint)
							  if(scan_d && res.deviceId == scan_d.deviceId){
								  scan_d.connected = false
								  uni.setStorageSync(devicesConfig.config.deviceScanGun,scan_d)
							  }
							  
							  if(print_d && res.deviceId == print_d.deviceId){
								  print_d.connected = false
								  uni.setStorageSync(devicesConfig.config.devicePrint,print_d)
							  }
						  }
						  
						})
						that.startFoundBluetooth()
					},
					fail(err) {
						that.searchDisabled = false
						console.log('初始化蓝牙失败')
						console.error(err)
						uni.showToast({
							title: '初始化蓝牙失败,请确保蓝牙已开启',
							icon: 'error'
						})
					}
				})
			},
			// 【2】开始搜寻附近设备
			startFoundBluetooth() {
				console.log('开始搜索蓝牙设备..............')
				let that = this
				that.blueDeviceList = []
				uni.startBluetoothDevicesDiscovery({
					success(res) {
						// 开启监听回调
						uni.onBluetoothDeviceFound(function(devices){
							that.found(devices)
						})
					},
					fail(err) {
						that.searchDisabled = false
						uni.showToast({
							title: '搜索蓝牙失败,请确保蓝牙已开启',
							icon: 'error'
						})
						console.error(err)
					}
				})
			},
			// 【3】找到新设备就触发该方法
			found(res) {
				console.log('搜索到蓝牙设备..............')
				console.log(res)
				if(res.devices[0].name){
					this.blueDeviceList.push(res.devices[0])
				}
				
			},
			
			// 【4】连接设备
			connect(data) {
				let that = this
				uni.showActionSheet({
					title: "设备类型",
					itemList:that.deviceType,
					success(res) {
						that.connectDeviceTypeIndex = res.tapIndex
						console.log('开始链接蓝牙设备..............'+data.name)
						this.deviceId = data.deviceId // 将获取到的设备ID存起来
						uni.createBLEConnection({
							deviceId: data.deviceId,
							success(res) {
								console.log('连接成功')
								console.log(res)
								that.connectedBlueDeviceList.push(data)
								// 停止搜索
								that.stopDiscovery()
								that.getServices(data.deviceId)
								uni.showToast({
									title: '连接成功'
								})
							},
							
							fail(err) {
								that.searchDisabled = false
								console.log('连接失败')
								console.error(err)
								uni.showToast({
									title: '蓝牙设备连接失败',
									icon: 'error'
								})
							}
						})
					}
				})
				
			},
			cancelConnect(data) {
				console.log('断开蓝牙设备..............'+data.name)
				let that = this
				uni.closeBLEConnection({
					deviceId: data.deviceId,
					success(res) {
						that.getBlue()
						uni.showToast({
							title: '断开成功'
						})
					},
					
					fail(err) {
						console.log('断开失败')
						console.error(err)
						uni.showToast({
							title: '蓝牙设备断开失败',
							icon: 'error'
						})
					}
				})
			},
			// 【5】停止搜索
			stopDiscovery() {
				this.searchDisabled = false
				console.log('停止搜索中。。。。。。。。。。')
				uni.stopBluetoothDevicesDiscovery({
					success(res) {
						console.log('停止搜索成功')
						console.log(res)
					},
					fail(err) {
						console.log('停止搜索失败')
						console.error(err)
						uni.showToast({
							title: '蓝牙设备连接失败',
							icon: 'error'
						})
					}
				})
			},
			// 【6】获取服务
			getServices(deviceId) {
				console.log("获取服务-------设备id:"+deviceId+"。。。。。。。。。。")
				let that = this
				// 如果是自动链接的话,uni.getBLEDeviceServices方法建议使用setTimeout延迟1秒后再执行
				setTimeout(function(){
					uni.getBLEDeviceServices({
						deviceId: deviceId,
						success(res) {
							console.log(res) // 可以在res里判断有没有硬件佬给你的服务
							for(let i=0;i<res.services.length;i++){
								that.getCharacteristics(deviceId,res.services[i].uuid)
							}
							
						},
						fail(err) {
							console.error(err)
							uni.showToast({
								title: '获取服务失败',
								icon: 'error'
							})
						}
					})
				},1000)
				
			},
			// 【7】获取特征值
			getCharacteristics(deviceId,serviceId) {
				let that = this
				console.log("获取特征值-----设备id:"+deviceId+",服务id:"+serviceId+"。。。。。。。。。。")
				// 如果是自动链接的话,uni.getBLEDeviceCharacteristics方法建议使用setTimeout延迟1秒后再执行
				uni.getBLEDeviceCharacteristics({
					deviceId: deviceId,
					serviceId: serviceId,
					success(res) {
						console.log(res) // 可以在此判断特征值是否支持读写等操作,特征值其实也需要提前向硬件佬索取的
						for(let i=0;i<res.characteristics.length;i++){
							let characteristics = res.characteristics[i]
							if(that.connectDeviceTypeIndex == 0 && characteristics.properties.notify){
								//扫码枪
								uni.setStorageSync(devicesConfig.config.deviceUseScanGun, true)
								that.scanSetting.useScanGun = true
								let d = {
									"connected": true,
									"deviceId":deviceId,
									"serviceId":serviceId,
									"characteristicId":characteristics.uuid
								}
								uni.setStorageSync(devicesConfig.config.deviceScanGun, d)
								//that.notify(deviceId,serviceId,characteristics.uuid)
								break
							}
							if(that.connectDeviceTypeIndex == 1 &&characteristics.properties.write){
								//打印机
								let d = {
									"connected": true,
									"deviceId":deviceId,
									"serviceId":serviceId,
									"characteristicId":characteristics.uuid
								}
								uni.setStorageSync(devicesConfig.config.devicePrint, d)
								break
							}
						}
						//that.notify()
					},
					
					fail(err) {
						console.error(err)
						uni.showToast({
							title: '获取特征值失败',
							icon: 'error'
						})
					}
				})
			},
			// 【8】开启消息监听
			notify(deviceId,serviceId,characteristicId) {
				let that = this
				console.log("开启监听----设备id:"+deviceId+",服务id:"+serviceId+",特征值id:"+characteristicId)
				uni.notifyBLECharacteristicValueChange({
					deviceId: deviceId, // 设备id
					serviceId: serviceId, // 监听指定的服务
					characteristicId: characteristicId, // 监听对应的特征值
					success(res) {
						console.log(res)
						//that.listenValueChange()
						// uni.showToast({
						// 	title: '已开启监听'
						// })
					},
					fail(err) {
						console.error(err)
						uni.showToast({
							title: '监听失败',
							icon: 'error'
						})
					}
				})
			},
			
			// 【9】监听消息变化
			listenValueChange() {
				let that = this
				console.log("监听到---设备id:"+that.deviceId+",服务id:"+that.serviceId+",特征值id:"+that.characteristicId+"----消息变化")
				uni.onBLECharacteristicValueChange(res => {
					console.log(res)
					let resHex = that.ab2hex(res.value)
					console.log(resHex)
					that.messageHex = resHex
					let result = that.hexCharCodeToStr(resHex)
					console.log(String(result))
					that.message = String(result)
				})
			},
			
			// ArrayBuffer转16进度字符串示例
			ab2hex(buffer){
				const hexArr = Array.prototype.map.call(
					new Uint8Array(buffer),
					function(bit) {
						return ('00' + bit.toString(16)).slice(-2)
					}
				)
				return hexArr.join('')
			},
			// 将16进制的内容转成我们看得懂的字符串内容
			hexCharCodeToStr(hexCharCodeStr) {
				let trimedStr = hexCharCodeStr.trim()
				let rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x" ? trimedStr.substr(2) : trimedStr
				let len = rawStr.length
				if (len % 2 !== 0) {
					alert("存在非法字符!")
					return ""
				}
				let curCharCode
				let resultStr = []
				for (let i = 0; i < len; i = i + 2) {
					curCharCode = parseInt(rawStr.substr(i, 2), 16)
					resultStr.push(String.fromCharCode(curCharCode))
				}
				return resultStr.join("").replace("\r",'')
			},
			
			getBlue(){
				let that = this
				uni.getConnectedBluetoothDevices({
				  success(res) {
				    console.log(res)
					that.connectedBlueDeviceList=res.devices
				  },
				  err(res){
					  
				  }
				})
			},
			
			
		}
	}
</script>
​
<style lang="scss" scoped>
  page {
    background-color: #f8f8f8;
  }

  .help-container {
    margin-bottom: 100rpx;
    padding: 30rpx;
  }

  .list-title {
    margin-bottom: 30rpx;
  }

  .childList {
    background: #ffffff;
    box-shadow: 0px 0px 10rpx rgba(193, 193, 193, 0.2);
    border-radius: 16rpx;
    margin-top: 10rpx;
  }

  .line {
    width: 100%;
    height: 1rpx;
    background-color: #F5F5F5;
  }

  .text-title {
    color: #303133;
    font-size: 32rpx;
    font-weight: bold;
    margin-left: 10rpx;

    .iconfont {
      font-size: 16px;
      margin-right: 10rpx;
    }
  }

  .text-item {
    font-size: 28rpx;
    padding: 10rpx;
  }

  .question {
    color: #606266;
    font-size: 28rpx;
  }
</style>

使用测试

<template>
  <view class="help-container" style="height: 100%">
	  <view class="text-title">
	  		<button type="primary"  @click="scanTest">
	  			<uni-icons type="checkbox" size="20" color="F000" style="margin-right: 5px;"></uni-icons>
	  			扫码测试
	  		</button>
	  </view>
	<view title="扫码测试结果" class="list-title" style="height: 30%;">
	  <view class="childList" scroll-y style="height: 200px;">
	    <view >
	      <view class="text-item">{{ message }}</view>
	    </view>
	  </view>
	</view>
	<view class="text-title">
			<button type="primary"  @click="printTest">
				<uni-icons type="checkbox" size="20" color="F000" style="margin-right: 5px;"></uni-icons>
				打印测试
			</button>
	</view>
  </view>
</template>
<script>
import devicesConfig from '@/utils/devicesConfig'
let tsc = require('@/utils/print/tsc.js')
	export default {
		data() {
			return {
				// 监听到的内容
				message : '',
				messageHex : '', // 十六进制
			}
		},
		
		methods: {
			scanTest(){
				let that = this
				uni.showToast({
					title: '请扫码',
					icon: 'success'
				})
				devicesConfig.onSacnGunValue(function(res){
					that.message = res
				})
				
			},
			printTest(){
				this.initBlue()
				let device = uni.getStorageSync(devicesConfig.config.devicePrint)
				if(!device){
					uni.showToast({
						title: '请链接打印设备',
						icon: 'error'
					})
					return
				}
				//标签模式
				let deviceId = device.deviceId;
				let serviceId = device.serviceId;
				let characteristicId = device.characteristicId;
				var command = tsc.jpPrinter.createNew()
				// console.log(command)
				command.setSize(40, 30)
				command.setGap(2)
				command.setCls()
				command.setText(100, 10, "TSS24.BF2", 2, 2, "Aromas")
				command.setText(40, 70, "TSS24.BF2", 1, 1, "测试打印")
				command.setText(230,120,"TSS24.BF2", 4, 4, "65")
				//command.setQR(50, 50, "L", 5, "A", "977767937@qq.com")
				command.setBar(20, 110,"128", 100, 1, 2,0, "977767937239")
				command.setPagePrint()
				devicesConfig.senBlData(deviceId, serviceId, characteristicId,command.getData())
			},
			
		}
	}
</script>
​
<style lang="scss" scoped>
  page {
    background-color: #f8f8f8;
  }

  .help-container {
    margin-bottom: 100rpx;
    padding: 30rpx;
  }

  .list-title {
    margin-bottom: 30rpx;
  }

  .childList {
    background: #ffffff;
    box-shadow: 0px 0px 10rpx rgba(193, 193, 193, 0.2);
    border-radius: 16rpx;
    margin-top: 10rpx;
  }

  .line {
    width: 100%;
    height: 1rpx;
    background-color: #F5F5F5;
  }

  .text-title {
    color: #303133;
    font-size: 32rpx;
    font-weight: bold;
    margin-left: 10rpx;

    .iconfont {
      font-size: 16px;
      margin-right: 10rpx;
    }
  }

  .text-item {
    font-size: 28rpx;
    padding: 24rpx;
  }

  .question {
    color: #606266;
    font-size: 28rpx;
  }
</style>

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值