Monday, September 30, 2013

Raspberry Pi - turn off HDMI after a specified period of time using xscreensaver

for my application I've set up xscreensaver ('sudo apt-get install xscreensaver' and reboot, use 'xscreensaver-demo' to configure it) to blank my 7" eGalax touchscreen after 10 minutes, but when it's left unattended for 12 hours the backlight is still on. The screen sleeping options don't work in xscreensaver, so I rolled my own solution.

I'd appreciate suggestions if it isn't the best solution. It should work for others too.

Step 1 - Create scripts that manually turn off and on the HDMI

- create file hdmi_off.sh
CODE: SELECT ALL
#!/bin/sh
#hdmi_off.sh - shell script to turn HDMI off - should work universially
tvservice -o


- create file hdmi_on.sh (make sure to set executable permissions on both - 'chmod 755 *sh')
CODE: SELECT ALL
#!/bin/sh

#hdmi_on.sh - custom script to turn HDMI on

#get the current tv state
tvstate=`tvservice -s | grep HDMI | wc -l`

#only turn on if its not already on
if [ "$tvstate" -eq 0 ]
then
   tvservice --explicit="DMT 87"
   fbset -depth 8
   fbset -depth 16
   xrefresh
else
   echo "HDMI already on";
fi

exit;


You should make sure these scripts work from the command line for you - all my other script does is call them, so if these do not work it will not work. Stop here until you get them working. You'll have to search the forums to find how to make your screen come on, and start displaying stuff.

Step 2 - Create a perl script to detect the screensaver state

Basically we can use "xscreensaver-command -watch" to follow text output of what xscreensaver is doing. What I do is detect when the screensaver becomes active, then start a timer to determine when to turn the screen off. It is a lot simpler if you just want the screen to power off immediately. But I wanted this dual off approach as it takes the screen longer to come on when its fully off.

CODE: SELECT ALL
#!/usr/bin/perl

#check_screen_state.pl - perl script to monitor screen state, and turn HDMI on or off
use strict;
use warnings;
use threads;
use Thread::Queue;
$|=1;

my $counter=-1;
#time in seconds
my $time_to_power_off_screen = 1200; #20 minutes
my $hdmi_off_command = "/home/pi/screensaver/hdmi_off.sh";
my $hdmi_on_command = "/home/pi/screensaver/hdmi_on.sh";

#code to run a shell command in a thread, and get output from it
sub pipeCommand {
    my $cmd = shift;
    my $Q = new Thread::Queue;
    async{
        my $pid = open my $pipe, $cmd or die $!;
        $Q->enqueue( $_ ) while <$pipe>;
        $Q->enqueue( undef );
    }->detach;
    return $Q;
}

my $pipe = pipeCommand(
    'xscreensaver-command -watch |'
) or die;

print_("Starting, waiting on input.\n");
while( 1 ) {
    if( $pipe->pending ) {
        my $line = $pipe->dequeue or last;
        print_("$line");
      if ($line =~ m/^(BLANK|LOCK)/) {
         $counter = $time_to_power_off_screen;
      } elsif ($line =~ m/^UNBLANK/) {
         if($counter <= 0){
            print_( "turn on\n");
            print_( `$hdmi_on_command `);
         }else{
            print_("screen already on\n");
         }
         $counter=-1;
       }
    }
    else {
        sleep 1;
      if($counter >0){
         $counter--;
      }
    
      if($counter == 0){
         $counter=-1;
         print_( "turn off acter $time_to_power_off_screen seconds\n");
         print_(`$hdmi_off_command`);       
      }
    }
}

sub print_{
   print "[".localtime(time) .  "] @_";
}


Step 3 - make the script run all the time

Make an inittab entry to make the perl script run all the time ('sudo vi /etc/inittab' and add the line to the bottom
CODE: SELECT ALL
hdmi:2345:respawn:/usr/bin/perl /home/pi/screensaver/check_screen_state.pl > /home/pi/screensaver/hdmi.log

Save the file, exit the editor, and then type 'sudo init q' to make the system re-read the file. 'ps -ef' should now list your script.

1 comment:

Unknown said...

Please take a look here-

https://github.com/ramses0/xscreensaver-pi-hdmi

I started with Simon's idea and tightened it up a bit, made a package for debian.

cd xscreensaver-pi-hdmi && dpkg-buildpackage && sudo dpkg -i ../xscreensaver-pi-hdmi_*.deb

You'll need some extra packages prior (maybe apt-get install build-essential) or you can just copy/run the command directly from the bin directory, as it's a single self-contained script.

I'll try to get in touch with Simon directly and see where to take it.

--Robert