Sniffing the Flip-dot Display
19 March 2014I 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.
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.