POV Generator

The persistence of vision experiment is pretty cool. But how did I get the picture into the proper array format? To enter it by hand would have been to tedious. Instead I used some very simple python script to generate the whole sketch from a picture.

#!/usr/bin/python
# -*- coding: utf-8 -*-

#
#   www.blinkenlight.net
#
#   Copyright 2011 Udo Klein
#
#   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
#   (at your option) 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/


import sys
from PIL import Image

# double check that there is at least one argument (the file name)
if len(sys.argv) != 2:
    print "Please specify a source filename"
    sys.exit(-1)

# determine source file name
sourcefilename = sys.argv[1]
# strip extension
name = sourcefilename.split(".")[0]

# let PIL determine the proper graphics file type
image = Image.open(sourcefilename)

# convert image to black and white
image = image.convert("1")

# get hold of image size
(xsize, ysize) = image.size
# ensure height is 20 pixels (=number of LEDs)
if ysize != 20:
    print "Image height must be 20 pixels but is {0} pixels".format(ysize)

# output common start of program
print """#include <MsTimer2.h>
#include <avr/io.h>
#include <avr/pgmspace.h>

uint8_t const pov_pattern[] PROGMEM = {"""

# for each colum in the picture output 3 bytes
# that will be copied into the LED output ports
for x in range(0, xsize):
    line = "                                  0b"
    for y in range(0, ysize):
        if y==6 or y==12:
            line = line+", 0b"
        if image.getpixel((x, ysize-1-y)) != 0:
            line = line + "0"
        else:
            line = line + '1'

#   add a comment that makes it easier to see
#   the intended meaning of the 3 bytes
    line = line + (", // line {0:>3}: ".format(x+1))
    
    for y in range(0, ysize):
        if image.getpixel((x, ysize-1-y)) != 0:
            line = line + "."
        else:
            line = line + 'X'

    print line

# output common end of program
print """                                };

void blink() {
    static uint16_t index = 0;

    PORTC = pgm_read_byte(pov_pattern+(index++));
    PORTB = pgm_read_byte(pov_pattern+(index++));
    PORTD = pgm_read_byte(pov_pattern+(index++));

    if (index >= sizeof(pov_pattern)) { index = 0; }
}

void setup() {
    DDRD = 0b11111111; // set digital  0- 7 to output
    DDRB = 0b00111111; // set digital  8-13 to output
    DDRC = 0b00111111; // set digital 14-19 to output (coincidences with analog 0-5)

    MsTimer2::set(2, blink);
    MsTimer2::start();
}

void loop() { }
"""

This script is pretty short due to the really helpful python imaging library.
The script requires pictures of height 20 and does no checking for the width. So you have to ensure that your picture will fit into flash memory. For the typical Arduino this means that width should be below 10000 pixels.
Now fire up GIMP (or your favourite graphics tool) and create your own custom POV display.

Blinkenlight POV

Blinkenlight POV

Read on to push this one step further.

13 Responses to POV Generator

  1. hunternet93 says:

    I did something similar using PyGame’s Surface.get_at() function. Also, mtPaint is great for making images for POV displays.

  2. Willilam says:

    I need help on writing the code. C programming never been my strong point. I tried using python but ran into a dead end.

    • It seems that you are a new to programming. So you should not start with this example. Instead you should start with something simpler like the basic sketches. Once you understand them you might consider the basic effects. Then work your way to the generator code. Otherwise this is probably way to hard for you. A clear indicator for code that is to hard for you is if you are not able to ask specific questions. Please try the basic stuff until you get to the point where you can ask for specific parts that you do not understand.

      • Willilam says:

        Thanks for the help I finally got it to work. I got to the point where I can do it all on notepad. This was a great learning experience for me.

  3. Paul says:

    I have downloaded and copied”MsTimer2″ and MsTimer2.h in all directories of arduino IDE,I could think of, but keep getting an error “MsTimer2” has not been declared in POV generator sketches.

    Any Suggestions?

    Thanks

    PBC

    • Hmm, this is strange. On my computer Arduino resides in the directory “arduino-1.0”. The MsTimer2 library then goes to “arduino-1.0/libraries”. Thus the files MsTimer2.cpp and MsTimer2.h will be stored in “arduino-1.0/libraries/MsTimer2”.

      Did you copy it into the library directory before you started the IDE? It seems as if the IDE will pick up libraries only at startup. Thus you might have done everything right and the IDE tricked you to believe otherwise.

      BTW: did I mention that IMHO the IDE is below average 😉

  4. Paul says:

    I have not upgraded to 1.0 yet for fear of potential problems with 1.0 until fully debugged. I am still using 022. Does/should the squetch work on 022? Or only on 1.0

    Thanks

  5. It also works with 022. I only referenced to the 1.0 directories because I use 1.0. The sketch generator was originally developed for 022 and I did not change it for 1.0.

    • Paul says:

      You are correct. It worked after rebooting the 022 IDE after copying MsTimer2 to Library

      Thank you

      PBC

  6. Piotr Kula says:

    That looks awesome!

  7. Äd Franzis says:

    Very nice project. Fantastic idea! Thank you very much.
    I converted the code to python3 and it works fine.
    Here is the code. I included in the code as comment, how to include PIL.
    I hope, the format of the code will not be destroyed, otherwise I will be looking for another method to post it.

    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    #
    ###############################################
    #
    # POV Generator
    #
    ###############################################
    #
    #
    # http://www.blinkenlight.net
    #
    # Copyright 2011 Udo Klein
    # Convertation to python 3: Äd Franzis
    #
    # 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
    # (at your option) 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/

    import sys
    from PIL import Image #for installation, see: https://pillow.readthedocs.io/en/stable/installation.html

    # double check that there is at least one argument (the file name)
    if len(sys.argv) != 2:
    print(“Please specify a source filename”)
    sys.exit(-1)

    # determine source file name
    sourcefilename = sys.argv[1]
    # strip extension
    name = sourcefilename.split(“.”)[0]

    # let PIL determine the proper graphics file type
    image = Image.open(sourcefilename)

    # convert image to black and white
    image = image.convert(“1”)

    # get hold of image size
    (xsize, ysize) = image.size
    # ensure height is 20 pixels (=number of LEDs)
    if ysize != 20:
    print(“Image height must be 20 pixels but is {0} pixels”.format(ysize))

    # output common start of program
    print(“””#include
    #include
    #include

    uint8_t const pov_pattern[] PROGMEM = {“””)

    # for each colum in the picture output 3 bytes
    # that will be copied into the LED output ports
    for x in range(0, xsize):
    line = ” 0b”
    for y in range(0, ysize):
    if y==6 or y==12:
    line = line+”, 0b”
    if image.getpixel((x, ysize-1-y)) != 0:
    line = line + “0”
    else:
    line = line + ‘1’

    # add a comment that makes it easier to see
    # the intended meaning of the 3 bytes
    line = line + (“, // line {0:>3}: “.format(x+1))

    for y in range(0, ysize):
    if image.getpixel((x, ysize-1-y)) != 0:
    line = line + “.”
    else:
    line = line + ‘X’

    print(line)

    # output common end of program
    print(“”” };

    void blink() {
    static uint16_t index = 0;

    PORTC = pgm_read_byte(pov_pattern+(index++));
    PORTB = pgm_read_byte(pov_pattern+(index++));
    PORTD = pgm_read_byte(pov_pattern+(index++));

    if (index >= sizeof(pov_pattern)) { index = 0; }
    }

    void setup() {
    DDRD = 0b11111111; // set digital 0- 7 to output
    DDRB = 0b00111111; // set digital 8-13 to output
    DDRC = 0b00111111; // set digital 14-19 to output (coincidences with analog 0-5)

    MsTimer2::set(2, blink);
    MsTimer2::start();
    }

    void loop() { }
    “””)

    Greetings
    Äd

  8. Äd Franzis says:

    Hello again,
    yes, the format got messed up. ;(
    I’m still not at github, but I posted the Python3 code of the POV Generator at pastbin: https://pastebin.com/yM1kADi0 (unfortunately accessible only for 1 year; maybe, Udo, you would like to copy the code into you blog and/or in your github):
    Greetings,
    Äd

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.