iOS,蓝牙
我们的项目是中心设备开发
1.在git上找代码。星星多,就这个了

BabyBluetooth
点上面的连接👇滑动

2.报错
This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSBluetoothAlwaysUsageDescription key with a string value explaining to the user how the app uses this data.
解决:
plist文件添加:
Privacy - Bluetooth Peripheral Usage Description
Privacy - Bluetooth Always Usage Description

蓝牙后台运行
是上面的那个Privacy - Bluetooth Peripheral Usage Description
3.调试,打开下面,两个代码一个mac,一个真机。运行配对成功。
下面那个是中心设备开发

4.搜索蓝牙基础词汇知识:
Central(中心设备);
Peripheral(外围设备);
advertising(广告);
Services(服务);
Characteristic(特征)
CoreBluetooth介绍
等———————
5.我们项目app是中心设备
6.向硬件传数据:(这里要配合硬件)

写数据包括3个步骤:1.数据类型转化,2数据拼接3.异或运算
1.数据类型转化:
data 或 byte 类型的长度
Byte *bytes2 = (Byte *)[data bytes];
NSLog(@"%ld",[data length]);
ascii类型(在 iOS中就是 Byte 类型)<->data
//data类型转byte类型
Byte *bytes2 = (Byte *)[data bytes];
//byte数组转data类型
Byte bytes[]={0x01,0x02,0x03};
[data appendBytes:bytes length:3];
//byte字节转data类型
Byte byteLon =0;
[data appendBytes:&byteLon length:1];
byte->16进制的打印字符串。
//开发过程中要和硬件比对数据是否正确,并不做传输使用。
//打印16进制的byte字符串
-(NSString*)get16StrFromBtyea:(Byte[])byte lenth:(NSUInteger)lenth{
NSMutableString *str16 = [NSMutableString stringWithFormat:@""];
for (int i = 0; i< lenth ; i++) {
NSString *strOne= [NSString stringWithFormat:@"%x",byte[i]];
if (strOne.length==1) {
strOne = [NSString stringWithFormat:@"0%@",strOne];
}
// NSLog(@"比特数组打印结果%@",strOne);
str16= [NSMutableString stringWithFormat:@"%@%@",str16,strOne];
}
NSLog(@"比特数组打印链接结果%@",str16);
return str16;
}
字符串 <->16进制字符串
//普通字符串转换为十六进制的。
- (NSString *)hexStringFromString:(NSString *)string{
NSData *myD = [string dataUsingEncoding:NSUTF8StringEncoding];
Byte *bytes = (Byte *)[myD bytes];
//下面是Byte 转换为16进制。
NSString *hexStr=@"";
for(int i=0;i<[myD length];i++)
{
NSString *newHexStr = [NSString stringWithFormat:@"%x",bytes[i]&0xff];///16进制数
if([newHexStr length]==1)
hexStr = [NSString stringWithFormat:@"%@0%@",hexStr,newHexStr];
else
hexStr = [NSString stringWithFormat:@"%@%@",hexStr,newHexStr];
}
return hexStr;
}
// 十六进制转换为普通字符串的。
- (NSString *)stringFromHexString:(NSString *)hexString {
char *myBuffer = (char *)malloc((int)[hexString length] / 2 + 1);
bzero(myBuffer, [hexString length] / 2 + 1);
for (int i = 0; i < [hexString length] - 1; i += 2) {
unsigned int anInt;
NSString * hexCharStr = [hexString substringWithRange:NSMakeRange(i, 2)];
NSScanner * scanner = [[NSScanner alloc] initWithString:hexCharStr];
[scanner scanHexInt:&anInt];
myBuffer[i / 2] = (char)anInt;
}
NSString *unicodeString = [NSString stringWithCString:myBuffer encoding:4];
NSLog(@"------字符串=======%@",unicodeString);
return unicodeString;
}
16进制字符串 <->NSData
// 16进制转NSData
- (NSData *)convertHexStrToData:(NSString *)str
{
if (!str || [str length] == 0) {
return nil;
}
NSMutableData *hexData = [[NSMutableData alloc] initWithCapacity:20];
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转16进制 第一种
- (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转16进制 第二种
-(NSString *)hexStringFormData:(NSData *)data
{
return [[[[NSString stringWithFormat:@"%@",data]
stringByReplacingOccurrencesOfString:@"<" withString:@""]
stringByReplacingOccurrencesOfString:@">" withString:@""]
stringByReplacingOccurrencesOfString:@" " withString:@""];
}
字符串<->data
//字符串转data
-(NSData*) strToDataWithString :(NSString *)te{
NSMutableData* data = [NSMutableData data];
int idx;
for (idx = 0; idx+2 <= te.length; idx+=2) {
NSRange range = NSMakeRange(idx, 2);
NSString* hexStr = [te substringWithRange:range];
NSScanner* scanner = [NSScanner scannerWithString:hexStr];
unsigned int intValue;
[scanner scanHexInt:&intValue];
[data appendBytes:&intValue length:1];
}
return data;
}
data数据的拼接/截取
data 或 byte 类型的长度
Byte *bytes2 = (Byte *)[data bytes];
NSLog(@"%ld",[data length]);
ascii类型:在 iOS中就是 Byte 类型, (例如:Byte byt[]={0x01,0x02,0x03};)最终传给硬件的是data类型,

NSMutableData* data=[NSMutableData data];
1.data拼接Byte 数组类型
Byte bytes[]={0x01,0x02,0x03};
[data appendBytes:bytes length:3];
2.data拼接Byte 单个字节
Byte byteLon =0;
[data appendBytes:&byteLon length:1];
3.data拼接data类型
[data appendData:data2];
4.data 截取
NSData*subData =[data subdataWithRange:NSMakeRange(0,3)];//截取从索引0开的3个数据
异或运算
我们要对bytes进行异或运算
//data类型数据的长度
[data length]
//异或
Byte bytes[]={0x01,0x02,0x03};
Byte sum =0;
for (int i = 0; i < 3; i++) {
sum^=bytes[i];
}
//sum 就是异或运算的结果,是一个byte类型
7.接收硬件传输数据的反馈
//所有从蓝牙传到app的数据都会走这个方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
注册通知

获取数据


接收不到数据FISH00016ffd--Reading is not permitted.
1.找到原因
我这里git上又找了原生代码,(这个三方找问题太难了)
//判断有没有通知权限,如果没有那是铁定收不到数据的。
if (self.characteristic.properties & CBCharacteristicPropertyNotify || self.characteristic.properties & CBCharacteristicPropertyIndicate) {
}else{
NSLog(@"这个characteristic没有nofity的权限");
}
//注册通知
[ self.selectedPeripheral setNotifyValue:YES forCharacteristic:characteristic];
/**
* 数据更新的回调(但是有人硬是注册了通知,硬是要写入数据,就会报下面的错)
*/
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error) {
NSLog(@"%@--%@",peripheral.name,[error localizedDescription]);
}
打印报错:FISH00016ffd--Reading is not permitted.
2.appstore 搜索:蓝牙助手,下载
3.打开,左边是我们项目,右边是别人的。可以发现别人的有一个特征是可读,可写。我们的特征只有一个可读性,或可写。😓😓

4.baby的代码里有获取特征属性的方法:

我们的特征没有读取的属性所以注事readValueForCharacteristic这个方法就不会报:Reading is not permitted.这个错了

取消配对弹窗,我们取消了这个操作。
//让硬件改安全等级
蓝牙提示框


- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
// 蓝牙状态可用
// if (central.state == CBCentralManagerStatePoweredOn) {
// 如果蓝牙支持后台模式,一定要指定服务,否则在后台断开连接不上,如果不支持,可设为nil, option里的CBCentralManagerScanOptionAllowDuplicatesKey默认为NO, 如果设置为YES,允许搜索到重名,会很耗电
// [self.centralManager scanForPeripheralsWithServices:@[[CBUUID UUIDWithString:kServiceUUID]] options:nil];
// [self.centralManager scanForPeripheralsWithServices:nil options:nil];
// }
// else {
// NSLog(@"蓝牙状态异常, 请检查后重试");
// }
NSString *strMessage = @"";
NSString *buttonTitle = nil;
switch (central.state) {
case CBManagerStatePoweredOn: {
NSLog(@"蓝牙开启且可用");
[self.centralManager scanForPeripheralsWithServices:nil options:nil];
return;
}
break;
case CBManagerStateUnknown: {
strMessage = @"手机没有识别到蓝牙,请检查手机。";
buttonTitle = @"前往设置";
}
break;
case CBManagerStateResetting: {
strMessage = @"手机蓝牙已断开连接,重置中...";
buttonTitle = @"前往设置";
}
break;
case CBManagerStateUnsupported: {
strMessage = @"手机不支持蓝牙功能,请更换手机。";
}
break;
case CBManagerStatePoweredOff: {
strMessage = @"手机蓝牙功能关闭,请前往设置打开蓝牙及控制中心打开蓝牙。";
buttonTitle = @"前往设置";
}
break;
case CBManagerStateUnauthorized: {
strMessage = @"手机蓝牙功能没有权限,请前往设置。";
buttonTitle = @"前往设置";
}
break;
default: { }
break;
}
NSLog(@"%@%@",strMessage,buttonTitle);
}
系统蓝牙弹窗控制,app蓝牙权限第一次会弹系统的没办法自定义,后续可以自定义。
//没打开蓝牙的时候,总是提示。
// self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil];
//没打开蓝牙的时候,不提示。
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:nil options:@{CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:NO]}];
跳转到蓝牙设置界面

跳转到了应用的设置界面

NSString* phoneVersion = [[UIDevice currentDevice] systemVersion];
NSLog(@"手机系统版本: %@", phoneVersion);
NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([phoneVersion floatValue] > 10.0) {
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url];
}
} else {
if ([[UIApplication sharedApplication] canOpenURL:url]) {
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
}
}
iOS蓝牙后台运行
https://www.jianshu.com/p/da9e5237f8b8