Home > uC and Electronics > Led Matrix Canvas

Led Matrix Canvas

I wanted something “flashy” that grabs my attention when I am working. I am flooded with constant influx of emails, internal chat windows, and many terminal screens full of code. Sometimes it’s hard to get my attention when critical things happen in the trading environment and I notice it too late.

I had 2 of sure electronics 16×24 led matrix boards laying around. So I set out to create a led matrix “canvas” out of these 2 display boards. I didn’t want just a simple led display board. I wanted to build something flexible that can make the best out of our precious LED “real estate”. I envisioned something that I can easily control from a host PC. To make matters more complex, I not only wanted scrolling text messages, but render monochrome animations and images through it.

I didn’t think it’d be feasible to implement _all_ the functionalities I wanted into teensy firmware.  One may argue that this is possible ( Peggy seems to have implemented lots of features into firmware), but I thought it’d be cooler to leave the complex image/animation processing on the host PC-side. I’ve decided to make teensy code concentrate on deciphering the drawing “commands”, and offload content generation on the host PC.

When you do a google search of led matrix boards, you can find tons of neat projects out there. However, many of them lack explanation or sufficient documentation on them. I am not sure if folks might find this project interesting or useful at all. I certain learned a lot from it, and herein I describe the details of this project hoping that it can be of use to someone who’s aiming to do something similar.

For the Impatient

Here’s the demo video of the project.

Parts/tools used:

  • Teensy 2.0
  • 2 x Sure Electronics LED Boards
  • 2 female headers
  • 2 male headers (2×5)
  • Protoboard for wiring parts together
  • C lib for communicating with ht1632 driver on led matrix board
  • C lib for usb_serial
  • C code to parse commands and control led matrix
  • Python code to translate imgs, text, and 3D cube into commands
  • PySerial lib to sent over serial device : /dev/ttyACM0

And here’s the source code for download. If you are on a linux box, you can simply run “make” in the project directory.

Now, if you want to know the details, continue reading below.


At the core, the project uses Teensy 2.0 (http://www.pjrc.com). It uses ATMEGA32U4, which has native usb support. This means you won’t need an extra FTDI chip that usually takes care of usb-to-serial communication on behalf of other chips such as ATMEGA168 or ATMEGA328p.

Here’s the shot of the protoboard I’ve made. I know, I suck at soldering so it’s messy.

The enclosure is made out of 2 separate sheets of plastic my buddy cut out for me (thanks norlin!), couple of screws, and plastic coupling parts I found in my junk drawer. The LED matrix board already has holes for the screws so it was just a matter of drilling holes into the plastic sheets and combining them with spacers, nuts, and bolts.

I have been receiving requests to put up schematics from various people looking to replicate this. So here you go!

Notice the junction points. Whilc CS connections need their own pins, RD,WR, and DATA pins share connections to both led panel 1 and 2.

  • CS1  – PF0
  • CS2 – PF1
  • RD – PB6
  • WR – PB5
  • DAT – PD7

Hope this helps, and let me know if you have any problems setting this up!


The code that run on Teensy is developed on Linux with avr-gcc toolchain. It’s written in plain C and I tried to be efficient with pin IO where possible and avoid copying bytes when I don’t have to. You can probably run the same code on Arduino or Nerdkits (atmega168) with minimal changes to the code. You’d only need to switch from the teensy usb_serial.h library to an equivalent serial data access library.

  • Code size is under 8k bytes.
  • The library that communicates with ht1632 (led driver chip) is designed to be fairly self-contained.
  • Supports 3 modes: demo mode, interactive mode, drawing mode.
  • Demo mode just sweeps a vertical bar across the screen and activates Conway’s Game of Life after the 5th iteration.
  • Interactive mode allows you to send commands to led matrix using serial term (minicom or screen).
  • Draw mode allows you to send character commands to enable frame-by-frame drawing.

In order to switch in-and-out of these modes, you will need to issue these commands:

:d    # enters into drawing mode
:i     # enters into interactive mode
:q    # quits the current mode and defaults to demo

Perhaps someone can come up with a way to make the firmware receive inputs from other external sources such as Ethernet (directly attached to network), RF wireless (zigbee?). I am currently using this Teensy library to send/receive data bytes over usb. The benchmark on that page says it can push 961 kbytes per sec on Ubuntu Linux (which is what I am using), which is pretty impressive.

The firmware is optimized for rendering information on the Led Matrix. As such, it maintains couple of features:

  • Keeps shadow ram buffer that maps to every “cell” on the led matrix.
  • Able to send single command to ht1632 driver for individual LED addressing.
  • Able to send batch command to ht1632 driver for bulk update.
  • Can maintain states using the shadow ram: this gives the user ability to update the ram multiple times and send the entire ram content to the led matrix in one go.
  • Each recv() call can read upto 255 bytes from USB bus – this is more than enough for addressing all leds.

The command string you send to led matrix is quite simple. The firmware expects hex characters with optional header:

[x00y00w0000-]<hex chars>[;:]

Each hex char can address upto 4 led’s simultaneously. (2^4 == 16) This means that given 48×16==768 led’s, we need 768/4=192 hex characters to control the entire led matrix.

These are the example commands.

# switch on all 4 leds on the top left cell.

# switch on 4 leds twice - delay defaults to 500ms

# on 8 leds and then 4 leds

# on 4 leds on 1st cell, light up only a single led on the 2nd cell and stay lit

# execute hex '11' wait 100 msec, execute hex '22' wait 200 msec, etc..

# go to position (1,1) and execute hex 'EE'

# go to 10th column, 9th row, and light up all 4 LED's in the next 2 banks
# and wait 1 sec

These commands can be tested by dropping into “interactive” mode and typing on the console.

In retrospect, I think it’s definitely possible to make the command data more compact (via encoding/compression techniques), while retaining the same amount of information. If anyone has good ideas, please let me know!


If firmware is specialized for accepting inputs via serial bus, the software can concentrate on drawing the content on the led matrix. The benefit of offloading content generation to software on PC (higher stack in the project) are several.

  1. You are not limited small memory space. Thus, you can build sizable library stack for sophisticated drawing features.
  2. You have more choices in programming languages to interact with led matrix. (python in my case)
  3. If you want to add new content to the led matrix, you can make changes quick without having to recompile and flash the code into the microcontroller.
  4. Logical abstraction of functionality – each layer has its specific problem it tries to solve.

There are several Python classes that help communicating with this led matrix easier:

  • LedMat – models the canvas
  • LedMatControl – convenience wrapper functions
  • Cube – creates object oriented representation of 3D cube

I’ve just picked up Python for this project, so you might find the code a bit unpython-like.

Hope you enjoyed the article. If you have implemented something similar, let me know. I’d like to know about it.

Categories: uC and Electronics
  1. Gilberto
    November 19, 2010 at 11:38 pm

    Hi, Very nice job. Is it possible to create a larger screen display with these?

    You have a 48×32 combining both screens.

    is it possible to to a 192×64 or larger?

    • November 20, 2010 at 1:59 am

      Hi Gilberto
      Small correction, I have 48×16, not 48×32. I wish I did. 🙂

      Yes, 192×64 would’d be possible, but probably not with the setup I have above.
      You will run into limitations with anything larger than 86×64 using teensy 2.0 (or any 24 Pin mcu) as I did above.

      These led matrix boards come with 4 different chipselects. In other words, you can share the same clock,read,write lines across 4 boards. So you can think of grouping them in packs of 4. The 192×64 config will require a total of 32 boards: 4 rows of 8 boards. That means it will require at least 32 IO pins for chipselect, and 2 (clk & write) x 8 (groups) =16 additional pins (clk/write) to drive all 32 boards – that’s a total of 48 pins to communicate with that many boards.

      You will probably want to use IO expander to solve 32 IO pins for chipselect. Or you can opt to use a beefier mcu with more pins for such config.

      In terms of firmware, the bottleneck will be largely on the MCU’s processing speed. 192×64 will require 16 times more data than what I currently have. My setup uses about 200 bytes over usb per frame. For the large setup, you will need to push about 3.2k Bytes for each frame to be rendered. If you use Teensy, 3.2k over USB would not be the source of bottleneck. Benchmarks show you can push about 9000kBytes using their usb_serial library. Now, provided that I can get at worst 160 frames per second, you’ll only be getting ~10 frames or less per second on that size – this is assuming you send data to target each board one by one in cascading fashion.

      A proposed solution would be to use multi-core mcu’s like this one:
      multiple core properller led addressing

      Now, having said this, I think it would be possible to achieve 192×64 with teensy 2.0 with more engineering.
      If you want to take the discussion further, feel free to contact me via email. I think it will be an interesting challenge/project.

  2. Frollard
    November 20, 2010 at 1:43 am

    Awesome build. I’ve wanted some of these displays for a long time, but lack the caring to actually build it 😀

    Good job on the HaD feature.

    May I suggest implementing a faked software pwm for 4 or 8 level grayscale? You’d only lose brightness where you wanted it, at full cycle you should still get the same brightness, and cutting down to 25 or 50fps is a totally decent tradeoff. I might have to build one of these now 😀

    • November 20, 2010 at 2:02 am

      Frollard, I like your suggestion. I had thought about it, but was more interested in getting the basics working. 🙂
      I’ll put it in the backburner for now and incorporate that feature sometime later. Perhaps with photoresistor hooked up to ADC for auto-controlling the intensity of the brightness. Ooh… that gives me more ideas. Anyways, thanks!

      • frollard
        November 20, 2010 at 1:17 pm

        Good stuff! Someone on HaD comments suggested this board supports 16 stage pwm brightness control already!

      • November 20, 2010 at 4:16 pm

        Yup! If you look in the source code, the precise cmd to control PWM duty cycle is defined in ht1632.h:
        #define HT1632_CMD_PWM 0xA0 /* CMD= 101x-PPPP-x PWM duty cycle */
        I think it should be trivial to implement.

  3. November 20, 2010 at 3:43 am

    Congrats on the feature!

    • November 20, 2010 at 4:17 pm

      Hey thanks! I want to use msp430’s for my next project so I can submit it to 430oh.com! 😉

  4. November 20, 2010 at 3:56 am

    When I first skimmed through, I didn’t watch the video. I didn’t realize all the functionality you implemented. Very nice work!

  5. November 20, 2010 at 7:21 am

    good work 🙂

  6. November 20, 2010 at 12:06 pm

    hey, cool project – I’m working on something similar, neorainbowduino (http://code.google.com/p/neorainbowduino/) – I wanted the same approach (generate the images on the pc side) and thus I created a Processing library for it. An old example can be viewed here: http://www.neophob.com/2010/07/rainbowduino-fun-aka-neorainbowduino/

    • November 20, 2010 at 4:15 pm

      Nice! I wanted something that can do RGB. Looks like rainbowduino took the complexity out of addressing individual led’s in RGB space.
      I pinged you back on your blog. Thanks for visiting.

  7. Onishin
    December 23, 2010 at 11:01 pm

    I want to do the same thing.
    But electronics is not specialty.
    To tell me if it is necessary to add resistors?
    Or better a little scheme. Because I think I already toast 2 Matrix LED

    • January 2, 2011 at 1:35 am

      Hi Onishin
      You don’t need any extra discrete components. All you need is the led matrix panels and teensy (http://www.pjrc.com).
      If you want identical setup, I suggest getting familiar with Teensy first.
      I don’t have the schematics, but all you need are the following connections:

      CS1 (Panel 1) : PORTF, PIN0
      CS2 (Panel 2) : PORTF, PIN1
      WR (shared) : PORTB, PIN5
      RD (shared) : PORTB, PIN6
      DAT (shared) : PORTD, PIN7

      Let me know if you need further help.

  8. January 4, 2011 at 3:11 pm

    I have constructed an exact one like in the diagram and successfully installed the serial_install.exe driver. The hyperterminal is able to response with the :i (interactive mode) but nothing is displayed on the led matrix.

    The baudrate is set to 9600 (even tried 38400) and the send command from the hyperterminal is “x01y02w1000-FF”. What seems to be wrong with it.

    As I do not have the wiring diagram between teensy and sure 2416, my wiring is connected based on the bottom view of the protoboard.

    My multimeter does shows the presence of 5V on both the sure 2416 socket and teensy.

    Can you help?

    • January 6, 2011 at 1:47 am

      Hi Gary
      Make sure the connections are correct. Did you set your led matrix boards on the correct CS settings? The first one should have CS1 turned on the dip switch and the second one should have CS2. I just posted the schematics so that you don’t have to rely on the photoshot of the protoboard.
      If you look at the code, you can tell double check where the wires should go.

      • January 12, 2011 at 2:12 am

        Can you give the compiled led_matrix.hex file which I can immediately use so that I would know for sure the wiring connections and sure 2416 are in working condition?

      • January 12, 2011 at 3:00 am

        I sent you the hex file via email. Let me know if that works.
        Also, I’ve updated this blog post with the schematic image. Check the “hardware” section on this post.

  9. January 9, 2011 at 3:47 pm

    Yes, both CS1 and CS2 dip switch are correctly set. It is also wired according to the photoshot of the protoboard but it still would not display. Can you forward me a copy of your ledmatrix.hex file so that I can interact with the led matrix display?

    All the .c and .h files are copied into my Windows folder and have it compiled using WinAVR and making sure that the usb_serial for the windows version is used. An error was generated but the hex is created. Could it be the incomplete hex file that is causing the display problem?

    Here are my results of the compilation:

    > “make.exe” all

    ——– begin ——–
    avr-gcc (WinAVR 20100110) 4.3.3
    Copyright (C) 2008 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions. There is NO

    Linking: ledmatrix_term.elf
    avr-gcc -mmcu=atmega32u4 -I. -gdwarf-2 -DF_CPU=16000000UL -Os -funsigned-char -funsigned-bitfields -ffunction-sections -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=build/ledmatrix_term.o -std=gnu99 -MMD -MP -MF .dep/ledmatrix_term.elf.d build/ledmatrix_term.o build/ht1632.o build/usb_serial.o –output ledmatrix_term.elf -Wl,-Map=ledmatrix_term.map,–cref -Wl,–relax -Wl,–gc-sections -lm

    Creating load file for Flash: ledmatrix_term.hex
    avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature ledmatrix_term.elf ledmatrix_term.hex
    ./tools/teensy_loader_cli -mmcu=atmega32u4 -w -v ledmatrix_term.hex
    process_begin: CreateProcess(D:\teensy\lmcanvas\tools\teensy_loader_cli, ./tools/teensy_loader_cli -mmcu=atmega32u4 -w -v ledmatrix_term.hex, …) failed.
    make (e=193): Error 193
    make.exe: *** [ledmatrix_term.hex] Error 193

    > Process Exit Code: 2
    > Time Taken: 00:03

    include “ht1632.h”
    include “usb_serial.h”

    define CPU_PRESCALE(n) (CLKPR = 0x80, CLKPR = (n))
    define CMD_INVALID 0
    define CMD_QUIT 1
    define CMD_INTERACTIVE 2
    define CMD_DRAW 3
    define BUFSIZE 254
    define DEBUG 0

    > “make.exe” all

    ——– begin ——–
    avr-gcc (WinAVR 20100110) 4.3.3
    Copyright (C) 2008 Free Software Foundation, Inc.
    This is free software; see the source for copying conditions. There is NO

    Compiling C: ledmatrix_term.c
    avr-gcc -c -mmcu=atmega32u4 -I. -gdwarf-2 -DF_CPU=16000000UL -Os -funsigned-char -funsigned-bitfields -ffunction-sections -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wa,-adhlns=./build/ledmatrix_term.lst -std=gnu99 -MMD -MP -MF .dep/ledmatrix_term.o.d ledmatrix_term.c -o build/ledmatrix_term.o
    ledmatrix_term.c:1: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘ Process Exit Code: 2
    > Time Taken: 00:04

  10. Matt
    January 20, 2011 at 4:35 am

    I have all the pieces I need to make this, and to the best of my knowledge wired correctly (the matrices are powered through the Teensy’s power source, correct?). I downloaded the source code, used the make command, and exported the hex file to the Teensy, and then rebooted the teensy but I have not been able to get anything to come up on the matrix, not even the demo(whilst running demo.py)

    Is there a step I am missing, maybe I am just being stupid and need an extra power source for the matrices. Any help would be appreciated, thank you.

    • January 20, 2011 at 5:18 am

      Hi Matt
      Yes, both the power and ground connections are sourced from teensy. Did you check out the schematics for the connections on the blog?
      Can you connect to teensy via serial using demo.py? If so, try dropping into interactive mode by typing “:i”.
      I sent you an email in case you need further help.

  11. crmb
    May 14, 2011 at 1:05 am

    Thank you for sharing !

    I project to do the exact same thing PC->Teensy (with 2416 LEDs) but until now i was not sure how i would manage to make the direct PC->Teensy connexion.

    I choose to buy the teensy over the arduino after reading some good feedback on a forum, and since i found this page i am happy to have make this choice.

    I will start my project in a few week with some “tricolor” RG(+Orange) 2416 and a teensy 2.0++ and i think your code will be very helpful.

    • May 14, 2011 at 1:54 am

      If you like the form factor and native usb communication, Teensy is hands down better choice. Yes, arduino has bigger community and has more shields/ accessories, but I like the raw c library of teensy rather than a dumbed down arduino sketches. Yes, you can program arduino the same way you do with any avrs, but I still like teensy better. 🙂

      • crmb
        May 20, 2011 at 8:44 pm

        I am starting trying to make one board working as a first step.
        (I am more a regular php programmer so this is totaly new to me).

        Since i am using a 3216 board (DE-DP14112), there is some changes (vs the 2416).

        The 3216 has a new pin : CLK, i have understand it is used for selecting the correct Holtek chip (since there is four on each board). I think the Sure datasheet sample code will help me.

        However my main concern is the loss of the READ pin (that you are using in your ht1632.c in ht1632_readbits() if i am correct).

        Have you any advice to make me go in the right direction about the modifications to make about this lost pin.

        Thank you again

      • crmb
        May 29, 2011 at 7:01 pm

        Finally i quickly figured out that the pin was only used to detect the presence of pannels.

        I have got almost everything working.

        in interactive/draw mode :
        F = 4 green pixels
        00000000000000000000000000000000F = 4 red pixels

        However i am still stuck with the various demo modes (the pannel only display sort of blinking junk pixels on the first col…….. but sometime it works(!?) )

        The printed ‘msg’ – before senddata(ser,msg – looks ok for some reason the data must be sent incorrectly

      • May 29, 2011 at 7:15 pm

        great, feel free to send me your code. I’ll review and let you know if I spot anything.my email is jbremnant at gmail.

  12. pinki
    June 20, 2011 at 12:53 pm

    Hi jbremnant,
    i am trying to reproduce your setup, but I still have some problems to get it working.
    I have almost the same hardware: teensy2, 2x “2416 3mm red dot matrix displays” and the same protoboard layout.

    But I found some changes in the in the 2416 matrix board. “Sure electronics” use ht1632c (C!) instead of ht1632. Could this be the reason?!

    Have you an idea?

  13. January 10, 2013 at 5:15 am

    How much time did it require u to write “Led Matrix Canvas
    jbremnant’s blog”? It carries a great deal of beneficial tips. Thank you ,Wendy

  14. March 2, 2013 at 2:09 pm

    “Led Matrix Canvas | jbremnant’s blog” shoband was indeed a superb blog post and therefore I was indeed quite content to read the blog post. Thanks a lot,Laurence

  1. November 19, 2010 at 10:01 pm
  2. November 21, 2010 at 6:05 pm
  3. November 22, 2010 at 1:28 pm
  4. November 27, 2010 at 10:23 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: