首页 > 移动平台 > 详细

基于swift语言iOS8的蓝牙连接(初步)

时间:2014-08-18 21:41:53      阅读:716      评论:0      收藏:0      [点我收藏+]

  看过一些蓝牙App的事例,大体上对蓝牙的连接过程进行了了解。但是开始真正自己写一个小的BLE程序的时候就举步维艰了。那些模棱两可的概念在头脑中瞬间就蒸发了,所以还是决定从最基本的蓝牙连接过程进行。这里所说的蓝牙是针对 bluetooth 4.0的。

  第一步就是去看官方的关于蓝牙框架的文档,即Core Bluetooth Programming Guide在苹果的官方网站上可以轻松找到,不管你对蓝牙的基本概念是否有了解,这个文件可以使你更好的对蓝牙的连接过程有个了解。这个文档的前面几张介绍了关于bluetooth 4.0开发过程中必要的概念(这些概念必不可少,一定要搞懂,否则后面会搞得很乱),真正开始将连接过程是从Performing Common Central Role Tasks 这一章开始,这里很清晰的将过程分成了以下这么几步,每一步对有对应的接口函数,只需要按着这一步一步写下去就可以。

  • Start up a central manager object
  • Discover and connect to peripheral devices that are advertising
  • Explore the data on a peripheral device after you’ve connected to it
  • Send read and write requests to a characteristic value of a peripheral’s service
  • Subscribe to a characteristic’s value to be notified when it is updated

  针对每一步我将我的Swift所对应的代码列出来:

Starting Up a Central Manager 

将CBCenteralManager实例化,如下:

1     //start up a central manager object
2     func startCentralManager(){
3         myCentralManager = CBCentralManager(delegate: self, queue: nil)
4     }

当实例化成功后,对调用如下函数:

 1     func centralManagerDidUpdateState(central: CBCentralManager!){
 2         println("CentralManager is initialized")
 3         
 4         switch central.state{
 5         case CBCentralManagerState.Unauthorized:
 6             println("The app is not authorized to use Bluetooth low energy.")
 7         case CBCentralManagerState.PoweredOff:
 8             println("Bluetooth is currently powered off.")
 9         case CBCentralManagerState.PoweredOn:
10             println("Bluetooth is currently powered on and available to use.")
11         default:break
12         }
13     }

此时CBCenteralManager实例化完毕,就可以开始进行扫描外设了。

2、Discovering Peripheral Devices That Are Advertising 

OC中扫描函数是:

1 [myCentralManager scanForPeripheralsWithServices:nil options:nil];

在swift中对应的是:

1 myCentralManager!.scanForPeripheralsWithServices(nil , options: nil)

当发现设备后,会调用如下的函数,这样我们就可以对设备进行一定的操作,当发现设备后接下来应该是连接设备。

 1     func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
 2         println("CenCentalManagerDelegate didDiscoverPeripheral")
 3         println("Discovered \(peripheral.name)")
 4         println("Rssi: \(RSSI)")
 5         
 6         println("Stop scan the Ble Devices")
 7         myCentralManager!.stopScan()
 8         cbPeripheral = peripheral
 9 
10     }

 

3、Connecting to a Peripheral Device After You’ve Discovered It 

OC中连接设备的代码是:

1 [myCentralManager connectPeripheral:peripheral options:nil];

Swift中的代码是:

1 myCentralManager!.connectPeripheral(cbPeripheral!, options: nil)

连接的结果会调用下面三个回调函数

 1     func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
 2         println("CenCentalManagerDelegate didConnectPeripheral")
 3         println("Connected with \(peripheral.name)")
 4         peripheral.delegate = self
 5         
 6         peripheral.discoverServices(nil)
 7     }
 8     
 9     func centralManager(central: CBCentralManager!, didFailToConnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
10         println("CenCentalManagerDelegate didFailToConnectPeripheral")
11     }
12     
13     func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
14         println("CenCentalManagerDelegate didDisconnectPeripheral")
15     }

具体含义可以查询官方文档。

注意:连接设备的函数不能在didDiscoverPeripheral回调函数中直接调用,这样是无法连接的,这个问题也困扰了我一会,后面歪打正着就成了, 要么设一个延迟再去connect,要么加个按钮触发事件再去连接。

 

4、Discovering the Services of a Peripheral That You’re Connected To

当连接完毕后,就是扫描这个外设(Peripheral)所支持服务。然后可以保存下来,下次可以直接调用

OC中扫描Services的代码如下:

1 ?[peripheral discoverServices:nil];

Swift中如下:

1 peripheral.discoverServices(nil)

当扫描到Service后对调用下面的回调函数:

 1 func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
 2         println("CBPeripheralDelegate didDiscoverServices")
 3         for  service in peripheral.services {
 4             println("Discover service \(service)")
 5             println("UUID \(service.UUID)")
 6             if(service.UUID == CBUUID.UUIDWithString("1802")){
 7                 println("Immediate_Alert_Service")
 8                 immediateAlertService = (service as CBService)
 9                 peripheral.discoverCharacteristics(nil , forService: immediateAlertService)
10             }else if(service.UUID == CBUUID.UUIDWithString("1803")){
11                 println("Link_Loss_Service")
12                 linkLossAlertService = (service as CBService)
13                 peripheral.discoverCharacteristics(nil , forService: linkLossAlertService)
14             }
15         }
16     }

扫描到Service后,要遍历这个Service所包含的Characteristics。

5、Discovering the Characteristics of a Service 

扫描Characteristics

1  peripheral.discoverCharacteristics(nil , forService: linkLossAlertService)

和扫描Services一样,会有回调函数

 1 func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
 2         for characteristic in service.characteristics{
 3             
 4             if(service == immediateAlertService && characteristic.UUID == CBUUID.UUIDWithString("2A06")){
 5                 println("immediateAlertService Discover characteristic \(characteristic)")
 6                 alertLevelCharacteristic = (characteristic as CBCharacteristic)
 7                 //immediateAlertCharacter 写入是有问题的
 8 //                cbPeripheral!.writeValue(NSData(bytes: &alertLevel, length: 1), forCharacteristic: characteristic as CBCharacteristic, type: CBCharacteristicWriteType.WithResponse)
 9             }else if(service == linkLossAlertService && characteristic.UUID == CBUUID.UUIDWithString("2A06")){
10                 println("linkLossAlertService Discover characteristic \(characteristic)")
11                 linkLossAlertCharacteristic = (characteristic as CBCharacteristic)
12                 //linkLossAlertCharacteristic 写入没有问题,所以通过这个写入来进行绑定
13                 cbPeripheral!.writeValue(NSData(bytes: &alertLevel, length: 1), forCharacteristic: characteristic as CBCharacteristic, type: CBCharacteristicWriteType.WithResponse)
14             }
15         }
16     }

这样就把每个Service所对应的Characteristics给读取出来了。

6、Retrieving the Value of a Characteristic 

  • Reading the Value of a Characteristic 
1 cbPeripheral!.readValueForCharacteristic(alertLevelCharacteristic!)

值在回调函数中获取,在read之前,要注意这个Characteristic是否可读。

1     func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
2         if(error != nil){
3             println("Error Reading characteristic value: \(error.localizedDescription)")
4         }else{
5             var data = characteristic.value
6             println("Update value is \(data)")
7         }
8         
9     }
  • Writing the Value of a Characteristic 
1 var alertLevel:Byte = 0x02 
2 cbPeripheral!.writeValue(NSData(bytes: &alertLevel, length: 1), forCharacteristic: alertLevelCharacteristic!, type: CBCharacteristicWriteType.WithoutResponse)

写是否成功会根据CBCharacteristicWriteType来决定是否调用下面的回调函数:

1 func peripheral(peripheral: CBPeripheral!, didWriteValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
2         if(error != nil){
3             println("Error writing characteristic value: \(error.localizedDescription)")
4         }else{
5             println("Write value success!")
6         }
7         
8     }

关于write我这里还有些注意的地方要强调!!!!

并不是每一个Characteristic都可以通过回调函数来查看它写入状态的。就比如针对 immediateAlertService(1802) 的 alertLevelCharacteristic(2A06),就是一个不能有response的Characteristic。刚开始我就一直用CBCharacteristicWriteType.WithResponse来进行写入始终不成功,郁闷坏了,最后看到每个Characteristic还有个属性值是指示这个的,我将每个Characteristic打印出来有如下信息:

immediateAlertService Discover characteristic <CBCharacteristic: 0x15574d00, UUID = 2A06, properties = 0x4, value = (null), notifying = NO>
linkLossAlertService Discover characteristic <CBCharacteristic: 0x15671d00, UUID = 2A06, properties = 0xA, value = (null), notifying = NO>

这个的properties是什么刚开始不知道,觉得他没意义,后面才注意到properties是Characteristic的一个参数,具体解释如下:

Declaration
SWIFT
struct CBCharacteristicProperties : RawOptionSetType {
    init(_ value: UInt)
    var value: UInt
    static var Broadcast: CBCharacteristicProperties { get }
    static var Read: CBCharacteristicProperties { get }
    static var WriteWithoutResponse: CBCharacteristicProperties { get }
    static var Write: CBCharacteristicProperties { get }
    static var Notify: CBCharacteristicProperties { get }
    static var Indicate: CBCharacteristicProperties { get }
    static var AuthenticatedSignedWrites: CBCharacteristicProperties { get }
    static var ExtendedProperties: CBCharacteristicProperties { get }
    static var NotifyEncryptionRequired: CBCharacteristicProperties { get }
    static var IndicateEncryptionRequired: CBCharacteristicProperties { get }
}
OBJECTIVE-C
typedef enum {
   CBCharacteristicPropertyBroadcast = 0x01,
   CBCharacteristicPropertyRead = 0x02,
   CBCharacteristicPropertyWriteWithoutResponse = 0x04,
   CBCharacteristicPropertyWrite = 0x08,
   CBCharacteristicPropertyNotify = 0x10,
   CBCharacteristicPropertyIndicate = 0x20,
   CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
   CBCharacteristicPropertyExtendedProperties = 0x80,
   CBCharacteristicPropertyNotifyEncryptionRequired = 0x100,
   CBCharacteristicPropertyIndicateEncryptionRequired = 0x200,
} CBCharacteristicProperties;

 

可以看到0x04对应的是CBCharacteristicPropertyWriteWithoutResponse

           0x0A对应的是CBCharacteristicPropertyNotify

所以 immediateAlertService(1802) 的 alertLevelCharacteristic(2A06)是不能用CBCharacteristicWriteType.WithRespons进行写入,只能用CBCharacteristicWriteType.WithOutRespons。这样在以后的开发中可以对每个Characteristic的这个参数进行检查再进行设置。

 

最后讲一下关于蓝牙绑定的过程,在iOS中,没有讲当绑定的过程,直接就是扫描、连接、交互。从而很多人会认为,连接就是绑定了,其实不然。在iOS开发中,连接并没有完成绑定,在网上找到了个很好的解释:

you cannot initiate pairing from the iOS central side. Instead, you have to read/write a characteristic value,
and then let your peripheral respond with an "Insufficient Authentication" error.
iOS will then initiate pairing, will store the keys for later use (bonding) and encrypts the link. As far as I know,
it also caches discovery information, so that future connections can be set up faster.

就是当发生读写交互时,系统在会和外设进行绑定操作!!!

 

下面我将我一天的源码贴出来,里面将整个连接过程简陋的实现了一遍。

  1 //
  2 //  ViewController.swift
  3 //  BleTest
  4 //
  5 
  6 import UIKit
  7 import CoreBluetooth
  8 
  9 class ViewController: UIViewController ,CBCentralManagerDelegate, CBPeripheralDelegate{
 10     
 11     var myCentralManager: CBCentralManager?
 12     let Link_Loss_Service = "1803"
 13     let Immediate_Alert_Service = "1802"
 14     let Alert_Level_Characteristic = "2A06"
 15 
 16     
 17     var cbPeripheral: CBPeripheral?
 18     
 19     var linkLossAlertService: CBService?
 20     var linkLossAlertCharacteristic: CBCharacteristic?
 21     
 22     var immediateAlertService: CBService?
 23     var alertLevelCharacteristic: CBCharacteristic?
 24     
 25     enum ElegantTagAlertLevel: Byte{
 26     case ELEGANT_TAG_ALERT_LEVEL_NONE = 0x00
 27     case ELEGANT_TAG_ALERT_LEVEL_MILD = 0x01
 28     case ELEGANT_TAG_ALERT_LEVEL_HIGH = 0x02
 29     } //TAG警报强度
 30     
 31     var alertLevel:Byte = 0x02
 32 //MARK: life cycle
 33     override func viewDidLoad() {
 34         super.viewDidLoad()
 35         // Do any additional setup after loading the view, typically from a nib.
 36         startCentralManager()
 37     }
 38     
 39     override func viewDidDisappear(animated: Bool) {
 40         if myCentralManager != nil{
 41             myCentralManager!.stopScan()
 42             println("Stop scan the Ble Devices")
 43         }
 44     }
 45     
 46 //MARK: Initialized
 47     
 48     //start up a central manager object
 49     func startCentralManager(){
 50         myCentralManager = CBCentralManager(delegate: self, queue: nil)
 51     }
 52 
 53     
 54 //MARK: IBAction function
 55     //Begin to scan the ble devices
 56     @IBAction func scanBleDevice(sender: AnyObject) {
 57         println("Begin scan the Ble Devices")
 58         //Discoering Peripheral Devices That Are Advertising
 59         let Link_Loss_UUID = CBUUID.UUIDWithString(Link_Loss_Service)
 60         let Immediate_Alert_UUID = CBUUID.UUIDWithString(Immediate_Alert_Service)
 61         myCentralManager!.stopScan()
 62         myCentralManager!.scanForPeripheralsWithServices([Link_Loss_UUID, Immediate_Alert_UUID] , options: nil)
 63         
 64     }
 65     
 66     @IBAction func connectBleDevice(sender: AnyObject) {
 67         //connect to a peripheral device
 68         println("connect to a peripheral device")
 69         myCentralManager!.connectPeripheral(cbPeripheral!, options: nil)
 70     }
 71     
 72     @IBAction func readValue(sender: UIButton) {
 73         println("Reading value for characteristic")
 74         //alertLevelCharacteristic不允许读取
 75         cbPeripheral!.readValueForCharacteristic(alertLevelCharacteristic!)
 76         cbPeripheral!.readRSSI()
 77     }
 78     
 79     @IBAction func alert(sender: UIButton) {
 80         if(alertLevelCharacteristic != nil){
 81             println("Write value")
 82             println("Characteristic \(alertLevelCharacteristic)")
 83             
 84             cbPeripheral!.writeValue(NSData(bytes: &alertLevel, length: 1), forCharacteristic: alertLevelCharacteristic!, type: CBCharacteristicWriteType.WithoutResponse)
 85         }else{
 86             println("alertLevelCharacteristic is nil")
 87         }
 88         
 89     }
 90     
 91 //MARK: CenCentalManagerDelegate
 92     //Explore the data on a peripheral device after you‘ve connected to it
 93     
 94     //Send read and write requests to a characteristic value of a peripheral‘s service
 95     
 96     //Subscribe to a characteristic‘s value to be notified when it is updated
 97     
 98     //CBCentralManager initialized
 99     func centralManagerDidUpdateState(central: CBCentralManager!){
100         println("CentralManager is initialized")
101         
102         switch central.state{
103         case CBCentralManagerState.Unauthorized:
104             println("The app is not authorized to use Bluetooth low energy.")
105         case CBCentralManagerState.PoweredOff:
106             println("Bluetooth is currently powered off.")
107         case CBCentralManagerState.PoweredOn:
108             println("Bluetooth is currently powered on and available to use.")
109         default:break
110         }
111     }
112 
113     func centralManager(central: CBCentralManager!, didDiscoverPeripheral peripheral: CBPeripheral!, advertisementData: [NSObject : AnyObject]!, RSSI: NSNumber!) {
114         println("CenCentalManagerDelegate didDiscoverPeripheral")
115         println("Discovered \(peripheral.name)")
116         println("Rssi: \(RSSI)")
117         
118         println("Stop scan the Ble Devices")
119         myCentralManager!.stopScan()
120         cbPeripheral = peripheral
121 
122     }
123     
124     func centralManager(central: CBCentralManager!, didConnectPeripheral peripheral: CBPeripheral!) {
125         println("CenCentalManagerDelegate didConnectPeripheral")
126         println("Connected with \(peripheral.name)")
127         peripheral.delegate = self
128         
129         peripheral.discoverServices(nil)
130     }
131     
132     func centralManager(central: CBCentralManager!, didFailToConnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
133         println("CenCentalManagerDelegate didFailToConnectPeripheral")
134     }
135     
136     func centralManager(central: CBCentralManager!, didDisconnectPeripheral peripheral: CBPeripheral!, error: NSError!) {
137         println("CenCentalManagerDelegate didDisconnectPeripheral")
138     }
139     
140 //MARK: CBPeripheralDelegate
141     func peripheral(peripheral: CBPeripheral!, didDiscoverServices error: NSError!) {
142         println("CBPeripheralDelegate didDiscoverServices")
143         for  service in peripheral.services {
144             println("Discover service \(service)")
145             println("UUID \(service.UUID)")
146             if(service.UUID == CBUUID.UUIDWithString("1802")){
147                 println("Immediate_Alert_Service")
148                 immediateAlertService = (service as CBService)
149                 peripheral.discoverCharacteristics(nil , forService: immediateAlertService)
150             }else if(service.UUID == CBUUID.UUIDWithString("1803")){
151                 println("Link_Loss_Service")
152                 linkLossAlertService = (service as CBService)
153                 peripheral.discoverCharacteristics(nil , forService: linkLossAlertService)
154             }
155         }
156     }
157     
158     func peripheral(peripheral: CBPeripheral!, didDiscoverCharacteristicsForService service: CBService!, error: NSError!) {
159         for characteristic in service.characteristics{
160             
161             if(service == immediateAlertService && characteristic.UUID == CBUUID.UUIDWithString("2A06")){
162                 println("immediateAlertService Discover characteristic \(characteristic)")
163                 alertLevelCharacteristic = (characteristic as CBCharacteristic)
164                 //immediateAlertCharacter 写入是有问题的
165 //                cbPeripheral!.writeValue(NSData(bytes: &alertLevel, length: 1), forCharacteristic: characteristic as CBCharacteristic, type: CBCharacteristicWriteType.WithResponse)
166             }else if(service == linkLossAlertService && characteristic.UUID == CBUUID.UUIDWithString("2A06")){
167                 println("linkLossAlertService Discover characteristic \(characteristic)")
168                 linkLossAlertCharacteristic = (characteristic as CBCharacteristic)
169                 //linkLossAlertCharacteristic 写入没有问题,所以通过这个写入来进行绑定
170                 cbPeripheral!.writeValue(NSData(bytes: &alertLevel, length: 1), forCharacteristic: characteristic as CBCharacteristic, type: CBCharacteristicWriteType.WithResponse)
171             }
172             
173         }
174     }
175     
176     func peripheral(peripheral: CBPeripheral!, didUpdateValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
177         if(error != nil){
178             println("Error Reading characteristic value: \(error.localizedDescription)")
179         }else{
180             var data = characteristic.value
181             println("Update value is \(data)")
182         }
183         
184     }
185     
186     func peripheral(peripheral: CBPeripheral!, didReadRSSI RSSI: NSNumber!, error: NSError!) {
187         println("CBPeripheralDelegate didReadRSSI")
188         println("RSSI value: \(RSSI)")
189     }
190     
191     func peripheral(peripheral: CBPeripheral!, didWriteValueForCharacteristic characteristic: CBCharacteristic!, error: NSError!) {
192         if(error != nil){
193             println("Error writing characteristic value: \(error.localizedDescription)")
194         }else{
195             println("Write value success!")
196         }
197         
198     }
199 
200 
201 }

界面如下:

bubuko.com,布布扣

这里简单的对应着代码俩面的三个IBAction,应该一看就明白。

 

这个文档写的有点乱,我也是刚开始写,等以后思路清晰了,再将蓝牙这块好好整理一遍。

 

 

 

 

基于swift语言iOS8的蓝牙连接(初步),布布扣,bubuko.com

基于swift语言iOS8的蓝牙连接(初步)

原文:http://www.cnblogs.com/scaptain/p/3920391.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!