1. 程式人生 > >IOS 藍牙(GameKit、Core Bluetooth)

IOS 藍牙(GameKit、Core Bluetooth)

one col logs nali param pla star close equal

GameKit的藍牙開發註意 只能用於iOS設備之間的連接
只能用於同一個應用程序之間的連接
最好別利用藍牙發送比較大的數據

iOS中藍牙的實現方案

iOS中提供了4個框架用於實現藍牙連接
GameKit.framework(用法簡單)
? 只能用於iOS設備之間的連接,多用於遊戲(比如五子棋對戰),iOS7開始過期

MultipeerConnectivity.framework
? 只能用於iOS設備之間的連接,iOS7開始引入,主要用於文件共享(僅限於沙盒的文

件)

ExternalAccessory.framework
? 可用於第三方藍牙設備交互,但是藍牙設備必須經過蘋果MFi認證(國內較少)

CoreBluetooth.framework(時下熱門)
? 可用於第三方藍牙設備交互,必須要支持藍牙4.0
硬件至少是4s,系統至少是iOS6
藍牙4.0以低功耗著稱,一般也叫BLE(Bluetooth Low Energy) ? 目前應用比較多的案例:運動手壞、嵌入式設備、智能家居

GameKit的藍牙開發步驟 顯示可以連接的藍牙設備列表
GKPeerPickerController *ppc = [[GKPeerPickerController alloc] init]; ppc.delegate = self;
[ppc show];

在代理方法中監控藍牙的連接
- (void)peerPickerController:(GKPeerPickerController *)picker

didConnectPeer:(NSString *)peerID toSession:(GKSession *)session { NSLog(@"連接到設備:%@", peerID);
// 關閉藍牙設備顯示界面
[picker dismiss];

// 設置接收到藍牙數據後的監聽器
[session setDataReceiveHandler:self withContext:nil]; // 保存session
self.session = session;

}

處理接收到的藍牙數據
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer

inSession: (GKSession *)session context:(void *)context {
}

利用GKSession給其他設備發送數據 給指定的連接設備發送數據

- (BOOL)sendData:(NSData *) data toPeers:(NSArray *)peers
withDataMode:(GKSendDataMode)mode error:(NSError **)error;

給所有連接的設備發送數據
- (BOOL)sendDataToAllPeers:(NSData *) data withDataMode:

(GKSendDataMode)mode error:(NSError **)error;

實例:

技術分享
#import "ViewController.h"
#include <GameKit/GameKit.h>

@interface ViewController ()<UINavigationControllerDelegate, UIImagePickerControllerDelegate, GKPeerPickerControllerDelegate>
/**
 *  連接
 */
- (IBAction)connect;
/**
 *  選擇圖片
 */
- (IBAction)selectedPhoto;
/**
 *  發送
 */
- (IBAction)send;

@property (weak, nonatomic) IBOutlet UIImageView *customIV;
/**
 *  會話
 */
@property (nonatomic, strong) GKSession *session;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}


- (IBAction)connect {
    
    // 1.創建選擇其他藍牙設備的控制器
    GKPeerPickerController *peerPk = [[GKPeerPickerController alloc] init];
    // 2.成為該控制器的代理
    peerPk.delegate = self;
    // 3.顯示藍牙控制器
    [peerPk show];
}
#pragma mark - GKPeerPickerControllerDelegate
// 4.實現dialing方法
/**
 *  當藍牙設備連接成功就會調用
 *
 *  @param picker  觸發時間的控制器
 *  @param peerID  連接藍牙設備的ID
 *  @param session 連接藍牙的會話(可用通訊), 以後只要拿到session就可以傳輸數據
 */
- (void)peerPickerController:(GKPeerPickerController *)picker didConnectPeer:(NSString *)peerID toSession:(GKSession *)session
{
    NSLog(@"%@", peerID);
    // 1.保存會話
    self.session = session;
    
    // 2.設置監聽接收傳遞過來的數據
    /*
     Handler: 誰來處理接收到得數據
     withContext: 傳遞數據
     */
    [self.session setDataReceiveHandler:self withContext:nil];
    
    
    // 2.關閉顯示藍牙設備控制器
    [picker dismiss];
}
/**
 *  接收到其它設備傳遞過來的數據就會調用
 *
 *  @param data    傳遞過來的數據
 *  @param peer    傳遞數據設備的ID
 *  @param session 會話
 *  @param context 註冊監聽時傳遞的數據
 */
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context
{
//    NSLog(@"%s", __func__);
    // 1.將傳遞過來的數據轉換為圖片(註意: 因為發送的時圖片, 所以才需要轉換為圖片)
    UIImage *image = [UIImage imageWithData:data];
    self.customIV.image = image;
}


- (void)peerPickerControllerDidCancel:(GKPeerPickerController *)picker
{
    
}


- (IBAction)send {
    // 利用session發送圖片數據即可
    // 1.取出customImageView上得圖片, 轉換為二進制
    UIImage *image =  self.customIV.image;
    NSData *data = UIImagePNGRepresentation(image);
    
    /*
     GKSendDataReliable, 數據安全的發送模式, 慢
     GKSendDataUnreliable, 數據不安全的發送模式, 快
     */
    
    /*
     data: 需要發送的數據
     DataReliable: 是否安全的發送數據(發送數據的模式)
     error: 是否監聽發送錯誤
     */
    [self.session sendDataToAllPeers:data withDataMode:GKSendDataReliable error:nil];
}


- (IBAction)selectedPhoto
{
    
    // 1.創建圖片選擇控制器
    UIImagePickerController *imagePk = [[UIImagePickerController alloc] init];
    // 2.判斷圖庫是否可用打開
    if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum])
    {
        // 3.設置打開圖庫的類型
        imagePk.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
        
        imagePk.delegate = self;
        
        // 4.打開圖片選擇控制器
        [self presentViewController:imagePk animated:YES completion:nil];
    }
}
#pragma mark - UIImagePickerControllerDelegate
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
//    NSLog(@"%@", info);
    self.customIV.image = info[UIImagePickerControllerOriginalImage];
    
    [picker dismissViewControllerAnimated:YES completion:nil];
}

@end
View Code

Core Bluetooth
● Core Bluetooth測試比較麻煩,正常情況下,得至少有2臺真實的藍牙4.0設備
● 如何讓iOS模擬器也能測試藍牙4.0程序?
● 買一個CSR藍牙4.0 USB適配器,插在Mac上
● 在終端輸入sudo nvram bluetoothHostControllerSwitchBehavior="never"
● 重啟Mac
● 用Xcode 4.6調試代碼,將程序跑在iOS 6.1的模擬器上 (蘋果把iOS 7.0模擬器對BLE的支持移除掉了)
● Core Bluetooth的使用場景
● 運動手環、智能家居、嵌入式設備等等(金融刷卡器、心電測量器)
Core Bluetooth的基本常識
● 每個藍牙4.0設備都是通過服務(Service)和特征(Characteristic)來展示自己 的
● 一個設備必然包含一個或多個服務,每個服務下面又包含若幹個特征 ● 特征是與外界交互的最小單位
? 比如說,一臺藍牙4.0設備,用特征A來描述自己的出廠信息,用特征B來收發 數據
 ● 服務和特征都是用UUID來唯一標識的,通過UUID就能區別不同的服務和特征 ● 設備裏面各個服務(service)和特征(characteristic)的功能,均由藍牙設備硬件廠
商提供,比如哪些是用來交互(讀寫),哪些可獲取模塊信息(只讀)等
Core Bluetooth的開發步驟
● 建立中心設備
● 掃描外設(Discover Peripheral)
● 連接外設(Connect Peripheral)
● 掃描外設中的服務和特征(Discover Services And Characteristics)
● 利用特征與外設做數據交互(Explore And Interact)
● 斷開連接(Disconnect)
實例: 技術分享
#import "ViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>

@interface ViewController ()<CBCentralManagerDelegate, CBPeripheralDelegate>
/**
 *  外設
 */
@property (nonatomic, strong) NSMutableArray *peripherals;
/**
 *  中心管理者
 */
@property (nonatomic, strong) CBCentralManager *mgr;
@end

@implementation ViewController

- (NSMutableArray *)peripherals
{
    if (!_peripherals) {
        _peripherals = [NSMutableArray array];
    }
    return _peripherals;
}

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    // 1.創建中心設備
    CBCentralManager *mgr = [[CBCentralManager alloc] init];
    self.mgr = mgr;
    
    
    // 設置代理
    mgr.delegate = self;
    
    // 2.利用中心設備掃描外部設備
    /*
     如果指定數組代表只掃描指定的設備
     */
    [mgr scanForPeripheralsWithServices:nil options:nil];
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{

    // 保存掃描到得外部設備
    // 判斷如果數組中不包含當前掃描到得外部設置才保存
    if (![self.peripherals containsObject:peripheral]) {
        
        peripheral.delegate = self;
        [self.peripherals addObject:peripheral];
    }
}

/**
 *  模擬點擊, 然後連接所有的外設
 */
- (void)start
{
    for (CBPeripheral *peripheral in self.peripherals) {
        /**
         *  連接外設
         */
        [self.mgr connectPeripheral:peripheral options:nil];
    }
}
/**
 *  連接外設成功調用
 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
    // 掃描外設中得服務
    [peripheral discoverServices:nil];
}
/**
 *  連接外設失敗調用
 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
    
}

#pragma makr - CBPeripheralDelegate
/**
 *  只要掃描到服務就會調用
 *
 *  @param peripheral 服務所在的外設
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
    
    // 獲取外設中所有掃描到得服務
    NSArray *services = peripheral.services;
    for (CBService *service in services) {
        // 拿到需要的服務
        if ([service.UUID.UUIDString isEqualToString:@"123"])
        {
            // 從需要的服務中查找需要的特征
            // 從peripheral中得service中掃描特征
            [peripheral discoverCharacteristics:nil forService:service];
        }
    }
}

/**
 *  只要掃描到特征就會調用
 *
 *  @param peripheral 特征所屬的外設
 *  @param service    特征所屬的服務
 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
    
    // 拿到服務中所有的特診
    NSArray *characteristics =  service.characteristics;
    // 遍歷特征, 拿到需要的特征處理
    for (CBCharacteristic * characteristic in characteristics) {
        if ([characteristic.UUID.UUIDString isEqualToString:@"8888"]) {
            NSLog(@"設置鬧鐘");

        }
    }
}
@end
View Code

藍牙的現狀
● 絕大多數智能手機支持藍牙 4.0(BLE)
● 藍牙芯片發展迅速,在性能和效率方面都有很大提高,且不斷變得更小更便宜
● iBeacon + 藍牙,前景一片光明
● 應用之一:室內導航
● Estimote公司為iBeacon提供基站
● 3個iBeacon基站的預購價格為99美元(約合人民幣610元)
● Estimote公司推出的iBeacon基站的最遠傳輸距離為50m,但是他們推薦在10m 範圍內的使用效果最好
● 一塊紐扣電池就能為一個iBeacon基站提供長達 2 年的使用壽命,而且是在設 備不斷對外發射信號的情況下

IOS 藍牙(GameKit、Core Bluetooth)