houjie 3 年之前
父节点
当前提交
bd47e779f5
共有 3 个文件被更改,包括 602 次插入1 次删除
  1. 267 0
      Lib/BleActivateDevice.js
  2. 2 1
      pages.json
  3. 333 0
      pages/demo/ble/activateDevice/activateDevice.vue

+ 267 - 0
Lib/BleActivateDevice.js

@@ -0,0 +1,267 @@
+const SERVER_UUID = "0000AB00-0000-1000-8000-00805F9B34FB";
+
+const DISSCONNECTED = 0
+const CONNECTING = 1
+const CONNECTED = 2
+const DISSCONNECTING = 3
+
+let callback = null
+
+let deviceId = null
+
+let writeCharacteristicId = null
+
+let configTimeout = null
+
+let state = DISSCONNECTED
+
+/**
+ * 连接ble设备
+ * @param {string} mac 设备mac地址
+ * @param {function} cb 连接的回调
+ */
+function connectBle(mac, cb) {
+    if (state === DISSCONNECTED) {
+        deviceId = mac
+        callback = cb
+        state = CONNECTING
+        uni.createBLEConnection({
+            deviceId,
+            success(res) {
+                console.log('ble连接成功')
+                state = CONNECTED
+                    //获取服务
+                getBleDeviceServices()
+            },
+            fail(err) {
+                console.log(`ble连接失败 code :${err.errCode}`)
+                state = DISSCONNECTED
+                callback(err)
+            }
+        })
+    }
+}
+
+/**
+ * 获取ble的特征
+ * @param {string} deviceId 设备mac地址
+ */
+function getBleDeviceServices() {
+    setTimeout(() => {
+        uni.getBLEDeviceServices({
+            deviceId,
+            success(res) {
+                console.log('ble获取服务成功')
+                    //let SERVER_UUID = res.services[0].uuid
+                    //getBLEDeviceCharacteristics()
+
+                for (let service of res.services) {
+                    console.log(service)
+                    if (SERVER_UUID.toUpperCase() == service.uuid.toUpperCase()) {
+                        //获取特征
+                        getBLEDeviceCharacteristics(service.uuid)
+                        return
+                    }
+                }
+            },
+            fail(err) {
+                console.log(`ble获取服务失败 code :${err.errCode}`)
+                closeBLEConnection()
+                callback(err)
+            }
+        })
+    }, 200)
+}
+
+/**
+ * 获取特征
+ */
+function getBLEDeviceCharacteristics(serverId) {
+    //setTimeout(() => {
+    uni.getBLEDeviceCharacteristics({
+            deviceId,
+            serviceId: serverId,
+            success(res) {
+                console.log(`ble获取特征成功`);
+                for (let characteristic of res.characteristics) {
+                    console.log(characteristic.properties);
+                    // console.log(characteristic.properties.notify);
+                    if (characteristic.properties.notify) {
+                        console.warn(`NOTIFY ${characteristic.uuid}`);
+                        notifyBLECharacteristicValueChange(characteristic.uuid)
+                    } else {
+                        writeCharacteristicId = characteristic.uuid
+                        startAcivateDevice(characteristic.uuid)
+                    }
+                }
+            },
+            fail(err) {
+                console.log(`获取特征失败 code :${err.errCode}`);
+                //console.log(err);
+                //closeBLEConnection()
+                callback(err)
+
+            }
+        })
+        //}, 1000)
+}
+
+
+/**
+ * 监听设备发送过来的数据
+ * @param {string} characteristicId 特征的uuid
+ */
+function notifyBLECharacteristicValueChange(nofifyCharacteristicId) {
+    setTimeout(() => {
+        //设置一个超时配网的时间
+        configTimeout = setTimeout(() => {
+            closeBLEConnection()
+            if (callback) {
+                callback({ errCode: -100 })
+            }
+        }, 60 * 1000)
+
+        uni.notifyBLECharacteristicValueChange({
+            state: true,
+            deviceId,
+            serviceId: SERVER_UUID,
+            characteristicId: nofifyCharacteristicId,
+        })
+
+        //监听设备发送过来的数据
+        uni.onBLECharacteristicValueChange(function(res) {
+            let buf = Buffer.from(res.value);
+            let receiveStr = buf.toString()
+            console.log(`收到数据:${receiveStr}`);
+            if (receiveStr.indexOf('ID0:') >= 0) {
+                //获取id1
+                let buffer = Buffer.from('getID1:').buffer
+                setTimeout(() => { sendCongifCmd(writeCharacteristicId, buffer) }, 500)
+            } else if (receiveStr.indexOf('ID1:') >= 0) {
+                let data = "E2E7A1A90C89E81A62BACA5DD386F2A8ADD1192C5F3400F4D4D5B2FBF32B3B60B1209598A82C679D30C4604726D30DC09A31E241B716258CC2887FE6395D6181B9D55476A4F8F4C3D11A850BE4FE10BC42F126D74053F7A1B9FB44F76FE79B9903F48B0C5FB38746E6CDCA35121C785F46FC66460C56A8382D7656FBE4D0DB3A363149E0BCF94C05C69D2120241A98C83C8D05BA409C6F981F3491226AFBB7ACF987BD8E72E7B53AE77CFFE51E8CDE6F787BA7641B4F601BC5744FB99C50E7AC"
+                setTimeout(() => { sendLongData(writeCharacteristicId, data) }, 500)
+            }
+        })
+
+    }, 200)
+}
+
+function startAcivateDevice(characteristicId) {
+    setTimeout(() => {
+
+        //获取id0
+        let buf = Buffer.from('getID0:').buffer
+        setTimeout(() => { sendCongifCmd(characteristicId, buf) }, 500)
+    }, 800)
+}
+
+/**
+ * 发送数据给设备
+ * @param {string} characteristicId 特征uuid
+ * @param {ArrayBuffer} buffer 发送的数据
+ */
+function sendCongifCmd(characteristicId, buffer) {
+    let count = Math.ceil(buffer.byteLength / 20)
+        //拆分成的ArrayBuffer数组,每个ArrayBuffer不超过20个字节
+    let buffers = []
+    for (var i = 0; i < count; i++) {
+        let size = Math.min(20, buffer.byteLength - i * 20)
+        let buf = buffer.slice(i * 20, size + i * 20)
+        buffers.push(buf)
+    }
+
+    //返回的是一个promise对象
+    function sendData(buffer) {
+        return uni.writeBLECharacteristicValue({
+            deviceId,
+            serviceId: SERVER_UUID,
+            characteristicId,
+            value: buffer,
+        })
+    };
+
+    (async() => {
+        for (let b of buffers) {
+            let res = await sendData(b)
+                //console.log(res);
+            console.log(`发送的数据为:${Buffer.from(b)} errorCode: ${res[res.length - 1].errCode}`);
+        }
+        // let promises = buffers.map((b) => sendData(b))
+        // let res  = await Promise.all(promises)
+    })();
+}
+
+
+function sendLongData(characteristicId, dataStr) {
+    let buffer = Buffer.from(dataStr)
+        //每个包20个字节    间隔200毫秒  
+        //0x54 + 0x3a +pack_count(总数)+pack_index(包号)+data_len+data(长度最长15位)
+    let count = Math.ceil(buffer.byteLength / 15)
+    let buffers = []
+    for (var i = 0; i < count; i++) {
+        let size = Math.min(15, buffer.byteLength - i * 15)
+        let buf = buffer.slice(i * 15, size + i * 15)
+        let pkgBuf = Buffer.allocUnsafe(size + 5)
+        pkgBuf.writeUInt8(0x54, 0) //固定0x54
+        pkgBuf.writeUInt8(0x3a, 1) //固定0x54
+        pkgBuf.writeUInt8(count, 2) //pack_count(包总数)
+        pkgBuf.writeUInt8(i, 3) //pack_index(包号)
+        pkgBuf.writeUInt8(size, 4) //data_len
+        pkgBuf.write(buf.toString(), 5) //data
+            //console.log(pkgBuf);
+        buffers.push(pkgBuf.buffer)
+    }
+
+    // for (let b of buffers) {
+    //     console.warn(Buffer.from(b));
+    // }
+
+
+    //返回的是一个promise对象
+    function sendData(buffer) {
+        return uni.writeBLECharacteristicValue({
+            deviceId,
+            serviceId: SERVER_UUID,
+            characteristicId,
+            value: buffer,
+        })
+    };
+
+    (async() => {
+        for (let b of buffers) {
+            let res = await sendData(b)
+                //console.log(res);
+            console.log(Buffer.from(b));
+            console.log(`errorCode: ${res[res.length - 1].errCode}`);
+            await sleep(200)
+        }
+    })();
+
+}
+
+function sleep(time) {
+    return new Promise((resolve) => setTimeout(resolve, time))
+}
+
+/**
+ * 关闭ble连接
+ */
+function closeBLEConnection() {
+    if (state !== DISSCONNECTED) {
+        state = DISSCONNECTING
+        uni.closeBLEConnection({
+            deviceId,
+            success(res) {
+                state = DISSCONNECTED
+                console.log('ble关闭连接成功');
+            },
+            fail(err) {
+                console.log(`ble关闭连接失败  code :${err.errCode}`);
+            }
+        })
+        clearTimeout(configTimeout)
+    }
+}
+
+
+module.exports = { closeBLEConnection, connectBle, sendLongData };

+ 2 - 1
pages.json

@@ -6,6 +6,7 @@
                 "navigationBarTitleText": "猫王妙播"
             }
         },
+
         {
             "path": "pages/play/play",
             "style": {
@@ -116,7 +117,7 @@
         "list": [{
                 "name": "设备详情",
                 "path": "pages/mine/device/deviceDetail/deviceDetail",
-				"query": "model=MW-M3&name=猫王·MW-M3&uuid=89860474192070498495"
+                "query": "model=MW-M3&name=猫王·MW-M3&uuid=89860474192070498495"
             },
             {
                 "name": "管理设备",

+ 333 - 0
pages/demo/ble/activateDevice/activateDevice.vue

@@ -0,0 +1,333 @@
+<template>
+  <view class="content">
+    <image class="img" :src="productUrl" mode="aspectFit" />
+    <view class="hint mt30">
+      <image
+        class="img1"
+        :src="stateIcon"
+        mode="aspectFit"
+        v-if="showStateIcon"
+      />
+      <text class="black-mudium-24rpx">{{ hintText }}</text>
+    </view>
+    <view class="hint mt30" v-if="showLink">
+      <image
+        class="img2"
+        src="../../../static/common/ic_smile.svg"
+        mode="aspectFit"
+      />
+      <text class="primary-normal-24rpx">{{ productHint }}</text>
+    </view>
+    <view class="bottom content">
+      <button
+        :loading="isloading"
+        :class="buttonClass"
+        :hover-class="buttonHoverClass"
+        @click="btnClick"
+      >
+        {{ buttonText }}
+      </button>
+    </view>
+    <bt-alert v-if="showAlert" @close="btAlertClose" />
+  </view>
+</template>
+
+<script>
+import BleConfigNet from "../../../../Lib/BleActivateDevice.js";
+import BtAlert from "../../../../components/btAlert/btAlert.vue";
+export default {
+  components: {BtAlert},
+  data: () => ({
+    /** 产品型号*/
+    model: "MW-M3",
+    /** 产品名称 */
+    productName: "",
+    /** 是否显示界面上提示的icon*/
+    showStateIcon: true,
+    /** 界面上提示icon的图片*/
+    stateIcon: "../../../static/common/ic_green_success.svg",
+    /** 界面上提示文字内容*/
+    hintText: "正在扫描设备,请保持设备开机状态",
+    /** 是否显示如何连接设备的提示*/
+    showLink: true,
+    /** 按钮的name*/
+    btnName: "scan",
+    /** 是否显示蓝牙开关弹窗 */
+    showAlert: false,
+    /** 超时扫描的定时器*/
+    stopScanTimer: null,
+    /** 要连接的设备*/
+    device: null,
+  }),
+  computed: {
+    buttonText() {
+      if (this.btnName == "scaning") {
+        return "正在搜索设备...";
+      } else if (this.btnName == "rescan") {
+        return "重新搜索";
+      } else if (this.btnName == "connect") {
+        return "连接";
+      } else if (this.btnName == "reconnect") {
+        return "重新连接";
+      } else if (this.btnName == "finish") {
+        return "完成";
+      } else if (this.btnName == "connecting") {
+        return "正在连接...";
+      } else if (this.btnName == "scan") {
+        return "搜索";
+      } else {
+        return this.btnName;
+      }
+    },
+    buttonClass() {
+      if (this.btnName == "scaning" || this.btnName == "connecting") {
+        return "btn disabled";
+      } else if (this.btnName == "finish") {
+        return "btn btn-primary";
+      } else {
+        return "btn";
+      }
+    },
+    buttonHoverClass() {
+      if (this.btnName == "finish") {
+        return "btn-primary-hover";
+      } else {
+        return "btn-hover";
+      }
+    },
+    productUrl() {
+      return `https://airsmart-photo1.oss-cn-shanghai.aliyuncs.com/wx/productModel/3X/${this.model}.png`;
+    },
+    productHint() {
+      return `如何连接${this.productName}设备?`;
+    },
+    isloading() {
+      return this.btnName == "scaning" || this.btnName == "connecting";
+    },
+  },
+  methods: {
+    
+    //关闭弹窗
+    btAlertClose() {
+      this.showAlert = false;
+    },
+    /**
+     * 按钮点击事件
+     */
+    btnClick() {
+      if (this.btnName == "rescan" || this.btnName == "scan") {
+        //搜索
+        this.init();
+      } else if (this.btnName == "connect" || this.btnName == "reconnect") {
+        //连接
+        this.btnName = "connecting";
+        if (this.device) {
+          BleConfigNet.connectBle(this.device.mac, this.bleConfigNetCallback);
+        } else {
+          that.hintText = "沒有搜索到设备";
+          that.btnName = "rescan";
+          that.showStateIcon = false;
+          that.showLink = true;
+        }
+      }
+    },
+    //初始化蓝牙
+    init() {
+      const that = this;
+      uni.openBluetoothAdapter({
+        success: (res) => {
+          console.log(`初始化蓝牙模块成功 ${res}`);
+          that.startBleScan();
+        },
+        fail: (error) => {
+          if (error.errCode === 10001) {
+            //在用户蓝牙开关未开启或者手机不支持蓝牙功能的情况下
+            this.showAlert = true;
+
+            //蓝牙状态监听
+            that.lisenBtState();
+          } else {
+            //其他错误
+            uni.showToast({
+              title: `查看手机蓝牙是否打开`,
+              icon: "none",
+            });
+            console.log(error);
+          }
+        },
+      });
+    },
+    //蓝牙开关的监听
+    lisenBtState() {
+      //监听蓝牙是否打开
+      let that = this;
+      uni.onBluetoothAdapterStateChange(function (res) {
+        console.log(
+          `onBluetoothAdapterStateChange available = ${res.available}`
+        );
+        //console.log(res);
+        if (res.available) {
+          //蓝牙已打开
+          //开始扫描设备
+          that.startBleScan();
+        }
+      });
+    },
+    //扫描蓝牙设备
+    startBleScan() {
+      this.showAlert = false;
+      this.btnName = "scaning";
+      this.hintText = "正在扫描设备,请保持设备开机状态";
+      this.showStateIcon = false;
+      this.showLink = true;
+      this.device = null;
+      const that = this;
+      clearTimeout(this.stopScanTimer);
+      //开始扫描
+      uni.startBluetoothDevicesDiscovery({
+        success: (res) => {
+          //设置扫描的超时时间
+          that.setStopScanTimeout();
+          //扫描到的设备的回调
+          uni.onBluetoothDeviceFound(function (res) {
+            let devices = res.devices;
+            for (var i = 0; i < devices.length; i++) {
+              console.log(devices[i]);
+              if (
+                "MW_BLE_M3" == devices[i].name ||
+                "MW_BLE_M4" == devices[i].name
+              ) {
+                that.stopScan();
+                that.device = {
+                  name: devices[i].name,
+                  mac: devices[i].deviceId,
+                };
+                that.showStateIcon = false;
+                that.hintText = `以搜索到${that.productName}设备`;
+                that.showLink = true;
+                that.btnName = "connect";
+                break;
+              }
+            }
+          });
+        },
+        fail: (err) => {
+          console.warn(err);
+          that.hintText = "搜索设备失败";
+          that.btnName = "rescan";
+          that.showStateIcon = false;
+          that.showLink = true;
+        },
+      });
+    },
+    //设置超时扫描的定时器
+    setStopScanTimeout() {
+      const that = this;
+      //N秒之后停止扫描
+      that.stopScanTimer = setTimeout(() => {
+        //停止扫描
+        uni.stopBluetoothDevicesDiscovery({
+          complete() {
+            if (that.device == null) {
+              that.hintText = "沒有搜索到设备";
+              that.btnName = "rescan";
+              that.showStateIcon = false;
+              that.showLink = true;
+            }
+          },
+        });
+      }, 30 * 1000);
+    },
+    //停止蓝牙的扫描
+    stopScan() {
+      uni.stopBluetoothDevicesDiscovery({});
+      clearTimeout(this.stopScanTimer);
+    },
+    //ble配网的回调
+    bleConfigNetCallback(res) {
+      console.log(res);
+      if (res.errCode == 0) {
+        if (this.device) {
+          this.device.uuid = res.iccid;
+        }
+        //配网成功
+        this.hintText = "连接成功";
+        this.btnName = "finish";
+        this.showStateIcon = true;
+        this.showLink = false;
+        this.stateIcon = "../../../static/common/ic_green_success.svg";
+      } else {
+        this.hintText = "连接失败,请确保设备开机状态";
+        this.btnName = "reconnect";
+        this.showStateIcon = true;
+        this.showLink = true;
+        this.stateIcon = "../../../static/common/ic_red_fail.svg";
+      }
+    },
+  },
+
+  // 页面周期函数--监听页面加载
+  onLoad(options) {
+    this.model = options.model;
+    this.productName = options.name;
+    if (this.productName && this.productName.length > 0) {
+      uni.setNavigationBarTitle({
+        title: `连接${this.productName}`,
+      });
+    }
+    this.init();
+  },
+
+  // 页面周期函数--监听页面卸载
+  onUnload() {
+    this.stopScan();
+    BleConfigNet.closeBLEConnection();
+  },
+};
+</script>
+
+<style>
+.content {
+  display: flex;
+  flex-direction: column;
+  justify-content: flex-start;
+  align-items: center;
+}
+.img {
+  margin-top: 19.79vh;
+  width: 37.33vw;
+  height: 34.4vw;
+}
+.img1 {
+  width: 32rpx;
+  height: 32rpx;
+  margin-right: 8rpx;
+}
+.img2 {
+  width: 24rpx;
+  height: 22rpx;
+  margin-right: 8rpx;
+}
+.hint {
+  height: 40rpx;
+  display: flex;
+  flex-direction: row;
+  justify-content: center;
+  align-items: center;
+}
+.mt30 {
+  margin-top: 30rpx;
+}
+
+.disabled {
+  background-color: #999999;
+  cursor: not-allowed;
+}
+.bottom {
+  position: fixed;
+  top: 73.8vh;
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+</style>