移动端开发进阶之蓝牙通讯(一)
移动端开发进阶之蓝牙通讯(一)
移动端进阶之蓝牙通讯需要综合考虑蓝牙版本选择、协议栈使用、服务匹配、设备连接、安全性和硬件支持等方面。
一、蓝牙版本选择
根据实际需求和应用场景选择合适的蓝牙版本;
1.0,1M/s。
2.0+EDR,2-3M/s,增加了简易配对的功能。
3.0+HS,24M/s-Generic Alternate MAC/PHY(AMP),支持802.11高速数据传输。
4.0,引入低功耗蓝牙BLE,适用于不需占用太多带宽的设备连接。
5.0,提升BLE的性能,增加了广播容量,扩大通信距离和速度。
5.1,增加定向定位和角度测量的功能。
5.2,增加多音频和低复杂度通信编码(LC3)的功能,可实现更好的音质和更低的功耗。
5.3,完善了BLE的周期性广播、连接更新、频道分级等功能,提高通讯效率、降低功耗并提高无线共存性。移除了AMP,不再支持24M/s。
5.4,新增带响应的周期性广播,带加密的EIR、广播数据加密、Host支持Advertising Coding选择等。
选择蓝牙版本时,主要应考虑以下几点:
蓝牙应用的需求:不同的蓝牙应用需要不同的蓝牙版本。例如,对于需要传输大量数据的应用,应选择蓝牙5.0或更高版本,因为它们提供了更高的传输速率和更低的功耗。而对于需要音频传输的应用,蓝牙4.0或更高版本即可满足需求。
设备兼容性:不同版本的蓝牙具有不同的兼容性。如果设备需要与其他蓝牙设备进行通信,应选择与这些设备兼容的蓝牙版本。
成本:不同版本的蓝牙模块成本不同。蓝牙5.0模块的成本高于蓝牙4.0模块,因此在预算有限的情况下,可以选择更低版本的蓝牙。
开发难度:不同版本的蓝牙模块开发难度不同。蓝牙5.0模块的开发难度高于蓝牙4.0模块,因此对于开发能力有限的企业或个人,可以选择更低版本的蓝牙。
二、蓝牙协议栈
蓝牙协议栈(Bluetooth Protocol Stack)是实现蓝牙通信的一组协议的集合,它定义了蓝牙设备之间如何进行数据传输和通信;
蓝牙协议栈包括多个层次,每个层次都有不同的功能和职责;
常见的蓝牙协议栈包括BlueCore、CSR8670/CSR8675和RTKBT11等。
物理层(Physical Layer):物理层负责无线信号的传输,包括信号的调制、解调、频率选择等。
数据链路层(Link Layer):数据链路层负责数据的打包和解包,以及设备间的连接和断开。它还负责处理数据传输中的错误检测和纠正。
网络层(Network Layer):网络层负责设备的寻址和连接管理,包括设备间的安全连接和数据传输。
传输层(Transport Layer):传输层负责数据的分段和重组,以及数据传输的可靠性和流量控制。
应用层(Application Layer):应用层负责提供各种蓝牙服务,如音频传输、文件传输、设备控制等。
BLUECORE(蓝牙核心动力)
这是长安汽车集团推出的高效节能环保动力总成(发动机+变速箱)的解决方案。BLUECORE 涵盖了长安汽车自主研发的多种技术,如TEi、GDi、D-VVT、TC、新能源等,致力于提供纯净、清新的动力系统。
CSR8670/CSR8675
这是CSR(Cambridge Silicon Radio)公司推出的蓝牙音频解决方案芯片。其中,CSR8670是CSR 86xx系列中的高级闪存产品,旨在提供高质量的无线音频性能并支持高差异优质无线音频产品的开发。而CSR8675是一款先进的蓝牙音频解决方案芯片,具有出色的音质表现,采用了aptX高清音频编解码器技术,可以在传输过程中保持音频的高质量。
RTKBT11
这是一款蓝牙模块,符合蓝牙5.0标准,支持经典蓝牙和低功耗蓝牙双模工作模式。该模块具有高性能、低功耗、易于集成等特点,适用于各种需要蓝牙连接的应用场景。
移动端开发通常使用的是Bluetooth Low Energy(BLE),常用的支持BLE的协议栈包括:
BlueZ:BlueZ是Linux操作系统下的开源蓝牙协议栈,支持蓝牙经典(Classic)和低功耗(Low Energy)两种模式。它是Linux下开发蓝牙应用程序的主要协议栈之一。
Windows Bluetooth API:Windows操作系统提供了对蓝牙的支持,包括对BLE的支持。Windows Bluetooth API提供了用于开发蓝牙应用程序的接口和协议栈。
Nordic Semiconductor SDK:Nordic Semiconductor是一家专注于蓝牙低功耗技术的芯片厂商,提供了针对其蓝牙SoC的软件开发套件(SDK),其中包括了完整的蓝牙协议栈和工具链,支持BLE和其他蓝牙技术。
CSR BLE SDK:CSR(Cambridge Silicon Radio)是一家提供蓝牙技术的芯片厂商,也提供了针对其蓝牙SoC的软件开发套件,支持BLE和其他蓝牙技术。
其中使用最多的就是CSR提供的CSR8670/CSR8675。
三、蓝牙服务
移动端需要提供或发现蓝牙服务,并进行服务匹配;
常见的蓝牙服务包括音频服务、文件传输服务、网络服务等;
需要了解并实现这些服务的通讯协议和逻辑。
蓝牙音频服务
在蓝牙音频服务中,主要有以下几个规范和协议:
A2DP(Advanced Audio Distribution Profile):这是一个专注于音频流传输的规范。它允许传输立体声音频信号,是蓝牙音频传输中的重要组成部分。典型的应用场景是将音乐内容从立体声音乐播放器流式传输到耳机或扬声器。音频数据以适当的格式压缩,以提高效率并使用有限的带宽。
HFP(Hands-free Profile):这是一个基于SCO(Synchronous Connection Oriented)链路的规范,用于双向传输通话语音。它让蓝牙设备可以控制电话,如接听、挂断、拒接、语音拨号等。
AVRCP(Audio/Video Remote Control Profile):这是一个用于远程控制音频和视频播放的规范。通过这个协议,用户可以通过无线方式控制音乐播放器和其他音频设备的基本操作,例如播放、暂停、下一曲和音量控制等。
aptX、aptX-HD和aptX voice:这些都是用于提升蓝牙音频质量的协议。它们通过不同的参数(如16bit/48k、24bit/48k和16bit/32k)提供更高质量的音频传输。
ANC(Active Noise Cancellation):这是一种主动降噪功能,通过消除外部噪音来提高音频质量。
TWS(True Wireless Stereo):这是一种蓝牙无线耳机技术,允许两个耳机在没有有线连接的情况下进行通信,从而提供真正的立体声体验。
四、蓝牙设备连接
移动端需要建立与蓝牙设备的连接,并进行数据传输。需要了解并实现连接的建立、管理和断开逻辑,以及数据传输的协议和流程。
例如在iOS开发中建立蓝牙连接的步骤如下:
- 导入蓝牙框架:在项目中使用蓝牙功能,需要导入CoreBluetooth框架。
- 创建CBCentralManager实例:CBCentralManager是iOS中用于管理蓝牙的中心管理者,负责扫描、连接和与外围设备的通信。
- 配置CBCentralManagerDelegate和CBPeripheralDelegate协议:需要遵守这两个协议,以便在蓝牙连接过程中处理相关事件。
- 初始化CBCentralManager:创建一个CBCentralManager实例,并传入self来遵守CBCentralManagerDelegate协议。
- 启动CBCentralManager:调用CBCentralManager的startScan方法开始扫描附近的蓝牙设备。
- 发现外围设备:当扫描到周围有可连接的蓝牙设备时,CBCentralManager会调用其代理方法并传入CBPeripheral对象。
- 连接外围设备:使用CBPeripheral对象的connect方法来连接指定的外围设备。
- 获取外围设备的服务:连接成功后,可以通过CBPeripheral对象获取外围设备提供的服务。
- 获取服务的特征:从服务中获取特征,这些特征用于读写数据。
- 读取和写入数据:通过特征值进行数据的读写操作。
五、蓝牙安全
移动端需要考虑蓝牙通讯的安全性。由于蓝牙通讯是一种无线通讯方式,容易被截获或干扰,因此需要进行安全设置和保护。需要了解并实现安全设置和保护的逻辑,如加密、认证等。
加解密:
#include <iostream>
#include <string>
#include <cryptopp/aes.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h> std::string EncryptAES(const std::string& plainText, const std::string& key) { CryptoPP::AES::Encryption aesEncryption(key.begin(), key.end()); CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, key.begin()); std::string cipherText; CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(cipherText)); stfEncryptor.Put(reinterpret_cast<const unsigned char*>(plainText.c_str()), plainText.length() + 1); stfEncryptor.MessageEnd(); return cipherText;
} std::string DecryptAES(const std::string& cipherText, const std::string& key) { CryptoPP::AES::Decryption aesDecryption(key.begin(), key.end()); CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, key.begin()); std::string plainText; CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(plainText)); stfDecryptor.Put(reinterpret_cast<const unsigned char*>(cipherText.c_str()), cipherText.size()); stfDecryptor.MessageEnd(); return plainText;
}
#include <iostream>
#include <string>
#include <cryptopp/sha.h>
#include <cryptopp/hex.h>
#include <cryptopp/modes.h>
#include <cryptopp/filters.h> std::string sha256(const std::string& data) { CryptoPP::SHA256 sha; byte digest[CryptoPP::SHA256::DIGESTSIZE]; sha.CalculateDigest(digest, reinterpret_cast<const byte*>(data.c_str()), data.size()); std::string hash; CryptoPP::HexEncoder encoder; encoder.Attach(new CryptoPP::StringSink(hash)); encoder.Put(digest, sizeof(digest)); encoder.MessageEnd(); return hash;
} std::string encryptSha(const std::string& plainText, const std::string& key) { CryptoPP::AES::Encryption aesEncryption(key.begin(), key.end()); CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption(aesEncryption, key.begin()); std::string cipherText; CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, new CryptoPP::StringSink(cipherText)); stfEncryptor.Put(reinterpret_cast<const unsigned char*>(plainText.c_str()), plainText.size()); stfEncryptor.MessageEnd(); std::string encryptedHash = sha256(cipherText); return encryptedHash;
} std::string decryptSha(const std::string& cipherText, const std::string& key) { std::string decryptedText = cipherText; // Assuming the SHA256 hash is the only encrypted data here. CryptoPP::AES::Decryption aesDecryption(key.begin(), key.end()); CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption(aesDecryption, key.begin()); CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, new CryptoPP::StringSink(decryptedText)); stfDecryptor.Put(reinterpret_cast<const unsigned char*>(decryptedText.c_str()), decryptedText.size()); stfDecryptor.MessageEnd(); std::string decryptedHash = sha256(decryptedText); // Decrypted text should match the original hash. return decryptedHash;
}
校验:
#include <bluetooth/bluetooth.h> uint16_t crc16(const uint8_t *data, size_t len) { uint16_t crc = 0xFFFF; const uint8_t *ptr = data; while (len--) { crc ^= (*ptr++) << 8; for (int i = 0; i < 8; i++) { if (crc & 0x8000) { crc = (crc << 1) ^ 0x1021; // Polynomial 0x1021 (CRC-CCITT) } else { crc <<= 1; } } } return crc & 0xFFFF;
}
六、蓝牙硬件支持
移动端需要使用合适的蓝牙硬件模块来支持蓝牙通讯。常见的蓝牙硬件模块包括CSR、Broadcom、Freescale等厂商提供的模块。需要了解并选择适合自己应用的蓝牙硬件模块。
相关文章:

移动端开发进阶之蓝牙通讯(一)
移动端开发进阶之蓝牙通讯(一) 移动端进阶之蓝牙通讯需要综合考虑蓝牙版本选择、协议栈使用、服务匹配、设备连接、安全性和硬件支持等方面。 一、蓝牙版本选择 根据实际需求和应用场景选择合适的蓝牙版本; 1.0,1M/s。 2.0EDR…...

一个完整的流程表单流转
1.写在前面 一个完整的流程表单审批(起表单-->各环节审批-->回退-->重新审批-->完成),前端由Vue2jsElement UI升级为Vue3tsElement Plus,后端流程框架使用Flowable,项目参考了ruoyi-vue-pro(https://gite…...

2024杭州国际智慧城市,人工智能,安防展览会(杭州智博会)
在智能化浪潮的冲击下,我们的生活与环境正在经历一场深刻的变革。这是一场前所未有的技术革命,它以前所未有的速度和广度,改变着我们的生活方式、工作方式、思维方式和社会结构。在这场变革中,有的人选择激流勇进,拥抱…...

编程笔记 html5cssjs 031 HTML视频
编程笔记 html5&css&js 031 HTML视频 一、<video>: 视频元素二、属性三、事件四、嵌入视频页面五、练习小结 视频应用广泛,当前的互联网应用中,视频越来越重要,比如抖音、快手、腾讯视频等应用。 一、<video>: 视频元素 …...

SpringBoot外部配置文件
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 循序渐进学SpringBoot ✨特色专栏: MySQL学习 🥭本文内容:SpringBoot外部配置文件 📚个人知识库: Leo知识库,欢迎大家访问 1.前言☕…...

99个Python脚本实用实例
题目:有四个数字:1、2、3、4,能组成多少个互不相同且无重复数字的三位数?各是多少? #!/usr/bin/python# -*- coding: UTF-8 -*-for i in range(1,5): for j in range(1,5): for k in range(1,5): …...

HarmonyOS 工程目录介绍
工程目录 AppScope:存放应用全局所需要的资源文件 base element:文件夹主要存放公共的字符串、布局文件等资源media:存放全局公共的多媒体资源文件app.json5:应用的全局的配置文件,用于存放应用公共的配置信息 {"…...

门店管理系统驱动智慧零售升级
在当今数字化经济的大潮中,实体门店正在经历一场由内而外的深度变革。门店管理系统以其高效、便捷和全面的功能特性,为实体店提供了高效的运营解决方案。 门店管理系统拜托了传统零售业对本地化软件的依赖,它将复杂的信息技术转化为易于获取…...

Iterator迭代器操作集合元素时,不能用集合删除元素
在使用Iterator迭代器对集合中的元素进行迭代时,如果调用了集合对象的remove()方法删除元素或者调用add()方法添加元素之后,继续使用迭代器遍历元素,会出现异常(java.util.ConcurrentModificationException)。 import java.util.ArrayList; …...

Spring Boot是什么-特点介绍
什么是SpringBoot Spring Boot是由Pivotal团队提供的全新框架,其中“Boot”的意思就是“引导”,Spring Boot 并不是对 Spring 功能上的增强,而是提供了一种快速开发 Spring应用的方式。 Spring Boot 特点 嵌入的 Tomcat,无需部署…...

相机成像之图像传感器与ISP【四】
文章目录 1、图像传感器基础1.1 基础原理——光电效应1.2 基础的图像传感器设计1.3 衡量传感器效率的一个关键指标:光量子效率(QE)1.4 感光单元的响应1.5 像素的满阱容量1.6 像素尺寸和填充比例1.7 微透镜的作用1.8 光学低通滤波器简介1.9 传…...

新手入门Java 方法带参,方法重载及面向对象和面向过程的区别介绍
第二章 方法带参 课前回顾 1.描述类和对象的关系 类是一组对象的共有特征和行为的描述。对象是类的其中一个具体的成员。 2.如何创建对象 类名 对象名 new 类名();3.如何定义和调用方法 public void 方法名(){}对象名.方法名();4.成员变量和局部变量的区别 成员变量有初…...

使用Sqoop将Hive数据导出到TiDB
关系型数据库与大数据平台之间的数据传输之前写过一些 使用Sqoop将数据在HDFS与MySQL互导 使用Sqoop将SQL Server视图中数据导入Hive 使用DataX将Hive与MySQL中的表互导 使用Sqoop将Hive数据导出到TiDB虽然没写过,但网上一堆写的,那为什么我要专门写一下…...

互联网上门洗衣洗鞋工厂系统搭建;
随着移动互联网的普及,人们越来越依赖手机应用程序来解决生活中的各种问题。通过手机预约服务、购买商品、获取信息已经成为一种生活习惯。因此,开发一款上门洗鞋小程序,可以满足消费者对于方便、快捷、专业的洗鞋服务的需求,同时…...

Redis面试题12
Redis 的主从复制是什么? Redis 的主从复制是一种数据备份和高可用性机制,通过将一个 Redis 服务器的数据复制到其他 Redis 从服务器上来实现数据的冗余备份和读写分离。 主从复制的工作原理如下: 配置主服务器并开启主从复制功能。从服务器…...

el-tree多个树进行节点同步联动(完整版)
2024.1.11今天我学习了如何对多个el-tree树进行相同节点的联动效果,如图: 这边有两棵树,我们发现第一个树和第二个树之间会有重复的指标,当我们选中第一个树的指标,我们希望第二个树如果也有重复的指标也能进行勾选上&…...

python两个字典合并,两个list合并
1.两个字典: a{‘a’:1,‘b’:2,‘c’:3} b {‘aa’:11,‘bb’:22,‘cc’:33} 合并1:dict(a, **b) 结果:{‘a’: 1,‘aa’: 11,‘c’: 3,‘b’: 2,‘bb’: 22,‘cc’: 33} 合并2:dict(a.items()b.items()) 结果:{‘…...

搜维尔科技:【简报】元宇宙数字人赛道,《全息影像技术应用》!
期待着看展的主角来到今天要参观的全息影像展,平时就喜欢看展的她对于所谓的全息影像非常好奇,于是她带着期待的心情进入展内。进入展内的主角看到的是与之前完全不同的画展,每幅画看起来就像真的一样,充满好奇的她在展览的各处游…...

SparkSQL和Hive语法差异
SparkSQL和Hive语法差异 1、仅支持Hive SparkSQL关联条件on不支持函数rand()创建零时表时,Spark不支持直接赋值nullSpark无法读取字段类型为void的表SparkSQL中如果表达式没有指定别名,SparkSQL会将整个表达式作为别名,如果表达式中包含特殊…...

XCODE IOS 静态链接库替换升级
XCODE 版本15.2. 一个很久需求没更新的IOS 应用,近来有新需求要开发。 拉下代码运行,出现了个BAD_ACCESS错误。出错的位置位于一个调用的第三方的.a静态库内部。因为调用代码并没有修改,很容易想到可能XCODE相关升级,导致的问题。…...

API设计:从基础到优秀实践
在这次深入探讨中,我们将深入了解API设计,从基础知识开始,逐步进阶到定义出色API的最佳实践。 作为开发者,你可能对许多这些概念很熟悉,但我将提供详细的解释,以加深你的理解。 API设计:电子商…...

路由的安装顺序
安装前端路由的顺序通常如下: 安装前端框架:选择并安装适合你的项目的前端框架,如React、Vue或Angular等。 创建路由配置文件:在项目根目录下创建一个路由配置文件,比如router.js或routes.js等,用于定义路…...

华为OD机试真题-围棋的气--Java-OD统一考试(C卷)
题目描述: 围棋棋盘由纵横各19条线垂直相交组成,棋盘上一共19x19=361个交点,对弈双方一方执白棋,一方执黑棋,落子时只能将棋子置于交点上。 “气”是围棋中很重要的一个概念,某个棋子有几口气,是指其上下左右方向四个相邻的交叉点中,有几个交叉点没有棋子,由此可知: …...

CANFD数据记录仪在新能源汽车复杂路测下的应用
CANFD数据记录仪在新能源汽车复杂路测下的应用 汽车制造商在生产预批量阶段的耐久性测试中,为了检测潜在故障,必须让车辆在严酷的路况和环境下接受测试。为确保能回溯故障发生的现场情况,我们需要对测试数据精准记录与储存。这些数据是新车型优化迭代的关键,也是确保产品质量的…...

java: 5-6 break
文章目录 1. break1.1 介绍1.2 语法和流程图1.3 入门练习1.4 细节说明1.5 练习 【老韩视频p137-】 1. break 看个需求:随机生成 1-100 的一个数,直到生成了 97 这个数,看看你一共用了几次? 【思路分析:循环,但是循环的次数不知道…...

如何使用Imagewheel搭建一个简单的的私人图床无公网ip也能访问
文章目录 1.前言2. Imagewheel网站搭建2.1. Imagewheel下载和安装2.2. Imagewheel网页测试2.3.cpolar的安装和注册 3.本地网页发布3.1.Cpolar临时数据隧道3.2.Cpolar稳定隧道(云端设置)3.3.Cpolar稳定隧道(本地设置) 4.公网访问测…...

响应式编程Reactor API大全(上)
Reactor 是一个基于响应式编程的库,主要用于构建异步和事件驱动的应用程序。Reactor 提供了丰富的 API,包括创建、转换、过滤、组合等操作符,用于处理异步数据流。以下是一些 Reactor 的主要 API 示例: pom依赖 <dependencyMan…...

vue3自定义指令
一个自定义指令由一个包含类似组件生命周期钩子的对象来定义。钩子函数会接收到指令所绑定元素作为其参数。 页面内创建自定义指令 下面是一个自定义指令的例子,当一个 input 元素被 Vue 插入到 DOM 中后,它会被自动聚焦: <script setu…...

ECharts 多季度连续显示到一个图中。
效果图 二.相关option 以下option可以复制到 echarts的编辑器 进行查看修改 const site test1; const site2 test2;const qtrlyOption function (data: any, titleText: string): any {//获取最大值 。最大最小值的目的是:使左右里边的所有bar使用同一个指标let …...

【Microsoft Copilot】手机端发布 ——GPT-4, DALL-E3 免费用
Microsoft Copilot 关于Microsoft CopilotMicrosoft Copilot 的特点1. 可以在手机端使用:2. 可以免费使用GPT-4。3. 可以无限制地使用GPT-4。4. 可以使用DALL-E3生成图片。5. 搜索功能6. 图像识别 Microsoft Copilot的缺点和注意事项1. 非常容易报错2. 不支持长篇聊…...