iOS 藍(lán)牙初章

本文主要以藍(lán)牙4.0做介紹,因?yàn)楝F(xiàn)在iOS能用的藍(lán)牙也就是只僅僅4.0的設(shè)備

用的庫就是core bluetooth

這里是關(guān)于藍(lán)牙的開發(fā)者官方文檔,感興趣的童鞋可以看一下點(diǎn)擊進(jìn)入 https://developer.apple.com/library/prerelease/content/documentation/NetworkingInternetWeb/Conceptual/CoreBluetooth_concepts/CoreBluetoothOverview/CoreBluetoothOverview.html

其次就是了解一下藍(lán)牙的一些概念吧.

core bluetooth就兩個(gè)東西,peripheral和central 也就是外設(shè)和中心

如下圖所示


20140523195339187.jpeg

圖為Core Bluetooth 對(duì)象類模型
一般情況下藍(lán)牙設(shè)備為外設(shè),手機(jī)為中心,也就是在APP里面建立CBCentralManager獲取到設(shè)備的peripheral這個(gè)類, 每個(gè)peripheral又會(huì)有若干個(gè)CBService,每個(gè)CBservice里面也會(huì)有若干個(gè)CBCharateristic,每個(gè)CBCharateristic里面又會(huì)有CBDescriptor.
看圖

1442301743869760.png

這句話說的真長(zhǎng).真費(fèi)勁...!! 所以從網(wǎng)上盜了圖.

藍(lán)牙外設(shè)模型
了解的差不多了,下面就是手機(jī)作為中心連接外設(shè)的步驟了:

1.第一步當(dāng)然是先建立中心管理者的角色了,成為代理,實(shí)現(xiàn)必須實(shí)現(xiàn)的代理方法也就是藍(lán)牙更新狀態(tài)的方法

2.確認(rèn)藍(lán)牙是打開的時(shí)候,我們就可以掃描周邊設(shè)備了.掃描設(shè)備的時(shí)候要同時(shí)把設(shè)備保存的數(shù)組中,以便接下來連接的時(shí)候使用,

3.掃描到我們的設(shè)備之后就可以連接了.可以根據(jù)藍(lán)牙的一些信息進(jìn)行指令連接.例如:根據(jù)藍(lán)牙名字連接等等.

4.連接上藍(lán)牙之后就會(huì)生成一個(gè)peripheral對(duì)象,這時(shí)候我們就可以拿著這個(gè)對(duì)象進(jìn)行操作了,例如查看外設(shè)信息,向這個(gè)外設(shè)讀寫信息等等.嗯,回歸正題,拿到這個(gè)peripheral對(duì)象之后

獲取peripheral的services,在獲取services里的characteristics,拿到可以讀寫的characteristic并把這個(gè)characteristic記錄下來

5.現(xiàn)在我們就可以進(jìn)行數(shù)據(jù)交互了.可以向characteristic里面寫數(shù)據(jù) 以及從characteristic讀數(shù)據(jù).嗯,讀數(shù)據(jù)也就是訂閱通知

6.完成后就可以斷開連接了.

手機(jī)作為中心連接外設(shè)的步驟也就到這里結(jié)束了.下面就開始貼代碼了.

我覺得這個(gè)代碼貼的不太好,留下github地址,下載可運(yùn)行.這樣方便多了.

https://github.com/907064772/iOS----BluetoothCentral

---------****--------開始貼代碼

import "ViewController.h"

import <CoreBluetooth/CoreBluetooth.h>

import "NSString+Emoji.h"

pragma step1 導(dǎo)入coreBluetooth頭文件,建立主設(shè)備管理類,設(shè)置主設(shè)備委托

@interface ViewController ()<CBCentralManagerDelegate,CBPeripheralDelegate,UITableViewDelegate,UITableViewDataSource>{
CBCentralManager * manager;
CBPeripheral *per;

// 記錄下寫的通道
CBCharacteristic * WriteCharacteristic;
// 記錄下讀的通道
CBCharacteristic * ReadCharacteristic;
}
//掃描到的藍(lán)牙外設(shè)
@property (nonatomic,strong) NSMutableArray *discoverPeripherals;

@property (nonatomic,strong) UITableView *tableview;
//通知顯示label
@property (weak, nonatomic) IBOutlet UILabel *notifyLabel;
//發(fā)送按鈕的方法

  • (IBAction)notifyBtnClick:(UIButton *)sender;

@property (weak, nonatomic) IBOutlet UITextView *textView;

@property (weak, nonatomic) IBOutlet UIButton *sendBtn;

/**

  • 點(diǎn)擊選擇是否以十六進(jìn)制發(fā)送顯示
    */
  • (IBAction)isHexBtnClick:(UIButton *)sender;

/**

  • 是否以十六進(jìn)制輸入輸出標(biāo)記
    /
    @property (nonatomic,assign) BOOL isHex;
    /
    *
  • 發(fā)送方法
    */
  • (IBAction)sendAction:(UIButton )sender;
    /
    *
  • 從藍(lán)牙串口監(jiān)聽到的所有消息字符串
    */
    @property (nonatomic,copy) NSMutableString *notifyString;

/**

  • 寫入的字符串
    */
    @property (nonatomic,copy) NSMutableString *writeString;

/**

  • 監(jiān)聽藍(lán)牙串口廣播按鈕
    */
    @property (weak, nonatomic) IBOutlet UIButton *listenBtn;

@property (weak, nonatomic) IBOutlet UIButton *cleanBtnClick;

  • (IBAction)cleanBtnClick:(id)sender;

@end

@implementation ViewController

  • (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    _discoverPeripherals = [[NSMutableArray alloc]init];
    // 初始化一個(gè)藍(lán)牙中心管理者并設(shè)置代理和線程,默認(rèn)線程為住線程
    manager = [[CBCentralManager alloc]initWithDelegate:self queue:dispatch_get_main_queue()];

// [self createTableView];

}
/**

  • 創(chuàng)建表格視圖
    */
    -(void)createTableView{
    CGRect frame = self.view.frame;
    frame.origin.y += 20;
    self.tableview = [[UITableView alloc]initWithFrame:frame style:UITableViewStylePlain];
    self.tableview.delegate = self;
    self.tableview.dataSource = self;
    [self.view addSubview:self.tableview];
    }

-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
return self.discoverPeripherals.count;
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
UITableViewCell *cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"cell"];
CBPeripheral * peripheral = self.discoverPeripherals[indexPath.row];
cell.textLabel.text = peripheral.name;
return cell;
}

-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath )indexPath{
CBPeripheral peripheral = self.discoverPeripherals[indexPath.row];
// 連接設(shè)備 如果藍(lán)牙串口名字前綴有 這個(gè)就連接
if ([peripheral.name hasPrefix:@"SerialCom"]) {
/

* 一個(gè)設(shè)備最多能連7個(gè)外設(shè),每個(gè)外設(shè)最多只能給一個(gè)住設(shè)備連接,連接成功,失敗,斷開會(huì)進(jìn)入各自的委托.
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(shè)連接失敗的委托
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開外設(shè)的委托
*/
//找到的設(shè)備必須持有它,否則CBCentralManager中也不會(huì)保存peripheral,那么CBPeripheralDelegate中的方法也不會(huì)被調(diào)用!!

    [manager connectPeripheral:peripheral options:nil];
}

}

pragma step2 掃描外設(shè)(discover),掃描外設(shè)的方法我們放在centrallManager成功打開的委托中,因?yàn)橹挥性O(shè)備成功打開,才能開始掃描,否則會(huì)報(bào)錯(cuò)

/**

  • 藍(lán)牙中心管理者必須實(shí)現(xiàn)的代理方法.更新狀態(tài)

  • @param central
    */
    -(void)centralManagerDidUpdateState:(CBCentralManager *)central{

    /*!

    • @enum CBCentralManagerState
    • @discussion Represents the current state of a CBCentralManager.
    • @constant CBCentralManagerStateUnknown 未知狀態(tài),更新迫在眉睫
    • @constant CBCentralManagerStateResetting 與系統(tǒng)連接服務(wù)暫時(shí)丟失,
    • @constant CBCentralManagerStateUnsupported 這個(gè)平臺(tái)不支持藍(lán)牙 中心/客戶端 角色
    • @constant CBCentralManagerStateUnauthorized 此應(yīng)用未被授權(quán)
    • @constant CBCentralManagerStatePoweredOff 藍(lán)牙當(dāng)前狀態(tài)為關(guān)閉
    • @constant CBCentralManagerStatePoweredOn 藍(lán)牙當(dāng)前狀態(tài)為打開
      /
      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(@"CBCentralMnagerStatePoweredOn");
      /
      *
      * 第一個(gè)參數(shù)nil就是掃描周圍所有的外設(shè),掃描到外設(shè)后會(huì)進(jìn)入
      */
      // [manager scanForPeripheralsWithServices:nil options:nil];
      [manager scanForPeripheralsWithServices:@[] options:nil];
      break;
      default:
      break;
      }

}

pragma step3 連接外設(shè)(connect)

/**

  • 掃描到設(shè)備會(huì)進(jìn)入方法

  • @param central <#central description#>

  • @param peripheral <#peripheral description#>

  • @param advertisementData <#advertisementData description#>

  • @param RSSI <#RSSI description#>
    */
    -(void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI{

    NSLog(@"當(dāng)前掃描到設(shè)備:%@",peripheral);
    [_discoverPeripherals addObject:peripheral];
    // 連接設(shè)備
    if ([peripheral.name hasPrefix:@"SerialCom"]) {
    /**
    * 一個(gè)設(shè)備最多能連7個(gè)外設(shè),每個(gè)外設(shè)最多只能給一個(gè)住設(shè)備連接,連接成功,失敗,斷開會(huì)進(jìn)入各自的委托.
    - (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral;//連接外設(shè)成功的委托
    - (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//外設(shè)連接失敗的委托
    - (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error;//斷開外設(shè)的委托
    */
    //找到的設(shè)備必須持有它,否則CBCentralManager中也不會(huì)保存peripheral,那么CBPeripheralDelegate中的方法也不會(huì)被調(diào)用!!
    [self.tableview reloadData];
    [manager connectPeripheral:peripheral options:nil];
    }

}

pragma step4 掃描外設(shè)中的服務(wù)和特征(discover)

/**

  • 設(shè)備連接成功后,就可以掃描設(shè)備的服務(wù)了.同樣是通過委托形式,掃描到結(jié)果后進(jìn)入委托方法,但是這個(gè)委托已經(jīng)不再是主設(shè)備的委托(CBCentralManagerDelegate),而是外設(shè)的委托(CBPeripheralDelegate),這個(gè)委托包含了主設(shè)備與外設(shè)交互的許多回調(diào)方法,包括獲取services,獲取characteristics ,獲取characteristics的值,獲取characteristics的descirptor和descriptor的只,寫數(shù)據(jù),讀rssi,用通知的方式訂閱數(shù)據(jù)等等.
    */

pragma mark - centralManagerDelegate

/**

  • 連接外設(shè)成功委托
  • @param central <#central description#>
  • @param peripheral <#peripheral description#>
    */
    -(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    NSLog(@"連接到名稱為(%@)的設(shè)備-成功",peripheral.name);
    // 設(shè)置peripheral的委托
    peripheral.delegate = self;
    // 記錄下當(dāng)前連接上的外設(shè)
    per = peripheral;
    // 掃描外設(shè)servers,成功后會(huì)進(jìn)入 - (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error;
    [peripheral discoverServices:nil];
    // 連接成功后停止掃描
    [central stopScan];
    }

/**

  • 連接外設(shè)失敗的委托
  • @param central
  • @param peripheral
  • @param error
    */
    -(void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral )peripheral error:(NSError )error{
    NSLog(@"連接到名稱為(%@)的設(shè)備-失敗,原因:%@",peripheral.name,[error localizedDescription]);
    }
    /
  • Peripheral斷開連接
  • @param central
  • @param peripheral
  • @param error
    */
    -(void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    NSLog(@"外設(shè)連接斷開連接設(shè)備:%@,原因:%@",peripheral.name,[error localizedDescription]);
    }

pragma mark - peripheralDelegate

/**

  • 掃描到services,獲取外設(shè)的services
  • @param peripheral
  • @param error
    */
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    if (error) {
    NSLog(@">>>discovered services for %@ with error: %@",peripheral.name,[error localizedDescription]);
    return;
    }
    for (CBService *service in peripheral.services) {
    NSLog(@"service.UUID:%@",service.UUID);
    // 掃描每個(gè)service的characteristics,掃描到會(huì)進(jìn)入 -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error 代理方法中
    [peripheral discoverCharacteristics:nil forService:service];
    }

}
/**

  • 獲取外設(shè)的characteristics,獲取characteristics的值,獲取characteristics的descriptor和descriptor的值

  • @param peripheral

  • @param service

  • @param error
    */
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
    if (error) {
    NSLog(@"error discovered characteristics for %@ with error: %@",service.UUID,[error localizedDescription]);
    return;
    }
    // 獲取characteristics的值.讀到數(shù)據(jù)會(huì)進(jìn)入 -peripheral:didUpdateValueForCharacteristic:error:方法
    for (CBCharacteristic *characteristic in service.characteristics) {
    [peripheral readValueForCharacteristic:characteristic];
    }

    // 獲取characteristics的descriptor值,讀取到數(shù)據(jù)會(huì)進(jìn)入到
    // - peripheral:didDiscoverDescriptorsForCharacteristic:error:

    for (CBCharacteristic *charateristic in service.characteristics) {
    [peripheral discoverDescriptorsForCharacteristic:charateristic];
    }

}
/**

  • 獲取characteristic的值
  • @param peripheral
  • @param characteristic
  • @param error
    */
    -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError )error{
    NSString hexString =[[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
    if (self.isHex) {
    hexString = [NSString hexStringFromString:hexString];
    }
    [_notifyString appendFormat:@"%@ ",hexString];
    // 打印charateristic的uuid值和value值
    _notifyLabel.text = [NSString stringWithFormat:@"%@",_notifyString];
    NSLog(@"characteristic.uuid:%@ value:%@",characteristic.UUID,characteristic.value);
    }
    /
  • 搜索到characteristic的descriptors
  • @param peripheral
  • @param descriptor
  • @param error
    */
    -(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError )error{
    NSLog(@"characteristic:%@ properties:%ld",characteristic.UUID,characteristic.properties);
    // 記錄下當(dāng)前通道.在我的藍(lán)牙設(shè)備里面,FFF6的權(quán)限是可讀寫的就是RW,但是實(shí)際應(yīng)用中應(yīng)該判斷 characteristic.properties然后在進(jìn)行保存
    if ([[characteristic.UUID UUIDString] isEqual:@"FFF6"]) {
    WriteCharacteristic = characteristic;
    }
    // 讀通道
    if ([[characteristic.UUID UUIDString] isEqual:@"FFF7"]) {
    ReadCharacteristic = characteristic;
    }
    for (CBDescriptor descriptor in characteristic.descriptors) {
    NSLog(@"descriptor.uuid:%@",descriptor.UUID);
    }
    _listenBtn.enabled = YES;
    }
    /
  • 獲取到descriptor的值
  • @param peripheral
  • @param descriptor
  • @param error
    */
    -(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{
    // 打印出descriptorUUID和value
    // 這個(gè)descriptor都是對(duì)于charateristic的描述,一般都是字符串.所以這里我們用了字符串拼接
    NSLog(@"characteristic.uuid:%@ value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);
    }
    //波特率115200

pragma step5 把數(shù)據(jù)寫到characteristic中

/**

  • 寫數(shù)據(jù)
    */
    -(void)writeCharacteristic:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic value:(NSData )value{
    //打印出 characteristic 的權(quán)限,可以看到有很多種,這是一個(gè)NS_OPTIONS,就是可以同時(shí)用于好幾個(gè)值,常見的有read,write,notify,indicate,知知道這幾個(gè)基本就夠用了,前連個(gè)是讀寫權(quán)限,后兩個(gè)都是通知,兩種不同的通知方式。
    /

    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
    };

    */

    NSLog(@"%lu",(unsigned long)characteristic.properties);
    // 只有characteristic.properties 有write 的權(quán)限才可以寫
    if (characteristic.properties & CBCharacteristicPropertyWrite) {
    // 最好一個(gè)type參數(shù)可以為CBCharacteristicWriteWithResponse或者type:CBCharacteristicWrite,區(qū)別是是否會(huì)有反饋
    [peripheral writeValue:value forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
    }else{
    NSLog(@"改字段不可寫");
    }
    }
    /**

  • 設(shè)置通知
    */
    -(void)notifyCharacteristic:(CBPeripheral )peripheral characteristic:(CBCharacteristic )characteristic{
    // 設(shè)置通知.數(shù)據(jù)通知會(huì)進(jìn)入didUpdateValueForCharacteristic方法
    [peripheral setNotifyValue:YES forCharacteristic:characteristic];
    }
    /

  • 取消通知
    */
    -(void)cancelNotifuCharacteristic:(CBPeripheral )perpheral characteristic:(CBCharacteristic )characteristic{
    // 取消通知
    [perpheral setNotifyValue:NO forCharacteristic:characteristic];
    }
    /

  • 停止掃描并斷開連接
    */
    -(void)disconnectPeripheral:(CBCentralManager *)centralManager peripheral:(CBPeripheral *)periphearl{
    // 停止掃描
    [centralManager stopScan];
    // 斷開連接
    [centralManager cancelPeripheralConnection:periphearl];
    }

-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
NSLog(@"peripheral:%@ characteristic:%@ error:%@",peripheral,characteristic,[error localizedDescription]);
}

  • (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
    }

  • (IBAction)sendAction:(UIButton *)sender {
    NSString *tmpString = _textView.text;
    // 判斷十六進(jìn)制是否打開,如果要發(fā)送十六進(jìn)制數(shù)據(jù)就轉(zhuǎn)換字符串
    if (self.isHex) {
    tmpString = [NSString hexStringFromString:tmpString];
    }
    // 想制定特征寫入字符
    [self writeCharacteristic:per characteristic:WriteCharacteristic value:[tmpString dataUsingEncoding:NSUTF8StringEncoding]];
    }
    //是否開啟藍(lán)牙串口讀取字符串

  • (IBAction)notifyBtnClick:(UIButton *)sender {
    sender.selected = !sender.isSelected;
    if (sender.selected) {
    _notifyString = [[NSMutableString alloc]init];
    [self notifyCharacteristic:per characteristic:ReadCharacteristic];
    }else{
    _notifyString = nil;
    [self cancelNotifuCharacteristic:per characteristic:ReadCharacteristic];
    }

}
//取消可編輯
-(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
[self.view endEditing:NO];
}
//點(diǎn)擊切換編碼

  • (IBAction)isHexBtnClick:(UIButton *)sender {
    sender.selected = !sender.selected;
    if (sender.selected) {
    _isHex = NO;
    [sender setTitle:@"UTF-8" forState:UIControlStateSelected];
    }else{
    _isHex = YES;
    [sender setTitle:@"Hex" forState:UIControlStateNormal];
    }

}
//清空消息

  • (IBAction)cleanBtnClick:(id)sender {
    _notifyLabel.text = [NSString stringWithFormat:@""];
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容