蓝牙控制阀小程序
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

612 lines
15 KiB

3 years ago
  1. <template>
  2. <view >
  3. <view class="content" v-if="!connected">
  4. <u-icon class="logo" size="300" color="#808080" @click="scanDevice" name="scan"></u-icon>
  5. <view class="text-area">
  6. <text class="title">{{title}}</text>
  7. </view>
  8. </view>
  9. <view v-else style="margin-top: 300rpx;padding-left: 10px;">
  10. <u-row gutter="16">
  11. <u-col span="7">
  12. <view ><u-slider :height="20" @end="kiTovalue" v-model="valueki" step="10"></u-slider></view>
  13. </u-col>
  14. <u-col span="4" offset="1">
  15. <u-number-box v-model="value" :min="0" :max="10" @change="valueToki"/>
  16. </u-col>
  17. </u-row>
  18. <u-row gutter="16" >
  19. <u-col span="3">
  20. <u-button plain size="mini" type="success" @click="sendValue">确认发送</u-button>
  21. </u-col>
  22. <u-col span="3">
  23. <u-button plain size="mini" type="error" @click="closeBLEConnection">断开连接</u-button>
  24. </u-col>
  25. <u-col span="3">
  26. <u-button plain size="mini" type="warning" @click="writeBLECharacteristicValueString('getd')">获取开合度</u-button>
  27. </u-col>
  28. </u-row>
  29. </view>
  30. <u-modal @cancel='stopBluetoothDevicesDiscovery,show=false' title="提示" content="未搜索到设备,是否继续搜索" :show-cancel-button="true" @confirm="continueSearch()" v-model="searchLoad" >
  31. </u-modal>
  32. <u-toast ref="uToast" />
  33. </view>
  34. </template>
  35. <script>
  36. export default {
  37. data() {
  38. return {
  39. searchLoad:false,
  40. valueki:0,
  41. value: 0,
  42. title: '扫描设备二维码',
  43. equipment: {},
  44. connected: false,
  45. isStop: true,
  46. list: [],
  47. imei: '',
  48. item: [],
  49. idObject:{
  50. serviceId:'',
  51. writeId:'',
  52. notifyId:'',
  53. },
  54. timeSearch:null,
  55. };
  56. },
  57. onLoad() {
  58. this.onBLEConnectionStateChange();
  59. },
  60. onUnload:function(){
  61. if(this.timeSearch) {
  62. clearTimeout(this.timeSearch)
  63. this.timeSearch = null
  64. }
  65. },
  66. methods: {
  67. kiTovalue(){
  68. this.value=this.valueki/10
  69. },
  70. valueToki(){
  71. this.valueki=this.value*10
  72. },
  73. sendValue() {
  74. this.writeBLECharacteristicValueString('vals'+this.value)
  75. },
  76. continueSearch(){
  77. clearTimeout(this.timeSearch)
  78. this.timeSearch=setTimeout(() => {
  79. if(!this.searchLoad&&!this.connected){
  80. this.searchLoad=true
  81. }
  82. }, 8000);
  83. uni.showToast({
  84. title: '搜索设备中...',
  85. icon: 'loading',
  86. duration: 8000
  87. });
  88. this.startBluetoothDevicesDiscovery();
  89. },
  90. scanDevice() {
  91. uni.openBluetoothAdapter({
  92. success: e => {
  93. var that = this
  94. uni.scanCode({
  95. success: function(res) {
  96. let str1 = res.result.replace(';', '');
  97. that.imei = str1
  98. uni.showToast({
  99. title: '搜索设备中...',
  100. icon: 'loading',
  101. duration: 8000
  102. });
  103. that.timeSearch=setTimeout(() => {
  104. if(!that.searchLoad&&!that.connected){
  105. that.searchLoad=true
  106. }
  107. }, 8000);
  108. that.startBluetoothDevicesDiscovery();
  109. },
  110. fail: (res) => {
  111. /* this.$refs.uToast.show({
  112. title: '已取消',
  113. type: 'error',
  114. }) */
  115. }
  116. });
  117. this.getBluetoothAdapterState();
  118. },
  119. fail: e => {
  120. console.log(e)
  121. console.log('初始化蓝牙失败,错误码:' + (e.errCode || e.errMsg));
  122. if (e.errCode !== 0) {
  123. initTypes(e.errCode, e.errMsg);
  124. }
  125. }
  126. });
  127. },
  128. /**
  129. * 初始化蓝牙设备
  130. */
  131. openBluetoothAdapter() {
  132. uni.openBluetoothAdapter({
  133. success: e => {
  134. console.log('初始化蓝牙成功:' + e.errMsg);
  135. console.log(JSON.stringify(e));
  136. this.isStop = false;
  137. this.getBluetoothAdapterState();
  138. },
  139. fail: e => {
  140. console.log(e)
  141. console.log('初始化蓝牙失败,错误码:' + (e.errCode || e.errMsg));
  142. if (e.errCode !== 0) {
  143. initTypes(e.errCode, e.errMsg);
  144. }
  145. }
  146. });
  147. },
  148. /**
  149. * 开始搜索蓝牙设备
  150. */
  151. startBluetoothDevicesDiscovery() {
  152. uni.startBluetoothDevicesDiscovery({
  153. success: e => {
  154. this.onBluetoothDeviceFound();
  155. },
  156. fail: e => {
  157. console.log('搜索蓝牙设备失败,错误码:' + e.errCode);
  158. if (e.errCode !== 0) {
  159. initTypes(e.errCode);
  160. }
  161. }
  162. });
  163. },
  164. /**
  165. * 停止搜索蓝牙设备
  166. */
  167. stopBluetoothDevicesDiscovery(types) {
  168. clearTimeout(this.timeSearch)
  169. uni.stopBluetoothDevicesDiscovery({
  170. success: e => {
  171. console.log('停止搜索蓝牙设备:' + e.errMsg);
  172. },
  173. fail: e => {
  174. console.log('停止搜索蓝牙设备失败,错误码:' + e.errCode);
  175. if (e.errCode !== 0) {
  176. initTypes(e.errCode);
  177. }
  178. }
  179. });
  180. },
  181. /**
  182. * 发现外围设备
  183. */
  184. onBluetoothDeviceFound() {
  185. uni.onBluetoothDeviceFound(devices => {
  186. // this.$set(this.disabled, 3, false);
  187. this.getBluetoothDevices();
  188. });
  189. },
  190. /**
  191. * 获取在蓝牙模块生效期间所有已发现的蓝牙设备包括已经和本机处于连接状态的设备
  192. */
  193. getBluetoothDevices() {
  194. // this.list=[]
  195. let that = this
  196. let imei = that.imei
  197. uni.getBluetoothDevices({
  198. success: res => {
  199. res.devices.forEach(device => {
  200. if (!device.name.indexOf('tpsl04v-' + imei.substr(imei.length - 6))) {
  201. uni.hideToast();
  202. that.stopBluetoothDevicesDiscovery()
  203. that.equipment = device;
  204. that.createBLEConnection()
  205. return
  206. } else {
  207. //that.list.push(device)
  208. }
  209. })
  210. },
  211. fail: e => {
  212. console.log('获取蓝牙设备错误,错误码:' + e.errCode);
  213. if (e.errCode !== 0) {
  214. initTypes(e.errCode);
  215. }
  216. }
  217. });
  218. },
  219. /**
  220. * 获取本机蓝牙适配器状态
  221. */
  222. getBluetoothAdapterState() {
  223. console.log('--->');
  224. uni.getBluetoothAdapterState({
  225. success: res => {
  226. console.log(JSON.stringify(res));
  227. },
  228. fail: e => {
  229. console.log('获取本机蓝牙适配器状态失败,错误码:' + e.errCode);
  230. if (e.errCode !== 0) {
  231. initTypes(e.errCode);
  232. }
  233. }
  234. });
  235. },
  236. /**
  237. * 连接低功耗蓝牙
  238. */
  239. createBLEConnection() {
  240. let deviceId = this.equipment.deviceId;
  241. uni.showToast({
  242. title: '连接蓝牙...',
  243. icon: 'loading',
  244. duration: 99999
  245. });
  246. if (!this.connected) {
  247. uni.createBLEConnection({
  248. deviceId,
  249. success: res => {
  250. this.connected = true;
  251. clearTimeout(this.timeSearch)
  252. console.log(res);
  253. console.log('连接蓝牙成功:' + res.errMsg);
  254. // 连接设备后断开搜索 并且不能搜索设备
  255. uni.hideToast();
  256. uni.showToast({
  257. title: '连接成功',
  258. icon: 'success',
  259. duration: 2000
  260. });
  261. // this.closeBLEConnection()
  262. this.getBLEDeviceServices()
  263. },
  264. fail: e => {
  265. uni.hideToast();
  266. /* uni.showToast({
  267. title: '连接错误',
  268. icon: 'error',
  269. duration: 2000
  270. }); */
  271. console.log('连接低功耗蓝牙失败,错误码:' + e.errCode);
  272. if (e.errCode !== 0) {
  273. initTypes(e.errCode);
  274. }
  275. }
  276. });
  277. }
  278. },
  279. /**
  280. * 断开与低功耗蓝牙设备的连接
  281. */
  282. closeBLEConnection() {
  283. let deviceId = this.equipment.deviceId;
  284. this.connected = false
  285. uni.closeBLEConnection({
  286. deviceId,
  287. success: res => {
  288. console.log(res);
  289. console.log('断开低功耗蓝牙成功:' + res.errMsg);
  290. this.equipment = {};
  291. this.idObject={}
  292. },
  293. fail: e => {
  294. console.log('断开低功耗蓝牙成功,错误码:' + e.errCode);
  295. if (e.errCode !== 0) {
  296. initTypes(e.errCode);
  297. }
  298. }
  299. });
  300. },
  301. /**
  302. * 获取所有服务
  303. */
  304. getBLEDeviceServices() {
  305. let deviceId = this.equipment.deviceId;
  306. uni.getBLEDeviceServices({
  307. deviceId,
  308. success: res => {
  309. this.list = res.services;
  310. this.idObject = {};
  311. if (this.list.length <= 0) {
  312. toast('获取服务失败,请重试!');
  313. return;
  314. }
  315. this.list.forEach(item => {
  316. this.getBLEDeviceCharacteristics(item)
  317. })
  318. this.maskShow = true;
  319. },
  320. fail: e => {
  321. console.log('获取设备服务失败,错误码:' + e.errCode);
  322. if (e.errCode !== 0) {
  323. initTypes(e.errCode);
  324. }
  325. }
  326. });
  327. },
  328. /**
  329. * 获取某个服务下的所有特征值
  330. */
  331. getBLEDeviceCharacteristics(item) {
  332. let deviceId = this.equipment.deviceId;
  333. let serviceId = item.uuid;
  334. let that = this
  335. let write=''
  336. let notify=''
  337. uni.getBLEDeviceCharacteristics({
  338. deviceId,
  339. serviceId,
  340. success: res => {
  341. this.list = res.characteristics;
  342. if (this.list.length <= 0) {
  343. // toast('获取特征值失败,请重试!');
  344. return;
  345. }
  346. this.list.forEach(characteristic => {
  347. /* if (characteristic.properties.read) {
  348. uni.readBLECharacteristicValue({
  349. deviceId,
  350. serviceId,
  351. characteristicId: characteristic.uuid,
  352. success: res => {
  353. console.log('读取设备数据值成功read');
  354. console.log(JSON.stringify(res));
  355. },
  356. fail(e) {
  357. console.log('读取设备数据值失败,错误码:' + e.errCode);
  358. if (e.errCode !== 0) {
  359. initTypes(e.errCode);
  360. }
  361. }
  362. });
  363. } */
  364. if (characteristic.properties.write ) {
  365. write=characteristic.uuid
  366. }
  367. if (characteristic.properties.notify || characteristic.properties.indicate) {
  368. notify=characteristic.uuid
  369. }
  370. })
  371. if(write!==''&&notify!==''){
  372. this.idObject={
  373. serviceId:serviceId,
  374. writeId:write,
  375. notifyId:notify,
  376. }
  377. console.log(this.idObject)
  378. uni.notifyBLECharacteristicValueChange({
  379. state: true, // 启用 notify 功能
  380. deviceId,
  381. serviceId,
  382. characteristicId: this.idObject.notifyId,
  383. success(res) {
  384. that.onBLECharacteristicValueChange()
  385. setTimeout(() => {
  386. that.writeBLECharacteristicValueString('getd')
  387. }, 1000);
  388. },
  389. fail(e) {
  390. console.log('失败,错误码:' + e.errCode);
  391. if (e.errCode !== 0) {
  392. initTypes(e.errCode);
  393. }
  394. }
  395. });
  396. }
  397. },
  398. fail: e => {
  399. console.log('获取特征值失败,错误码:' + e.errCode);
  400. if (e.errCode !== 0) {
  401. initTypes(e.errCode);
  402. }
  403. }
  404. });
  405. },
  406. /**
  407. * 监听低功耗蓝牙连接状态的改变事件包括开发者主动连接或断开连接设备丢失连接异常断开等等
  408. */
  409. onBLEConnectionStateChange() {
  410. uni.onBLEConnectionStateChange(res => {
  411. // 该方法回调中可以用于处理连接意外断开等异常情况
  412. console.log(`蓝牙连接状态 -------------------------->`);
  413. console.log(JSON.stringify(res));
  414. if (!res.connected) {
  415. this.connected=false
  416. console.log('断开低功耗蓝牙成功:');
  417. this.equipment = {};
  418. this.idObject={}
  419. toast('已经断开当前蓝牙连接');
  420. }
  421. });
  422. },
  423. writeBLECharacteristicValueString(str) {
  424. console.log(str)
  425. let deviceId = this.equipment.deviceId;
  426. let serviceId = this.idObject.serviceId;
  427. let characteristicId = this.idObject.writeId;
  428. // 向蓝牙设备发送16进制数据
  429. let buffer = new ArrayBuffer(str.length);
  430. let dataView = new DataView(buffer);
  431. for (let i in str) {
  432. dataView.setUint8(i, str[i].charCodeAt() | 0);
  433. }
  434. uni.writeBLECharacteristicValue({
  435. deviceId,
  436. serviceId,
  437. // 这里的 characteristicId 需要在 getBLEDeviceCharacteristics 接口中获取
  438. characteristicId,
  439. value: buffer,
  440. success: function(res) {
  441. console.log(res);
  442. },
  443. fail: function(res) {
  444. console.log(res);
  445. }
  446. })
  447. },
  448. // ArrayBuffer转16进度字符串示例
  449. ab2hex(buffer) {
  450. const hexArr = Array.prototype.map.call(
  451. new Uint8Array(buffer),
  452. function(bit) {
  453. return ('00' + bit.toString(16)).slice(-2)
  454. }
  455. )
  456. return hexArr.join('')
  457. },
  458. /**
  459. * 监听低功耗蓝牙设备的特征值变化事件必须先启用 notifyBLECharacteristicValueChange 接口才能接收到设备推送的 notification
  460. */
  461. onBLECharacteristicValueChange() {
  462. // 必须在这里的回调才能获取
  463. let that = this
  464. uni.onBLECharacteristicValueChange(function(res) {
  465. console.log(`characteristic ${res.characteristicId} has changed, now is ${res.value}`)
  466. // console.log(that.ab2hex(res.value))
  467. console.log(res)
  468. let hex = new Uint8Array(res.value);
  469. hex = String.fromCharCode.apply(null, hex);
  470. that.value= parseInt(hex.replace('valve status: ', ''));
  471. that.valueki=parseInt(hex.replace('valve status: ', ''))*10;
  472. console.log(that.value)
  473. })
  474. },
  475. /**
  476. * 断开蓝牙模块
  477. */
  478. closeBluetoothAdapter(OBJECT) {
  479. uni.closeBluetoothAdapter({
  480. success: res => {
  481. console.log('断开蓝牙模块成功');
  482. this.equipment = {};
  483. this.idObject={}
  484. toast('断开蓝牙模块');
  485. }
  486. });
  487. }
  488. }
  489. };
  490. /**
  491. * 判断初始化蓝牙状态
  492. */
  493. function initTypes(code, errMsg) {
  494. switch (code) {
  495. case 10000:
  496. console.log('未初始化蓝牙适配器');
  497. break;
  498. case 10001:
  499. toast('未检测到蓝牙,请打开蓝牙重试!');
  500. break;
  501. case 10002:
  502. console.log('没有找到指定设备');
  503. break;
  504. case 10003:
  505. console.log('连接失败');
  506. break;
  507. case 10004:
  508. console.log('没有找到指定服务');
  509. break;
  510. case 10005:
  511. console.log('没有找到指定特征值');
  512. break;
  513. case 10006:
  514. console.log('当前连接已断开');
  515. break;
  516. case 10007:
  517. console.log('当前特征值不支持此操作');
  518. break;
  519. case 10008:
  520. console.log('其余所有系统上报的异常');
  521. break;
  522. case 10009:
  523. console.log('Android 系统特有,系统版本低于 4.3 不支持 BLE');
  524. break;
  525. default:
  526. console.log(errMsg);
  527. }
  528. }
  529. /**
  530. * 弹出框封装
  531. */
  532. function toast(content, showCancel = false) {
  533. uni.showModal({
  534. title: '提示',
  535. content,
  536. showCancel
  537. });
  538. }
  539. </script>
  540. <style>
  541. page {
  542. height: 100%;
  543. width: 100%;
  544. }
  545. .content {
  546. display: flex;
  547. width: 100%;
  548. height: 100%;
  549. flex-direction: column;
  550. align-items: center;
  551. justify-content: center;
  552. }
  553. .logo {
  554. margin: 0;
  555. padding: 0;
  556. margin-top: 300rpx;
  557. }
  558. .text-area {
  559. display: flex;
  560. justify-content: center;
  561. }
  562. .title {
  563. font-size: 36rpx;
  564. color: #8f8f94;
  565. }
  566. .u-row {
  567. margin: 40rpx 0;
  568. }
  569. </style>