Saturday, February 13, 2016

Printing on a white label BLE thermal printer from iOS - a quick story

Over at OneGreenDiary.com, we are building tools to help small merchants better connect with their customers. Check OneGreenDiary.com to see what we do, and do not forget to watch that video, we are sure you would love it :-)

While one of the core principles at OneGreenDiary is to be as paper less as possible, there are still places where a printed paper bill may be required. We avoided print support for our merchant apps, till one of our early adopters specifically asked for it. Since we are kind of cash strapped, and can not afford to spend on a damn printer that is iOS certified, we went over to alibaba.com and ordered this cheap Bluetooth enabled thermal printer that claimed to work with iOS : http://tousei.en.alibaba.com/product/60371489659-802143527/TS_M230_mini_bluetooth_portable_handheld_receipt_bill_printer_for_android_ios_mobile_58mm.html


Great. After the printer arrived, we though that it would magically connect to out iOS device and we did start printing instantly. Bummer. Ok, how about Android ? No luck. Apparently neither iOS nor Android provide native support for BLE printers. I tried to install the outdated Windows driver, and Linux cups drivers with no luck. Not even able to print on desktop. 

But then, Abhay C, a wanderer with us, found an iOS app called printer-x, that was a demo app to print on bluetooth enabled thermal printers. He printed this:  "hello, world!"

At this point we knew, it could be done. But how? All communications with the vendor didn't go anywhere. They send some sample iOS (Objective C) code, and some Android code, both of which didn't compile, and neither we could understand what was going on in the code. There were a few comments in Chinese that tried to be helpful. Chinese, not Swift. The programmers manual that came with printer reminded me of good old DOS assembly days. There was something, but didn't know where to start.

Next we tried to venture into programming our own BLE layer for printer communication. Instead of using the CoreBluetooth library we zeroed in on using BluetoothKit, which is Swift wrapper over CoreBluetooth library (https://github.com/rasmusth/BluetoothKit). So basically it is this: BLE frameworks try to give you two interfaces: Peripherals and Central. Peripheral refers to a device which can be discovered, asked for data, or can receive data. Central on the other hand scans for Peripherals, discovers characteristics offered by Peripheral, receive / send data to the Peripheral. One of the important thing to enable discovery is the UUID of the Bluetooth device, this can be easily obtained by using a discovery code, or a number of BLE query apps available on the App store/ Play store. I used the LightBlue app from the App store for this purpose. Once this device was discovered, we needed to connect to the device. The BluetoothKit provides a elegant interface to connect. But the issue started after this: apparently BluetoothKit's connect call back returns an instance of BKRemotePeripheral class, and this class exposes API to only receive data, not send! Apparently the class was more designed for bluetooth sensors, that only transmit data, not receive anything. Digging into the BKRemotePeripheral class, I found out a way to expose the embedded CBPeripheral object along with capturing CBCharasteristic objects. These are the objects of CoreBluetooth iOS library that allow lower level controls.

Next is when the DOS assembly like programmer's manual, which came with the printer, came up for help. After this I could easily figure out the code to issue a line feed to the printer device:

let lineFeed = self.hexToNSData("0A")
let printer = self.connectedDevice!.getPeripheral()!
printer.writeValue(lineFeed, forCharacteristic: self.connectedDevice!.getCharacteristic()!, type: CBCharacteristicWriteType.WithResponse)
At this point, I knew, things were pretty much in control. The above code basically writes the hex code "0A" to the discovered characteristic. This initiates a line feed operation on the printer. Once this is done, it was a wow moment to print this:


and then this:

Since all of this code (written in Swift) was majorly based on BluetoothKit, we felt it was best to open up this part of the code, which is now hosted on GitHub: https://github.com/tovganesh/bleprinter-ios

Hope this is of some help, especially if you are trying to programatically access these kind of printers with iOS.

Note: Before you ask, I am still connected with VLife for all official (and practical) purposes. Once an officer at immigration and citizenship department in Oz told me, it is always good to be in love with two countries, you are contributing to both with your heart. Fits well here. 

22 comments:

ag said...

Thanks for sharing the info.,
but have you printed the images/logos using same

V. Ganesh said...

Not yet. Just a huge list of other things to handle at the moment :)

Unknown said...

Hi Sir, I came across your blog about BluetoothKit and it seems interesting. But I have a question, is possible to print qr code or bar code in a bluetooth printing device, instead of just printing text?

Unknown said...

Hi Sir, I came across your blog about BluetoothKit and it seems interesting. But I have a question, is possible to print qr code or bar code in a bluetooth printing device, instead of just printing text?

V. Ganesh said...

Dear Joriel,

Haven't tried myself, but yes, it is possible. Search for "POS-80 Series Programmer's Manual" and refer to section of Bar code printing. Specifically the commands are GS k m d1...dk NUL and GS k m n d1..dn (in ASCII code).

Let me know if it works out for you.

Regards
Ganesh

Unknown said...
This comment has been removed by the author.
V. Ganesh said...

hi siva

you need to first install the BluetoothKit dependency, see here: https://github.com/rhummelmose/BluetoothKit

regards
Ganesh

Unknown said...

hi ganesh

i am able to print text but coming to image

i am tring in this manner
let image1 = #imageLiteral(resourceName: "images")

let data3 = UIImagePNGRepresentation(image1) as NSData?
var commands:[UInt8] = [0x1D,0x76,0x30,0x0,0x32,0x0,0x96,0x0]
let commandsnsdata = NSData(bytes: &commands, length: commands.count)
let mutableData = NSMutableData()
mutableData.append((commandsnsdata ) as Data)
mutableData.append(data3 as! Data)
peripheral.writeValue(mutableData as Data, for: printCharacterstics, type: CBCharacteristicWriteType.withResponse)
but it is printing some unknow text with different language

Unknown said...

Hello,
Can you update the code for swift 3? Thanks a bunch

Anonymous said...

Nice to read the informative story on how to print the text. It seem to be interesting! Thanks for the blog post! keep on sharing!

V. Ganesh said...

Tim, Will let you know when I update it. Alternatively this is open source, if you would like to contribute it would be be great as a community effort.

Unknown said...

hi Ganesh, it it possible to print PDF file using this printer? -Thanks

Unknown said...

Hi Ganesh, is it possible to print PDF file using this printer? -Thanks

V. Ganesh said...

Haven't tried. But this doesn't support the postscript language as such - so you might have to write some kind of a converter to actually print.

Unknown said...

Nice to read the informative story on how to print the text thank you

Unknown said...

Hi, can u update code for swift4 please

Unknown said...

Hi, can u update code for swift4 please

Anonymous said...

Hi, can u update in swift 4

Anonymous said...

can u update to swift4

appreciate said...

hi, thank you so much for contributing the example. this is the most complete example that I can find online. but can you please update the code to swift 5?

appreciate said...

my discovery list is always empty

V. Ganesh said...

Dear All, for those asking for an updated version of this code, kindly note that I no longer actively work in iOS, the code is available on Github and people are free to fork and update it for later versions of Swift. A tip: syntax would be different but the mechanism is the same. Thanks - Ganesh!