Wednesday, July 8, 2015

Launch Python script at boot

Sometimes we could need to start a Python script when Raspberry Pi boots up in a full autonomous way.

RPi could be really useful if used headless, without a direct user interaction (at least when starting the system), so if we created a script for a RPi that needs to be executed at startup, we also need a way to accomplish this task.

In this post I will describe a way to execute a Python script (but this works also for every other kind of software) when booting the RPi.

The best way is to run the script at boot is to launch it as a daemon.

To keep it simple, a daemon is a task executed in background, so the system can run it in parallel with other tasks.

Let's see a small example script:

#!/usr/bin/env python
#-*- coding: utf-8 -*-

import time

i=0
while True:
    i=i+1
    if (i>10): i=0
    time.sleep(10)

This script just increments a variable every ten seconds. Not quite a useful task indeed, but can shows some things that we need.

First of all the beginning row is mandatory. It specify how to execute the script (actually calling it with the Python interpreter).

The second line could be omitted, but some script may have problems without it, so it's better to keep it in the source.

Everything else is just a simple Python script, but note that we have an infinite loop (the while True statement). Without this loop the script will run just one time.

Now save this script. For this post I'm saving as testing.py in  the /home/pi folder.

If you wish, you can check if it works with the command

python testing.py

You can of course stop it with CTRL-C.

As we specified in the first line of the script that this is a Python script, we can run it without invoking the interpreter. Let's make it executable with the following command:

sudo chmod 755 testing.py

and let's try to launch the script directly:

./testing.py

The ./ before the script name is needed by Linux if you are executing something in the current folder.
Now create a new text file named testing.sh (it's better to keep the same name of the python script, just for clarity) and fill it with the following code:

#!/bin/sh

### BEGIN INIT INFO
# Provides:          testing
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: testing boot execution
# Description:       example for launchinga python script at boot time as a daemon
### END INIT INFO

# Change the next 3 lines to specify where you install your script and what you want to call it
DIR=/home/pi
DAEMON=$DIR/testing.py
DAEMON_NAME=testing

# Add any command line options for the daemon here
DAEMON_OPTS=""

# This next line specify what user the script runs as.
DAEMON_USER=pi

# The process ID of the script when it runs is stored here:
PIDFILE=/var/run/$DAEMON_NAME.pid

. /lib/lsb/init-functions

do_start () {
    log_daemon_msg "Starting system $DAEMON_NAME daemon"
    start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS
    log_end_msg $?
}
do_stop () {
    log_daemon_msg "Stopping system $DAEMON_NAME daemon"
    start-stop-daemon --stop --pidfile $PIDFILE --retry 10
    log_end_msg $?
}

case "$1" in
    start|stop)
        do_${1}
        ;;
    restart|reload|force-reload)
        do_stop
        do_start
        ;;
    status)
        status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $?
        ;;
    *)
        echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}"
        exit 1
        ;;
esac
exit 0

This will run our script as a daemon. You can change some variable to adapt to other scripts (just look at the comments). Note that for some script you should change the daemon user from pi to root to make it work.

Next we need to copy this script to the init.d folder and make it executable:

sudo mv ./testing.sh /etc/init.d
sudo chmod 755 /etc/init.d/testing.sh

You can start/stop/check/restart the daemon using the following commands:

sudo /etc/init.d/testing.sh start
sudo /etc/init.d/testing.sh stop
sudo /etc/init.d/testing.sh status
sudo /etc/init.d/testing.sh restart

but of course we want to make it start automatically, so just type one last command:

sudo update-rc.d testing.sh defaults

and reboot the Raspberry Pi. You can check if your script is working by using the status command above.

That's all, but of course remember that if an error occours in your script, you need to restart the daemon manually.

Also note that when you run something as a daemon, no output text will be generated, so you may need some logging to eventually keep track of information from your script.

13 comments:

  1. Hi Carlo, many thanks again. I followed your instructions, and after reboot I got the error code "[Fail] startpar: service(s) returned failure: testing.sh ... failed!" Do you have any advise, what I have made wrong?

    ReplyDelete
    Replies
    1. pi@raspberry ~$ ./testing.py works fine.

      sudo /etc/init.d/testing.sh start
      sudo /etc/init.d/testing.sh stop
      sudo /etc/init.d/testing.sh status
      sudo /etc/init.d/testing.sh restart
      does not work: "sudo: unable to execute /etc/init.d/testing.sh: No such file or directory."
      But the file "testing.sh" is located in /etc/init.d

      Delete
    2. Can the file be executed? You shoul have done with the command "sudo chmod 755 testing.sh"...

      Delete
    3. ...yes - I've did it. As user "pi" may be thats my mistake?

      Delete
    4. That should not matter as the sudo command gives root permissions. I really do not know what to think... If the file is in the correct folder and it's executable then it should work.
      Try to copy (or create again) that file in the local folder (where you put the testing.py file), make it executable and try to launch it form there (using ./testing.sh), just to see if it can run...

      Delete
    5. ok:
      I recreated "testing.sh" in /home/pi -> ok
      I tried "sudo chmod 755 /home/pi/testing.sh" -> obviously ok, no error message
      Then "./testing.sh" -> error message: "-bash: ./testing.sh: /bin/sh^M: Defekter Interpreter: Datei oder Verzeichnis nicht gefunden"
      I am stumped....

      Delete
    6. Ok, it seems a char coding issue. We can try in two ways: you can type the whole script, without copying from the blog, or just wait that i upload my script, so you can download it and try to execute...

      Delete
    7. ... I appreciate your 2nd proposal ;-)

      Delete
    8. Donwload from the following link:http://www.mascal.it/public/testing.sh
      Note that you can download directly from the RPi typing: wget http://www.mascal.it/public/testing.sh

      If also this doesn't work, we can check the char encoding of your debian installation...

      Delete
    9. Hi Carlo,

      just back from a canoe-trip without any mobilephone ;-)))) I just tried your file - It seems to work properly!
      I will try to learn, what happens exactly with the code and I'am sure, I will come back with a few questions: ok, testing.py is running automtically after reboot. But what exactly in which file do I have to write to get Automatically Answering Machine powered by Whatsapp: For example: I send "Whats your time, raspberry?" via Whatsapp, and Raspberry should answer with his system time like "Hi +4912345678, it's now 20:42!"..... follow up, warm regard!

      Delete
    10. Glad to hear that now the script works!
      Of course testing.py it's just an extremely simple demo to show how to start a python script at boot. For what you wish to do with the Raspberry, you should take a look at the post about the whatsapp parser in this blog as I told you some times ago.
      Shortly, you can check the incoming message if it contains the words "what" and "time" and if they are both inside the message just answer with the current time.

      Delete
    11. yes, I thought to follow your advise, that's why I yesterday evening entered a new post in your blog http://rpihome.blogspot.de/2015/01/using-whatsapp-for-home-automation.html. I still does not know, in which file I have to put the code from your parser blog.... Must I generate a new file called "parser.py", copy your code into that file, make that file exessible and runable by the daemon? Please refer to my last post yesterday evening. Again, thank you very much for your patience!

      Delete
  2. It may be that your testing.sh file was edited under Windows? This will change line endings from the unix \n to Windows style \n\r and this results in a : No such file or directory error when you try to execute it with ./testing.sh

    You can remove all \r with sed...

    sed -i 's/\r//' testing.py

    ReplyDelete