最近競品公司出了一個接入藍牙打印機的功能,作為競爭對手公司肯定不能少所以就給我分了任務宾濒,搞定藍牙打印機
首先介紹一個公司的藍牙打印功能設想,因為公司已經(jīng)具備了wife打印機的打印功能而且非常完善皆看,于是就在想是不是可以吧藍牙當一個wife去使用這樣工作量能少N倍啊龙誊,想到這里那就很簡單了找資料開始搞
一.首先你得先能和你玩具-藍牙打印機連接上并且確定藍牙打印機并且能給藍牙打印機透傳數(shù)據(jù)韭寸,這里科普一下藍牙協(xié)議這個玩意藍牙協(xié)議分為藍牙2.0和藍牙4.0春哨;
首先你得清楚一點支持藍牙2.0協(xié)議的設備和藍牙4.0的區(qū)別,那就是藍牙2.0的設備只能手動去建立藍牙連接 而4.0設備卻可以被簡單的程序掃描到并且建立連接恩伺,為什么2.0設備沒有被拋棄呢赴背,這樣能確保你的藍牙設備非常安全的比如蘋果的很多藍牙設備都是2.0的
1.藍牙2.0
iOS開發(fā)中藍牙2.0協(xié)議使用ExternalAccessory這個基礎庫 由于2.0協(xié)議的特殊所以你使用遵循2.0協(xié)議的藍牙設備的時候需要得知該設備藍牙協(xié)議名稱
只有在這里加了這個藍牙協(xié)議名稱你才能找到你的設備
具體視線代碼如下,不建議直接復制 因為個人寫的有殘缺 ?但是主要的功能都已經(jīng)實現(xiàn)
.h中代碼如下
#import#import@interface ExternalAccessoryManager : NSObject
//會話渠道
@property (nonatomic,strong) EASession *session;
//代表打印機
@property (nonatomic,strong) EAAccessory *accessory;
//藍牙設備列
@property (nonatomic,strong) NSMutableArray *deviceArr;
/**
藍牙2.0協(xié)議管理單例
@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];
//? ? ? ? ? ? // 硬件的協(xié)議字符串和硬件廠商提供的一致晶渠,這個就是我們要找的設備了凰荚!
//? ? ? ? ? ? // log:可以打印一下該硬件的相關資訊
//? ? ? ? ? ? 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];
//有打印設備并且有打印內(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設備未能創(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;
//事件結束后
case NSStreamEventEndEncountered:{
//寫入數(shù)據(jù)成功后關閉通道
[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)上找到了一個大神封裝的關于打印方面的一個第三方使用很是方便 大家可以去看看這里附上大神demo的readeME ?這里可以很方便快捷的找到大神的數(shù)據(jù)
# HLBluetoothDemo# 提醒有部分開發(fā)人員番川,加群之后到涂,直接問設備掃描不到,服務掃描出錯颁督,特性掃描報錯践啄,怎么辦?對于這類問題适篙,要嚴厲批評往核,都是因為懶,沒有用心去看官方文檔或者關于CoreBluetooth的教程嚷节。
所以對于藍牙的連接處理流程不清晰的聂儒,嚴重建議先看[iOS CoreBluetooth 的使用講解](http://www.reibang.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)* 掃描藍牙外設* 連接、掃描服務闽撤、掃描特性得哆、掃描描述。因為連接哟旗、掃描服務贩据、掃描特性、掃描描述也是屬于不同的階段热幔,所以在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]; ```* 左標題右參數(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)# 使用方式關于詳細的BLE使用方式和打印小票的功能场勤,在[這里有篇文章詳細說明](http://www.reibang.com/p/90cc08d11b5a)打印機的指令有ASCII戈锻、10進制和16進制三種,我使用的是16進制和媳。```? ? Byte QRSize [] = {0x1D,0x28,0x6B,0x03,0x00,0x31,0x43,size}; // 這是16進制格遭,其中最后一個size是10進制數(shù),轉換為NSData后留瞳,會被轉換為16進制拒迅。? ? Byte QRSize [] = {29,40,107,3,0,49,67,size}; // 這是10進制。```# 補充一些參數(shù):>據(jù)佳博的一技術人員提供的一些參數(shù):
漢字是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藍牙打印機開發(fā))素邪。
具體4.0代碼請大家自己去根據(jù)https://github.com/Halley-Wong/SEBLEPrinter 簡單清晰明了 可以直接拿來使用的代碼
3.以上兩步做完之后就建立了你的app和藍牙設備之間的連接葱色,但是因為我是要打印所以那么還需要和服務器建立長連接實時接收服務器給的數(shù)據(jù)同時將數(shù)據(jù)透傳給藍牙打印機。關于打印機指令以及對應的什么模板 打印原理之類這些廠商都會給你提供文檔 ?之前沒有想到這樣解決打印之前我就是通過了解這些指令用一行行的代碼對其進行模板化 其中的辛酸苦辣簡直不是一句話能解釋通的 ?當然這只是一部分打印機 ?大多數(shù)打印機廠商提供的SDK還是很好用的 ?所以考驗你們老板對你是不是真心的時候到了?
這里我是使用MQTT協(xié)議對服務器進行連接的娘香,這個連接將有助與你詳細了解MQTT
https://mcxiaoke.gitbooks.io/mqtt-cn/content/mqtt/01-Introduction.html
關于MQTT在iOS中的具體使用見我的另一片文章在這里就不多說了 ?
所以總結一下我的app就是找到打印機并建立與打印機之間的聯(lián)系 ?同時與服務器建立長連接 ?那么這時候你就可以理解我的app和藍牙設備加起來這才是一個完整的打印機 ?然后這個打印機與服務器連接起來了 就可以接受服務器發(fā)送的打印指令了
好了文筆不好就寫到這吧