Bluetooth蓝牙4.0低功耗蓝牙简单使用

BLE ==== buletouch low energy,蓝牙4.0设备因为低耗电,所以也叫做BLE。

一、名字术语

Service:服务,是把数据分成一个个的独立逻辑项,它包含一个或者多个 Characteristic。每个 Service 有一个 UUID 唯一标识。UUID 有 16 bit 的,或者 128 bit 的。16 bit 的 UUID 是官方通过认证的,需要花钱购买,128 bit 是自定义的,可以自己设置。每个外设会有很多服务,每个服务中包含很多字段,这些字段的权限一般分为读 read,写 write,通知 notiy几种,就是我们连接设备后具体需要操作的内容。

Characteristic:特征,GATT 事务中的最低界别,Characteristic 是最小的逻辑数据单元,当然它可能包含一个组关联的数据,例如加速度计的 X/Y/Z 三轴值。与 Service 类似,每个 Characteristic 用 16 bit 或者 128 bit 的 UUID 唯一标识。每个设备会提供服务和特征,类似于服务端的 API,但是机构不同。

Description:每个 Characteristic 可以对应一个或多个Description用户描述 Characteristic 的信息或属性。

Peripheral、Central:外设和中心,发起连接的是 Central,被连接的设备为 Peripheral。

二、工作模式

这两组 API 分别对应不同的业务场景,如下图,左侧叫做中心模式,就是以你的手机(App)作为中心,连接其他的外设的场景。而右侧称为外设模式,使用手机作为外设连接其他中心设备操作的场景。

一般业务中Central模式使用的多,这里我就说下Central模式的流程

蓝牙数据接收的一般流程:
1、蓝牙外设设备在不断地在广播信号;
2、建立中心角色CBCentralManager,开启扫描;
3、发现设备(根据唯一标志来辨别是不是我们要连接的设备);
4、连接(成功);
5、调用方法发现「服务」;
6、调用方法发现「服务」里的「特征」(一般通过UUID来唯一辨识某个服务);
7、发现硬件数据写入的「特征」,记录便于后面数据写入;(一般通过UUID来唯一辨识写入write特征)
8、发现硬件用于数据输出的「特征」,进行「监听」((一般通过UUID来唯一辨识notiy特征);
9、利用数据输入「特征」发送数据,或者等待数据输出「特征」发出来的数据。

三、代码实现

  1. 导入头文件:

#import <CoreBluetooth/CoreBluetooth.h>

  1. 创建初始化蓝牙设备,同时系统会回调centralManagerDidUpdateState,告诉我们手机当前蓝牙状态:

#pragma mark - CBCentralManagerDelegate

CBManager *manager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];

- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
//   根据 central.state的值判断当前蓝牙的状态
//    CBManagerStateUnknown = 0,状态未知,更新迫在眉睫。
//    CBManagerStateResetting,与系统服务的连接暂时丢失,即将更新。
//    CBManagerStateUnsupported,该平台不支持蓝牙低能耗中央/客户端角色。
//    CBManagerStateUnauthorized,应用程序未被授权使用蓝牙低能耗中央/客户端角色。
//    CBManagerStatePoweredOff,蓝牙目前处于关闭状态
//    CBManagerStatePoweredOn,蓝牙目前处于开机状态,可以使用。

if(central.state == CBManagerStatePoweredOn){
 //不过滤服务搜索蓝牙
    [_centralManager scanForPeripheralsWithServices:nil options:@{ CBCentralManagerScanOptionAllowDuplicatesKey : @YES }];
    
}
  1. 开始搜索外设

//在设备搜索到外设后,会回调以下方法
//这里如果你要连接唯一蓝牙设备可以通过蓝牙名字等唯一表示帅选 :
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
    NSString *key = CBAdvertisementDataLocalNameKey;
    if ([advertisementData[key] hasPrefix:@"KdsLock"]){
    
       [self.searchArray addObject:peripheral];
    }

    if ([_delegate respondsToSelector:@selector(didDiscoverPeripheral:)]) {
            [_delegate didDiscoverPeripheral:peripheral];
        }
//[self.centralManager connectPeripheral:peripheral options:nil];发现外设就连接就用此方法
    
}

这里插播一条,如何确定设备唯一标识符

在有些时候,需要获取 peripheral 的唯一标示符(比如要做自动连接或绑定用户等操作),但是在搜索到 peripheral 之后,只能拿到 identifier,而且这个 identifier 根据连接的 central 不同而不同。也就是说,不同的手机连上之后,identifier 是不同的。虽然比较坑爹,但是这并不影响你做蓝牙自动连接。

/**给系统的CBPeripheral增加属性:用来保存广播包中的数据*/
@property (nonatomic, copy, nullable)NSString *advDataLocalName;
///mac地址,从advDataLocalName中提取后12位后添加:号,如果advDataLocalName长度小于12,则返回nil。
@property (nonatomic, strong, nullable, readonly) NSString *mac;
///是否是新蓝牙设备。bleVersion:1为旧蓝牙透传协议,2和3为新蓝牙自有协议。
@property (nonatomic, assign) BOOL isNewDevice;
///新蓝牙锁的产品型号,180A服务2A26特征的值,蓝牙协议标注的是FirmwareRev。大写如果包含DB2可以添加20个密码,其它可以添加10个密码。
@property (nonatomic, strong, nullable) NSString *lockModelType;
///新蓝牙锁最大能设置的密码(用户)数,根据lockModelType判断。不失一般性,如果lockModelType属性为nil,返回默认的10个。
@property (nonatomic, assign, readonly) NSUInteger maxUsers;
///新蓝牙模块代号,180A服务的2A24特征的值,如果等于RGBT1761,则开锁时不用密码。
@property (nonatomic, strong, nullable) NSString *lockModelNumber;
///新蓝牙用,根据lockModelNumber是否等于RGBT1761或RGBT1761D判断开锁时是否需要密码。
@property (nonatomic, assign, readonly) BOOL unlockPIN;
///蓝牙锁的序列号,180A服务2A25特征。
@property (nonatomic, strong, nullable) NSString *serialNumber;
///蓝牙锁的硬件版本号,180A服务2A27特征。
@property (nonatomic, strong, nullable) NSString *hardwareVer;
///蓝牙锁的软件版本号,180A服务2A28特征。
@property (nonatomic, strong, nullable) NSString *softwareVer;

如果一定有这样的需求(即一定要使用 MAC 地址),可以和硬件工程师沟通,使用下面的某一种方式解决:
将 MAC 地址写在某一个蓝牙特征中,当我们连接蓝牙设备之后,通过某一个特征获取 MAC 地址。
将 MAC 地址放在蓝牙设备的广播数据当中,然后在广播的时候,将 MAC 地址以广播的形式发出来,在不建立连接的情况下,就能拿到 MAC 地址。
我们可以通过蓝牙设备的出厂设备或者后期手动修改蓝牙设备的 name,作为唯一标识。

  1. 是否连接上指定设备
#pragma mark - CBCentralManagerDelegate  连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
    [_cbCM stopScan];
    HSLog(@"已连接到蓝牙%@",peripheral.name);
    _connectedPeripheral = peripheral;
    peripheral.delegate=self;
    [peripheral discoverServices:nil];
}

#pragma mark - CBCentralManagerDelegate  连接失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
    _connectedPeripheral=nil;
    [self searchBleDevices];
}

  1. 开始搜索服务[peripheral discoverServices:nil];
#pragma mark - CBPeripheralDelegate   已搜索到Services
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
    for (CBService *s in peripheral.services) {
            if ([s.UUID.UUIDString isEqualToString:@"你要的服务ID"]) {
              [peripheral discoverCharacteristics:nil forService:s];//这里可以通过service的UUID属性来辨识你要的服务,订阅服务下面所有的特征
      }
    }
 if ([_delegate respondsToSelector:@selector(didConnectPeripheral:)]) {
            [_delegate didConnectPeripheral:peripheral];
        }
}

  1. 发现服务里的特征值[peripheral discoverCharacteristics:nil forService:s]
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error
{
//(我这里得到两个特征,一个读,一个写,也有可能是读写都可以,就一个特征的)
    //开启订阅(开启监听数据)
    for (CBCharacteristic *characteristic in service.characteristics) {
     if (error){
        KDSLog(@"获取服务:%@的特征失败: %@", service.UUID, error);
        return;
    }
UInt64 uuid = strtoul(service.UUID.UUIDString.UTF8String, 0, 16);
    KDSBleService serviceType = (KDSBleService)uuid;
    KDSLog(@"读的服务-service===%@ %llx",service.characteristics, uuid);
    
        if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"0003CDD2-0000-1000-8000-00805F9B0131"]]){
            self.writeCharacteristic = characteristic;
        }else if([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"0003CDD1-0000-1000-8000-00805F9B0131"]]){
            // 订阅, 实时接收
            [peripheral setNotifyValue:YES forCharacteristic:characteristic];
        }
    }
}

/**
 *@abstract kds蓝牙服务,枚举值是对应服务的UUID。
 */
typedef NS_ENUM(NSInteger, KDSBleService) {
    ///模块参数。该服务包含电量和时间2个特征。
    KDSBleServiceModule = 0xFFB0,
    ///设备信息参数。该服务包含设备MAC地址、模块代号、序列号、锁型号、硬件版本、软件版本6个特征。
    KDSBleServiceDevice = 0x180A,
    ///门锁参数。该服务包含锁的类型、功能、状态等特征。
    KDSBleServiceLock = 0xFFF0,
    ///app发送数据到ble模块的服务通道。
    KDSBleServiceApp2BleTunnel = 0xFFE5,
    ///ble模块发送数据到app的服务通道。
    KDSBleServiceBle2AppTunnel = 0xFFE0,
    ///APP->BLE配网通道【服务UUID:0xFFC0】
    KDSApp2BleDisNetworkTunnel = 0xFFC0,
    ///BLE->APP配网通道【服务UUID:0xFFC5】
    KDSBle2AppDisNetworkTunnel = 0xFFC5
    
};

  1. 收到蓝牙返回的数据后会调用didUpdateValueForCharacteristic
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
    if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"0003CDD1-0000-1000-8000-00805F9B0131"]])
    {
        NSString *hexValue =[self bytesToHex:characteristic.value];
       NSString *ASCIIValue = [[NSString alloc] initWithData:characteristic.value encoding:NSASCIIStringEncoding];
        KDSLog(@"%@",[NSString stringWithFormat:@"hexValue--%@",hexValue]);
    }
}

  1. 发送数据给从设备
 if ((characteristic.properties & CBCharacteristicPropertyWriteWithoutResponse) != 0)
    {
        [[HSBleManager sharedManager].connectedDevice writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
        characteristicWriteCompletionHandler (YES,nil);
    }else{
        [[HSBleManager sharedManager].connectedDevice writeValue:data forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值