微信小程序蓝牙通信HC08

news2025/2/25 15:52:09

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

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

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

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.deviceid
  wx.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.coloradmin.cn/o/1473720.html

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

相关文章

C# 水排序 微信小游戏

来只 水排序谜题启发式搜索方法_水排序解法小程序-CSDN博客 大神的C语言转换成C# 语言&#xff0c;更多的请看原作者&#xff0c;这里直接贴C#代码 using System; using System.Collections.Generic; using System.Linq; using System.Text;namespace ConsoleApp2 {class Pro…

再见,Visual Basic——曾经风靡一时的编程语言

2020年3月&#xff0c;微软团队宣布了对Visual Basic&#xff08;VB&#xff09;的“终审判决”&#xff1a;不再进行开发或增加新功能。这意味着曾经风光无限的VB正式退出了历史舞台。 VB是微软推出的首款可视化编程软件&#xff0c;自1991年问世以来&#xff0c;便受到了广大…

vue插件——vue-print-nb 实现打印功能

之前写过好多关于打印的功能&#xff0c;通过windowprint组合键来实现打印。 今天遇到一个需求&#xff0c;就是使用vue插件来实现打印功能。 最终效果图如下&#xff1a; 下面介绍解决步骤&#xff1a; 解决步骤1&#xff1a;安装vue-print-nb插件——npm install vue-p…

2024年,“智慧城市”到底是持续拉跨还是稳步向前?

2024年“智慧城市”的发展趋势&#xff0c;总体上来看&#xff0c;是稳步向前的。 随着科技的不断发展&#xff0c;特别是物联网、云计算、大数据、人工智能等技术的日益成熟和普及&#xff0c;智慧城市的建设有了更为坚实的基础。这些技术不仅可以帮助城市实现更高效、智能的…

智慧公厕让社区生活更美好

随着科技的迅猛发展&#xff0c;城市管理、城市服务均使用科技化的手段进行升级改造&#xff0c;社区生活更美好赋予全新的智慧效能&#xff0c;其中智慧公厕也成为了城市环卫设施的新宠。智慧公厕以物联网、互联网、大数据、云计算、5G通信、自动化控制等技术为核心&#xff0…

Leetcoder Day23| 回溯part03:组合+分割

语言&#xff1a;Java/Go 39. 组合总和 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的所有不同组合 &#xff0c;并以列表形式返回。你可以按任意顺序返回这些组合。 candidates 中的同一个…

经典文献阅读之--Scale jump-aware pose graph...(尺度跳跃感知位姿图)

0. 简介 我们常说的位姿图松弛指的就是基于闭环检测的边进行位姿图优化。而位姿图松弛已成为SLAM中不可或缺的补充&#xff0c;能够在满足逐对相对变换约束的目标下&#xff0c;实现传感器参考帧的高效全局配准。这些约束可以通过增量运动估计或全局地点识别来给出。尽管后一种…

SpreadJS+vue3练手使用

SpreadJS的练手使用 // 首先在 package.json 这个文件里{"name": "app-admin","private": true,"version": "0.0.0","type": "module","scripts": {"dev": "vite",&quo…

el-submenu is-opened 展开/闭合;el-submenu is-opened保持一个子菜单的展开控制

写了个mes系统目录 点击子菜单展开后&#xff0c;上一级菜单没有默认关闭。主流后台管理系统大部分都是保持一个子菜单关闭状态、 问度娘无果后&#xff0c;查询官网&#xff0c;一个属性搞定。 unique-opened 是否只保持一个子菜单的展开 加在 <el-menu 组件上即可 完整代…

《品牌声量》开播啦!

11月7日&#xff0c;中国品牌人物高端访谈节目《品牌声量》将在中央新影老故事频道和央视频等平台陆续播出。 《品牌声量》栏目一档专注中国企业/行政区品牌建设的大型演播厅访谈节目。栏目以“汇聚品牌思想&#xff0c;提升品牌声量”为宗旨&#xff0c;采用“著名主持人权威…

平价护眼台灯推荐知乎,真人测评五大优质护眼台灯推荐

孩子的学习情况是父母最关心的话题之一&#xff0c;当然&#xff0c;孩子的学习环境也是十分重要的&#xff0c;只有保持明亮且舒适的光线环境才能让孩子获得高效率。因此护眼台灯的出现成为众多宝妈人群青睐的对象&#xff0c;都希望能借助它的力量来保护孩子的视力健康。然而…

python matplotlib-->柱形图

环境 python:python-3.12.0-amd64 包: matplotlib 3.8.2 #https://blog.csdn.net/aobulaien001/article/details/134611729 import pandas as pd import numpy as np import matplotlib.pyplot as plt import randomclass TestPltText():def __init__(self):super(TestPltTex…

Java 学习和实践笔记(22):package(包机制)、JDK常见的包、类的导入

前面学的类&#xff0c;每创建一个类&#xff0c;在电脑上就是创建了一个对应的类文件。而package 相当于文件夹对文件的管理作用。主要用于管理类、用于解决类的重名问题。这个含义很简单。因为实际的程序&#xff0c;类可能有成千上万个&#xff0c;这样就需要把不同功能的类…

国创证券:沪指止步八连阳 市场上涨核心逻辑未变

2月26日&#xff0c;商场全天震荡调整&#xff0c;三大指数均小幅跌落。到收盘&#xff0c;上证指数收盘报2977.02点&#xff0c;跌落0.93%&#xff0c;停步八连阳&#xff1b;深证成指报收9066.09点&#xff0c;跌落0.04%&#xff1b;创业板指报收1751.7点&#xff0c;跌落0.3…

一款.NET下 WPF UI框架介绍

WPF开源的UI框架有很多,如HandyControl、MahApps.Metro、Xceed Extended WPF Toolkit™、Modern UI for WPF (MUI)、Layui-WPF、MaterialDesignInXamlToolkit、等等,今天小编带大家认识一款比较常用的kaiyuanUI---WPF UI,这款ui框架美观现代化,用起来也超级方便, 界面展示…

Linux之定时任务①(实施必会!!!)

文章目录 常见脚本一、 什么是crond二、crond的使用场景一、apache服务器监控三、crond服务四、命令格式五、cron格式六、定时任务备份七、数据库定时备份八、使用shell脚本发送邮件 常见脚本 [rootlocalhost ~]# vim apacheSentry.sh#!/bin/bash # author: tt # description:…

IP 电话

1 IP 电话概述 IP 电话是在互联网上传送多媒体信息。 多个英文同义词&#xff1a; VoIP (Voice over IP) Internet Telephony VON (Voice On the Net) 1.1 狭义的和广义的 IP 电话 狭义的 IP 电话&#xff1a;指在 IP 网络上打电话。 广义的 IP 电话&#xff1a;不仅仅是…

NoSQL数据库介绍

目录 一、发展历史 二、什么是NoSQL&#xff1f; 三、为什么使用NoSQL&#xff1f; 四、NoSQL vs. RDBMS 五、NoSQL的四种类型 键值存储 文档存储 列式存储 图形存储 六、NoSQL的优缺点 七、NoSQL的特点 灵活的可扩展性 灵活的数据模型 与云计算紧密融合 大数据量…

算法为什么难=》领悟学习方法论

原文参考&#xff1a;为什么算法这么难&#xff1f;&#xff1f;&#xff1f;_过程_逻辑_时候 广大码农同学们大多都有个共识&#xff0c;认为算法是个硬骨头&#xff0c;很难啃&#xff0c;悲剧的是啃完了还未必有用——除了面试的时候。实际工程中一般都是用现成的模块&…

基于uniapp大学生社团活动管理系统python+java+node.js+php微信小程序

uni-app框架&#xff1a;使用Vue.js开发跨平台应用的前端框架&#xff0c;编写一套代码&#xff0c;可编译到Android、小程序等平台。 语言&#xff1a;pythonjavanode.jsphp均支持 框架支持:springboot/Ssm/thinkphp/django/flask/express均支持 运行软件:idea/eclipse/vscod…