ios开发蓝牙的基本使用
用到的数据
static NSString *kCellReuseIdentifier = @"cell";
static NSString *READ_WRITE_SERVICEID = @"要搜索的设备的特征";
static NSString *CMD_DOWN = @"指令";
static NSString *RES_SUCCEED_DOWN = @"指令";
static NSString *CMD_UP = @"指令";
static NSString *RES_SUCCEED_UP = @"指令";
static NSString *RES_FAILURE_UP = @"指令(主要发生之前需要转换为16进制的数据,及得跟做硬件的哥们沟通数据格式)";
1. 声明一下下
#import <CoreBluetooth/CoreBluetooth.h>
2 初始化在viewdidload 中
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];
3遵守协议
<CBCentralManagerDelegate,CBPeripheralDelegate>
4一些需要用到的属性
@property (nonatomic, strong) CBCentralManager *centralManager;
@property (nonatomic, strong) CBCharacteristic *writeCMDCharacteristic;
@property (nonatomic, strong) CBPeripheral *connectedPeripheral;
@property (nonatomic, strong) CBService *readWriteService;
@property (nonatomic, strong) NSMutableArray<CBUUID *> *characteristicIDs;
@property (nonatomic, strong) NSMutableArray<CBPeripheral *> *carLocksArray;
@property (nonatomic, strong) CBCharacteristic *readRESCharacteristic;
@property (nonatomic, strong) CBCharacteristic *readWritePasswordCharacteristic;
5 具体代码,发送信息给设备
[self.connectedPeripheral writeValue:[self convertHexStrToData:CMD_UP] forCharacteristic:self.writeCMDCharacteristic type:CBCharacteristicWriteWithoutResponse];
其中CMD_UP 是跟硬件约定好的要发的指令/我这硬件做的是要求发送的信息是16进制的 ,所以得转发,接下来的接受到的依然是16进制的信息,需要转发,方法在文章最后面。
#pragma mark - 字符串与十六进制数据转换
- (NSData *)convertHexStrToData:(NSString *)str
#pragma mark - 懒加载
-(NSMutableArray *)carLocksArray {
if (!_carLocksArray) {
_carLocksArray = [NSMutableArray array];
}
return _carLocksArray;
}
- (NSMutableArray<CBUUID *> *)characteristicIDs {
if (!_characteristicIDs) {
_characteristicIDs = [NSMutableArray array];
for (NSInteger i= 0; i < 6; i ++) {
CBUUID *uuid = [CBUUID UUIDWithString:[NSString stringWithFormat:@"FF0%ld",i+1]];
[_characteristicIDs addObject:uuid];
}
}
return _characteristicIDs;
}
addStateMessage:@"正在搜索'FF12'服务..."];
NSLog(@"正在搜索'FF12'服务...");
self.connectedPeripheral = peripheral;
[self refreshUIYes];
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
[self addStateMessage:[NSString stringWithFormat:@"已断开与%@的连接",peripheral.name]];
NSLog(@"已断开与%@的连接",peripheral.name);
self.connectedPeripheral = nil;
[self refreshUINo];
[self.centralManager connectPeripheral:selectedCarLock options:nil];
[self addStateMessage:[NSString stringWithFormat:@"正在连接 %@...",selectedCarLock.name]];
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
[self addStateMessage:[NSString stringWithFormat:@"未能连接上%@",peripheral.name]];
NSLog(@"未能连接上%@",peripheral.name);
[self refreshUINo];
}
#pragma mark - CBPeripheralDelegate
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
if (error) {
[self addStateMessage:[NSString stringWithFormat:@"code:%ld,发现特征值错误: %@",error.code ,error.localizedDescription]];
NSLog(@"code:%ld,发现特征值错误: %@",error.code ,error.localizedDescription);
return;
}
NSString *res = [self convertDataToHexStr:characteristic.value];
//[self addStateMessage:res];
NSLog(@"res = %@",res);
//电量
NSMutableString *muStr = [NSMutableString stringWithFormat:@"%@",res];
NSString *middleStr = [muStr substringWithRange:NSMakeRange(4, 8)];
NSString *lasterStr = [muStr substringFromIndex:muStr.length-2];
//NSLog(@"result:%@",middleStr);
float batteryLow = [[self convertHexStrToString:middleStr] floatValue]/6000;
NSLog(@"10----%f",batteryLow);
NSLog(@"10----%@",[self convertHexStrToString:middleStr]);
//判断是不是回复的
NSString *topStr = @"";
if (muStr.length > 22) {
topStr = [muStr substringToIndex:22];
}
if ([topStr isEqualToString:@"ffacffffffffffffff2902"]) {//是回复的指令
self.top_rightBtn.userInteractionEnabled = YES;
NSString *lasterTwoStr = [muStr substringFromIndex:muStr.length-2];
NSString *lasterFour_TwoStr = [muStr substringWithRange:NSMakeRange(muStr.length-4, 2)];
if ([lasterTwoStr isEqualToString:@"00"]) {
NSLog(@"=====success");
[self refreshUIYes];
if ([lasterFour_TwoStr isEqualToString:@"00"]) {//关锁
NSLog(@"%@",[NSString stringWithFormat:@"关锁成功-%d",repeatNumThree]);
[self addStateMessage:@"关锁成功"];
deviceStatus = NO;
[self refreshUIYes];
}else{//开锁
NSLog(@"%@",[NSString stringWithFormat:@"开锁成功-%d",repeatNumThree]);
[self addStateMessage:@"开锁成功"];
deviceStatus = YES;
[self refreshUIYes];
}
repeatNumThree = 0;
}else if ([lasterTwoStr isEqualToString:@"01"]) {
if ([lasterFour_TwoStr isEqualToString:@"00"]) {//关锁
NSLog(@"关锁失败");
if (repeatNumThree <= 3) {
repeatNumThree ++;
[self liftUpAction];
}else{
[self addStateMessage:@"关锁失败"];
}
}else{//开
NSLog(@"开锁失败");
if (repeatNumThree <= 3) {
repeatNumThree ++;
[self liftDownAction];
}else{
[self addStateMessage:@"开锁失败"];
}
}
}
}else{
NSLog(@"=====自动回复的指令");
//开关状态
//NSLog(@"%@",lasterStr);
if ([lasterStr isEqualToString:@"43"]) {
//NSLog(@"升起的状态");
[self addStateMessage:@"车锁已升起,请放心"];
deviceStatus = NO;
}else{
//NSLog(@"落下的状态");
[self addStateMessage:@"车锁已经落下,可入位"];
deviceStatus = YES;
}
[self refreshUIYes];
//电池电量
if ([lasterStr isEqualToString:@"43"] || [lasterStr isEqualToString:@"4f"]) {
if (batteryLow <= 0.2) {
self.batteryImg.image = [UIImage imageNamed:@"dian_red"];
}else{
self.batteryImg.image = [UIImage imageNamed:@"dian_man"];
}
self.batteryImg.frame = CGRectMake(2, 2, 92*batteryLow, 26);
}
}
// // 读取密码
// if (characteristic == self.readWritePasswordCharacteristic) {
// NSString *hexPassword = [self convertDataToHexStr:characteristic.value];
// NSString *decimalPassword = [self convertHexStrToString:hexPassword];
// [self addStateMessage:[NSString stringWithFormat:@"password = %@",decimalPassword]];
// NSLog(@"password = %@",decimalPassword);
//
// if ([self.inputPassword isEqualToString:decimalPassword]) {
// // 当前点击的是更改设备名称
// if (self.currentSelectedIndexPath.section == 1 && self.currentSelectedIndexPath.row == 0) {
// // TODO 点击的是更改设备名称
// }
//
// // 当前点击的是更改密码
// if (self.currentSelectedIndexPath.section == 1 && self.currentSelectedIndexPath.row == 1) {
// ChangePasswordViewController *changePasswordVC = [[ChangePasswordViewController alloc] initWithNibName:NSStringFromClass([ChangePasswordViewController class]) bundle:nil];
// changePasswordVC.delegate = self;
// [self.navigationController pushViewController:changePasswordVC animated:YES];
// }
// }else {
// [self addStateMessage:@"密码输入错误"];
// NSLog(@"密码输入错误");
// }
// return;
// }
//
// if (characteristic == self.readRESCharacteristic) {
// NSString *resString = [self convertDataToHexStr:characteristic.value];
// [self addStateMessage:[NSString stringWithFormat:@"resString = %@",resString]];
// NSLog(@"resString = %@",resString);
//
// if ([resString isEqualToString:RES_SUCCEED_UP]) {
//// [self addStateMessage:@"指令发送成功,设备已升起"];
// NSLog(@"指令发送成功,设备已升起");
// self.liftUpButton.enabled = NO;
// self.liftDownButton.enabled = YES;
// }
//
// if ([resString isEqualToString:RES_SUCCEED_DOWN]) {
//// [self addStateMessage:@"指令发送成功,设备已降下"];
// NSLog(@"指令发送成功,设备已降下");
// self.liftDownButton.enabled = NO;
// self.liftUpButton.enabled = YES;
// }
//
// if ([resString isEqualToString:RES_FAILURE_UP]) {
//// [self addStateMessage:@"车位锁为能成功升起,可能遇到障碍物"];
// NSLog(@"车位锁为能成功升起");
// self.liftUpButton.enabled = NO;
// self.liftUpButton.enabled = YES;
// }
// }
}
-(void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
//[self addStateMessage:[NSString stringWithFormat:@"已找到指定服务,扫描特征..."]];
NSLog(@"已找到指定服务,扫描特征...");
for (CBService *service in peripheral.services) {
if ([service.UUID.UUIDString isEqualToString:READ_WRITE_SERVICEID]) {
self.readWriteService = service;
break;
}
}
if (self.readWriteService != nil) {
[self.connectedPeripheral discoverCharacteristics:self.characteristicIDs forService:self.readWriteService];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error {
[self addStateMessage:[NSString stringWithFormat:@"已经链接车位锁,等待发送指令"]];
NSLog(@"已找到相应特征,等待发指令...");
self.writeCMDCharacteristic = [service.characteristics objectAtIndex:0];
self.readRESCharacteristic = [service.characteristics objectAtIndex:1];
self.readWritePasswordCharacteristic = [service.characteristics objectAtIndex:4];
[self.connectedPeripheral setNotifyValue:YES forCharacteristic:self.readRESCharacteristic];
[self.connectedPeripheral setNotifyValue:YES forCharacteristic:self.readWritePasswordCharacteristic];
// self.liftUpButton.enabled = YES;
//self.liftDownButton.enabled = YES;
[self.connectedPeripheral writeValue:[self convertHexStrToData:RECEIVESTATUS] forCharacteristic:self.writeCMDCharacteristic type:CBCharacteristicWriteWithoutResponse];
}
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error {
[self addStateMessage:[NSString stringWithFormat:@"发送指令已完成,等待结果..."]];
NSLog(@"发送指令已完成,等待结果...");
}
- (void)addStateMessage:(NSString *)message {
NSLog(@"%@",message);
int xStart = 10;
if (bgStatuslab ==nil) {
UIImage *img = [UIImage imageNamed:@"green_icon"];
CGFloat scale = [UIScreen mainScreen].scale;
UIImage *dot9 = [img resizableImageWithCapInsets:(UIEdgeInsets){0/scale,20/scale,5/scale,20/scale} resizingMode:UIImageResizingModeStretch]; //{top/scale,left/scale,bottom/scale,right/scale} top left bottom right 表示图片不拉伸的尺寸.
self.status_new_img.image = dot9;
bgStatuslab = [[UILabel alloc] initWithFrame:CGRectMake(xStart, 5, self.status_new_img.frame.size.width - xStart*2, self.status_new_img.frame.size.height - 10)];
bgStatuslab.textAlignment = NSTextAlignmentCenter;
bgStatuslab.textColor = [UIColor whiteColor];
bgStatuslab.font = [UIFont systemFontOfSize:13];
[self.status_new_img addSubview:bgStatuslab];
}
self.status_new_img.frame = CGRectMake(110, 50, [self receiveTextWidthWithString:message] + 30, 30);
bgStatuslab.frame = CGRectMake(xStart, 5, self.status_new_img.frame.size.width - xStart*2, self.status_new_img.frame.size.height - 10);
bgStatuslab.text = message;
}
// 得到文字的宽度
-(int)receiveTextWidthWithString:(NSString *)str
{
CGSize maxWidth = CGSizeMake(MAXFLOAT, 20);
NSDictionary *dic = @{NSFontAttributeName:[UIFont systemFontOfSize:13]};
CGRect rect = [str boundingRectWithSize:maxWidth options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil];
int width = (int)rect.size.width;
return width;
}
#pragma mark - 字符串与十六进制数据转换
- (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;
}
NSLog(@"hexdata: %@", hexData);
return hexData;
}
- (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;
}
//16-->10
- (NSString *)convertHexStrToString:(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;
}
NSString *string = [[NSString alloc]initWithData:hexData encoding:NSUTF8StringEncoding];
return string;
}
//将NSString转换成十六进制的字符串则可使用如下方式:
- (NSString *)convertStringToHexStr:(NSString *)str {
if (!str || [str length] == 0) {
return @"";
}
NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
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;
}
更多的需要跟做硬件的哥们去商议具体怎么沟通.
下面说说开发蓝牙遇到的坑
1.ios开发有时候连接上了无法获取蓝牙模块自动返回的值那么监听一下
[self.connectedPeripheral setNotifyValue:YES forCharacteristic:self.readRESCharacteristic];
[self.connectedPeripheral setNotifyValue:YES forCharacteristic:self.readWritePasswordCharacteristic];
2.ios开发蓝牙,发觉手机已经连接上了,就搜索不到了.这是需要把搜索到的设备以及中央设备等弄成全局的持有.每次进来之前断开重新初始化在连接
[centerMangerDevice cancelPeripheralConnection:selectedCarLock];
centerMangerDevice = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()];