djk3000 / ME

4 stars 2 forks source link

iOS-蓝牙开发CoreBluetooth中心外围设备数据互联 #124

Open djk3000 opened 11 months ago

djk3000 commented 11 months ago

iOS蓝牙简介

我们可以将iOS设备和MAC设备或者其他更多BLE设备连接起来进行数据的交互。 BLE:bluetouch low energy,蓝牙4.0设备因为低功耗,所有也叫作BLE。 一般应用苹果的官方框架CoreBluetooth开发。 一般我们的设备可以将一端作为中心设备,另一端作为外设设备。发起连接的是中心设备(Central),被连接的是外围设备(Peripheral),对应传统的客户机-服务器体系结构。Central能够扫描侦听到,正在播放广告包的外设。 image

流程

  1. 蓝牙在不断地在广播信号;
  2. APP扫描;
  3. 发现设备(根据名称或「服务」的UUID来辨别是不是我们要连接的设备);
  4. 连接(成功);
  5. 调用方法发现「服务」;
  6. 调用方法发现服务」里的「特征」;
  7. 发现硬件用于数据输人的「特征」,保存(APP发送数据给硬件时要用到这个「特征」);
  8. 发现硬件用于数据输出的「特征」,进行「监听」(硬件就是从这个「特征」中发送数据给手机端);
  9. 利用数据输入「特征」发送数据,或者等待数据输出「特征」发出来的数据。

中心设备Central

主要是用作扫描和连接设备(通过CBService和CBCharacteristic来连接对应的设备)。 具体步骤为:

  1. 初始化CBCentralManager

    func initialize() {
        centralManager = CBCentralManager(delegate: self, queue: nil)
    }
  2. 初始化后判断蓝牙状态后开始扫描

    /**
     初始化后开始扫描
     */
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        guard central.state == .poweredOn else { return }
        startScanning()
    }
  3. 继承CBCentralManagerDelegate委托,当扫描到想要设备的时候连接该设备。

    /**
     判断连接设备
     */
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
    
        guard let advertismentServiceUUIDs = advertisementData[CBAdvertisementDataServiceUUIDsKey] as? [CBUUID] else {
            return
        }
    
        if RSSI.intValue < rssiThreshold {
            // RSSI 低于阈值,断开连接
            disconnectFromPeripheral()
            return
        }
    
        let uuid = advertismentServiceUUIDs.first!
        name = peripheral.name ?? uuid.uuidString
        self.peripheral = peripheral
        connectToPeripheral()
        }
  4. 连接设备

    /*
     连接该设备
     */
    func connectToPeripheral() {
        if let peripheral = peripheral {
            centralManager?.connect(peripheral, options: nil)
        }
    }
  5. 连接成功后发现服务

    
    /**
     是否外围设备连接成功
     */
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        //        stopScanning()
        peripheral.delegate = self
    
        peripheral.discoverServices(nil)
    }
    
    /**
     连接service后发现特征
     */
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        if let error = error {
            print("Unable to discover services: \(error.localizedDescription)")
            return
        }
    
        if let services = peripheral.services {
            for service in services {
                //                print(service.uuid.uuidString)
                if service.uuid == serviceUUID {
                    peripheral.services?.forEach { service in
                        peripheral.discoverCharacteristics([characteristicUUID], for: service)
                    }
                }
            }
        }
    }
6. 之后订阅特征,然后就可以和外围设备互相发送数据

/* 订阅特征 / func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) { if let error = error { print("Error discovering characteristics: (error.localizedDescription)") return }

    if let characteristics = service.characteristics {
        for characteristic in characteristics {
            if characteristic.uuid == characteristicUUID {
                // 发现所需的特性后,订阅它
                self.characteristic = characteristic
                peripheral.setNotifyValue(true, for: characteristic)
            }
        }
    }
}

// 写入数据到特性
func writeDataToPeripheral(data: Data) {
    if let characteristic = characteristic {
        peripheral?.writeValue(data, for: characteristic, type: .withResponse)
    }
}

/**
 处理从外围收到的数据
 */
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
    // 处理特性的值更新
    if characteristic.uuid == characteristicUUID {
        if let data = characteristic.value {
            // 处理接收到的数据
            let receivedString = String(data: data, encoding: .utf8)
            print("Received data from Peripheral: \(receivedString ?? "")")
        }
    }
}

## 外围设备Peripheral
处理和中心设备差不多,主要是用来广播自己的服务及特征,等待连接,连接后就可以发送数据。代码就一起贴出来了。

import Foundation import CoreBluetooth

class PeripheralManager: NSObject, CBPeripheralDelegate, CBPeripheralManagerDelegate, ObservableObject { private var peripheralManager: CBPeripheralManager? var serviceUUID = CBUUID(string: "9f37e282-60b6-42b1-a02f-7341da5e2eba") let characteristicUUID = CBUUID(string: "87654321-4321-8765-4321-876543218765") private var characteristic: CBMutableCharacteristic?

var canAdvertising: Bool = false
var services: [CBMutableService]?

@Published var offsetX: CGFloat = 0
@Published var offsetY: CGFloat = 0

@Published var pairCode: String = ""

/**
 初始化
 */
func initialize() {
    peripheralManager = CBPeripheralManager(delegate: self, queue: nil)
}

func peripheralManagerDidUpdateState(_ peripheral: CBPeripheralManager) {
    guard peripheral.state == .poweredOn else { return }
    canAdvertising = true

// startAdvertising() }

func enterPairCode(code: String) {
    if pairCode.count > 3 { return }
    pairCode += code
    if pairCode.count == 4 {
    }
}

/**
 开始广播
 */
func startAdvertising() {
    if peripheralManager?.isAdvertising == true {
        stopAdvertising()
    }
    // 创建特性
    characteristic = CBMutableCharacteristic(
        type: characteristicUUID,
        properties: [.read, .write, .notify],
        value: nil,
        permissions: [.readable, .writeable]
    )

    // 创建服务
    let uuidString = "9f37e282-60b6-42b1-a02f-7341da5e\(pairCode)"
    serviceUUID = CBUUID(string: uuidString)
    let service = CBMutableService(type: serviceUUID, primary: true)
    service.characteristics = [characteristic!]

    services=[service]
    peripheralManager?.add(service)
    peripheralManager?.startAdvertising([CBAdvertisementDataServiceUUIDsKey: [services![0].uuid]])
}

/**
 停止广播
 */
func stopAdvertising() {
    peripheralManager?.stopAdvertising()
}

/**
 中央设备请求读取特性的值
 */
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveRead request: CBATTRequest) {
    if request.characteristic == characteristic {
        let data = "Hello from Peripheral".data(using: .utf8)!
        request.value = data
        peripheralManager?.respond(to: request, withResult: .success)
    }
}

/**
 处理中央设备写入的数据
 */
func peripheralManager(_ peripheral: CBPeripheralManager, didReceiveWrite requests: [CBATTRequest]) {
    guard let request = requests.first, let data = request.value else { return }

    let receivedString = String(data: data, encoding: .utf8)
    print("Received data from Central: \(receivedString ?? "")")
    //这里需要有一个respond不然数据会阻塞
    peripheral.respond(to: request, withResult: .success)
}

/**
 数据准备好发送到已订阅的中央设备
 */
func sendUpdateToCentral(pos: String) {
    if let dataToSend = pos.data(using: .utf8) {
        peripheralManager?.updateValue(dataToSend, for: characteristic!, onSubscribedCentrals: nil)
    }
}

}



## 参考资料

- [iOS蓝牙知识快速入门(详尽版)](https://juejin.cn/post/6844903824847536135)
- [关于Core Bluetooth](https://www.cnblogs.com/zbblog/articles/16381228.html)
- [iOS Bluetooth官网](https://developer.apple.com/bluetooth/)
- [Demo地址](https://github.com/djk3000/CoreBluetoothControl)