最近竞品公司出了一个接入蓝牙打印机的功能,作为竞争对手公司肯定不能少所以就给我分了任务,搞定蓝牙打印机
首先介绍一个公司的蓝牙打印功能设想,因为公司已经具备了wife打印机的打印功能而且非常完善,于是就在想是不是可以吧蓝牙当一个wife去使用这样工作量能少N倍啊,想到这里那就很简单了找资料开始搞
一.首先你得先能和你玩具-蓝牙打印机连接上并且确定蓝牙打印机并且能给蓝牙打印机透传数据,这里科普一下蓝牙协议这个玩意蓝牙协议分为蓝牙2.0和蓝牙4.0;
首先你得清楚一点支持蓝牙2.0协议的设备和蓝牙4.0的区别,那就是蓝牙2.0的设备只能手动去建立蓝牙连接 而4.0设备却可以被简单的程序扫描到并且建立连接,为什么2.0设备没有被抛弃呢,这样能确保你的蓝牙设备非常安全的比如苹果的很多蓝牙设备都是2.0的
1.蓝牙2.0
iOS开发中蓝牙2.0协议使用ExternalAccessory这个基础库 由于2.0协议的特殊所以你使用遵循2.0协议的蓝牙设备的时候需要得知该设备蓝牙协议名称
只有在这里加了这个蓝牙协议名称你才能找到你的设备
具体视线代码如下,不建议直接复制 因为个人写的有残缺 但是主要的功能都已经实现
.h中代码如下
#import#import@interface ExternalAccessoryManager : NSObject
//会话渠道
@property (nonatomic,strong) EASession *session;
//代表打印机
@property (nonatomic,strong) EAAccessory *accessory;
//蓝牙设备列
@property (nonatomic,strong) NSMutableArray *deviceArr;
/**
蓝牙2.0协议管理单例
@return self
*/
+ (instancetype)shareExternalAccessoryManager;
//得到比索隆2.0蓝牙外设
- (NSArray *)getConnectBuleToolthDevice;
//打印
- (void)printMsgData:(NSData *)data;
@end
.m文件代码如下
#import "ExternalAccessoryManager.h"static ExternalAccessoryManager * g_ExternalAccessoryManager = nil;@interface ExternalAccessoryManager (){
}
@property (nonatomic,strong) NSData *printData;
@end
@implementation ExternalAccessoryManager
+ (instancetype)shareExternalAccessoryManager{
static dispatch_once_t oneceToken;
dispatch_once(&oneceToken, ^{
g_ExternalAccessoryManager = [[self alloc] init];
});
return g_ExternalAccessoryManager;
}
- (id)init{
self = [super init];
if (self) {
self.deviceArr = [NSMutableArray array];
}
return self;
}
- (NSArray *)getConnectBuleToolthDevice{
EAAccessoryManager *accessoryManager = [EAAccessoryManager sharedAccessoryManager];
NSArray *connectedAccessories = [accessoryManager connectedAccessories];
for (EAAccessory *accessory in connectedAccessories) {
//找到比索隆打印机
if ([@"com.bixolon.protocol" isEqualToString:[accessory.protocolStrings firstObject]] == YES) {
[self.deviceArr addObject:accessory];
}
}
return connectedAccessories;
}
//打印
- (void)printMsgData:(NSData *)data{
// search our device
for (EAAccessory *accessory in self.deviceArr) {
if ([@"com.bixolon.protocol" isEqualToString:[accessory.protocolStrings firstObject]] == YES) {
// NSMutableString *info = [[NSMutableString alloc] init];
// // 硬件的协议字符串和硬件厂商提供的一致,这个就是我们要找的设备了!
// // log:可以打印一下该硬件的相关资讯
// for (NSString *proStr in accessory.protocolStrings) {
// [info appendFormat:@"protocolString = %@\n", proStr];
// NSLog(@"打印机协议%@",proStr);
// }
// [info appendFormat:@"\n"];
// [info appendFormat:@"manufacturer = %@\n", accessory.manufacturer];
// [info appendFormat:@"name = %@\n", accessory.name];
// [info appendFormat:@"modelNumber = %@\n", accessory.modelNumber];
// [info appendFormat:@"serialNumber = %@\n", accessory.serialNumber];
// [info appendFormat:@"firmwareRevision = %@\n", accessory.firmwareRevision];
// [info appendFormat:@"hardwareRevision = %@\n", accessory.hardwareRevision];
//有打印设备并且有打印内容
if (accessory && data.length > 0) {
self.printData = data;
self.accessory = accessory;
[self openSession];
}
}
}
}
//打开传输通道
- (BOOL)openSession {
// 根据已经连接的EAAccessory对象和这个协议(反向域名字符串)来创建EASession对象,并打开输入、输出通道
self.session = [[EASession alloc] initWithAccessory:self.accessory forProtocol:@"com.bixolon.protocol"];
if(self.session != nil) {
// open input stream 接收
self.session.inputStream.delegate = self;
[self.session.inputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.session.inputStream open];
// open output stream 传输
self.session.outputStream.delegate = self;
[self.session.outputStream scheduleInRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
[self.session.outputStream open];
}
else {
NSLog(@"2.0设备未能创建会话");
}
return (nil != self.session);
}
//NSStreamDelegate
// delegate回调的方法
- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {
switch (eventCode) {
//没有事件
case NSStreamEventNone:
break;
//事件打开完成
case NSStreamEventOpenCompleted:
break;
//有数据传过来
case NSStreamEventHasBytesAvailable:{
//NSLog(@"Input stream is ready");
// 接收到硬件数据了,根据指令定义对数据进行解析。
}
break;
//有可用控件(发送内容)
case NSStreamEventHasSpaceAvailable:{
//NSLog(@"Output stream is ready");
// 可以发送数据给硬件了
[self writeToDevice];
}
break;
//事件发生错误
case NSStreamEventErrorOccurred:{
}
break;
//事件结束后
case NSStreamEventEndEncountered:{
//写入数据成功后关闭通道
[self.session.outputStream close];
}
break;
default:
break;
}
}
- (void)writeToDevice{
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"print_guide_airkiss"ofType:@"txt"];
self.printData = [NSData dataWithContentsOfFile:filePath];
NSInteger length = [self.printData length];
NSString *dateStr = [[NSString alloc] initWithData:self.printData encoding:NSUTF8StringEncoding];
NSLog(@"内容编码%@",dateStr);
if (length > 0) {
[self.session.outputStream write:[self.printData bytes] maxLength:length];
}
}
2.蓝牙4.0就更简单了 它使用的是CoreBluetooth我在网上找到了一个大神封装的关于打印方面的一个第三方使用很是方便 大家可以去看看这里附上大神demo的readeME 这里可以很方便快捷的找到大神的数据
# HLBluetoothDemo# 提醒有部分开发人员,加群之后,直接问设备扫描不到,服务扫描出错,特性扫描报错,怎么办?对于这类问题,要严厉批评,都是因为懒,没有用心去看官方文档或者关于CoreBluetooth的教程。
所以对于蓝牙的连接处理流程不清晰的,严重建议先看[iOS CoreBluetooth 的使用讲解](http://www.jianshu.com/p/1f479b6ab6df)
自己动手写Demo,把系统的CoreBluetooth的使用,以及蓝牙的流程搞懂了,再开始做蓝牙打印的功能。# 引言该项目中包含两个部分的工具类`HLBluetooth` 和`HLPrinter`,蓝牙操作和打印小票功能。
> 如果只是做蓝牙打印机打印小票的功能,可以看我的另一个工程[SEBLEPrinter](https://github.com/Halley-Wong/SEBLEPrinter)因为系统的蓝牙操作库是用delegate实现的,步骤比较繁多,操作很零散,需要写一堆的代理方法,特别麻烦
所以我用block方式重写了,蓝牙管理的所有代码在HLBluetooth目录中。
又因为项目中要用蓝牙控制打印机打印小票,我又把蓝牙打印机的操作封装了一下,所有代码在HLPrinter目录下。
# HLBluetooth介绍用block改写后,使用大致分为三步:* 获取蓝牙模块的状态* 扫描蓝牙外设* 连接、扫描服务、扫描特性、扫描描述。因为连接、扫描服务、扫描特性、扫描描述也是属于不同的阶段,所以在block返回时,也有阶段值返回。
~~---------------------------------------------------------------------------------------------------------~~
除了上面这些代理方法改写的block API之外,还有一些操作性方法,比如:* 读取特性值* 读取描述值* 往特性中写入数据* 往描述中写入数据* 读取信号数据* 取消蓝牙连接...以上这些方法也提供block方式和一般的调用方式。
# HLPrinter介绍蓝牙打印机模板可以打印的格式有* 单行文字格式```[printer appendText:title alignment:HLTextAlignmentCenter fontSize:HLFontSizeTitleBig];[printer appendText:str1 alignment:HLTextAlignmentCenter]; ```* 左标题右参数格式```[printer appendTitle:@"时间:" value:@"2016-04-27 10:01:50" valueOffset:150];[printer appendTitle:@"订单:" value:@"4000020160427100150" valueOffset:150];[printer appendTitle:@"总计:" value:totalStr];[printer appendTitle:@"实收:" value:@"100.00"];```* 三列数据格式```[printer appendLeftText:@"商品" middleText:@"数量" rightText:@"单价" isTitle:YES];[printer appendLeftText:dict[@"name"] middleText:dict[@"amount"] rightText:dict[@"price"] isTitle:NO];```* 分隔线```[printer appendSeperatorLine];```* 图片```[printer appendImage:[UIImage imageNamed:@"ico180"] alignment:HLTextAlignmentCenter maxWidth:300];```* 二维码```[printer appendQRCodeWithInfo:@"www.baidu.com" size:10];[printer appendQRCodeWithInfo:@"www.baidu.com"];```* 条形码```[printer appendBarCodeWithInfo:@"123456789012"];```# 效果图![1.png](https://github.com/Halley-Wong/HLBluetoothDemo/blob/master/HLBluetoothDemo/images/1.png) ![2.png](https://github.com/Halley-Wong/HLBluetoothDemo/blob/master/HLBluetoothDemo/images/2.png)![03.png](https://github.com/Halley-Wong/HLBluetoothDemo/blob/master/HLBluetoothDemo/images/03.png)![printer.png](https://github.com/Halley-Wong/HLBluetoothDemo/blob/master/HLBluetoothDemo/images/printer.png)# 使用方式关于详细的BLE使用方式和打印小票的功能,在[这里有篇文章详细说明](http://www.jianshu.com/p/90cc08d11b5a)打印机的指令有ASCII、10进制和16进制三种,我使用的是16进制。``` Byte QRSize [] = {0x1D,0x28,0x6B,0x03,0x00,0x31,0x43,size}; // 这是16进制,其中最后一个size是10进制数,转换为NSData后,会被转换为16进制。 Byte QRSize [] = {29,40,107,3,0,49,67,size}; // 这是10进制。```# 补充一些参数:>据佳博的一技术人员提供的一些参数:
汉字是24 x 24点阵,字符是12 x 24。
58mm 型打印机横向宽度384个点。(可是我用文字设置相对位置测试确实368,囧)
80mm 型打印机横向宽度576个点。
1mm 大概是8个点。
# 更新修复部分型号打印乱码,乱码后再次打印没反应的Bug。(2016-06-13,佳博 Gp-58MBIII和GP58MBIII和芯烨 XPrinter某型号测试通过) demo中也有一个使用的例子
如有使用错误或者更好的建议,请issues我。关于蓝牙打印机的问题,也欢迎大家加入群:552735579(iOS蓝牙打印机开发)。
具体4.0代码请大家自己去根据https://github.com/Halley-Wong/SEBLEPrinter 简单清晰明了 可以直接拿来使用的代码
3.以上两步做完之后就建立了你的app和蓝牙设备之间的连接,但是因为我是要打印所以那么还需要和服务器建立长连接实时接收服务器给的数据同时将数据透传给蓝牙打印机。关于打印机指令以及对应的什么模板 打印原理之类这些厂商都会给你提供文档 之前没有想到这样解决打印之前我就是通过了解这些指令用一行行的代码对其进行模板化 其中的辛酸苦辣简直不是一句话能解释通的 当然这只是一部分打印机 大多数打印机厂商提供的SDK还是很好用的 所以考验你们老板对你是不是真心的时候到了
这里我是使用MQTT协议对服务器进行连接的,这个连接将有助与你详细了解MQTT
https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html
关于MQTT在iOS中的具体使用见我的另一片文章在这里就不多说了
所以总结一下我的app就是找到打印机并建立与打印机之间的联系 同时与服务器建立长连接 那么这时候你就可以理解我的app和蓝牙设备加起来这才是一个完整的打印机 然后这个打印机与服务器连接起来了 就可以接受服务器发送的打印指令了
好了文笔不好就写到这吧
网友评论