收藏
回答

wx.onBLECharacteristicValueChange IOS能回调,安卓收不到

框架类型 问题类型 API/组件名称 终端类型 微信版本 基础库版本
小程序 Bug wx.onBLECharacteristicValueChange 微信安卓客户端 8.0.50 3.4.4
// index.js
Page({
  data: {
    CustomBar: getApp().globalData.CustomBar,
    deviceList: [],
    deviceId: null,
    serviceId: null,
    characteristicId: null,
    characteristicId2: null,
    deviceIndex: 0,
    wifiName: null,
    wifiPass: null,
    isShow: false,
    searchWifiShow: true,
    wifiList: [],
    isShowDialog: false,
    isShowPassDialog: false,
    wifiIndex:-1,
  },
  onLoad: function () {
   
  },
  onReady: function () {

  },
  onShow: function () {
    wx.onGetWifiList((result) => {
      console.log(result);
      this.setData({
        wifiList:result.wifiList
      })
    })
  },
  changeWifiName(e) {
    var name = e.detail.value;
    console.log("wifi-name > " + name);
    this.setData({
      wifiName: name
    })
  },
  changeWifiPass(e) {
    var pass = e.detail.value;
    console.log("wifi-pass > " + pass);
    this.setData({
      wifiPass: pass
    })
  },
  initBluetooth() {
    let that = this;
    this.clearData();
    that.setData({
      searchWifiShow:false
    })
    wx.showLoading({
      title:'搜索中,请稍候...',
      mask:true
    });
    wx.openBluetoothAdapter({
      success: (res) => {
        console.log('初始化蓝牙模块成功:'JSON.stringify(res));
        this.findBluetoothList();
      },
      fail: (err) => {
        console.log('初始化蓝牙模块失败:'JSON.stringify(err));
        wx.hideLoading();
        wx.showModal({
          title: '提示',
          content: '蓝牙未开启!请前往系统设置打开蓝牙~',
          showCancel: false// 是否显示取消按钮
          confirmText: '我知道了'// 确认按钮的文本
          confirmColor: '#3CC51F'// 确认按钮的文字颜色
          success (res) {
            if (res.confirm) {
              console.log('用户点击确定')
              that.setData({
                searchWifiShow:true
              })
            }
          }
        })
      }
    });
  },
  findBluetoothList() {
    let that = this;
    wx.startBluetoothDevicesDiscovery({
      allowDuplicatesKey: false,
      success: (res) => {
        console.log('开始扫描附近蓝牙设备:'JSON.stringify(res));
        this.onBluetoothDeviceFound();
      },
      fail: (err) => {
        that.setData({
          searchWifiShow:true
        })
        wx.hideLoading();
        console.log('扫描附近蓝牙设备失败:'JSON.stringify(err));
        wx.showModal({
          title: '提示',
          content: '未搜索到相关配网设备~',
          confirmText: '再试一次'// 确认按钮的文本
          success (res) {
            if (res.confirm) {
              console.log('用户点击重试启动配网')
              that.initBluetooth()
            }
          }
        })
      }
    });
  },
  onBluetoothDeviceFound() {
    let that = this;
    wx.onBluetoothDeviceFound((res) => {
      var newDevices = res.devices;
      newDevices.isConn = false;
      if (newDevices[0].name == "MX") {
        console.log('找到蓝牙设备:'JSON.stringify(newDevices));
        newDevices[0].shortId = newDevices[0].deviceId.split("-")[0]
        var isAdd = false;
        for (let j = 0; j < that.data.deviceList.length; j++) {
          if (that.data.deviceList[j].deviceId == newDevices[0].deviceId) {
            isAdd = true;
          }
        }
        if(!isAdd){
          this.setData({
            deviceList: this.data.deviceList.concat(newDevices)
          });
        }
      }
    });
    setTimeout(() => {
      wx.hideLoading();
       // 找到要搜索的设备后,及时停止扫描
       wx.stopBluetoothDevicesDiscovery();
       if(this.data.deviceList.length == 0){
        that.setData({
          searchWifiShow:true
        })
        wx.showModal({
          title: '提示',
          content: '未搜索到相关配网设备~',
          confirmText: '再试一次'// 确认按钮的文本
          success (res) {
            if (res.confirm) {
              console.log('用户点击重试启动配网')
              that.initBluetooth()
            }
          }
        })
       }else{
          that.setData({
            searchWifiShow:false
          })
       }
    }, 5000);
  },
  connBluetooth(e) {
    let that = this;
    // 当前已连接设备,再点击列表item连接时,断开当前连接的并清除信息
    if (that.data.deviceId != null && that.data.serviceId != null) {
      wx.closeBLEConnection({
        deviceId: that.data.deviceId,
      })
      that.setData({
        deviceList: that.data.deviceList,
        deviceId: null,
        serviceId: null,
        characteristicId: null,
        characteristicId2: null,
      });
    }

    var name = e.currentTarget.dataset.name;
    var deviceId = e.currentTarget.dataset.deviceId;
    var index = e.currentTarget.dataset.index;
    that.data.deviceIndex = index;
    var isConn = that.data.deviceList[index].isConn;
    if (isConn) {
      wx.closeBLEConnection({
        deviceId: deviceId,
        success: () => {
          that.data.deviceList[index].isConn = false;
          that.setData({
            deviceList: that.data.deviceList
          })
          wx.showToast({
            title: name + '已断开',
            icon: 'none'
          })
        }
      })
    } else {
      wx.createBLEConnection({
        deviceId, // 搜索到设备的 deviceId
        success: () => {
          // 连接成功,获取服务
          that.data.deviceList[index].isConn = true;
          that.setData({
            deviceList: that.data.deviceList
          })
          wx.showToast({
            title: name + '已连接',
            icon: 'none'
          })
          wx.startWifi({
            success(res){
                wx.getWifiList({
                  success(e){
                   that.showModal()
                  }
                })
            }
          })
          wx.getBLEDeviceServices({
            deviceId, // 搜索到设备的 deviceId
            success: (res) => {
              if (res.errCode == 0) {
                wx.showToast({
                  title: res.errMsg,
                  icon: 'none'
                })
                console.log(deviceId + " > " + res.errMsg);
              }
              for (let i = 0; i < res.services.length; i++) {
                console.log(JSON.stringify(res.services[i]));
                if (res.services[i].isPrimary) {
                  var serviceId = res.services[i].uuid;
                  if (serviceId.startsWith('00000922')) {
                    wx.showToast({
                      title: '0922已找到',
                      icon: 'none'
                    })
                    this.writeAndRead(deviceId, serviceId);

                  }
                }
              }
            },
            fail: (err) => {
              console.log(JSON.stringify(err));
            }
          })
        }
      })
    }
  },
  writeAndRead(deviceId, serviceId) {
    let that = this;
    wx.getBLEDeviceCharacteristics({
      deviceId, // 搜索到设备的 deviceId
      serviceId, // 上一步中找到的某个服务
      success: (res) => {
        for (let i = 0; i < res.characteristics.length; i++) {
          let item = res.characteristics[i]
          console.log('特征值 >>> ' + JSON.stringify(item));
          if (item.properties.write) { // 该特征值可写
            that.setData({
              deviceId: deviceId,
              serviceId: serviceId,
              characteristicId: item.uuid
            })
          }
          if (item.properties.read) { // 该特征值可读

          }
          if (item.properties.notify || item.properties.indicate) {
            that.setData({
              characteristicId2: item.uuid
            })
          }
        }
      }
    })
  },
  hexStringToArrayBuffer(hexString) {
    const len = hexString.length / 2;
    const buffer = new ArrayBuffer(len);
    const dataView = new DataView(buffer);
    for (let i = 0; i < len; i++) {
      dataView.setUint8(i, parseInt(hexString.substr(i * 22), 16));
    }
    return buffer;
  },
  // 发送数据函数
  sendData() {
    let that = this;

    var mtu = 20;
    wx.getBLEMTU({
      deviceId: that.data.deviceId,
      writeType: 'write',
      success (res) {
        console.log('getBLEMTU >>>');
        console.log(res)
        mtu = res.mtu;
        console.log('getBLEMTU <<<');
      }
    })
    console.log('获取到的mtu >>> '+mtu);
    wx.setBLEMTU({
      deviceId: that.data.deviceId,
      mtu,
      success(res){
        console.log('setBLEMTU S>>>');
        console.log(res);
        console.log('setBLEMTU S>>>');
      },
      fail(err){
        console.log('setBLEMTU F>>>');
        console.log(res);
        console.log('setBLEMTU F>>>');
      }
    })

    if (that.data.wifiName == null || that.data.wifiPass == null) {
      wx.showToast({
        title: '请填写wifi信息~',
        icon: 'none'
      });
      return;
    }
    that.hideModalPass()
    that.hideModal()
    var dataObj = {
      ssid: that.data.wifiName,
      password: that.data.wifiPass
    }
    var tranObj = {
      type'apinfo',
      data: dataObj
    }
    setTimeout(() => {
      console.log('等3s再执行');
      console.log("配网明文 > " + JSON.stringify(tranObj));
    var jsonString = JSON.stringify(tranObj).replace(/:/g': ');
    jsonString = jsonString.replace(/…/g'...');
    console.log("配网明文TRAN > " + jsonString);
    var cmdHex = this.stringToHex(jsonString);
    console.log("配网HEX > " + cmdHex);
    const hexData = cmdHex;
    console.log(JSON.stringify(that.data));
    let buffer = this.hexStringToArrayBuffer(hexData);
    const packages = this.splitDataIntoPackages(buffer,mtu-1);
    const hexArray = this.traverseArrayBufferWithUint8Array(buffer);
    console.log(hexArray.join(' '));

    this.sendPackages(that.data.deviceId, that.data.serviceId, that.data.characteristicId, packages, () => {
      console.log("end >>>>>");
      console.log('2次确认 >>> ' + JSON.stringify(that.data));
      // 延时设置通知
      setTimeout(() => {
        wx.readBLECharacteristicValue({
          deviceId: that.data.deviceId,
          serviceId: that.data.serviceId,
          characteristicId: that.data.characteristicId2,
          success: (res) => {
            console.log('读取设备信息成功 >>> ' + JSON.stringify(res));
          }
        })
        wx.notifyBLECharacteristicValueChange({
          deviceId: that.data.deviceId,
          serviceId: that.data.serviceId,
          characteristicId: that.data.characteristicId2,
          state: true,
          type'notification',
          success: (res) => {
            console.log('监听已开启~');
            wx.showToast({
              title: '监听已开启~',
              icon: 'none'
            });
            wx.showLoading({
              title:'配网进行中,请耐心等待设备回执~(超过2分钟,请尝试重新配网)',
              mask:true
            })
            wx.onBLECharacteristicValueChange((result) => {
              var receiveText = that.buf2string(result.value);
              console.log('监听到的数据回执1 >>> ' + receiveText);
              // 两个输出结果是一样的,转换过程都一样,这里都打印,用一个就行
              var content = that.receiveData(result.value);
              console.log('监听到的数据回执2 >>> ' + content);
              wx.hideLoading();
              if (receiveText.indexOf('wifi_reply') !== -1) {
                if (receiveText.indexOf('connected') !== -1) {
                  wx.showModal({
                    title: '提示',
                    content: '配网连接成功 > connected',
                    showCancel: false, 
                    confirmText: '我知道了',
                    success (res) {
                      if (res.confirm) {
                        console.log('用户点击确定')
                        that.setData({
                          searchWifiShow:true
                        })
                      }
                    }
                  })
                  
                } else if (receiveText.indexOf('not') !== -1) {
                  wx.showModal({
                    title: '提示',
                    content: '未找到该WIFI > ap not found',
                    showCancel: false, 
                    confirmText: '我知道了',
                    success (res) {
                      if (res.confirm) {
                        console.log('用户点击确定')
                        that.setData({
                          searchWifiShow:true
                        })
                      }
                    }
                  })
                } else if (receiveText.indexOf('low') !== -1) {
                  wx.showModal({
                    title: '提示',
                    content: 'RSSI信号过低 > rssi too low',
                    showCancel: false, 
                    confirmText: '我知道了',
                    success (res) {
                      if (res.confirm) {
                        console.log('用户点击确定')
                        that.setData({
                          searchWifiShow:true
                        })
                      }
                    }
                  })
                } else if (receiveText.indexOf('fail') !== -1) {
                  wx.showToast({
                    title: '配网连接失败 > fail connected',
                    icon: 'none'
                  });
                  wx.showModal({
                    title: '提示',
                    content: '配网连接失败 > fail connected',
                    showCancel: false, 
                    confirmText: '我知道了',
                    success (res) {
                      if (res.confirm) {
                        console.log('用户点击确定')
                        that.setData({
                          searchWifiShow:true
                        })
                      }
                    }
                  })
                }
                that.data.deviceList[that.data.deviceIndex].isConn = false;
                that.setData({
                  deviceList: that.data.deviceList
                })
              }
            });
          },
          fail: (err) => {
            console.error('设置通知失败 >>>', err);
          }
        });
      }, 200); // 设置 1 秒延时
    });
    },3000)
    
  },
  // 分包数据并添加前缀
  splitDataIntoPackages(buffer,mtu) {
    console.log('mtu size >>> '+ mtu);
    const chunkSize = mtu; // 每包 20 字节,包括 1 字节前缀,实际数据 19 字节
    const uint8Array = new Uint8Array(buffer);
    const packages = [];
    const totalLength = uint8Array.length;

    for (let offset = 0; offset < totalLength; offset += chunkSize) {
      let end = Math.min(offset + chunkSize, totalLength);
      let chunk = uint8Array.slice(offset, end);

      // 创建一个新的 Uint8Array 包含前缀和数据
      const packageBuffer = new Uint8Array(chunk.length + 1);

      // 添加前缀
      if (offset === 0) {
        packageBuffer[0] = 0x01// 首包前缀
      } else if (end === totalLength) {
        packageBuffer[0] = 0x11// 尾包前缀
      } else {
        packageBuffer[0] = 0x10// 中间包前缀
      }

      // 将数据写入包中
      packageBuffer.set(chunk, 1);

      packages.push(packageBuffer.buffer);
    }

    return packages;
  },

  // 发送数据包
  sendPackages(deviceId, serviceId, characteristicId, packages, callback) {
    let that = this;
    let index = 0;

    function sendNextPackage() {
      if (index < packages.length) {
        const currentPackage = new Uint8Array(packages[index]);
        const hexString = that.uint8ArrayToHexString(currentPackage);
        console.log(`发送数据包 HEX >>> 包序号: ${index} 数据: ${hexString}`);

        wx.writeBLECharacteristicValue({
          deviceId: deviceId,
          serviceId: serviceId,
          characteristicId: characteristicId,
          value: packages[index],
          success: (res) => {
            console.log(`发送数据包成功 >>> 包序号: ${index}`JSON.stringify(res));
            index++;
            sendNextPackage(); // 发送下一个包
          },
          fail: (err) => {
            console.error(`发送数据包失败 >>> 包序号: ${index}`, err);
          }
        });
      } else {
        console.log('所有数据包发送完成');
        if (callback) callback();
      }
    }

    sendNextPackage();
  },
  clearData() {
    let that = this;
    wx.closeBLEConnection({
      deviceId: that.data.deviceId,
    })
    wx.closeBluetoothAdapter({})
    that.setData({
      deviceList: [],
      deviceId: null,
      serviceId: null,
      characteristicId: null,
      characteristicId2: null,
    });
  },
  traverseArrayBufferWithUint8Array(buffer) {
    const uint8Array = new Uint8Array(buffer);
    const length = uint8Array.length;
    const hexArray = [];
    for (let i = 0; i < length; i++) {
      // 获取每个字节并转换为 HEX 格式
      const byte = uint8Array[i];
      const hex = byte.toString(16).padStart(2'0').toUpperCase();
      hexArray.push(hex);
    }

    return hexArray;
  },
  // 将 Uint8Array 转换为 HEX 字符串
  uint8ArrayToHexString(uint8Array) {
    return Array.from(uint8Array).map(byte => byte.toString(16).padStart(2'0').toUpperCase()).join(' ');
  },
  /* 转换成需要的格式 */
  buf2string(buffer) {
    var arr = Array.prototype.map.call(new Uint8Array(buffer), x => x)
    return arr.map((char, i) => {
      return String.fromCharCode(char);
    }).join('');
  },
  receiveData(buf) {
    return this.hexCharCodeToStr(this.ab2hex(buf))
  },
  /* 转成二进制 */
  ab2hex(buffer) {
    var hexArr = Array.prototype.map.call(
      new Uint8Array(buffer), function (bit{
        return ('00' + bit.toString(16)).slice(-2)
      }
    )
    return hexArr.join('')
  },
  /* 转成可展会的文字 */
  hexCharCodeToStr(hexCharCodeStr) {
    var trimedStr = hexCharCodeStr.trim();
    var rawStr = trimedStr.substr(02).toLowerCase() === '0x' ? trimedStr.substr(2) : trimedStr;
    var len = rawStr.length;
    var curCharCode;
    var resultStr = [];
    for (var i = 0; i < len; i = i + 2) {
      curCharCode = parseInt(rawStr.substr(i, 2), 16);
      resultStr.push(String.fromCharCode(curCharCode));
    }
    return resultStr.join('');
  },
  stringToHex(str) {
    let hex = '';
    for (let i = 0; i < str.length; i++) {
      hex += str.charCodeAt(i).toString(16);
    }
    return hex;
  },
  showModal() {
    let that = this;
    that.setData({
      isShowDialog: true,
    })
  },
  hideModal() {
    let that = this;
    that.setData({
      isShowDialog: false
    })
  },showModalPass() {
    let that = this;
    that.setData({
      isShowPassDialog: true,
    })
  },
  hideModalPass() {
    let that = this;
    that.setData({
      isShowPassDialog: false
    })
  },connWifi(e){
    let that = this;
    var index = e.currentTarget.dataset.index;
    var SSID = e.currentTarget.dataset.ssid;
    that.setData({
      wifiIndex:index,
      wifiName:SSID
    })
    wx.showModal({
      title: '提示',
      content: '要连接此【'+SSID+'】网络配网吗?',
      showCancel: true// 是否显示取消按钮
      confirmText: '是的'// 确认按钮的文本
      confirmColor: '#3CC51F'// 确认按钮的文字颜色
      cancelText: '取消',
      success (res) {
        if (res.confirm) {
          console.log('用户点击确定')
          that.showModalPass()
        }else if (res.cancel) {
          console.log('用户点击取消')
        }
      }
    })
  }
})

蓝牙连接,特征值等都正常,数据也可以正常发送设备,设备也回执了配网成功,ios可以正常收到设备给的回执,安卓始终收不到,获取和设置mtu 增加延迟 ,设置wx.notifyBLECharacteristicValueChange的type为‘notification’ 都尝试过了,只有ios正常,安卓就是不行啊


回答关注问题邀请回答
收藏
登录 后发表内容