iOS CoreBluetooth 框架
1. 發現 心率服務(0x180D)和 電池服務(0x180F)。
2. 讀取 電池電量特征值(0x2A19)。
3.讀取和監聽 心率數據特征值(0x2A37),讓設備主動通知 App 心率變化。
1. 掃描 BLE 設備,只搜索支持 心率服務 (0x180D) 的設備。
2. 連接設備 后,發現 心率服務 和 電池服務 (0x180F)。
3.讀取電池電量 (0x2A19),電池電量是 0-100 的整數。
4. 監聽心率 (0x2A37):
??? 解析 8-bit 或 16-bit 的心率數據。
???設備每秒更新心率,主動通知 App。
5.解析 BLE 數據,并顯示電池電量和心率信息。
import UIKit
import CoreBluetooth
class ViewController: UIViewController, CBCentralManagerDelegate, CBPeripheralDelegate {
var centralManager: CBCentralManager!
var heartRatePeripheral: CBPeripheral?
// 心率和電池服務 & 特征值 UUID
let heartRateServiceUUID = CBUUID(string: "180D") // 心率服務
let heartRateCharacteristicUUID = CBUUID(string: "2A37") // 心率數據特征值
let batteryServiceUUID = CBUUID(string: "180F") // 電池服務
let batteryCharacteristicUUID = CBUUID(string: "2A19") // 電池電量特征值
override func viewDidLoad() {
super.viewDidLoad()
// 初始化 Bluetooth 中心管理器
centralManager = CBCentralManager(delegate: self, queue: nil)
}
// MARK: - CBCentralManagerDelegate
// 1. 檢查藍牙狀態
func centralManagerDidUpdateState(_ central: CBCentralManager) {
switch central.state {
case .unknown:
print("藍牙狀態未知")
case .resetting:
print("藍牙正在重置")
case .unsupported:
print("設備不支持藍牙")
case .unauthorized:
print("未授權使用藍牙")
case .poweredOff:
print("藍牙已關閉")
case .poweredOn:
print("藍牙已開啟,開始掃描設備...")
centralManager.scanForPeripherals(withServices: [heartRateServiceUUID], options: nil)
@unknown default:
print("未知藍牙狀態")
}
}
// 2. 發現 BLE 設備
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
print("發現設備: \(peripheral.name ?? "未知設備")")
heartRatePeripheral = peripheral
heartRatePeripheral?.delegate = self
centralManager.stopScan() // 停止掃描,連接設備
centralManager.connect(peripheral, options: nil)
}
// 3. 連接設備成功
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("成功連接設備: \(peripheral.name ?? "未知設備")")
peripheral.discoverServices([heartRateServiceUUID, batteryServiceUUID]) // 發現服務
}
// MARK: - CBPeripheralDelegate
// 4. 發現服務
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
guard let services = peripheral.services else { return }
for service in services {
print("發現服務: \(service.uuid)")
if service.uuid == heartRateServiceUUID || service.uuid == batteryServiceUUID {
peripheral.discoverCharacteristics(nil, for: service) // 發現特征值
}
}
}
// 5. 發現特征值
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
guard let characteristics = service.characteristics else { return }
for characteristic in characteristics {
print("發現特征值: \(characteristic.uuid)")
if characteristic.uuid == batteryCharacteristicUUID {
// 讀取電池電量
peripheral.readValue(for: characteristic)
} else if characteristic.uuid == heartRateCharacteristicUUID {
// 監聽心率數據(開啟通知)
peripheral.setNotifyValue(true, for: characteristic)
}
}
}
// 6. 讀取數據(適用于電池電量)
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
guard let data = characteristic.value else { return }
if characteristic.uuid == batteryCharacteristicUUID {
let batteryLevel = data.first ?? 0
print("電池電量: \(batteryLevel)%")
} else if characteristic.uuid == heartRateCharacteristicUUID {
let heartRate = parseHeartRateData(data)
print("心率: \(heartRate) BPM")
}
}
// 解析心率數據(部分設備用第一個字節標志心率格式)
private func parseHeartRateData(_ data: Data) -> Int {
let bytes = [UInt8](data)
if bytes.first! & 0x01 == 0 {
return Int(bytes[1]) // 8-bit 心率值
} else {
return Int(bytes[1]) | (Int(bytes[2]) << 8) // 16-bit 心率值
}
}
// 7. 設備主動通知心率變化
func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
if error != nil {
print("設置通知失敗: \(error!.localizedDescription)")
} else {
print("成功開啟心率通知")
}
}
}
藍牙已開啟,開始掃描設備...
發現設備: MyHeartRateMonitor
成功連接設備: MyHeartRateMonitor
發現服務: 180D
發現服務: 180F
發現特征值: 2A37
發現特征值: 2A19
電池電量: 85%
成功開啟心率通知
心率: 72 BPM
心率: 73 BPM
心率: 74 BPM
...
如果你的 BLE 設備 需要 定期主動獲取(輪詢)某個特征值(比如每隔 5 秒讀取一次溫度、血氧等),可以使用 定時器(Timer) 來定期讀取特征值。
方法 1:使用 Timer 定時讀取
可以使用 Timer.scheduledTimer 每隔 N 秒主動讀取 BLE 設備的特征值:
查看代碼
var readTimer: Timer? // 定時器
var myPeripheral: CBPeripheral? // 目標 BLE 設備
let myCharacteristicUUID = CBUUID(string: "2A19") // 例如讀取電池電量(0x2A19)
// 啟動定時器,每 5 秒讀取一次特征值
func startReadingCharacteristic() {
readTimer?.invalidate() // 先清除舊定時器
readTimer = Timer.scheduledTimer(timeInterval: 5.0, target: self, selector: #selector(readCharacteristicValue), userInfo: nil, repeats: true)
}
// 定時器觸發,讀取特征值
@objc func readCharacteristicValue() {
guard let peripheral = myPeripheral,
let characteristic = findCharacteristic(by: myCharacteristicUUID) else { return }
print("定時讀取特征值: \(characteristic.uuid)")
peripheral.readValue(for: characteristic)
}
// 停止定時器
func stopReadingCharacteristic() {
readTimer?.invalidate()
readTimer = nil
}
// 通過 UUID 查找特征值
func findCharacteristic(by uuid: CBUUID) -> CBCharacteristic? {
guard let services = myPeripheral?.services else { return nil }
for service in services {
if let characteristic = service.characteristics?.first(where: { $0.uuid == uuid }) {
return characteristic
}
}
return nil
}
// 處理讀取的特征值數據
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid == myCharacteristicUUID, let data = characteristic.value {
let batteryLevel = data.first ?? 0
print("電池電量: \(batteryLevel)%")
}
}
? 特點:
?適用于 BLE 設備不支持 Notify,但你需要定期獲取數據。
? 適用于 讀取溫度、血氧、電池等信息。
方法 2:使用 DispatchSource 定時讀取
如果你的 BLE 讀取任務運行在 后臺線程,可以使用 DispatchSource 定時讀取:
查看代碼
var readTimer: DispatchSourceTimer?
func startReadingCharacteristicWithGCD() {
readTimer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
readTimer?.schedule(deadline: .now(), repeating: 5.0)
readTimer?.setEventHandler {
DispatchQueue.main.async {
self.readCharacteristicValue()
}
}
readTimer?.resume()
}
func stopReadingCharacteristicWithGCD() {
readTimer?.cancel()
readTimer = nil
}
? 特點:
? 不需要手動定時讀取,BLE 設備主動通知更省電。
? 適用于心率、步數等動態數據。
|
適用場景 |
優點 |
缺點 |
|
|
方法 1: Timer 定時讀取 |
設備不支持 Notify,需要手動讀取 |
代碼簡單,適用于周期性數據 |
可能增加功耗 |
|
方法 2: DispatchSource 低功耗定時讀取
|
后臺任務,低功耗需求 |
更省電,適合后臺讀取 |
代碼稍復雜 |
|
方法 3: Notify 監聽設備主動推送
|
設備支持 Notify(心率、步數等) |
最省電,響應快 |
需要設備支持 |
浙公網安備 33010602011771號