微信小程序 蓝牙设备连接,控制开关灯
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 = truewx.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.devicesconst 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.datasetconst deviceId = ds.deviceIdconst name = ds.namewx.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获取的多个特性服务)中是否有与(蓝牙助手获取的写入特性服务),相一致的serviceidif (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 = deviceIdthis._serviceId = serviceIdthis._characteristicId = item.uuidconsole.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);elseval += "," + str.charCodeAt(i).toString(16);}//十六进制 转 ArrayBuffervar 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值),
就可以测试了
最后如果还满意,记得点下赞、收藏加关注、我也好回关,相互进步!!!!!!!!!!
相关文章:
微信小程序 蓝牙设备连接,控制开关灯
1.前言 微信小程序中连接蓝牙设备,信息写入流程 1、检测当前使用设备(如自己的手机)是否支持蓝牙/蓝牙开启状态 wx:openBluetoothAdapter({}) 2、如蓝牙已开启状态,检查蓝牙适配器的状态 wx.getBluetoothAdapterState({}) 3、添加…...
Python 矢量数据库和矢量索引:构建 LLM 应用程序
推荐:使用 NSDT场景编辑器 助你快速搭建可二次编辑的3D应用场景 由于使用其硬件创建的生成式AI应用程序,Nvidia经历了显着的增长。另一项软件创新,矢量数据库,也正在乘着生成式人工智能的浪潮。 开发人员正在向量数据库上用Pytho…...
-Webkit-Box 在 Safari 中出现的兼容性问题
一、问题背景: UI要求要实现这样的效果,使用 display:-webket-box在chrome浏览器下完美解决 但是马上啪啪打脸,在safari浏览器下显示空白 ,不能不说浏览器之间的兼容性简直就是天坑 二、解决办法 通过浏览器调试发现原本float的…...
后端项目打包上传服务器记录
后端项目打包上传服务器记录 文章目录 后端项目打包上传服务器记录1、项目打包2、jar包上传服务器 本文记录打包一个后端项目,上传公司服务器的过程。 1、项目打包 通过IDEA的插件进行打包: 打成一个jar包,jar包的位置在控制台可以看到。 2、…...
ubuntu部署haproxy
HAProxy是可提供高可用性、负载均衡以及基于TCP和HTTP应用的代理. 1、更新系统报 通过在终端中运行以下命令,确保所有系统包都是最新的 sudo apt updatesudo apt upgrade2、安装Haproxy sudo apt install haproxy设置开机自动启动haproxy服务 sudo systemctl en…...
vue利用 sortable 完成表格拖拽
先讲一下vue2,使用sortable完成表格拖拽【不只是表格,div也可以实现,但我项目中是表格拖拽】 github地址 安装 npm install sortablejs --save使用 (我的项目中是拖拽一个小按钮移动,而不是整行) <te…...
CNN简介3
...
新能源电动车充电桩控制主板安全特点
新能源电动车充电桩控制主板安全特点 你是否曾经担心过充电桩的安全问题?充电桩主板又是什么样的呢?今天我们就来聊聊这个话题。 充电桩主板采用双重安全防护系统,包括防水、防护、防尘等,确保充电桩安全、可靠。不仅如此,充电桩主板采用先…...
公路桥梁有哪些安全隐患?
在现代社会,公路桥梁作为连接城市、串联交通的重要纽带,扮演着无可替代的角色。然而,我们常常忽视的是,这些高架构筑物也存在着潜在的安全隐患,可能随时影响着交通的畅通和人们的生命财产安全。为了更好地认识和理解这…...
【C语言】每日一题(错误的集合)
最近在牛客、力扣上做题,花费海量时间,苦不堪言,有时绞尽脑汁也想不出,痛定思痛,每日记录写的比较困难的题。 错误的集合 题目如上图所示 题主乍看之下觉得很简单,再看例子,不就是一个有序数组…...
[JavaWeb]【四】web后端开发-SpringBootWeb入门
目录 一 Spring 二 SpringBootWeb入门 2.1 入门需求 2.2 分析 2.3 开始创建SpringBootWeb 2.4 创建类实现需求 2.5 启动程序 2.6 访问 三 HTTP协议 3.1 HTTP-概述 3.2 HTTP-请求协议 3.3 HTTP-响应协议 3.3.1 响应状态码 && 响应类型 3.4 HTTP-协议解析 前言…...
前端css
day03-CSS基础 目标:掌握 CSS 属性基本写法,能够使用文字相关属性美化文章页。 01-CSS初体验 层叠样式表 (Cascading Style Sheets,缩写为 CSS),是一种 样式表 语言,用来描述 HTML 文档的呈现(…...
vb+sql医院门诊管理系统设计与系统
摘要 信息时代已经来临,计算机应用于医院的日常管理,为医院的现代化带来了从未有过的动力和机遇,为医疗卫生领域的发展提供了无限的潜力。采用计算机管理信息系统已成为医院管理科学化和现代化的标志,给医院带来了明显的经济效益和社会效益。 本文介绍了数据库管理系统的…...
bootstrap-modal调用ajax后不经过回调函数
说明:我用的是boostrap的弹框,表单用的是layui的,个人觉得bootstrap比layui的弹框好看点,能自适应高度。 如图:点击保存后里面的内容不执行 原因:type用的是submit 解决:把submit改为button...
【【典型电路设计之片内存储器的设计之RAM的Verilog HDL描述一】】
典型电路设计之片内存储器的设计之RAM的Verilog HDL描述一 RAM是随机存储器,存储单元的内容可按需随意取出或存入。这种存储器在断电后将丢失所有数据,一般用来存储一些短时间内使用的程序和数据。 其内部结构如下图所示: 例:用…...
食品行业案例 | 燕千云助力头部食品企业搭建数智化 IT服务管理体系及平台
随着数字化时代的到来,食品行业呈现出多个发展趋势。首先,消费者对健康食品和功能性食品的关注度提高,推动了市场需求的增长。其次,便利食品和物流行业迅速发展,满足了快节奏生活的需求。再者,电商渠道和网…...
SpringBoot的日志信息及Lombok的常用注解
文章目录 一. 日志的介绍1. 什么是日志2. 日志的作用 二. 日志的使用1. 日志格式说明2. 自定义日志的输出3. 日志级别4. 日志级别的配置5. 日志持久化6. 更简单的输出日志-Lomok7. Lombok框架实现原理以及其他常见注解 一. 日志的介绍 1. 什么是日志 日志是我们程序重要组成部…...
Genoss GPT简介:使用 Genoss 模型网关实现多个LLM模型的快速切换与集成
一、前言 生成式人工智能领域的发展继续加速,大型语言模型 (LLM) 的用途范围不断扩大。这些用途跨越不同的领域,包括个人助理、文档检索以及图像和文本生成。ChatGPT 等突破性应用程序为公司进入该领域并开始使用这项技术进行构建铺平了道路。 大公司正…...
淘宝API接口的实时数据和缓存数据区别
电商API接口实时数据是指通过API接口获取到的与电商相关的实时数据。这些数据可以包括商品库存、订单状态、销售额、用户活跃度等信息。 通过电商API接口,可以实时获取到电商平台上的各种数据,这些数据可以帮助企业或开发者做出及时的决策和分析。例如&…...
excel统计函数篇1之average系列
一、excel中的统计函数 1、AVERAGE(number1,number2,...):返回其参数的平均值 2、AAVERAGEA(value1,value2,...):返回其参数的平均值,包括数字、文本和逻辑值 可以在括号内手动输入,也可以引用单元格,对序列求平均的…...
利用最小二乘法找圆心和半径
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
Java - Mysql数据类型对应
Mysql数据类型java数据类型备注整型INT/INTEGERint / java.lang.Integer–BIGINTlong/java.lang.Long–––浮点型FLOATfloat/java.lang.FloatDOUBLEdouble/java.lang.Double–DECIMAL/NUMERICjava.math.BigDecimal字符串型CHARjava.lang.String固定长度字符串VARCHARjava.lang…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...
【电力电子】基于STM32F103C8T6单片机双极性SPWM逆变(硬件篇)
本项目是基于 STM32F103C8T6 微控制器的 SPWM(正弦脉宽调制)电源模块,能够生成可调频率和幅值的正弦波交流电源输出。该项目适用于逆变器、UPS电源、变频器等应用场景。 供电电源 输入电压采集 上图为本设计的电源电路,图中 D1 为二极管, 其目的是防止正负极电源反接, …...
