IOS 藍牙(GameKit、Core Bluetooth)
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;
實例:
![技術分享](/img/jia.gif)
#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]; } @endView 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)
實例:
![技術分享](/img/jia.gif)
#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(@"設置鬧鐘"); } } } @endView Code
藍牙的現狀
● 絕大多數智能手機支持藍牙 4.0(BLE)
● 藍牙芯片發展迅速,在性能和效率方面都有很大提高,且不斷變得更小更便宜
● iBeacon + 藍牙,前景一片光明
● 應用之一:室內導航
● Estimote公司為iBeacon提供基站
● 3個iBeacon基站的預購價格為99美元(約合人民幣610元)
● Estimote公司推出的iBeacon基站的最遠傳輸距離為50m,但是他們推薦在10m 範圍內的使用效果最好
● 一塊紐扣電池就能為一個iBeacon基站提供長達 2 年的使用壽命,而且是在設 備不斷對外發射信號的情況下
IOS 藍牙(GameKit、Core Bluetooth)