? 藍牙開發分為兩種模式:管理者模式和中心者模式。管理者模式基本很少用到,相當於iPhone手機作為外設,自己創建服務和特性,然後用其他設備連接iPhone手機;中心者模式壹般是大部分情況下都會使用的,使用中心者模式開發相當於iPhone手機作為主機,連接藍牙外設,下面介紹藍牙開發的例子就是使用的中心者模式來講解的。
在這裏我還是要推薦下我自己建的iOS開發學習群:680565220,群裏都是學ios開發的,如果妳正在學習ios ,我歡迎妳加入,今天分享的這個案例已經上傳到群文件,大家都是軟件開發黨,不定期分享幹貨(只有iOS軟件開發相關的),包括我自己整理的壹份2018最新的iOS進階資料和高級開發教程
壹、關於藍牙開發的壹些重要的理論概念:
1、服務(services):藍牙外設對外廣播的時候壹定會有壹個服務,有些時候也可以是有多個服務,服務下面包含壹些特性,服務可以理解成壹個模塊的窗口;
2、特征(characteristic):特征存在於服務下面的,壹個服務下面可以有多個特征,特征可以理解成具體實現功能的窗口,壹般的特性都會有value,也就是特征值,是特征和外界交互的最小單位;
? 3、UUID:藍牙上的唯壹標示符,為了區分不同服務和特征,就用UUID來表示。
二、藍牙連接的主要步驟
?1、創建壹個CBCentralManager實例來進行藍牙管理;
?2、搜索掃描外圍設備;
?3、連接外圍設備;
?4、獲得外圍設備的服務;
?5、獲得服務的特征;
?6、從外圍設備讀取數據;
?7、給外圍設備發送(寫入)數據。
三、藍牙連接和數據讀寫的具體步驟
?1、導入蘋果系統藍牙框架
#import
?2、遵循兩個藍牙框架相關的協議
?3、新建兩個實例屬性,壹個特征屬性
@property (nonatomic, strong) CBCentralManager *centralManager; //中心管理者
@property (nonatomic, strong) CBPeripheral *peripheral; //連接到的外設
@property (nonatomic, strong) CBCharacteristic *characteristic; //特征
?4、初始化CBCentralManager,進行藍牙管理
- (void)viewDidLoad {
[super viewDidLoad];
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]; //創建實例進行藍牙管理
}
?//若中心管理者初始化之後 就會觸發下面這個代理方法 該代理方法是用來判斷手機藍牙的狀態的
- (void)centralManagerDidUpdateState:(CBCentralManager *)central {
// 藍牙可用,開始掃描外設
if (central.state == CBManagerStatePoweredOn) {
NSLog(@"藍牙可用");
//在中心管理者成功開啟之後再進行壹些操作
//搜索掃描外設
// 根據SERVICE_UUID來掃描外設,如果不設置SERVICE_UUID,則掃描所有藍牙設備
// [self.centralManager startAdvertising:@{CBAdvertisementDataServiceUUIDsKey:@[[CBUUID UUIDWithString:SERVICE_UUID]]}];
[central scanForPeripheralsWithServices:nil options:nil];
}
if(central.state == CBManagerStateUnsupported) {
NSLog(@"該設備不支持藍牙");
}
if (central.state == CBManagerStatePoweredOff) {
NSLog(@"藍牙已關閉");
}
if (central.state == CBManagerStateUnknown) {
NSLog(@"藍牙當前狀態不明確");
}
if (central.state == CBManagerStateUnauthorized) {
NSLog(@"藍牙未被授權");
}
}
? 5、搜索外圍設備
//執行掃描動作之後,如果掃描到外設了,就會自動回調下面的協議方法
/** 發現符合要求的外設,回調 */
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
NSLog(@"%@====",peripheral.name);
//根據外設名字有選擇性的篩選連接藍牙設備
if ([peripheral.name hasPrefix:@"TEAMOSA"]) {
//在這裏對外設攜帶的廣播數據進行進壹步的處理
if ([self.peripheraNames containsObject:peripheral.name]) {
//如果數組中包含了就不再添加
return;
}
//添加到外設名字數組中
[self.peripheraNames addObject:peripheral.name];
//標記外設,讓它的生命周期與控制器的壹致
self.peripheral = peripheral;
// 可以根據外設名字來過濾外設
// [central connectPeripheral:peripheral options:nil];
}
// 連接外設
// [central connectPeripheral:peripheral options:nil];
}
6、連接外圍設備
//連接外圍設備,中心管理者連接外設成功,如果連接成功就會回調這個協議方法
/** 連接成功 */
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
//連接成功之後,可以進行服務和特性的發現。 停止中心管理設備的掃描動作,要不然在妳和已經連接好的外設進行數據溝通時,如果又有壹個外設進行廣播且符合妳的連接條件,那麽妳的iOS設備也會去連接這個設備(因為iOS BLE4.0是支持壹對多連接的),導致數據的混亂。
//停止掃描動作
[self.centralManager stopScan];
// 設置外設的代理
peripheral.delegate = self;
// 根據UUID來尋找服務
// [peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];
//外設發現服務,傳nil代表不過濾,壹次性讀出外設的所有服務
[peripheral discoverServices:nil];
NSLog(@"%s, line = %d, %@=連接成功", __FUNCTION__, __LINE__, peripheral.name);
}
//外設連接失敗
/** 連接失敗的回調 */
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"%s, line = %d, %@=連接失敗", __FUNCTION__, __LINE__, peripheral.name);
}
//丟失連接 掉線
/** 斷開連接 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
NSLog(@"%s, line = %d, %@=斷開連接", __FUNCTION__, __LINE__, peripheral.name);
// 斷開連接可以設置重新連接
[central connectPeripheral:peripheral options:nil];
}
7、獲取外圍設備服務和特征
/** 發現服務 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
// 遍歷出外設中所有的服務
for (CBService *service in peripheral.services) {
// NSLog(@"所有的服務:%@",service);
}
// 這裏僅有壹個服務,所以直接獲取
CBService *service = peripheral.services.lastObject;
// 根據UUID尋找服務中的特征
// [peripheral discoverCharacteristics:@[[CBUUID UUIDWithString:CHARACTERISTIC_UUID]] forService:service];
// [peripheral discoverCharacteristics:@[service.UUID] forService:service];
[peripheral discoverCharacteristics:nil forService:service];
}
8、從外圍設備讀取數據
// 更新特征的value的時候會調用 (凡是從藍牙傳過來的數據都要經過這個回調,簡單的說這個方法就是妳拿數據的唯壹方法) 妳可以判斷是否 從外圍設備讀數據
/** 接收到數據回調 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
// if (characteristic == @"妳要的特征的UUID或者是妳已經找到的特征") {
// //characteristic.value就是妳要的數據
// }
if ([peripheral.name hasPrefix:@"TEAMOSA"]){
NSData *data = characteristic.value;
NSString *value = [self hexadecimalString:data];
// NSLog(@"characteristic(讀取到的): %@, data : %@, value : %@", characteristic, data, value);
}
// 拿到外設發送過來的數據
// NSData *data = characteristic.value;
// self.textFild.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
9、向外圍設備發送(寫入)數據
//這個方法妳可以放在button的響應裏面,也可以在找到特征的時候就寫入,具體看妳業務需求怎麽用
//[self.peripherale writeValue:_batteryData forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];//第壹個參數是已連接的藍牙設備; 第二個參數是要寫入到哪個特征; 第三個參數是通過此響應記錄是否成功寫入 需要註意的是特征的屬性是否支持寫數據
/** 寫入數據回調 */
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
/*
typedef NS_OPTIONS(NSUInteger, CBCharacteristicProperties) {
CBCharacteristicPropertyBroadcast = 0x01,
CBCharacteristicPropertyRead = 0x02,
CBCharacteristicPropertyWriteWithoutResponse = 0x04,
CBCharacteristicPropertyWrite = 0x08,
CBCharacteristicPropertyNotify = 0x10,
CBCharacteristicPropertyIndicate = 0x20,
CBCharacteristicPropertyAuthenticatedSignedWrites = 0x40,
CBCharacteristicPropertyExtendedProperties = 0x80,
CBCharacteristicPropertyNotifyEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x100,
CBCharacteristicPropertyIndicateEncryptionRequired NS_ENUM_AVAILABLE(NA, 6_0) = 0x200
};
打印出特征的權限(characteristic.properties),可以看到有很多種,這是壹個NS_OPTIONS的枚舉,可以是多個值
常見的又read,write,noitfy,indicate.知道這幾個基本夠用了,前倆是讀寫權限,後倆都是通知,倆不同的通知方式
*/
// NSLog(@"%s, line = %d, char.pro = %d", __FUNCTION__, __LINE__, characteristic.properties);
// 此時由於枚舉屬性是NS_OPTIONS,所以壹個枚舉可能對應多個類型,所以判斷不能用 = ,而應該用包含&
NSLog(@"write value success(寫入成功) : %@", characteristic);
}
10、具體調用給藍牙外設寫入數據方法,這裏的例子是以按鈕點擊事件裏面來調用處理
//發送按鈕點擊事件
- (void)sendClick {
if (!self.characteristic) {
return;
}
_tempValue = [NSString stringWithFormat:@"%.0f", progressView.centigradeDegree];
_timeValue = [NSString stringWithFormat:@"%.0ld", (long)progressView1.timeDegree];
NSString *ttData = [NSString stringWithFormat:@"%@,%@U", _tempValue, _timeValue];
// NSString *aaa = [DataCoverTool coverFromStringToHexStr:ttData];
// 用NSData類型來寫入
// NSData *data = [NSKeyedArchiver archivedDataWithRootObject:arry];
NSData *data = [ttData dataUsingEncoding:NSUTF8StringEncoding];
// NSData *data = [self dataWithString:ttData];
// 根據上面的特征self.characteristic來寫入數據
[self.peripheral writeValue:data forCharacteristic:self.characteristic type:CBCharacteristicWriteWithResponse];