swift3.0蓝牙开发(2)

承接上篇swift3.0蓝牙开发(1)

三.代码展示

1.设置代理
CBCentralManagerDelegate 中心者的代理
CBPeripheralDelegate 外设的代理

class ViewController: UIViewController,CBCentralManagerDelegate,CBPeripheralDelegate

2.定义全局的中心者对象

    /// 中心者对象
    var central: CBCentralManager
    /// 外设数组
    var peripheralArray = NSMutableArray.init()

3.初始化中心者对象(第一个参数是设置代理,第二个参数是队列,这不讲,涉及多线程开发了)

     /// 初始化中心设备
    func initBluetooth() {
        //MARK: -1.初始化本地中心设备对象
        central = CBCentralManager.init(delegate: self, queue: nil)
        }

4.当初始化完中心者对象后,就会回调以下的方法,就是当初始化中心者对象那句代码运行过完后,就会走到以下的方法
这个方法是用来检查手机(中心者)的蓝牙状态,比如是否打开啊,是否支持蓝牙4.0啊

      func centralManagerDidUpdateState(_ central: CBCentralManager) {
        self.writeToTextView(string: "初始化对象后,来到centralManagerDidUpdateState")
        switch central.state {
        case .unknown:
            print("CBCentralManager state:", "unknown")
            break
        case .resetting:
            print("CBCentralManager state:", "resetting")
            break
        case .unsupported:
            print("CBCentralManager state:", "unsupported")
            break
        case .unauthorized:
            print("CBCentralManager state:", "unauthorized")
            break
        case .poweredOff:
            print("CBCentralManager state:", "poweredOff")
            break
        case .poweredOn:
            print("CBCentralManager state:", "poweredOn")
            
           
            //MARK: -3.扫描周围外设(支持蓝牙)
            // 第一个参数,传外设uuid,传nil,代表扫描所有外设
            central.scanForPeripherals(withServices: nil, options: nil) 
            // 每次搜索到一个外设都会回调起代理方法centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
            
            
           
            break
        }

5.在以上方法的蓝牙打开的控制流分支中,扫描周围的外设,当成功扫描到外设的时候,就会来到以下的方法

以下方法参数:
/// - central: 中心设备实例对象
/// - peripheral: 外设实例对象
/// - advertisementData: 一个包含任何广播和扫描响应数据的字典
/// - RSSI: 外设的蓝牙信息强度

     func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
     
        
        // 添加外设到外设数组(如果不保存这个外设或者说没有对这个外设对象有个强引用的话,就没有办法到达连接成功的方法,因此没有强引用的话,这个方法过后,就销毁外设对象了)
        // 连接外设成功或者失败分别会进入回调方法
        // MAKE: -连接外设
        peripheralArray.add(peripheral)
        
        // 或者,你可以建立一个全局的外设对象。例如
//        self.peripheral = peripheral  这就是强引用这个局部的外设对象,这样就不会导致出了这个方法这个外设对象就被销毁了
        
        //MARK: -5.连接外设
        // 添加完后,就开始连接外设
        central.connect(peripheral, options: nil) // 会根据条件触发,连接成功,失败,断开连接的代理方法
        
        
        // 如果你扫描到多个外设,要连接特定的外设,可以用以下方法
//        if peripheral.name == "A" {
//            // 连接设备
//             central.connect(peripheral, options: nil)
//        }        
        
    }

6.当连接外设成功后,就会来到外设连接成功的回调方法,失败也有失败的回调方法,分别如下

    // 连接成功
    func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
        
        
        // 连接成绩后,接下来就要开始对外设进行寻找服务 特征 读写,所以要开始用到外设的代理方法,所以这里要设置外设的代理为当前控制器
        peripheral.delegate = self
        
        // 设置完后,就开始扫描外设的服务
        // 参数设了nil,是扫描所有服务,如果你知道你想要的服务的UUID,那么也可以填写指定的UUID
        peripheral.discoverServices(nil) //这里会回调代理peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)  这里方法里有这个外设的服务信息
    }


// 连接失败
    func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?) {
        
    }

7.在上面连接成功的回调方法里,扫描外设的服务,当扫描成功后,就会来到发现外设服务的方法,如下

 // 外设的服务信息
    func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
        for service in peripheral.services! {
            peripheral.discoverCharacteristics(nil, for: service)// 发现特征,第二个参数填哪个服务的特征,这个方法会回调特征信息的方法
            print("服务UUID:\(service.uuid.uuidString)")
        }
    }

这里要注意的是

peripheral.services!是一个数组
所以不能
peripheral.services.uuid  这样是错误的

8.以上是发现服务的回调方法,在上面的方法中,可以使用发现服务下的特征的方法,为什么,我发现特征的方法一定要在这个发现服务的方法里面写,我不能直接写发现特征吗

答:你可以去其他地方写写看,peripheral.discoverCharacteristics(nil, for: service)这是发现特征的方法,这个方法的第二个参数是填写服务的,那么我们是只能在这个发现服务的回调方法里才可以拿到这个服务的对象,所以就只能在这个方法服务的方法里写了

当运行完发现特征的方法后,就会来到发现特征的回调方法


 //  特征信息的方法
    func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
        for characteristic in service.characteristics! {
            print("\(service.uuid.uuidString)服务下的特性:\(characteristic)")
            if characteristic.uuid == CBUUID (string: "FFF0")  {
                //MARK: -9.读取指定特征的信息
                //读取特征数据
                peripheral.readValue(for: characteristic)//读取都就会进入didUpdate的代理方法
            }
            
            if  characteristic.uuid == CBUUID (string: "FFF0") {
                //订阅通知
                /**
                 -- 通知一般在蓝牙设备状态变化时会发出,比如蓝牙音箱按了上一首或者调整了音量,如果这时候对应的特征被订阅,那么app就可能收到通知
                 -- 阅读文档查看需要对该特征的操作
                 -- 订阅成功后回调didUpdateNotificationStateForCharacteristic
                 -- 订阅后characteristic的值发生变化会发送通知到didUpdateValueForCharacteristic
                 -- 取消订阅:设置setNotifyValue为NO
                 */
                //这里我们可以使用readValueForCharacteristic:来读取数据。如果数据是不断更新的,则可以使用setNotifyValue:forCharacteristic:来实现只要有新数据,就获取。
                peripheral.setNotifyValue(true, for: characteristic)
            }
            
            //扫描描述
            /**
             -- 进一步提供characteristic的值的相关信息。(因为我项目里没有的特征没有进一步描述,所以我也不怎么理解)
             -- 当发现characteristic有descriptor,回调didDiscoverDescriptorsForCharacteristic
             */
            peripheral.discoverDescriptors(for: characteristic)
            
            
        }
        
    
    }

上面方法有大家应该会有点疑惑的,就是什么时候才调用didUpdateNotificationStateForCharacteristic方法,这个方法有什么用,这个方法会获取到外设发送到手机的数据,还有什么的订阅特征

答:didUpdateNotificationStateForCharacteristic这个方法有两种情况会调用:
(1).订阅特征的时候 peripheral.setNotifyValue(true, for: characteristic)
(2).读取数据的时候peripheral.readValue(for: characteristic)

什么是订阅特征?

答:比如说现在我的外设是一个蓝牙音箱,里面有一个特征是下一首歌的特征,那么对于这种特征,我们就要采用订阅特征的方法,这个订阅有什么用呢,如果我订阅了这个特征,那么这个特征一更新数据,我就可以在didUpdateNotificationStateForCharacteristic方法中获取到数据

下一首歌开发思路
1.我定于下一首歌的特征
2.用户按了蓝牙音箱下一首歌
3.因为我订阅了这个特征,所以我可以在didUpdateNotificationStateForCharacteristic方法中拿到用户按了下一首歌这个按钮的信息
4.调用程序下一首歌的方法,执行
5.在didUpdateNotificationStateForCharacteristic方法中给蓝牙写数据,因为要快啊,我在这里拿到数据下一首歌,我应该马上响应切换下一首歌,并且给蓝牙的另一个特征写数据,告诉他,我切歌了

9.didUpdateNotificationStateForCharacteristic方法


/**
     -- peripheral调用readValueForCharacteristic成功后会回调该方法
     -- peripheral调用setNotifyValue后,特征发出通知也会调用该方法
     */
    func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
        // 写数据,第一个参数转data类型的数据,第二个参数传写入数据的特征,第三个参数是枚举类型分别是CBCharacteristicWriteWithResponse和                                                  CBCharacteristicWriteWithoutResponse;
//        peripheral.writeValue(<#T##data: Data##Data#>, for: <#T##CBCharacteristic#>, type: <#T##CBCharacteristicWriteType#>)
    }


 // 对于以上的枚举类型的第一个CBCharacteristicWriteWithResponse,每往硬件写入一次数据就会调用一下代理方法
    func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
        
    }
    
    
    //订阅通知的回调
    func peripheral(_ peripheral: CBPeripheral, didUpdateNotificationStateFor characteristic: CBCharacteristic, error: Error?) {
        
    }

#后附上讲解demo
https://github.com/ChenZeBin/CoreBluetoothDemo.git

demo中是很多注释,基本看demo也可以看懂


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容