移动端开发进阶之蓝牙通讯(一)

移动端开发进阶之蓝牙通讯(一)

移动端进阶之蓝牙通讯需要综合考虑蓝牙版本选择、协议栈使用、服务匹配、设备连接、安全性和硬件支持等方面。
移动端

一、蓝牙版本选择

根据实际需求和应用场景选择合适的蓝牙版本;
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开发中建立蓝牙连接的步骤如下:

  1. 导入蓝牙框架:在项目中使用蓝牙功能,需要导入CoreBluetooth框架。
  2. 创建CBCentralManager实例:CBCentralManager是iOS中用于管理蓝牙的中心管理者,负责扫描、连接和与外围设备的通信。
  3. 配置CBCentralManagerDelegate和CBPeripheralDelegate协议:需要遵守这两个协议,以便在蓝牙连接过程中处理相关事件。
  4. 初始化CBCentralManager:创建一个CBCentralManager实例,并传入self来遵守CBCentralManagerDelegate协议。
  5. 启动CBCentralManager:调用CBCentralManager的startScan方法开始扫描附近的蓝牙设备。
  6. 发现外围设备:当扫描到周围有可连接的蓝牙设备时,CBCentralManager会调用其代理方法并传入CBPeripheral对象。
  7. 连接外围设备:使用CBPeripheral对象的connect方法来连接指定的外围设备。
  8. 获取外围设备的服务:连接成功后,可以通过CBPeripheral对象获取外围设备提供的服务。
  9. 获取服务的特征:从服务中获取特征,这些特征用于读写数据。
  10. 读取和写入数据:通过特征值进行数据的读写操作。

五、蓝牙安全

移动端需要考虑蓝牙通讯的安全性。由于蓝牙通讯是一种无线通讯方式,容易被截获或干扰,因此需要进行安全设置和保护。需要了解并实现安全设置和保护的逻辑,如加密、认证等。
加解密

#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等厂商提供的模块。需要了解并选择适合自己应用的蓝牙硬件模块。

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

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

相关文章

线程安全的集合类

Java中提供了许多集合类&#xff0c;其中有的是线程安全的&#xff0c;有的是线程不安全的。线程安全的集合类有&#xff1a; 1. Vector&#xff1a;Vector类实现了一个动态数组&#xff0c;与ArrayList相似&#xff0c;但Vector是同步访问的 2. Stack&#xff1a;Stack是Vec…

C++ 数论相关题目(约数)

1、试除法求约数 主要还是可以成对的求约数进行优化&#xff0c;不然会超时。 时间复杂度根号n #include <iostream> #include <vector> #include <algorithm>using namespace std;int n;vector<int> solve(int a) {vector<int> res;for(int i…

leetcode 每日一题 2024年01月18日 拿出最少数目的魔法豆

题目 给定一个 正整数 数组 beans &#xff0c;其中每个整数表示一个袋子里装的魔法豆的数目。 请你从每个袋子中 拿出 一些豆子&#xff08;也可以 不拿出&#xff09;&#xff0c;使得剩下的 非空 袋子中&#xff08;即 至少还有一颗 魔法豆的袋子&#xff09;魔法豆的数目 …

保证Kafka消息有序性

一、Kafka特性 写入同一个partion分区中的数据是一定有顺序的kafka中一个消费者消费一个partion的数据&#xff0c;消费者取出数据时&#xff0c;也是有顺序的 二、保证消息Kafka消息有序性 在生产者端&#xff0c;应保证消息被写入同一分区。可以在构造消息时指定消息的key…

如何将音频转文字?4个方法帮你轻松搞定!

如何将音频转文字&#xff1f;在日常生活中&#xff0c;将音频转换为文字的应用可以带来很多便利。例如&#xff0c;在会议、讲座、课堂等场合&#xff0c;我们可以使用识别软件将语音转换为文字&#xff0c;方便记录和整理。此外&#xff0c;在语言学习、语音翻译等领域&#…

基于springboot+vue的在线拍卖系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目背景…

预处理详解(#和##运算符、命名约定、#undef​​、命令行定义​、条件编译、头文件的包含​)

目录 一、#和## 1.1#运算符 1.2## 运算符​ 二、命名约定​ 三、#undef​ 四、命令行定义​ 五、条件编译​ 六、头文件的包含​ 4.1 头文件被包含的方式&#xff1a;​ 4.1.1 本地文件包含​ Linux环境的标准头文件的路径&#xff1a;​ VS环境的标准头文件的路径&…

Vue3在点击菜单切换路由时,将ElementPlus UI库中el-main组件的内容滚动恢复到顶部

功能&#xff1a;Vue3在点击菜单切换路由时&#xff0c;将页面el-main的内容滚动到顶部&#xff0c;布局如下&#xff0c;使用UI组件库为ElementPlus 在网上搜很多都是在route.js中的router.beforeEach中使用window.scrollTop(0,0) 或 window.scrollTo(0,0) 滚动&#xff0c;但…

如何用GPT 运行python?GPT4科研应用与AI绘图及论文高效写作

详情点击链接&#xff1a;如何用GPT 运行python&#xff1f;GPT4科研应用与AI绘图及论文高效写作 一OpenAI 1.最新大模型GPT-4 Turbo 2.最新发布的高级数据分析&#xff0c;AI画图&#xff0c;图像识别&#xff0c;文档API 3.GPT Store 4.从0到1创建自己的GPT应用 5. 模型…

SpringBoot 统计API接口用时该使用过滤器还是拦截器?

统计请求的处理时间&#xff08;用时&#xff09;既可以使用 Servlet 过滤器&#xff08;Filter&#xff09;&#xff0c;也可以使用 Spring 拦截器&#xff08;Interceptor&#xff09;。两者都可以在请求处理前后插入自定义逻辑&#xff0c;从而实现对请求响应时间的统计。 …

【前端】vue.js从入门到项目实战笔记

文章目录 第三章3.1 插值绑定&#xff08;{{}}&#xff0c; v-html&#xff09;3.1.1 文本插值3.1.2 HTML插值 3.2 属性绑定 v-bind3.2.1 指令v-bind3.2.3 类名和样式绑定 【前端目录贴】 第三章 3.1 插值绑定&#xff08;{{}}&#xff0c; v-html&#xff09; 文本插值中的代…

vue3前端开发,感受一下组合式api和VUE2选项式的差异

vue3前端开发,感受一下组合式api和VUE2选项式的差异&#xff01;今天开始&#xff0c;正式开始&#xff0c;进入学习Vue3的内容。以后代码&#xff0c;案例分享&#xff0c;都会采用组合式api的模式为大家做展示。 今天是第一节&#xff0c;带大家感受一下&#xff0c;Vue3的组…

css-盒子等样式学习

盒子居中&#xff0c;继承外层盒子的宽高 兼容性&#xff08;border-box&#xff09;将边框收到盒子内部 初始化div 不用管box-setting content-box 还原 创建为一个类 &#xff0c;让所有需要还原的类 进行继承 padding 用法表示margin上下左右边距 body 外边距&…

线程和进程的区别(从JVM角度出发)

进程与线程的区别 线程具有许多传统进程所具有的特征&#xff0c;故又称为轻型进程(Light—Weight Process)或进程元&#xff1b;而把传统的进程称为重型进程(Heavy—Weight Process)&#xff0c;它相当于只有一个线程的任务。在引入了线程的操作系统中&#xff0c;通常一个进…

linux-nfc neard 编译、安装与运行

项目github地址&#xff1a; https://github.com/linux-nfc/neard git clone地址&#xff1a; https://github.com/linux-nfc/neard.git 1.安装依赖库 clone完源码切换到目录neard里。这个项目需要依赖一下库&#xff1a; - GCC compiler - D-Bus library - GLib library …

CHAPTER 9: 《DESIGN A WEB CRAWLER》第9章 《设计一个web爬虫》

CHAPTER 9: 《DESIGN A WEB CRAWLER》第九章 设计一个web爬虫 在本章中&#xff0c;我们将重点介绍网络爬虫设计&#xff1a;一种有趣而经典的系统设计 面试问题。 网络爬虫被称为机器人或蜘蛛。它被搜索引擎广泛用于发现网络上的新内容或更新内容。内容可以是网页、图像、视频…

66.Go从零搭建一个orm框架【简版】

文章目录 一&#xff1a;前置学习1、 为什么要用orm2、Golang里面是如何原生连接MySQL的3、ORM框架构想 二: 开始造1、连接Connect2、设置/读取表名Table/GetTable3、新增/替换Insert/Replace4、条件Where5、条件OrWhere6、删除Delete7、修改Update8、查询9、设置查询字段Field…

使用python将图像复刻到excel表格

上班摸鱼的时候突然想到以前有个日本老爷爷用excel表格作画感觉挺牛的&#xff0c; 然后想到图像可以分割成一个个小块&#xff0c;excel表格也是一个个小格子&#xff0c;将小块的坐标和颜色对应填充到表格中&#xff0c;不就行了。 效果如下&#xff1a; 完整代码&#xff1…

electron+vite+vue3 快速入门教程

文章目录 前言一、electron是什么&#xff1f;二、electron 进程模型1.主进程2.渲染进程3.预加载脚本4.进程通信4.1 sendon&#xff08;单向&#xff09;4.2 invokehandle (双向)4.3 主进程向渲染进程发送事件 三、窗口创建与应用事件四、技术栈和构建工具五、electron-vite安装…

如何实现指定列值排序? ------ MySQL中的field()函数 [让排序更简单]

想自定义排序规则就用field&#xff08;&#xff09; filed(“列名”&#xff0c;“值1”,“值2”…) 案例&#xff1a;要求 STATUS 列 按 N&#xff0c;Y&#xff0c;E&#xff0c; 排序。 select * from 表名 ORDER BY field(STATUS,N,"Y","E") 效果…