微信小程序 蓝牙设备连接,控制开关灯

news2024/9/27 9:24:40

1.前言

微信小程序中连接蓝牙设备,信息写入流程
1、检测当前使用设备(如自己的手机)是否支持蓝牙/蓝牙开启状态
wx:openBluetoothAdapter({})
2、如蓝牙已开启状态,检查蓝牙适配器的状态
wx.getBluetoothAdapterState({})
3、添加监听蓝牙适配器状态变化
wx.onBluetoothAdapterStateChange({})
4、搜索附近蓝牙设备
wx.startBluetoothDevicesDiscovery({})
5、监听搜索的到设备
wx.onBluetoothDeviceFound({})
遍历设备列表找到和macAddr(看自己家的蓝牙装置的物理地址)匹配的设备的deviceId
6、连接想要连接的切匹配成功的设备
wx.createBLEConnection({})
7、获取连接成功的设备的设备服务servicesID
wx.getBLEDeviceServices({})
8、获取设备特征id
wx.getBLEDeviceCharacteristics({})
9、向设备写入指令
wx.writeBLECharacteristicValue({})

以下是代码重点,本人亲测有效,集官网和百家之所长,汇聚之大成,入我门来,使君不负观赏

#2. 小程序前端index.wxml

<wxs module="utils">
module.exports.max = function(n1, n2) {
  return Math.max(n1, n2)
}
module.exports.len = function(arr) {
  arr = arr || []
  return arr.length
}
</wxs>
<button bindtap="openBluetoothAdapter"   class="primary-btn" >开始扫描</button>
<button bindtap="closeBLEConnection"  class="default-btn" >断开连接</button>
<button bindtap="opendeng"   class="sumit-btn" >开灯</button>
<button bindtap="guandeng"   class="warn-btn" >关灯</button>

<view class="devices_summary">已发现 {{devices.length}} 个外围设备:</view>
<scroll-view class="device_list" scroll-y scroll-with-animation>
  <view wx:for="{{devices}}" wx:key="index"
   data-device-id="{{item.deviceId}}"
   data-name="{{item.name || item.localName}}"
   bindtap="createBLEConnection" 
   class="device_item"
   hover-class="device_item_hover">
    <view style="font-size: 16px; color: #333;">{{item.name}}</view>
    <view style="font-size: 10px">信号强度: {{item.RSSI}}dBm ({{utils.max(0, item.RSSI + 100)}}%)</view>
    <view style="font-size: 10px">UUID: {{item.deviceId}}</view>
    <view style="font-size: 10px">Service数量: {{utils.len(item.advertisServiceUUIDs)}}</view>
  </view>
</scroll-view>

<view class="connected_info" wx:if="{{connected}}">
   <view>
    <icon class="icon-box-img" type="success" size="33"></icon>
    <view class="icon-box-text"> {{name}}连接成功!!!</view>
   </view> 
 
</view>

3.小程序index.wxss


page {
  color: #333;
}
.icon-box-img{
  height: 20px;
 float: left;
 margin-left: 20%;
}
.icon-box-text{
  height: 30px;
   line-height: 30px;
  margin-left:5px;
 float: left;
}

.primary-btn{
  margin-top: 30rpx;
  /* border: rgb(7, 80, 68) 1px solid; */
  color: rgb(241, 238, 238);
  background-color: rgb(14, 207, 46);
}
.default-btn{
  margin-top: 30rpx;
  /* border: #333 1px solid; */
  color: rgb(243, 236, 236);
  background-color: rgb(189, 112, 25);
}

.warn-btn{
  margin-top: 30rpx;
  /* border: #333 1px solid; */
  color: rgb(240, 231, 231);
  background-color: rgb(235, 10, 10);
}
.sumit-btn{
  margin-top: 30px;
  width: 60px;
  color: rgb(240, 231, 231);
  background-color: rgb(10, 100, 235);
}

.devices_summary {
  margin-top: 30px;
  padding: 10px;
  font-size: 16px;
}
.device_list {
  height: 300px;
  margin: 50px 5px;
  margin-top: 0;
  border: 1px solid #EEE;
  border-radius: 5px;
  width: auto;
}
.device_item {
  border-bottom: 1px solid #EEE;
  padding: 10px;
  color: #666;
}
.device_item_hover {
  background-color: rgba(0, 0, 0, .1);
}
.connected_info {
  position: fixed;
    bottom: 10px;
    width: 80%;
    margin-left: 6%;
    background-color: #F0F0F0;
    padding: 10px;
    padding-bottom: 20px;
    margin-bottom: env(safe-area-inset-bottom);
    font-size: 18px;
    /* line-height: 20px; */
    color: #1da54d;
    text-align: center;
    /* height: 20px; */
    box-shadow: 0px 0px 3px 0px;
}
.connected_info .operation {
  position: absolute;
  display: inline-block;
  right: 30px;
}


4.小程序 index.ts


const app = getApp()

function inArray(arr, key, val) {
  for (let i = 0; i < arr.length; i++) {
    if (arr[i][key] === val) {
      return i;
    }
  }
  return -1;
}

// ArrayBuffer转16进度字符串示例
function ab2hex(buffer) {
  var hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function (bit) {
      return ('00' + bit.toString(16)).slice(-2)
    }
  )
  return hexArr.join('');
}

Page({
  data: {
    devices: [],
    connected: false,
    chs: [],
  },
  getCeshi() {
    wx.navigateTo({
      url: '/pages/main/main',
    })
  },
  // 搜寻周边蓝牙
  openBluetoothAdapter() {
    //console.log('openBluetoothAdapter success')
    wx.openBluetoothAdapter({
      success: (res) => {
        console.log('openBluetoothAdapter success', res)
        this.startBluetoothDevicesDiscovery()
      },
      fail: (res) => {
        if (res.errCode === 10001) {
          wx.onBluetoothAdapterStateChange(function (res) {
            console.log('onBluetoothAdapterStateChange', res)
            if (res.available) {
              this.startBluetoothDevicesDiscovery()
            }
          })
        }
      }
    })
  },
  // 停止搜寻周边蓝牙
  getBluetoothAdapterState() {
    wx.getBluetoothAdapterState({
      success: (res) => {
        console.log('getBluetoothAdapterState', res)
        if (res.discovering) {
          this.onBluetoothDeviceFound()
        } else if (res.available) {
          this.startBluetoothDevicesDiscovery()
        }
      }
    })
  },
  // 开始搜寻附近的蓝牙外围设备
  startBluetoothDevicesDiscovery() {
    if (this._discoveryStarted) {
      return
    }
    this._discoveryStarted = true
    wx.startBluetoothDevicesDiscovery({
      allowDuplicatesKey: true,
      success: (res) => {
        console.log('startBluetoothDevicesDiscovery success', res)
        this.onBluetoothDeviceFound()
      },
    })
  },
  //停止搜寻附近的蓝牙外围设备。若已经找到需要的蓝牙设备并不需要继续搜索时,建议调用该接口停止蓝牙搜索。
  stopBluetoothDevicesDiscovery() {
    wx.stopBluetoothDevicesDiscovery()
  },
  //监听搜索到新设备的事件
  onBluetoothDeviceFound() {
    wx.onBluetoothDeviceFound((res) => {
      res.devices.forEach(device => {
        if (!device.name && !device.localName) {
          return
        }
        const foundDevices = this.data.devices
        const idx = inArray(foundDevices, 'deviceId', device.deviceId)
        const data = {}
        if (idx === -1) {
          data[`devices[${foundDevices.length}]`] = device
        } else {
          data[`devices[${idx}]`] = device
        }
        this.setData(data)
      })
    })
  },
  //连接蓝牙低功耗设备。
  createBLEConnection(e) {
    const ds = e.currentTarget.dataset
    const deviceId = ds.deviceId
    const name = ds.name
    wx.createBLEConnection({
      deviceId,
      success: (res) => {
        this.setData({
          connected: true,
          name,
          deviceId,
        })
        this.getBLEDeviceServices(deviceId)
      }
    })
    this.stopBluetoothDevicesDiscovery()
  },
  closeBLEConnection() {
    wx.closeBLEConnection({
      deviceId: this.data.deviceId
    })
    this.setData({
      connected: false,
      chs: [],
      canWrite: false,
    })
  },
  //获取蓝牙低功耗设备所有服务。
  getBLEDeviceServices(deviceId) {
    console.log('deviceId ========', deviceId)
    wx.getBLEDeviceServices({
      deviceId,
      success: (res) => {
        for (let i = 0; i < res.services.length; i++) {
          //获取通过设备id多个特性服务serviceid (包含 读、写、通知、等特性服务)
         //   console.log('serviceId ========', res.services[2].uuid)
          
         // 通过上边注释解封,排查到判断服务id 中第三个服务id  代表写入服务
        //  isPrimary代表 :判断服务id是否为主服务
         // if (res.services[2].isPrimary) {
         //  this.getBLEDeviceCharacteristics(deviceId, res.services[2].uuid)
        //  }
           //判断通过(设备id获取的多个特性服务)中是否有与(蓝牙助手获取的写入特性服务),相一致的serviceid
          if (res.services[i].uuid=="0000FFE0-0000-1000-8000-00805F9B34FB") {
            this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid) 
          }
        }
      }
    })
  },
  //获取蓝牙低功耗设备某个服务中所有特征 (characteristic)。
  getBLEDeviceCharacteristics(deviceId, serviceId) {
    console.info("蓝牙的deviceId====" + deviceId);
    console.info("蓝牙服务的serviceId====" + serviceId);
    wx.getBLEDeviceCharacteristics({
      deviceId,
      serviceId,
      success: (res) => {
        console.log('getBLEDeviceCharacteristics success', res.characteristics)
        for (let i = 0; i < res.characteristics.length; i++) {
          let item = res.characteristics[i]
          if (item.properties.read) {
            wx.readBLECharacteristicValue({
              deviceId,
              serviceId,
              characteristicId: item.uuid,
            })
          }
          if (item.properties.write) {
            this.setData({
              canWrite: true
            })
            this._deviceId = deviceId
            this._serviceId = serviceId
            this._characteristicId = item.uuid

            console.info("写入(第一步)的characteristicId====" + item.uuid);
            //初始化调用 写入信息的方法  1:代表开灯
            this.writeBLECharacteristicValue("1")
          }
          if (item.properties.notify || item.properties.indicate) {
            wx.notifyBLECharacteristicValueChange({
              deviceId,
              serviceId,
              characteristicId: item.uuid,
              state: true,
            })
          }
        }
      },
      fail(res) {
        console.error('getBLEDeviceCharacteristics', res)
      }
    })
    // 操作之前先监听,保证第一时间获取数据
    wx.onBLECharacteristicValueChange((characteristic) => {
      const idx = inArray(this.data.chs, 'uuid', characteristic.characteristicId)
      const data = {}
      if (idx === -1) {
        data[`chs[${this.data.chs.length}]`] = {
          uuid: characteristic.characteristicId,
          value: ab2hex(characteristic.value)
        }
      } else {
        data[`chs[${idx}]`] = {
          uuid: characteristic.characteristicId,
          value: ab2hex(characteristic.value)
        }
      }
     
      this.setData(data)
    })
  },
  
/**
 * 写入的数据 格式转换
 * @param str
 * @returns 字符串 转 ArrayBuffer
 */
hex2buffer(str) {
  console.info("写入的数据====" + str);
  //字符串 转 十六进制
  var val = "";
  for (var i = 0; i < str.length; i++) {
      if (val == "")
          val = str.charCodeAt(i).toString(16);
      else
          val += "," + str.charCodeAt(i).toString(16);
  }
  //十六进制 转 ArrayBuffer
  var buffer = new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
      return parseInt(h, 16)
  })).buffer;

  return buffer;
},
//开灯
opendeng() {
  
  // 调用写入信息的方法 向蓝牙设备发送一个开灯的参数数据 ,  1:代表开灯
  var writeValue ="1";
  this.writeBLECharacteristicValue(writeValue)
 
},
//关灯
guandeng() {

    // 调用写入信息的方法 向蓝牙设备发送一个关灯的参数数据 ,  0:代表关灯
     var writeValue ="0";
     this.writeBLECharacteristicValue(writeValue)
    
},
writeBLECharacteristicValue(writeValue) {
  // 向蓝牙设备发送一个0x00的16进制数据
 // let buffer = new ArrayBuffer(1)
 // let dataView = new DataView(buffer)
 //  dataView.setUint8(0, 0)
 //调用hex2buffer()方法,转化成ArrayBuffer格式
 console.log('获取传递参数writeValue的数据为=====',writeValue)
 var buffer =this.hex2buffer(writeValue);
  console.log('获取二进制数据',buffer)
  //向低功耗蓝牙设备特征值中写入二进制数据。
  wx.writeBLECharacteristicValue({
    deviceId: this._deviceId,
    serviceId: this._serviceId,
    characteristicId: this._characteristicId,
    value: buffer,
    success (res) {
      console.log('成功写数据writeBLECharacteristicValue success', res)
      //如果 uni.writeBLECharacteristicValue 走 success ,证明你已经把数据向外成功发送了,但不代表设备一定就收到了。通常设备收到你发送过去的信息,会返回一条消息给你,而这个回调消息会在 uni.onBLECharacteristicValueChange 触发
    },
     fail(res) {
      console.error('失败写数据getBLEDeviceCharacteristics', res)
    }
  })
},
//断开与蓝牙低功耗设备的连接。
  closeBluetoothAdapter() {
    wx.closeBluetoothAdapter()
    this._discoveryStarted = false
  },
})



5.疑难点

1.如果你不确定写入的特性服务值

可以通过(安卓)蓝牙助手获取特性值 deviceId(mac地址)、serviceId、characteristicId

(苹果手机)蓝牙助手获得特性值deviceId(uuid地址)、serviceId、characteristicId  
 

注意:手机品类不同获取的deviceId的名称不同,但serviceId、characteristicId,是相同的

1.安卓手机(蓝牙助手获取的信息)
在这里插入图片描述
在这里插入图片描述

2.苹果手机(蓝牙助手)获取的信息

在这里插入图片描述
在这里插入图片描述

FFF0代表的serviceId全称:0000FFF0-0000-1000-8000-00805F9B34FB

FFF3代表的characteristicId全称:0000FFF3-0000-1000-8000-00805F9B34FB

最后:

祝愿你能一次成功
(代码直接复制粘贴到你的小程序中,替换下你自己设备的写入特性serviceId值),
就可以测试了
最后如果还满意,记得点下赞、收藏加关注、我也好回关,相互进步!!!!!!!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/889635.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PHP 公交公司充电桩管理系统mysql数据库web结构apache计算机软件工程网页wamp

一、源码特点 PHP 公交公司充电桩管理系统是一套完善的web设计系统&#xff0c;对理解php编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。 源码下载 https://download.csdn.net/download/qq_41221322/88220946 论文下…

如何把PDF转换成PPT?试试这个转换方法

在现代办公和学习中&#xff0c;我们经常会遇到需要将PDF文件转换成PPT文件的需求。PDF作为一种通用的文件格式&#xff0c;被广泛应用于文档的共享和传递。通过将PDF转换成PPT&#xff0c;我们可以更好地编辑、演示和展示内容&#xff0c;适应不同设备和平台&#xff0c;提升交…

PyQt6学习第一篇

PyQt6中文文档 PyQt6 Digia 公司的 Qt 程序的 Python 中间件。Qt库是最强大的GUI库之一。PyQt6 是基于 Python 的一系列模块。它是一个多平台的工具包&#xff0c;可以在包括Unix、Windows和Mac OS在内的大部分主要操作系统上运行。PyQt6 有两个许可证&#xff0c;开发人员可以…

OJ练习第149题—— 二叉树中的最大路径和

二叉树中的最大路径和 力扣链接&#xff1a;124. 二叉树中的最大路径和 题目描述 二叉树中的 路径 被定义为一条节点序列&#xff0c;序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点&#xff0c;且不一定经过根…

三分钟上手!一文看懂 Git 的底层工作原理

这是一篇能让你迅速了解 Git 工作原理的文章&#xff0c;实战案例解析&#xff0c;相信我&#xff0c;3 分钟&#xff0c;绝对能够有收获&#xff01; Git 目录结构 Git 的本质是一个文件系统&#xff08;很重要&#xff0c;记住这句话&#xff0c;理解这句话&#xff09;&…

极简版VS自定义:我选择极简版

极简版VS自定义&#xff1a;我选择极简版 &#x1f607;博主简介&#xff1a;我是一名正在攻读研究生学位的人工智能专业学生&#xff0c;我可以为计算机、人工智能相关本科生和研究生提供排忧解惑的服务。如果您有任何问题或困惑&#xff0c;欢迎随时来交流哦&#xff01;&…

图数据库_Neo4j_Centos7.9安装Neo4j社区版3.5.4_基于jdk1.8---Neo4j图数据库工作笔记0011

首先上传安装包,到opt/soft目录 然后看一下jdk安装的是什么版本的,因为在neo4j 4以后就必须要用jdk11 以上的版本,我这里还用着jdk1.8 所以 我这里用3.5.4的版本 关于下载地址: https://dist.neo4j.org/neo4j-community-3.5.4-unix.tar.gz 然后再去解压到/opt/module目录下 …

用递归与迭代完成二叉树的三种遍历

目录 二叉树的前序遍历 题目 前序遍历题目链接 递归代码 1.利用方法返回值的代码 2.返回值为void的代码 非递归实现前序遍历(利用栈stack) 1.利用方法返回值的代码 2.返回值为void的代码 二叉树的中序遍历 题目 :给定一个二叉树的根节点 root &#xff0c;返回 它…

小红书数据分析:如何借势热点ip,打造爆款热度笔记

导语 影视作为小红书的后起板块之一&#xff0c;今夏来势汹汹~众多「爆款」影视/电影的出现极大程度上刺激着社区用户保持发布欲望&#xff0c;抬升了用户碎片化表达的意愿&#xff0c;刺激了小红书社区内容的活跃度。 小红书独有的社区特色&#xff0c;让影视作品除了解说外…

冠霖团队与TPC公司线下会议共谋TPC资产管理基金项目合作

近年来&#xff0c;金融市场蓬勃发展&#xff0c;为全球各大金融机构提供了前所未有的合作机会。在这个背景下&#xff0c;冠霖团队和TPC公司的线下会议成为一场引人瞩目的合作洽谈&#xff0c;旨在共同探讨TPC资产管理基金项目的合作机会与前景。 TPC公司作为一个拥有广泛国际…

3D模型格式转换工具如何与Parasolid集成?

概述 HOOPS Exchange包括一个 Parasolid 连接器&#xff0c;它允许 Parasolid 开发人员轻松地将 CAD 数据导入到活动的 Parasolid 会话中。如果源数据基于 Parasolid&#xff08;NX、Solid Edge 或 SolidWorks&#xff09;&#xff0c;则数据将按原样导入。 这意味着您可以假…

京东门详一码多端探索与实践 | 京东云技术团队

本文主要讲述京东门详业务在支撑过程中遇到的困境&#xff0c;面对问题我们在效率提升、质量保障等方向的探索和实践&#xff0c;在此将实践过程中问题解决的思路和方案与大家一起分享&#xff0c;也希望能给大家带来一些新的启发 一、背景 1.1、京东门详介绍 1.1.1、京东门…

python怎么下载以前的版本,python怎么下载第三方库

大家好&#xff0c;给大家分享一下python怎么下载以前的版本&#xff0c;很多人还不知道这一点。下面详细解释一下。现在让我们来看看&#xff01; Python 下载安装&#xff08;文末附python全套学习资料&#xff09; 安装python分三个步骤&#xff1a;1.下载python 2.安装pyth…

计网第三章(数据链路层)(二)

目录 一、可靠传输 二、可靠传输的三种实现机制 1.停止等待协议SW&#xff08;自动重传协议ARQ&#xff09; &#xff08;1&#xff09;理想情况&#xff08;即无差错情况&#xff09;&#xff1a; &#xff08;2&#xff09;有误码的情况&#xff1a; &#xff08;3&#…

Python中5种下划线的含义,你都知道吗

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 1.单前导下划线&#xff1a;_var 当涉及到变量和方法名称时&#xff0c;单个下划线前缀有一个约定俗成的含义。 它是对程…

【04 英语语法:非谓语动词(不定式、分词、动名词)详解】

非谓语动词 非谓语动词&#xff1a;不定式&#xff0c;分词&#xff0c;动名词1. 不定式&#xff1a;动词原形&#xff08;do/原形不定式&#xff09;&#xff0c;带 to 不定式 &#xff08;to do&#xff09;1.1 带 to 不定式&#xff08;to do&#xff09;的功能&#xff1a;…

Apipost预执行脚本如何调用外部jar包

近期版本更新中Apipost推出插件管理&#xff0c;可以直接在预、后执行脚本中调用本地的脚本文件 导入脚本 在「系统设置」—「插件管理」中打开目录将要执行的脚本文件拖入到文件夹下 执行脚本 需要获取请求参数&#xff1a; const requestData request.request_bodys; 在…

版本控制工具Git集成IDEA的学习笔记(第二篇GitHub)

目录 一、团队内协作和跨团队协作讲述 1、团队内协作 2、跨团队协作 二、团队内合作交互方式 1、初始化本地库 2、创建远程库 3、在本地创建远程库地址的别名 4、推送操作 5、克隆操作 6、邀请加入团队&#xff0c;push操作 7、远程库修改的拉取操作 8、远程库修改…

【Vue】yarn 安装包时权限不足或者文件夹被占用导致安装失败

在一个 Vue3 项目中&#xff0c;用 yarn 安装 Vue 插件或者 Vue-Router 时&#xff0c;出现同样的 error &#xff0c;如下&#xff1a; An unexpected error occurred: “EPERM: operation not permitted, unlink ‘C:\Codefield\项目\yupao-frontend\node_modules\esbuild\w…

zabbix配置微信报警

如有错误&#xff0c;敬请谅解&#xff01; 此文章仅为本人学习笔记&#xff0c;仅供参考&#xff0c;如有冒犯&#xff0c;请联系作者删除&#xff01;&#xff01; 6.1 注册企业微信 企业微信注册地址&#xff1a;https://work.weixin.qq.com 设置总部门名称添加成员 也可以…