日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ios 蓝牙相关

發(fā)布時(shí)間:2024/8/26 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 ios 蓝牙相关 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?


ios藍(lán)牙開發(fā)項(xiàng)目實(shí)戰(zhàn) -(附小米手環(huán)實(shí)例)

前言

最近一直在開發(fā)關(guān)于藍(lán)牙的功能,本來是不想寫這一篇文章,因?yàn)榫W(wǎng)上關(guān)于ios藍(lán)牙開發(fā)的文章實(shí)在太多了,成噸成噸的文章出現(xiàn),但是很遺憾都只是一些皮毛,或者只是簡(jiǎn)單的介紹一下基本概念而已,對(duì)于一些小白可能還有很多很多疑惑,所以萌生了寫一篇文章,并附上實(shí)際例子的demo,供即將項(xiàng)目中準(zhǔn)備開發(fā)的伙伴參考。

正文

首先

我們得明確一下很重要的幾個(gè)概念
1.當(dāng)前ios中開發(fā)藍(lán)牙所運(yùn)用的系統(tǒng)庫是<CoreBluetooth/CoreBluetooth.h>。
2.藍(lán)牙外設(shè)必須為4.0及以上,否則無法開發(fā),藍(lán)牙4.0設(shè)備因?yàn)榈秃碾?#xff0c;所以也叫做BLE。
3.CoreBluetooth框架的核心其實(shí)是兩個(gè)東西,peripheral和central, 可以理解成外設(shè)和中心,就是你的蘋果手機(jī)就是中心,外部藍(lán)牙稱為外設(shè)。
4.服務(wù)和特征(service and characteristic):簡(jiǎn)而言之,外部藍(lán)牙中它有若干個(gè)服務(wù)service(服務(wù)你可以理解為藍(lán)牙所擁有的能力),而每個(gè)服務(wù)service下?lián)碛腥舾蓚€(gè)特征characteristic(特征你可以理解為解釋這個(gè)服務(wù)的屬性)。
5.Descriptor(描述)用來描述characteristic變量的屬性。例如,一個(gè)descriptor可以規(guī)定一個(gè)可讀的描述,或者一個(gè)characteristic變量可接受的范圍,或者一個(gè)characteristic變量特定的單位。
6.跟硬件親測(cè),Ios藍(lán)牙每次最多接收155字節(jié)的數(shù)據(jù),安卓5.0以下最大接收20字節(jié),5.0以上可以更改最大接收量,能達(dá)到500多字節(jié)。

其次

通過以上關(guān)鍵信息的解釋,然后看一下藍(lán)牙的開發(fā)流程:

  • 建立中心管理者
  • 掃描外設(shè)(discover)
  • 連接外設(shè)(connect)
  • 掃描外設(shè)中的服務(wù)和特征(discover)
    4.1 獲取外設(shè)的services
    4.2 獲取外設(shè)的Characteristics,獲取Characteristics的值,
    獲取Characteristics的Descriptor和Descriptor的值
  • 與外設(shè)做數(shù)據(jù)交互(explore and interact)
  • 斷開連接(disconnect)
  • 具體代碼

    1.創(chuàng)建一個(gè)中心管理者

    //只要一觸發(fā)這句代碼系統(tǒng)會(huì)自動(dòng)檢測(cè)手機(jī)藍(lán)牙狀態(tài),你必須實(shí)現(xiàn)其代理方法,當(dāng)然得添加<CBCentralManagerDelegate> CBCentralManager *theManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil];//從這個(gè)代理方法中你可以看到所有的狀態(tài),其實(shí)我們需要的只有on和off連個(gè)狀態(tài) - (void)centralManagerDidUpdateState:(CBCentralManager *)central{ switch (central.state) { case CBCentralManagerStateUnknown: NSLog(@">>>CBCentralManagerStateUnknown"); break; case CBCentralManagerStateResetting: NSLog(@">>>CBCentralManagerStateResetting"); break; case CBCentralManagerStateUnsupported: NSLog(@">>>CBCentralManagerStateUnsupported"); break; case CBCentralManagerStateUnauthorized: NSLog(@">>>CBCentralManagerStateUnauthorized"); break; case CBCentralManagerStatePoweredOff: NSLog(@">>>CBCentralManagerStatePoweredOff"); break; case CBCentralManagerStatePoweredOn: NSLog(@">>>CBCentralManagerStatePoweredOn"); break; default: break; }

    當(dāng)發(fā)現(xiàn)藍(lán)牙狀態(tài)是開啟狀態(tài),你就可以利用中央設(shè)備進(jìn)行掃描外設(shè),如果為關(guān)閉狀態(tài),系統(tǒng)會(huì)自動(dòng)彈出讓用戶去設(shè)置藍(lán)牙,這個(gè)不需要我們開發(fā)者關(guān)心。

    2.利用中心去掃描外設(shè)

    //兩個(gè)參數(shù)為nil,默認(rèn)掃描所有的外設(shè),可以設(shè)置一些服務(wù),進(jìn)行過濾搜索[theManager scanForPeripheralsWithServices:nil options:nil];

    2.1當(dāng)掃描到外設(shè),觸發(fā)以下代理方法

    在這里需要說明的是,
    一.當(dāng)掃描到外設(shè),我們可以讀到相應(yīng)外設(shè)廣播信息,RSSI信號(hào)強(qiáng)度(可以利用RSSI計(jì)算中心和外設(shè)的距離)。
    二.我們可以根據(jù)一定的規(guī)則進(jìn)行連接,一般是默認(rèn)名字或者名字和信號(hào)強(qiáng)度的規(guī)則來連接。
    三.像我現(xiàn)在做的無鑰匙啟動(dòng)車輛鎖定車輛,就需要加密通訊,不能誰來連接都可以操作車輛,需要和外設(shè)進(jìn)行加密通訊,但是一切的通過算法的校驗(yàn)都是在和外設(shè)連接上的基礎(chǔ)上進(jìn)行,例如連接上了,你發(fā)送一種和硬件約定好的算法數(shù)據(jù),硬件接收到校驗(yàn)通過了就正常操作,無法通過則由硬件(外設(shè))主動(dòng)斷開。

    //這里默認(rèn)掃到MI,主動(dòng)連接,當(dāng)然也可以手動(dòng)觸發(fā)連接 - (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI{NSLog(@"掃描連接外設(shè):%@ %@",peripheral.name,RSSI); if ([peripheral.name hasSuffix:@"MI"]) { //保存外設(shè),并停止掃描,達(dá)到節(jié)電效果 thePerpher = peripheral; [central stopScan]; //進(jìn)行連接 [central connectPeripheral:peripheral options:nil]; } }

    3.當(dāng)連接到外設(shè),會(huì)調(diào)用以下代理方法

    這里需要說明的是
    當(dāng)成功連接到外設(shè),需要設(shè)置外設(shè)的代理,為了掃描服務(wù)調(diào)用相應(yīng)代理方法

    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{NSLog(@"連接外設(shè)成功!%@",peripheral.name);[peripheral setDelegate:self]; [peripheral discoverServices:nil]; } //連接外設(shè)失敗 -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error { NSLog(@"連接到外設(shè) 失敗!%@ %@",[peripheral name],[error localizedDescription]); }

    4.掃描外設(shè)中的服務(wù)和特征

    //掃描到服務(wù) -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{if (error){NSLog(@"掃描外設(shè)服務(wù)出錯(cuò):%@-> %@", peripheral.name, [error localizedDescription]); return; } NSLog(@"掃描到外設(shè)服務(wù):%@ -> %@",peripheral.name,peripheral.services); for (CBService *service in peripheral.services) { [peripheral discoverCharacteristics:nil forService:service]; } NSLog(@"開始掃描外設(shè)服務(wù)的特征 %@...",peripheral.name); } //掃描到特征 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{ if (error) { NSLog(@"掃描外設(shè)的特征失敗!%@->%@-> %@",peripheral.name,service.UUID, [error localizedDescription]); return; } NSLog(@"掃描到外設(shè)服務(wù)特征有:%@->%@->%@",peripheral.name,service.UUID,service.characteristics); //獲取Characteristic的值 for (CBCharacteristic *characteristic in service.characteristics){ //這里外設(shè)需要訂閱特征的通知,否則無法收到外設(shè)發(fā)送過來的數(shù)據(jù) [peripheral setNotifyValue:YES forCharacteristic:characteristic]; //這里以小米手環(huán)為例,當(dāng)我們定義好每個(gè)特征是干什么用的,我們需要讀取這個(gè)特征的值,當(dāng)特征值更新了會(huì)調(diào)用 //- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error方法 //需要說明的是UUID是硬件定義好給你,如果硬件也是個(gè)新手,那你可以先打印出所有的UUID,找出有用的 //步數(shù) if ([characteristic.UUID.UUIDString isEqualToString:@"FF06"]) { [peripheral readValueForCharacteristic:characteristic]; } //電池電量 else if ([characteristic.UUID.UUIDString isEqualToString:@"FF0C"]) { [peripheral readValueForCharacteristic:characteristic]; } else if ([characteristic.UUID.UUIDString isEqualToString:@"2A06"]) { //震動(dòng) theSakeCC = characteristic; } } } } //掃描到具體的值->通訊主要的獲取數(shù)據(jù)的方法 - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error { if (error) { NSLog(@"掃描外設(shè)的特征失敗!%@-> %@",peripheral.name, [error localizedDescription]); return; } NSLog(@"%@ %@",characteristic.UUID.UUIDString,characteristic.value); if ([characteristic.UUID.UUIDString isEqualToString:@"FF06"]) { Byte *steBytes = (Byte *)characteristic.value.bytes; int steps = bytesValueToInt(steBytes); } else if ([characteristic.UUID.UUIDString isEqualToString: @"FF0C"]) { Byte *bufferBytes = (Byte *)characteristic.value.bytes; int buterys = bytesValueToInt(bufferBytes)&0xff; NSLog(@"電池:%d%%",buterys); } else if ([characteristic.UUID.UUIDString isEqualToString:@"2A06"]) { Byte *infoByts = (Byte *)characteristic.value.bytes; //這里解析infoByts得到設(shè)備信息 } }

    5.與外設(shè)做數(shù)據(jù)交互

    需要說明的是蘋果官方提供發(fā)送數(shù)據(jù)的方法很簡(jiǎn)單,只需要調(diào)用下面的方法

    - (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type

    我們只需要在搜索每個(gè)服務(wù)的特征,記錄這個(gè)特征,然后向這個(gè)特征發(fā)送數(shù)據(jù)就可以了。

    6.斷開連接

    調(diào)用以下代碼,需要說明的是中心斷開與外設(shè)的連接。

    - (void)cancelPeripheralConnection:(CBPeripheral *)peripheral;

    以上呢是整個(gè)藍(lán)牙的開發(fā)過程,系統(tǒng)提供的框架api就這么多,開發(fā)起來也不是很難,要是你認(rèn)為這篇文章到這里就結(jié)束了,你就大錯(cuò)特錯(cuò)了,這篇文章的精華內(nèi)容將從這里開始,由于公司項(xiàng)目的保密性,我不能以它為例,那我就以小米手環(huán)為實(shí)例,主要分享一下數(shù)據(jù)解析。

    精華部分

    1.當(dāng)調(diào)用了以下代理方法的時(shí)候,我們需要處理接收到的數(shù)據(jù)

    - (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error

    小米手環(huán)所定義的幾個(gè)UUID如下:

    @"FF06" 這個(gè)UUID定義的是步數(shù)
    @"FF0C" 這個(gè)UUID定義的是電量
    @"2A06"這個(gè)UUID定義的是震動(dòng)
    @"FF01"這個(gè)UUID定義的是相關(guān)的設(shè)備信息

    通過以上的UUID,我們可以讀取到步數(shù),電量,操作手環(huán)震動(dòng),并讀取手環(huán)相應(yīng)設(shè)備的信息,這里需要說明的是我并不清楚設(shè)備信息的具體協(xié)議,所以這里沒法解析。

    if ([characteristic.UUID.UUIDString isEqualToString:STEP]) {Byte *steBytes = (Byte *)characteristic.value.bytes; int steps = bytesValueToInt(steBytes); NSLog(@"步數(shù):%d",steps); }

    當(dāng)我們讀到步數(shù)這個(gè)UUID時(shí),我們可以拿到value,小米手環(huán)所定義的協(xié)議是4個(gè)字節(jié),我們需要將4個(gè)字節(jié)轉(zhuǎn)換為int 類型即可
    方法如下

    //4個(gè)字節(jié)Bytes 轉(zhuǎn) int unsigned int bytesValueToInt(Byte *bytesValue) { unsigned int intV; intV = (unsigned int ) ( ((bytesValue[3] & 0xff)<<24) |((bytesValue[2] & 0xff)<<16) |((bytesValue[1] & 0xff)<<8) |(bytesValue[0] & 0xff)); return intV; }

    需要說明的是這個(gè)方法是C語言的方法,采用位與運(yùn)算,當(dāng)然如果項(xiàng)目中需要另一種方式的轉(zhuǎn)換,如:發(fā)過來兩字節(jié)需要你轉(zhuǎn)換為int,如果你不會(huì)轉(zhuǎn)換,可以去網(wǎng)上搜索,我會(huì)在文章后附一些常用的轉(zhuǎn)換方法。
    這里重點(diǎn)說明的是步數(shù)讀取,剩余類似。

    2.當(dāng)我們給外設(shè)發(fā)送數(shù)據(jù)時(shí),我們需要跟硬件定協(xié)議,當(dāng)然這是在開始項(xiàng)目之前做好的事情。

    小米手環(huán)協(xié)議中震動(dòng)命令的觸發(fā),是向硬件發(fā)送一個(gè)10進(jìn)制的 2
    這里需要說明的是我們發(fā)送數(shù)據(jù)給硬件一般是字節(jié)數(shù)組,然后將他轉(zhuǎn)換為NSData發(fā)送。

    //這里為了嚴(yán)謹(jǐn)起見,需要判斷外設(shè)和特征是否存在,如果存在發(fā)送數(shù)據(jù)if (thePerpher && theSakeCC) {Byte zd[1] = {2}; NSData *theData = [NSData dataWithBytes:zd length:1]; [thePerpher writeValue:theData forCharacteristic:theSakeCC type:CBCharacteristicWriteWithoutResponse]; }

    這里需要再添加一點(diǎn),如果協(xié)議要求你發(fā)ASCII碼,例如‘SHAKE’,只需要這么處理

    if (thePerpher && theSakeCC) {Byte zd[] = {'S','H','A','K','E'}; NSData *theData = [NSData dataWithBytes:zd length:1]; [thePerpher writeValue:theData forCharacteristic:theSakeCC type:CBCharacteristicWriteWithoutResponse]; }

    3.項(xiàng)目中實(shí)際開發(fā)的運(yùn)用
    當(dāng)我們面對(duì)實(shí)際開發(fā)時(shí),我們不可能這么直接去在一個(gè)控制器中去寫這么多代碼,如果你說這沒多少啊,那我無話可說了?。。。當(dāng)然有人會(huì)說運(yùn)用三方庫的啊,babyBluetooth在github上star還是挺高的,我的觀點(diǎn)是沒有必要去依賴所謂的三方庫,有的三方庫高度封裝性會(huì)致使我們?nèi)绻龅藉e(cuò)誤時(shí)無法排查,所以三方庫慎用,當(dāng)然你可以參考一些Star很高的三方庫,看大神的代碼思想,有利于自己讀寫代碼的能力。

    我的主要思路是封裝一個(gè)單例類,封裝好掃描的方法,讀取數(shù)據(jù)的方法(一般是代理回調(diào)),發(fā)送指令(例如小米的震動(dòng))方法,而在讀取數(shù)據(jù)中我們可以采用模型的思想,當(dāng)收到藍(lán)牙外設(shè)發(fā)送過來的數(shù)據(jù)時(shí)候,我們解析完后包裝成模型通過代理回傳過去,以后我們?cè)诳刂破髦忻看文玫降亩际悄P蛿?shù)據(jù),這樣處理起來方便大大的。
    下面將小米手環(huán)demo附上,供需要的朋友參考學(xué)習(xí),如果文章中我有什么沒有說的很明白,或者什么疑惑可以留言。

    demo https://github.com/markdashi/MIBLE

    附常用轉(zhuǎn)換方法

    @interface NSString (Extension)//16進(jìn)制字符串轉(zhuǎn)成int - (int)convertHexStringToINT; //16進(jìn)制字符串轉(zhuǎn)換為2進(jìn)制的字符串 - (NSString *)getBinaryByhex; @end @implementation NSString (Extension) //不考慮內(nèi)存溢出 - (int)convertHexStringToINT { UInt64 mac1 = strtoul([self UTF8String], 0, 16); return [[NSString stringWithFormat:@"%llu",mac1] intValue]; } - (NSString *)getBinaryByhex { NSMutableDictionary *hexDic = [[NSMutableDictionary alloc] init]; hexDic = [[NSMutableDictionary alloc] initWithCapacity:16]; [hexDic setObject:@"0000" forKey:@"0"]; [hexDic setObject:@"0001" forKey:@"1"]; [hexDic setObject:@"0010" forKey:@"2"]; [hexDic setObject:@"0011" forKey:@"3"]; [hexDic setObject:@"0100" forKey:@"4"]; [hexDic setObject:@"0101" forKey:@"5"]; [hexDic setObject:@"0110" forKey:@"6"]; [hexDic setObject:@"0111" forKey:@"7"]; [hexDic setObject:@"1000" forKey:@"8"]; [hexDic setObject:@"1001" forKey:@"9"]; [hexDic setObject:@"1010" forKey:@"A"]; [hexDic setObject:@"1011" forKey:@"B"]; [hexDic setObject:@"1100" forKey:@"C"]; [hexDic setObject:@"1101" forKey:@"D"]; [hexDic setObject:@"1110" forKey:@"E"]; [hexDic setObject:@"1111" forKey:@"F"]; NSMutableString *binaryString=[[NSMutableString alloc] init]; for (int i=0; i<[self length]; i++) { NSRange rage; rage.length = 1; rage.location = i; NSString *key = [self substringWithRange:rage]; binaryString = (NSMutableString *)[NSString stringWithFormat:@"%@%@",binaryString,[NSString stringWithFormat:@"%@",[hexDic objectForKey:key]]]; } return binaryString; } //NSData轉(zhuǎn)換為16進(jìn)制字符串,NSData的分類 - (NSString *)dataToHexString { NSUInteger len = [self length]; char * chars = (char *)[self bytes]; NSMutableString * hexString = [[NSMutableString alloc] init]; for(NSUInteger i = 0; i < len; i++ ) [hexString appendString:[NSString stringWithFormat:@"%0.2hhx", chars[i]]]; return hexString; }

    2016.8.29補(bǔ)充

    由于項(xiàng)目中需要做關(guān)于后臺(tái)持續(xù)掃描,類似于常見的藍(lán)牙音箱,打開手機(jī)APP連接藍(lán)牙音箱,播放音樂,當(dāng)手機(jī)遠(yuǎn)離藍(lán)牙音箱后,停止播放,當(dāng)手機(jī)靠近的時(shí)候,藍(lán)牙音箱又開始播放了,對(duì)于這中需求的實(shí)現(xiàn)我開始很困惑,藍(lán)牙如何后臺(tái)持續(xù)掃描呢,我嘗試了很多方法是失敗的,經(jīng)過我多方面查詢資料弄清楚如何實(shí)現(xiàn)這個(gè)需求:

    1.需要后臺(tái)運(yùn)行需申請(qǐng)后臺(tái)權(quán)限



    勾選即可擁有后臺(tái)權(quán)限,如果外設(shè)持續(xù)發(fā)送數(shù)據(jù),APP端可以接收到數(shù)據(jù)。

    2.掃描時(shí)需指定serviceUUID,需外設(shè)廣播出自己的SeviceUUID,APP端作為掃描的條件。

    這是蘋果掃描方法的的官方解釋:

    Applications that have specified the bluetooth-central background mode are allowed to scan while backgrounded, with two

    • caveats: the scan must specify one or more service types in serviceUUIDs, and the CBCentralManagerScanOptionAllowDuplicatesKey
    • scan option will be ignored.

    顯而易見的說的很清楚,后臺(tái)模式時(shí)藍(lán)牙作為central,必須指定serviceUUIDs,scan option忽略。


    例子

    掃描方法:

    [self.centralManger scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:@"FEE0"],[CBUUID UUIDWithString:@"FEE7"]] options:nil];

    這樣當(dāng)在后臺(tái)的時(shí)候是可以持續(xù)掃描的。

    3.當(dāng)后臺(tái)斷開連接時(shí)候會(huì)調(diào)用系統(tǒng)方法

    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error

    我們需要在這里設(shè)置自動(dòng)重連,即可實(shí)現(xiàn)上述的需求。

    2016.10.19補(bǔ)充

    關(guān)于藍(lán)牙需要做一些補(bǔ)充及修改,首先對(duì)于后臺(tái)掃描時(shí)候,我們申請(qǐng)后臺(tái)權(quán)限只需要申請(qǐng)


    權(quán)限申請(qǐng)

    如果兩個(gè)都申請(qǐng),如果提交安裝包到APPStore,你會(huì)被拒絕

    If your app is meant to work with external hardware, supported protocols must be included in the UISupportedExternalAccessoryProtocols key in your app's Info.plist file - and the hardware's PPID # should be provided in the Review Notes field of your app in iTunes Connect.
    Additionally, your app must be authorized by MFi to use the desired hardware. If you are not yet in the MFi Program, you can enroll at MFi program.
    Please either revise your Info.plist to include the UISupportedExternalAccessoryProtocols key and update your Review Notes to include the PPID # - or remove the external-accessory value from the UIBackgroundModes key.

    轉(zhuǎn)載于:https://www.cnblogs.com/zyjzyj/p/6029968.html

    總結(jié)

    以上是生活随笔為你收集整理的ios 蓝牙相关的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。