首先要在Info.plist中添加一个项目
key为Required background modes
类型为array
添加两个值App shares data using CoreBluetooth和App communicates using CoreBluetooth
Central:
//
// CentralViewController.m
// CentralPeripheralTool
//
// Created by LiuMingchuan on 2016/11/29.
// Copyright © 2016年 LiuMingchuan. All rights reserved.
//
#import "CentralViewController.h"
#import "SinglePeripheralViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>
@interface CentralViewController ()<UITableViewDelegate,UITableViewDataSource,CBCentralManagerDelegate,CBPeripheralDelegate>{
NSMutableArray *peripherals;
}
@property (strong, nonatomic) IBOutlet UITableView *peripheralsTV;
@property (strong, nonatomic) IBOutlet UITextView *logTxtV;
@property (strong, nonatomic) CBCentralManager *centralManager;
@end
@implementation CentralViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setTitle:@"Central"];
_peripheralsTV.delegate = self;
_peripheralsTV.dataSource = self;
peripherals = [NSMutableArray array];
//调整因版本问题出现的导航遮挡视图
if ([[UIDevice currentDevice]systemVersion].doubleValue>7.0) {
[self setEdgesForExtendedLayout:UIRectEdgeNone];
[self.navigationController.navigationBar setTranslucent:NO];
}
_centralManager = [[CBCentralManager alloc]initWithDelegate:self queue:nil options:@{CBCentralManagerOptionRestoreIdentifierKey:@"Ryoma"}];
_centralManager.delegate = self;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc]initWithTitle:@"ClearLog" style:UIBarButtonItemStylePlain target:self action:@selector(clearLog)];
}
#pragma mark - 清空记录
- (void)clearLog {
_logTxtV.text = @"";
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [peripherals count];
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *identifer = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifer];
}
cell.textLabel.text = ((CBPeripheral *)[peripherals objectAtIndex:indexPath.row]).name;
return cell;
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CBPeripheral *peripheral = [peripherals objectAtIndex:indexPath.row];
switch (peripheral.state) {
case CBPeripheralStateConnected:
{
[self showLogMsg:[NSString stringWithFormat:@"%@已经连接",peripheral.name]];
SinglePeripheralViewController *singlePeripheralVC = [[SinglePeripheralViewController alloc]initWithPeripheral:peripheral];
[self.navigationController pushViewController:singlePeripheralVC animated:YES];
break;
}
case CBPeripheralStateDisconnected:
{
[_centralManager connectPeripheral:peripheral options:nil];
[self showLogMsg:[NSString stringWithFormat:@"%@正在连接。。。。。。",peripheral.name]];
break;
}
default:
break;
}
}
#pragma mark - 是否连接了周边设备
-(void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral {
[self showLogMsg:[NSString stringWithFormat:@"%@连接成功",peripheral.name]];
SinglePeripheralViewController *singlePeripheralVC = [[SinglePeripheralViewController alloc]initWithPeripheral:peripheral];
[self.navigationController pushViewController:singlePeripheralVC animated:YES];
}
#pragma mark - 是否断开了周边设备
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
[self showLogMsg:[NSString stringWithFormat:@"%@断开连接",peripheral.name]];
[peripherals removeObject:peripheral];
[_peripheralsTV reloadData];
}
#pragma mark - 连接周边设备失败
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
[self showLogMsg:[NSString stringWithFormat:@"%@连接失败",peripheral.name]];
[peripherals removeObject:peripheral];
[_peripheralsTV reloadData];
}
#pragma mark - 中心设备状态监测
-(void)centralManagerDidUpdateState:(CBCentralManager *)central {
switch (central.state) {
case CBManagerStatePoweredOn:
[_centralManager scanForPeripheralsWithServices:nil options:nil];
break;
case CBManagerStatePoweredOff:
[self showLogMsg:@"蓝牙未打开"];
break;
default:
[self showLogMsg:@"设备不支持BLE"];
break;
}
}
#pragma mark - 检测到周边设备
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *,id> *)advertisementData RSSI:(NSNumber *)RSSI {
if (![peripherals containsObject:peripheral]) {
[peripherals addObject:peripheral];
[self showLogMsg:[NSString stringWithFormat:@"检索到周边%@",[peripheral name]]];
[_peripheralsTV reloadData];
}
}
#pragma mark - 状态保存和恢复
-(void)centralManager:(CBCentralManager *)central willRestoreState:(NSDictionary<NSString *,id> *)dict {
//central提供信息,dict包含了应用程序关闭是系统保存的central的信息,用dic去恢复central
//app状态的保存或者恢复,这是第一个被调用的方法当APP进入后台去完成一些蓝牙有关的工作设置,使用这个方法同步app状态通过蓝牙系统
}
#pragma mark - Log信息显示
- (void)showLogMsg:(NSString *)logMsg {
if ([@"" isEqualToString:_logTxtV.text]) {
_logTxtV.text = [NSString stringWithFormat:@">>%@",logMsg];
} else {
_logTxtV.text = [NSString stringWithFormat:@"%@\n>>%@",_logTxtV.text,logMsg];
}
[_logTxtV scrollRangeToVisible:NSMakeRange(_logTxtV.text.length, 1)];
_logTxtV.layoutManager.allowsNonContiguousLayout = NO;
}
#pragma mark - other
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
//
// SinglePeripheralViewController.m
// CentralPeripheralTool
//
// Created by LiuMingchuan on 2016/11/29.
// Copyright © 2016年 LiuMingchuan. All rights reserved.
//
#import "SinglePeripheralViewController.h"
#import "SingleCharacteristicViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>
@interface SinglePeripheralViewController ()<UITableViewDelegate,UITableViewDataSource,CBPeripheralDelegate> {
NSMutableArray *services;
}
@property (strong, nonatomic) IBOutlet UITableView *singlePeripheralTV;
@property (strong, nonatomic) IBOutlet UITextView *logTxtV;
@property (strong, nonatomic) CBPeripheral *singlePeripheral;
@end
@implementation SinglePeripheralViewController
-(instancetype)initWithPeripheral:(CBPeripheral *)peripheral {
self = [super init];
_singlePeripheral = peripheral;
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setTitle:_singlePeripheral.name];
services = [NSMutableArray array];
_singlePeripheral.delegate = self;
[_singlePeripheral discoverServices:nil];
_singlePeripheralTV.delegate = self;
_singlePeripheralTV.dataSource = self;
}
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
CBService *service = (CBService *)[services objectAtIndex:section];
[self showLogMsg:[NSString stringWithFormat:@"%ld",[service.characteristics count]]];
return [service.characteristics count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return [services count];
}
- (NSString *)tableView:(UITableView *)tableView titleForHeaderInSection:(NSInteger)section {
return ((CBService *)[services objectAtIndex:section]).UUID.UUIDString;
}
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
NSString *identifer = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifer];
if (!cell) {
cell = [[UITableViewCell alloc]initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifer];
}
CBService *service = (CBService *)[services objectAtIndex:indexPath.section];
CBCharacteristic *characteristic = (CBCharacteristic *)[service.characteristics objectAtIndex:indexPath.row];
cell.textLabel.text = characteristic.UUID.UUIDString;
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
CBService *service = (CBService *)[services objectAtIndex:indexPath.section];
CBCharacteristic *characteristic = (CBCharacteristic *)[service.characteristics objectAtIndex:indexPath.row];
SingleCharacteristicViewController *singleCharacteristicVC = [[SingleCharacteristicViewController alloc]initWithPeripheral:_singlePeripheral characteristic:characteristic];
[self.navigationController pushViewController:singleCharacteristicVC animated:YES];
}
#pragma mark - 搜索周边设备
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
for (CBService *service in peripheral.services) {
if ([services count]==0) {
[services addObject:service];
[peripheral discoverCharacteristics:nil forService:service];
} else {
if (![self containsService:service]) {
[services addObject:service];
[peripheral discoverCharacteristics:nil forService:service];
}
}
}
}
#pragma mark - 是否已经保存服务
- (Boolean)containsService:(CBService *)service {
for (CBService *single in services) {
if ([single.UUID.UUIDString isEqualToString:service.UUID.UUIDString]) {
return YES;
}
}
return NO;
}
#pragma mark - 有新的特征,更新列表
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
[_singlePeripheralTV reloadData];
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
}
#pragma mark - Log信息显示
- (void)showLogMsg:(NSString *)logMsg {
if ([@"" isEqualToString:_logTxtV.text]) {
_logTxtV.text = [NSString stringWithFormat:@">>%@",logMsg];
} else {
_logTxtV.text = [NSString stringWithFormat:@"%@\n>>%@",_logTxtV.text,logMsg];
}
[_logTxtV scrollRangeToVisible:NSMakeRange(_logTxtV.text.length, 1)];
_logTxtV.layoutManager.allowsNonContiguousLayout = NO;
}
#pragma mark - other
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
//
// SingleCharacteristicViewController.m
// CentralPeripheralTool
//
// Created by LiuMingchuan on 2016/11/30.
// Copyright © 2016年 LiuMingchuan. All rights reserved.
//
#import "SingleCharacteristicViewController.h"
@interface SingleCharacteristicViewController ()<CBPeripheralDelegate>
@property (strong, nonatomic) CBPeripheral *singlePeripheral;
@property (strong, nonatomic) CBCharacteristic *singleCharacteristic;
@property (weak, nonatomic) IBOutlet UITextView *logTxtV;
@end
@implementation SingleCharacteristicViewController
-(instancetype)initWithPeripheral:(CBPeripheral *)peripheral characteristic:(CBCharacteristic *)characteristic {
self = [super init];
_singlePeripheral = peripheral;
_singleCharacteristic = characteristic;
return self;
}
- (void)viewDidLoad {
[super viewDidLoad];
[self setTitle:_singleCharacteristic.UUID.UUIDString];
_singlePeripheral.delegate = self;
[_singlePeripheral setNotifyValue:YES forCharacteristic:_singleCharacteristic];
// Do any additional setup after loading the view from its nib.
}
-(void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
if (characteristic.value) {
NSString *value = [[NSString alloc]initWithData:characteristic.value encoding:NSUTF8StringEncoding];
[self showLogMsg:value];
} else {
[self showLogMsg:@"没有特征值"];
}
}
#pragma mark - Log信息显示
- (void)showLogMsg:(NSString *)logMsg {
if ([@"" isEqualToString:_logTxtV.text]) {
_logTxtV.text = [NSString stringWithFormat:@">>%@",logMsg];
} else {
_logTxtV.text = [NSString stringWithFormat:@"%@\n>>%@",_logTxtV.text,logMsg];
}
[_logTxtV scrollRangeToVisible:NSMakeRange(_logTxtV.text.length, 1)];
_logTxtV.layoutManager.allowsNonContiguousLayout = NO;
}
#pragma mark - other
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end
Peripheral:
//
// PeripheralViewController.m
// BluetoothDemo
//
// Created by LiuMingchuan on 2016/11/26.
// Copyright © 2016年 LiuMingchuan. All rights reserved.
//
#import "PeripheralViewController.h"
#import <CoreBluetooth/CoreBluetooth.h>
@interface PeripheralViewController ()<CBPeripheralManagerDelegate>
@property (strong,nonatomic)CBPeripheralManager *peripheralManager;
@property (strong,nonatomic)NSMutableArray *centralList;
@property (strong,nonatomic)CBMutableService *service;
@property (strong,nonatomic)CBMutableCharacteristic *characteristic;
@property (strong,nonatomic) NSTimer *timer;
@property (weak, nonatomic) IBOutlet UIButton *startPeripheralBtn;
@property (weak, nonatomic) IBOutlet UIButton *updateCharacteristicBtn;
@property (weak, nonatomic) IBOutlet UITextView *logTxtV;
@end
@implementation PeripheralViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self setTitle:@"Peripheral"];
if([[[UIDevice currentDevice]systemVersion]floatValue]>7.0){
[self.navigationController.navigationBar setTranslucent:NO];
self.edgesForExtendedLayout = UIRectEdgeNone;
}
[_startPeripheralBtn addTarget:self action:@selector(startPeripheralBtnAction:) forControlEvents:UIControlEventTouchUpInside];
[_updateCharacteristicBtn addTarget:self action:@selector(updateCharacteristicBtnAction:) forControlEvents:UIControlEventTouchUpInside];
// Do any additional setup after loading the view from its nib.
}
#pragma mark - 打开外围设备
- (void)startPeripheralBtnAction:(id)sender {
if (!_peripheralManager) {
_peripheralManager = [[CBPeripheralManager alloc]initWithDelegate:self queue:nil];
_centralList = [NSMutableArray array];
}
}
#pragma mark - 使用计时器开始更新特征值
- (void)updateCharacteristicBtnAction:(id)sender {
if (!_timer) {
_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(autoMakeRate) userInfo:nil repeats:YES];
} else {
[_timer invalidate];
_timer = nil;
}
}
- (void)autoMakeRate {
if (_peripheralManager) {
NSString *value = [NSString stringWithFormat:@"%@:%d",[NSDate date],arc4random()%30+60];
NSData *data = [value dataUsingEncoding:NSUTF8StringEncoding];
[_peripheralManager updateValue:data forCharacteristic:_characteristic onSubscribedCentrals:nil];
[self showLogMsg:[NSString stringWithFormat:@"新值 %@",value]];
} else {
[self showLogMsg:@"未打开周边模式"];
[_timer invalidate];
_timer = nil;
}
}
-(void)peripheralManagerDidUpdateState:(CBPeripheralManager *)peripheral {
switch (peripheral.state) {
case CBManagerStatePoweredOn:
//
[self setUpService];
break;
case CBManagerStatePoweredOff:
[self showLogMsg:@"蓝牙未打开"];
break;
default:
//不支持BLE
[self showLogMsg:@"设备不支持BLE"];
_peripheralManager = nil;
break;
}
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didSubscribeToCharacteristic:(CBCharacteristic *)characteristic {
[self showLogMsg:[NSString stringWithFormat:@"中心:%@ 订阅特征:%@。",central.identifier.UUIDString,characteristic.UUID]];
if (![_centralList containsObject:central]) {
[_centralList addObject:central];
}
}
-(void)peripheralManager:(CBPeripheralManager *)peripheral central:(CBCentral *)central didUnsubscribeFromCharacteristic:(CBCharacteristic *)characteristic {
[self showLogMsg:[NSString stringWithFormat:@"中心:%@ 取消订阅特征:%@。",central.identifier.UUIDString,characteristic.UUID]];
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
[self showLogMsg:@"Central Read Data From Peripheral"];
[self showLogMsg:[[NSString alloc]initWithData:request.value encoding:NSUTF8StringEncoding]];
}
- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests {
[self showLogMsg:@"Receive Data From Peripheral"];
CBATTRequest *request = requests.lastObject;
[self showLogMsg:[[NSString alloc]initWithData:request.value encoding:NSUTF8StringEncoding]];
[peripheral respondToRequest:request withResult:CBATTErrorSuccess];
}
-(void)peripheralManager:(CBPeripheralManager *)peripheral didAddService:(CBService *)service error:(NSError *)error {
NSDictionary *dic = @{CBAdvertisementDataLocalNameKey:@"Ryoma's Peripheral"};
[_peripheralManager startAdvertising:dic];
[self showLogMsg:@"想周边添加了服务"];
}
-(void)peripheralManagerDidStartAdvertising:(CBPeripheralManager *)peripheral error:(NSError *)error {
[self showLogMsg:@"开始广播。。。。。。"];
}
#pragma mark - Log信息显示
- (void)showLogMsg:(NSString *)logMsg {
if ([@"" isEqualToString:_logTxtV.text]) {
_logTxtV.text = [NSString stringWithFormat:@">>%@",logMsg];
} else {
_logTxtV.text = [NSString stringWithFormat:@"%@\n>>%@",_logTxtV.text,logMsg];
}
[_logTxtV scrollRangeToVisible:NSMakeRange(_logTxtV.text.length, 1)];
_logTxtV.layoutManager.allowsNonContiguousLayout = NO;
}
#pragma mark - 服务设定
- (void)setUpService {
CBUUID *characteristicUUID = [CBUUID UUIDWithString:@"18035BA8-C04C-4CBF-93F4-FB2BB5ACFC46"];
_characteristic = [[CBMutableCharacteristic alloc]initWithType:characteristicUUID properties:CBCharacteristicPropertyWrite|CBCharacteristicPropertyNotify value:nil permissions:CBAttributePermissionsWriteEncryptionRequired];
CBUUID *serviceUUID = [CBUUID UUIDWithString:@"C03D84A9-F731-4B38-9486-DCA7387A9753"];
_service = [[CBMutableService alloc]initWithType:serviceUUID primary:YES];
[_service setCharacteristics:@[_characteristic]];
[_peripheralManager addService:_service];
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)dealloc {
[_timer invalidate];
}
/*
#pragma mark - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
// Get the new view controller using [segue destinationViewController].
// Pass the selected object to the new view controller.
}
*/
@end