微信小程序蓝牙通信HC08

总结这两天研究的蓝牙串口。人话版资料不多,主要靠翻别人的仓库和文档。

  • 单片机部分,与蓝牙串口通信是通过串口。比我想的要简单,
  • 小程序部分,有非常多的服务和特征,而且人话版资料不多。

如果本文有什么问题,或仍有不理解的地方,可以私信交流。

HC08蓝牙串口

蓝牙部分已经由硬件厂商完成,对外只暴露了几根铁丝,与主机通信。
HC08与主机通信的协议是串口。
控制蓝牙串口模块,不需要轮询0011,只需要通过串口的方式,向从机HC08发送命令即可。
image.png
连接与断开交由外设完成。连接成功之后就是一个串口,对蓝牙通过串口发送的数据会透传到另一端,传入的数据也会被串口响应。
配置HC08,其实就是配置UART。也可以通过USB转TTL连接到电脑上。

配置串口

现在原理图中找到引脚所在的位置。
image.png
image.png
PA9和PA10也是USART的输入输出引脚。

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1|RCC_APB2Periph_TIM1,ENABLE);GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_SetBits(GPIOA,GPIO_Pin_10);

这款stm32已经集成了USART的硬件,只需要调用库函数初始化。
具体的参数含义在之前的文章中有介绍。

USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate=9600;
USART_InitStructure.USART_HardwareFlowControl=ENABLE;
USART_InitStructure.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART1,&USART_InitStructure);

硬件只是完成了读入读出操作,在收到串口发来的电平变化时,自动把1个字节的数据放入移位寄存器,将USART_IT_RXNE标志位置为高电平。
在设置为高电平时,触发中断,读出一个字节的数据,并清除中断标志。如果不清除,会导致无法接收下一个字节的数据。

USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_Init(&NVIC_InitStructure);

发送来的数据往往是多个字节的,如何判断消息是否结束?
通常的做法包括,约定好消息尾,比如当结尾为\r\n时标注当前消息已结束。
在本文中,采用的方法是:定时器中断。如果一段时间都没有新数据,那么表面当前数据已经结束。

void TIM1_UP_IRQHandler(){if(rxBufferPointer&&millis-lastTime>10){rxBufferPointer=0;isOK=1;}millis++;TIM_ClearITPendingBit(TIM1,TIM_IT_Update);
}

每毫秒触发一次定时器中断,存储一个定时值。
rxBufferPointer是指向下一个字节数据的指针。
当前消息结束时,该指针应复位为0,标志isOK置一。外界判断消息是否结束,就是通过查看isOK标志的状态。

void USART1_IRQHandler(){if(USART_GetFlagStatus(USART1,USART_IT_RXNE)==SET){lastTime=millis;rxBuffer[rxBufferPointer++]=USART_ReceiveData(USART1);rxSize=rxBufferPointer;USART_ClearITPendingBit(USART1,USART_IT_RXNE);}
}

如果消息没有结束,自动将当前接收的数据存入rxBufferPointer指向的下一个字节位置。
image.png
将extern修饰的变量放到头文件中,之后可以在导入这个头文件后直接读取。
数组大小256,指针为8位,最多指向256个内存地址。
传递的消息没有结束标志,为了标注结束位置,需要通过rxSize存储结束读取时的消息长度。
字符串比较需要用strcmp,而不能用简单的==
image.png
这一部分简单带过,配置蓝牙串口其实就是配置USART,因为stm32与HC08的通信方式就是串口。更详细的配置过程可以翻看我之前的博客。

微信小程序

通用项目搭建

有小程序搭建经验的,可以跳过这一部分。

创建一个微信小程序

没有AppID的可以去注册一个,配置成什么样子,几乎不影响之后开发。
我的选择是:不使用云服务、JS基础模板。
image.png

设置全局统一样式

把这段代码CV到app.wxss中:

page {font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica,Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei',sans-serif;
}

通过这段代码,实现在不同设备上一样的显示情况。
image.png

干掉用不到的页面

删除logs文件夹。
app.json中删除"pages/logs/logs"这一行。
(其实不删也不影响使用)
image.png

一个纯净的App()和Page()

删除app.jsindex.wxmlindex.wxssindex.js中的全部内容。
然后选择带有方块□的初始化模板。,初始化app.jsindex.js
image.png
本文一共配置了三个页面,另外两个页面的初始化同上。

"pages": ["pages/index/index","pages/BLE/Services/Services","pages/BLE/control/control"
],

设备扫描界面

这一步的目标是,在index页面,显示扫描到的蓝牙设备。
根据微信官方的要求,流程为:

  • 开启蓝牙适配器
  • 开启扫描

蓝牙的可用状态和扫描状态可以在wx.onBluetoothAdapterStateChange()回调中获取。
扫描的设备可以在wx.onBluetoothDeviceFound()回调中获取。
为了方便调试:

  • 在App.js中,挂载全局工具方法fail
  • onBluetoothAdapterStateChange的通知结果打印在页面上。

app.js挂载全局的fail处理方法

App({fail:(res)=>{wx.showToast({title: res.errMsg,icon:"none"})}
})

index.js响应适配器状态改变事件

这里把onBluetoothAdapterStateChange单独封装,挂载在this下。是为了使代码结构更清晰,避免在onLoad()下出现层层嵌套。

Page({data: {available: false,discovering: false},onLoad: function (options) {this.onBluetoothAdapterStateChange();},onBluetoothAdapterStateChange() {wx.onBluetoothAdapterStateChange(({available,discovering}) => {this.setData({available,discovering})})}
})

在前端显示部分值,方便后续调试。
image.png
开启适配器之后,需要开始扫描。在扫描之前,先设置设备发现后的处理函数。
这里的处理方案是:把发现的设备添加到数组中。如果报告了重复的设备,那么需要通过数组的.splice()方法,替换为新的设备。
为了方便判断是否重复,可以创建一个数组_deviceIds挂载在this下,存储设备的唯一标识deviceId
为了简化代码结构,避免层层嵌套,将代码实现单独封装,挂载在this下。
image.png
开始搜索的点击事件为onTapDiscover

<button bind:tap="onTapDiscover">{{discovering?"结束搜索":"开始搜索"}}</button>

这一事件要根据当前情况执行不同的策略:

  • 如果未打开适配器,那么开启适配器,并在success回调中搜索蓝牙设备。
  • 如果已打开适配器,但没有处于扫描状态,那么直接开启扫描。
  • 如果正在扫描,那么关闭扫描。

对于前两种情况,在执行前需要清空已扫描到的设备列表,以保证扫描到的设备都是最新有效的。

onTapDiscover() {if (this.data.discovering) {wx.stopBluetoothDevicesDiscovery();} else {this.setData({devices: []})this._deviceIds = []if (this.data.available) {wx.startBluetoothDevicesDiscovery({allowDuplicatesKey: true})} else {this.openBluetoothAdapter();}}
},
openBluetoothAdapter() {wx.openBluetoothAdapter({success: () => {wx.startBluetoothDevicesDiscovery({allowDuplicatesKey: true})},fail: getApp().fail})
}

对于前端界面,这不是本文的重点,粗略带过,具体的wxss设置可翻代码,根据需求自定义。
image.png
通过onTapDevice函数,处理连接事件,通过data-deviceId传入。通过deviceId获取服务列表。
在成功连接之后,应停止扫描,关闭这一耗费资源的操作。
服务列表操作在新的页面完成。

onTapDevice(e){let deviceId=e.currentTarget.dataset.deviceidwx.showModal({title: 'Connected or not',content: deviceId,success (res) {if (res.confirm) {getApp().Toast("connecting");wx.createBLEConnection({deviceId,success:()=>{wx.stopBluetoothDevicesDiscovery();wx.navigateTo({url: `/pages/BLE/Services/Services?deviceId=${deviceId}`})}})}}})
}

服务列表界面

image.png
这一步的操作比较少,所以可以直接将获取服务列表的方法定义在onLoad里。
如果返回上一页面,意味着中断连接。所以需要在onUnload方法中断开当前连接。
onUnload方法会在当页面的生命周期结束时自动执行。
具体的代码将在之后的源代码中呈现。本项目未使用第三方组件库,为原生的微信小程序,兼容大多数环境。

控制界面

这是本文中最复杂的部分。(理解之后不复杂)
image.png
在一开始,我扫描到多个服务,每个服务又有多个特征,对此不知道该怎么做。
尽管有些特征携带了notify属性,但在尝试notify的时候还是报错。或者read、write没有任何响应。
目前的解决方案是,遍历服务特征,尝试read/write/notify,在success回调中设置服务特征为当前成功的这个。
目前在HC08上可以正常通信。
我之前的理解是,在一个特征上同时进行read/write/notify。但实际可能是分散在多个特征上的,共同完成同一个服务。
image.png
为了简化代码结构,采用Command命令模式,每个按钮执行的是同一个方法,只是传入的命令参数不同。
HC08发送来的数据在onBLECharacteristicValueChange中处理。而不是read,目前read是干什么的我也不清除。
发送来的是ArrayBuffer,发出去的时候也要转换成ArrayBuffer,需要实现:

  • ab2str
  • ab2hex
  • str2ab

str就是字符串,hex就是十六进制,最终表现形式也是字符串,ab是ArrayBuffer,这种数据流传输的形式。2就是to,为了省事,读音相同,就简写作了2
具体过程可翻看源代码。
代码仓库:https://github.com/WuShFeng/BLE

年轻人的第一辆新能源四驱

本文正值开学季,中断了很多次,有好多想写的都忘了。想起来的时候再补充。

参考

  • HC-08V3.1.pdf
  • https://developers.weixin.qq.com/miniprogram/dev/framework/device/bluetooth.html
  • https://github.com/zengwangfa/BluetoothControl

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

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

相关文章

AI绘画工具合集,让想象触手可及!

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

【目标检测新SOTA!v7 v4作者新作!】YOLO v9 思路设计 + 全流程优化 + 手把手训练自己数据

YOLO v9 思路复现 全流程优化 手把手训练自己数据 提出背景&#xff1a;深层网络的 信息丢失、梯度流偏差YOLO v9 设计逻辑可编程梯度信息&#xff08;PGI&#xff09;&#xff1a;使用PGI改善训练过程广义高效层聚合网络&#xff08;GELAN&#xff09;&#xff1a;使用GELAN…

华为数通方向HCIP-DataCom H12-821题库(单选题:481-500)

第481题 以下关于基于SD-WAN思想的EVPN互联方案的描述,错误的是哪一项? A、通过部署独立的控制面,将网络转发和控制进行了分离,从而实现了网络控制的集中化 B、通过对WAN网络抽象和建模,将上层网络业务和底层网络具体实现架构进行解耦,从而实现网络自动化 C、通过集中的…

四、分类算法 - 决策树

目录 1、认识决策树 2、决策树分类原理详解 3、信息论基础 3.1 信息 3.2 信息的衡量 - 信息量 - 信息熵 3.3 决策树划分的依据 - 信息增益 3.4 案例 4、决策树API 5、案例&#xff1a;用决策树对鸢尾花进行分类 6、决策树可视化 7、总结 8、案例&#xff1a;泰坦尼…

深度学习手写字符识别:推理过程

说明 本篇博客主要是跟着B站中国计量大学杨老师的视频实战深度学习手写字符识别。 第一个深度学习实例手写字符识别 深度学习环境配置 可以参考下篇博客&#xff0c;网上也有很多教程&#xff0c;很容易搭建好深度学习的环境。 Windows11搭建GPU版本PyTorch环境详细过程 数…

stable diffusion学习笔记 手部修复

图片手部修复原理 某张图片在生成后&#xff0c;仅有手部表现不符合预期&#xff08;多指&#xff0c;畸形等&#xff09;。这种情况下我们通常使用【局部重绘】的方式对该图片的手部进行【图生图】操作&#xff0c;重新绘制手部区域。 但是仅采用重绘的方式也很难保证生成的…

python爬虫实战:获取电子邮件和联系人信息

引言 在数字时代&#xff0c;电子邮件和联系人信息成为了许多企业和个人重要的资源&#xff0c;在本文中&#xff0c;我们将探讨如何使用Python爬虫从网页中提取电子邮件和联系人信息&#xff0c;并附上示例代码。 目录 引言 二、准备工作 你可以使用以下命令来安装这些库&a…

将文件从windows传入到ubuntu

实现效果图 2.方法&#xff1a; 2.1打开 Ubuntu 的终端窗口&#xff0c;然后执行如下命令来安装 FTP 服务 输入&#xff1a;sudo apt-get install vsftpd 等待软件自动安装&#xff0c;安装完成以后使用如下 VI 命令打开/etc/vsftpd.conf&#xff0c;命令如下&#xff1a;su…

Git Windows安装教程

Git简介 Git是目前世界上最先进的分布式版本控制系统。它的工作原理 / 流程如下&#xff1a; [ Workspace:工作区 Index / Stage:暂存区 Repository:仓库区&#xff08;或本地仓库&#xff09; Remote:远程仓库 ] Git的下载 去 Git 官网下载对应系统的软件了&#xff0c;下…

用39块钱的全志V851se视觉开发板做了个小相机,还可以物品识别、自动追焦!

用39块钱的V851se视觉开发板做了个小相机。 可以进行物品识别、自动追焦&#xff01; 这个超低成本的小相机是在V851se上移植使用全志在线开源版本的Tina Linux与OpenCV框架开启摄像头拍照捕获视频&#xff0c;并结合NPU实现Mobilenet v2目标分类识别以及运动追踪等功能…并最终…

dolphinscheduler集群部署教程

文章目录 前言一、架构规划二、配置集群免密登录1. 配置root用户集群免密登录1.1 hadoop101节点操作1.2 hadoop102节点操作1.3 hadoop103节点操作 2. 创建用户2.1 hadoop101节点操作2.2 hadoop102节点操作2.3 hadoop103节点操作 三、安装准备1. 安装条件2. 安装jdk3. 安装MySQL…

“智能语音指令解析“ 基于NLP与语音识别的工单关键信息提取

“智能语音指令解析“ 基于NLP与语音识别的工单关键信息提取 1. 背景介绍1.1 场景痛点1.2 方案选型 2. 准备开发环境3. PaddleSpeech 语音识别快速使用4. PaddleNLP 信息抽取快速使用5. 语音工单信息抽取核心功能实现6. 语音工单信息抽取网页应用6.1 网页前端6.2 网页后端6.3 a…

C# Onnx yolov8-obb 旋转目标检测

目录 效果 模型信息 项目 代码 下载 C# Onnx Yolov8-OBB 旋转目标检测 效果 模型信息 Model Properties ------------------------- date&#xff1a;2024-02-26T08:38:44.171849 description&#xff1a;Ultralytics YOLOv8s-obb model trained on runs/DOTAv1.0-ms.ya…

解决:“出现问题,Outlook 无法设置你的账户”

原文&#xff1a;https://blog.iyatt.com/?p14213 1 问题描述 Office 专业版 2024 预览版 在 Outlook 输入邮箱后无法进一步配置登录信息&#xff08;腾讯企业邮箱 Exchange 登录&#xff09; 2 解决方法 通过控制面板里的邮箱设置可以正常添加登录&#xff0c;而且能…

【总第49篇】2.3深度学习开发任务实例(2)机器学习和深度学习的对比【大厂AI课学习笔记】

机器学习和深度学习都是用于图片分类任务的强大工具&#xff0c;但它们采用的方法和原理有所不同。下面我将分别解释这两种技术是如何应用于图片分类的&#xff0c;并着重讨论深度学习中的卷积概念。 机器学习在图片分类中的应用 传统的机器学习方法在进行图片分类时&#xf…

用GGUF和Llama .cpp量化Llama模型

用GGUF和Llama .cpp量化Llama模型 什么是GGML如何用GGML量化llm使用GGML进行量化NF4 vs. GGML vs. GPTQ结论 由于大型语言模型&#xff08;LLMS&#xff09;的庞大规模&#xff0c;量化已成为有效运行它们的必要技术。通过降低其权重的精度&#xff0c;您可以节省内存并加快推理…

多模态表征—CLIP及中文版Chinese-CLIP:理论讲解、代码微调与论文阅读

我之前一直在使用CLIP/Chinese-CLIP&#xff0c;但并未进行过系统的疏导。这次正好可以详细解释一下。相比于CLIP模型&#xff0c;Chinese-CLIP更适合我们的应用和微调&#xff0c;因为原始的CLIP模型只支持英文&#xff0c;对于我们的中文应用来说不够友好。Chinese-CLIP很好地…

Linux使用Docker部署在线协作白板WBO并结合内网穿透发布公网远程访问

文章目录 前言1. 部署WBO白板2. 本地访问WBO白板3. Linux 安装cpolar4. 配置WBO公网访问地址5. 公网远程访问WBO白板6. 固定WBO白板公网地址 前言 WBO在线协作白板是一个自由和开源的在线协作白板&#xff0c;允许多个用户同时在一个虚拟的大型白板上画图。该白板对所有线上用…

【Vue3】插槽使用和animate使用

插槽使用 插槽slot匿名插槽具名插槽插槽作用域简写 动态插槽transition动画组件自定义过渡class类名如何使用animate动画库组件动画生命周期appear transition- group过渡列表 插槽slot 插槽就是子组件中提供给父组件使用的一个占位符父组件可以在这个占位符智能填充任何模板代…

SpringBoot -【BeanPostProcessor】基础使用及应用场景

BeanPostProcessor应用与优化 1. 引言 在现代软件开发中&#xff0c;企业开发面临着越来越复杂的系统架构和业务需求。随着项目规模的扩大和技术栈的增多&#xff0c;需要更高效的工具来应对这些挑战&#xff0c;并确保代码的可维护性和扩展性。 在这样的背景下&#xff0c;Be…