Costruire una applicazione convergente per la sorveglianza domestica (parte 1)
by admin on May.06, 2010, under Hacking, Linux, Sicurezza
Ho voluto dare inizio, con l’ introduzione a Motion, a una serie di articoli incentrati su tecnologie varie, ma tutte inerenti al “telecontrollo”, e sopratutto, almeno nelle mie intenzioni, sviluppati intorno alle possibili integrazioni tra esse, nel pieno spirito di ciò che intendo per convergenza.
Ho usato il termine telecontrollo per riferirmi ad una classe di possibili applicazioni basate sulle interazioni fra terminali audio e video oramai divenuti intelligenti, operanti tendenzialmente su reti TCP/IP, ma che non disdegnano affatto l’integrazione con i protocolli dedicati alla domotica, tramite i quali possono notevolmente estendere le proprie funzionalità di controllo e risposta al verificarsi di particolari eventi.
Mi riferisco alla grande varietà di sensori, tipo controllo luci, antiintrusione, antiincendio, antiallagamento ecc., in grado di comprendere il cosidetto standard X10.
Ribadisco l’intenzione già espressa di trattare ancora l’argomento videosorveglianza, ma proprio per esplorarne alcune possibili integrazioni in applicazioni convergenti, ho deciso di farlo illustrando la genesi di un prototipo di sistema, che pure volendo rimanere minimale, fosse di stimolo a chi provasse interesse per l’argomento.
Il proof of concept consisterebbe di un ipotetico sistema di sicurezza, messo a protezione di una abitazione privata, diciamo una villetta monofamiliare, tanto per definire un modello, in cui interagiscano videosorveglianza, intercettazione audio ambientale, unità di controllo dell’illuminazione e sensori antiintrusione, tipo quelli basati su infrarossi. Inoltre il sistema dovrà essere particolarmente efficace non solo in senso informativo, ma sopratutto reattivo.Inoltre attivazione e disattivazione dovranno essere possibili anche da remoto, ad esempio via bluetooth, wifi o chiamata telefonica.
Delle numerose tecnologie coinvolte in una soluzione del genere, quella sicuramente meno conosciuta è proprio quella relativa allo standard X10, cosa che rende doveroso cercare di illustrarlo, almeno in quantità sufficiente al nostro scopo.
X10 è uno standard internazionale industriale aperto per permettere la comunicazione tra devices elettronici tipicamente utilizzati in domotica.
Tipicamente esso fa uso della rete di alimentazione elettrica cablata sia per il signaling che per il controllo, coi relativi segnali costituiti da brevi radio frequency bursts a rappresentare una informazione digitale. Esiste anche un equivalente protocollo basato su wireless radio.
Sebbene esista un certo numero di alternative a maggior consumo di banda (e migliori performances, se si ricorda che lo standard fu sviluppato nel lontano 1975 da parte di Pico Electronics, Glenrothes, Scotland, per consentire il controllo remoto di devices domestici e relative applicazioni), X10 rimane ancora una scelta molto diffusa in ambiente domestico, anche a causa della sua economicità e della ampia disponibilità di nuovi componenti.
L’impianto elettrico domestico può essere usato per trasmettere dati digitali tra devices X10. Tali dati digitali vengono codificati in una portante a 120 kHz trasmessa durante la relativamente tranquilla fase di zero crossings della forma d’onda della currente alternata (AC) a 50 o 60 Hz. Un bit viene trasmesso durante ciascun “zero crossing”.
I dati digitalizzati consistono di un indirizzo e di un comando inviato da un controller ad un device controllato. Controllers maggiormente avanzati sono anche in grado di interrogare devices altrettanto avanzati, in modo che questi ultimi restituiscano il proprio status. Tale status può consistere semplicemente in “off” o “on”, o rappresentare l’attuale livello di regolazione, oppure la temperatura o gli altri tipici valori restituiti da altri tipi di sensore. I vari devices si interfacciano con la rete elettrica come qualsiasi elettrodomestico; tuttavia esistono comuni esempi di controllers built-in, ad esempio in interruttori a muro.
A causa della relativamente alta-frequenza utilizzata per il trasporto del segnale non è in grado di passare attraverso un trasformatore.
Il protocollo X10
Sia che utilizzino l’impianto elettrico cablato oppure le communicazioni radio, i pacchetti trasmessi tramite il protocollo di controllo X10 consistono di un cosidetto house code di quattro bit seguito da uno o più codici di quattro bit relativi a ciascuna unità interessata, seguito infine da un comando codificato sempre in quattro bit. Per facilitare la configurazione di sistema, l’ house code di quattro bit viene indicato come una lettera variabile tra A e P mentre lo “unit code”, ugualmente di quattro bit, come un numero compreso tra 1 e 16.
Quando un sistema viene installato, ciascun device controllato deve essere configurabile per rispondere a ciascuno di 256 possibili indirizzi (16 house codes × 16 unit codes); ogni device reagisce ai comandi specificamente indirizzatigli, e possibilmente, a differenti comandi broadcast.
Il protocollo può trasmettere l’equivalente di un messaggio che dica “seleziona il codice A3″, seguito da “attiva”, cosa che di fatto impartisce alla unità “A3″ di attivare il proprio device. Differenti unità possono venire indirizzate prima di impartire il comando, permettendo a quest’ultimo di agire su più unità simultaneamente. Per esempio, “seleziona A3″, “seleziona A4″, “seleziona A5″, ed infine, “attiva”, fa in modo che le unità A3, A4, ed A5 vengano tutte attivate.
È notevole (in termini di comodità) il fatto che non vi siano restrizioni che possano impedire di utilizzare più di un house code in una abitazione singola. Pertanto una installazione che utilizzi house codes multipli in pratica può suddividere i propri devices in zone separate.
Per completezza, questa è la lista dei comandi X10:
Codice Funzione Descrizione
0000 Disattiva tutte le unità Commuta su off tutti i devices con HC indicato nel msg
0001 Attiva tutte le luci Commuta su on tutte le luci
0010 Attiva Commuta su on un device
0011 Disattiva Commuta su off un device
0100 Diminuisci (Dim) Riduce l'intensità della illuminazione
0101 Esalta (Bright) Aumenta l'intensità della illuminazione
0111 Extended code Specifica un codice esteso
1000 Richiesta Hail Richiede 1 risposta al device con HC indicato nel msg
1001 Ricevuta Hail (ack) Risposta alla precedente richiesta
101x Pre-set dim Selezione tra due livelli predefiniti di intensità
1101 Status is on Risposta indicante che il device è attivo
1110 Status is off Risposta indicante che il device non è attivo
1111 Status request Richiesta dello status di un device
Indipendentemente comunque da come vengono rappresentate nella corrente alternata a 60 Hz, quelli che vengono trasmessi sono i soliti bit di valore uno e bit di valore zero. Tutti i messagggi vengono inviati due volte per ridurre il falso signaling. Considerando ritrasmissione, controllo della linea, etc, la velocità di trasmissione si aggira intorno ai 20 bit/s, rendendo la trasmissione di dati basata su X10 così lenta da relegarla spesso e volentieri alla semplice attivazione e disattivazione dei devices o altre operazioni piuttosto semplici.
Per poter fornire uno inizio identificabile, ciascun data frame trasmesso inizia sempre con uno start code di 1110. Subito dopo lo start code, è presente un house code (A–P), e successivamente un codice di funzione. I codici di funzione specificano uno unit number code (1–16) o un command code, la differenza tra i due modi essendo espressa dall’ultimo bit dove uno zero rapprestta uno unit number ed 1 un comando. Uno start code, un codice corrispondente ad una lettera, ed un codice di funzione sono conosciuti come un frame X10 e rapresentano il minimo di componenti di un data packet X10 ritenuto valido.
Ciascun frame viene inviato due volte in successione per assicurare che i ricevitori lo interpretino correttamente anche in presenza di rumore sulla linea di alimentazione (per assicurare ridondanza, affidabilità).
Anche se i dati variano da un indirizzo ad un altro, da un indirizzo ad un comando, o da un comando ad un altro comando, i data frames devono risultare separati da almeno 6 zero crossings puliti (o “000000″). La sequenza di sei zero resetta il decoder hardware del device.
Il protocollo radio
Per consentire la comunicazione con wireless keypads, remote switches, e simili, è stato pure definito un protocollo radio. Operanti ad una frequenza di 310 MHz negli U.S.A. e a 433 MHz nei sistemi Europei, i devices wireless inviano pacchetti di dati in modo molto simile agli ordinari pacchetti di controllo X10 su linea elettrica. Un rivevitore radio quindi provvede a tradurre tali radio packets in normali pacchetti di controllo X10 su linea elettrica.
I vari controllers X10 spaziano dall’ estremamente semplice al molto sofisticato.
I controllers più semplici sono predisposti per controllare quattro devices X10 identificati da quattro indirizzi in sequenza (1–4 o 5–8).
Controllers più sofisticati possono controllare più unità e/o incorporare timers che effettuino funzioni preprogrammeate in momenti specifici del giorno. day.
Sono anche disponibili unità che fanno uso di sensori passivi di movimento a raggi infrarossi o fotocellule in grado di accendere o spegnere fonti luminose in base a particolari condizioni esterne.
Per finire, è possibile allestire soluzioni anche molto sofisticate programmate tramite software in esecuzione su di un computer.
Per rimanere nell’ambito dell’opensource, programmi di controllo sono disponibili sia per piattaforme Linux che FreeBSD.
Occorre precisare che il protocollo X10 soffre anche di alcuni problemi e limitazioni, ad esempio:
- Compatibilità con l’impianto e gli altri apparecchi presenti
TV o wireless devices possono originare segnali spuri off oppure on. Meccanismi di filtraggio del rumore (as installed on computers as well as many modern appliances) possono salvaguardare i X10 signals da questo problema.
- Perdita di comandi
I segnali X10 possono unicamente venire trasmessi uno alla volta, dapprima indirizzando il device da controllare, quindi richiedendo a quel device di effettuare una operazione. Se due segnali X10 vengono trasmessi contemporaneamente possono collide o interleave, colla conseguenza che i comandi possano non venire decodificati per nulla o dare origine a operazioni non corrette.
- Relativa lentezza
Il protocollo X10 è anche lento. Impiega più o meno tre quarti di secondo per trasmettere un indirizzo di device ed un comando.Con i moduli più avanzati una opzione consiste nell’utilizzare comandi estesi per il controllo di gruppo (ambito illuminazione). Ciò consente la gestione di differenti moduli contemporaneamente tramite un singolo comando.
- Interferenza e mancanza di criptazione
I protocolli standard X10 su linea elettrica e RF mancano di un supporto per la criptazione, e sono in grado di indirizzare solamente 256 devices. Se non filtrati, segnali su linea elettrica provenienti da vicini di casa che pure utilizzino X10 possono interferire tra loro se uno stesso indirizzo di device dovesse venire utilizzato. Interferenze sui segnali RF wireless possono ugualmente manifestarsi.
Per Linux (in realtà per tutti i sistemi POSIX (Linux/BSD/e ad altri OS UNIX-like), esiste un device driver universale X10 (aka Project WiSH), distribuito secondo la GNU Library General Public License
Siamo alla seconda generazione delle versioni dei drivers X10 per Linux. Dal punto di vista dell’utente, la nuova revisione si comporta esattamente come la precedente con l’eccezione di un certo numero di features aggiunte.
Incluse nella attuale release vi sono:
- Pieno supporto delle funzionalità /dev/x10 con estensione al supportrto di scritture non-bloccanti
- Pieno supporto del PowerLinc Serial transceiver
- Pieno supporto del CM11A Serial transceiver
- Pieno supporto del PowerLinc USB transceiver (con kernel patches per USB)
Cosa è cambiato rispetto alla versione precedente:
- Un simulatore della macchina a stati X10 opera in userspace
- Un kernel module maintiene lo status di singoli devices ed implementa solamente le API
- Scritture non-bloccanti così che i comandi possano essere accodati in rapida successione.Cosa sono le scritture non-bloccanti?
Conescendo l’intrinseca lentezza del sistema e volendo comunque inviare comandi senza dover attendere che il sistema completi ogni comando. Impartendo comandi in modalità bloccante (typicamente tramite il comando echo) il driver si comporta esattamente come prima. Ma se il comando veniisse impartito in “non-blocking mode”, il driver schedulerebbe il comando e lo farebbe eseguire una volta arrivato il suo turno. Per rendere possibile ciò una nuova utility chiamata “nbecho” è stata inserita nelle distribuzione.
- PowerLinc USB ora utilizza l’interfaccia HID.
- I drivers versione 2.0 operano su kernel 2.6.7 e successivi
Il download del pacchetto wish si ottiene da http://sourceforge.net/projects/wish
Programmato in C e Java, WISH consiste di un (anzi più) moduli non-interattivi (Daemon), e di una interfaccia utente Web-based
Per quanto riguarda la sua installazione, avviene tramite compilazione da sorgente:
Ottenuto il source code di WiSH lo si scomprime in una locazione temporanea (io lo faccio tipicamente in /opt):
cd /opt/
tar xvzf wish-2.1.0.tar.gz; cd wish-2.1.0)
make
make install
- I daemons plusbd, cm11ad, e pld verranno copiati in /usr/sbin
- Le utilities x10logd ed x10watch verranno copiate pure esse in /usr/sbin
- Le utilities nbread ed nbecho verranno copiate in /usr/bin
- I moduli del kernel verranno copiati nella directory che ospita i moduli specifici per il nostro kernel (ad esempio /lib/modules/2.6.30/kernel/drivers/char/x10)
Ora che i drivers sono compilati ed installati, occorre caricarli. Per prima cosa occorre caricare il device manager (x10.ko per il kernel 2.6) e quindi mandare in esecuzione il daemon appropriato al transceiver X10 di cui si dispone.
Alcuni scripts destinati ad automatizzare start e stop dei drivers sono presenti nella directory example_scripts/ (ed installati in /usr/local/etc). Si può copiare uno script appropriato in /etc/rc.d/init.d/x10 per un sistema RedHat-like o richiamare lo specifico script a partire da /etc/rc.d/rc.local, ad esempio su di un sistema Slackware o anche Debian-like .
Nel caricare i moduli manualmente (pratica raccommendabile almeno in occasione del lancio iniziale), va caricato il device module e quindi i programmi userspace secondo le sequenze seguenti.
Serial PowerLinc
# modprobe x10
# /usr/sbin/pld -device /dev/ttyS0
# /usr/sbin/x10logd
USB PowerLinc
# modprobe x10
# /usr/sbin/plusbd -device /dev/usb/hiddev0
# /usr/sbin/x10logd
CM11A
# modprobe x10
# /usr/sbin/cm11ad -device /dev/ttyS0
# /usr/sbin/x10logd
I parametri che possono venire specificati per il modulo x10.o sono:
- data_major: il major number riservato al device per l’accesso alle singole unità (per default 120)
- control_major: il major device number per l’accesso alle funzioni riguardanti housecodes e status/log (per default 101)
- debug: impostandolo a 1 provoca la scrittura su console del tracciamento delle API (per default vale 0)
- syslogtraffic: se tale flag è impostato ad 1, tutto il traffico sulla rete X10 network verrà registrato da syslogd come messaggi kern.notice. Essi vengono tipicamente indirizzati alla console, su /var/log/dmesg, e su /var/log/messages.
- Impostandolo a 0 interrompe il logging del traffico da parte del driver sui syslogs. (per default vale 0, dunque off).
I parametri che possono venire specificati per i daemons in userspace sono:
- -api: Il file device driver interface X10 (per default: /dev/x10/.api)
- -pid: Il file su cui registrare il PID del driver (per default: /var/run/x10d.pid)
- -tag: Il tag da utilizzare nel syslog (per default corrisponde al nome del daemon)
- -timeout: Timeout espresso in millisecondi per l’attesa di una risposta dal device o dalla API (per default: 1000)
- -retries: Numero di possibili tentativi per attivazione (per default: 5)
- -delay: Numero di millisecondi di ritardo tra gli accessi al device (il valore di default dipende dal daemon). Potrebbe risultare importante per transceivers fiacchi o che non rispondano in maniera particolarmente affidabile. Inserendo una pausa dopo ogni accesso al device, al device viene dato il tempo di stabilizzarsi prima del successivo accesso.
- -fakereceive: fa in modo che il driver simuli la ricezione di ciò che trasmette. Il valore di default depende dal driver utilizzato. La maggior parte dei transceivers non rilevano ciò che inviano nel momento in cui lo inviano. Al contrario, transceivers come il PowerLinc Serial odono i loro stessi segnali. Il valore di default del driver può venire comunque sovrascritoo sulla linea di comando. Da notare che il log file conterrà un formato differente per qualsiasi dato artificialmente dovuto a fakereceive.
- -debug: attiva il debug
- -device: device interface per transceiver (nessun valore di default)
Se tutto è stato eseguito correttamente, dovreste ottenere i drivers caricati ed inoltre una linea nel kernel log ad indicare che sono stati avviati.
Per scaricare i drivers, occorre prima interrompere tutti i processi daemons che accedono al device e quindi scaricare il device module. Per terminare i daemons occorre inviargli un segnale HUP. Il daemon transceiver avvia un thread individuabile nella process list. Occorre inviare il kill al processo col numero più basso per rilasciare regolarmente i drivers. Per renderlo facilmente possibile, il process number viene memorizzato nel file in /var/run/x10d.pid.
Ad esempio, per terminare il daemon USB PowerLinc, vanno eseguiti i seguenti comandi:
kill -HUP `cat /var/run/x10d.pid`
kill -QUIT `cat /var/run/x10logd.pid`
kill -QUIT `cat /var/run/x10watch.pid`
rmmod x10
Il software ha la caratteristica particolare di generare devices a caratteri con due distinti major number e di conseguenza molti devices differenziati solo dal minor number.
Una volta che i devices siano stati creati e caricati, /var/log/messages dovrebbe evidenziare “x10 Transciever module v<version> (wsh@sprintmail.com)” ad indicare che il modulo del device è stato felicemente installato e conterrà “transmitter connected” ad indicare che il daemon è stato avviato con successo.
A questo punto, i normali programmi userspace sono in grado di accedere alla rete X10.
Dato che la soluzione che andremo a costruire sarà, per la parte sensori, funzionalmemte limitata ad interagire con la videosorveglianza, citerò solo i comandi che possono essere inviati ad unità individuali (/dev/x10/a1) o housecodes (/dev/x10/a) allo scopo di effettuare una attivazione oppure una disattivazione.
CMD Funzione
1, on, ON Commuta il device o gruppo di devices ad on
0, off, OFF Commuta il device o gruppo di devices ad off
I seguenti comandi possono venire inviati alle housecodes ma non alle unità individuali:
CMD Significato
aon Attiva tutte le luci per uno specifico housecode
aoff Disattiva tutte le luci per uno specifico housecode
uoff Disattiva tutte le unità relative all' housecode specificato
Mandare in esecuzione gli script di esempio o script simili come utente “root” dovrebbe essere una pratica da evitare, abbastanza ovviamente.
Dato che è potenzialmente insicura, è preferibile accedere alla propria rete X10 impersonando un utente con minori privilegi. Occorre comunque modificare le permissioni sui files presenti in /dev/x10/ in modo che l’utente scelto possa leggere e scrivere i device files. Per default i files vengono creati con la possibilità di essere scrivibili solo da “root”. Il programma in userspace può essere mandato in esecuzione come un normale utente senza alcun problema ma i moduli del kernel devono venire caricati come root.
Esempi di scripts
Come già anticipato, la directory example_scripts mette a disposizione alcuni scripts utilizzabili a casa per automatizzare un certo numero di compiti di base.
Alcuni files vengono utilizzati per l’automazione di compiti. Per rendere la cosa più flessibile e comprensibile, si possono creare dei links simbolici ai device, e modificare i links in quasiasi momento per modificare la configurazione della casa.
Per esempio si possono impostare alcuni links/aliases si potrebbero utilizzare nella nostra applicazione prototipo:
# mkdir /home/C1P8
# ln -s /dev/x10/a1 /home/C1P8/entratafronte
# ln -s /dev/x10/b1 /home/C1P8/entrataretro
# ln -s /dev/x10/b2 /home/C1P8/giardino
# ln -s /dev/x10/b3 /home/C1P8/frontegarage
# ln -s /dev/x10/b4 /home/C1P8/cancello
# ln -s /dev/x10/c1 /home/C1P8/soggiorno
# ln -s /dev/x10/c2 /home/C1P8/cucina
# ln -s /dev/x10/c3 /home/C1P8/ingresso
# ln -s /dev/x10/c4 /home/C1P8/corridoio
# ln -s /dev/x10/c5 /home/C1P8/camera
# ln -s /dev/x10/c6 /home/C1P8/bagnocamera
# ln -s /dev/x10/d1 /home/C1P8/lightsensor
Utilizzando links di questo tipo, cioè altamente mnemonici, gli scripts di controllo possono venire eseguiti per rilevare la comparsa di eventi che si verifichino nell’ambito del network X10.
Lo script seguente assume ad esempio che tutto sia posto ad off quando si avvia, entrando immediatamente in un ciclo continuo. Essa utilizza la utility “nbread” (distribuita col codice sorgente ed installata per default in /usr/bin/) per poter leggere lo status di una fotocellula. Quando è buio, la cellula invia un 1 sul proprio unitcode e invece quando c’è luce, invia uno 0.
#!/bin/sh
#
dir="`dirname $0`/"
device="/home/C1P8/photocell"
old_status=" "
while [ 1 = 1 ]
do
status=`nbread $device`
if [ "$status" != "$old_status" ]
then
old_status="$status"
if [ "$status" = "000" ]
then
${dir}action_daynight.sh off
else
${dir}action_daynight.sh on
fi
fi
sleep 10
done
action_daynight.sh è a sua volta uno script che viene richiamato per riconfigurare la rete X10 e serve nella fattispecie a spegnere i faretti esterni per la notte, fatta eccezione per la luce dell’ingresso centrale.
#!/bin/sh
#
dir="`dirname $0`/"
case "$1" in
on)
action="on"
;;
off)
action="off"
;;
*)
echo $"Usage: $0 {on|off}"
exit 1
esac
echo $action > /home/C1P8/entrataretro
echo $action > /home/C1P8/giardino
echo $action > /home/C1P8/frontegarage
La luce dell’ingresso centrale potrebbe venire disattivata tramite una azione programmata per essere mandata in esecuzione intorno all’ora d’alba.
Attivazioni schedulabili sono infatti possibili anche attraverso cron, utilizzando:
# crontab -e -u root
# minute hour dayofmonth month dayofweek command
30 22 * * * /usr/local/bin/extern_lights_off.sh
Il vantaggio di poter utilizzare un languaggio di programmazione consiste nel poter usare al bisogno letture bloccanti oppure non-bloccanti, per controllare lo status entrando in loop solo quando dei dati siano effettivamente disponibili. Tramite una lettura bloccante, viene eliminata la necessità di inutili loops continui in attesa di un evento.
Per fare un esempio, un frammento di codice che fa qualcosa di simile allo shell script che controllava il fotosensore potrebbe essere:
fd = open("/home/C1P8/lightsensor,O_RDWR);
status = 0;
if (fd < 0) {
fprintf(stderr,"Error opening x10 device\n");
exit 1;
}
while (1) {
n = read(inf,line,256);
if (n < 0) {
printf("Error: Unable to read %s\n",argv[1]);
return 1;
}
if (!strcmp(line,"000")) { // un nuovo status dalla fotocellula indica che rileva luce
fdtmp=open("/dev/x10/b",O_RDWR);
if (fdtmp < 0) {
fprintf(stderr,"Error opening /dev/x10/b\n");
break;
}
write(fdtmp,"aoff",1); // spegne tutte le luci relative all' housecode B
close(fdtmp);
}
else { // un nuovo status dalla fotocellula indica che rileva buio
nightlight=open("/home/C1P8/entratafronte",O_RDWR);
if (nightlightfd < 0) {
fprintf(stderr,"Error opening /home/C1P8/entratafronte\n");
break;
}
write(nightlight,"on",2); // la luce all'ingresso principale rimane sempre accesa
close(nightlight);
}
}
close(fd);
Per adesso mi pare abbastanza, pertanto vi do appuntamento alla prossima puntata:

