Feb 11, 2011

Programming the GTT Touchscreen

I was recently tasked with creating a GUI for a laboratory instrument using the Matrix Orbital GTT480272A touch panel display. There are a couple of minor gotchas to programming this device and I thought I would document some essential getting started tips here for anyone using the device.[Edit - I finished the original product. You can see a  30 second video of the GTT being controlled from a NetBurner embedded processor on YouTube.]


The first step of course is to download and read the manuals. The GTT Protocol contains the basic programming information. There is also a Hardware manual, and three useful application notes, one on updating the firmware, updating the splash screen and using the file interface. Finally if you want to program in C# (it’s a fast and easy way to test out the device) there is an “On Code” pdf. Matrix Oribital has a very well organized web site and fining what you want is pretty easy using the navigation panel on the left hand side.  They also have a support forum although at the time of this writing the usage appears to be very low.
Another tool, as important (and maybe even more so) is the GTT Support Tool available for free from the software download area. There were multiple errors in the 1.0 version [See Footnote 1]  of the Protocol manual but I was able to identify the errors and confirm the correct data by using the GTT Support Tool. Your first step when using any new feature if the GTT should be to fully explore that feature with this tool. There is a debug window for the tool. Open that as soon as you boot the tool. In my opinion the debug area should have opened automatically or even better been integrated into the main window. You won’t see any error messages if you don’t use the debug Window.

Bug in the GTT Tool

imageThe GTT Support Tool in general works very well but it has one glaring bug. When you save a script with any rounded rectangle commands (empty or filled) it will not load correctly. The reloading of the script ignores the radius bytes causing the rounded rectangle to not be drawn. You might think you can easily work around this by just not using the rounded rectangles but the tool doesn’t expose a command for the regular rectangle. The choice labeled Draw Filled Rectangle actually creates the Draw Filled Rounded Rectangle command. Just be aware that once you have a working script, saving it off and reloading it later will produce different results.

The Missing Manual

I think there are a several vital items missing from the manual. I’ll detail the ones I think it would be very helpful to know about.

Programming Over the Serial Port

The first vital piece of information I found missing from the manuals was the serial port protocol information. The GTT will communicate at 115,200 baud, with 8 data bits, No parity, and 1 stop bit. While programming it from .NET I also had to enable RTS and DTR. Then I noticed that sometimes I got inconsistent results so I turned on handshaking. In C# my open method looks like this:
public void OpenSerialPort(string portName)
{
if (PortIsOpen) return;
_serialPort = new SerialPort(portName)
{
BaudRate = 115200,
ReadTimeout = 10,
WriteTimeout = 10,
Parity = Parity.None,
DataBits = 8,
StopBits = StopBits.One,
RtsEnable = true,
DtrEnable = true,
Handshake = Handshake.RequestToSendXOnXOff,
Handshake = Handshake.RequestToSend,
ReceivedBytesThreshold = 2,
Encoding = Encoding.ASCII
};
_serialPort.DataReceived += SerialPortDataReceived;
_serialPort.Open();   
PortIsOpen = _serialPort.IsOpen;
}
I originally turned on XOn/XOff because I'm so used to sending ASCII data. However, the touchscreen sends binary data and I believe sending XOn/Xoff characters will corrupt the data stream.  I left the error evident so that YOU don't make the same mistake. 
File Path Format
Several commands require that you specify the path to where you stored files on the included memory card. For instance I created a folder called Art at the root level and inside that I have all my bmp and gif artwork. If I had a file called logo.gif, I would specify it with the following string:
Art\logo.gif
I’m sure all the unix programmers wouldn’t have thought it could possibly be any other way, but the world of windows gets your pretty well trained to use Art/logo.gif. In C# you can save yourself some escaping by using the verbatim literal syntax:
string logo_path = @"Art\logo.gif"


Rectangles vs. Rounded Rectangles

I’m not sure why there are two sets of commands. Personally I only support drawing rounded rectangles. A rectangle is just a rounded rectangle with a radius of zero. So supporting the extra commands will just add noise to your API. If you get paid by the line of code you may want to support all four.


Big Endian and the Groocrux King

The Protocol manual has a section on data types and it does mention it but it bears repeating: int values are expected to be sent in Big Endian format (most significant byte first) and byte values go from 0-255. The manual doesn’t explicitly state it, but it appears that all “index” value commands work with the full range from 0 to 255. However, for convenience you may want to avoid using 0 for touch regions so that you have a value you can return indicating either no touch or an error.


Capacitance Versus Resistive Technology

The iPhone and related family use Capacitive technology based screens. The GTT does NOT. Touching with your finger doesn’t work very well if you treat it like an iTouch. You need to TAP with some force. I find it works much better with a fingernail. In fact it works best with a plastic pen (cap on please), or a stylus (I finally have a use for my old Palm styli). In fact for a laboratory instrument, I think this was a good decision, a pointing device like a stylus provides better accuracy and we all know the lab rats aren’t good at cleaning the solder flux off their fingers before they start touching things.


The Deep Dark Dank Corners of C#

I’ve programmed in C# for close to ten years and there are some corners of the framework I’ve never visited until this project. Granted I would normally do this type of programming in an embedded C++ environment but prototyping and testing in C# is very fast. The following .NET APIs and techniques can save you some time.


Rectangles and BitConverter

One of the first things you will find is that the concept of a rectangle is very useful. Don’t pass around X, Y, width and height. Just pass around a .NET rectangle. In fact when I move to C++ the first thing I’m going to do is write a Rectangle class. When you want to get from an abstract rectangle to the low level byte stream required by the GTT you just need this method:
static private byte[] GetRectangleBytes(Rectangle rect)
{
byte[] x_loc = BitConverter.GetBytes(rect.X);
byte[] y_loc = BitConverter.GetBytes(rect.Y);
byte[] width = BitConverter.GetBytes(rect.Width);
byte[] height = BitConverter.GetBytes(rect.Height);

byte[] rect_bytes = {
x_loc[1], x_loc[0],
y_loc[1], y_loc[0],
width[1], width[0],
height[1], height[0],
};
return rect_bytes;
}

Notice how the bytes are sent out in Big Endian order.  This type of helper method is an ideal candidate for using C# extension methods. Anytime I have a static method, I pause to consider why and decide if that method should be littering up my class and its interface. I typically have an Extensions.cs file but you can put extensions inside any file you like. Create a public static class and add “this” to the method signature of the target method and change the visibility to public:

public static class Extensions
{
public static byte[] GetGttBytes(this Rectangle rect)
{
      //Nothing in the body will change from the code above. Notice
//I added the “this” qualifer to the method signature and the
//whole method is now in a static class
    }
}

Now some obvious refactoring can make our code even better.If we write methods for doing this for Point and Size our final GetGttBytes for a Rectangle will look like this:
public static IEnumerable<byte> GetGttBytes(this Rectangle rect)
{
IEnumerable<byte> loc_bytes = rect.Location.GetGttBytes();
IEnumerable<byte> size_bytes = rect.Size.GetGttBytes();
return loc_bytes.Concat(size_bytes);
}
       public static  IEnumerable GetGttBytes(this Size sizeData)
        {
            byte[] width = BitConverter.GetBytes(sizeData.Width);
            byte[] height = BitConverter.GetBytes(sizeData.Height);
            byte[] size_bytes = {
                                    width[1], width[0],
                                    height[1], height[0],
                                };
            return size_bytes;
        }


        public static IEnumerable GetGttBytes(this Point loc)
        {
            byte[] x_loc = BitConverter.GetBytes(loc.X);
            byte[] y_loc = BitConverter.GetBytes(loc.Y);
            byte[] point_bytes = {
                                     x_loc[1], x_loc[0],
                                     y_loc[1], y_loc[0],
                                 };
            return point_bytes;
        }

Other commands for the GTT use X and Y values (a Point in .NET) so writing the Point.GetGttBytes() method will pay dividends. I’ll show GetGttBytes in action in the next section.


Buffer.BlockCopy and the Back breakin’ beat

.NET has a variety of tools for manipulating arrays of data. There is some duplication among them and I ended up using the Buffer class versions, you may also want to explore the static methods on the Array class. I’m not sure why, but trying to do multiple serial sends with the command broken up between sends does not work. So if I build a command in pieces I need to piece it all together. Here’s one way to do that in C#, I’m going to construct a valid GTT command sequence for drawing a rectangle. I’ll pass in the two byte command sequence so that I can reuse this method for rectangles and filled rectangles.

public void DrawRectangle(byte[] cs, Rectangle rect, byte[] radius)
{
byte[] rect_bytes = rect.GetGttBytes();
    byte[] msg = new byte[cs.Length + rect_bytes.Length + radius.Length];

Buffer.BlockCopy(cs, 0, msg, 0, cs.Length);
Buffer.BlockCopy(rect_bytes, 0, msg, cs.Length, rect_bytes.Length);
Buffer.BlockCopy(radius, 0, msg, cs.Length + rect_bytes.Length, radius.Length);

_serialPortHelper.Write(msg); 
}

While that’s not too bad it is a little verbose and .NET’s LINQ syntax can save a lot of typing and make our code easier to read.  We need to add the using statements:
using System.Collections.Generic;
using System.Linq;


We could leave all the parameters to DrawRectangle as byte[] and the following changes will work but I prefer to define them as IEnumerable<byte>. The calling code can still pass in byte arrays.  Our above DrawRectangle method body reduces to a much more succinct form:

private void DrawRectangle(IEnumerable<byte> cs, Rectangle rect, IEnumerable<byte> radius)
{
    IEnumerable<byte> msg = cs.Concat(rect.GetGttBytes()).Concat(radius);
    _serialPortHelper.Write(msg.ToArray());
}


If we overload our Write method to take IEnumerable<byte> we can get rid of the ToArray() call as well. Now given a few constants like:
private const byte CMD_INIT = 254;
private static readonly byte[] STD_RADIUS = { 0, 9 };
private static readonly byte[] RECT_FILLED =  { CMD_INIT, 128 };
private static readonly byte[] RECT_UNFILLED = { CMD_INIT, 127 };
The public DrawRectangle commands become:

public void DrawUnfilledRectangle(Rectangle rect)
{
DrawRectangle(RECT_UNFILLED, rect, STD_RADIUS);
}

public void DrawFilledRectangle(Rectangle rect)
{
DrawRectangle(RECT_FILLED, rect, STD_RADIUS);  
}

public void DrawFilledRectangle(Rectangle rect, IEnumerable<byte> radius)
{
DrawRectangle(RECT_FILLED, rect, radius);
}

Getting Strung Along

When you want to pass a .NET String out as a series of bytes out to the display you need System’s Encoding class. Here’s the method:
public void Write(string data)
{
byte[] byte_data = Encoding.ASCII.GetBytes(data);
_serialPortHelper.Write(byte_data);
}

Reading back Data

So far the only data I’m reading back is for detecting touch regions. This works but I fear it will be inadequate. I’ve noticed some strange anomalies where the same code executed multiple times can produce incorrect results. There is no check summing of the data and I suspect some of the commands aren’t getting correctly processed some of the time. I’ve also noticed the display behaves better if a wait of 5 ms is added after every write command is executed. I’ll provide more data on writing a touch pad responder class in a future post.

Footnote 1 – Manual Errata that caused me pain.

4.18  Manual Update  message byte shown as 89,  same as Scroll command, correct value is 91.
8.1 Set Font – I suspect the message byte is 49 not 19.
10.7  Change Reporting Style both tables  specify  the message type as  29, correct value is 135.

About Me

My photo
Tod Gentille (@todgentille) is now a Curriculum Director for Pluralsight. He's been programming professionally since well before you were born and was a software consultant for most of his career. He's also a father, husband, drummer, and windsurfer. He wants to be a guitar player but he just hasn't got the chops for it.