Analysing the Flip Dot Display Command

20 March 2014

On the back of my previous post I've modified the LINQPad script to allow me to define a conversation with the display and set it up with the observed conversation from last time.

static int baudRate = 9600;
static int dataBits = 8;
void Main()
{
    using (var port = new SerialPort("COM3", baudRate, Parity.None, dataBits, StopBits.One))
    {
        try {
            port.Open();

            port.Send(2, 49, 3, 225, 213, 19);
            port.Expect(2, 49, 4, 224, 0, 144, 26);
            port.Send(2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 76, 97, 110, 103, 115, 116, 111, 110, 101, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188);
            port.Expect(2, 49, 3, 232, 68, 1);

            port.Close();
        }
        catch (Exception e) {
            e.Dump();
        }
    }
}

static class Extensions {
    public static string ToDebugString(this IEnumerable<byte> bytes) {
        return string.Join(", ", bytes.Select(x=>x.ToString()).ToArray());
    }

    public static void Send(this SerialPort port, params byte[] data) {
        port.Write(data, 0, data.Length);
        data.ToDebugString().Dump("Sent");
    }

    public static void Expect(this SerialPort port, params byte[] expectedData) {
        var timeout = TimeSpan.FromMilliseconds(500);

        var received = new List<byte>();
        var dataReceived = new SerialDataReceivedEventHandler((sender, args) => {
            while (port.BytesToRead != 0) {
                received.Add((byte)port.ReadByte());
            }
        });

        try {
            port.DataReceived += dataReceived;
            Thread.Sleep(timeout);
            if (received.SequenceEqual(expectedData)) {
                received.ToDebugString().Dump("Received");
            }
            else {
                throw new DataException("Expected { " + expectedData.ToDebugString() + " } but got { " + received.ToDebugString() + " }");
            }
        }
        finally {
            port.DataReceived -= dataReceived;
        }
    }
}

This will allow me to quickly test ideas about the protocol to see how the display responds.

Idea 1 - Addressing Dots/Pixels

I'm assuming that the protocol sends data about individual pixels and that all the brains behind converting text into pixels sits in the controller, which my code is replacing. In order to address each pixel I would expect some form of xy coordinates to be sent so I'm going to think about how that might be encoded in the command.

This display is 8 dots tall so encoding a y-coordinate could use 3 bits. It's also 90 dots wide, to encode the the x-coordinate would take 7 bits. This makes 10 bits, perhaps an 11th bit to indicate whether the dot should be on or off, meaning each pixel would be 2 bytes long. Turning the pixel at (42,3) could look something like this:

0 0 1 0 1 0 0 1 | 0 0 0 0 0 1 0 1
  ------x------ |         --y-- on/off

Not only is that lot of wasted bits, it also means that just the first L on the screen would take 34 bytes to communicate yet we only have 40 bytes in the command after the initial 2 display address bytes. Interestingly the 3rd byte is 39, could this be representing the size of the message that follows? I'm going to assume yes for now, I can always backtrack.

Another possibility for addressing individual pixels is for it to send a full sweep of data for the whole display. So one byte for every column, with the 8 bits representing each row. This would be more efficient with no wasted bits, however it would still require 90 bytes in the message.

Idea 2 - Hack, Prod, Hack

I don't have another idea, so I'm going to try changing numbers in the command to see what the outcome is.

I choose 101, I think because it's palindromic, I like palindromes. I'll change it to 121. Sending this modified command results in no response from the display and no change in the dots on show, this is not what I expected or wanted. I was hoping for some form of error code if I sent a bad command. Reverting my change and running the original to make sure I've not broken something elsewhere results in no data being returned after the first polling command. But wait...

Display showing "Langstony"

The display has updated and a character has changed!

This is interesting, I'm assuming that 2, 49, 3, 225, 213, 19 somehow made the modified command I sent take effect. Let's change some more characters and send that extra message afterwards.

port.Send(2, 49, 3, 225, 213, 19);
port.Expect(2, 49, 4, 224, 0, 144, 26);
port.Send(2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 78, 98, 110, 103, 115, 116, 141, 110, 121, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188);
port.Send(2, 49, 3, 225, 213, 19);
port.Expect(2, 49, 3, 232, 68, 1);

Display showing random characters

ASCII and ye shall receive

It's hit me. These are ASCII codes.

I'm running the original command through a bit of conversion like so:

data.Select(b => new {
    dec = b,
    hex = b.ToString("X2"),
    bin = Convert.ToString(b, 2).PadLeft(8, '0'),
    ascii = ASCIIEncoding.ASCII.GetString(new []{ b }),
}).Dump();
dec hex bin ascii
2 02 00000010
49 31 00110001 1
39 27 00100111 '
229 E5 11100101 ?
229 E5 11100101 ?
32 20 00100000  
156 9C 10011100 ?
170 AA 10101010 ?
57 39 00111001 9
228 E4 11100100 ?
32 20 00100000  
40 28 00101000 (
166 A6 10100110 ?
76 4C 01001100 L
dec hex bin ascii
97 61 01100001 a
110 6E 01101110 n
103 67 01100111 g
115 73 01110011 s
116 74 01110100 t
111 6F 01101111 o
110 6E 01101110 n
101 65 01100101 e
32 20 00100000  
66 42 01000010 B
117 75 01110101 u
115 73 01110011 s
105 69 01101001 i
110 6E 01101110 n
dec hex bin ascii
101 65 01100101 e
115 73 01110011 s
115 73 01110011 s
228 E4 11100100 ?
40 28 00101000 (
78 4E 01001110 N
166 A6 10100110 ?
80 50 01010000 P
97 61 01100001 a
114 72 01110010 r
107 6B 01101011 k
254 FE 11111110 ?
152 98 10011000 ?
188 BC 10111100 ?

The text is quite clear in there, 9 Langstone Business Park. There are spaces between the first two words but some sort of control characters between the rest. Before both the number 9 and the text there is a 32 followed by 2 characters. I think these 2 characters indicate the beginning of some segment of the route and the 229, 229, 32 is the type of command, which is displaying a bus route on the display.

Modifying the Message

I want to display mattscode.com on this, my current thinking is summarised as:

  • 2 bytes - display address
  • 1 byte - command size
  • 3 bytes - type of command
  • 2 bytes - control characters indicating start of some command section
  • 13 bytes - ASCII string
  • 3 bytes - control characters indicating end of command

Putting this together I have:

port.Send(
    2, 49,            // display address
    21,                // command size
    229, 229, 32,    // type of command
    40, 166,        // start of command section
    109, 97, 116, 116, 115, 99, 111, 100, 101, 46, 99, 111, 109,    // ASCII string
    254, 152, 188    // end of command
);

Display showing "mattscode.com"

Excellent!

Next Step

I've now got the ability to write my own text and I know the make-up of a command. Next I need to work out what the different control characters mean for different command sections and why the display resets after 50 seconds.

Sniffing the Flip-dot Display

19 March 2014

I switched over to the 90x8 display and flicked through some of the different destinations on the controller. I noticed that they were all showing only the bottom half of the characters, so I'm making the assumption that they were intended for a larger display. I happened upon one particular destination, however, that displayed in the correct line-height - Langstone Business Park.

Display showing "Langstone Busin"

That's something else that needs figuring out now, how does it determine the line-height to use.


Note

I didn't know the baud rate used so I had to guess. I started off at the high ones and repeated a failed version of the analysis process outlined below for each until I got it right. There any many assumptions in this and it took a lot of trial-and-error and backtracking, I've kept only the details of my work with the correct baud rate in this post as the rest is significantly less interesting.


Sniffing

The USB RS-485 dongle arrived. It presents itself to the PC as a serial port so writing a bit of code to read the data is a fairly straightforward task.

static int baudRate = 9600;
static int dataBits = 8;

void Main()
{
    using (var port = new SerialPort("COM3", baudRate, Parity.None, dataBits, StopBits.One))
    {        
        var bytes = new List<int>();

        port.DataReceived += (sender, args) => {
            while (port.BytesToRead != 0) {
                bytes.Add(port.ReadByte());
            }
        };

        port.Open();
        Thread.Sleep(TimeSpan.FromSeconds(20));
        port.Close();

        string.Join(", ", bytes.Select(x=>x.ToString()).ToArray()).Dump();
    }
}

I ran the above in LINQPad and then plugged the power in to the display and controller. The resulting data dump was:

0, 0, 2, 49, 3, 225, 213, 19, 2, 49, 3, 225, 213, 19, 2, 49, 3, 225, 213, 19, 2, 49, 3, 225, 213, 19, 2, 49, 3, 225, 213, 19, 2, 49, 3, 225, 213, 19, 2, 49, 3, 225, 213, 19, 2, 49, 4, 224, 0, 144, 26, 2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 76, 97, 110, 103, 115, 116, 111, 110, 101, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188, 2, 49, 3, 232, 68, 1, 2, 50, 3, 225, 240, 11, 2, 50, 3, 225, 240, 11, 2, 50, 3, 225, 240, 11, 2, 51, 3, 225, 10, 3, 2, 51, 3, 225, 10, 3, 2, 51, 3, 225, 10, 3

One thing to bear in mind is that the 2-wire RS-485 protocol is half-duplex. This means that data will go one way first, then the response will be sent on the same 2 wires. As I'm reading all data across those 2 wires there will be no way to determine whether any given byte has come from the display or the controller. What I'm looking for in the above is any repeated patterns that might indicate the start of a command. I'm going to assume that ignoring 0's is okay for the time being and that they just indicate a break in the messaging. We'll see how that pans out.

2, 49, 3, 225, 213, 19 is repeated at the beginning several times, there was also a period just after power-up where the display sweeps across all of the pixels turning them on and off again. I'm wondering if these repeated bytes at the start are the controller polling for an attached display, if this is the case then for the controller to find one it's going to have to receive a message back from the display. Working with these assumptions the data can be split up a bit:

0, 0
2, 49, 3, 225, 213, 19
2, 49, 3, 225, 213, 19
2, 49, 3, 225, 213, 19
2, 49, 3, 225, 213, 19
2, 49, 3, 225, 213, 19
2, 49, 3, 225, 213, 19
2, 49, 3, 225, 213, 19
2, 49, 4, 224
0
144, 26, 2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 76, 97, 110, 103, 115, 116, 111, 110, 101, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188, 2, 49, 3, 232, 68, 1, 2, 50, 3, 225, 240, 11, 2, 50, 3, 225, 240, 11, 2, 50, 3, 225, 240, 11, 2, 51, 3, 225, 10, 3, 2, 51, 3, 225, 10, 3, 2, 51, 3, 225, 10, 3

That last string of bytes could well be the command to display "Langstone Busin", so I want to try sending it to the display.

Replaying Commands

Here's a bit of code that should send the command through to the display.

static int baudRate = 9600;
static int dataBits = 8;

void Main()
{
    using (var port = new SerialPort("COM3", baudRate, Parity.None, dataBits, StopBits.One))
    {
        var command = new byte[] { 144, 26, 2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 76, 97, 110, 103, 115, 116, 111, 110, 101, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188, 2, 49, 3, 232, 68, 1, 2, 50, 3, 225, 240, 11, 2, 50, 3, 225, 240, 11, 2, 50, 3, 225, 240, 11, 2, 51, 3, 225, 10, 3, 2, 51, 3, 225, 10, 3, 2, 51, 3, 225, 10, 3, };

        port.Open();
        port.Write(command, 0, command.Length);
        port.Close();
    }
}

With the controller disconnected the display is powered up. Once it's finished its start-up routine I run the LINQPad script to send the command across. It worked, but I'm not convinced that all of those bytes are part of the command. As there has already been a command/response witnessed, I suspect that only part of this is the command and the rest is a response, perhaps with further command/response pairs. I also notice that after a short period of time (50 seconds, I timed it) the display resets itself to an blank state.

Rather than blindly cut bytes off and send the command again I'm looking for patterns towards the end. I notice two, actually: 2, 50, 3, 225, 240, 11 and 2, 51, 3, 225, 10, 3. Splitting the command down might look like this:

144, 26, 2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 76, 97, 110, 103, 115, 116, 111, 110, 101, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188, 2, 49, 3, 232, 68, 1
2, 50, 3, 225, 240, 11
2, 50, 3, 225, 240, 11
2, 50, 3, 225, 240, 11
2, 51, 3, 225, 10, 3
2, 51, 3, 225, 10, 3
2, 51, 3, 225, 10, 3

Substituting that trimmed-down first line into the LINQPad script still results in the message being shown so I'm on the right track here. Sticking with the assumption that the display is going to send some sort of response back I'm going to merge those 2 scripts so that any response is captured.

static int baudRate = 9600;
static int dataBits = 8;

void Main()
{
    using (var port = new SerialPort("COM3", baudRate, Parity.None, dataBits, StopBits.One))
    {        
        var bytes = new List<int>();

        port.DataReceived += (sender, args) => {
            while (port.BytesToRead != 0) {
                bytes.Add(port.ReadByte());
            }
        };

        var command = new byte[] { 144, 26, 2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 76, 97, 110, 103, 115, 116, 111, 110, 101, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188, 2, 49, 3, 232, 68, 1, };

        port.Open();
        port.Write(command, 0, command.Length);
        Thread.Sleep(TimeSpan.FromSeconds(5));
        port.Close();

        string.Join(", ", bytes.Select(x=>x.ToString()).ToArray()).Dump();
    }
}

The response from this is 119, 13, 232, 68, 1 but this doesn't match anything in the original data so I'm going to remove a few more bytes from the message to see it starts looking better. I notice that 2, 49 is in there towards the end. These 2 bytes are at the start of the polling patterns in the original data and also the start of the response from the display. Another assumption to join the party: 2, 49 is the address of the display on the RS-485 bus and it's prefixed to messages to indicate the sender or intended recipient. This being the current assumption I'm going to remove that possible response from the command I'm sending. I'm also going to remove the first 2 bytes as 2, 49 is near the start too and will indicate the start of the actual command. We're now looking at:

2, 49, 39, 229, 229, 32, 156, 170, 57, 228, 32, 40, 166, 76, 97, 110, 103, 115, 116, 111, 110, 101, 32, 66, 117, 115, 105, 110, 101, 115, 115, 228, 40, 78, 166, 80, 97, 114, 107, 254, 152, 188
2, 49, 3, 232, 68, 1

Sending the first line as the command results in the display showing the desired message and also a response of 2, 49, 3, 232, 68, 1

Next Step

I've now got a command that works, the next step is to analyse this to determine its make-up and what the protocol might be, but it's late and I'm tired so I'm going to leave that for tomorrow.

Flip-dot Display

13 March 2014

I've got my hands on 6 flip-dot displays through an eBay auction.

  • 1 old Bright Tech, I've not looked at this yet but it has a fluorescent tube light in it
  • 1 small Hanover (20 x 7 dots)
  • 2 Bright Tech Bright Dots (95 x 16 dots)
  • 1 Bright Tech Bright Dot (90 x 8 dots)
  • 1 Bright Tech Bright Dot (20 x 14 dots)
  • 1 Bright Tech controller of some sort

Not knowing much about them other than what I could discern from the Wikipedia article I'm endeavouring, first of all, to make them function in their current state.

Interfaces

The front of the controller and display are fairly non-revealing: Front of controller and display

The rear are where the connectors sit, but unfortunately I have no cable to run between them and no pin-outs to determine what should connect to what. Opening them up to take a peek at the innards is helpful though, after a quick Google for some of the various IC's (integrated circuits) involved.

The 3 important parts of the display board are the power, the data, and this IC. Display board

The PCB is marked up with "power input (24v)" by the bottom connector. I do not have a 24 volt supply to hand, so 12 volts will have to suffice.

And suffice it does, the video above shows the power up sequence when 12 volts are applied to that cable.

The SN75176B IC is an RS-485 tranceiver, so the communication is likely done over an RS-485 bus. The 2 pins used for communication are A and B, these should be wired up to the matching A and B on the controller. A helpful little multimeter revealed both of these wires in the cable.

I've highlighted the corresponding parts of the controller board too and with the multimeter found the correct pins in the external connector: Controller board

I cut the P4 power connector off an old PC power supply in order to get a data connector for the controller side.

Testing

I plugged it all together, powered it up, and... nothing :-(

As I change the selection on the controller I can hear the display whining as though it's powering through all of the dots, but they're all being set to off. I'm wondering if this is because the big messages are intended for the large display at the front of the bus and this is only a rear number sign. Flicking through to find a message with a number in it still doesn't show anything on the display though.

Now I'm wondering how the controller would even know not to send the long message to the rear sign. There must be some sort of addressing used in the protocol and these DIP switches look a likely culprit for setting the address of any given display: DIP switches

If this does represent the address then it's currently set to 3. Let's see what changing it to 1 does: Display showing "So"

As seen in the picture above, it's now a little bit less broken. It's displaying the first 2 characters of "Sorry Not In Service". From this I'm assuming that the DIP switches are in fact some sort of address, and that address 1 is probably for the big signs from the front of the bus. This is the only message that displayed anything though, I'm a bit confused about this.

Next Step

Next up I need to figure out the communication protocol that's going over the RS-485 bus and see what's going on a bit more. I'm hoping I'll see messages being sent out for more addresses that I can then select on the DIP switches. In order to do this I've ordered a USB RS-485 dongle. Once it's arrived I'll be diving straight back in to this.

Pan Etching

9 March 2014

It's Sunday, I'm hungover, I'm bored, I've just read this, and so pictures:

I couldn't find vinegar so I had to waste some delicious sauce, nor do I have any stencils, so I used fake butter to mask off the area I didn't want etching rather crudely.

Battery hooked up, red to the handle, black to the sauce, for 30 seconds or so.

And I have a little bit less pan now.

Making a Razor Compiler - The Tokeniser

21 November 2013

I recently evaluated SemanticMerge at work during a large merge. It was very good at understanding c# code files and determining what methods had been moved around and changed, but unfortunately didn't have any idea about WebForms or Razor markup files. A brief discussion with a co-worker about the difficulties involved in parsing a Razor file lead to me wanting to implement one. I decided to start it in TypeScript just because that's my flavour of the month. It also means I can gear it towards being a compiler for a templating engine.

The components

There are three main components involved in the compiler, the tokeniser, the parser, and the compiler. The tokeniser is responsible for presenting the razor markup as individual tokens ready for the parser to consume and understand. The parser constructs a logical view of razor markup which the compiler then takes to generate the executable code that will produce the final rendered markup.

I've hit a snag

At the moment the tokeniser, or token iterator, consumes whitespace and presents everything else as individual tokens.

Given the razor: <div>Hello. I'm @model.name! Nice to meet you.</div>

The tokens presented are:
< div > Hello . I ' m @ model . name ! Nice to meet you . < / div >

For the HTML and code side of things this is great, whitespace is pretty much meaningless. Unfortunately in blocks of text it does have meaning and that is lost in this whitespace consuming token iterator. I naively tried to resolve this by having the parser join the tokens together with a single space character, but then the output becomes Hello . I'm with some extra spaces in there - not great.

I started to think about how I could get the iterator to include the full text phrase so that the following tokens would be presented:
< div > Hello. I'm  @ model . name ! Nice to meet you. < / div >

The problem with this is that the iterator can't judge whether or not to include more than one word in a token. As far as it's aware meet could be a variable name, or model could just be a bit of plain text. It's not the iterators job know what things mean but rather to present small tokens to the parser which has a full understanding of the token's context in relation to the rest of the markup.

Therefore a solution is to have the iterator present the following:
< div > Hello .   I ' m   @ model . name !   Nice   to   meet   you . < / div >

The interface to the iterator will have to be extended to allow the parser to ask for either the next token, or the next non-whitespace token, but this is a straightforward change. The next question I need to answer is whether all whitespace should be preserved in the output, or only whitespace that has meaning, I'm leaning towards the latter.