微信小程序 蓝牙通讯
客户的需求如下:通过微信小程序控制蓝牙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产品进行了对比分析,涵…...
【笔记】自动驾驶预测与决策规划_Part4_时空联合规划
文章目录 0. 前言1. 时空联合规划的基本概念1.1 时空分离方法1.2 时空联合方法 2.基于搜索的时空联合规划 (Hybrid A* )2.1 基于Hybrid A* 的时空联合规划建模2.2 构建三维时空联合地图2.3 基于Hybrid A*的时空节点扩展2.4 Hybrid A* :时空节…...
Linux指令收集
文件和目录操作 ls: 列出目录内容。 -l 显示详细信息。-a 显示隐藏文件(以.开头的文件)。cd: 改变当前工作目录。 cd ~ 返回主目录。cd .. 上移一级目录。pwd: 显示当前工作目录。mkdir: 创建目录。 mkdir -p path/to/directory 创建多级目录。rmdir: 删…...
《C++并发编程实战》笔记(五)
五、内存模型和原子操作 5.1 C中的标准原子类型 原子操作是不可分割的操作,它或者完全做好,或者完全没做。 标准原子类型的定义在头文件<atomic>中,类模板std::atomic<T>接受各种类型的模板实参,从而创建该类型对应…...
在Python中实现多目标优化问题(5)
在Python中实现多目标优化问题 在Python中实现多目标优化,除了传统的进化算法(如NSGA-II、MOEA/D)和机器学习辅助的方法之外,还有一些新的方法和技术。以下是一些较新的或较少被提及的方法: 1. 基于梯度的多目标优化…...
【Linux:共享内存】
共享内存的概念: 操作系统通过页表将共享内存的起始虚拟地址映射到当前进程的地址空间中共享内存是由需要通信的双方进程之一来创建但该资源并不属于创建它的进程,而属于操作系统 共享内存可以在系统中存在多份,供不同个数,不同进…...
今年Java回暖了吗
今年回暖了吗 仅结合师兄和同学的情况 BG 大多双非本 少部分211本 985硕 去年十月一之前 基本转正都失败 十月一之前0 offer 只有很少的人拿到美团 今年十月一之前 有HC的基本都转正了(美团、字节等),目前没有HC的说也有机会(…...
a = Sw,其中a和w是向量,S是矩阵,求w等于什么?w可以写成关于a和S的什么样子的公式
给定公式: a S w a S w aSw 其中: a a a 是已知向量, S S S 是已知矩阵, w w w 是未知向量。 我们的目标是求解 w w w,即将 w w w 表示为 a a a 和 S S S 的函数。 情况 1:矩阵 S S S 可逆 如果矩…...
多线程事务管理:Spring Boot 实现全局事务回滚
多线程事务管理:Spring Boot 实现全局事务回滚 在日常开发中,我们常常会遇到需要在多线程环境下进行数据库操作的场景。这类操作的挑战在于如何保证多个线程中的数据库操作要么一起成功,要么一起失败,即 事务的原子性。尤其是在多个线程并发执行的情况下,确保事务的一致性…...
Vue3 中集成海康 H5 监控视频播放功能
🌈个人主页:前端青山 🔥系列专栏:Vue篇 🔖人终将被年少不可得之物困其一生 依旧青山,本期给大家带来Vuet篇专栏内容:Vue-集成海康 H5 监控视频播放功能 目录 一、引言 二、环境搭建 三、代码解析 子组件部分 1.…...
Linux: eBPF: libbpf-bootstrap-master 编译
文章目录 简介编译运行展示输出展示:简介 这个是使用libbpf的一个例子; 编译 如果是一个可以联网的机器,这个libbpf-bootstrap的编译就方便了,完全是自动化的下载依赖文件;如果没有,就只能自己准备这些个软件。 需要:libbpf-static; [root@RH8-LCP c]# makeLIB …...
重庆建设工程信息网官网安全监督/郑州seo阿伟
http://blog.csdn.net/ouyangtianhan/article/details/7835041 http://stackoverflow.com/questions/16071503/how-to-tell-when-uitableview-has-completed-reloaddata UITableView reloadData的正确方法。 相信很多人会遇到这种情况,当tableView正在滚动的时候…...
wordpress相对路径设置/高端网站建设哪个好
Vue JSX Vue JSX 语法 参考文章:在Vue3.0中使用JSX的简单入门_咲奈的博客-CSDN博客_vue3使用jsxVue3 使用 jsx_沿着路走到底的博客-CSDN博客_vue3 使用jsx在vue3中使用jsx语法_艳子的皮皮猪的博客-CSDN博客_vue3使用jsx【组件库从0到1】Vite Vue3 TSX开发指南…...
手机单页网站生成系统/广告门
有天上飞的概念,就要有落地的实现概念十遍不如代码一遍,朋友,希望你把文中所有的代码案例都敲一遍先赞后看,养成习惯SpringBoot 图文教程系列文章目录前言问个问题:通过Java代码怎么发送Http请求,请求另一个…...
东莞高端网站建设多少钱/全球网络营销公司排行榜
七夕情——代腾飞 2009年8月26日 于成都七夕期,七夕许只为期许着这等待了一年的再遇七夕遇,七夕聚只为这一刻诉说衷肠的短暂相聚! 牛郎织女谱写着千百年来爱的传奇今晚,我守候在葡萄架下只为向你们学习爱的私语只是啊࿰…...
外海赌博如何做网站的推广/职业教育培训机构排名前十
求助:如何实现不关机SIM卡插拔(不用PUSH式卡座)kenny_nap Post at 2010/12/14 20:27:37有一个平板项目,SIM卡座是侧边,使用频率很高,push式卡座寿命普遍较短,准备使用普通抽屉式卡座,请教各位有什么好的办法…...
wordpress同步qq空间/宁波seo博客
中国人血白蛋白行业发展分析与投资战略研究报告2022-2028年 详情内容请咨询鸿晟信合研究院! 【全新修订】:2022年2月 【撰写单位】:鸿晟信合研究研究 【报告目录】 第1章:人血白蛋白行业界定及数据统计标准说明 1.1 人血白蛋白的…...