Utilisant un Raspberry Pi comme enregistreur TV-TNT via un adaptateur DVB‑T et Tvheadend, il m’est déjà arrivé de flinguer un enregistrement (en fait introduire un saut temporel dans l’enregistrement) :
- parce que lors d’une manipulation ou d’un test, j’avais besoin de redémarrer le Pi et que j’ai quelque peu oublié l’enregistrement en cours ;
- parce qu’en trifouillant des branchements derrière la TV, j’ai eu besoin d’éteindre le Pi et que je l’ai débranché de nouveau en oubliant l’enregistrement en cours.
Même si cela n’arrive pas si souvent, j’ai songé à une solution de reconversion des DEL/LED afin d’éviter cela. Je vous explique dans cette dépêche comment je m’y suis pris.
Sommaire
- La solution envisagée
- Voir l’état des LED
- Éteindre et allumer les LED
- Sauvegarder l’état des LED
- Restaurer l’état des LED
- Autoriser un utilisateur standard à modifier l’état des LED
- Créer un script « recording »
- Tester
- Appeler le script en début et fin d’enregistrement
- Tests
- Dépannage
- Références
La solution envisagée
Pour prévenir les redémarrages à distance, on peut imaginer une modification de /etc/motd*
ou /etc/issue
qui prévienne : « ATTENTION, enregistrement en cours ! » Mais pour les débranchements de câbles, on va utiliser les LED afin de prévenir les personnes aux alentours qu’un enregistrement est en cours.
Par défaut, la LED rouge est allumée (témoin power/PWR), la LED verte, elle, s’allume lorsquNil y a de l’activité sur la carte SD (lecture ou écriture). Il est possible de les éteindre ou de les utiliser pour tout autre chose. C’est ce qu’on va faire ici avec les moyens du bord : utiliser une des LED comme témoin d’enregistrement en cours. Pour ma part, en temps normal, je les désactive au démarrage.
Voir l’état des LED
L’état des LED est dans ces répertoires virtuels (kernel filesystem) :
-
/sys/class/leds/led0/
: verte ; -
/sys/class/leds/led1/
: rouge.
Ces répertoires contiennent, entre autres, ces deux fichiers :
- brightness : intensité de la LED (0 pour éteinte, toute autre valeur jusque 255 pour allumée) ;
- trigger : déclencheur, quand et pourquoi la LED s’allume.
# cat /sys/class/leds/led0/brightness
0
# cat /sys/class/leds/led0/trigger
none rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock timer oneshot heartbeat backlight gpio cpu cpu0 cpu1 cpu2 cpu3 default-on input panic mmc1 [mmc0] rfkill-any rfkill-none rfkill0
# cat /sys/class/leds/led1/brightness
255
# cat /sys/class/leds/led1/trigger
none rc-feedback kbd-scrolllock kbd-numlock kbd-capslock kbd-kanalock kbd-shiftlock kbd-altgrlock kbd-ctrllock kbd-altlock kbd-shiftllock kbd-shiftrlock kbd-ctrlllock kbd-ctrlrlock timer oneshot heartbeat backlight gpio cpu cpu0 cpu1 cpu2 cpu3 default-on [input] panic mmc1 mmc0 rfkill-any rfkill-none rfkill0
Les triggers sont entre crochets [mmc0]
pour led0
(verte) et [input]
pour led1
(rouge). On a donc, la LED verte (led0) qui s’allume en cas d’activité sur la carte SD (mmc0) et la LED rouge allumée lorsqu’il y a de l’alimentation électrique (input).
Éteindre et allumer les LED
Ces fichiers virtuels sont lisibles par tous et modifiables par root uniquement :
-rw-r--r-- 1 root root 4096 avril 24 10:54 /sys/class/leds/led0/brightness
-rw-r--r-- 1 root root 4096 avril 24 15:55 /sys/class/leds/led0/trigger
-rw-r--r-- 1 root root 4096 avril 24 10:54 /sys/class/leds/led1/brightness
-rw-r--r-- 1 root root 4096 avril 24 16:03 /sys/class/leds/led1/trigger
Les commandes suivantes devront donc être exécutées en tant que root.
Extinction via trigger none
Éteindre la LED verte (led0) :
echo none > /sys/class/leds/led0/trigger
Éteindre la LED rouge (led1) :
echo none > /sys/class/leds/led1/trigger
Pourquoi je touche aux fichiers trigger
et pas aux fichiers brightness
? Car si le trigger reste mmc0 pour la LED verte, placer un 0 dans /sys/class/leds/led0/brightness
va juste éteindre la LED si elle était allumée à cet instant précis, puis la prochaine activité sur mmc0
va la faire clignoter encore.
Allumage permanent via trigger default-on
Allumer la LED verte (led0) :
echo default-on > /sys/class/leds/led0/trigger
Le contenu du fichier brightness
correspondant passe à 255 :
# cat /sys/class/leds/led0/brightness
255
Note : placer toute autre valeur que 0 dans brightness
allume la LED, mais il n’est pas possible de modifier son intensité.
Faire clignoter une LED
Maintenant qu’on maîtrise l’allumage et l’extinction, faisons clignoter la LED verte.
Un clignotement, c’est :
- j’allume la LED verte (j’écris
default-on
dans le trigger) ; - j’attends une seconde ;
- j’éteins la LED verte (j’écris none dans le trigger) ;
- j’attends une seconde.
Et je recommence tout ça à l’infini.
On peut le faire via le shell, toujours en tant que root :
while true
do
echo default-on > /sys/class/leds/led0/trigger
sleep 1
echo none > /sys/class/leds/led0/trigger
sleep 1
done
Ctrl
+ C
pour arrêter le script.
On peut même imaginer d’autres séquences invoquant les deux LED pour faire un chenillard rapide :
while true
do
echo default-on > /sys/class/leds/led0/trigger
sleep 0.1
echo default-on > /sys/class/leds/led1/trigger
sleep 0.1
echo none > /sys/class/leds/led0/trigger
sleep 0.1
echo none > /sys/class/leds/led1/trigger
sleep 0.1
done
Ou allumer les LED alternativement à cinq secondes d’intervalle :
while true
do
echo default-on > /sys/class/leds/led0/trigger
sleep 5
echo default-on > /sys/class/leds/led1/trigger
echo none > /sys/class/leds/led0/trigger
sleep 5
echo none > /sys/class/leds/led1/trigger
done
Note — La ou les LED restent dans l’état où elles sont lorsque l’on interrompt le script. Il va nous falloir sauvegarder leur état initial au début du script pour pouvoir restaurer cet état à la fin du script.
Sauvegarder l’état des LED
Afin de nettoyer après notre passage, il sera nécessaire de sauvegarder l’état des LED avant d’y toucher pour les restaurer à la sortie de notre script final.
On peut le faire comme ça : pour chaque LED (0 et 1), lire l’état actuel des fichiers virtuels trigger et brightness, stocker ces états dans un emplacement temporaire.
Restaurer l’état des LED
Pour chaque LED (0 et 1), écrire les triggers avec les infos temporaires précédemment enregistrées.
Autoriser un utilisateur standard à modifier l’état des LED
Jusqu’ici, nos expérimentations étaient exécutées en tant que root pour pouvoir modifier les fichiers /sys
directement. Par la suite, nous allons vouloir lancer ce script en tant que l’utilisateur qui effectue les enregistrements ; avec Tvheadend, c’est l’utilisateur hts:hts
. Quelles sont les possibilités pour arriver à faire ça en tant qu’utilisateur, leurs inconvénients s’il y en a :
- lancer le script avec
sudo
; - changer les permissions des fichiers
brightness
ettrigger
au démarrage avec un script appelé via systemd.
J’avais commencé avec la méthode sudo, même si notre script est au final inoffensif, psychologiquement, la méthode ne m’emballait pas. J’ai préféré par la suite modifier les permissions des fichiers virtuels afin que les membres du groupe hts
(donc l’utilisateur hts
de Tvheadend) puissent les modifier.
Pour chaque fichier (brightness
et trigger
), changer le propriétaire vers le groupe hts
et autoriser le groupe à modifier les fichiers. En shell, ça donne un script ledpermissions
à placer dans /usr/local/sbin
(car il sera exécuté par root) :
#!/bin/bash
chown :hts /sys/class/leds/led*/{brightness,trigger}
chmod g+w /sys/class/leds/led*/{brightness,trigger}
On le rend exécutable :
chmod +x /usr/local/sbin/ledpermissions
Accompagné par une fichier Unit ledpermissions.service
pour systemd qui appellera ce script au démarrage du système :
[Unit]
Description=Set leds permissions
[Service]
Type=oneshot
User=root
ExecStart=/usr/local/sbin/ledpermissions
[Install]
WantedBy=multi-user.target
On vérifie son fonctionnement en affichant les permissions des fichiers avant et après lancement :
$ ls -l /sys/class/leds/led*/{brightness,trigger}`
-rw-r--r-- 1 root root 4096 avril 23 17:40 /sys/class/leds/led0/brightness
-rw-r--r-- 1 root root 4096 avril 23 17:40 /sys/class/leds/led0/trigger
-rw-r--r-- 1 root root 4096 avril 23 17:40 /sys/class/leds/led1/brightness
-rw-r--r-- 1 root root 4096 avril 23 17:40 /sys/class/leds/led1/trigger
Groupe : root
, permissions du groupe : r--
.
# systemctl start ledpermissions.service
# ls -l /sys/class/leds/led*/{brightness,trigger}
-rw-rw-r-- 1 root hts 4096 avril 23 17:40 /sys/class/leds/led0/brightness
-rw-rw-r-- 1 root hts 4096 avril 23 17:40 /sys/class/leds/led0/trigger
-rw-rw-r-- 1 root hts 4096 avril 23 17:40 /sys/class/leds/led1/brightness
-rw-rw-r-- 1 root hts 4096 avril 23 17:40 /sys/class/leds/led1/trigger
Groupe : hts
, permissions du groupe : rw-
.
Et l’on active le service pour le prochain démarrage du système :
systemctl enable ledpermissions.service
Créer un script « recording »
Ce que je veux pour ce script :
- qu’il puisse être lancé par l’utilisateur hts (OK si les permissions des LED sont modifiées) ;
- qu’il ne se plante pas si on le lance plusieurs fois de suite tout en ne laissant qu’une seule instance s’exécuter ;
- qu’il puisse être appelé avec
start
oustop
pour démarrer ou arrêter le clignotement et restaurer l’état initial de la LED ; - qu’il fasse clignoter la LED verte comme ça : deux secondes allumée, une seconde éteinte, etc.
Son déroulement :
- vérifier la syntaxe,
recording start
ourecording stop
, ignorer les paramètres supplémentaires, et en cas d’erreur de syntaxe, afficher la syntaxe correcte et sortir en erreur ; - si l’argument est «
start
», vérifier que le script n’est pas déjà en cours d’exécution via un fichier PID, si ce n’est pas le cas, écrire notre PID dans ce fichier, écrire dans un fichier journal la date de début d’enregistrement et appeler la boucle de clignotement ; - boucle de clignotement :
- sauvegarde dans deux variables des valeurs initiales de
brightness
ettrigger
de la LED verte (0), - se préparer à mourir « proprement » si l’on reçoit
Ctrl
+C
ou un autre signal, en appelant si ça arrive une fonctioncleanup
qui restaurera les valeurs initiales debrightness
ettrigger
de la LED verte (0), - clignoter selon notre volonté.
- sauvegarde dans deux variables des valeurs initiales de
#!/bin/bash
# define PID file
PID_FILE="/run/shm/$(basename $0)"
# define which LED we will use
LED_PATH="/sys/class/leds/led0"
# Define blinking delays in seconds
ON_DELAY=2.0
OFF_DELAY=1.0
do_start() {
# are we already running ?
if [ -f $PID_FILE ]
then
printf "Already running or badly terminated !\n"
exit 1
else
echo $$ > $PID_FILE
echo [$(date '+%F %H:%M:%S')] start >> ~/recording.log
blink_loop
fi
}
cleanup() {
# Restore initial values (BRIGHTNESS and TRIGGER) of the led
echo $LED_INITIAL_BRIGHTNESS > $LED_PATH/brightness
echo $LED_INITIAL_TRIGGER > $LED_PATH/trigger
# Remove pid file if present
[ -f $PID_FILE ] && rm $PID_FILE
exit 0
}
blink_loop() {
# Get initial values (BRIGHTNESS and TRIGGER) of the red led to restore it at
# exit time
LED_INITIAL_BRIGHTNESS=$(cat $LED_PATH/brightness)
LED_INITIAL_TRIGGER=$(sed 's/.*\[\(.*\)\].*//' < $LED_PATH/trigger)
trap 'cleanup' EXIT HUP INT QUIT TERM
while true
do
echo default-on > $LED_PATH/trigger
sleep $ON_DELAY
echo none > $LED_PATH/trigger
sleep $OFF_DELAY
done
}
do_stop() {
echo [$(date '+%F %H:%M:%S')] stop >> ~/recording.log
# kill the process otherwise, previous led states are unknown unless writed
# to a file before blink loop
pkill -TERM -x $(basename $0)
}
print_syntax() {
printf "Syntax : $(basename $0) <start|stop>\n"
}
if [ "$#" -ge "1" ]
then
case ${1,,} in
start)
do_start
;;
stop)
do_stop
;;
*)
print_syntax
exit 1
;;
esac
else
print_syntax
exit 1
fi
Le rendre exécutable :
chmod +x /usr/local/bin/recording
Tester
recording start
doit faire clignoter la LED comme convenu, et Ctrl
+ C
doit arrêter le script et restaurer l’état initial de la LED utilisée.
Depuis un autre terminal avec le même utilisateur, recording stop
doit arrêter le script et restaurer l’état initial de la LED utilisée.
Appeler le script en début et fin d’enregistrement
Via l’interface Web de Tvheadend, dans la configuration des profils d’enregistrement :
Configuration > Recording > Digital Video Recorder Profiles
Choisir le profile DVR behavior
Pre-processor command: /usr/local/bin/recording start
Post-processor command: /usr/local/bin/recording stop
Save
Seulement entrer ça dans les deux champs concernés ne fonctionne pas ; il ne se passe rien.
Après tatonnements, j’en suis arrivé à la conclusion que les commandes entrées dans ces champs n’acceptent pas d’arguments ?!
On contourne donc en créant deux scripts distincts recording-start
et recording-stop
qui vont appeler le script recording
avec les bons arguments :
-
/usr/local/bin/recording-start
:#!/bin/bash /usr/local/bin/recording start
-
/usr/local/bin/recording-stop
:#!/bin/bash /usr/local/bin/recording stop
Les rendre exécutables :
chmod +x /usr/local/bin/recording-*
Configuration > Recording > Digital Video Recorder Profiles
Choisir le profile DVR behavior
Pre-processor command: /usr/local/bin/recording-start
Post-processor command: /usr/local/bin/recording-stop
Save
Tests
Enregistrez avec Tvheadend, Ça fonctionne correctement ? Well done! Sinon, il doit manquer une étape quelque part.
Dépannage
Au détour d’un site parlant de Pi, j’ai trouvé ces paramètres à placer dans /boot/config.txt
pour controler les LED :
dtparam=act_led_trigger=none
dtparam=act_led_activelow=off
dtparam=pwr_led_trigger=none
dtparam=pwr_led_activelow=off
Seulement, au deuxième redémarrage, la LED rouge est restée allumée ! La méthode via script de démarrage et systemd ci‑dessus fonctionne mieux.
Références
- Raspberry Pi : https://www.raspberrypi.org ;
- Raspberry Pi LED : https://mlagerberg.gitbooks.io/raspberry-pi/content/5.2-leds.html ;
- DVB-T : https://www.linuxtv.org/wiki/index.php/DVB-T ;
- Tvheadend : https://tvheadend.org ;
- modifier l’état des LED au démarrage du système via systemd : https://git.sekoya.org/mb/rpi-leds.
Voir cet article « chez moi » : https://www.sekoya.org/#!blog/raspberrypi-tvheadend-recording-led.md.
Aller plus loin
- Journal à l’origine de la dépêche (46 clics)
# Perte d'infos ?
Posté par windu.2b . Évalué à 3.
Très intéressant, ton script. Moi qui envisage de me mettre à Raspberry, je suis toujours amusé/étonné de voir la multitude d'usages qu'on peut en faire.
Une question, cependant. Quand tu écris :
Tu ne perds pas la liste des triggers disponibles, en faisant ça ?
[^] # Re: Perte d'infos ?
Posté par Jehan (site web personnel, Mastodon) . Évalué à 10.
C'est parce que tu penses que ce sont des fichiers textes. Beaucoup de fichiers dans Linux ne sont pas des données stockées. Ce sont en fait des interfaces pour communiquer avec le noyau (une API). Tu as peut-être entendu parler de la fameuse philosophie "tout est fichier" avec Linux; cela ne veut pas dire que les logiciels ont des fichiers de configuration en texte ou quelque chose du genre comme certains croient souvent. C'est que les intéractions peuvent se faire aussi simplement que de la manipulation de fichiers. Mais cela reste des interfaces et ce qu'on y écrit peut être différent de ce qu'on y lit. Dans le cas de ce fichier
trigger
dans l'API "leds", c'est l'équivalent de 2 functions: en lisant le contenu, c'est une fonction de listing des options et aussi qui indique quelle est l'option actuellement sélectionnée; en écriture, c'est une fonction de sélection d'option.Petite démo, si je regarde mon ordinateur, qui a un clavier rétro-éclairable:
Comme tu le vois, quand je lis à nouveau le fichier, il affiche toujours la liste des triggers, par contre maintenant les crochets sont autour de
[audio-mute]
pour indiquer que c'est l'option sélectionnée. Et maintenant mon clavier s'éclaire quand je coupe le son, ce qui bien entendu ne me sert à rien et je vais donc m'empresser d'annuler cela. Ahahah. 🤣En cherchant sur le web, tu peux aisément trouver les diverses interfaces fichiers pour communiquer avec le noyau. Pour l'interface LED par exemple: https://www.kernel.org/doc/Documentation/leds/leds-class.txt
En dehors de
/sys/
, Il existe de nombreuses autres interfaces sous Linux, par exemple dans/proc/
, tu as notamment l'ensemble des processus qui tournent (si tu veux faire un logiciel commetop
oups
, c'est à ça que tu dois t'intéresser), ou/dev/
pour les périphériques, etc.Film d'animation libre en CC by-sa/Art Libre, fait avec GIMP et autre logiciels libres: ZeMarmot [ http://film.zemarmot.net ]
[^] # Re: Perte d'infos ?
Posté par barmic 🦦 . Évalué à 6.
Pour en ajouter une couche :)
Le paradigme "tout est fichier" commun aux unix est une base sur la quelle les différents unix ce sont construit.
La base c'est de considérer toutes les entrées/sorties des programmes comme des fichiers (donc la sortie standard aussi, les pipes, les sockets,…).
Du coup je suis allé vérifier les pseudo systèmes de fichiers sont apparu dans unix himself et là il s'agit d’interagir avec un programme au travers d'un système de fichier. On utilise le fait de voir des dossiers/fichiers et lire le contenu d'un fichier comme les sorties d'un programme et le déplacement dans un dossier, la création de dossier/fichier et l'écriture dans un fichier comme les entrées du programme.
L'intérêt est évident pour les programme qui ne peuvent que difficilement avoir des interfaces comme le noyau, mais c'est aussi très intéressant car les gestionnaires de fichiers et éditeur de texte peuvent naturellement interagir avec ce type de programme.
Sur linux tu trouvera 3 pseudo systèmes de fichiers classiques : udev (monté sur /dev et qui représente le hardware de la machine), procfs (monté sur /proc qui représente tous les processus de la machine (c'est une mine d'or d'aller s'y balader quand on ne connait pas)) et cgroup de plus en plus qui représente des limitations et quotas données aux processus. Mais fuse en possède pleins pour faire pleins de choses rigolotes : liste de filesystem de fuse.
Dernier point hurd avait un concept de translator pour faire ça qui a l'air plutôt cool.
https://linuxfr.org/users/barmic/journaux/y-en-a-marre-de-ce-gros-troll
# Heartbeat
Posté par Marcel4567 . Évalué à 2.
Très intéressant usecase !
Au lieu de faire une boucle on-off pour le clignotement, as-tu essayé le mode
[heartbeat]
?Il est spécialement conçu pour faire clignoter la led. Je l'utilise sur mon BananaPi (pas testé sur RasberryPi mais ça doit être pareil)
[^] # Re: Heartbeat
Posté par Eric . Évalué à 2.
Avec heartbeat la led clignote en fonction de la charge cpu.
Il y a le trigger timer qui permet de configurer une durée (en ms) pour chaque état :
modprobe ledtrig-timer
cd /sys/class/leds/la_led_qui_va_bien
echo timer > trigger
echo 250 > delay_on
echo 750 > delay_off
# Udev rules
Posté par Cyril Pascal (site web personnel) . Évalué à 0.
Hello,
un classique pour modifier les permissions est d'utiliser une règle personnalisée udev.
La première ligne c'est pour le prochain redémarrage, la deuxième pour le test.
Et pour tester :ACTION=="add", SUBSYSTEM=="leds", RUN+="/bin/sh -c 'chown :plugdev /sys$devpath/trigger /sys$devpath/brightness && chmod g+w /sys$devpath/trigger /sys$devpath/brightness'"
ACTION=="change", SUBSYSTEM=="leds", RUN+="/bin/sh -c 'chown :plugdev /sys$devpath/trigger /sys$devpath/brightness && chmod g+w /sys$devpath/trigger /sys$devpath/brightness'"
Comme ça on évite tout systemd :)udevadm control --reload && udevadm trigger /sys/devices/platform/leds
[^] # Re: Udev rules
Posté par Benoît Sibaud (site web personnel) . Évalué à 4. Dernière modification le 26 mai 2020 à 16:31.
Tu parles de systemd-udevd.service ?
Suivre le flux des commentaires
Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.