Recently I saw an outdated (I thought) LED marquee sign in a thrift store. As these things make great tinkering objects I went inside and took a closer look. The sign looked fine and (tinkerer’s heaven) did have a serial port. Complete with RS232-to-USB adapter, remote control and power supply.
I did a quick research and decided to buy that thing. The technical data I found on the Internet were talking about up to 26 “pages” of text with 420 characters each, multiple effects and so on. Simply a must have…
The most interesting thing was, of course, the serial port. The idea is to have multiple RSS feeds on display. The news, the weather and so on. Everything controlled from a Raspberry Pi.
To understand how I can interface with the sign I will (of course) open it and take look at the insides. I thought I make this a blog entry to let you participate in my findings and (hopefully) show you something about “reverse engineering” electronic devices.
For testing things quickly I installed the software that came with the module and gave it a try. I suspected that things would not be totally as expected as soon as I saw that the documentation I had was only 7 pages, whereas the documentation I had found online had more than twenty pages. So there clearly was something missing…
It turns out the sign is an AM03127 (7×50), made by Hong Kong manufacturer Amplus. There are multiple version of this sign and I had just bought the simplest version. So here is the technical data for my cheapo-sign:
- 7×50 LEDs, red
- 1 “Logo” page
- 1 message page, maximum 420 characters
- no effects
- no scheduling
So obviously the idea of having different messages scrolling in turns was off. My first idea had been to run a python script via cron every hour, have it update the sign’s memory and let the sign display the content. To accomplish this I would now need something that can take over control of the sign on its own, leaving my Raspberry Pi available for other things. But first, let’s see if this can be controlled from the Raspberry Pi at all.
Plugging the USB-to-RS232 adapter into the Raspberry Pi went flawlessly. The kernel did recognize the Prolific chipset and created a device. Very good. You can always check which USB devices are present with the lsusb command. You should get an output like this:
A Python class for communications
The communications protocol is straightforward. Assemble the message, create a checksum and transmit. Needing a checksum prevented quick testing with a terminal program, so I wrote a simple Python class for me. The complete source code will be available at my GitHub repository, together with an example. The software simply assembles the message, with checksum and all, and sends it to the serial port. To read RSS feeds I use the excellent feedparser library. So with using my class (LedTicker.py) my simple test program was very short:
#!/usr/bin/python # -*- encoding: utf-8 -*- # # LedTicker class encapsulating the protocol for AM03127 LED displays # Copyright (C) 2013 Thomas Henkel (firstname.lastname@example.org) # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. from LedTicker import LedTicker as LED import feedparser max_length = 400 sign = LED(1, "/dev/ttyUSB0", False) #sign.resetDisplay() #sign.setID() sign.setTime() sign.sendPage('A', LED.SPEED_SLOW, "Logo") sign.sendPage('B', LED.SPEED_SLOW, "mypiandme") #sign.setDisplayPage() #print "Testing RSS" msg = '' print "Reading feed" feed = feedparser.parse("http://www.eevblog.com/feed/") print "Processing feed" for i in range(0,len(feed['entries'])): msg1 = feed['entries'][i].title + " -- " if (len(msg)+len(msg1)) > max_length: break msg = msg + msg1 print msg print "send to display:" sign.sendPage('B', LED.SPEED_FASTEST, msg)
That went well. Parsing the feed takes some time, about two minutes actually, so I need to rethink this. But on to the fun part:
Teardown and Analysis
When testing I recognized something very weird. As soon as I set the time from my program, the display would begin to flicker, displaying time and date in rapid succession. And there was no way to switch to the message that should be displayed. This stopped immediately when I removed the RS232 cable.
Second, there is no way to switch between displaying the message or the time from software. Using the remote control’s “Select” button, the display happily changed.
So this would be a great start. Let’s have a look at the connections to the outer world. Therefore I simply took my multimeter and probed the RS232 cable. Here’s the connections. RS-232 is the sub-d female connector, RJ-12 is the little connector that goes into the sign.
RS-232 ----------- RJ-12 (5) --- GND --- (2) black (3) --- TxD --- (3) white (2) --- RxD --- (4) red
This is a serial connection. So the “TxD” (transmit) on the RS-232 side is plugged into the “RxD” (receive) connection of the LED sign.
Next I opened the remote control. Have a look at the inside:
On the right there are 5 connections that correspond to the buttons. Very nice of the manufacturer to have that printed on the PCB. Saves some time. And the design is extremely simple. When pressing a button, the output pin is set to GND. Cool. That could be done with a microprocessor… So here’s the complete connections:
Remote ----------- RJ-12 SELECT----------- (1) black GND ----------- (2) blue DOWN ----------- (4) red UP ----------- (5) yellow ENTER ----------- (6) green
So the remote does not use pin 3, because that is the TxD line for serial communication. But wait! Pin 4 (DOWN) is connected on both cables. My first thought was that this does not matter, because the sign will never send something back to the computer. But the RxD line may be in an undefined state, making the sign interpret any voltage change as a button press. I drew a sketch of the RJ-12 connectors with pin numbers and everything which helped me thinking this over:
After some thinking I realized that the easiest way of testing this would be to program the sign from the Raspberry Pi, disconnect the RS-232 cable and connect the remote. Then hold the “DOWN” button pressed. Usually I do not feel comfortable with unplugging/plugging things into running hardware, but it worked. As soon as I held the button, the display flickered between time and date.
Perfect. So now we know which lines go where and that pressing a button on the remote just pulls a line to GND. It’s time to open the thing up (click for bigger image). The blue text is an annotation by me.
The best thing about this LED sign can be found in the lower right corner. It’s an Atmel Atmega8 processor. So we immediately know where to find the pins needed to communicate, reset and so on. The most relevant pins are VCC (7), GND (8), RXD (2) and TXD (3). Here’s a picture with the complete pinout for the Atmega8. Of course the Raspberry Pi uses 3.3V and the Atmega8 5V! Otherwise it would have been to easy.
Another important thing is to find out how the RS-232 signal is handled. RS-232 uses voltage levels of +12V and -12V, and, too complicate things further, a negative logic. That means levels between -12V and -4V are logic HIGH, whereas levels between +12V and +4V are logic LOW. So there is need to invert the signal and change the voltage level. If you remember my post about my All-Out-Board you may have heard the term level-shifter. And that is exactly what they built into the board. The most simple form of an inverting level shifter:
The resistors R1 and R2 serve as some sort of a voltage divider to limit the voltage applied to the B-E part of the transistor. If TXD is HIGH (negative voltage), the NPN-transistor is “open” (no flow between Emitter and Collector). So RXD (top right) is pulled up to VCC via R3, that’s a TTL level HIGH.
If the TXD line goes to LOW (positive voltage), the NPN-transistor shorts between Collector and Emitter. So RXD is connected to GND, a TTL level LOW.
And as we expect, RXD is directly connected to pin 2 of the Atmega8.
The Atmega’s RESET line is currently set to VCC so it does not reset. The RESET pin is inverted, hence the overlined label. This means to activate a reset you need to pull it to GND. Atmel strongly discourages you to directly set GND to the reset line. They provide some reference reset circuits in the 600 page manual for the Atmega…
So what is left? Oh yes, where do the buttons from the remote control connect to? Again with my trusty multimeter I did some measurements. The button lines are directly connected to some of the Atmega’s PIO-pins:
RJ-12 Pin -------------- Atmel Pin/Name (1) --- SELECT --- (14) / PB0 (2) --- GND --- (8) / GND (3) --- TXD --- (2) / RXD (4) --- DOWN --- (15) / PB1 (5) --- UP --- (23) / PC0 (6) --- ENTER --- (24) / PC1
This following picture is the complete sign turned upside down, showing only the back of the Atmega pins. In this picture the corner of the sign would be on the right top corner.
Well, we know how that thing works. We know where we could solder some wires to. And, let’s say, what about connecting an Arduino to that thing? Have the Arduino transmit the messages via serial and simulate button presses. Whoa! Cool.
And that’s exactly what is going to happen next…