Piero V.

Motion: videosorveglianza con Raspberry Pi

Questo weekend io e la mia famiglia abbiamo avuto un impegno, per cui siamo stati tutti via, lasciando la casa vuota.

Allora, ho pensato di sfruttare la mia Raspberry Pi 3, che da tempo era rimasta inutilizzata, per creare un mini sistema di sorveglianza.

Cercando su Internet, ho trovato subito un programmino molto semplice da configurare, ma anche alquanto efficace, chiamato motion.

Si interfaccia con le webcam e può registrare un video qualora rilevi del movimento. Inoltre, può eseguire dei comandi quando il movimento inizia e finisce, permettendo interessanti integrazioni. Infine, mette a disposizione anche uno stream del video stesso tramite HTTP. L’unico neo è che gli stream sono solo video, senza audio, anche qualora la webcam fosse dotata di un microfono.

Visto che usa l’API V4L2 (Video For Linux 2), può funzionare su tutti i sistemi Linux, non solo con la Raspberry. Io stesso, con la mia Raspberry, ho provato sia su Debian, che su Raspbian. Qualora vogliate comprare un single-board computer da adibire allo scopo, la lista dei dispositivi supportati da Armbian potrebbe esservi utile.

Molte distribuzioni basate su Debian includono motion nei loro repository, e si può installare semplicemente con apt install motion. Ho notato che ha tantissime dipendenze, e purtroppo anche --no-install-recommends non aiuta molto. Certi pacchetti, come la libreria client di MySQL, sono proprio obbligatori.

In seguito, lo possiamo impostare l’avvio automatico con systemctl enable motion, anche se non sono sicuro questo passaggio sia necessario.

Configurazione

Per la configurazione, basta modificare /etc/motion/motion.conf, e quanto segue dovrebbe valere per tutte le distribuzioni.

Non c’è bisogno di modificare molti valori predefiniti, a parte per stream_maxrate, cioè i massimi FPS per lo streaming HTTP. Di default è 1, quindi lo streaming verrà aggiornato solo una volta al secondo, e sembrerà bloccato. Io lo ho impostato a 15, ma è un valore massimo e, nella pratica, il mio hardware arriva a 8 FPS. Il risultato quindi è scattoso, ma almeno si vede che è uno streaming. Un motivo per limitare il framerate è una eventuale scarsità di upload. Ho riscontrato che 8 FPS a 640×480 utilizzavano meno di 10Mb/s. Infine, questo parametro non era presente nel mio file di configurazione iniziale, ma si può aggiungere manualmente. Ci ho messo un po’ a trovarlo e all’inizio pensavo il mio hardware non fosse abbastanza potente 😅.

Sempre per lo streaming, una direttiva piuttosto importante è stream_localhost, di default on. Questo significa che lo streaming può essere visualizzato solo dalla Raspberry stessa. Se volete accedervi tramite la LAN, o tramite una VPN, dovrete impostarlo a off.

Attenzione: non contate sulla security through obscurity! Non fate un port forwarding direttamente a motion, sperando che un numero di porta non standard basti a non esporre il vostro video a degli estranei. Piuttosto inoltrate SSH (possibilmente usando solo l’autenticazione tramite chiave), e guardate il video tramite un tunnel SSH! Con questo approccio, stream_localhost può rimanere su on.

Altre impostazioni utili sono la risoluzione e il framerate.

Io ho modificato anche la destinazione dei video: la mia root è piccolina (4GB), quindi ho deciso di attaccare un disco da 2,5” per tre giorni che sono stato via.

Ho modificato anche il percorso del file di log, perché quello predefinito aveva qualche problema di permessi.

Altre impostazioni utili, che però io non ho modificato, sono le soglie per la rilevazione dei movimenti.

Un valore che invece non va modificato è daemon: deve rimanere off, perché ci pensa systemd a fare quanto serve.

Una volta cambiata la configurazione, motion va riavviato con systemctl restart motion.

Integrazione con un bot Telegram

In realtà c’è un’ulteriore impostazione davvero molto interessante: on_event_start. Permette di eseguire un comando qualsiasi del sistema quando viene rilevato un movimento. Con un po’ di fantasia e di abilità, le integrazioni possibili sono tendenzialmente illimitate.

Io ho pensato di farmi inviare un messaggio da un bot Telegram, sviluppato con python-telegram-bot.

Questa libreria è asincrona, e io non so quasi niente di Python asincrono 😒. Ho però notato che fornisce una coda di task in un componente opzionale, e questa può essere riempita sincronicamente, bingo!

Quindi, il thread principale del mio script è il bot, e poi, in un secondo thread, faccio girare un server HTTP, che invia i messaggi accodando nuovi task. Niente male, per essere uno script quick&dirty.

A questo punto, sul motion.conf ho impostato come on_event_start semplicemente wget -O /dev/null -q http://localhost:2196/motion_started. Il vantaggio di un server HTTP è che ci sono tanti modi per poterci interagire, e si evitano eventuali problemi di permessi.

L’unica cosa che non mi piace molto del mio script, è che HTTPServer di Python accetta una classe per gestire le richieste, ma non fornisce alcun metodo per passarle dei parametri personalizzati. Quindi, ho dovuto tenere la coda in una variabile globale.

Considerazioni sulla soluzione

La soluzione ha funzionato meglio di quanto mi aspettassi. Ho avuto diversi falsi positivi, ma meno del previsto. Forse la mia configurazione è anche un po’ troppo conservativa, perché mi sembra ci metta qualche secondo a dirmi che ha rilevato un movimento.

La qualità del video però è abbastanza scarsa: 8 FPS sono proprio pochi. Ma non sono nemmeno limitato dall’hardware: solo un unico core su 4 viene utilizzato, e solo al 30-40%!

Ad un certo punto la chiavetta USB che ho usato come root del sistema ha cominciato ad avere problemi, finché non mi ha abbandonato, proprio mentre ero via. Linux l’ha impostata su read-only, ma fortunatamente tutto ha retto lo stesso. Aver deciso di usare un disco esterno per le clip è stata una vera fortuna.

Se dovessi averne ancora bisogno, in futuro potrei avvalermi di questa soluzione di nuovo. Magari potrei riuscire anche a ritoccare qualche parametro per migliorare la fluidità e il tempo di rilevazione.