Using Maplins N00GA or AM03127 LED Display as an LED Matrix

May 5, 2015 at 10:47 pm (maker) (, , , )

One of the reasons for playing around with my N00GA (the AM03127 based LED sign you can get from Maplins) was to see if I could get to use it as a large LED matrix rather than as a scrolling text sign.  You can, but it is a bit involved.

As described in my previous post, the sign can be controlled over RS232, but to use it as a raw matrix, you need to do the following:

  • Send a message to the sign to define a series of graphic blocks
  • Send a message to the sign to display the graphic blocks

The technical manual does a pretty good job of telling you how to define the graphic blocks, but it is not a complete description and a bit of experimentation is in order.   Whilst there is some code around that appears to include functions to define and show graphic blocks, again there isn’t much by way of explanation of how it works, so here goes.

First, from the manual …

N00GA-DefineGraphicBlock1 N00GA-DefineGraphicBlock2

So what this means is that each block of 4 LEDs is encoded into a single byte, with 2 bits per LED.  The values for each LED are as follows:

00 - Off
01 - Green
10 - Red
11 - Both (i.e. orange/yellow)

Each graphic block is defined in an 8×8 chunk, and there are 4 of these 8×8 chunks in each graphics block – as per the diagram in the manual.  So that there are so that there are 32×8 “pixels” in each graphic block.
Each block itself is defined using a <Gpn> command and then incorporated into a message to be displayed so it is trivial to define one block and repeat it many times in the display (once you realise these are two different commands).

So, for the first block of 8×8 LEDs:

Row 1 Byte 1 = D0-D3 Byte 2 = D4-D7
Row 2 Byte 3 = D8-D11 Byte 4 = D12-15
Row 3 Byte 5 = D16-D19 Byte 6 = D20-23
Row 4 Byte 7 = D24-D27 Byte 8 = D28-D31
Row 5 Byte 9 = D32-D35 Byte 10 = D36-D39
Row 6 Byte 11 = D40-D43 Byte 12 = D44-D47
Row 7 Byte 13 = D48-D51 Byte 14 = D52-D55
Row 8 Byte 15 = D56-D59 Byte 16 = D60-D63

In my case, there is no Row 8 for the N00GA – it is a 80×7 display, so these two bytes are ignored (but still required).

  • Byte 17 -> Byte 32 is the next 8×8 rows (with Bytes 31+32 unused)
  • Byte 33 -> Byte 48 is the next 8×8 rows (with Bytes 47+48 unused)
  • Byte 49 -> Byte 64 is the last 8×8 rows (with Bytes 63+64 unused)

Now this is the bit that no-one seems to mention in anything I’ve found discussing these modules so far.

Whilst most of the protocol for driving these devices via RS232 uses ASCII encoded messages, this bit does not! These are encoded sequentially as raw binary values (not encoded into ASCII as dec or hex) so they will go over the serial line using the numerical range 0..255.

Yes, if echoed to a terminal, you’d see everything from NULL (0, 0x00) through the control characters right up to DEL (127, 0x7F) and then beyond using the extended 0x80-0xFF range.

So to use graphics, you really have to avoid any of the useful line-terminal related options – like echo or cat and just send it out ‘as is’. For me, this mean grabbing the Perl Device::SerialPort module.

These values are written sequentially following the <GA1>, <GA2> or <GA3> command, which defines graphic blocks 1, 2 or 3 for the first page (A).

So the full command to define graphics block 1 on page A is

   <GA1>nnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnn

where “<” “G” “A” “1” and “>” are all the ASCII characters for <GA1> as you’d expect, but each of the 64 “n”s is a single byte “raw” value from 0x00 through to 0xFF. To encode a full 80×7 matrix, it will need GA1, GA2 and some of GA3 (GA1 = columns 1 to 32, GA2 = columns 33 to 64, GA3 = columns 65 to 80, with 81 to 96 unused).

When it comes to actually insert the character into the display, then use the defined message format to create a static display of the three now defined graphics characters as follows:

   <ID01><L1><PA><FA><MA><WA><FK><GA1><GA2><GA3>48<E>

The commands used here are:

  • L1 – Select line 1
  • PA – Select page A
  • FA – Immediate display (no scrolling, etc)
  • MA – Normal display method (no blinking or “songs” playing)
  • WA – Waiting time (0.5 seconds – not sure if this has an effect for immediate displays?)
  • FK – Hold the display static (i.e. no “lagging” command to remove the display)
  • GA – As discussed above to insert the graphic blocks.
  • 48 – checksum for the command

So all that remains is to write a function to take an 80×7 structure as an input and turn it into the three 64-byte sequences required to define GA1, GA2 and GA3 and then send this command to display them.

The following perl subroutine will take a single 8 bit value, representing one of the rows in one of the 8×8 chunks of a graphics block, and return the two raw bytes ready to be inserted into the <GAn> command. I expect there is a much more elegant way to do this, but this will do for now!

sub led2gfxblock
{
    my ($row) = @_;
    
    # Expand - each pixel is 2 bits:
    #  10 - red
    #  01 - green
    #  11 - yellow
    #  00 - black (off)
    # defining the colour here:
    my $col = 2;
    
    # D0-3 returned first, then D4-7
    my $val1=0;
    my $val2=0;
    for (my $b=0; $b<4; $b++)
    {
        if ($row & (1 << (4+$b)))
        {
            $val1 += ($col << ($b*2));
        }
        if ($row & (1 << $b))
        {
            $val2 += ($col << ($b*2));
        }
    }
    
    return chr($val1).chr($val2);
}

Then something will need to chop the 80×7 input into these 8 bit rows calling this subroutine as required for each value (and not forgetting to insert a dummy “row 8” value each time).

Unfortunately, so far, every time I send a new command across to define a new graphics block, the display appears to flicker … not quite sure if that can be prevented. But it seems that once it has the message to display the three graphics blocks, that message does not need sending again if the graphic block changes.

Kevin.

Permalink 1 Comment

RPI UART and Maplins N00GA or AM03127 LED Display

April 27, 2015 at 9:42 pm (maker) (, , , , )

I have a tri-colour LED matrix sign from Maplins (a bit like this one) that has an 80×7 two-colour LED display (red and green, and when both are on, a kind of orange).  This has a serial port connection so I wanted to hook this up to my Raspberry Pi.  The original sign is supposed to come with a USB to DB9 Serial and then DB9 Serial to RJ11 set of leads to allow you to connect the sign to a PC, but mine doesn’t have these.

Now there are some excellent resources out there for programming these signs, see:

But what I wanted to do was drive this from the on-board GPIO UART from the Raspberry Pi, and I couldn’t find an obvious description of what was required to make this happen.

After much reading, searching and a bit of experimentation, eventually I managed to assemble all the bits and work through the joys of the many standard RS232 pin-outs for DB9 connectors and the non-standard RS232 pin-outs for RJ11 connectors and come up with the following.

First, to get to any kind of standard RS232 port from the GPIO UART a level shifter is required to go from the 3.3/5v RPi I/O to the +-12v required for RS232 serial ports.  Thankfully these are trivial to find based on the MAX3232 chip, so I got one that includes a built-in DB9 port and 4 pin connections for VCC, RXD, TXD, GND.

max3232

For some reason, these all seem to come with a female DB9 port, so I guess they are designed to link to a PC as if the jumper wires and MAX3232 breakout board make the equivalent of a null modem cable between the Pi and the PC.

So, then I needed to decipher the pin-outs for the RJ socket on the display.  The manual for the LED sign shows the socket view as  follows:

N00GA-SerialPort

I had an old RJ11 to RJ11 telephone cable, so I ripped the plug off one end and set about working out which wires go where.  The combination that worked for me was linking TXD on the display to TXD on the DB9 socket, RXD to RXD and GND to GND.  I think that sort of follows the diagram in the manual, but the cable colours of my telephone lead didn’t match that at all.

So, the resulting combination of pins to connectors to wires to sockets is as follows:

RPI GPIO MAX3232 DB9(F) Phone Lead RJ11 Plug AM03127 RJ11 Skt
4 VCC 1 VCC
6 GND 4 GND 5 GND Black 1 Black 4 GND (Red)
8 TXD 3 TXD 3 TXD Green 3 Green 2 TXD (Black)
10 RXD 2 RXD 2 RXD Red 2 Red 3 RXD (Green)

So once you realise that everything connects to the same labelled pin, you should be fine.

Unfortunately it is not possible to test this with a simple terminal program such as minicom, as the time between key presses is too much for the protocol to be followed (I guess if you had it line buffered it might work, but I didn’t look into this), but it is possible to “cat” some commands to the display to see if it is all working ok.

Also, you need to have disabled the RPi console, which by default will be attached to the serial port.  There are good instructions for that here.

Example – to set the ID and dim the display, create two text files with the following in them:

id.cmd:
   <ID><01><E>

dim.cmd:
   <ID01><DB>04<E>

The first sets the ID of the display to 01 (the display ID can be anything from 01 to FF, 00 means broadcast to all connected displays).  The second command sets the display to its dimmest setting.  The 04 is a checksum value and will depend on the command given.

To use, simply cat these files to the serial port:

   cat id.cmd > /dev/ttyAMA0
   cat dim.cmd > /dev/ttyAMA0

Of course, you will never see the returned acknowledgement or results, but whatever text was on the display should now be dimmed.  Ideally, use the remote control to reset the display first (FUNCT -> Setup -> Clear All -> Enter)

If this doesn’t work, you’ll need to get debugging and checking cables, etc.  One tool that is great for driving the sign programmatically is the sample python code mentioned above.  I had to install the python-serial library before using it, but then it worked very well.

   sudo apt-get install python-serial
   python am03127.py -p /dev/AMA0 -v --message Hello

The only slight quirk, is that the sample code doesn’t appear to call the sync_set_sign_id() function, so if you are not sure what ID your sign is (the default should be 01) you might need to set it using the cat method above or hack a call to the function into the code.  Having an incorrect ID may generate a “timeout” error when using the script in verbose mode (i.e. with -v as shown above).

If you want to experiment with your own commands, then you’ll have to implement something to calculate the checksums.  The following simple Perl script will take a command message for the display and output the text you’ll need for the complete command, including the checksum value.

#!/usr/bin/perl

my $str = $ARGV[0];
my @ch = split (//, $str);
my $chk =0 ;
foreach my $c (@ch)
{
    $chk ^= ord ($c);
}

printf "<ID01>$str%02X<E>\n", $chk;

So, for example, a Hello World message will be:

  <ID01><L1><PA><FE><MA><WB><FE>hello world55<E>

I now plan to get to work on the technical manual and decipher some of the more sophisticated commands, especially those that let me get graphics sent to the display.  All this was possible thanks to those who have already worked out how to drive this thing.  All I needed was the details of how to connect it physically to my  RPi GPIO.  Which I now have.

Standard disclaimer – this worked for me, YMMV, I won’t be held responsible for you pumping 12v or worse into your RPi GPIO if something goes wrong! 🙂

Kevin

Permalink 1 Comment