微信小程序 蓝牙通讯
客户的需求如下:通过微信小程序控制蓝牙ble设备(电子面膜),通过不同指令控制面膜的亮度和时间。
01.首先看下客户的ble设备服务文档:(本部分需要有点蓝牙基础,在调试过程中可以用安卓软件nRF Connect软件来执行测试命令)
0xFFF1灯控命令
命令格式:
命令类型:
0x01 – 常规模式,命令数据第一个字节为模式(1-3表示模式1-3),第二个字节为开关状态(0为暂停,1为启动)。
0x02 – 个性模式,命令数据第一个数据为强度百分比(1-100),第二个字节是时间低位,第三个字节是时间高位(单位秒)。
FFF2 灯状态
4个字节,数据同灯控命令的(命令类型+命令数据)。
FFF3验证码算法
通道用户连接时加密验证,该通道具有read/write 两种属性。
以下是手机端连接上设备后的加密流程。
手机连接SKLight(记录MAC地址) => 使能SKLight FFF2通道完成 => 读取(read) FFF3 新生成的4个字节的随机数 => 随机数结合设备的MAC地址计算出验证码=> FFF3 将验证码写给 SKLight (建议发3次) 读取验证结果(建议500-1000ms后读取,这时只有0x01一个字节正确,0x00则为失败)=>完成(失败请APP断开连接)
计算验证码的 C 函数:
// mac 为设备MAC地址 , rand为读到的随机数// auth_data 为计算得到的验证码,2个字节void getAuthenticationData(uint8_t *mac, uint8_t *rand,uint8_t *auth_data){auth_data [0] = mac[0]^ mac [1]^mac[2]^rand[0]^rand[1];auth_data [1] = mac[3]^mac[4]^mac[5]^rand[2]^rand[3];}
FFF4 电池状态
6个字节,字节1为电池电压高8位,字节2为电池电压低8位;字节3为电池电流高8位,字节4为电池电流低8位;字节5为电池电量(1-5),1表示低电量,5表示满电量;字节6为工作计时,为0时表示负载开路。
FFF8修改密码
验证成功后,可以通过该通道进行修改密码:
密码应为字符格式。
数据格式:共12字节:
修改密码应该在 500~1000ms 以后读取该通道,读到0x01 说明密码修改成功,否则失败。
FFF9修改设备名称
验证成功后,可以通过该通道进行修改设备广播名称:
数据格式:1~20字节:
修改密码应该在 500~1000ms 以后读取该通道,读到0x01 说明名称修改成功,否则失败。
名称修改完成后于断开连接时生效广播。
02.现在开始进行开发小程序端:
0201.蓝牙适配器开启
wx.openBluetoothAdapter({success: function (res) {//开启成功,继续搜索操作},fail:function(){//开启失败,后台监听状态处理,注意:在安卓系统中手动开启蓝牙可以监听,苹果在设置中开启监听不到,必须使用快捷图标开启(算是小程序蓝牙之坑)wx.onBluetoothAdapterStateChange(function (res) {if (res.available) {//后台监听到蓝牙适配器的状态变化,并且可用.}}) }
})
0202.搜索设备
//单纯的去搜索设备,并不会返回搜索列表
wx.startBluetoothDevicesDiscovery({success: function (res) {//已经执行搜索,查看搜索到的设备列表wx.getBluetoothDevices({success: function (res) {//打印获取到的设备列表,此处可以获取到设备的广播消息//设备的deviceId字段要非常注意,安卓返回的硬件mac地址,苹果返回的是uuid//当然无论返回什么都不影响你使用小程序蓝牙api//但是如果你的服务uuid需要你提供硬件mac地址交互的话需要做兼容处理//例如你可以要求蓝牙方在广播数据中保存硬件mac地址.(第2坑)console.log(res)}}) }
})
0203.连接ble设备
wx.createBLEConnection({//这里的deviceId就是上一步获取的设备列表的deviceId属性,不用关心这个字段的值,不关心是安卓还是苹果deviceId: deviceId,success: function (res) {console.log(res)}
})
0204.获得验证码
//上面的文档指示我通过FFF3服务uuid读取1个4位数字的验证码
//然后结合设备硬件mac地址通过C算法生成验证码发送给设备//002.读取后我拿来计算
wx.onBLECharacteristicValueChange(function (characteristic) {var macstring = deviceId;//设备mac地址,我处理过.不能直接用搜索列表的deviceIdvar randstring = ab2hex(characteristic.value);//设备给我的4位数字var verifycode = getAuthenticationData(macstring, randstring);//需要将C语言的算法转javascript})
//001.我来读取4位数字
wx.readBLECharacteristicValue({deviceId: deviceId,serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',characteristicId: '0000FFF3-0000-1000-8000-00805F9B34FB',success: function (res) {}
})//003.C语法转换的javascript
function getAuthenticationData(macstring, randstring) {var auth_data = new Array();//Macvar Tempmac = macstring.split(':');for (var i = 0; i < Tempmac.length; i++) {Tempmac[i] = '0x' + Tempmac[i];}var mac = new Uint8Array(Tempmac);//Mac-End//Randvar Temprand = new Array();var temprandj = 0;for (var i = 0; i < randstring.length; i++) {Temprand[temprandj] = '0x' + randstring.slice(i, i + 2);i++;temprandj++;}var rand = new Uint8Array(Temprand);//Rand-Endauth_data[0] = mac[0] ^ mac[1] ^ mac[2] ^ rand[0] ^ rand[1];auth_data[1] = mac[3] ^ mac[4] ^ mac[5] ^ rand[2] ^ rand[3];auth_data[0] = auth_data[0].toString(16);auth_data[1] = auth_data[1].toString(16);return auth_data;
}
0205.发送验证码
sendverify(verifycode);//发送验证码。文档建议发送3次然后再读取值,如果值是1那么验证通过,其他的uuid指令也通过验证sendverify: function (verifycode) {var self = this;var deviceId =app.globalData.deviceId;for (var i = 0; i < 3; i++) {var hex =verifycode[0] + verifycode[1];var typedArray = new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function (h) {return parseInt(h, 16)}))//console.log('本次执行命令:' + hex);var buffer = typedArray.bufferwx.writeBLECharacteristicValue({deviceId: deviceId,serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',characteristicId: '0000FFF3-0000-1000-8000-00805F9B34FB',value: buffer,success: function (res) {//console.log('writeBLECharacteristicValue success', res.errMsg)}})}//文档建议1000MS后读取验证码setTimeout(function () {wx.onBLECharacteristicValueChange(function (characteristic) {var rescode = parseInt(ab2hex(characteristic.value),10);if (rescode ==1) {//console.log('通过验证');wx.notifyBLECharacteristicValueChange({state: true, deviceId: deviceId,serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',characteristicId: '0000FFF4-0000-1000-8000-00805F9B34FB',success: function (res) {//读取电量信息wx.onBLECharacteristicValueChange(function (res) {var charge = ab2hex(res.value);console.log('原始数据:'+charge);app.globalData.bettery=parseInt('0x' + charge[8] + charge[9], 16);app.globalData.blecurrent = parseInt('' + charge[4] + charge[5] + charge[6] + charge[7]+'', 16)var dianya = parseInt('' + charge[0] + charge[1] + charge[2] + charge[3] + '', 16)console.log('电压:' + dianya);console.log('电量:' + app.globalData.bettery);console.log('电流:' + app.globalData.blecurrent); })//读取电量信息}}) return; }else {//验证码验证失败self.setData({ishow:0,showstatus: { text: 'Err,请尝试重新连接!', status: 1 }}); wx.closeBLEConnection({deviceId: deviceId}) }})wx.readBLECharacteristicValue({deviceId: deviceId,serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',characteristicId: '0000FFF3-0000-1000-8000-00805F9B34FB',success: function (res) {}})}, 1000)},
0206.灯控测试,常规模式3启动
//指令构造要注意高位和低位的处理
var hex = 'fa010301000c22'var typedArray = new Uint8Array(hex.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
}))
var buffer = typedArray.buffer
//指令构造
wx.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: '0000FFF0-0000-1000-8000-00805F9B34FB',
characteristicId: '0000FFF1-0000-1000-8000-00805F9B34FB',
value: buffer,
success: function (res) {
//console.log('writeBLECharacteristicValue success', res.errMsg)
}
})
相关文章:

微信小程序 蓝牙通讯
客户的需求如下:通过微信小程序控制蓝牙ble设备(电子面膜),通过不同指令控制面膜的亮度和时间。 01.首先看下客户的ble设备服务文档:(本部分需要有点蓝牙基础,在调试过程中可以用安卓软件nRF Connect软件来执行测试命令) 0xFFF1灯控命令 命…...

java后端项目技术记录
后端使用技术记录 一、软件1. apifox,API管理软件问题 2. nginx前端服务器(1) 反向代理(2) 负载均衡 二、问题1. 使用spring全局异常处理器处理特定的异常2. 扩展springmvc的消息转换器(对象和json数据的转换)3. 路径参数的接收4. 实体构建器…...

PostgreSQL数据库与PostGIS在Windows中的部署与运行
本文介绍在Windows电脑中,下载、安装、部署并运行PostgreSQL与PostGIS数据库服务的方法。 PostgreSQL是一种功能强大的开源关系型数据库管理系统(RDBMS),以其稳定性、可靠性和丰富的功能而闻名;其支持多种高级特性&…...

高级算法设计与分析 学习笔记10 平摊分析
动态表,可以变长。 一溢出就另起一个两倍大小的表。 可以轻易证明把n个数字放进去的时间复杂度是O(n),n n/2 n/4……也就2n,插入数字本身也就是n,加起来最多不超过3n. 这种复杂度究竟是怎么算的?毕竟每次插入复杂度…...
从“纸面算力”到“好用算力”,超聚变打通AI+“最后一公里”
如果要评选2024年的年度科技名词,AI当属最热门的候选项。 年初的《政府工作报告》中首次提出了“人工智能”行动,正在从顶层设计着手,加快形成以人工智能为引擎的新质生产力。 折射到市场层面,AI作为一种新的范式,不…...

【有啥问啥】具身智能(Embodied AI):人工智能的新前沿
具身智能(Embodied AI):人工智能的新前沿 引言 在人工智能(AI)的进程中,具身智能(Embodied AI)正逐渐成为研究与应用的焦点。具身智能不仅关注于机器的计算能力,更强调…...

11-pg内核之锁管理器(六)死锁检测
概念 每个事务都在等待集合中的另一事务,由于这个集合是一个有限集合,因此一旦在这个等待的链条上产生了环,就会产生死锁。自旋锁和轻量锁属于系统锁,他们目前没有死锁检测机制,只能靠内核开发人员在开发过程中谨慎的…...
Git 与标签管理
在 Git 中,标签 tag 是指向某个 commit 的指针(所以创建和删除都很快)。Git 有 commit id 了,为什么还要有 tag?commit id 是一串无规律的数字,不好记;而 tag 是我们自定义的,例如我…...
【0334】Postgres内核之 auxiliary process(辅助进程)初始化 MyPgXact
1. MyPgXact(ProcGlobal->allPgXact)间接初始化 在上一篇文章【0333】Postgres内核之 auxiliary process(辅助进程)创建 PGPROC 中, 讲解了Postgres内核完成 AuxiliaryProcess 初始化 pid、lxid、procLatch、myProcLocks、lockGroupMembers等所有成员的过程。 这些成员…...

20.1 分析pull模型在k8s中的应用,对比push模型
本节重点介绍 : push模型和pull模型监控系统对比为什么在k8s中只能用pull模型的k8s中主要组件的暴露地址说明 push模型和pull模型监控系统 对比下两种系统采用的不同采集模型,即push型采集和pull型采集。不同的模型在性能的考虑上是截然不同的。下面表格简单的说…...
Ubuntu 镜像替换为阿里云镜像:简化你的下载体验
Ubuntu,作为一款广受欢迎的Linux发行版,以其稳定性和易用性著称。但你是否曾因为下载速度慢而感到沮丧?现在,你可以通过将Ubuntu的默认下载源替换为阿里云镜像来解决这个问题。本文将指导你如何完成这一过程。 为什么选择阿里云镜…...
The Sandbox 游戏制作教程第 6 章|如何使用装备制作出色的游戏 —— 避免环境危险
欢迎回到我们的系列,我们将记录 The Sandbox Game Maker 的 “On-Equip”(装备)功能的多种用途。 如果你刚加入 The Sandbox,装备功能是 “可收集组件”(Collectable Component)中的一个多功能工具…...
JavaScript中的输出方式
1. console.log() console.log() 是开发者在调试代码时最常用的方法。它将信息打印到浏览器的控制台,使开发者能够查看变量的值、程序的执行状态以及其他有用的信息。 用途:用于调试和记录程序运行时的信息。优点:简单易用,适合…...
力扣9.25
2306. 公司命名 给你一个字符串数组 ideas 表示在公司命名过程中使用的名字列表。公司命名流程如下: 从 ideas 中选择 2 个 不同 名字,称为 ideaA 和 ideaB 。 交换 ideaA 和 ideaB 的首字母。 如果得到的两个新名字 都 不在ideas 中,那么 …...

从零开始之AI面试小程序
从零开始之AI面试小程序 文章目录 从零开始之AI面试小程序前言一、工具列表二、开发部署流程1. VMWare安装2. Centos安装3. Centos环境配置3.1. 更改子网IP3.2. 配置静态IP地址 4. Docker和Docker Compose安装5. Docker镜像加速源配置6. 部署中间件6.1. MySQL部署6.2. Redis部署…...

Html2OpenXml:HTML转化为OpenXml的.Net库,轻松实现Html转为Word。
推荐一个开源库,轻松实现HTML转化为OpenXml。 01 项目简介 Html2OpenXml 是一个开源.Net库,旨在将简单或复杂的HTML内容转换为OpenXml组件。 该项目始于2009年,最初是为了将用户评论转换为Word文档而设计的 随着时间的推移,Ht…...
HumanNeRF:Free-viewpoint Rendering of Moving People from Monocular Video 精读
1. 姿态估计和骨架变换模块 人体姿态估计:HumanNeRF 通过已知的单目视频对视频中人物的姿态进行估计。常见的方法是通过人体姿态估计器(如 OpenPose 或 SMPL 模型)提取人物的骨架信息,获取 3D 关节的位置信息。这些关节信息可以帮…...

Springboot中基于注解实现公共字段自动填充
1.使用场景 当我们有大量的表需要管理公共字段,并且希望提高开发效率和确保数据一致性时,使用这种自动填充方式是很有必要的。它可以达到一下作用 统一管理数据库表中的公共字段:如创建时间、修改时间、创建人ID、修改人ID等,这些…...
Android 已经过时的方法用什么新方法替代?
过时修正举例 (Kotlin): getColor(): resources.getColor(R.color.white) //已过时// 修正后:ContextCompat.getColor(this, R.color.white) getDrawable(): resources.getDrawable(R.mipmap.test) //已过时//修正后:ContextCompat.getDrawable(this, R.mipmap.test) //…...

【RocketMQ】MQ与RocketMQ介绍
🎯 导读:本文介绍了消息队列(MQ)的基本概念及其在分布式系统中的作用,包括实现异步通信、削峰限流和应用解耦等方面的优势,并对ActiveMQ、RabbitMQ、RocketMQ及Kafka四种MQ产品进行了对比分析,涵…...

【Axure高保真原型】引导弹窗
今天和大家中分享引导弹窗的原型模板,载入页面后,会显示引导弹窗,适用于引导用户使用页面,点击完成后,会显示下一个引导弹窗,直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

国防科技大学计算机基础课程笔记02信息编码
1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

Lombok 的 @Data 注解失效,未生成 getter/setter 方法引发的HTTP 406 错误
HTTP 状态码 406 (Not Acceptable) 和 500 (Internal Server Error) 是两类完全不同的错误,它们的含义、原因和解决方法都有显著区别。以下是详细对比: 1. HTTP 406 (Not Acceptable) 含义: 客户端请求的内容类型与服务器支持的内容类型不匹…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
FastAPI 教程:从入门到实践
FastAPI 是一个现代、快速(高性能)的 Web 框架,用于构建 API,支持 Python 3.6。它基于标准 Python 类型提示,易于学习且功能强大。以下是一个完整的 FastAPI 入门教程,涵盖从环境搭建到创建并运行一个简单的…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...
今日学习:Spring线程池|并发修改异常|链路丢失|登录续期|VIP过期策略|数值类缓存
文章目录 优雅版线程池ThreadPoolTaskExecutor和ThreadPoolTaskExecutor的装饰器并发修改异常并发修改异常简介实现机制设计原因及意义 使用线程池造成的链路丢失问题线程池导致的链路丢失问题发生原因 常见解决方法更好的解决方法设计精妙之处 登录续期登录续期常见实现方式特…...

处理vxe-table 表尾数据是单独一个接口,表格tableData数据更新后,需要点击两下,表尾才是正确的
修改bug思路: 分别把 tabledata 和 表尾相关数据 console.log() 发现 更新数据先后顺序不对 settimeout延迟查询表格接口 ——测试可行 升级↑:async await 等接口返回后再开始下一个接口查询 ________________________________________________________…...