iOS之藍牙打印機

最近競品公司出了一個接入藍牙打印機的功能,作為競爭對手公司肯定不能少所以就給我分了任務(wù),搞定藍牙打印機

首先介紹一個公司的藍牙打印功能設(shè)想,因為公司已經(jīng)具備了wife打印機的打印功能而且非常完善,于是就在想是不是可以吧藍牙當(dāng)一個wife去使用這樣工作量能少N倍啊,想到這里那就很簡單了找資料開始搞

.首先你得先能和你玩具-藍牙打印機連接上并且確定藍牙打印機并且能給藍牙打印機透傳數(shù)據(jù),這里科普一下藍牙協(xié)議這個玩意藍牙協(xié)議分為藍牙2.0和藍牙4.0;

首先你得清楚一點支持藍牙2.0協(xié)議的設(shè)備和藍牙4.0的區(qū)別,那就是藍牙2.0的設(shè)備只能手動去建立藍牙連接 而4.0設(shè)備卻可以被簡單的程序掃描到并且建立連接,為什么2.0設(shè)備沒有被拋棄呢,這樣能確保你的藍牙設(shè)備非常安全的比如蘋果的很多藍牙設(shè)備都是2.0的

1.藍牙2.0

iOS開發(fā)中藍牙2.0協(xié)議使用ExternalAccessory這個基礎(chǔ)庫 由于2.0協(xié)議的特殊所以你使用遵循2.0協(xié)議的藍牙設(shè)備的時候需要得知該設(shè)備藍牙協(xié)議名稱

只有在這里加了這個藍牙協(xié)議名稱你才能找到你的設(shè)備

具體視線代碼如下,不建議直接復(fù)制 因為個人寫的有殘缺 ?但是主要的功能都已經(jīng)實現(xiàn)

.h中代碼如下

#import#import@interface ExternalAccessoryManager : NSObject

//會話渠道

@property (nonatomic,strong) EASession *session;

//代表打印機

@property (nonatomic,strong) EAAccessory *accessory;

//藍牙設(shè)備列

@property (nonatomic,strong) NSMutableArray *deviceArr;

/**

藍牙2.0協(xié)議管理單例

@return self

*/

+ (instancetype)shareExternalAccessoryManager;

//得到比索隆2.0藍牙外設(shè)

- (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];

//? ? ? ? ? ? // 硬件的協(xié)議字符串和硬件廠商提供的一致,這個就是我們要找的設(shè)備了!

//? ? ? ? ? ? // log:可以打印一下該硬件的相關(guān)資訊

//? ? ? ? ? ? for (NSString *proStr in accessory.protocolStrings) {

//? ? ? ? ? ? ? ? [info appendFormat:@"protocolString = %@\n", proStr];

//? ? ? ? ? ? ? ? NSLog(@"打印機協(xié)議%@",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];

//有打印設(shè)備并且有打印內(nèi)容

if (accessory && data.length > 0) {

self.printData = data;

self.accessory = accessory;

[self openSession];

}

}

}

}

//打開傳輸通道

- (BOOL)openSession {

// 根據(jù)已經(jīng)連接的EAAccessory對象和這個協(xié)議(反向域名字符串)來創(chuàng)建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設(shè)備未能創(chuàng)建會話");

}

return (nil != self.session);

}

//NSStreamDelegate

// delegate回調(diào)的方法

- (void)stream:(NSStream *)aStream handleEvent:(NSStreamEvent)eventCode {

switch (eventCode) {

//沒有事件

case NSStreamEventNone:

break;

//事件打開完成

case NSStreamEventOpenCompleted:

break;

//有數(shù)據(jù)傳過來

case NSStreamEventHasBytesAvailable:{

//NSLog(@"Input stream is ready");

// 接收到硬件數(shù)據(jù)了,根據(jù)指令定義對數(shù)據(jù)進行解析。

}

break;

//有可用控件(發(fā)送內(nèi)容)

case NSStreamEventHasSpaceAvailable:{

//NSLog(@"Output stream is ready");

// 可以發(fā)送數(shù)據(jù)給硬件了

[self writeToDevice];

}

break;

//事件發(fā)生錯誤

case NSStreamEventErrorOccurred:{

}

break;

//事件結(jié)束后

case NSStreamEventEndEncountered:{

//寫入數(shù)據(jù)成功后關(guān)閉通道

[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(@"內(nèi)容編碼%@",dateStr);

if (length > 0) {

[self.session.outputStream write:[self.printData bytes] maxLength:length];

}

}

2.藍牙4.0就更簡單了? 它使用的是CoreBluetooth我在網(wǎng)上找到了一個大神封裝的關(guān)于打印方面的一個第三方使用很是方便 大家可以去看看這里附上大神demo的readeME ?這里可以很方便快捷的找到大神的數(shù)據(jù)

# HLBluetoothDemo# 提醒有部分開發(fā)人員,加群之后,直接問設(shè)備掃描不到,服務(wù)掃描出錯,特性掃描報錯,怎么辦?對于這類問題,要嚴(yán)厲批評,都是因為懶,沒有用心去看官方文檔或者關(guān)于CoreBluetooth的教程。

所以對于藍牙的連接處理流程不清晰的,嚴(yán)重建議先看[iOS CoreBluetooth 的使用講解](http://www.lxweimin.com/p/1f479b6ab6df)

自己動手寫Demo,把系統(tǒng)的CoreBluetooth的使用,以及藍牙的流程搞懂了,再開始做藍牙打印的功能。# 引言該項目中包含兩個部分的工具類`HLBluetooth` 和`HLPrinter`,藍牙操作和打印小票功能。

> 如果只是做藍牙打印機打印小票的功能,可以看我的另一個工程[SEBLEPrinter](https://github.com/Halley-Wong/SEBLEPrinter)因為系統(tǒng)的藍牙操作庫是用delegate實現(xiàn)的,步驟比較繁多,操作很零散,需要寫一堆的代理方法,特別麻煩

所以我用block方式重寫了,藍牙管理的所有代碼在HLBluetooth目錄中。

又因為項目中要用藍牙控制打印機打印小票,我又把藍牙打印機的操作封裝了一下,所有代碼在HLPrinter目錄下。

# HLBluetooth介紹用block改寫后,使用大致分為三步:* 獲取藍牙模塊的狀態(tài)* 掃描藍牙外設(shè)* 連接、掃描服務(wù)、掃描特性、掃描描述。因為連接、掃描服務(wù)、掃描特性、掃描描述也是屬于不同的階段,所以在block返回時,也有階段值返回。

~~---------------------------------------------------------------------------------------------------------~~

除了上面這些代理方法改寫的block API之外,還有一些操作性方法,比如:* 讀取特性值* 讀取描述值* 往特性中寫入數(shù)據(jù)* 往描述中寫入數(shù)據(jù)* 讀取信號數(shù)據(jù)* 取消藍牙連接...以上這些方法也提供block方式和一般的調(diào)用方式。

# HLPrinter介紹藍牙打印機模板可以打印的格式有* 單行文字格式```[printer appendText:title alignment:HLTextAlignmentCenter fontSize:HLFontSizeTitleBig];[printer appendText:str1 alignment:HLTextAlignmentCenter]; ```* 左標(biāo)題右參數(shù)格式```[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"];```* 三列數(shù)據(jù)格式```[printer appendLeftText:@"商品" middleText:@"數(shù)量" 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)# 使用方式關(guān)于詳細的BLE使用方式和打印小票的功能,在[這里有篇文章詳細說明](http://www.lxweimin.com/p/90cc08d11b5a)打印機的指令有ASCII、10進制和16進制三種,我使用的是16進制。```? ? Byte QRSize [] = {0x1D,0x28,0x6B,0x03,0x00,0x31,0x43,size}; // 這是16進制,其中最后一個size是10進制數(shù),轉(zhuǎn)換為NSData后,會被轉(zhuǎn)換為16進制。? ? Byte QRSize [] = {29,40,107,3,0,49,67,size}; // 這是10進制。```# 補充一些參數(shù):>據(jù)佳博的一技術(shù)人員提供的一些參數(shù):

漢字是24 x 24點陣,字符是12 x 24。

58mm 型打印機橫向?qū)挾?84個點。(可是我用文字設(shè)置相對位置測試確實368,囧)

80mm 型打印機橫向?qū)挾?76個點。

1mm 大概是8個點。

# 更新修復(fù)部分型號打印亂碼,亂碼后再次打印沒反應(yīng)的Bug。(2016-06-13,佳博 Gp-58MBIII和GP58MBIII和芯燁 XPrinter某型號測試通過) demo中也有一個使用的例子

如有使用錯誤或者更好的建議,請issues我。關(guān)于藍牙打印機的問題,也歡迎大家加入群:552735579(iOS藍牙打印機開發(fā))。

具體4.0代碼請大家自己去根據(jù)https://github.com/Halley-Wong/SEBLEPrinter 簡單清晰明了 可以直接拿來使用的代碼

3.以上兩步做完之后就建立了你的app和藍牙設(shè)備之間的連接,但是因為我是要打印所以那么還需要和服務(wù)器建立長連接實時接收服務(wù)器給的數(shù)據(jù)同時將數(shù)據(jù)透傳給藍牙打印機。關(guān)于打印機指令以及對應(yīng)的什么模板 打印原理之類這些廠商都會給你提供文檔 ?之前沒有想到這樣解決打印之前我就是通過了解這些指令用一行行的代碼對其進行模板化 其中的辛酸苦辣簡直不是一句話能解釋通的 ?當(dāng)然這只是一部分打印機 ?大多數(shù)打印機廠商提供的SDK還是很好用的 ?所以考驗?zāi)銈兝习鍖δ闶遣皇钦嫘牡臅r候到了?

這里我是使用MQTT協(xié)議對服務(wù)器進行連接的,這個連接將有助與你詳細了解MQTT

https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html

關(guān)于MQTT在iOS中的具體使用見我的另一片文章在這里就不多說了 ?

所以總結(jié)一下我的app就是找到打印機并建立與打印機之間的聯(lián)系 ?同時與服務(wù)器建立長連接 ?那么這時候你就可以理解我的app和藍牙設(shè)備加起來這才是一個完整的打印機 ?然后這個打印機與服務(wù)器連接起來了 就可以接受服務(wù)器發(fā)送的打印指令了

好了文筆不好就寫到這吧

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容