domenica 26 gennaio 2014

Interfacciare Arduino alle Philips Hue - parte 2 - il progetto

Prosegue la mia sperimentazione con le Philips Hue. L'idea di base era quella di rimpiazzare una lampada rotta in ufficio dotata di sensore di movimento (pir) e di luce, in grado di autonomamente determinare la presenza di una persona e di accendere le lampade fluorescenti dimmerandone la luminosità in funzione della reale illuminazione naturale della stanza.
Per replicare il funzionamento ho pensato di interfacciare le Phlips Hue con una scheda Arduino Ethernet, un sensore di presenza di tipo infrarosso (pir) e un sensore di luce.
Questa volta ho fatto la spesa su Exp-Tech che ha prezzi in linea con il resto del mercato, ma con un assortimento e disponibilità decisamente sopra la media.

  
Per una contenuta spesa di 71 euro ho portato a casa anche una Skeleton Box, ovvero un piccolo case trasparente in metallo e plexyglass tutto montabile senza viti, con dei semplici rivetti in plastica a pressione.
Il primo prototipo lo potete vedere in questa foto:


Come ben visibile, i due sensori li ho volutamente lasciati appesi all'esterno con del filo di ferro, in modo da poterne facilmente variare l'orientamento.
In particolare il sensore PIR è quello più delicato, in quanto pur avendo una copertura su quasi 180°, di fatto ha una copertura "a macchia di leopardo", per cui è bene avere ampia libertà di posizionamento per evitare poi malfunzionamenti una volta posizionato in modo definitivo il case.

Funzionamento

L'idea è molto semplice. Voglio che in presenza di una persona le luci si accendano automaticamente, ma solo quando sono realmente necessarie, ovvero in caso di scarsa illuminazione ambientale. In condizioni di luce naturale, o altra luce accesa nelle vicinanze, le luci invece dovrebbero spegnersi o restare inattive. Per evitare accensioni/spegnimenti troppo frequenti e innaturali, il sistema deve anche tenere conto dell'ultimo "avvistamento" di persone, e spegnere la luce 10 minuti dopo l'ultima attività rilevata (questo perchè una persona che lavora al computer si muove poco ed è facile che il pir rilevi movimenti a distanza anche di minuti).
Come evoluzione del progetto, oltre a gestire accensione e spegnimento automatico, si vuole dimmerare le luci in modo da compensare la carenza di luce naturale in modo progressivo man mano che viene notte (e viceversa, nella giornate invernali, quando il sole si alza nelle prime ore della mattinata).

Interfacciare i sensori Pir e di Luce

I due sensori vanno collegati alle porte analogiche. Entrambi hanno un funzionamento semplicissimo:
  • il sensore di luce Grove Light Sensor ha 4 pin, di cui 1 non utilizzato. 2 sono relativi all'alimentazione (+5V) e il terzo fornisce un segnale in tensione proporzionale alla luce misurata. Colleghiamo quindi i primi due a +5V/Gnd e il terzo su A0 di Arduino.
  • il PIR Sparkfun: va alimentato sempre +5V, e sul terzo polo relativo al segnale di allarme (SIG), applichiamo un pullup a +5V tramite una resistenza da 10K. Otteniamo così un segnale stabile a +5V in caso di sensore a riposo, e un segnale basso a GND quando viene rilevata attività. Colleghiamo il tutto a A1 di Arduino

Collegamento con le Philips Hue

Come già scritto nel post precedente, il Bridge della Philips deve essere collegato nella lan alla quale colleghiamo Arduino, e nella stessa classe di indirizzi ip.
Ad esempio, nella mia rete il Bridge ha assunto in automatico via DHCP l'indirizzo 10.10.1.160, e tale indirizzo dovrà essere cablato nel codice arduino per permettere l'invio di comandi tramite un semplice protocollo REST HTTP. Tutti i dettagli relativi a come individuare l'indirizzo corrente del bridge e l'attivazione di un utente li trovate nel precedente post.

Il programma

Per controllare le nostre luci attraverso i sensori necessitiamo di un ciclo che interroghi periodicamente i due sensori e mandi il comando al Bridge delle Philips Hue.
Per semplificare il coding ho utilizzato una libreria già vista nei post precedenti chiamata TimeAction. Tale library nasconde la gestione del tempo permettendo di definire la periodicità di esecuzione di una o più funzioni.
Il loop principale di arduino è costituito da 4 funzioni:

void loop() { 
 checkLightSensorAction.check();
 checkPirAction.check();
 checkPirTimeAction.check(); 
 setLightAction.check(); 
 delay(50); 
}
servono nel rispettivo ordine a leggere il sensore di luce, leggere il pir, scalare il tempo di accensione e inviare i comandi alla luce. Non fatevi ingannare da come è scritto il codice: la .check() su ogni action è di fatto temporizzata secondo la definizione, quindi non è che tutte e 4 le funzioni sono invocate ogni 50ms, ma ciascuna rispondere secondo la definizione che è impostata in questo modo:

#define PIR_CHECK_TIME 250  //tempo di check in ms del pir
#define LIGHT_CHECK_TIME 1000 //tempo in ms di check della luce
#define PIR_RETAIN_TIME  600 //tempo in secondi per tenere buono l'ultimo segnale pir ricevuto
#define LIGHT_SET_TIME 1 //tempo in s di invio dei comandi alle luci
TimedAction checkPirAction     = TimedAction(PIR_CHECK_TIME,checkPir);  //lettura pir
TimedAction checkPirTimeAction     = TimedAction(1000,checkPirTime);    //decremento tempo "on" , ogni secondo
TimedAction checkLightSensorAction     = TimedAction(LIGHT_CHECK_TIME,checkLightSensor);    //lettura sensore di luce
TimedAction setLightAction     = TimedAction(LIGHT_SET_TIME * 1000,setLight);    //pilotaggio delle luci
Per la lettura del sensore di luce viene usata la seguente funzione:
void checkLightSensor()
{
   sensorValueL = analogRead(sensorPinL);  
   Serial.print("L:");
   Serial.println(sensorValueL);
   if (sensorValueL<500)
   {
     lightState=HIGH;  //è buio, ma deve essere necessaria la presenza del pir per accendere
   }
   if (sensorValueL>700)
   {
     lightState=LOW; //c'è luce, quindi spegne comunque indipendentemente dal pir
   };
   currentBrightness= 255;
}
in pratica si verifica se la luminosità è minore di 500 (valore trovato sperimentalmente sul posto di installazione), in questo caso lo stato di accensione dovuto a luminosità, variabile lightState viene posta a HIGH. Se la luce cresce oltre i 700 (pieno sole) allora lo stato viene impostato a LOW.
Questa finestra di valori è necessaria, altrimenti alla prima accensione la luce si spegnerebbe immediatamente, questo perchè sperimentalmente ho verificato che in situazione di buio con la luce accesa il valore si attesta attorno ai 600-650. Un valore di 700 è quindi sufficiente a scongiurare "loop" inattesi.

Per il sensore pir la lettura è molto più semplice in quanto basata su due stati, ma per questo tipo di sensore è invece necessario lavorare con un tempo di "ritenzione" per far sì che la luce di spenga solo dopo tot minuti dall'ultima attività rilevata
void checkPir()
{
   sensorValueP = analogRead(sensorPinP);   
   if (sensorValueP < 500)
   { 
     personDetectedTime=PIR_RETAIN_TIME;
     setLed(HIGH);
   }
   else
   {
     setLed(LOW);
   }
}
Questa funzione ha anche lo scopo di accendere un led che mostri l'attività rilevata dal pir. Questo a livello sperimentale è indispensabile, altrimenti diventa molto difficile capire bene il campo di azione del pir.
Il led nel mio caso è collegato al pin 2 tramite una resistenza da 1K.
In pratica l'idea è: tutte le volte che viene rilevata attività, reimposto il tempo di "retain" al valore iniziale. 
E' compito di una terza funzione che entra in attività ogni secondo, di scalare il tempo di retain. In questo modo in caso di inattività prolungata il valore di retain (variabile personDetectedTime) viene decrementato ogni secondo fino ad arrivare a zero.
void checkPirTime() //decrementa il tempo di ritenuta ogni secondo 
{
  personDetectedTime--;
  if (personDetectedTime < 0) personDetectedTime=0;
  if (personDetectedTime > 0)
  {
    pirState=HIGH;
  }
  else
  {
    pirState=LOW;
  }
  Serial.println(pirState);
}
la combinazione delle due variabili pirState e lightState ci dice se la luce deve essere accesa o meno. Quando entrambe sono HIGH la luce deve accendersi (c'è una persona AND è buio), in tutti gli altri casi la luce deve spegnersi.
Un'altra particolarità del codice è che non vengono inviati comandi in modo continuo al Bridge, ma solo in caso di cambio stato oppure di variazione significativa della luminosità (in un ottica di espandere poi il progetto implementando il dimmer automatico delle luci).

Considerazioni Finali

Il prototipo funziona molto bene. Le luci si accendono in modo rapido appena rilevata la presenza. Per dare un tocco diverso ho sfruttato uno degli effetti disponibili via api, che permettono di ciclare le luci attraverso tutta la gamma di colori disponibili: all'accensione, per i primi 60 secondi viene attivata questa spettacolare opzione, per poi transitare al colore finale dopo un minuto.
Il colore finale NON viene comunicato, ma viene utilizzato l'ultimo schema di colori settato via iPhone/iPad, il chè significa che viene mantenuta la libertà di operare con l'app di controllo e rimane la possibilità di settare il colore desiderato. Arduino infatti invia solo 2 tipi di comandi: On/Off e Brightness (luminosità).


Materiale

Il sorgente completo del prototipo è disponibile qui


Nessun commento:

Posta un commento