公司最近帮一家物流公司做一个员工使用的APP,需要用到蓝牙去连接设备打印运单信息,下面记录一些在对接蓝牙打印时候遇到的问题,也总结一下其他技术文章中的一些知识点供大家参考,网上也有别人做好的第三方BabyBluetooth,这个第三方也可以在我们找的几种设备上正常打印,同学们可以去看看这个怎么使用,有问题的地方希望大家提出来。
一、关于蓝牙开发的一些重要的理论概念:
1、服务(services):蓝牙外设对外广播的时候一定会有一个服务,有些时候也可以是有多个服务,服务下面包含一些特性,服务可以理解成一个模块的窗口;
2、特征(characteristic):特征存在于服务下面的,一个服务下面可以有多个特征,特征可以理解成具体实现功能的窗口,一般的特性都会有value,也就是特征值,是特征和外界交互的最小单位;
3、UUID:蓝牙上的唯一标示符,为了区分不同服务和特征,就用UUID来表示。
二、蓝牙连接的主要步骤
1、创建一个CBCentralManager实例来进行蓝牙管理;
2、使用CBCentralManager对象搜索扫描外围设备;
3、连接外围设备;
4、获得外围设备的服务;
5、获得服务的特征;
6、从外围设备读取数据;
7、给外围设备发送(写入)数据。
三、蓝牙连接和数据读写的具体步骤
1、导入苹果系统蓝牙框架
2、遵循两个蓝牙框架相关的协议
3、新建两个实例属性,一个特征属性
@property (nonatomic, strong) CBCentralManager *centralManager; //中心管理者
@property (nonatomic, strong) CBPeripheral *peripheral; //连接到的外设
@property (nonatomic, strong) CBCharacteristic *characteristic; //特征
4、实例化CBCentralManager对象进行蓝牙管理
self.centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue()]; //创建实例进行蓝牙管理
在实例化该对象之后,就会触发下面这个必须实现的代理方法,否则在实例化之后会闪退,在该方法里面也可以获取到蓝牙的状态,可以在此处提示用户去打开蓝牙
- (void)centralManagerDidUpdateState:(CBCentralManager *)central{
NSString * state = nil;
switch ([central state])
{
case CBCentralManagerStateUnsupported:
state = @"The platform/hardware doesn't support Bluetooth Low Energy.";
break;
case CBCentralManagerStateUnauthorized:
state = @"The app is not authorized to use Bluetooth Low Energy.";
break;
case CBCentralManagerStatePoweredOff:
state = @"Bluetooth is currently powered off.";
break;
case CBCentralManagerStatePoweredOn:
state = @"work";
break;
case CBCentralManagerStateUnknown:
default:
;
}
}
客户的需求里面有下次进入该界面自动连接设备的功能,开始做的时候是实例化之后马上开始连接之前的设备,但是总是连接不上。实际上等这个回调函数调用之后就可以连接了,包括去搜索也是一样,如果大家操作相关功能之后没有效果,可能是这个函数还没调用的原因,怎么连接之前的设备一会会讲到。
5.扫描蓝牙设备(根据SERVICE_UUID来扫描外设,如果不设置则扫描所有蓝牙设备)
[central scanForPeripheralsWithServices:nil options:nil];
执行扫描动作之后,如果扫描到外设了,就会自动回调下面的协议方法
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary *)advertisementData RSSI:(NSNumber *)RSSI {
/*这边可以打印设备的名称,我们可以先将所有搜索到的设备都存储起来显示到UI上,根据选择来连接
搜索到的设备会重复,所以需要过滤
peripheral.name
*/
if(![self.deviceLists containsObject: peripheral]){
[self.deviceLists addObject: peripheral];
}
}
6.连接外围设备
选择设备进行连接
[central connectPeripheral:peripheral options:nil];
连接外围设备,中心管理者连接外设成功,如果连接成功就会回调这个协议方法
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral{
/连接成功之后,可以进行服务和特性的发现。 停止中心管理设备的扫描动作,要不然在你和已经连接好的外设进行数据沟通时,如果又有一个外设进行广播且符合你的连接条件,那么你的iOS设备也会去连接这个设备(因为iOS BLE4.0是支持一对多连接的),导致数据的混乱。
//停止扫描
[self.centralManager stopScan];
// 设置外设的代理
peripheral.delegate = self;
// 根据UUID来寻找服务
//[peripheral discoverServices:@[[CBUUID UUIDWithString:SERVICE_UUID]]];
//外设发现服务,传nil代表不过滤,一次性读出外设的所有服务
[peripheral discoverServices:nil];
}
/** 连接失败的回调 */
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(NSError *)error {
NSLog(@"%s, line = %d, %@=连接失败", __FUNCTION__, __LINE__, peripheral.name);
}
/** 断开连接 */
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error {
NSLog(@"%s, line = %d, %@=断开连接", __FUNCTION__, __LINE__, peripheral.name);
// 断开连接可以设置重新连接
[central connectPeripheral:peripheral options:nil];
}
7、获取外围设备服务和特征
peripheral调用discoverServices方法之后,在下面的回调函数中发现服务
/** 发现服务 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(NSError *)error {
// 遍历出外设中所有的服务
for (CBService *service in peripheral.services) {
//查找服务中所有的特征
[peripheral discoverCharacteristics:nil forService:service];
}
}
peripheral调用查询对应服务下面的特征之后,会在下面的回调函数中返回
/** 发现特征 */
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error{
if (error==nil) {
for (CBCharacteristic *characteristic in service.characteristics){
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"49535343-8841-43F4-A8D4-ECBE34729BB3"]]){
self. characteristic = characteristic;
}else if([characteristic.UUID isEqual:[CBUUID UUIDWithString:@"49535343-1E4D-4BD9-BA61-23C647249616"]]){
[peripheral setNotifyValue:TRUE forCharacteristic:characteristic];
}
self.peripheral = peripheral;
}
}
}
到现在打印所需要的所有内容都获取到了,就可以往打印机发送数据了
8、从外围设备读取数据
凡是从蓝牙传过来的数据都要经过这个回调,简单的说这个方法就是你拿数据的唯一方法,你可以判断是否 从外围设备读数据
/** 接收到数据回调 */
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSData *data = characteristic.value; //获取到的数据
NSLog(@"bbb-----%@----",data); //打印出来应该是<01a5>这样的数据,可以通过下面的方式去判断一些自己想要的逻辑
Byte * resultByte = (Byte *)[data bytes];
if(data.length>=1){
if(resultByte[0]==16){ //16代表前两位的值
NSLog(@"---打印机打开---");
}else if (resultByte[0]==1){ //01
NSLog(@"----打印机缺纸-----");
}
}
//下面的处理方式我没处理过,大家可以试试
//self.textFild.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
}
9、向蓝牙设备写数据
Byte byte[] = {0x1B,0x68};//打印机状态指令
NSData *data = [[NSData alloc] initWithBytes:byte length:3];
if(self.peripheral&&self.characteristic) {
NSLog(@"发送获取状态指令");
[peripheral writeValue:data forCharacteristic: characteristic type:CBCharacteristicWriteWithResponse];
}
这里说明怎么连接上一次或者之前连接的设备,可以保存之前设备的标识,进入App之后通过这个标识实例化服务来进行链接;通过这种方式不行的同学可以试试保存这个标识,下次进入的时候后台扫描设备,判断那个设备的标识和存储的一样,连接表示一样的那个...
//这个是需要保存的标识
NSString *connectId = peripheral.identifier.UUIDString;
//进入App之后获取存储的标识进行初始化服务
NSUUID *identifier = [[NSUUID UUID]initWithUUIDString: connectId];
CBPeripheral *peri = [[self.centralManager retrievePeripheralsWithIdentifiers:@[identifier]] firstObject];
if (peri == nil) {
return;
}
if(peri.state==CBPeripheralStateConnected){
[self alertMs:@"设备已被连接"];
return;
}
[centralManager connectPeripheral:peri options:@{CBConnectPeripheralOptionNotifyOnConnectionKey : @YES}];
关于蓝牙打印的知识暂时分享到这里,下面贴出参考的文章
蓝牙的连接和数据的读写