LowEndBox - Cheap VPS, Hosting and Dedicated Server Deals

Raspberry Pi Photo Frame: The Major Slideshow Options + Thermal Monitoring Script

Digital Photo Frame

I mentioned in a previous post that I created a Raspberry Pi-powered digital photo frame for our living room.  Every hour, it cycles through 240 photos with a 15-second delay between each.  In days of yore, I used fbi, the framebuffer imageviewer on an R Pi 3.  I wanted to upgrade to a later version of Raspberry Pi OS (which is a Debian reskin, previously called Raspbian), and decided it was time to move to an R Pi 5.

fbi still works on Raspberry Pi OS (aka Bookworm), but the tvservice command to turn HDMI on/off at night does not, so I had to reengineer that, as discussed.  This necessitated moving to an X-window-based setup, and I thought I’d list some of the major options for doing a full-screen slideshow on Raspberry Pi or Linux

The Surrounding Infrastructure

In this environment, I have a “playlist” of images that is created every hour, with some code to put pictures on a 1920×1080 background with the phot date and interleave various seasonal and inspirational posters.  I won’t be covering that, except to say that you can do amazing things with ImageMagick.

The key is that

  • Every hour I assume a new playlist of 240 images.  Some are just existing full-screen JPGs of famous monuments, inspirational quotes, etc., and others are photos placed on backgrounds.
  • For the latter, I create a UUID-named JPG with ImageMagick
  • At the top of the hour, I kill the existing slideshow and restart it with that hour’s playlist

Software Options

ImageMagick animate: This would be something like

animate -pause 15 *.jpg

I didn’t choose this one as my playlist (a list of files) points to files in many directories (sorted by date, theme, etc.).  And because I create many of these files dynamically, they have names like


That’s 65 characters, and 240*65 = 15,600 which is greater than the maximum length of the command line in Linux (I think it’s 4K).  It doesn’t actually matter if I say “animate…” with each file name explicitly or say “*.jpg” with all files in a directory because the shell will expand that wildcard.

Eye of Gnome (eog): This would be…eh, never mind.  I installed it but

  • the man page is very brief
  • eog –help discusses flags that don’t appear on the man page
  • the project’s home page has no docs whatsoever

I guess it’s too l33t for me.

mplayer: I didn’t actually try this one, but I saw it mentioned while I was researching.  mplayer is, of course, a movie player but you can coax it into being a slideshow app:

mplayer mf://*.jpg -mf fps=10

feh: Now we’re getting into serious competitors.  feh is a “light-weight, configurable and versatile image viewer” that’s aimed at command-line users.  You can use it as a slideshow like this:

feh --verbose --geometry 1920x1080 --borderless --hide-pointer \
--slideshow-delay 15  -f file_list.txt

This was my first choice, but for whatever reason, feh frequently hung on my RPi.  The screen would go blank and it’d stay that way until I recycled it.  I tried to narrow this behavior down to a particular photo, but it was random and all of my photos/generated images cycled perfectly with fbi and the next app.  Also, I was disappointed that –verbose wasn’t actually verbose.  I was calling feh from a script called from cron and redirecting stdout and stderr to an outfile, but it was always empty.

To be fair, this was feh 3.9.1-2 from the R Pi OS package manager.  There’s a later version (3.10) on the feh web site, so perhaps these issues are resolved.

However, I’ve since discovered…

The Winner: Impressive

Impressive is a “presentation tool with eye candy” according to the man page.

Impressive is a simple presentation program that displays slideshows of PDF documents, image files or video files. Rendering is done via OpenGL, which allows for some “eye candy” effects.

It’s hosted on SourceForgeshudder…but that’s about the only thing wrong with it.

Here’s an example command line:

impressive --verbose --cache memory --wrap --auto 15 \
--fullscreen --geometry 1920x1080 @/some/path/to/filelist.txt

The –auto flag determines how many seconds before each “slide” (in my case image) is auto-advanced.  I probably don’t need –wrap which means “after the last slide, start over”.  Note that “@” in the file list.

I think I’m only using about 1% of Impressive’s…wait for it…impressive list of features.  You can do very sophisticated presentation control with its built-in scripting language, which is Python.  I’m probably doing the simplest thing possible with this app.

One nice thing I like with Impressive is that instead of simply switching to each issue, it uses random transitions: wipes, fades, etc.  Eye candy!  Of course, I could pick which transitions, etc. but I just say “use them all” by not specifying.

Impressive does emit some warnings from the Pillow library on startup about deprecated APIs.  If you look at the app’s news, you’ll see these are fixed in a later version (0.13.2) of Impressive, but I’m using 0.13.1 from the package manager.  They’re just warnings and don’t impact the app’s functioning.

Caching in Impressive

Initially when I was using Impressive, it would lock up.  It also drove the CPU to 100%.  Several times I’d come into the living room and see it frozen mid-transition.  This made me sad.

I was about to abandon it, when I noticed the –cache memory option.

If you have no caching, Impressive must pre-render each page before displaying.  Remember it’s presentation software, so imaging you have a .PDF.  Impressive would need to parse the next page, render how it’s supposed to look on the page, etc.

By default, Impressive caches on disk, which is fine for a typical laptop, but on the R Pi with a Micro SD card, it was thrashing the disk.  The –cache memory option notes on the man page that it is “the fastest  method,  but it  requires  very large amounts of memory (about 3 MiB per page at 1024×768 resolution).”

This may be a bit naive, but here’s my thought process:

  • 1024×768 = 786,432 pixels
  • 1920×1080 =2,073,600 pixels
  • So 1920×1080 is about 2.6 more size than 1024×768.  Call it 3x.
  • So roughly, each 1920×100 will take 3x more RAM than a 1024×768, or perhaps 9MiB.

With 240 images per hourly “presentation,” that’s (240*9MiB = ) 2.16GB.  My R Pi 5 has 4GB RAM so this isn’t a problem.

Impressive actually tells you how much memory it uses.  After invocation and render, it outputs:

Background rendering finished, using 1423.8 MiB of memory.

That’s a little under 6MB per image.

With Impressive running, I’ve still got plenty of head room:

# free -m
               total        used        free      shared  buff/cache   available
Mem:            4042        2166         879         101        1170        1876
Swap:             99           0          99

Thermal Impact

Unlike previous R Pis, the 5 is a bit more heat-sensitive.  You won’t actually cook your board by running a ton of work, but the board will cycle down performance until things get cool.  This thermal stress isn’t good for it, and of course you’re getting lower performance.

I went with an aluminum heat sink case instead of a fan because I suspected that a fan running 24×7 would eventually need to be replaced, and I wasn’t planning on driving the Pi that hard.

But that I thought…actually, every hour I’ve got Impressive running its caching and rendering, plus my ImageMagick work in the background, plus normal OS stuff, driving the wifi and display…am I stressing the board during periods when all that is running?

I created a cron job to dump temperature info into a file every minute:

/usr/bin/echo "$(/usr/bin/date) $(/usr/bin/vcgencmd measure_temp)" \
>> /pictures/log/temperature.$(date '+%Y-%m-%d').log 2>&1

This creates lines like this in the log file:

Mon 15 Jan 12:10:01 PST 2024 temp=42.2'C

Then I wrote a quick script to analyze the data.

#!/usr/bin/perl -w

my $file = shift || die "no file!\n";
%peak = ();
$peak{'temp'} = 0;

open (IN,$file);
while (<IN>) {
 $info = $temp = $_;
 $temp =~ s/^.*=//;
 $temp =~ s/\'.*$//;
 if ( $temp > $peak{'temp'} ) {
   $info =~ s/ temp=.*$//;
   $peak{'date'} = $info;
   $peak{'temp'} = $temp;
close (IN);
$fahr = sprintf("%.1f",( $peak{'temp'} * 1.8 ) + 32);
# print 115 not 115.0
if ( int($fahr) == $fahr ) { $fahr = int($fahr); }
print ("Peak temp: $peak{'temp'}C (${fahr}F) at $peak{'date'}\n");

For today, my R Pi says:

Peak temp: 47.2C (117F) at Mon 15 Jan 12:02:01 PST 2024



1 Comment

  1. Tim:

    Thank you for this post! Been searching for a solution for a random slideshow for ages. Know that it’s not the focus of the article but would love to know the command you’re using for generating the random playlist in the first place, along with how you get the slideshow to “refresh” on the hour.

    May 19, 2024 @ 8:25 am | Reply

Leave a Reply

Some notes on commenting on LowEndBox:

  • Do not use LowEndBox for support issues. Go to your hosting provider and issue a ticket there. Coming here saying "my VPS is down, what do I do?!" will only have your comments removed.
  • Akismet is used for spam detection. Some comments may be held temporarily for manual approval.
  • Use <pre>...</pre> to quote the output from your terminal/console, or consider using a pastebin service.

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