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.
Read on to push this one step further.
I did something similar using PyGame’s Surface.get_at() function. Also, mtPaint is great for making images for POV displays.
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.
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.
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 😉
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
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.
You are correct. It worked after rebooting the 022 IDE after copying MsTimer2 to Library
Thank you
PBC
That looks awesome!
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
Looks like the format gets messed up. Maybe you want to put it into some repo (e.g. github) and just submit a link?
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