Pulsante di shutdown/reboot per Raspbery Pi

IMG_5340
Per spegnere un Raspberry Pi nella maniera giusta si deve fare il login e dare il comando ‘sudo shutdown -h now’. In questo modo il sistema si arresterà in modo pulito e non rischieremo di corrompere il file system della scheda SD o di altri dischi collegati. Poco male se il Raspberry Pi è connesso a monitor e tastiera. Ma se invece si tratta di dispositivo headless, al quale ci si connette esclusivamente da remoto via ssh, può capitare di dover accendere un altro computer solo per arrestarlo… una vera scocciatura!

“Sarebbe bello” mi sono detto “avere sul Raspy un bel pulsante per lanciare la procedura di shutdown. Dato che sul Raspberry Pi ci sono diversi pin GPIO (General Purpose Input Output) la cosa non sembra affatto complicata. Vediamo se c’è già qualcosa in rete!”. Ed in effetti di esempi ce ne sono parecchi: questo, questo oppure questo, tanto per citarne alcuni.

Ma a uno trovavo un difetto, a un altro trovavo un altro difetto… insomma: nessuno che mi piacesse fino in fondo. Per questo ho pensato di realizzare un pulsante, con relativa circuiteria e software, diverso da tutti gli altri e che avrebbe dovuto possedere i seguenti requisiti:

  1. avere una duplice funzione: pressione breve (ad esempio < 3 s) per innescare il reboot; pressione lunga (>= 3 s) per innescare lo shutdown;
  2. impiegare il minimo di risorse di sistema possibile, ed evitare completamente il polling (ossia la tecnica – dispendiosa in termini di cicli di CPU – di testare ciclicamente lo stato del pulsante per vedere se sia premuto o meno);
  3. avere un feedback per l’utente: il Raspberry Pi deve mostrare in qualche modo se ha intercettato la pressione del pulsante, e quale tipo di pressione (breve o lunga) ha riconosciuto.

Con questi tre requisiti in mente, e dopo aver studiato i vari esempi trovati in rete, ho progettato e realizzato il “mio” pulsante di spegnimento.

Il circuito

Il circuito è veramente semplice, anche per i principianti di elettronica. Si tratta di collegare un pulsante tra un pin GPIO qualsiasi (da configurare via software come ingresso) e massa e, per il feedback, collegare la serie di un LED e un resistore da 220 Ω tra un altro pin GPIO (da configurare via software come uscita) e massa. Lo schema dei collegamenti è il seguente:

shutdown_pi_bb

, dove ho usato il pin 11 (cavo blu) per il LED e il pin 12 (cavo rosso) per il pulsante. I due cavi neri sono collegati entrambi a massa (su due pin diversi, ma entrambi i pin, internamente al Raspberry Pi, sono collegati alla stessa massa). Più avanti si vedrà come identificare i pin.

I componenti utilizzati sono:

  1. un Raspberry Pi
  2. un pulsante a pressione
  3. un LED (Light Emitting Diode)
  4. un resistore da 220 Ω
  5. una breadboard da prototipazione (opzionale)
  6. vari jumper assortiti (femmina/femmina se senza breadboard, oppure maschio/femmina se con la breadboard)

A proposito dei jumper: magari qualcuno ha Arduino e avrà già pensato di usare gli stessi jumper… beh, non si può fare: i pin GPIO del Raspberry Pi sono maschi, mentre su Arduino gli IO sono femmine. I jumper maschio/maschio che si usano tra Arduino e breadboard non possono essere usati con il Raspberry Pi (e viceversa).

Che ci sta a fare quel resistore in serie al LED? Serve a limitare la corrente che scorre nel LED in modo da non vederlo “fiammare” alla prima accensione. Con un resistore più grande si ha meno corrente, quindi minor luminosità e, ovviamente, con un resistore più piccolo si ha più corrente e più luminosità (ma con il rischio di bruciare il LED). Occhio al verso del LED: i LED, come tutti i diodi, fanno passare corrente in un verso solo: quello che va dall’anodo (terminale lungo) al catodo (terminale corto). Per far sì che  possa scorrere corrente e il LED si accenda, occorre collegare il catodo a massa; se si collega alla rovescia (ossia con l’anodo a massa) non si rischia nulla, ma il LED semplicemente non si accenderà.

Io ho trovato le varie componenti elettroniche in un kit da hobbista (veramente ricco!) che ho preso su Amazon, ma gli appassionati di elettronica avranno senz’altro tutto già in casa, o potranno procurarselo per pochi spiccioli dal loro abituale “pusher” di componenti.

Il programma in Python

Un modo molto semplice per realizzare programmi che lavorano con i pin GPIO del Raspberry Pi è utilizzare il linguaggio Python assieme alla libreria RPi.GPIO. Sia Python che RPi.GPIO sono già installati su Raspbian (il sistema operativo di riferimento per il Raspberry Pi) e, quindi, non resta che scrivere il programma.

Questo è lo script Python che fa tutto il lavoro:

import RPi.GPIO as GPIO
import time
import os

def blink(channel, count = 1, timeHigh = 1000, timeLow = 1000):
    """Blink count times, timeHigh milliseconds high, timeLow milliseconds low."""

    if count <= 0:
         return
 
    GPIO.output(channel, GPIO.HIGH)
    time.sleep(timeHigh / 1000.0)
    GPIO.output(channel, GPIO.LOW)
 
    for i in range(count - 1):
        time.sleep(timeLow / 1000.0)
        GPIO.output(channel, GPIO.HIGH)
        time.sleep(timeHigh / 1000.0)
        GPIO.output(channel, GPIO.LOW)

led = 17
button = 18

# Init GPIO
GPIO.setmode(GPIO.BCM)
GPIO.setup(led, GPIO.OUT)
GPIO.setup(button, GPIO.IN, pull_up_down = GPIO.PUD_UP)

GPIO.wait_for_edge(button, GPIO.FALLING)
channel = GPIO.wait_for_edge(button, GPIO.RISING, timeout = 3000)
if channel is None:
    blink(led, 3, 500, 500)
    print "Shutdown"
    os.system("/sbin/shutdown -h now")
else:
    blink(led, 1, 500)
    print "Reboot"
    os.system("/sbin/shutdown -r now")
 
GPIO.cleanup()

A questo punto occorre aprire una parentesi sulla numerazione dei pin del Raspberry Pi. Ci sono 3 diversi sistemi per numerare, o identificare, i pin GPIO del Raspberry Pi:

  • numerazione “fisica”
  • numerazione “BCM”
  • numerazione “wiringPi”

La numerazione fisica è quella che identifica i pin con la loro posizione sul connettore; la numerazione BCM identifica i pin in base a come sono collegati al SOC Broadcom (ad esempio, il pin 11 del connettore è collegato al pin GPIO17 del chip BCM2835, e quindi avrà identificativo BCM pari a 17); la numerazione wiringPi è quella che viene usata dalla libreria C wiringPi, ed è basata sulla posizione che i bit, corrispondenti a ciascun pin, hanno nelle porte I/O che si programmano direttamente da C e assembly (ad esempio, il pin 11 del connettore ha identificativo 0 nella numerazione wiringPi perché corrisponde al bit 0 della prima porta I/O, la porta A). Uno schema che consente di identificare i pin in ognuno dei tre sistemi può essere reperito qui: http://pinout.xyz/. Quando si scrive un programma, una delle prime scelte da fare è quale tipo di numerazione dei pin si intende utilizzare. Posto che nessuno di questi sistemi è immune da difetti (potrà sempre capitare di dover riscrivere il codice e/o dover ricablare l’impianto nel momento in cui si cambia modello, o anche solo revisione, di Raspberry Pi), io ho scelto la numerazione BCM (GPIO.setmode(GPIO.BCM)): alcuni pin hanno anche delle funzioni alternative imposte dal chip BCM (interfaccia seriale, I2C, SPI etc.) che restano “ancorate” all’identificativo BCM e quindi, con la numerazione BCM, sarà più semplice gestire futuri cambiamenti del pinout anche relativamente alle funzioni alternative.

La pressione del pulsante collega il pin GPIO18 a massa, facendo leggere una tensione di 0 V sul pin stesso (valore logico 0). Quando il pulsante non è premuto, la tensione letta potrebbe essere un valore qualsiasi. È per questo motivo che, sul pin GPIO18, viene abilitato il circuito di pull-up interno al Raspberry Pi (istruzione GPIO.setup(button, GPIO.IN, pull_up_down = GPIO.PUD_UP)): in questo modo si ha la garanzia che il valore di tensione letto con il pulsante non premuto sia 3.3 V (valore logico 1).

La funzione utilizzata per monitorare il pulsante è GPIO.wait_for_edge(). Questa è una funzione bloccante che non consuma risorse di sistema e che rimane in attesa all’infinito sul pin GPIO18, fino a che non viene rilevato un fronte in discesa (1 -> 0): la pressione del pulsante. Successivamente si rimane in attesa, per un periodo massimo di tre secondi, di un fronte in salita (0 -> 1, il rilascio del pulsante) con l’istruzione GPIO.wait_for_edge(button, GPIO.RISING, timeout = 3000). Se si esce perché il fronte in salita è stato rilevato, significa che la pressione del pulsante è durata meno di 3 secondi, e quindi si opta per il reboot; altrimenti si esce perché sono passati 3 secondi senza che il pulsante sia stato rilasciato, e quindi si opta per lo shutdown.

La funzione blink(), definita all’inizio dello script, si occupa di notificare all’utente il riconoscimento della pressione del pulsante: un unico lampeggio nel caso di pressione breve, tre lampeggi nel caso di pressione lunga.

La funzione os.system() serve per lanciare l’esecuzione di un comando utilizzando la shell di sistema. In questo caso i comandi da lanciare sono ‘/sbin/shutdown -h now’ per lo shutdown, o ‘/sbin/shutdown -r now’ per il riavvio.

Per salvare lo script sul Raspberry Pi occorre effettuare il login su di esso (direttamente o via ssh), lanciare l’editor nano con il comando:

$ nano shutPi.py

, inserire il testo dello script (facendo bene attenzione all’indentazione, che in Python serve per identificare i blocchi di codice), e chiudere nano con Ctrl+X (ricordandosi di salvare il file in uscita). A questo punto lo script dovrebbe essere stato salvato sul file col nome /home/pi/shutPi.py.

Ora si può passare al collaudo. Per eseguire lo script si deve dare il comando:

$ sudo python /home/pi/shutPi.py

, dove il comando sudo è necessario perché il comando shutdown, lanciato dallo script, richiede i privilegi di super utente. Alla pressione del pulsante, dopo il lampeggio del LED e un attimo prima dell’arresto del sistema, sul terminale dovrebbe comparire la scritta Shutdown quando viene intercettata una pressione lunga, e la scritta Reboot quando viene intercettata una pressione breve.

L’avvio automatico

Una volta che si è verificato che lo script funziona come si deve, si può impostare il Pi per lanciarlo automaticamente ad ogni avvio. Per fare questo occorre modificare il file /etc/rc.local:

$ sudo nano /etc/rc.local

ed aggiungere, prima della riga ‘exit 0’, le righe che seguono:

if [ -f /home/pi/shutPi.py ]; then
    python /home/pi/shutPi.py &
fi

Qui sudo non serve, perché lo script /etc/rc.local viene eseguito già con i privilegi di super utente; la e commerciale (&) in fondo alla seconda linea serve per lanciare il nostro script in background, in modo da non bloccare l’esecuzione di eventuali altre parti del file /etc/rc.local.

A questo punto si può chiudere con Ctrl+X, avendo cura di salvare il file. D’ora in avanti lo  script shutPi.py verrà lanciato automaticamente, e in background, ad ogni avvio. Per verificarlo, dopo aver riavviato, si può dare il comando:

$ ps ax | grep shutPi.py

Possibili semplificazioni

Se non interessa avere il feedback fornito dal LED, o non ne abbiamo uno a disposizione, la parte relativa al LED può essere tranquillamente stralciata sia dal circuito che dal programma e il pulsante continuerà a funzionare come prima.

Io uso correntemente una versione ulteriormente ridotta del circuito che fa a meno anche del pulsante: del circuito originale ho mantenuto solamente il jumper rosso e uno dei due jumper neri. I due jumper, all’avvio del Raspberry Pi e per tutta la fase di normale funzionamento, non devono essere lasciati liberi di toccarsi. Quando si deve arrestare il sistema, per innescare la procedura di shutdown o reboot sarà sufficiente toccare tra di loro (rispettivamente: almeno 3 secondi per lo shutdown, meno di 3 secondi per il reboot) i capi liberi dei due jumper.

Riferimenti

Annunci

7 Pensieri su &Idquo;Pulsante di shutdown/reboot per Raspbery Pi

  1. Ciao, molto interessante il tuo lavoro!
    Io sto realizzando una mini console con un RaspBerry pi Zero.
    Volevo collegare un pulsante per il shutdown, quando é premuto crea il contatto, quando lo ripresi stacca il contatto.
    In teoria dovrebbe essere un pulsante NA.
    Ah il sistema operativo é Retropie/Emulationstation.
    Puoi aiutarmi in alcuni passaggi che non capisco?
    Grazie.

  2. Ciao, ho utilizzato il tuo programma montando un pulsante ed un led (stessi pin). Funziona tutto /reboot e shutdown), ma spesso il rasp da solo esegue un reboot o uno shutdown…. ho dovuto rimuover eil programma dall’autostart per risolvere. Ho anche cambiato pin. Cosa ne pensi?

    • Ciao Guido!
      È possibile che il tuo pulsante sia difettoso e provochi dei contatti non voluti? In questo caso dovresti vedere anche il led accendersi.
      Saluti,
      A.

  3. ciao, interessante il tuo lavoro. sto cercando di implementare un pulsante per accendere e spegnere il raspberry su cui ho installato volumio. Non riesco però a creare la cartella home/pi perché mi dice che non ho il permesso. ho creato pertanto la cartella pi direttamente fuori la cartella home e ci ho salvato lo script. Ho infine lanciato il comando sudo python /pi/shutPi.py ma non mi riconosce il comando. Perché non riconosce i comandi?

    • Ciao Modesto,
      questo post si basa su Raspbian, che è il sistema operativo più diffuso per Raspberry Pi, ma non l’unico ;-). Ci sono sensibili differenze tra Volumio e Raspbian: Volumio è orientato ad un uso specifico (fare da player audio), mentre Raspbian è un sistema operativo “general purpose”, in grado di far girare un ampio spettro di applicazioni diverse.

      Per quanto riguarda la cartella /home/pi, quella è semplicemente la cartella home dell’utente pi, l’utente predefinito di Raspbian. Volumio non ha, per default, un utente di nome pi, ma un utente di nome volumio. Lo script può essere tranquillamente piazzato in una cartella qualsiasi (/dove/piace/a/te/shutPi.py), quindi anche /pi/shutPi.py va bene: l’importante è essere coerenti con la posizione scelta.

      Per il secondo problema, non so se su Volumio è presente tutto il necessario: il comando sudo, l’interprete Python e il modulo Python RPi.GPIO. In modo da capire cosa manca, puoi riportare esattamente il messaggio di errore che ti viene restituito?

      Ciao e grazie per l’interessamento!

  4. alla fine il file l’ho salvato nella directory /home/volumio/shutPi.py

    ecco l’errore che mi da

    volumio@volumio:~$ sudo python /home/volumio/shutPi.py
    Traceback (most recent call last):
    File “/home/volumio/shutPi.py”, line 1, in
    import RPi.GPIO as GPIO
    ImportError: No module named RPi.GPIO

Rispondi

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:

Logo WordPress.com

Stai commentando usando il tuo account WordPress.com. Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...