Friday, January 20, 2017

Raspberry Pi Console Matrix Screensaver

Overview




While developing on the Raspberry Pi I have long wished to have a screen saver on the console rather than the screen just going blank. One option is to use xscreensaver but I like cmatrix (Console Matrix - a "matrix the movie" like display usually run via the CLI). If you do want to try xscreensaver then you can install using:

sudo apt-get install xscreensaver


Install cmatrix


For those that want to go down the cmatrix path, this is the way to do it. Start by installing cmatrix if you don't already have it.

sudo apt-get install cmatrix

To run cmatrix just type the following in Terminal:

cmatrix

You have the following command line options:

    -a: Asynchronous scroll, more like the movie/original screensaver
    -b: Partial bold text mode
    -B: All bold text mode
    -f: Force the linux $TERM type to on
    -l: Linux support for linux console matrix.fnt font
    -n: No bold text mode
    -o: Use "old-style" scrolling - less like the movie, more like the Win/Mac screensaver.
    -s: "Screen saver" mode.  Any key aborts (default is "eye candy" mode, must abort with control C)
    -x: X window mode, use if your xterm is using mtx.pcf
    -u [update]: Update delay (0-9, default 4).
    -C [color]: Use color for matrix (default green).

While cmatrix is running you can press various keys to adjust the display.

    a: Toggle asynch mode
    b: Enable partial bold text
    B: Enable all bold text
    n: Disable bold text
    0-9: Change update delay

You can change the text colour using the following symbols :

    ! – red
    @ – green
    # – yellow
    $ – blue
    % – magenta
    ^ – cyan
    & – white
    ) – black

Making cmatrix a Screen Saver


At the moment you have to type cmatrix every time you want it to run which isn't really a screensaver. The trick is to combine cmatrix with GNU screen.  Screen enables you to run processes within a “terminal tty instance”. The install command is as you would expect:

sudo apt-get install screen

Type screen to run it and screen -list to see all the screen instances that you have running. Before you do that we need to create the screen configuration file. This will allow us to get the screen saver functionality (more or less).

When it first runs, screen will check its configuration file (.screenrc) in your home directory first. This is normally a hidden file but you can see it if you use:

ls -a

If you have an existing file then edit that, if not create a new one in your home directory.

nano .screenrc

Within the .screenrc file you want to add the following lines:

blankerprg cmatrix -BC blue -s
idle 60 blanker

You can obviously use whatever options you want for cmatrix, these are just the ones I like. Credit to K. Mandla for working this out. Note that blanker is the invocation and blankerprg is the “variable” it starts. You can replace cmatrix with something else if you get board with the matrix (e.g. tty-clock -r).

Idle is a command that is run after the specified number of seconds of inactivity is reached. This command will normally be the blanker command to create a screen blanker, but it can be any screen command. You can change this from 60 seconds if you want.

If you now type "screen" in Terminal then after 60 seconds of no activity cmatrix should run.

We almost have a fully functional screen saver. The last step is to get screen to run automatically whenever the Raspberry Pi reboots. I thought this would be easy but it took me 3 attempts to find a solution that worked. The upside is I learnt a lot about the various start up scripts and screen options.

Auto run GNU Screen on Boot - 1st Attempt


As always in linux there are lots of ways to do anything. I thought the easiest approach would be to edit the rc.local file.

The script /etc/rc.local is for use by the system administrator. It is traditionally executed after all the normal system services are started, at the end of the process of switching to a multiuser runlevel. You might use it to start a custom service, for example a server that's installed in /usr/local.

In order to have a command or program run when the Pi boots, you can add commands to the rc.local file. On your Pi, edit the file /etc/rc.local using the editor of your choice. You must edit with root, for example:

sudo nano /etc/rc.local

Add commands below the comment, but leave the line exit 0 at the end, then save the file and exit. Your file will look something like this:

#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

# Print the IP address
_IP=$(hostname -I) || true
if [ "$_IP" ]; then
    printf "My IP address is %s\n" "$_IP"
fi 
screen -dmS "launch"
exit 0

The only problem is that rc.local runs early in the boot process, before a terminal has been created. So you can only use screen to create a detached screen (i.e. not visible). You can run processes in this detached screen but that is unhelpful for what we are trying to do. Interestingly when researching this I found that the code that should print out your IP address on boot no longer works because rc.local runs before the network is setup.

You will notice in the video that I have a Sense Hat installed on my Pi, so I do use the rc.local file to start a python program which runs on the Sense Hat as a sort of "screen saver". Actually I just like the look of it. The program is a looping version of Conway's Game of Life. Since I can't use rc.local to start an attached screen, I replaced the screen -dmS "launch" line with:

sudo python /home/pi/python_code/intro/SenseConway.py &
Notice the & after the line launching the python app?

WARNING:: If your command runs continuously (perhaps it has an infinite loop) or is likely not to exit, you must be sure to fork the process by adding an ampersand (&) to the end of the command. 

Otherwise, the script will not end and the Pi will lock up - I admit I made this mistake at one stage.  Not even CTRL C will get you out of this. In the end I fixed the issue by swapping the SD card with a new version of Raspbian and mounting the old SD card with a USB Card Reader and then editing the rc.local file. You will find the mounted SD Card under media which wasn't an obvious place and took a bit of searching. You have to use chown to change ownership of the rc.local file to the current yser, which allows you to edit it.

The ampersand allows the command to run in a separate process and continue booting with the process running.

Also, be sure to reference absolute filenames rather than relative to your home folder; for example, /home/pi/myscript.py rather than myscript.py.

Auto run GNU Screen on Boot - 2nd Attempt


An alternative to editing rc.local is to use cron. Cron is the general name for the service that runs scheduled actions. crond is the name of the daemon that runs in the background and reads crontab files. Edit your crontab file using:

sudo crontab -e
A crontab is a file containing jobs in the format:

minute hour day-of-month month day-of-week  command
You can also use cron to schedule a job to run at reboot using @reboot. Perfect I thought, except you run into the same problem as using rc.local, using @reboot you can only add detached screens.

I then tried getting cron to start screen 60 seconds after reboot, using something like:

@reboot sleep 60 && my_script.sh
But this didn't work either.

Auto run GNU Screen on Boot - 3rd Attempt


 ~/.profile (historically referred to as ./bash_profile or ~/bash_login) is another shell script run at login time. This is the script that ended up doing the trick for me. It is obviously run after boot has completed and the terminal shell is in place.

I added the following line to the end of my .profile file. "launch" is just the name of the screen session.

[ -z "$STY" ] && screen -Rd "launch"

Note - STY: Alternate socket name. If screen is invoked, and the environment variable STY is set, then it creates only a window in the running screen session rather than starting a new session.

If .profile hadn't worked, then I would have tried .bashrc - there is always another way with linux!

.bashrc is a shell script that Bash runs whenever it is started interactively. A common thing to put in .bashrc are aliases that you want to always be available.

That's it! Well not quite...

Stop Screen Blanking - 1st Attempt


You will now have cmatrix automatically running as your screen saver but after a preset time your screen will still go blank! Back to the drawing board. If you ask Dr Google you will find a number of proposed solutions.

The first thing I tried was editing the /etc/kbd/config file.

sudo nano /etc/kbd/config
I made the following two changes to the file:

# screen blanking timeout. monitor remains on, but the screen is cleared to
# range: 0-60 min (0==never) kernels I've looked at default to 10 minutes.
# (see linux/drivers/char/console.c)
BLANK_TIME=0 (Was 30)

# Powerdown time. The console will go to DPMS Off mode POWERDOWN_TIME
# minutes _after_ blanking. (POWERDOWN_TIME + BLANK_TIME after the last input)
POWERDOWN_TIME=0 (Was 30)
Then rebooted, but CLI screen blanking still occurred.

Stop Screen Blanking - 2nd Attempt


The next thing I tried was editing:

sudo nano /etc/lightdm/lightdm.conf
In that file, look for: [SeatDefaults] and insert this line:

xserver-command=X -s 0 dpms
Try rebooting again. This didn't work for me.

Stop Screen Blanking - 3rd Attempt


Then I tried adding setterm -powersave off -blank 0 to our old friend rc.local (see autorun GNU screen - 1st attempt above).

This worked!

No comments:

Post a Comment