鸿蒙蓝牙通信

https://developer.huawei.com/consumer/cn/doc/best-practices/bpta-bluetooth-low-energy

蓝牙权限

module.json5

{"module": {"requestPermissions": [{"name": "ohos.permission.ACCESS_BLUETOOTH","reason": "$string:app_name","usedScene": {"when": "inuse"}}],}
}

蓝牙管理工具类

  1. 申请蓝牙权限 (requestPermission)
    ○ 使用@kit.AbilityKit中的权限管理模块创建AtManager实例。
    ○ 请求用户授权ohos.permission.ACCESS_BLUETOOTH权限。
    ○ 如果用户拒绝,则跳转到设置界面让用户授权。
    ○ 异常时通过promptAction显示错误信息。
  2. 检查蓝牙权限 (checkPermission)
    ○ 获取当前应用的Bundle信息。
    ○ 检查是否已授予蓝牙权限。
    ○ 返回布尔值,表示权限是否被授予。
  3. 检查蓝牙开关状态 (checkOpen)
    ○ 使用@kit.ConnectivityKit的access模块获取蓝牙状态。
    ○ 返回布尔值,表示蓝牙是否开启(返回access.BluetoothState.STATE_ON即开启)。
  4. 扫描蓝牙设备 (scanDevice)
    ○ 启动蓝牙扫描,设置扫描间隔、扫描模式等参数。
    ○ 监听BLEDeviceFind事件,收集扫描到的设备并去重。
    ○ 通过回调函数将扫描结果数组返回给调用者。
    ○ 设置10秒超时自动停止扫描。
  5. 连接蓝牙设备 (connectDevice)
    ○ 根据设备ID创建GATT客户端设备实例。
    ○ 连接设备,并监听连接状态变化事件。
    ○ 当连接成功(状态变为STATE_CONNECTED)时,执行回调函数并显示成功提示,同时调用listenChange方法开启通知监听。
  6. 断开蓝牙连接 (disconnectDevice)
    ○ 如果存在连接,断开并关闭GATT客户端设备,重置引用。
    ○ 显示断开提示。
  7. 发送数据 (sendData)
    ○ 向已连接设备写入数据。
    ○ 查找UUID以0000AE30开头的服务,然后在该服务下查找UUID以0000AE10开头的特征。
    ○ 将JSON格式的数据编码为Uint8Array并写入特征值。
  8. 开启通知监听 (listenChange方法)
    ○ 在连接成功后调用,用于监听特征值变化(通知)。
    ○ 监听BLECharacteristicChange事件,接收到数据时解析为BlueData结构。
    ○ 如果数据命令为wifi,则显示接收到的数据。
    ○ 查找UUID以0000AE30开头的服务,然后查找UUID以0000AE04开头的特征,并启用该特征的通知。

blueManager.ets

import { abilityAccessCtrl, bundleManager } from '@kit.AbilityKit' // 导入能力访问控制相关模块
import { promptAction } from '@kit.ArkUI' // 导入用户界面交互模块
import { access, ble, constant } from '@kit.ConnectivityKit' // 导入蓝牙相关功能模块
import { util } from '@kit.ArkTS' // 导入文本编码工具
import { emitter } from '@kit.BasicServicesKit'// 1. 蓝牙开门 2. 配置设备 wifi 连网
interface BlueData {status?: 200 | 400 //  200 成功  400 失败msg?: string // 消息提示command?: 'open' | 'wifi' // 命令类型:开门或配置Wi-Fidata?: string[] // 例如配置Wi-Fi时的数据:[ssid, pwd]
}class BlueManager {// 1. 申请蓝牙权限async requestPermission() {try {const AtManager = abilityAccessCtrl.createAtManager() // 创建权限管理器实例const res =await AtManager.requestPermissionsFromUser(getContext(), ['ohos.permission.ACCESS_BLUETOOTH']) // 向用户请求蓝牙权限if (res.authResults[0] === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) { // 如果权限被拒绝await AtManager.requestPermissionOnSetting(getContext(), ['ohos.permission.ACCESS_BLUETOOTH']) // 在设置中请求权限}} catch (e) {promptAction.showToast({ message: JSON.stringify(e) }) // 显示错误信息}}// 2. 检测蓝牙权限checkPermission() {const bundleInfo =bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION) // 获取当前应用的bundle信息const AtManager = abilityAccessCtrl.createAtManager() // 创建权限管理器实例const grantStatus =AtManager.checkAccessTokenSync(bundleInfo.appInfo.accessTokenId, 'ohos.permission.ACCESS_BLUETOOTH') // 检查蓝牙权限状态return grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED // 返回权限是否被授予}// 3. 检测蓝牙开关checkOpen() {return access.getState() === access.BluetoothState.STATE_ON // 检查蓝牙是否开启}timeoutID: number = 0 // 扫描超时ID,用于控制扫描时长// 4. 扫描蓝牙设备scanDevice(callback: (res: ble.ScanResult[]) => void) {try {clearTimeout(this.timeoutID) // 清除之前的超时设置const scanList: ble.ScanResult[] = [] // 初始化扫描结果列表// 监听蓝牙设备发现事件ble.on('BLEDeviceFind', (res: ble.ScanResult[]) => {scanList.push(...res.filter(v => !scanList.some(vv => v.deviceId === vv.deviceId))) // 过滤重复设备并更新扫描列表callback(scanList) // 调用回调函数传递扫描结果})// 开始蓝牙扫描ble.startBLEScan(null, {interval: 500, // 设置扫描间隔时间为500毫秒dutyMode: ble.ScanDuty.SCAN_MODE_LOW_POWER, // 设置低功耗扫描模式matchMode: ble.MatchMode.MATCH_MODE_AGGRESSIVE // 设置积极匹配模式})this.timeoutID = setTimeout(() => {ble.stopBLEScan() // 停止扫描ble.off('BLEDeviceFind') // 停止监听蓝牙设备发现事件}, 10000)} catch (e) {promptAction.showToast({ message: JSON.stringify(e) }) // 显示错误信息}}currentClient: ble.GattClientDevice | null = null // 当前连接的蓝牙客户端设备// 5. 连接蓝牙设备connectDevice(deviceId: string, callback?: () => void) {if (deviceId) {this.currentClient = ble.createGattClientDevice(deviceId) // 创建Gatt客户端设备实例this.currentClient.connect() // 连接到蓝牙设备this.currentClient.on('BLEConnectionStateChange', async (res) => {if (res.state === constant.ProfileConnectionState.STATE_CONNECTED) { // 如果连接状态为已连接callback?.() // 调用回调函数promptAction.showToast({ message: '蓝牙连接成功' }) // 显示连接成功提示// 监听特征值变化this.listenChange()}})}}// 6. 断开蓝牙连接disconnectDevice() {if (this.currentClient) {this.currentClient.disconnect() // 断开蓝牙连接this.currentClient.close() // 关闭客户端设备this.currentClient = null // 重置当前客户端设备为nullpromptAction.showToast({ message: '蓝牙已断开' }) // 显示断开连接提示}}// 7. 发送数据async sendData(data: BlueData) {try {if (this.currentClient) {const list = await this.currentClient.getServices() // 获取蓝牙设备的所有服务const doorService = list.find(v => v.serviceUuid.startsWith('0000AE30')) // 查找门控服务const message = doorService?.characteristics.find(v => v.characteristicUuid.startsWith('0000AE10')) // 查找门控特征值const encoder = new util.TextEncoder() // 创建文本编码器const u8a = encoder.encodeInto(JSON.stringify(data)) // 将数据编码为Uint8Array// 写入特征值await this.currentClient.writeCharacteristicValue({serviceUuid: message?.serviceUuid,characteristicUuid: message?.characteristicUuid,characteristicValue: u8a.buffer,descriptors: [],}, ble.GattWriteType.WRITE)}} catch (e) {promptAction.showToast({ message: JSON.stringify(e) }) // 显示错误信息}}// 8. 监听特征值变化async listenChange() {if (this.currentClient) {// 注册特征值变化事件监听器this.currentClient?.on('BLECharacteristicChange', (res) => {// 创建文本解码器const decoder = util.TextDecoder.create()// 将特征值数据转换为Uint8Arrayconst buffer = new Uint8Array(res.characteristicValue)// 将Uint8Array解码为字符串并解析为JSON对象const result = JSON.parse(decoder.decodeToString(buffer)) as BlueData// 判断命令类型是否为wifiif (result.command === 'wifi') {// 触发wifi连接事件,并传递状态信息promptAction.showToast({ message: JSON.stringify(result, null, 2) })}})// 获取蓝牙设备的服务列表const serviceList = await this.currentClient?.getServices()// 查找门控服务const doorService = serviceList?.find(v => v.serviceUuid.startsWith('0000AE30'))// 查找门控特征值const message = doorService?.characteristics.find(v => v.characteristicUuid.startsWith('0000AE04'))// 启用特征值变化通知await this.currentClient?.setCharacteristicChangeNotification(message, true)}}
}export const blueManager = new BlueManager() // 导出BlueManager实例

蓝牙测试页面

Test.ets

import { blueManager } from '../utils/blueManager'
import { ble } from '@kit.ConnectivityKit'@Entry
@Component
struct Index {@State isPermission: boolean = false@State isOpen: boolean = false@State list: ble.ScanResult[] = []aboutToAppear(): void {this.checkPermission()this.checkOpen()}checkPermission() {this.isPermission = blueManager.checkPermission()}checkOpen() {this.isOpen = blueManager.checkOpen()}build() {Column({ space: 10 }) {Button('申请蓝牙权限').onClick(async () => {await blueManager.requestPermission()this.checkPermission()})Button('检测蓝牙权限是否开启:' + this.isPermission).onClick(() => {this.isPermission = blueManager.checkPermission()})Button('检测蓝牙开关是否开启:' + this.isOpen).onClick(() => {this.isOpen = blueManager.checkOpen()})Button('扫描蓝牙设备').onClick(() => {blueManager.scanDevice((res) => {this.list = res})})List({ space: 5 }) {ForEach(this.list, (item: ble.ScanResult) => {if (item.deviceName) {ListItem() {Row() {Text(item.deviceName)Button('连接蓝牙').size({ height: 30 }).onClick(() => {blueManager.connectDevice(item.deviceId)})}}}})}.width('100%').height(300)Button('断开蓝牙连接').onClick(() => {blueManager.disconnectDevice()})Button('发送数据-解锁开门').onClick(() => {blueManager.sendData({command: 'open'})})Button('发送数据-配网').onClick(() => {blueManager.sendData({command: 'wifi',data: ['Megasu_iPhone', 'nideshengri'] // 写入自己的 WIFI 和 密码})})}.height('100%').width('100%')}
}

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

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

相关文章

Java:Map

文章目录Map常用方法Map遍历的三种方法先获取Map集合的全部键,再通过遍历来找值Entry对象forEach结合lambda表达式Map 案例分析需求我的代码(不好)老师的代码(好)好在哪里另外集合分为Collection和MapMap常用方法 代码…

fastjson2 下划线字段转驼峰对象

在对接第三方或查询数据库时,返回的字段是下划线分隔的,而在业务中需要转成java对象,java对象的字段是驼峰的,使用fastjson2时,有两种方法可以实现: 比如数据格式是: {"item_id": &q…

【硬件】蓝牙音频协议

1. 无线音频传输的工作原理 在无线传输的过程中,音源设备首先将MP3、FLAC等音频文件还原为PCM格式。通过蓝牙音频编码转为蓝牙无线传输的文件,发送到音频设备段。将蓝牙无线传输的文件再次还原为PCM格式,之后转为模拟信号并放大,通…

【宇树科技:未来1-3年,机器人可流水线打螺丝】

在第三届中国国际供应链促进博览会上,宇树科技工作人员表示,未来1到3年内,机器人产品有望从单一工业化产品,发展至复合化工业场景,如机器人搬完箱子后,换个 “手” 就能在流水线上打螺丝。在3到10年内&…

Spring AI 1.0版本 + 千问大模型之 文本记忆对话

上篇文章,主要是简单讲解了一下文本对话的功能。由于模型不具备上下文记忆功能,只能一问一答。因此我们需要实现记忆对话功能,这样大模型回答信息才能够更加准确。 1、pom依赖 项目构建就不详细说了,大家可以参考上篇 文本对话 文…

测试学习之——Pytest Day2

一、Pytest配置框架Pytest的配置旨在改变其默认行为,以适应不同的测试需求和项目结构。理解其配置层级和常用参数,是高效使用Pytest的基础。1. 配置的意义与层级配置的本质在于提供一种机制,允许用户根据项目特点、团队规范或特定测试场景&am…

Go-Redis × RediSearch 全流程实践

1. 连接 Redis ctx : context.Background()rdb : redis.NewClient(&redis.Options{Addr: "localhost:6379",Password: "",DB: 0,Protocol: 2, // 推荐 RESP2// UnstableResp3: true, // 若要体验 RESP3 Raw* })2. 准备示例数据 u…

深入理解指针(指针篇2)

在指针篇1我们已经了解了整型指针,当然还有很多其他类型的指针,像字符指针、数组指针、函数指针等,他们都有他们的特别之处,让我们接着学习。1. 指针类型介绍和应用1.1 字符指针变量字符指针变量类型为char*,一般这样使…

Python+Selenium自动化爬取携程动态加载游记

1. 引言 在旅游行业数据分析、舆情监测或竞品研究中&#xff0c;获取携程等平台的游记数据具有重要价值。然而&#xff0c;携程的游记页面通常采用动态加载&#xff08;Ajax、JavaScript渲染&#xff09;&#xff0c;传统的**<font style"color:rgb(64, 64, 64);backg…

ESP8266服务器建立TCP连接失败AT+CIPSTART=“TCP“,“192.168.124.1“,8080 ERROR CLOSED

1.检查服务器端口8081是否开启监听2.检查路由项是否被防火墙拦截方法 1&#xff1a;使用 netsh查看防火墙规则​netsh advfirewall firewall show rule nameall dirout | findstr "8081"如果无输出&#xff0c;说明防火墙未针对该端口设置规则&#xff08;可能默认拦…

Linux 内存管理(2):了解内存回收机制

目录一、透明大页1.1 原理1.2 透明大页的三大优势1.3 透明大页控制接口详解1.4 使用场景与最佳实践1.5 问题排查与监控1.6 与传统大页的对比二、Linux伙伴系统水位机制详解2.1 三种核心水位详解2.2 水位在伙伴系统中的实现2.3 水位触发机制的实际行为2.4 水位关键操作接口2.5 水…

前端学习7:CSS过渡与动画--补间动画 (Transition) vs 关键帧动画 (Animation)

一、补间动画&#xff08;Tween Animation&#xff09;vs 关键帧动画&#xff08;Keyframe Animation&#xff09;概念对比表&#xff1a;补间动画 (Transition)关键帧动画 (Animation)定义元素从初始状态到结束状态的过渡效果通过定义多个关键帧控制动画的中间状态触发方式需要…

PyTorch 损失函数详解:从理论到实践

目录 一、损失函数的基本概念 二、常用损失函数及实现 1. 均方误差损失&#xff08;MSELoss&#xff09; 2. 平均绝对误差损失&#xff08;L1Loss/MAELoss&#xff09; 3. 交叉熵损失&#xff08;CrossEntropyLoss&#xff09; 4. 二元交叉熵损失&#xff08;BCELoss&…

MinIO深度解析:从核心特性到Spring Boot实战集成

在当今数据爆炸的时代&#xff0c;海量非结构化数据的存储与管理成为企业级应用的关键挑战。传统文件系统在TB级数据面前捉襟见肘&#xff0c;而昂贵的云存储服务又让中小企业望而却步。MinIO作为一款开源高性能对象存储解决方案&#xff0c;正以其独特的技术优势成为开发者的首…

腾讯云服务上下载docker以及使用Rabbitmq的流程

执行以下命令&#xff0c;添加 Docker 软件源并配置为腾讯云源。sudo yum-config-manager --add-repohttps://mirrors.cloud.tencent.com/docker-ce/linux/centos/docker-ce.repo sudo sed -i "s/download.docker.com/mirrors.tencentyun.com\/docker-ce/g" /etc/yu…

UE5 一些关于过场动画sequencer,轨道track的一些Python操作

删除多余的轨道 import unreal def execute():movie_scene_actors []sequence_assets []data 0.0# 获取编辑器实用工具库lib unreal.EditorUtilityLibrary()selected_assets lib.get_selected_assets()for asset in selected_assets:if asset.get_class() unreal.LevelS…

前端性能优化“核武器”:新一代图片格式(AVIF/WebP)与自动化优化流程实战

前端性能优化“核武器”&#xff1a;新一代图片格式(AVIF/WebP)与自动化优化流程实战 当你的页面加载时间超过3秒时&#xff0c;用户的跳出率会飙升到40%以上。而在所有的前端性能优化手段中&#xff0c;图片优化无疑是投入产出比最高的一环。一张未经优化的巨大图片&#xff0…

单元测试学习+AI辅助单测

标题单元测试衡量指标具体测试1、Resource2、MockBean3、Test4、Test模板5、单测示例H2数据库JSON1、使用方式AI辅助单测使用方法单元测试 单元测试一般指程序员在写好代码后&#xff0c;提交测试前&#xff0c;需要验证自己的代码是否可以正常工作&#xff0c;同时将自己的代…

Spring Cloud Gateway与Envoy Sidecar在微服务请求路由中的架构设计分享

Spring Cloud Gateway与Envoy Sidecar在微服务请求路由中的架构设计分享 在现代微服务架构中&#xff0c;请求路由层承担着流量分发、安全鉴权、流量控制等多重职责。传统的单一网关方案往往面临可扩展性和可维护性挑战。本文将从真实生产环境出发&#xff0c;分享如何结合Spri…

GitHub Pages+Jekyll 静态网站搭建(二)

GitHub PagesJekyll 静态网站搭建&#xff08;二&#xff09;GitHub PagesJekyll 静态网站搭建&#xff08;二内容简介搭建模板网站部署工作流程GitHub PagesJekyll 静态网站搭建&#xff08;二 内容简介 &#x1f6a9; Tech Contents 该文主要涉及Jekyll主题的下载与使用。Gi…