iOS Swift 蓝牙开发(二)iOS中心设备的代码实现

2,234 阅读4分钟

上一篇文章介绍了蓝牙的技术知识,这里我们具体说明一下中心模式的应用场景。主设备(手机去扫描连接外设,发现外设服务和属性,操作服务和属性的应用。一般来说,外设(蓝牙设备,比如智能手环之类的东西), 会由硬件工程师开发好,并定义好设备提供的服务,每个服务对于的特征,每个特征的属性(只读,只写,通知等等),本文例子的业务场景,就是用一手机app去读写蓝牙设备。

iOS连接外设的代码实现流程

  1. 建立中心角色 

  2. 扫描外设(discover) 

  3. 连接外设(connect) 

  4. 扫描外设中的服务和特征(discover)

  • 4.1 获取外设的services

  • 4.2 获取外设的Characteristics,获取各个Characteristic 

  1. 与外设做数据交互(explore and interact) 

  2. 订阅 Characteristic 的通知 

  3. 断开连接(disconnect)

准备环境

1 Xcode 

2 开发证书和手机(一台做为中心设备的iPhone)(蓝牙程序需要使用真机调试) 

3 蓝牙外设(一台BLE外设或者一台模拟BLE外设的手机)

实现步骤(代码)

1、连接Peripheral

//导入CoreBluetooth头文件
import UIKit
 
import CoreBluetooth
 
class ViewController: UIViewController,CBCentralManagerDelegate{
 
//系统蓝牙设备管理对象,可以把他理解为主设备,通过他,可以去扫描和链接外设
var centralManager: CBCentralManager!
 
//一个全局的:CBPeripheral属性
var peripheral:CBPeripheral!
 
    override func viewDidLoad() {
        super.viewDidLoad()
 
//设置主设备的委托,设置完成后会执行下面的centralManagerDidUpdateState函数
        centralManager = CBCentralManager(delegate: self, queue: nil)
 
    }
    func centralManagerDidUpdateState(_ central: CBCentralManager) {
        //确保本中心设备支持蓝牙低能耗(BLE)并开启时才能继续操作
        switch central.state{
        case .unknown:
        print("未知")
        case .resetting:
        print("蓝牙重置中")
        case .unsupported:
            print("本机不支持BLE")
        case .unauthorized:
            print("未授权")
        case .poweredOff:
         print("蓝牙未开启")
        case .poweredOn:
            print("蓝牙开启")
            //MARK: 扫描正在广播的外设--每当发现外设时都会调用didDiscover peripheral方法
            central.scanForPeripherals(withServices: nil)
        @unknown default:
            print("来自未来的错误")
        }
}
       //MARK: didDiscover peripheral方法
    func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
        //在这里(还未连接设备时)可以获取advertisementData和rssi
        //MARK: 注意点
        //如果想连接这个外设的话,一定要赋给某个变量(弄了个强引用)
        //不然系统不会把当前发现的这个外设分配给下面钩子函数里面的peripheral参数
        self.peripheral = peripheral
}
   //MARK: 连接成功
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        //即将要使用peripheral的delegate方法,所以先委托self
        //寻找服务--立即(由已连接上的peripheral来)调用didDiscoverServices方法
 
        peripheral.delegate = self
 
    }
 
    //MARK: 连接失败
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        if let error = error {
            print("连接失败,原因是(error.localizedDescription)")
        }
    }
 
    //MARK: 连接断开
        func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
 
            if let error = error {
                print("连接失败,原因是(error.localizedDescription)")
 
            }
 
        }
}

2、对peripheral的characteristic进行操作

extension ViewController: CBPeripheralDelegate{
 
      //MARK: 已发现服务(或发现失败)
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
 
        if let error = error {
 
            print("没找到服务,原因是:(error.localizedDescription)")
 
        }
 
        for service in peripheral.services! {
 
            //MARK: 寻找特征--立即调用didDiscoverCharacteristicsFor方法
            peripheral.discoverCharacteristics(nil, for: service)
        }
 
    }
 
    //MARK: 发现特征(重要方法)
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        if let error = error {
            print("没找到特征,原因是:(error.localizedDescription)")
        }
 
        guard let characteristics = service.characteristics else { return }
 
        for characteristic in characteristics{
             //type--若需要反馈,则调用didWriteValueForCharacteristic方法
            if characteristic.properties.contains(.write){
                peripheral.writeValue("100".data(using: .utf8)!, for: characteristic, type: .withResponse)
 
            }
 
            if characteristic.properties.contains(.read){
                //读取外设的数据(其实就是读取外设里某个特征的值value)--立即调用didUpdateValueFor
                //若读取成功则可通过characteristic.value取出值
                //适合读取静态值
                peripheral.readValue(for: characteristic)
            }
            if characteristic.properties.contains(.notify){
                //订阅外设的某个数据(某个特征的值)--达到实时更新数据的目的
                //订阅后会先调用didUpdateNotificationStateFor
                //若订阅成功,则每当特征值变化时都会(若true)调用didUpdateValueFor
                //适合读动态值--一直在变化的值--如:心率
                peripheral.setNotifyValue(true, for: characteristic)
            }
            //这里可以继续发现特征下面的描述--也可不做for循环而单独指定某个特征
            //peripheral.discoverDescriptors(for: characteristic)
            //之后会立即调用didDiscoverDescriptorsFor,可在里面获取描述值
        }
 
        }
 
    //MARK: 订阅状态
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error{
            print("订阅失败,原因是:(error.localizedDescription)")
        }
        print("订阅成功")
    }
  
    //MARK: 读取(订阅)characteristic
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            print("读取失败,原因是:(error.localizedDescription)")
        }
 
    }
 
    //MARK: 写入characteristic
    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        if let error = error {
            print("写入失败,原因是:(error.localizedDescription)")
        }
 
    }
}

最后:不要忘了在Info文件里设置蓝牙隐私权限哦

若有收获,就点个赞吧

iOS Swift 蓝牙开发(一) 蓝牙相关基础知识juejin.cn/post/703666…