Introduction: LED-matrix on a Budget

A unique piece of furniture without spending a whole lot of money?! That is what i wanted to achive in this project. Also i am not completely unfamiliar with programming and electronics and ,like most of us do, i think that LEDs are just awesome, which is why i started this project. So without further ado, lets get started, shall we?

Step 1: Tools/parts/software

In order to build a LED-matrix just like I did, you will need to gather the following tools and parts. Luckily i had all the tools and most of the parts already at home. I just had to order the LED's, the capacitors, the power supply and the acrylic glass, which is why I only spend about 40€ on this project, which at least in my opinion is a reasonable price. You can find most, if not all of the parts on ebay. The software used in this project is available for free.

Tools:

  • srewdriver
  • benchsaw
  • multimeter
  • computer
  • soldering iron + solder
  • rule
  • clamps
  • glue

Parts:

  • 4x wooden planks
    1. 2x(~9,15cm x ~2,3cm x ~62cm)
    2. 2x(~9,15cm x ~2,3cm x ~96,5cm)
  • 1x wooden backplate(~59,5cm x~98,5cm x ~2mm)
  • 1x acrylic glass(~98,5cm x ~60cm x ~2mm 79% opaque)
  • solid cardboard or thin wood
  • lots of wire (preferably different colors + female to female jumper wires)
  • heatshrink tubing
  • 60x WS2812 compatible RGB-LED's (PL9823)
  • 60x ceramic capacitors (preferably 100nF)
  • 1x switching power supply (5V DC 12A 60W)
  • 1x Arduino Uno or Nano or an Arduino compatible board + a matching USB-cable (a raspberry pi or an esp8266 should also work, since there are tons of libraries for various plaftforms that support the WS2812, however you have to come up with your own solution software wise if you choose to use a raspi or an esp)
  • 8x screws

Software:

Step 2: Building the Frame

The frame

The process of building the frame is pretty straight-forward, which is why i am just going to sum up the basic steps. Start of by cutting your wooden planks to the right size. Look at the pictures above for the measurements i used or come up with your own (which would probably be a good idea, given the fact that i built the project without a specific plan). Also cut slots into your 4 wooden planks using a bench-saw. Proceed by drilling holes into the 2 shorter pieces of wood. If your back-plate and your acrylic front-plate are not cut into the right size, you obviously also have to do that. My design (dimensions) is based on the back-plate, which i found in my dad's basement, which is why i could skip that part. Also my acrylic glass pane was delivered pre-cut, which came in handy. Try to slide the back-plate and your acrylic glass into the slots you previously cut into wooden planks that make up your frame BEFORE gluing all the frame related parts together just to make sure everything fits nice and snugly. Now, after removing the acrylic pane and the wooden back-plate, glue the bottom plank to the sides.(Leave out the top, you will want to glue it on later, after assembling and inserting the grid, which creates the actual "pixels"). Clamp all the pieces together while the glue is drying to achieve a solid structure. Also use screws just to make sure nothing falls apart later on.

The interior

I used cardboard to build a grid that fits right in between the back-plate and the front. the process of building the grid is rather easy, however time-consuming as well. i made several stripes of cardboard, which then snap-fitted nicely into each other.Look at this pictures to get a rough idea of what i did. Next, drill evenly distributed holes into the back-plate that hold your LED's tightly. I used a drill-bit with a diameter of 8mm to achieve a good fit. Now you can slide the back-plate and the acrylic glass into the U-shaped frame, slide in your cardboard-matrix and mount the top plank. Use glue and screws to create a solid construction.

Step 3: Electronics

The most time consuming step of this instructable... Get yourself ready for countless hours of soldering.

Prepare the LEDs

After fully assembling the frame, you can proceed by pushing 1 LED in each of the 60 holes you drilled into your backplate. Make sure that all the LED's are oriented the same way, then bend the data connections sideways and the power and GND lanes vertically down wards.

Power

solder a capacitor between each LEDs GND and Vin connections. These capacitors prevent voltage spikes from destroying your circuit. Then solder all the GND pins of all LEDs of each column together. Do the same procedure for all the Vin pins. Dont forget to use heatshrink-tubing to avoid exploding LEDs. Test each column by applying a voltage of 5 volts to it. (Note that polarity matters, so dont ever connect +5V to an LED's GND Pin.) If every column seems to work, ( the LEDs light up blue even without a signal) you can solder the GND lanes of all your columns together and do the same for all the Vin lanes. I also soldered 2 jumper wires to one column's GND and Vin lanes to power my micro-controller. Now connect the the GND wire, which connects all the columns of your matrix, to your power-supply's GND-terminal. the main power wire, which distributes the current evenly on all columns connects to a 5V-terminal of your power-supply. Before connecting the wires to the power-supply make sure, that the power-supply is not plugged in, or you risk getting shocked!

Data

Connect the Data-Out Pin of each LED to the Data-In pin of the LED below it. ( I used jumper wires, because at that point i was tired of soldering...) Connect the Data-Out of the last LED of each column to the Data-In of the first LED of the next column. connect the first Data-In-Pin to a GPIO of your Arduino using a jumper wire. (Look at my sketch to get an idea of how i did it)

Step 4: Software

Choices...

When it comes to software you have multiple options, you can either:

  1. run the matrix as a standalone device
  2. use your PC to push images/animations to the matrix
  3. or come up with your own idea (e.g connect it to an esp8266 to display content of the internet

I. Standalone

just play around with the FastLED library for the Arduino platform. There are tons of good examples online, which you can find here in the examples folder. You might have to change a few lines in the sketches, such as:

  1. the type of LED you are using(just change it to "WS2812").
  2. the amount of LEDs your matrix consists of.

II. Control the matrix via PC

This is the way i prefer to do it. GreatScott recently did a great tutorial on how to use the glediator software. He also came up with a sketch (an Arduino program) that worked just fine for me.

However I wanted to write my own code and came up with this python-script. Just make sure you chose the right com-port in line 95 and run these on your computer with your matrix connected to it. It (the script) also work just fine with GreatScott's glediator sketch, so you wont have to reprogramm your Arduino. Note, that I only tested them with Debian Linux, however it should not be too complicated to get them to work under Windows or OSX. Feel free to copy and alter the code to fit your needs. If you have any questions regarding the code, which is a bit cryptic, feel free to ask in the comments below. Also make sure to check out the next page, where I run Conway's Game of Life on the matrix.

But how exactly is all of this working?

The LEDs used in this project contain shift-registers, which are basically small circuits that can hold a small amount of data (in the form of ones and zeros). In this application the data stored in each LEDs shift register represents its colour and consists of 24 bits (8 bit per color). The firmware running on the Arduino receives data via serial communication and then shifts it through the daisy chain of shift registers/LEDs. The Arduino itself deals with the matrix as if it was a normal LED stripe (1 dimensional). My script below calculates each pixels colours (RGB) and stores it into a 2-dimensional array, which basically represents the matrix itself. After calculating each pixels RGB values (each represented by a value between 0 and 255 [2⁸] ) it then proceeds by pushing these values to the Arduino via a serial port on your computer. This happens at a specific order, since the Arduino cant address the LEDs in a cartesian kind of way (x,y) . The data is pushed at a given rate of frames per second, which leads to the illusion of a moving/fading pattern on the matrix.

# -*- coding: utf-8 -*-

import copy
import time
import serial</p><p>WIDTH=6
HEIGHT=10
PIXELS=WIDTH*HEIGHT
FPS=120.0

#--RGB--object
class RGB:
    def __init__(self,red,green,blue):
        self.r=red
        self.g=green
        self.b=blue
    def setcolor(self,red,green,blue):
        self.r=red
        self.g=green
        self.b=blue
       
#--create--matrix
def new_m(w,h):			#width/height    // returns 2 dimensional list
    matrix=[]         
    row=[]  
    for x in xrange(w):
        row.append(RGB(0,0,0))
    
    for y in xrange(h):
        matrix.append(copy.deepcopy(row))
    
    return matrix

#--print--matrix
def print_m(m):#matrix // 2 dimensional list
    h=len(m)
    w=len(m[0])
    for y in xrange(h):
        print y,"> |",
        for x in xrange(w):
            print x,"",m[y][x].r,m[y][x].g,m[y][x].b," |",
        print""

def img_to_m(path,m):  #path // matrix//width//height
    i = Image.open(path)
    pixels = i.load()</p><p>    h=len(m)
    w=len(m[0])    
    
    for x in range(w):
        for y in range(h):
            m[y][x].r,m[y][x].g,m[y][x].b = pixels[x, y]
 
          
          
def send_serial(ttyobj,m):	#n=anzahl der pixel,m=matrix(liste) // n= amount of pixels ,m = matrix (list)
	h=len(m)
	w=len(m[0])
	ttyobj.write(chr(1))	#eine 1 pro anzahl aller pixel(hier 60) // glediator protocol
	for x in xrange(w):			#layout der pixel:erst spalten, dann zeilen von oben rechts beginnend // first rows, then columns, starting in the upper right corner...
		for y in xrange(h):
			
			tty.write(chr(m[y][w-1-x].r))
			tty.write(chr(m[y][w-1-x].g))
			tty.write(chr(m[y][w-1-x].b))    
            

def calc(f,m): #frame, matrix
	h=len(m)
	w=len(m[0])
	while f > 255:
		f=(f-255)
	for y in xrange(h):		#alle pixel durchgehen // cycle through all the pixels
		for x in xrange(w):
			m[y][x].setcolor(255-f,f,255)
	return m #returns processed image
               

#MAINLOOP

running=True
frame=0
target_delta=1/FPS

print "creating matrix..."
matrix=new_m(WIDTH,HEIGHT)

print "opening serial port..."
tty=serial.Serial("/dev/ttyACM3", 500000)	#EDIT TO COMX
time.sleep(1)

while running:
    time_a=time.time()
    matrix=calc(frame,matrix)
    #print frame    
    time_b=time.time()  
    time.sleep(target_delta-(time_b-time_a))

    send_serial(tty,matrix)
    print 1/(time.time()-time_a)
    frame=frame+1</p>

Step 5: The Game of Life


The Game of Life, also known simply as Life, is a cellular automaton devised by the Britishmathematician John Horton Conway in 1970.[Wikipedia]

Just copy these lines or download the game_of_matrix.py below:

# -*- coding: utf-8 -*-

import copy
from random import randint
import time
import serial

WIDTH=6
HEIGHT=10
PIXELS=WIDTH*HEIGHT
FPS=3.0

#--RGB--object
class RGB:
    def __init__(self,red,green,blue):
        self.r=red
        self.g=green
        self.b=blue
    def setcolor(self,red,green,blue):
        self.r=red
        self.g=green
        self.b=blue
       
#--create--matrix
def new_m(w,h):#width/height    // returns 2 dimensional list
    matrix=[]         
    row=[]  
    for x in xrange(w):
        row.append(RGB(0,0,0))
    
    for y in xrange(h):
        matrix.append(copy.deepcopy(row))
    
    return matrix

#--print--matrix
def print_m(m):#matrix // 2 dimensional list
    h=len(m)
    w=len(m[0])
    for y in xrange(h):
        print y,"> |",
        for x in xrange(w):
            print x,"",m[y][x].r,m[y][x].g,m[y][x].b," |",
        print""

def img_to_m(path,m):  #path // matrix//width//height
    i = Image.open(path)
    pixels = i.load()</p><p>    h=len(m)
    w=len(m[0])    
    
    for x in range(w):
        for y in range(h):
            m[y][x].r,m[y][x].g,m[y][x].b = pixels[x, y]
 
          
          
def send_serial(ttyobj,m):#n=anzahl der pixel,m=matrix(liste)
	h=len(m)
	w=len(m[0])
	ttyobj.write(chr(1))#eine 1 pro anzahl aller pixel(hier 60)
	for x in xrange(w):#layout der pixel:erst spalten, dann zeilen von oben rechts beginnend
		for y in xrange(h):
			
			tty.write(chr(m[y][w-1-x].r))
			tty.write(chr(m[y][w-1-x].g))
			tty.write(chr(m[y][w-1-x].b))    
            

def populate(c,m):	#anzahl der startzellen, matrix
	h=len(m)
	w=len(m[0])
	for y in xrange(h):			#alle pixel durchgehen
		for x in xrange(w):
			m[y][x].b=127
			m[y][x].r=127
	h=len(m)
	w=len(m[0])
	for i in xrange(c): #zufaelliges besetzen
		y=randint(0,h-1)
		x=randint(0,w-1)
		m[y][x].setcolor(255,0,0)
	return m 

def isPopulated(m,y,x,yBorder,xBorder):
	if 0 <= y < yBorder:
		if 0 <= x < xBorder:
			if m[y][x].r==255: #schwellwert fuer lebende zelle
				return True
	else:
		return False
		
def checkNeighbours(m,y,x):
	h=len(m)
	w=len(m[0])	
	c=0
	if isPopulated(m,y-1,x-1,h,w):
		c=c+1
	if isPopulated(m,y-1,x,h,w):
		c=c+1	
	if isPopulated(m,y-1,x+1,h,w):
		c=c+1
		
		
	if isPopulated(m,y,x-1,h,w):
		c=c+1
	if isPopulated(m,y,x+1,h,w):
		c=c+1
		
		
	if isPopulated(m,y+1,x-1,h,w):
		c=c+1
	if isPopulated(m,y+1,x,h,w):
		c=c+1
	if isPopulated(m,y+1,x+1,h,w):
		c=c+1		
	return c
		
		
		
def nextGen(f,m): #frame, matrix
	equalPixels=0
	h=len(m)
	w=len(m[0])
	bufferMatrix=new_m(w,h)
	for y in xrange(h):			#alle pixel durchgehen
		for x in xrange(w):
			n=checkNeighbours(m,y,x)
			if n<2 or n>3:
				bufferMatrix[y][x].b=127
				bufferMatrix[y][x].r=m[y][x].r/2
			elif m[y][x].r<255 and n==3:
				bufferMatrix[y][x].r=255
			elif m[y][x].r==255 and 2<=n<=3:
				bufferMatrix[y][x].r=255
			else:
				bufferMatrix[y][x].b=127
				bufferMatrix[y][x].r=m[y][x].r/2
			
			if bufferMatrix[y][x].r==m[y][x].r:
				equalPixels=equalPixels+1
	if equalPixels==h*w:
		return populate(randint(0,60),new_m(WIDTH,HEIGHT))
	return bufferMatrix #returns processed image


#MAINLOOP

running=True
frame=0
target_delta=1/FPS
print "creating matrix..."
matrix=new_m(WIDTH,HEIGHT)
print "populating..."
matrix=populate(40,matrix)
print_m(matrix)
print "opening serial port..."
tty=serial.Serial("/dev/ttyACM3", 500000)
time.sleep(1)
send_serial(tty,matrix)
time.sleep(target_delta)</p><p>while running:
    time_a=time.time()
    matrix=nextGen(frame,matrix)
    #print_m(matrix)
        
    time_b=time.time()</p><p>    
    time.sleep(target_delta-(time_b-time_a))</p><p>    send_serial(tty,matrix)
    print 1/(time.time()-time_a)
    frame=frame+1</p>
Mind for Design

Participated in the
Mind for Design