Ahoy!
I’m now able to run the sample scripts delivered with Blinkt! from the command line. Hitting [Ctrl]+[C]
terminates the script (I mostly used larson_hue.py
) the “safe” way, which includes switching off all LEDs. When trying to autostart the script upon boot by using systemd
, however, the LEDs keep on showing the last pattern after stopping the script and even after a halt
of the Pi.
Here is my service file:
[Unit]
Description=Background process for the Pimoroni Blinkt! LED stripe
[Service]
Type=simple
User=mixtile
ExecStart=/home/mixtile/Pimoroni/blinkt/larson_hue
ExecStop=/bin/kill -SIGINT $MAINPID
ExecReload=/bin/kill -SIGINT $MAINPID
IgnoreSIGPIPE=false
KillMode=control-group
Restart=on-failure
RemainAfterExit=no
SendSIGHUP=yes
[Install]
WantedBy=multi-user.target
Alias=blinkt.service
…whereas mixtile
is the standard user on my Pi. And this is the shell script, which in turn starts the Python script supplied by you. Please note that contatenating the two lines of shell script with &&
and using them as ExecStart
parameter in the service file does not work:
#!/bin/sh
. /home/mixtile/.virtualenvs/pimoroni/bin/activate
python3 /home/mixtile/Pimoroni/blinkt/examples/larson_hue.py
Without the ExecStop
and ExecReload
statements, stopping the service leaves me with a still runninng Python script. Querying the service status after stopping gives me this:
May 11 17:36:01 pizero systemd[1]: Started blinkt.service - Background process for the Pimoroni Blinkt! LED stripe.
May 11 17:36:06 pizero systemd[1]: Stopping blinkt.service - Background process for the Pimoroni Blinkt! LED stripe.
May 11 17:36:06 pizero systemd[1]: blinkt.service: Deactivated successfully.
May 11 17:36:06 pizero systemd[1]: Stopped blinkt.service - Background process for the Pimoroni Blinkt! LED stripe.
May 11 17:36:06 pizero systemd[1]: blinkt.service: Consumed 3.688s CPU time.
Replacing SIGINT
with SIGTERM
does not help.
So: Is there a possibility to stop the script “safely”, so that all LEDs are shut down upon service stop?
Look at the script and what it does when you hit CTRL-c. Then write a script that does exactly that after killing the main-script. Use that script as ExecStop.
Another option: write a simple ExecStop-script that creates a /tmp/foo-whatever file. From the main script, check this script from within the loop and exit gracefully when the file turns up.
When running the script from the shell and stopping it by using [Ctrl]+[C]
after a couple of seconds, I get this:
mixtile@pizero:/lib/systemd/system $ /home/mixtile/Pimoroni/blinkt/larson_hue
^CTraceback (most recent call last):
File "/home/mixtile/Pimoroni/blinkt/examples/larson_hue.py", line 49, in <module>
blinkt.show()
File "/home/mixtile/.virtualenvs/pimoroni/lib/python3.11/site-packages/blinkt/__init__.py", line 107, in show
_write_byte(b)
File "/home/mixtile/.virtualenvs/pimoroni/lib/python3.11/site-packages/blinkt/__init__.py", line 57, in _write_byte
time.sleep(sleep_time)
KeyboardInterrupt
The only thing, which is apparently different from terminating the process by sending a TERM
or INT
signal, is the nature of the interrupt: [Ctrl]+[C]
raises a KeyboardInterrupt
. This puzzles me 'cause a keyboard interrupt in fact sends an INT
signal to the running process:
Pressing Ctrl-C while a Python script is running sends a SIGINT (signal interrupt) to the Python interpreter, telling it to immediately stop whatever it’s doing . This SIGINT signal effectively pauses and interrupts the execution of the program. It raises a KeyboardInterrupt exception within the Python script.
Theoretically, upon arrival of an INT
signal, the _exit()
method in blinkt.py
should be called, but for whatever reason, this is not the case when the sgnal comes from systemd
.
Got it: It’s necessary to handle the corresponding signals by using a handler, as shown in this tutorial.
The start of the Python script now reads as follows:
#!/usr/bin/env python
import colorsys
import math
import time
import signal
import sys
import blinkt
FALLOFF = 1.9
SCAN_SPEED = 4
def interrupt_handler(signum, frame):
print(f'Handling signal {signum} ({signal.Signals(signum).name}).')
blinkt._exit()
sys.exit(0)
blinkt.set_clear_on_exit()
signal.signal(signal.SIGINT, interrupt_handler)
signal.signal(signal.SIGTERM, interrupt_handler)
Now stopping the script from systemd
works neatly. :)
1 Like