:: Research Group Embedded Interaction :: Media Informatics :: Ludwig-Maximilians-University Munich


Java Driver for Brother MW-140BT mobile bluetooth printer

The goal of this project is to use the Brother MW-140BT printer (which is connected via bluetooth) from within a java environment (especially J2ME). Therefore it is crucial to know how the communication between the host (driver) and the printer is done. Brother provides a manual which covers most of the information that is needed. The remaining details were extracted from monitoring the shipped windows driver which communicates with the printer using a virtual com port.

As testing on a mobile device can be very frustrating because of the limited debug posibilities and because it may be also useful to have a java based printer driver on the standard platform this driver software was developed in a modular way which allows for changing the parts being responsible for platform specific tasks easily. Due to that design it is very simple to develop driver adoptions for other platform as well as different implementations for the already supported platforms without rewriting the whole code.

The printer only supports the printing of images - so it is not possible to print text without converting it to an image before. As there are different implementations for images on J2SE and J2ME this part of the driver is considered platform specific as well as the part that deals with the bluetooth communication process.

Note: Due to memory restrictions on mobile devices and the fact that the only available image class on J2ME only supports true color with alpha channel (meaning four bytes per pixel) it is not possible to process images that cover the whole printable area which is about 816 x 1190 pixels. In upcoming versions of this driver one may think about implementing an image class which is capable of dealing with black and white images.

Used Software

Used Hardware

Package structure

Core

The core package contains the code that is platform independent. Further more the interface definitions for platform dependent classes are stored here as well.

org.hcilab.btprinter

Here you find the class Driver that is responsible for the communication protocol and the invokation of all other supporting classes. It is initialized with a String that identifies the package which holds the platform specific implementations. Use the method print to send an image to the printer. The class Status provides easy acces to the status bytes that are sent by the printer. As the observer pattern is not supported on J2ME by default Observer handels the communication with the user interface.

org.hcilab.btprinter.abstractionlayer

The interfaces IOHandler and LogHandler define methods for the communication with the printer (access to input and output streams) respectivly methods for logging purposes. In addition to that the abstract class ImageHandler provides on the one hand some code for processing and compressing an image that is the same on every platform and on the other hand it defines methods to access the image data line by line which has to be implemented for every supported platform. IOHandler and ImageHandler have to be implemented for a platform specific adoption (just hang on an "Impl" to the names, for example IOHandlerImpl).

Please see the javadocs for further details.

J2SE

org.hcilab.btprinter.platform.j2se

This is a package that supports communication via a virtual com port (and therefore needs the Java Communication API) which points to the printer and uses the BufferedImage to send images to the printer. As the focus of this project lies on J2ME there follows no further description of this package. See org.hcilab.btprinter.platform.j2se.PrintingTest for an example on how to use the driver.

Please see the javadocs for further details.

J2ME

org.hcilab.btprinter.platform.j2me

Alternatively to the J2SE package you can use the J2ME package. It supports the printing from within a J2ME environment and uses the bluetooth serial port profile (btspp) for communication and the javax.microedition.lcdui.Image class for image handling.

As mentioned before you have to deal with some memory restrictions on mobile devices. Therefore it is right now not possible on the tested devices to print images which will cover the full printable area of 816 x 1190 pixels. Please note also that the image is not (yet) being dithered. This means that it is decided on a per pixel base if the output is either black or white using a threshold value. This works very good if you print for example text but fails to handle complex pictures probably.

In org.hcilab.btprinter.platform.j2me.PrintingTest you will find a sample usage of the printer driver which creates a rather small image, draws some text (device identifier and current time) into it and prints it. It also provides some feedback about the driver status and printing progress using the Observer interface (see above).

Please see the javadocs for further details.

Code elements

Image processing and compressing

Coming from a color image the image data has to be converted to black and white and after that it needs to be compressed. Everything is done per line. The first step is to convert the colors to gray (this is an extra step because it is platform dependent, here for J2ME):

			int[] temp = new int[width];
			raster.getRGB(temp, 0, width, 0, y, width, 1);

			byte[] pixels = new byte[width];
			for (int x = 0; x < temp.length; x++) {
				pixels[x] = (byte) ((((temp[x] & 0x00FF0000) >> 16)
					+ ((temp[x] & 0x0000FF00) >> 8) + (temp[x] & 0x000000FF)) / 3);
			}
		
The conversion to black and white takes place right after that (the following steps are platform independent). Eight pixels are stored in one byte. The direction is reversed as this is requiered by the printer:
			byte[] processedImg = new byte[width / b];

			int first = 0;
			int last = width - 1;

			for (int x = last; x >= first; x -= b) {
				// Result block (byte)
				int s = 0;
				// Bit mask (exmple: 1000 0000)
				int a = 1 << (b - 1);

				for (int i = x; i > x - b; i--) {
					// Set bit if threshold is reached, shift byte value to a range
					// from 0 to 255 for easier comparison
					if ((pixels[i] & 0xFF) < threshold)
						s |= a;
					// Update bitmask (example: 1000 0000 -> 0100 0000)
					a = a >> 1;
				}
				// Output byte
				processedImg[(last - x) / b] = (byte) s;
			}
		
The compression is the last step before the data is being sent to the printer (please see the source code in ImageHandler.java for further details):
			int line = 0;
			while (this.imageHandler.hasNextLine()) {
				byte[] compressedImgLine = imageHandler.getNextLine();
				out.write(CMD_RASTER_GRAPH);
				out.write((byte) compressedImgLine.length);
				out.write(0x00);
				out.write(compressedImgLine);

				// [...]
			}
		

Conclusion:

The driver works so far very good on the tested devices but there are for sure a lot of things that may be implemented in future releases:

  • Cancel print option
  • Check for errors while printing
  • Align images on page
  • Support printing of smaller pictures at an arbitrary horizontal position on the page
  • Proper state support
  • Support of mulitple pages
  • Possibly support direct input of byte arrays instead of image objects to save memory

Download

Source code archives

Libraries

Use these jar libraries to print from your application. You will need the core package and at least one driver package. See the examples coming with the driver packages (PrintingTest) to understand how to use it.

Sample midlet

This sample midlet just loads the PrintingTest of org.hcilab.btprinter.platform.j2me. It can be directly distributed to a java and bluetooth enabled mobile device.

References

  • Brother SDK manual for MW100

If you have any comments or questions, please feel free to ask us!