移动端开发进阶之蓝牙通讯(一)
移动端进阶之蓝牙通讯需要综合考虑蓝牙版本选择、协议栈使用、服务匹配、设备连接、安全性和硬件支持等方面。
一、蓝牙版本选择
根据实际需求和应用场景选择合适的蓝牙版本;
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等厂商提供的模块。需要了解并选择适合自己应用的蓝牙硬件模块。