Telecamera di sorveglianza con Raspberry Pi

Da qualche tempo posseggo un Raspberry Pi 3 B+ e lo utilizzo prevalentemente come console per Retro Gaming. Preso dalla voglia di un progetto serio, ho pensato di creare una telecamera di video sorveglianza statica (cioè fissa in un punto). Dopo alcune prove con la PI 3B+ ho deciso di comprare un kit Raspberry Pi Zero W, meno potente, più piccolo e con case più adatto.

Hardware

Cosa serve

I link riportati sotto sono sponsorizzati, cioè finanziano il sito con una percentuale sulle vendite (dal 3 al 10%).

Il kit del Raspberry PI 3 A+ e B+ contiene:

  • Raspberry PI 3 A+ o B+
  • alimentatore
  • scheda SD da 16 GB marchiata Raspberry (con il lampone fa la sua figura)
  • Cavo HDMI
  • Case (non molto adatto allo scopo, non c'è il buco per la telecamera, quindi telecamera fuori e tappo aperto)

Il kit per Raspberry PI Zero W 1 o 2 contiene:

  • il minuscolo Raspberry PI Zero W (1 o 2)
  • Pin GPIO da saldare sulla scheda (esiste una versione Zero WH con i PIN già saldati, tenetela in considerazione se avete in mente anche altri progetti)
  • Adattatore da MicroHDMI ad HDMI
  • Adattatore da MicroUSB a USB
  • Slitta mini per PiCamera (quindi prima di procedere al montaggio dovete sostituirla)
  • Case con 3 "tappi": intero, apertura GPIO, foro centrale per Pi Camera

La camera è molto facile da montare comunque ci sono tantissimi video su Youtube.

Software

Sistema operativo

Per il progetto ho utilizzato la classico e ottimo Raspberry Pi OS (per l'installazione vedere qui). Una volta portata a termine l'installazione del sistema operativo è necessario avviare il sistema, loggarci (utente pi password raspberry) attivare il supporto ssh e camera (per questi primi minuti ci servono un monitor e una tastiera) utilizzando il tool raspi-config:

sudo raspi-config

Prima di tutto cambiamo la password dell'utente (la default non è il massimo della sicurezza), quindi scelta 1. Dopo di che possiamo attivare SSH (scelta 5 -> 2) e la PiCamera (Scelta 5 -> 1) . Dalla serie mai tralasciare le cose ovvie, ci muoviamo con il tasto Tab e con le frecce.

raspy_config

Script Python per gestire la telecamera e il server Web

Riportato di seguito lo script della documentazione ufficiale della PI Camera leggermente modificato da me medesimo. Ho aggiunto la possibilità di avviarlo senza usare il comando python3 (#!/bin/env python3 all'inizio), ho personalizzato il codice html della pagina, ho cambiato la risoluzione (sia in html che più sotto) e ho aggiunta metodo per la rotazione (#camera.rotation = 90 commentato ma attivabile alla bisogna). Potete copiare su un nuovo file il codice dello script,  personalmente ho creato il file camera.py nella home dell'utente pi, quindi /home/pi/camera.py

#!/bin/env python3

import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server

PAGE="""\
<html>
<head>
<title>PiCamera Telecamera di sorveglianza </title>
</head>
<body>
<h1>PiCamera Telecamera di sorveglanza</h1>
<img src="stream.mjpg" width="800" height="600"/>
</body>
</html>
"""

class StreamingOutput(object):
    def __init__(self):
        self.frame = None
        self.buffer = io.BytesIO()
        self.condition = Condition()

    def write(self, buf):
        if buf.startswith(b'\xff\xd8'):
            # New frame, copy the existing buffer's content and notify all
            # clients it's available
            self.buffer.truncate()
            with self.condition:
                self.frame = self.buffer.getvalue()
                self.condition.notify_all()
            self.buffer.seek(0)
        return self.buffer.write(buf)

class StreamingHandler(server.BaseHTTPRequestHandler):
    def do_GET(self):
        if self.path == '/':
            self.send_response(301)
            self.send_header('Location', '/index.html')
            self.end_headers()
        elif self.path == '/index.html':
            content = PAGE.encode('utf-8')
            self.send_response(200)
            self.send_header('Content-Type', 'text/html')
            self.send_header('Content-Length', len(content))
            self.end_headers()
            self.wfile.write(content)
        elif self.path == '/stream.mjpg':
            self.send_response(200)
            self.send_header('Age', 0)
            self.send_header('Cache-Control', 'no-cache, private')
            self.send_header('Pragma', 'no-cache')
            self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
            self.end_headers()
            try:
                while True:
                    with output.condition:
                        output.condition.wait()
                        frame = output.frame
                    self.wfile.write(b'--FRAME\r\n')
                    self.send_header('Content-Type', 'image/jpeg')
                    self.send_header('Content-Length', len(frame))
                    self.end_headers()
                    self.wfile.write(frame)
                    self.wfile.write(b'\r\n')
            except Exception as e:
                logging.warning(
                    'Removed streaming client %s: %s',
                    self.client_address, str(e))
        else:
            self.send_error(404)
            self.end_headers()

class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
    allow_reuse_address = True
    daemon_threads = True

with picamera.PiCamera(resolution='800x600', framerate=24) as camera:
    output = StreamingOutput()
    #Uncomment the next line to change your Pi's Camera rotation (in degrees)
    #camera.rotation = 90
    camera.start_recording(output, format='mjpeg')
    try:
        address = ('', 8000)
        server = StreamingServer(address, StreamingHandler)
        server.serve_forever()
    finally:
        camera.stop_recording()

Lanciamo lo script e facciamolo partire in automatico

Per poter lanciare lo script dobbiamo dare prima di tutto i permessi di esecuzione al file che lo contiene:

chmod a+x /home/pi/camera.py

Essendo una telecamera di sorveglianza privata ma raggiungibile dall'esterno ho preferito non lasciare sempre lo script attivo. Quando necessito della telecamera mi collego via SSH al Raspberry  facendo partire lo script. Per avviare automaticamente lo script al login è necessario inserire il comando seguente alla fine del file di configurazione .profile, presente nella home dell'utente pi (attenzione a non toccare altro, potreste compromettere il login):

/home/pi/camera.py >> log.txt 2>&1 &

Lo script parte in background (liberando cioè la shell) "sparando" ogni output (>> per lo standard output e 2>&1 per lo standard error) sul file log.txt. Per terminare lo script (e non lasciare trasmettere la telecamera quando non ci serve) è necessario terminare ogni istanza del processo python3. Per evitare di dimenticarcelo, lo facciamolo eseguire alla disconnessione della sessione, per farlo basta inserire la riga seguente alla fine del file /home/pi/.bash_logout:

killall python3

Colleghiamoci

Lo script ha aperto un server Web sulla porta 8000 dove, utilizzando l'indirizzo IP associato alla nostra scheda, possiamo collegarci. Vediamo come reperire l'IP, se abbiamo collegato il Raspberry via cavo:

ifconfig eth0

o se siamo collegati via Wireless:

ifconfig wlan0

Alla voce inet troviamo un indirizzo che inizia con 192.168. (nel mio caso è 1.216). Usiamolo da un altro device (PC, Tablet, Smartphone, SmartTV o altro), connesso alla stessa rete per collegarci al Raspberry: nel browser  inseriamo l'URL http://192.168.x.x:8000 (nel mio caso http://192.168.1.216:8000). Ecco il risultato:

risultato

Conclusioni

Con pochi euro e tanto divertimento siamo riusciti a creare un ottimo servizio di sorveglianza in streaming.

Pregi

Costo esiguo. Abbiamo una telecamera ma, visto che ci gira un sistema operativo completo, possiamo nel frattempo fargli fare di tutto.

Difetti

La telecamera che ho acquistato non è il massimo (per 10 euro me l'aspettavo) quindi funziona perfettamente solo con una buona luce. Praticamente possono entrarmi in casa dopo le 16.30 da Novembre a Marzo.