蓝牙介绍:使用的是网上购买的一个简单的蓝牙模块
蓝牙模块地址:蓝牙模块地址
USB转TTL:转接模块地址
AT-09蓝牙4.0BLE 模块
该蓝牙模块相当简单,适合初学者入门使用
下面是该蓝牙的设备信息以及传输特性:
广播数据:
{
是否可连接
kCBAdvDataIsConnectable = 1;
Mac地址
kCBAdvDataManufacturerData = <1a9888a0 3ca30807 b90a>;
两种服务
kCBAdvDataServiceUUIDs = (
FFE0,
FEE0
);
}
这里的服务有FFE0和FEE0两种,FFE0代表携带设备信息的服务,FEE0代表传输特性
FFE0服务的特征:
(
携带设备信息
"<CBCharacteristic: 0x13d6ba1d0, UUID = System ID, properties = 0x2, value = (null), notifying = NO>",
"<CBCharacteristic: 0x13d6ba390, UUID = Model Number String, properties = 0x2, value = (null), notifying = NO>",
"<CBCharacteristic: 0x13d6ba3f0, UUID = Serial Number String, properties = 0x2, value = (null), notifying = NO>",
"<CBCharacteristic: 0x13d6ba330, UUID = Firmware Revision String, properties = 0x2, value = (null), notifying = NO>",
"<CBCharacteristic: 0x13d6ba550, UUID = Hardware Revision String, properties = 0x2, value = (null), notifying = NO>",
"<CBCharacteristic: 0x13d6ba480, UUID = Software Revision String, properties = 0x2, value = (null), notifying = NO>",
"<CBCharacteristic: 0x13d6ba750, UUID = Manufacturer Name String, properties = 0x2, value = (null), notifying = NO>",
"<CBCharacteristic: 0x13d6ba800, UUID = IEEE Regulatory Certification, properties = 0x2, value = (null), notifying = NO>",
"<CBCharacteristic: 0x13d6ba860, UUID = PnP ID, properties = 0x2, value = (null), notifying = NO>"
)
FEE0服务的特征:
"<CBCharacteristic: 0x13d563890, UUID = FFE1, properties = 0x1E, value = (null), notifying = NO>"
此蓝牙模块只有简单的传输特性,读写的特征为同一个,并且由于读写服务FEE0
的属性properties
为0x1E
,按照网上的资料,0x1E
应该是CBCharacteristicPropertyRead=0x02
,CBCharacteristicPropertyWriteWithoutResponse=0x04
,CBCharacteristicPropertyWrite=0x08
,CBCharacteristicPropertyNotify=0x10
的组合,可是实际上当发送数据后去读数据时只能采取手动调用的方式读取并且返回固定值;
蓝牙相关对象需要被控制器对象持有
创建NSObject
类BLEManager
导入<CoreBluetooth/CoreBluetooth.h>
框架,在.h
文件接口遵守CBCentralManagerDelegate,CBPeripheralDelegate
协议,在控制器文件BLEManager
下创建以下属性:
@property (retain, nonatomic) CBCentralManager *centralManager; //中心设备
@property (retain, nonatomic) CBPeripheral *discoveredPeripheral; //外围设备
@property (retain, nonatomic) CBCharacteristic *discoveredCharacteristic; //服务特征
用于持有相关对象。
- 1.实例化一个中心设备对象
if (!self.centralManager){
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
设置代理,默认主线程操作。
- 2.设备对象实例化代理
#pragma mark - CentralManager Delegate
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
if (central.state==CBCentralManagerStateUnsupported)
{
//手机不支持低功耗蓝牙
return;
}
if (central.state==CBCentralManagerStatePoweredOff) {
//手机蓝牙未开启
return;
}
if (central.state == CBCentralManagerStateUnauthorized)
{
//系统设置不允许
return;
}
if (central.state == CBCentralManagerStatePoweredOn) {
在这里开始扫描外围设备的服务
这里的Services表示UUID,传参代表值扫描带有该UUID的设备,也可以传nil
CBCentralManagerScanOptionAllowDuplicatesKey设置为NO表示不会重复扫描已经发现的设备
[self.centralManager scanForPeripheralsWithServices:nil options:@{CBCentralManagerScanOptionAllowDuplicatesKey : @NO }];
}
}
这个代理返回中心设备能否进行蓝牙操作的信息,这里列出了不支持,未开启,支持三种状态。
- 3.扫描周围设备
在这里可以得到扫描到广播的设备信息,包括设备对象,信号,广播信息等,
这个方法不断刷新,一直寻找周围的设备,筛选设备连接成功之后,可以手动停止扫描。
/**
扫描周围设备的回调
@param central 中心设备对象
@param peripheral 周围设备对象
@param advertisementData 广播数据
@param RSSI 信号
*/
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
可以在这里设置连接名字以MLT开头的设备
if ([peripheral.name hasPrefix:@"MLT"]) {
self.discoveredPeripheral = peripheral;
[self.centralManager connectPeripheral:peripheral options:nil];
}
}
- 4连接结果回调
//成功链接外围设备
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
//停止扫描
[self.centralManager stopScan];
//设置外围设备代理
peripheral.delegate = self;
//查找连接设备的所有服务
[peripheral discoverServices:nil];
}
//连接外围设备失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
//与外围设备连接断开
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error
{
可以在这里向外部发送连接断开的通知
可以这里可以设置自动重新扫描连接
}
- 5外围设备的代理方法
#pragma mark - Peripheral Delegate
//找到对应的服务代理方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error{
if (error) {
NSLog(@"Error is %@", [error localizedDescription]);
return;
}
//查找连接设备的服务的所有特征
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:nil forService:service];
}
}
- 6.查找特征的代理方法
//找到特性代理方法
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error) {
NSLog(@"Error is %@", [error localizedDescription]);
return;
}
for (CBCharacteristic *characteristic in service.characteristics)
{
//设置特性2的通知为YES,以收取外围设备发过来的数据 - 读数据
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFE1"]])
{
开启读服务通知
[peripheral setNotifyValue:YES forCharacteristic:characteristic];
self.discoveredCharacteristic=characteristic; //保持服务特征
}
}
}
特征值的通知设置发生改变时触发的方法,在代码中设置允许读写通知时都会进入此方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error){
NSLog(@"改变特征通知错误: %@", error.localizedDescription);
}
当不是传输特征时,不做处理
if (![characteristic.UUID isEqual:[CBUUID UUIDWithString:@"FFE1"]]){
return;
}
当传输特征通知开启时,连接成功(到这一步才可以正常进行数据交互)
if (characteristic.isNotifying){
在这里可以向外发送连接成功通知
}else{
否则取消连接
[self.centralManager cancelPeripheralConnection:peripheral];
}
}
- 7.写数据
//自定义写数据的动态方法
-(void)writeDataToBle:(NSString *)instruction{
if (self.discoveredPeripheral&&self.discoveredCharacteristic)
{
[self.discoveredPeripheral writeValue:[[NSData alloc] initWithBytes:[self hexStringToBytes:instruction] length:[instruction length]/2.0] forCharacteristic:self.discoveredCharacteristic type:CBCharacteristicWriteWithResponse];
}
}
其中CBCharacteristicWriteType
有两个选择:
1 . CBCharacteristicWriteWithResponse
接收写操作回调
2 . CBCharacteristicWriteWithoutResponse
不接受写操作回调
前者表示写数据成功或者失败都会得到回调,后者则没有。
当选择2的模式时候,蓝牙的读数据必须允许通知,不然读数据的回调将没有反应。在蓝牙读数据通知开启,特征的属性值包含CBCharacteristicWriteWithoutResponse
,并且选择2的方式发送数据的情况下,一旦发送数据到蓝牙,蓝牙将通过读数据回调返回数据。
当选择1的模式的时候,发送数据操作将进入下面的回调方法里面,我们可以判断发送数据的操作是否成功,如果成功可以手动调用,读取蓝牙返回数据的方法来得到蓝牙返回的数据
写数据的回调
-(void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
//当错误为空的时候可以在这里手动调用读数据的方法,调用该方法,程序也会在读数据的代理里面得到蓝牙返回的数据。
[self.discoveredPeripheral readValueForCharacteristic: self.discoveredCharacteristic];
}
- 8.读数据的回调
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
if (error) {
NSLog(@"Error: %@", [error localizedDescription]);
return;
}
NSString *stringFromData = [characteristic.value description];
//删除字符串中的空格
stringFromData=[stringFromData stringByReplacingOccurrencesOfString:@" " withString:@""];
//删除字符串中的尖括号
stringFromData=[stringFromData stringByReplacingOccurrencesOfString:@"<" withString:@""];
stringFromData=[stringFromData stringByReplacingOccurrencesOfString:@">" withString:@""];
NSLog(@"Received: %@", stringFromData);
}
附录:
下面文章作为借鉴