LED-matrix on a Budget

15K2259


Intro: 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>

9 Comments

Looks Great! Can you tell me where you found the WS2812 LEDs? I have looked on eBay and all I see are Strip Tape type WS2812 LEDs. Could you perhaps post a picture of one of these LEDs so I know exactly what I am looking for? Very much appreciated. Thanks.

hello friend , first my congratulations on your project!
Good am Brazilian and I have a difficulty speaking English so I'm struggling to figure out what code should I send the Arduino for them to get glediator data and send the ws2811 LEDs. Can you help me? thank you

I want to make something similar but like a 3x3 Rubik's cube. Do you thinks it is possible for your coding to recognize a LED matrix like the one in the picture below?

it should be possible to build 6 individual matrices for each side of your cube and connect them in series. Of course you would have to tweak the code quite a bit to fit your needs, but in general it is totall possible. :D sorry for the delayed reply, i havent had that much time lately.

Very nice project,i have one question. How would connect 2 of this matrix and that would work as one on the same program, If you know that please post it.

Connecting 2 matrices should be quite easy. Just connect the data-pin of the last LED of the first matrix to the input (data-pin of the first LED) of the second matrix. You would also have to change the variable that defines the amount of LEDs in the Arduino-Sketch, as well as the variables WIDTH and HEIGHT of my python script. The glediator software also allows to run matrices on various sizes, so running it on 2 connected matrices should work just fine. Keep in mind that running multiple matrices will cause an increased current, so make sure, that your power supply is capable of delivering such current. I hope that this is helpful :)

Thanks, glad you like it ! :)