example photo

So you’ve managed to get an LED to flash on your Raspberry Pi, and you are feeling pretty proud of yourself, but what next? Well, great little project to test your programming skills is a Persistence of Vision.

This project uses a pretty simple concept, but you have to pull together a number of different coding skills! So first let’s look at what is involved.

With a project like this it always best to break it down in to manageable chunks, so we are going to break the project down in to 3 parts:

  1. first get the whole project working inside python, by emulating the POV concept printing to the screen.
  2. then we are going to adapt our code to turn on and off LEDs
  3. Finally we are going to look at how we can get our project mobile and embed it in the real world.

 

Part 1 – Emulating Persistence Of Vision in Python

Before we start coding, let’s look at the some of the programming stuff we are going to need..

Letters represented using a matrix.

In order to print our letters using LEDs we are going to need to map them to a matrix, or grid, like below:

a     b      c

Here we are using a 3 by 5 matrix. This gives us a really basic font. The text you are reading here uses a  bigger matrix to print to the screen and a special technique called anti-aliasing(notice the different shades of gray)  to display smooth fonts, as can be seen below:

text

textbig

Once the characters are in the matrix, we need to display the characters by turning the LEDs on column by column, with a small pause in between each column.

slow

At about half speed it looks like this:

fast

So how do we get this wokring with just 5 LEDs? Well what we do is move the LEDs as they are lighting, and this creates the persistence of vision effect!

Implementing the matrix in Python

In order to this in Python we are going to use a dictionary to store our letters.

An example of the letter “A” is below:

letters = {"a":
"###" + "\n" + 
"# #" + "\n" +
"###" + "\n" +
"# #" + "\n" +
"# #"}

Using a dictionary means that we can use the letter as the key, and the matrix representation as the value.

>>>print letters["a"]
###
# #
###
# #
# #

Using a letter from the matrix

So we have the letters in the dictionary ready to use, what do we need to do? Well, let’s think about it.

For each column in the letter we need to:

  1. work out which of the 5 LEDs to turn on and turn on the correct ones.
  2. Then we need to wait a small amount of time.
  3. Then move on to the next column in the letter.

First we need to split the letter in to lines:

>>> letters["a"].split("\n")
['###', '# #', '###', '# #', '# #', '']
>>>

Then we need to iterate through column in the matrix, lighting the LED ( or in  this case just printing the “#” Symbol). In order to do this we need to use two for loops, one nested inside the other.

Since to we need to go through each line for each column, the line by line code needs to be nested inside the column by column code:

for each column in the columns:

for each line in the lines:

#do something here…

 

for column in range(0,3): #each letter is 3 wide
     os.system("cls")
     for line in lines:
        if len(line) > column: #check if column has content
            print line[column] #draw the content
        else:
            print " " # otherwise print empty space
        time.sleep(0.1) # This delay will need to change later

This will give us something like this:

pov python demo

Obviously just printing one letter isn’t enough, we need to print a whole word, or words! for let’s put all this code inside another for loop that iterates through each letter in the word or words you want to print!

sentence = "Hello World"
for letter in sentence:
   lines = letters[letter].split("\n") #Break letter in to vertical lines
   lines.pop(5) # Get rid of trailing newline
   '''Draw the letter'''
   for column in range(0,3): #each letter is 3 wide
       os.system("cls")
       for line in lines:
          if len(line) > column: #check if the part of the letter has content
              print line[column] #draw the content
          else:
              print " " # otherwise print empty space
       time.sleep(0.2) # This delay will need to change later

 

This is great, but what if we want to print multiple sentences? It’s best to wrap the whole thing up inside a function, that we can call whenever we want.

The complete code will look a bit like this:

import time,os

letters = {"a":
"###" + "\n" + 
"# #" + "\n" +
"###" + "\n" +
"# #" + "\n" +
"# #",
"b":
"###" + "\n" +
"# #" + "\n" +
"###" + "\n" +
"# #" + "\n" +
"###",
"c":
"###" + "\n" + 
"#" + "\n" + 
"#" + "\n" +
"#" + "\n" +
"###"
}

def printSentenceAsPOV(sentence):
    for letter in sentence:
        lines = letters[letter].split("\n") #Break letter in to vertical lines
        '''Draw the letter'''
        for column in range(0,3): #each letter is 3 wide
            os.system("cls")
            for line in lines:
                if len(line) > column: #check if the part of the letter has content
                    print line[column] #draw the content
                else:
                    print " " # otherwise print empty space
            time.sleep(0.2) # This delay will need to change later

printSentenceAsPOV("abc")
# Try adding more letters to your dictionary to make a full sentence. Include " " and "." so that you can type proper sentences!

Too lazy to type in the letters by hand? Here is a link to the letters file

Part 2 – Getting your POV working on a Raspberry Pi

The next step is to get your POV outputting to LEDs instead of to the screen. First we need to set up the hardware:

POV RPi Breadboard

Once we have set up the Hardware we need to adjust our code to make the LEDs light up

Import the GPIO module and do some setup

import RPi.GPIO as GPIO

GPIO.setmode(GPIO.BOARD)
ledMap = {1:3,2:5,3:7,4:11,5:13}# Map our LED numbers to the board pins.
for pin in ledMap.values():
    GPIO.setup(pin,GPIO.OUT) # Set all the Pins to output.

Change the print statements to GPIO outputs:

for line in lines:
   if len(line) > column: #check if the part of the letter has content
       if line[column] == "#":
           GPIO.output(ledMap[lineNumber],True)
       else:
           GPIO.output(ledMap[lineNumber],False)
   else:
        GPIO.output(ledMap[lineNumber],False)

The complete function will look like this:

def printSentenceAsPOV(sentence):
    for letter in sentence.lower():
        lines = letters[letter].split("\n") #Break letter in to vertical lines
        '''Draw the letter'''
        for column in range(0,3): #each letter is 3 wide
            lineNumber = 1
            for line in lines:
                if len(line) > column: #check if the part of the letter has content
                    if line[column] == "#":
                        GPIO.output(ledMap[lineNumber],True)
                    else:
                        GPIO.output(ledMap[lineNumber],False)
                else:
                    GPIO.output(ledMap[lineNumber],False)
                lineNumber +=1
            time.sleep(0.02) # notice we have changed the delay here
        for pin in [3,5,7,11,13]: # Add a delay to give some space between letters
            GPIO.output(pin,False)
        time.sleep(0.02)      

Fire up your Raspberry Pi and give the code a go!