Displaying images on 8×8 RGB LED displays

Just a short proof of principle thing, badly coded and terribly slow, but it works :-)

Images must be converted to the RAW PPM format using e.g. GIMP. If you think of using ImageMagick and convert: doesn’t work properly !

Here is an example of a small image in this format.

P6
# feep.ppm
4 4
15
0  0  0    0  0  0    0  0  0   15  0 15
0  0  0    0 15  7    0  0  0    0  0  0
0  0  0    0  0  0    0 15  7    0  0  0
15  0 15    0  0  0    0  0  0    0  0  0

Short description of the lines starting from 1:

  • ASCII: Identifier
  • ASCII: Comment
  • ASCII: x-resolution space y-resolution
  • ASCII: max color value
  • HEX: sequential data: R,G,B,R,G,B …

If you look at such a RAW PPM image with a hex editor it might look like this:

00000000  50 36 0a 23 20 43 52 45  41 54 4f 52 3a 20 47 49  |P6.# CREATOR: GI|
00000010  4d 50 20 50 4e 4d 20 46  69 6c 74 65 72 20 56 65  |MP PNM Filter Ve|
00000020  72 73 69 6f 6e 20 31 2e  31 0a 38 20 38 0a 32 35  |rsion 1.1.8 8.25|
00000030  35 0a 00 00 ab 00 00 ab  00 00 a4 00 00 43 07 07  |5............C..|
00000040  4a 00 00 a0 00 00 ab 00  00 ab 00 00 ab 00 00 ab  |J...............|
00000050  03 03 86 42 3f 35 4c 4c  4a 00 00 64 00 00 ab 00  |...B?5LLJ..d....|
00000060  00 ab 00 00 ab 00 00 ab  02 02 89 dc b2 20 b5 9b  |............. ..|
00000070  43 08 08 49 00 00 ab 00  00 ab 00 00 ab 00 00 aa  |C..I............|
00000080  43 43 74 f3 f3 f3 f4 f4  f4 2e 2e 2f 00 00 78 00  |CCt......../..x.|
00000090  00 ab 00 00 ab 03 03 78  cb cb cb fd fd fd ff ff  |.......x........|
000000a0  ff a2 a2 a2 04 04 15 00  00 a9 07 05 a5 7d 65 3a  |.............}e:|
000000b0  d4 d0 c0 f9 f9 f9 ff ff  ff bb ae 83 2c 24 09 07  |............,$..|
000000c0  05 a3 a5 84 4b ff d1 31  c0 9d 1d ee ed eb d8 d8  |....K..1........|
000000d0  d8 ce a7 26 fa ca 26 79  63 6f 33 27 86 73 59 5b  |...&..&yco3'.sY[|
000000e0  a0 7a 3a 12 0c 62 00 00  69 80 5e 3d 65 4d 67 04  |.z:..b..i.^=eMg.|
000000f0  03 a8                                             |..|
000000f2

( space = 0x20 (hex), n = 0x0A (hex) )

tux using 8x8 pixel This is an image of ... TUX (of course) !

Such an image is easy to read and process in Perl:

#!/usr/bin/perl -W -T

use strict;
use Device::SerialPort;
use Time::HiRes qw(sleep);

my $separator = "n";  # 0x0A
my $start_byte = "n"; # 0x0A
my $stop_byte = "r";  # 0x0D
my $image_id = "P6";   # ID for .ppm image
my $color_scaler = 8;

my $port = "/dev/ttyUSB0"; # <-- find right port !
my $link = Device::SerialPort->new($port) || die("could not open port: $port - $!"); # <-- match to right com port

$link->databits(8);
$link->baudrate(19200); # <-- match to arduino settings
$link->parity("none");
$link->stopbits(1);
$| = 1; # buffers disabled

open(FILE, "< $ARGV[0]"); # open .ppm image
  my @image = ; # load the data in an array
close(FILE);

my $tmp = $image[0]; # get .ppm ID from image
$tmp =~ s/n//;      # remove n

shift @image; #remove ID line

if ( $tmp ne $image_id ) { print "nnot .ppm image!nn"; exit; }

$tmp = $image[0];
if ( $tmp =~ m/^#.*/ ) { # comment line ?
  shift @image; # remove comment line
}

my $x_res = $image[0];
$x_res =~ m/^(d*) /;
$x_res = $1;
my $y_res = $image[0];
$y_res =~ m/^d* (d*)/;
$y_res = $1;
my $c_max = $image[1];

shift @image; # remove X x Y line
shift @image; # remove color line

if ( ($x_res > 8) || ($y_res > 8) ) { print "n($x_res x $y_res)nimage larger than 8x8nn"; exit; }

print "x_res: ",$x_res,"n";
print "y_res: ",$y_res,"n";
print "c_max: ",$c_max,"n";

@image = split('',$image[0]); # put every single character into a new line --> access it c-style

my $row;
my $led;

for ($row = 0; $row < 8; $row++) {
  for ($led = 0; $led < 8; $led++) {
    $link->write($start_byte);
    $link->write(pack("C",$row));
    $link->write(pack("C",$led));
    $link->write(pack("C",unpack("C",$image[$row*3*8+$led*3+0])/$color_scaler));
    $link->write(pack("C",unpack("C",$image[$row*3*8+$led*3+1])/$color_scaler));
    $link->write(pack("C",unpack("C",$image[$row*3*8+$led*3+2])/$color_scaler));
    $link->write($stop_byte);
    sleep(0.035);
  }
}

The pack() and unpack() functions are used to determine how Perl interprets the raw data. It’s too difficult a thing to explain here, so just a link.

Here’s the code/example: show_ppm.tgz

I updated the code a bit and improved transfer speed quite a lot:

  • upped serial speed to 57600
  • transmit one row in one go, reduce start/stop byte overhead
  • tweaked delay times in Perl

Here’s the improved code: show_ppm_v2.tgz

And another update. Here’s an animation stored in PROGMEM.

Here’s the code: show_ppm_v3.tgz

Related posts:

  1. 128 = 64 x 2 — Extending to larger matrix displays
  2. 8×8 RGB Matrix — 2nd arduino project
  3. Showing live video on my 64-pixel display
  4. Interactively controlled animation on a 64-pixel RGB LED display
  5. Meggy Jr RGB made it into German newspapers

This entry was posted in Arduino., Electronics. and tagged , , , , , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>