概念
外围设备
可以被其他蓝牙设备连接的外部蓝牙设备,不断广播自身的蓝牙名及其数据,如小米手环、共享单车、蓝牙体重秤
中央设备
可以搜索并连接周边的外围设备,并与之进行数据读写通讯,如手机
日常生活中常见的场景是手机app通过蓝牙开启共享单车,手机app通过蓝牙获取蓝牙体重秤的体重结果,这时候共享单车、蓝牙体重秤就称为外围设备,而手机就称为中央设备
经典蓝牙BT
泛指支持蓝牙协议在4.0以下的模块,一般用于数据量比较大的传输,如:语音、音乐等较高数据量的传输。经典蓝牙模块又可细分为:传统蓝牙和高速蓝牙模块。传统蓝牙模块在2004年推出,主要代表是支持蓝牙2.1协议的模块,在智能手机爆发的时期得到了广泛的使用。高速蓝牙模块在2009年推出,速率提高到约24Mbps,传输速率是经典蓝牙的八倍,可以轻松的应用于录像机到电视、PC到PMP、UMPC到打印机之间的资料传输。
低功耗蓝牙BLE
是指支持蓝牙协议4.0或者以上的模块,也被称为BLE模块,最大的特点就是成本和功耗的降低,可以应用于实时性要求较高的产品当中,比如:智能家居类(蓝牙锁、蓝牙灯)、传感设备的数据发送(血压计、温度传感器)、消费类电子(电子烟、遥控玩具)等。
目前市面上大部分的蓝牙都是4.0以上的低功耗蓝牙,经典蓝牙已经很少见到了。
通讯过程
一个外围设备可以发布多个服务service,每个服务可以包含多个特征值characteristic,每个特征值都有他的属性,例如长度(size),权限(permission),值(value),描述(descriptor),读写通讯都是通过Characteristic进行的。
每个service、characteristic都含有一个对应的UUID,通过和外围设备蓝牙约定UUID来进行读写通讯。
整个通讯流程为:
graph TB
B[创建蓝牙实例] --> S[搜索扫描外围设备]
S --> C[连接外围设备]
C --> SE[获取外围设备的服务service]
SE --> CH[获取服务的特征characteristic]
CH --> R[从外围设备读取数据,即读数据]
R --> W[给外围设备发送数据写数据,即写数据]
W --> D[断开连接]
常用业务API
1.判断当前蓝牙是否已经开启,如果没有开启提示用户开启
2.实时扫描周边蓝牙,获取蓝牙名给用户选择
3.解析蓝牙广播数据处理业务
4.监听外围设备发送给app的数据,处理对应业务
5.app发送数据给外围设备以处理业务
配置Info.plist
NSBluetoothAlwaysUsageDescription
开机访问蓝牙需要您的授权
NSBluetoothPeripheralUsageDescription
开机访问蓝牙需要您的授权
完整的代码:
Bluetooth.swift
//
// Bluetooth.swift
// ProjectApp
//
// Created by jack on 2021/1/11.
//
import Foundation
import CoreBluetooth
class Bluetooth: NSObject, CBCentralManagerDelegate, CBPeripheralDelegate {
var centralMgr: CBCentralManager!
var curPeripheral: CBPeripheral?
var writeCharacteristic: CBCharacteristic?
override init() {
super.init()
var backgroundMode: Bool = false
if let infoDic = Bundle.main.infoDictionary {
let tempModes = infoDic["UIBackgroundModes"]
if let modes = tempModes as? Array {
if modes.contains("bluetooth-central") {
backgroundMode = true
}
}
}
if backgroundMode {
let options: [String : Any] = [
CBCentralManagerOptionShowPowerAlertKey: true, // 初始化时若此时蓝牙系统为关闭状态,是否向用户显示警告对话框
CBCentralManagerOptionRestoreIdentifierKey: "MWellnessBluetoothRestore" // 中心管理器的唯一标识符,系统根据这个标识识别特定的中心管理器,为了继续执行应用程序,标识符必须保持不变,才能还原中心管理类
]
self.centralMgr = CBCentralManager(delegate: self, queue: nil, options: options)
} else {
self.centralMgr = CBCentralManager(delegate: self, queue: nil)
}
}
// MARK: - CBCentralManagerDelegate
func centralManagerDidUpdateState(_ central: CBCentralManager) {
self.centralMgr = central
switch central.state {
case .poweredOn:
DLOG(message: "蓝牙打开")
// 蓝牙已经打开,可以开始扫描蓝牙设备
self.scanBluetooth()
break;
case .poweredOff:
DLOG(message: "蓝牙关闭")
// 这里检测到蓝牙关闭,一般会弹框提醒用户开启蓝牙
break
default:
break
}
}
/// 开始扫描蓝牙
/// - Returns:
func scanBluetooth() -> Void {
var options: [String: Any] = [String: Any]()
options[CBCentralManagerScanOptionAllowDuplicatesKey] = NSNumber(value: false)
var services: [CBUUID]? // 这里可以通过筛选蓝牙的广播service来筛选扫描蓝牙
self.centralMgr.scanForPeripherals(withServices: services, options: options)
// TODO 扫描时,一般需要开个定时器来处理扫描超时业务,自行添加
}
/// 实时扫描到蓝牙时回调
/// - Parameters:
/// - central:备
/// - peripheral: 扫描到的蓝牙设备
/// - advertisementData: 广播内容
/// - RSSI: 蓝牙信号强度,大部分为负数,负数值越大表示信号越强
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
if let name = peripheral.name {
// 实时扫描到蓝牙后,一般有两种常用业务
// 1.弹框列出扫描的的蓝牙让用户选择连接哪个蓝牙
// 2.业务已经约定好蓝牙名的匹配方式,扫描到后直接连接
if name.lowercased().starts(with: "SHww-") {
// 先停止扫描,再连接
central.stopScan()
self.curPeripheral = peripheral
self.centralMgr.connect(peripheral, options: nil)
}
}
// 获取对方蓝牙广播中的厂家数据
if let manData = advertisementData[CBAdvertisementDataManufacturerDataKey] as? Data {
}
// 获取对方蓝牙广播中的蓝牙名,从peripheral.name获取的蓝牙名可能是手机缓存中的名称,从广播中获取的是实时的名称
if let localName = advertisementData[CBAdvertisementDataLocalNameKey] as? String {
}
if let txPowerLevel = advertisementData[CBAdvertisementDataTxPowerLevelKey] as? NSNumber {
}
if let serviceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID] {
}
if let serviceData = advertisementData[CBAdvertisementDataServiceDataKey] as? [String : Any] {
}
// 其他的还有CBAdvertisementDataOverflowServiceUUIDsKey,CBAdvertisementDataIsConnectable,CBAdvertisementDataSolicitedServiceUUIDsKey
}
/// 蓝牙连接失败
/// - Parameters:
/// - central:
/// - peripheral:
/// - error:
func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
}
/// 蓝牙断开连接
/// - Parameters:
/// - central:
/// - peripheral:
/// - error:
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
}
/// 蓝牙连接成功
/// - Parameters:
/// - central:
/// - peripheral:
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
// 连接成功后,先设置蓝牙代理,再查找蓝牙对应的service
peripheral.delegate = self
peripheral.discoverServices(nil)
}
// MARK: - CBPeripheralDelegate
/// 找到对方蓝牙service
/// - Parameters:
/// - peripheral:
/// - error:
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
// 找到对方蓝牙服务后,筛选service再查找服务对应的Characteristic,service的UUID双方先约定要,这里以FFE0开头的就是要找的服务,实际情况根据自己业务自行处理
if let services = peripheral.services, let per = self.curPeripheral {
for service in services {
if service.uuid.uuidString.starts(with: "FFE0") {
per.discoverCharacteristics(nil, for: service)
}
}
}
}
/// 找到对方蓝牙的Characteristic
/// - Parameters:
/// - peripheral:
/// - service:
/// - error:
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
// 找到读、写Characteristic,读、写Characteristic的UUID双方先约定要,这里以FFE0开头的就是要找的服务,以FFE1开头的就是要找的可读Characteristic,以FFE2开头的就是要找的可写Characteristic,实际情况根据自己业务自行处理
if let services = peripheral.services {
for service in services {
let serviceUUID = service.uuid.uuidString
if serviceUUID.uppercased().contains("FFE0") {
if let characteristics = service.characteristics {
for characteristic in characteristics {
let characteristicUUID = characteristic.uuid.uuidString
if characteristicUUID.uppercased().contains("FFE1") { // 找到读Characteristic,设置为可通知
peripheral.setNotifyValue(true, for: characteristic)
} else if characteristicUUID.uppercased().contains("FFE2") { // 找到写Characteristic,临时保存为变量
self.writeCharacteristic = characteristic
}
}
}
}
}
}
}
/// 收到对方蓝牙发送的数据时回调
/// - Parameters:
/// - peripheral:
/// - characteristic:
/// - error:
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
}
/// 发送数据成功时回调
/// - Parameters:
/// - peripheral:
/// - characteristic:
/// - error:
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
}
/// 给对方蓝牙发送数据
/// - Parameter data:
/// - Returns:
func sendData(data: [UInt8]) -> Void {
if let per = self.curPeripheral, let ch = self.writeCharacteristic {
let sendData: Data = Data(data)
let properties: CBCharacteristicProperties = ch.properties
if properties == CBCharacteristicProperties.writeWithoutResponse {
per.writeValue(sendData, for: ch, type: CBCharacteristicWriteType.withoutResponse)
} else {
per.writeValue(sendData, for: ch, type: CBCharacteristicWriteType.withResponse)
}
}
}
}
项目源码下载