题外话:
两个月多月之前,华夏大地有多危险?
如果武汉没有封城、如果湖北没有封省、如果春节期间坚持走亲串户。
那么,现在欧洲处境就是我们处境。医疗资源耗尽、医疗体系崩溃、不断有人离去。真的不敢往下想。
如今,正式大家努力,让中国变成世界最安全的国家。从“危”转“机”。
而我本人也经历了一场“危”“机”,虽然跟新冠病毒没有直接关系。去年的项目因为各种原因,团队解散,在一次面临着失业。
好在朋友介绍,顺利尽力新公司,开始新的征程。
原生开发做了几年,其实涉及的内容并不多,无非就是界面的布局,与服务器做交互等待相关事情。
新岗位也是开发相关的,但是一开就是蓝牙通讯,这个之前未曾涉及。也很久没有更新博客,这篇文章算是做个记录:
做蓝牙开发,如果没有看过苹果的蓝牙相关的文档,个人建议先去看苹果的官方文档,虽然全市英文,啃完以后会清楚很多。以下是苹果官方文档的地址:
看完官方文档以后,可以看看别人的翻译和理解,这个也是很有帮助的:
http://www.saitjr.com/ios/core-bluetooth-overview.html
蓝牙开发其实也不难,个人理解大概可以分按照下面的思路进行:
打开蓝牙--搜索外设--连接--获取外设的服务--获取服务的特征--绑定通知--读写数据--断开连接
1 判断设备是否有蓝牙功能,即CBManagerState,如果设备都没蓝牙,其他都是纸上谈兵。只有设备支出蓝牙并且出于开启状态才能往下进行。
2 蓝牙连接的2端分为 中心和外设,中心Central:接收数据就是中心,外设Peripheral:发送数据就是外设。正常的开发中,手机是作为中心。蓝牙框架中,中心、外设都是对象,相关的方法是通过协议(代理)完成。
3 中心要搜索正在广播的蓝牙设备,那么要先有个中心对象,Central对象调用scanForPeripheralsWithServices:options 方法搜索,第一个参数传入要搜索的蓝牙设备UUID数组,如果没有传,则会搜索正在广播的所有蓝牙设备,第二参数一般为nil,表示在主线操作。
4 只有中心搜索到外设,会调用(搜索到一个调用一次)centralManager:centraldidDiscoverPeripheral:peripheraladvertisementData:advertisementDataRSSI:
5 找到自己需要的外设,中心调用connectPeripheral: options:nil连接
连接成功调用centralManager: didConnectPeripheral: 这个时候可以搜索外设的服务:外设调用discoverServices方法,可以传入外设服务uuid,如果没有传则搜索所有服务。
连接失败:centralManager: didFailToConnectPeripheral: error:
断开连接:centralManager: didDisconnectPeripheral: error:
6 连接成功,获取外设的服务,如果上面提到的discoverServices的方法有传入参数,就可以直接找服务的特征,如果没有传,这里要遍历服务,找到需要的服务,发现服务的方法外设的代理方法:peripheral: didDiscoverServices: 发现了服务:紧接着是特征:外设discoverCharacteristics:nil forService:发现对象服务的特征,
7 发现特征以后,调用peripheral:didDiscoverCharacteristicsForService: error:这里要根据自己开发中的蓝牙协议,哪些是特征是可以订阅的,哪些是特征是可以写的,哪些特征是可以读的(很少用)
8 特征订阅是否成功都会调用peripheral: didUpdateNotificationStateForCharacteristic: error:
9 写入成功回调方法:peripheral:didWriteValueForDescriptor:error:
外设写入数据的方法:writeValue:forCharacteristic: type:
10 收到蓝牙数据回调方法:peripheral:didUpdateValueForCharacteristic: error:
如果看过官方文档,获取随便度娘找,做到这一步应该都没问题,
实际开发中,最主要的是写入数据的方法(writeValue:forCharacteristic: type:),以及收到数据的方法:peripheral:didUpdateValueForCharacteristic: error:
至于用什么格式写入什么数据,要根据自己的协议完成,转成NSData在写入
收到数据的处理,也要自己的协议,完成解析,才能拿到数据。
代买如下:
- (void)centralManagerDidUpdateState:(nonnull CBCentralManager *)central {
// 更新中心蓝牙状态回调 central.state == CBManagerStatePoweredOn 才是可用的
}
// 扫描到外设,
- (void)centralManager:(CBCentralManager*)centraldidDiscoverPeripheral:(nonnullCBPeripheral*)peripheraladvertisementData:(nonnullNSDictionary *)advertisementDataRSSI:(nonnullNSNumber*)RSSI {
NSLog(@"%@", peripheral.name);
// 这里要找到自己的需要的外设才能连接
self.peripheral= peripheral;
[centralconnectPeripheral:peripheraloptions:nil];
}
// 链接成功,调用下面的方法
- (void)centralManager:(CBCentralManager*)centraldidConnectPeripheral:(CBPeripheral*)peripheral {
// NSLog(@"外设连接成功,先停止扫描");
// 链接成功,出于对性能的考虑,应该停止扫描
[self.centralManager stopScan];
// 设置外设代理
peripheral.delegate=self;
// 根据uuid来寻找服务 这个参数不能为空,否则会崩
[peripheraldiscoverServices:nil];
}
- (void)centralManager:(CBCentralManager*)centraldidFailToConnectPeripheral:(CBPeripheral*)peripheralerror:(NSError*)error {
NSLog(@"连接失败");
}
- (void)centralManager:(CBCentralManager*)centraldidDisconnectPeripheral:(CBPeripheral*)peripheralerror:(NSError*)error {
NSog(@"连接断开");
}
#pragma mark CBPeripheralDelegate 外设代理方法
// 发现服务 回调
- (void)peripheral:(CBPeripheral*)peripheraldidDiscoverServices:(NSError*)error {
if(error) {
PLog(@"发生错误");
}else{
// 在这里遍历服务,找到需要的服务
[peripheral.servicesenumerateObjectsUsingBlock:^(CBService*obj,NSUIntegeridx,BOOL*stop) {
CBService*service = obj;
if([obj.UUID.UUIDStringisEqual:SERVICES_UUID]) {
[self.peripheral discoverCharacteristics:nil forService:service];
}
}];
}
}
// 发现特征 回调
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(nonnull CBService *)service error:(nullable NSError *)error {
// 遍历特征
[service.characteristicsenumerateObjectsUsingBlock:^(CBCharacteristic*_Nonnullobj,NSUIntegeridx,BOOL*_Nonnullstop) {
if([obj.UUID.UUIDStringisEqual:ChAR_NOTIFY_UUID]) {// 支持订阅
self.bleNotifyChar= obj;
// 开启通讯通道
[peripheral setNotifyValue:YES forCharacteristic:obj];
}
if([obj.UUID.UUIDStringisEqual:CHAR_WRITE_UUID]) {// 支持写入
self.bleWriteChar = obj;
}
}];
}
// 订阅状态发送改变,调用下面这个方法
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(nonnull CBCharacteristic *)characteristic error:(nullable NSError *)error {
if(error) {
NSLog(@"订阅失败----%@", error);
return;
}
NSString*rst = characteristic.isNotifying?@"订阅成功 --------> 读取数据":@"取消订阅";
NSLog(@"%@", rst);
}
// 接收到数据回调
- (void)peripheral:(CBPeripheral*)peripheraldidUpdateValueForCharacteristic:(CBCharacteristic*)characteristicerror:(NSError*)error {
if(error) {
PLog(@"发生错误了");
return;
}
NSData*data = characteristic.value;
// 只有当返回的数据不为空,并且回调有实现才执行回调
NSString*str = [Tools convertDataToHexStr:data];
}
- (void)peripheral:(CBPeripheral*)peripheraldidWriteValueForDescriptor:(CBDescriptor*)descriptorerror:(NSError*)error {
NSLog(@"写入成功");
}