首页 星云 工具 资源 星选 资讯 热门工具
:

PDF转图片 完全免费 小红书视频下载 无水印 抖音视频下载 无水印 数字星空

微信小程序 BLE 基础业务接口封装

编程知识
2024年08月28日 09:48

写在前面:本文所述未必符合当前最新情形(包括蓝牙技术发展、微信小程序接口迭代等)。

微信小程序为蓝牙操作提供了很多接口,但在实际开发过程中,会发现隐藏了不少坑。目前主流蓝牙应用都是基于低功耗蓝牙(BLE)的,本文介绍相关的几个基础接口,并对其进行封装,便于业务层调用。

蓝牙发展

在开发蓝牙应用程序之前,有必要对蓝牙这项技术做大致了解。

经典蓝牙

一种短距离无线通信标准,运行在 2.4GHz 频段,主要用于两个设备之间的数据传输。

一般将蓝牙 4.0 之前的版本称为经典蓝牙,其传输速率在 1-3Mbps 之间。虽然有着不错的传输速率,但由于功耗较大,难以满足移动终端和物联网的需求,逐渐被更先进的版本所取代。‌

低功耗蓝牙(BLE)

蓝牙 4.0‌ 引入了低功耗蓝牙(BLE)技术,其最大数据吞吐量仅为1Mbps,但相对经典蓝牙,BLE 拥有超低的运行功耗和待机功耗。

BLE 的低功耗是如何做到的呢?主要是缩减广播通道数量(由经典蓝牙的 16-32个,缩减为 3 个)、缩短广播射频开启时间(由经典蓝牙的 22.5ms,减少到 0.6-1.2ms)、深度睡眠模式及针对低功耗场景优化了协议栈等,此处不赘述。

当前最新版本

‌当前大版本是蓝牙 5.0,传输速度达到了 24Mbps,是 4.2 版本的两倍,有效工作距离可达 300 米,是 4.2 版本的四倍。低功耗模式下的传输速度上限为 2Mbps,适合于影音级应用,如高清晰度音频解码协议的应用。

蓝牙特征值

GATT(Generic Attribute Profile)协议定义了蓝牙设备之间的通信方式,其中单个服务(Service)可以包含多个特征值(Characteristic),每个服务和特征值都有特定的‌ UUID 来唯一标识。特征值是蓝牙设备中用于存储和传输数据的基本单元,每个特征值都有其特定的属性和值

属性协议(ATT)定义数据的检索,允许设备暴露数据给其他设备,这些数据被称为属性(attribute)

通过属性可以设置特征值操作类型,如读取、写入、通知等,操作对象即为特征值的值(value)。一个特征值可以同时拥有多种操作类型。

为了实现数据的传输,服务需要暴露两个主要的特征值:write和‌notify 或 indication。write 特征值用于接收数据,而 notify 特征值用于发送数据。这些特征值类型为 bytes,并且一次传输的数据长度可以根据不同的特征值类型有所不同。

小程序接口封装

需要知道的是,虽然蓝牙是开放协议,但由于苹果 IOS 系统的封闭设计,目前苹果设备无法与 Android 及其它平台设备通过蓝牙相连。

本文描述皆基于 Android 平台。

关键接口

使用蓝牙传输数据都会涉及以下步骤及接口:

  1. 激活设备蓝牙(如在手机上点按蓝牙图标);
  2. wx.openBluetoothAdapter:初始化小程序蓝牙模块;
  3. 搜索外围设备
    1. wx.onBluetoothDeviceFound:监听搜索到新设备的事件;
    2. wx.startBluetoothDevicesDiscovery:开始搜索附近设备;
    3. wx.stopBluetoothDevicesDiscovery:找到待连的对手设备后停止搜索;
  4. wx.createBLEConnection:连接 BLE 设备;
  5. 接收数据
    1. wx.notifyBLECharacteristicValueChange:为下一步骤做铺垫(注意:必须对手设备的特征支持 notify 或者 indicate 才可以成功调用);
    2. wx.onBLECharacteristicValueChange:监听对手设备特征值变化事件,可以获得变化后的特征 value,如此数据就从对手设备传递过来了;
  6. wx.writeBLECharacteristicValue:向对手设备特征值中写入二进制数据(注意:必须对手设备的特征支持 write 才可以成功调用);
  7. wx.closeBLEConnection:断开连接;
  8. wx.closeBluetoothAdapter:关闭小程序蓝牙模块;
  9. 关闭设备蓝牙。

坑及注意点(仅限于笔者基于开发过程使用到的机型观察记录,未必有普遍性):

  • wx.onBluetoothDeviceFound 这个方法只能找到新的蓝牙设备,之前搜索过的在部分安卓机型上,不算做新的蓝牙设备,因此重新搜索不到。这种情况,要么重启小程序蓝牙模块或者重启小程序,或者使用wx.getBluetoothDevices获取在蓝牙模块生效期间所有搜索到的蓝牙设备。
  • 连接未必能一次成功,需要多连几次。
  • 每次连接最好能重启 BluetoothAdapter,否则在后续 wx.notifyBLECharacteristicValueChange 时容易报 10005-没有找到指定特征 错误。
  • 若小程序在之前已有搜索过某个蓝牙设备,并成功建立连接,可直接传入之前搜索获取的 deviceId 直接尝试连接该设备,无需再次进行搜索操作。
  • 系统与蓝牙设备会限制蓝牙 4.0 单次传输的数据大小,超过最大字节数后会发生写入错误,建议每次写入不超过 20 字节。
  • 一旦过程中出现任何异常,就必须断开连接重连,否则后续会一直报 notifyblecharacteristicValuechange:fail: no characteristic 错误

主要代码

注:本文代码块为笔者临时盲敲,仅作参考。

定义一个工具对象

const ble = {}

由于可能会遇到的各类问题,我们先全局定义运行时异常枚举和 throw/handle 方法,免得后面遇到异常处理各写各的。

const ble = {
  errors: {
    OPEN_ADAPTER: '开启蓝牙模块异常',
    CLOSE_ADAPTER: '关闭蓝牙模块异常',
    CONNECT: '蓝牙连接异常',
    NOTIFY_CHARACTERISTIC_VALUE_CHANGE: '注册特征值变化异常',
    WRITE: '发送数据异常',
    DISCONNECT: '断开蓝牙连接异常',
    //...
  },

  _throwError(title, err) {
    //... 可以考虑在这里调用 wx.closeBLEConnection

    if (err) {
      err.Title = title
      throw err
    }
    throw new Error(title)
  },  

蓝牙连接。注意到这是个有限递归方法,且每次连接都先重启 BluetoothAdapter,原因请看上节。

/** 
   * @param {string} deviceId 设备号
   * @param {int} tryCount 已尝试次数
   */
  async connectBLE(deviceId, tryCount = 5) {
    await wx.closeBluetoothAdapter().catch(err => { ble._throwError(this.errors.CLOSE_ADAPTER, err) })
    await wx.openBluetoothAdapter().catch(err => { ble._throwError(this.errors.OPEN_ADAPTER, err) })
    await wx.createBLEConnection({
      deviceId: deviceId,
      timeout: 5000
    })
      .catch(async err => {
        if (err.errCode === -1) { //蓝牙已是连接状态
          // continue work
        } else {
          console.log(`第${6 - tryCount}次蓝牙连接出错`, err.errCode, err.errMsg)
          tryCount--
          if (tryCount === 0) {
            ble._throwError(this.errors.CONNECT, err)
          } else {
            await ble.connectBLE(deviceId, tryCount)
          }
        }
      })
      //蓝牙连接成功
  },

连接成功后,可能需要监听对手设备,用于接收其传过来的数据。

  async onDataReceive(deviceId, serviceId, characteristicId, callback) {
    await wx.notifyBLECharacteristicValueChange({
      deviceId: deviceId,
      serviceId: serviceId,
      characteristicId: characteristicId,
      state: true
    }).catch(err => { ble._throwError(this.errors.NOTIFY_CHARACTERISTIC_VALUE_CHANGE, err) })

    wx.onBLECharacteristicValueChange(res => {
      let data = new Uint8Array(res.value)
      callback(data)
    })
  },

发送数据,须切片,每次发送不多于 20字节。此处增加了在固定时长内的重试机制。

  /** 
   * @param {Uint8ClampedArray} data 待发送数据
   * @param {boolean} holdConnWhenDone 发送完毕后是否保持连接
   */
  async send(deviceId, serviceId, characteristicId, data, holdConnWhenDone = false) {
    let idx = 0 //已传输字节数
    let startTime = Date.now(),
      duration = 800 //发送失败重试持续时间  
    while (idx < data.byteLength) {
      await wx.writeBLECharacteristicValue({
        deviceId: deviceId,
        serviceId: serviceId,
        characteristicId: characteristicId,
        value: data.slice(idx, idx += 20).buffer
      })
        .then(_ => startTime = Date.now()) //成功则now重置
        .catch(err => {
          if (Date.now() - startTime >= duration) {
            ble._throwError(this.errors.WRITE, err)
          } else {
            //重试
            idx -= 20
          }
        })
    }
    if (!holdConnWhenDone)
      await wx.closeBLEConnection({ deviceId: deviceId }).catch(err => { ble._throwError(this.errors.DISCONNECT, err) })
  }

在实际项目中,可能需要在每次发送数据片之后得到对手设备响应后,根据响应决定重发(校验错误或响应超时等)、中止(设备繁忙)、还是接着发送下一个数据片。这种情况则需配合 onDataReceive 方法协同工作,向其传入合适的 callback 参数,此处不赘述。

From:https://www.cnblogs.com/newton/p/18384145
本文地址: http://shuzixingkong.net/article/1515
0评论
提交 加载更多评论
其他文章 Cloud Studio:颠覆传统的云端开发与学习解决方案
Cloud Studio 的推出无疑为开发者和学习者们提供了一种全新的工作和学习体验。作为一款基于浏览器的集成开发环境,它不仅让开发者能够随时随地访问自己的开发工具,还极大地简化了开发过程。无论是初学者还是经验丰富的开发者,Cloud Studio 都通过其全面的功能和丰富的资源,满足了不同层次的需
Cloud Studio:颠覆传统的云端开发与学习解决方案 Cloud Studio:颠覆传统的云端开发与学习解决方案 Cloud Studio:颠覆传统的云端开发与学习解决方案
OpenTelemetry 实战:从零实现应用指标监控
前言 在上一篇文章:OpenTelemetry 实战:从零实现分布式链路追踪讲解了链路相关的实战,本次我们继续跟进如何使用 OpenTelemetry 集成 metrics 监控。 建议对指标监控不太熟的朋友可以先查看这篇前菜文章:从 Prometheus 到 OpenTelemetry:指标监控的
OpenTelemetry 实战:从零实现应用指标监控 OpenTelemetry 实战:从零实现应用指标监控 OpenTelemetry 实战:从零实现应用指标监控
实用接地气的 .NET 微服务框架
前言 微服务架构已经成为搭建高效、可扩展系统的关键技术之一,然而,现有许多微服务框架往往过于复杂,使得我们普通开发者难以快速上手并体验到微服务带了的便利。为了解决这一问题,于是作者精心打造了一款最接地气的 .NET 微服务框架,帮助我们轻松构建和管理微服务应用。 本框架不仅支持 Consul 服务注
实用接地气的 .NET 微服务框架 实用接地气的 .NET 微服务框架 实用接地气的 .NET 微服务框架
折腾 Quickwit,Rust 编写的分布式搜索引擎 - 从不同的来源摄取数据
摄取 API 在这节教程中,我们将介绍如何使用 Ingest API 向 Quickwit 发送数据。 要跟随这节教程,您需要有一个本地的 Quickwit 实例正在运行。 https://quickwit.io/docs/get-started/installation 要启动它,请在终端中运行
折腾 Quickwit,Rust 编写的分布式搜索引擎 - 从不同的来源摄取数据
【VMware VCF】VCF 5.2:挂载远程 vSAN 数据存储。
VMware vSAN 解决方案中,为了充分利用 vSAN HCI 集群内的存储资源, vSAN HCI 和 vSAN HCI 集群之间可以相互共享存储资源,这种解决方案早期叫 vSAN HCI Mesh,现在被称为具有数据存储共享的 vSAN HCI(vSAN HCI with datastore
【VMware VCF】VCF 5.2:挂载远程 vSAN 数据存储。 【VMware VCF】VCF 5.2:挂载远程 vSAN 数据存储。 【VMware VCF】VCF 5.2:挂载远程 vSAN 数据存储。
Python中的分布式框架Ray的安装与使用
本文介绍了基于Python的分布式框架Ray的基本安装与使用。Ray框架下不仅可以通过conda和Python十分方便的构建一个集群,还可以自动的对分布式任务进行并发处理,且支持GPU分布式任务的提交,极大的简化了手动分布式开发的工作量。
Python中的分布式框架Ray的安装与使用 Python中的分布式框架Ray的安装与使用 Python中的分布式框架Ray的安装与使用
B 端产品未来几年的发展趋势
未来几年,B 端产品领域将面临着诸多挑战和机遇。人工智能与机器学习的深度融合、云计算与容器化技术的持续发展、用户体验与设计的重要性日益凸显、数据安全与隐私保护的挑战与机遇、行业垂直化与专业化发展以及敏捷开发与持续交付的普及等趋势,将对 B 端产品经理提出更高的要求
B 端产品未来几年的发展趋势 B 端产品未来几年的发展趋势 B 端产品未来几年的发展趋势
【音视频通话】使用asp.net core 8+vue3 实现高效音视频通话
引言 在三年前,写智能小车的时候,当时小车上有一个摄像头需要采集,实现推拉流的操作,技术选型当时第一版用的是nginx的rtmp的推拉流,服务器的配置环境是centos,2H4G3M的一个配置,nginx的rtmp的延迟是20秒,超慢,后来研究了SRS以及ZLMediaKit这两个开源的推拉流服务器
【音视频通话】使用asp.net core 8+vue3 实现高效音视频通话 【音视频通话】使用asp.net core 8+vue3 实现高效音视频通话 【音视频通话】使用asp.net core 8+vue3 实现高效音视频通话