蓝牙功能
作为蓝牙中心,连接外设
一般性的步骤是先扫描设备,然后根据蓝牙设备的名称等信息找到需要连接的设备,进行连接
然后获取设备的服务,可以订阅对应的服务,可以对设备进行写入
//
// ViewController.m
// BlueTooth
//
// Created by JackXu on 15/6/9.
// Copyright (c) 2015年 BFMobile. All rights reserved.
//
#import "ViewController.h"
#define MyDeviceName @"HMSoft"
@interface ViewController ()
@property (nonatomic, strong) CBCentralManager *centralMgr;
@property (nonatomic, strong) CBPeripheral *discoveredPeripheral;
@property (nonatomic, strong) CBCharacteristic *writeCharacteristic;
@property (nonatomic,strong) CBCharacteristic *testCharacteristic;
@property (weak, nonatomic) IBOutlet UITextField *editText;
@property (weak, nonatomic) IBOutlet UILabel *resultText;
@property (nonatomic,strong) CBPeripheral *smartWatchPeripheral;
@property (nonatomic,strong) NSMutableArray *discoveredPerpheralArray;
@property (nonatomic,strong) CBService *testService;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.centralMgr = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
}
//检查App的设备BLE是否可用 (ensure that Bluetooth low energy is supported and available to use on the central device)
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
switch (central.state)
{
case CBCentralManagerStatePoweredOn:
//discover what peripheral devices are available for your app to connect to
//第一个参数为CBUUID的数组,需要搜索特点服务的蓝牙设备,只要每搜索到一个符合条件的蓝牙设备都会调用didDiscoverPeripheral代理方法
[self.centralMgr scanForPeripheralsWithServices:nil options:nil];
break;
default:
NSLog(@"Central Manager did change state");
break;
}
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI
{
NSLog(@"peripheral == %@ rssi == %@",peripheral,RSSI);
//找到需要的蓝牙设备,停止搜素,保存数据
if([peripheral.name isEqualToString:MyDeviceName]){
_discoveredPeripheral = peripheral;
[_centralMgr connectPeripheral:peripheral options:nil];
}
for ( CBPeripheral *savedPeripheral in self.discoveredPerpheralArray) {
if (![[savedPeripheral.identifier UUIDString] isEqualToString:[peripheral.identifier UUIDString]]) {
// self.discoveredPerpheralArray
}
}
NSString *peripheralName = peripheral.name;
NSRange wannaFitLocation = [peripheralName rangeOfString:@"WannaFit"];
if (wannaFitLocation.location != NSNotFound){
self.smartWatchPeripheral = peripheral;
[_centralMgr connectPeripheral:peripheral options:nil];
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
NSLog(@"didDisconnectPeripheral %s",__FUNCTION__);
}
//连接成功
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
NSLog(@"didConnectPeripheral %s",__FUNCTION__);
//Before you begin interacting with the peripheral, you should set the peripheral’s delegate to ensure that it receives the appropriate callbacks(设置代理)
[_discoveredPeripheral setDelegate:self];
//discover all of the services that a peripheral offers,搜索服务,回调didDiscoverServices
[_discoveredPeripheral discoverServices:nil];
[self.smartWatchPeripheral setDelegate:self];
[self.smartWatchPeripheral discoverServices:nil];
}
//连接失败,就会得到回调:
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error{
//此时连接发生错误
NSLog(@"connected periphheral failed");
}
//获取服务后的回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error
{
if (error)
{
NSLog(@"didDiscoverServices : %@", [error localizedDescription]);
return;
}
for (CBService *s in peripheral.services)
{
NSLog(@"Service found with UUID : %@", s.UUID);
//Discovering all of the characteristics of a service,回调didDiscoverCharacteristicsForService
[s.peripheral discoverCharacteristics:nil forService:s];
}
}
//获取特征后的回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error)
{
NSLog(@"didDiscoverCharacteristicsForService error : %@", [error localizedDescription]);
return;
}
NSLog(@"service == %@",service);
for (CBCharacteristic *c in service.characteristics)
{
NSLog(@"c.properties:%lu",(unsigned long)c.properties) ;
//Subscribing to a Characteristic’s Value 订阅
NSLog(@"characteristic == %@",c);
[peripheral setNotifyValue:YES forCharacteristic:c];
// read the characteristic’s value,回调didUpdateValueForCharacteristic
[peripheral readValueForCharacteristic:c];
_writeCharacteristic = c;
}
}
//订阅的特征值有新的数据时回调
- (void)peripheral:(CBPeripheral *)peripheral
didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error changing notification state: %@",
[error localizedDescription]);
}
[peripheral readValueForCharacteristic:characteristic];
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
NSLog(@"characteristic value == %@",characteristic.value);
if (error)
{
NSLog(@"didUpdateValueForCharacteristic error : %@", error.localizedDescription);
return;
}
NSData *data = characteristic.value;
_resultText.text = [[ NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(@"characteristic properties == %lu value == %@ ",(unsigned long)characteristic.properties,characteristic.value);
// NSLog(@"hex string == %@",[self convertDataToHexStr:data]);
//
//
}
- (NSMutableArray *)discoveredPerpheralArray{
if (!_discoveredPerpheralArray) {
_discoveredPerpheralArray = [NSMutableArray array];
}
return _discoveredPerpheralArray;
}
#pragma mark 写数据
-(void)writeChar:(NSData *)data
{
//回调didWriteValueForCharacteristic
[_discoveredPeripheral writeValue:data forCharacteristic:_writeCharacteristic type:CBCharacteristicWriteWithoutResponse];
NSData *data2 = [self test2Data];
NSLog(@"data2 == %@",data2);
for (CBService *service in self.smartWatchPeripheral.services) {
for (CBCharacteristic *characteristic in service.characteristics) {
[self.smartWatchPeripheral writeValue:data2 forCharacteristic:characteristic type:CBCharacteristicWriteWithoutResponse];
[self.smartWatchPeripheral writeValue:data2 forCharacteristic:characteristic type:CBCharacteristicWriteWithResponse];
}
}
}
//搜索到Characteristic的Descriptors
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error{
//打印出Characteristic和他的Descriptors
NSLog(@"characteristic uuid:%@",characteristic.UUID);
for (CBDescriptor *d in characteristic.descriptors) {
NSLog(@"Descriptor uuid:%@",d.UUID);
}
}
//获取到Descriptors的值
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(NSError *)error{
//打印出DescriptorsUUID 和value
//这个descriptor都是对于characteristic的描述,一般都是字符串,所以这里我们转换成字符串去解析
NSLog(@"characteristic uuid:%@ value:%@",[NSString stringWithFormat:@"%@",descriptor.UUID],descriptor.value);
}
#pragma mark 写数据后回调
- (void)peripheral:(CBPeripheral *)peripheral
didWriteValueForCharacteristic:(CBCharacteristic *)characteristic
error:(NSError *)error {
if (error) {
NSLog(@"Error writing characteristic value: %@",
[error localizedDescription]);
return;
}
NSLog(@"写入%@成功",characteristic);
}
#pragma mark 发送按钮点击事件
- (IBAction)sendClick:(id)sender {
// 字符串转Data
NSData *data =[_editText.text dataUsingEncoding:NSUTF8StringEncoding];
[self writeChar:data];
}
- (NSString *)convertDataToHexStr:(NSData *)data {
if (!data || [data length] == 0) {
return @"";
}
NSMutableString *string = [[NSMutableString alloc] initWithCapacity:[data length]];
[data enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
unsigned char *dataBytes = (unsigned char*)bytes;
for (NSInteger i = 0; i < byteRange.length; i++) {
NSString *hexStr = [NSString stringWithFormat:@"%x", (dataBytes[i]) & 0xff];
if ([hexStr length] == 2) {
[string appendString:hexStr];
} else {
[string appendFormat:@"0%@", hexStr];
}
}
}];
return string;
}
- (NSData *)convertHexStrToData:(NSString *)str {
if (!str || [str length] == 0) {
return nil;
}
NSMutableData *hexData = [[NSMutableData alloc] initWithCapacity:8];
NSRange range;
if ([str length] % 2 == 0) {
range = NSMakeRange(0, 2);
} else {
range = NSMakeRange(0, 1);
}
for (NSInteger i = range.location; i < [str length]; i += 2) {
unsigned int anInt;
NSString *hexCharStr = [str substringWithRange:range];
NSScanner *scanner = [[NSScanner alloc] initWithString:hexCharStr];
[scanner scanHexInt:&anInt];
NSData *entity = [[NSData alloc] initWithBytes:&anInt length:1];
[hexData appendData:entity];
range.location += range.length;
range.length = 2;
}
return hexData;
}
- (NSData *)test1Data{
Byte bytes[20] = {0x89,0x11,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x9A};
NSData *data2 = [NSData dataWithBytes:bytes length:20];
return data2;
}
- (NSData *)test2Data{
Byte bytes[20] = {0xA5,0x11,0x00,0x00,0x02,0x0A,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xC2};
NSData *data2 = [NSData dataWithBytes:bytes length:20];
return data2;
}
- (NSData *)setWatchTime{
Byte bytes[10] = {0x08,0x14,0x0e,0x0a,0x1f,0x0f,0x38,0x1e,0x01,0x00};
Byte resultByte = 0x00;
for (int i = 0; i<9; i++) {
resultByte += bytes[i];
}
NSLog(@"result bye == %x",resultByte);
resultByte = resultByte & 0xff;
NSLog(@"result bye == %x",resultByte);
bytes[9] = resultByte;
NSData *data2 = [NSData dataWithBytes:bytes length:10];
NSLog(@"data2 == %@",data2);
return data2;
}
- (NSData *)getWatchInfo{
Byte bytes[10] = {0xF6,0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x01,0x00};
Byte resultByte = 0x00;
for (int i = 0; i<9; i++) {
resultByte += bytes[i];
}
NSLog(@"result bye == %x",resultByte);
resultByte = resultByte & 0xff;
NSLog(@"result bye == %x",resultByte);
bytes[9] = resultByte;
NSData *data2 = [NSData dataWithBytes:bytes length:10];
NSLog(@"data2 == %@",data2);
return data2;
}
- (NSData *)findTheWatch{
Byte bytes[10] = {0x0B,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x00};
Byte resultByte = 0x00;
for (int i = 0; i<9; i++) {
resultByte += bytes[i];
}
resultByte = resultByte & 0xff;
bytes[9] = resultByte;
NSData *data2 = [NSData dataWithBytes:bytes length:10];
NSLog(@"data2 == %@",data2);
return data2;
}
@end
作为蓝牙设备
#import "BePeripheralViewController.h"
static NSString *const ServiceUUID1 = @"FFF0";
static NSString *const notiyCharacteristicUUID = @"FFF1";
static NSString *const readwriteCharacteristicUUID = @"FFF2";
static NSString *const ServiceUUID2 = @"FFE0";
static NSString *const readCharacteristicUUID = @"FFE1";
static NSString *const localTimeUUID = @"Local Time Information";
static NSString * const LocalNameKey = @"WannaFit-T10D-5F29";
@implementation BePeripheralViewController{
CBPeripheralManager *peripheralManager;
//定时器
NSTimer *timer;
//添加成功的service数量
int serviceNum;
UILabel *info;
}
- (void)viewDidLoad {
[super viewDidLoad];
/*
和CBCentralManager类似,蓝牙设备打开需要一定时间,打开成功后会进入委托方法
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral;
模拟器永远也不会得CBPeripheralManagerStatePoweredOn状态
*/
peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
//页面样式
[self.view setBackgroundColor:[UIColor whiteColor]];
info = [[UILabel alloc]initWithFrame:self.view.frame];
[info setText:@"正在打开设备"];
[info setTextAlignment:NSTextAlignmentCenter];
[self.view addSubview:info];
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
[peripheralManager stopAdvertising];
}
//配置bluetooch的
-(void)setUp{
//characteristics字段描述
CBUUID *CBUUIDCharacteristicUserDescriptionStringUUID = [CBUUID UUIDWithString:CBUUIDCharacteristicUserDescriptionString];
/*
可以通知的Characteristic
properties:CBCharacteristicPropertyNotify
permissions CBAttributePermissionsReadable
*/
CBMutableCharacteristic *notiyCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:notiyCharacteristicUUID] properties:CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsReadable];
/*
可读写的characteristics
properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead
permissions CBAttributePermissionsReadable | CBAttributePermissionsWriteable
*/
CBMutableCharacteristic *readwriteCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readwriteCharacteristicUUID] properties:CBCharacteristicPropertyWrite | CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable | CBAttributePermissionsWriteable];
//设置description
CBMutableDescriptor *readwriteCharacteristicDescription1 = [[CBMutableDescriptor alloc]initWithType: CBUUIDCharacteristicUserDescriptionStringUUID value:@"name"];
[readwriteCharacteristic setDescriptors:@[readwriteCharacteristicDescription1]];
/*
只读的Characteristic
properties:CBCharacteristicPropertyRead
permissions CBAttributePermissionsReadable
*/
CBMutableCharacteristic *readCharacteristic = [[CBMutableCharacteristic alloc]initWithType:[CBUUID UUIDWithString:readCharacteristicUUID] properties:CBCharacteristicPropertyRead value:nil permissions:CBAttributePermissionsReadable];
//service1初始化并加入两个characteristics
CBMutableService *service1 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID1] primary:YES];
NSLog(@"%@",service1.UUID);
[service1 setCharacteristics:@[notiyCharacteristic,readwriteCharacteristic]];
//service2初始化并加入一个characteristics
CBMutableService *service2 = [[CBMutableService alloc]initWithType:[CBUUID UUIDWithString:ServiceUUID2] primary:YES];
[service2 setCharacteristics:@[readCharacteristic]];
//添加后就会调用代理的- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error
[peripheralManager addService:service1];
[peripheralManager addService:service2];
}
#pragma mark -- CBPeripheralManagerDelegate
//peripheralManager状态改变
- (void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral{
switch (peripheral.state) {
//在这里判断蓝牙设别的状态 当开启了则可调用 setUp方法(自定义)
case CBPeripheralManagerStatePoweredOn:
NSLog(@"powered on");
[info setText:[NSString stringWithFormat:@"设备名%@已经打开,可以使用center进行连接",LocalNameKey]];
[self setUp];
break;
case CBPeripheralManagerStatePoweredOff:
NSLog(@"powered off");
[info setText:@"powered off"];
break;
default:
break;
}
}
//perihpheral添加了service
- (void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error{
if (error == nil) {
serviceNum++;
}
//因为我们添加了2个服务,所以想两次都添加完成后才去发送广播
if (serviceNum==2) {
//添加服务后可以在此向外界发出通告 调用完这个方法后会调用代理的
//(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error
[peripheralManager startAdvertising:@{
CBAdvertisementDataServiceUUIDsKey : @[[CBUUID UUIDWithString:ServiceUUID1],[CBUUID UUIDWithString:ServiceUUID2]],
CBAdvertisementDataLocalNameKey : LocalNameKey
}
];
}
}
//peripheral开始发送advertising
- (void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error{
NSLog(@"in peripheralManagerDidStartAdvertisiong");
}
//订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"订阅了 %@的数据",characteristic.UUID);
//每秒执行一次给主设备发送一个当前时间的秒数
timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(sendData:) userInfo:characteristic repeats:YES];
}
//取消订阅characteristics
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic{
NSLog(@"取消订阅 %@的数据",characteristic.UUID);
//取消回应
[timer invalidate];
}
//发送数据,发送当前时间的秒数
-(BOOL)sendData:(NSTimer *)t {
CBMutableCharacteristic *characteristic = t.userInfo;
NSDateFormatter *dft = [[NSDateFormatter alloc]init];
[dft setDateFormat:@"ss"];
NSLog(@"%@",[dft stringFromDate:[NSDate date]]);
//执行回应Central通知数据
return [peripheralManager updateValue:[[dft stringFromDate:[NSDate date]] dataUsingEncoding:NSUTF8StringEncoding] forCharacteristic:(CBMutableCharacteristic *)characteristic onSubscribedCentrals:nil];
}
//读characteristics请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request{
NSLog(@"didReceiveReadRequest");
NSLog(@"request == %@ request.value == %@",request,request.value);
//判断是否有读数据的权限
if (request.characteristic.properties & CBCharacteristicPropertyRead) {
NSData *data = request.characteristic.value;
[request setValue:data];
//对请求作出成功响应
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
//写characteristics请求
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray *)requests{
NSLog(@"didReceiveWriteRequests");
CBATTRequest *request = requests[0];
//判断是否有写数据的权限
if (request.characteristic.properties & CBCharacteristicPropertyWrite) {
//需要转换成CBMutableCharacteristic对象才能进行写值
CBMutableCharacteristic *c =(CBMutableCharacteristic *)request.characteristic;
c.value = request.value;
[peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}else{
[peripheralManager respondToRequest:request withResult:CBATTErrorWriteNotPermitted];
}
}
//
- (void)peripheralManagerIsReadyToUpdateSubscribers:(CBPeripheralManager *)peripheral{
NSLog(@"peripheralManagerIsReadyToUpdateSubscribers");
}
@end
更进一步的蓝牙功能
https://my.oschina.net/u/2340880/blog/644432
https://github.com/ZYHshao/BlueGame
参考资料
iOS简易蓝牙对战五子棋游戏设计思路之一——核心蓝牙通讯类的设计
https://my.oschina.net/u/2340880/blog/644432
ios蓝牙开发(二)ios连接外设的代码实现
http://liuyanwei.jumppo.com/2015/08/14/ios-BLE-2.html
iOS-BLE蓝牙开发持续更新
http://www.jianshu.com/p/84b5b834b942
刘彦玮的技术博客中文章对应的demo http://liuyanwei.jumppo.com http://liuyanwei.jumppo.com
https://github.com/coolnameismy/demo
来源:oschina
链接:https://my.oschina.net/u/2360054/blog/751036