最近很闲,但是行业很卷!因为公司有硬件设备对接,但是介于原生app。闲来无事,便研究了下这个小程序通过蓝牙与硬件设备进行通讯。废话少说上干货!

本次讲解的目录大致分为三模块。根据我写的代码做讲解。

  • 初始化并搜索蓝牙
  • 获取并启用service服务
  • 数据读取初始化并监听设备返回的数据

html部分

uniAPP ios开发蓝牙 uniapp蓝牙通信_开发语言

js逻辑部分-分开讲解

页面进来是需要初始化的date中的数据,所以需要在onload中执行

uniAPP ios开发蓝牙 uniapp蓝牙通信_ecmascript_02

onload中的初始化代码-就是设置列表高度、蓝牙列表的初始化

uniAPP ios开发蓝牙 uniapp蓝牙通信_uniAPP ios开发蓝牙_03

其中初始化最后面有一段代码注释也写得很明确了,第一种的过滤出你设置好的蓝牙,第二种反之。

在onHide事件就是--重置清空

uniAPP ios开发蓝牙 uniapp蓝牙通信_开发语言_04

接下来就是事件逻辑了!!!

我们这里罗列下uniapp官网提供的接下来需要用到的api

// 关闭蓝牙
uni.closeBluetoothAdapter({})
// 打开蓝牙
uni.openBluetoothAdapter({})
// 搜索附近的蓝牙
uni.startBluetoothDevicesDiscovery({})
// 停止搜索
uni.stopBluetoothDevicesDiscovery({})
// 连接低功耗BLE蓝牙
uni.createBLEConnection({})
// 获取蓝牙设备所有服务(service)
uni.getBLEDeviceServices({})
// 获取蓝牙特征值
uni.getBLEDeviceCharacteristics({})
// 启用低功耗蓝牙设备特征值变化时的 notify 功能,订阅特征值
uni.notifyBLECharacteristicValueChange({})
// 监听低功耗蓝牙设备的特征值变化事件
uni.onBLECharacteristicValueChange({})
// 读取低功耗蓝牙设备的特征值的二进制数据值
uni.readBLECharacteristicValue({})

我是把各个事件归总到三个事件里面去执行了。没有分事件来讲解,见谅

首先,是搜索蓝牙:搜索蓝牙里面需用到的就是,关闭蓝牙-打开蓝牙-搜索蓝牙-停止搜索。

uniAPP ios开发蓝牙 uniapp蓝牙通信_开发语言_05

搜索成功后点击蓝牙列表进行连接

uniAPP ios开发蓝牙 uniapp蓝牙通信_开发语言_06

紧接着获取蓝牙设备所有服务(service)及相关的格式转换

uniAPP ios开发蓝牙 uniapp蓝牙通信_uniAPP ios开发蓝牙_07

启动所有服务(service)后,进行蓝牙读取的初始化并监听

uniAPP ios开发蓝牙 uniapp蓝牙通信_搜索_08

附上代码,希望对大家有所帮助。全文干货满满,实战,文章附上实战视频。

<template>
  <view class="device">
    <scroll-view scroll-y :style="'width:500rpx;height:' + list_height + 'rpx'">
      <block v-for="(item, index) in devicesList" :key="index">
        <view class="list-item" :id="item.deviceId" @tap="Connect">
          <view style="display: flex; flex-direction: column; width: 80%">
            <text style="font-size: medium; word-break: break-all"
              >设备名称: {{ item.name }}</text
            >
            <text style="font-size: x-small; color: gray; word-break: break-all"
              >设备ID: {{ item.deviceId }}</text
            >
            <text style="font-size: x-small; color: gray; word-break: break-all"
              >信号强度RSSI: {{ item.RSSI }}</text
            >
          </view>
          <image
            style="width: 36px; height: 36px"
            mode="aspectFit"
            src="/static/images/bluetooth.png"
          ></image>
        </view>
      </block>
    </scroll-view>
    <button
      type="primary"
      class="button"
      :loading="searching"
      @tap="Search"
      style="margin-bottom: 10px"
    >
      {{ searching ? '搜索中...' : '1.搜索蓝牙设备' }}
    </button>
    <button
      type="primary"
      class="button"
      @click="getBLEDeviceServices"
      style="margin-bottom: 10px"
    >
      2.获取servies服务
    </button>
    <button
      type="primary"
      class="button"
      :loading="initLoading"
      @click="readBluetoothAdapter"
      style="margin-bottom: 10px"
    >
      3.初始化数据
    </button>
  </view>
</template>

<script>
const app = getApp()
export default {
  data () {
    return {
      searching: false,
      deviceName: 'EV-IC-1961B', // 指定蓝牙名称
      deviceId: null, // 设备id
      services: null, // services服务
      connectedDeviceId: null, // 记录设备id
      notifyCharacteristicsId: null, // 蓝牙特征值
      balanceData: null, // 转换的格式
      notifyServicweId: null, // 特征id
      hexstr: null, // 转换格式
      devicesList: [], // 列表
      list_height: '',
      initLoading: false
    }
  },
  onLoad () {
    var that = this
    var list_height =
      (app.globalData.SystemInfo.windowHeight - 200) *
        (750 / app.globalData.SystemInfo.windowWidth) -
      60
    that.$set(that, 'list_height', list_height)
    uni.onBluetoothAdapterStateChange(function (res) {
      that.$set(that, 'searching', res.discovering)
      if (!res.available) {
        that.$set(that, 'searching', false)
      }
    })
    that.$set(that, 'list_height', list_height)
    uni.onBluetoothAdapterStateChange(function (res) {
      that.$set(that, 'searching', res.discovering)
      if (!res.available) {
        that.$set(that, 'searching', false)
      }
    })
    uni.onBluetoothDeviceFound(function (devices) {
      //剔除重复设备,兼容不同设备API的不同返回值
      var isnotexist = true
      if (devices.deviceId) {
        if (devices.advertisData) {
          devices.advertisData = app.globalData.buf2hex(devices.advertisData)
        } else {
          devices.advertisData = ''
        }
        for (var i = 0; i < that.devicesList.length; i++) {
          if (devices.deviceId == that.devicesList[i].deviceId) {
            isnotexist = false
          }
        }
        if (isnotexist) {
          that.devicesList.push(devices)
        }
      } else if (devices.devices) {
        if (devices.devices[0].advertisData) {
          devices.devices[0].advertisData = app.globalData.buf2hex(
            devices.devices[0].advertisData
          )
        } else {
          devices.devices[0].advertisData = ''
        }
        for (var i = 0; i < that.devicesList.length; i++) {
          if (devices.devices[0].deviceId == that.devicesList[i].deviceId) {
            isnotexist = false
          }
        }
        if (isnotexist) {
          that.devicesList.push(devices.devices[0])
        }
      } else if (devices[0]) {
        if (devices[0].advertisData) {
          devices[0].advertisData = app.globalData.buf2hex(
            devices[0].advertisData
          )
        } else {
          devices[0].advertisData = ''
        }
        // console.log(devices[0])
        for (var i = 0; i < devices_list.length; i++) {
          if (devices[0].deviceId == that.devicesList[i].deviceId) {
            isnotexist = false
          }
        }
        if (isnotexist) {
          that.devicesList.push(devices[0])
        }
      }
      let devList = []
      // 专用名称 意思是只有这名字才显示出来
      // that.devicesList.forEach(item=>{
      //   if(item.name == that.deviceName){
      //     that.deviceId = item.deviceId; // 直接获取指定设备的id
      //     devList.push(item)
      //   }
      // })
      // 有设备名 意思是有设备名就显示
      that.devicesList.forEach(item => {
        if (item.name) {
          devList.push(item)
        }
      })
      // that.$set(that, 'devicesList', that.devicesList)
      that.$set(that, 'devicesList', devList) // 过滤
    })
  },
  onHide: function () {
    var that = this
    that.$set(that, 'devicesList', [])
    if (this.searching) {
      uni.stopBluetoothDevicesDiscovery({
        success: function (res) {
          that.$set(that, 'searching', false)
        }
      })
    }
  },
  methods: {
    // 搜索设备
    Search: function () {
      var that = this
      if (!that.searching) {
        // 关闭蓝牙
        uni.closeBluetoothAdapter({
          complete: function (res) {
            // 打开蓝牙
            uni.openBluetoothAdapter({
              success: function (res) {
                // 搜索蓝牙
                uni.startBluetoothDevicesDiscovery({
                  allowDuplicatesKey: false,
                  success: function (res) {
                    that.$set(that, 'searching', true)
                    that.$set(that, 'devicesList', [])
                    setTimeout(() => {
                      // 停止搜索
                      uni.stopBluetoothDevicesDiscovery({
                        success: function (res) {
                          uni.showToast({
                            title: '搜索完成',
                            icon: 'success',
                            duration: 500
                          })
                          that.$set(that, 'searching', false)
                        }
                      })
                    }, 2000)
                  }
                })
              },
              fail: function (res) {
                uni.showModal({
                  title: '提示',
                  content: '请检查手机蓝牙是否打开',
                  showCancel: false,
                  success: function (res) {
                    that.$set(that, 'searching', false)
                  }
                })
              }
            })
          }
        })
      } else {
        uni.stopBluetoothDevicesDiscovery({
          success: function (res) {
            that.$set(that, 'searching', false)
          }
        })
      }
    },

    // 5连接蓝牙Connect connectTO
    Connect (e) {
      var that = this
      uni.showLoading({
        title: '连接蓝牙设备中...'
      })
      that.deviceId = e.currentTarget.id // 这里是做非指定设备的兼容赋值
      console.log('设备id', that.deviceId)
      app.globalData.deviceId = that.deviceId
      uni.createBLEConnection({
        deviceId: that.deviceId, // 蓝牙设备id
        success: function (res) {
          that.connectedDeviceId = that.deviceId
          uni.hideLoading()
          uni.showToast({
            title: '连接成功',
            icon: 'success',
            duration: 1000
          })
        },
        fail: function (res) {
          uni.hideLoading()
          uni.showModal({
            title: '提示',
            content: '连接失败',
            showCancel: false
          })
        }
      })
    },
    // 6获取蓝牙设备的service服务
    getBLEDeviceServices () {
      var that = this
      uni.getBLEDeviceServices({
        deviceId: that.deviceId || app.globalData.deviceId,
        success: function (res) {
          uni.showToast({
            title: '启动成功',
            icon: 'success',
            duration: 1000
          })
          that.services = res.services // 拿到数组对象
          /* 获取连接设备的所有特征值 */
          that.getBLEDeviceCharacteristics()
        },
        fail: error => {
          console.log(error, '错误反馈')
        }
      })
    },
    // 7获取蓝牙设备特征值
    getBLEDeviceCharacteristics () {
      var that = this
      // console.log(that.services,'that.services');  // 拿到了数据
      uni.getBLEDeviceCharacteristics({
        //   deviceId: that.connectedDeviceId,
        deviceId: that.deviceId || app.globalData.deviceId,
        serviceId: that.services[0].uuid,
        success: function (res) {
          console.log(res, 'res')
          for (var i = 0; i < res.characteristics.length; i++) {
            if (
              (res.characteristics[i].properties.notify ||
                res.characteristics[i].properties.indicate) &&
              res.characteristics[i].properties.read &&
              res.characteristics[i].properties.write
            ) {
              console.log(res.characteristics[i].uuid, '蓝牙特征值 ==========') // 已获得
              /* 获取蓝牙特征值 */
              that.notifyCharacteristicsId = res.characteristics[i].uuid
              // 启用低功耗蓝牙设备特征值变化时的 notify 功能
              that.notifyBLECharacteristicValueChange()
            }
          }
        },
        fail: function (res) {}
      })
    },
    // 启动notify 蓝牙监听功能 后使用 wx.onBLECharacteristicValueChange用来监听蓝牙设备传递数据
    // 接收到的数据和发送的数据必须是二级制数据, 页面展示的时候需要进行转换
    notifyBLECharacteristicValueChange () {
      // 启用低功耗蓝牙设备特征值变化时的 notify 功能
      var that = this
      console.log('6.启用低功耗蓝牙设备特征值变化时的 notify 功能')
      uni.notifyBLECharacteristicValueChange({
        state: true,
        deviceId: that.deviceId || app.globalData.deviceId,
        serviceId: that.services[0].uuid, // 蓝牙特征值对应服务的 uuid
        characteristicId: that.notifyCharacteristicsId,
        complete (res) {
          console.log('7启用低功耗蓝牙设备监听成功')
          /*用来监听手机蓝牙设备的数据变化*/
          uni.onBLECharacteristicValueChange(function (res) {
            /**/
            that.balanceData += that.buf2string(res.value)
            that.hexstr += that.receiveData(res.value)
          })
        },
        fail (res) {
          console.log(res, '启用低功耗蓝牙设备监听失败')
        }
      })
    },
    /*转换成需要的格式*/
    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(0, 2).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('')
    },
    // 读取蓝牙传输的数据
    readBluetoothAdapter () {
      var that = this
      that.initLoading = true
      // 必须在这里的回调才能获取
      uni.onBLECharacteristicValueChange(function (characteristic) {
        console.log(that.buf2string(characteristic.value), '16进制解析')
      })
      uni.readBLECharacteristicValue({
        // 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
        deviceId: that.deviceId || app.globalData.deviceId,
        // 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
        serviceId: that.services[0].uuid,
        // 这里的 characteristicId 需要在上面的 getBLEDeviceCharacteristics 接口中获取
        characteristicId: that.notifyCharacteristicsId,
        success: function (res) {
          console.log('readBLECharacteristicValue:', res) // ok
        }
      })
      setTimeout(() => {
        that.initLoading = false
        uni.showToast({
          title: '初始化成功',
          icon: 'success'
        })
      }, 2000)
    }
    // 在向蓝牙设备传递数据和接收数据的过程中,并未使用到read的API 不知道有没有潜在的问题,目前线上运行为发现任何的问题
  }
}
</script>

<style>
.device {
  padding: 0 15px;
}
</style>

视频链接:小程序蓝牙设备数据对接实战