Interruttore luce automatico

Interruttore_luce_automatico

Stufi di accendere e spegnere la luce, ma non volete o non potete modificare l’impianto elettrico? Bene vediamo allora come costruire un sistema automatico che prema l’interruttore al posto nostro, alimentato a batteria.

Avremo bisogno sostanzialmente di una breadboard, un sensore pir, un micro servo sg90, due atmega328p (ogni cosa a circa 1 euro o meno su Ebay o Aliexpress), due led, resistenze da 1k, un p-mos, una batteria ricaricabile da 3,6V o in alternativa 3 batterie da 1,2V o 1,5V con il proprio portabatterie, per i pin di connessione alla breadboard e cavetti basta cercare su Ebay o Aliexpress pin male o dupont wire o cable o jumper.

Una volta individuato l’interruttore, bisogna fissare il servo sulla plastica dell’interruttore con un po’ di colla a caldo.

Interruttore_luce_automatico1Interruttore_luce_automatico2Interruttore_luce_automatico3

Una volta fatto questo bisognerà piazzare il sensore pir in una posizione centrale che copra tutta la zona di lavoro o movimento, per evitare letti o divani basterà incollare un po’ di cartone di fianco al sensore, fate tutte le prove del caso, per fissarlo al muro, sconsiglio adesivi molto forti in quanto potrebbero togliere l’intonaco della parete, aiutatevi con del fil di ferro da giardinaggio o cavi rigidi isolati.

Interruttore_luce_automatico6Useremo una fotoresistenza (ldr) come sensore per far sapere al nostro Atmega328p se è giorno o se è sera, se è giorno semplicemente spegnerà i dispositivi che consumano corrente e andrà in sleep per un po’ di tempo, poi il timer interno lo sveglierà di tanto in tanto per controllare nuovamente la fotoresistenza.

Se è sera invece, il servo verrà alimentato solo se ci sono variazioni di segnale da parte del sensore pir.

Interruttore_luce_automatico18Pur troppo la libreria servo.h e quella del risparmio energetico JeeLib.h non sono compatibili, nemmeno lowPower.h lo è, quindi la soluzione più economica è di usare due Atmega328p, uno per gestire il servo e uno per i controlli e le alimentazioni, in alternativa per chi volesse usare un solo microcontrollore, al posto del servo può usare due pistoni solenoidi in spinta da posizionare agli estremi dell’interruttore, si possono trovare su Ebay o Aliexpress digitando solenoid linear push 5V, fate attenzione che la corsa in mm e la forza siano sufficienti.

Per adesso vediamo nel dettaglio il codice del primo Arduino:

#include <JeeLib.h>
ISR(WDT_vect) { Sleepy::watchdogEvent(); }

byte ledr=7;
byte ledv=8;
int luce=0;
int luce_min=800;
byte pir_out=4;
byte pmos=0;
int pir;
byte vecchio_pir=HIGH;
int changed=0;
byte segnale1_out=5;
byte segnale2_out=6;
byte microVcc=1;
 
void setup() 
{
 pinMode(pmos,OUTPUT);
 pinMode(microVcc,OUTPUT);
 pinMode(pir_out,INPUT);
 pinMode(ledr,OUTPUT);
 pinMode(ledv,OUTPUT);
 pinMode(segnale1_out,OUTPUT);
 pinMode(segnale2_out,OUTPUT);
} 

void loop() 
{
 Sleepy::loseSomeTime(4000); 
 luce=analogRead(A0); 
 if(luce<luce_min)
 { 
 digitalWrite(ledr,LOW);
 pir=digitalRead(pir_out);
 
 if((pir!=vecchio_pir)&&(vecchio_pir==LOW)&&changed==0)
 { 
 vecchio_pir=pir;
 changed=1;
 }
 else if((pir!=vecchio_pir)&&(vecchio_pir==HIGH)&&changed==0)
 { 
 vecchio_pir=pir;
 changed=1;
 }
 else
 changed=0;
 
 if(changed==1)
 { 
 digitalWrite(microVcc,HIGH);
 digitalWrite(pmos,LOW);
  if(pir==LOW)
  { 
  digitalWrite(ledr,HIGH);
  digitalWrite(ledv,LOW);
  digitalWrite(segnale1_out,LOW);
  digitalWrite(segnale2_out,HIGH);
  delay(500);
  }
  else if(pir==HIGH)
  {
  digitalWrite(ledr,LOW); 
  digitalWrite(ledv,HIGH);
  digitalWrite(segnale1_out,HIGH);
  digitalWrite(segnale2_out,LOW);
  delay(500); 
  } 
  digitalWrite(ledr,LOW);
  digitalWrite(ledv,LOW);
  digitalWrite(segnale1_out,LOW);
  digitalWrite(segnale2_out,LOW); 
  delay(1500);
  digitalWrite(microVcc,LOW);
  digitalWrite(pmos,HIGH);
  }
 }
 else
 {
 digitalWrite(segnale1_out,LOW);
 digitalWrite(segnale2_out,LOW);
 digitalWrite(pmos,HIGH);
 digitalWrite(ledv,LOW);
 digitalWrite(ledr,HIGH);
 Sleepy::loseSomeTime(50);
 digitalWrite(ledr,LOW);
 Sleepy::loseSomeTime(10000);
 }
}

Per prima cosa si manda in sleep il microcontrollore per qualche secondo, si fa una lettura della fotoresistenza sul pin A0 e si memorizza questo valore in una variabile luce, se il valore di luce è sufficientemente alto, verrà fatto lampeggiare un led ogni circa 14 secondi e messo in sleep il microcontrollore, il tempo di sleep sarà infatti la somma di due sleep da 4 + 10 secondi .

Di notte verrà usato solo lo sleep di 4 secondi, spento il led rosso e letto il sensore pir. Il pir ha due stati in uscita alto o basso, siccome il microprocessore ripete ciclicamente le istruzioni, se avessimo eseguito solo alto o basso il servo avrebbe continuamente premuto il contatto, impedendoci di accendere o spegnere anche manualmente l’interruttore.

Se avessimo eseguito prima lo stato alto e poi quello centrale, il microcontrollore sarebbe entrato in un loop infinito eseguendo entrambe le operazioni continuamente, facendo oscillare il servo, quindi bisogna creare alla fine delle operazioni una posizione centrale stabile. Inoltre per risparmiare corrente bisogna rilevare solo i cambiamenti di stato del sensore pir, in questo modo il microcontrollore si sveglia ogni tanto, sente se lo stato è cambiato e torna a dormire.

Per fare questo controlleremo, ogni tanto, se lo stato di pir e vecchio_pir sono diversi e useremo una variabile changed, questa può assumere due posizioni stabili: 0 oppure 1, se la variabile pir non cambia di stato rispetto allo stato precedente vecchio_pir, rimane tutto com’è con changed a 0, il microcontrollore dopo il controllo torna in sleep, se però la variabile pir è diversa dallo stato precedente e changed è 0, verrà messa changed a 1 ed aggiornato lo stato di vecchio_pir, ed alimentato e fatto lavorare il secondo microcontrollore e il servo.

Se changed è 1 e lo stato di pir è LOW infatti verrà inviato un segnale alto dal pin 5 del primo Arduino al pin 7 del secondo Arduino, altrimenti verrà inviato un segnale alto, dal pin 6 del primo, al pin 8 del secondo Arduino, i segnali durano mezzo secondo dopodichè passano entrambi a LOW, con una pausa di circa un secondo e mezzo, questo permetterà al servo di lavorare e tornare in posizione centrale, dopodichè il servo verrà spento, se usate velocità del servo molto lente allora dovrete ritoccare il valore, aggiungendo più tempo di pausa.

Inoltre ci sono anche un led rosso e verde che si accendono per indicare la corsa del servo, a questo punto verrà spento il secondo microcontrollore e anche il servo.

Vediamo il codice del secondo Arduino:

#include <Servo.h>
 
Servo myservo;
int alto=1800;
int centro=1500;
int basso=1200;
int velox=2;
int pos=1500;
int pos1=0;
int segnale1;
int segnale2;
byte segnale1_in=7;
byte segnale2_in=8;

 
void setup() 
{
 myservo.attach(9); 
 pinMode(segnale1_in,INPUT);
 pinMode(segnale2_in,INPUT);
} 

void loop() 
{ 
 segnale1=digitalRead(segnale1_in);
 segnale2=digitalRead(segnale2_in);
 if(segnale1==HIGH)
 sposta_in_basso();
 else if(segnale2==HIGH)
 sposta_in_alto();
 sposta_in_centro();
}

void sposta_in_alto()
{
 pos1 = alto;
 sposta_lentamente(pos,pos1);
 pos = pos1;
}
void sposta_in_centro()
{
 pos1 = centro;
 sposta_lentamente(pos,pos1);
 pos = pos1;
}
void sposta_in_basso()
{
 pos1 = basso;
 sposta_lentamente(pos,pos1);
 pos = pos1;
}

 
 void sposta_lentamente(int posX, int posY)
{
 while(posX!=posY)
 { 
 if(posX<posY)
 posX++;
 else 
 posX--;
 myservo.writeMicroseconds(posX);
 delay(velox);
 } 
}

Il secondo microcontrollore quando è alimentato, legge continuamente i pin 7 e 8, se uno di questi due segnali in uscita dal primo microcontrollore è alto si muoverà il servo in una specifica direzione e poi in centro, diversamente non si muoverà.

Per muovere il servo ricorriamo a delle funzioni, sposta_in_alto(), sposta_in_centro() e sposta_in_basso(), a loro volta queste funzioni ricorrono ad una funzione che si occupa di muovere lentamente il servo, se preferite muoverlo velocemente con maggiore consumo, potete evitare l’ultima funzione e usare servo.writeMicroseconds() specificando tra parentesi la posizione, le funzioni partono tutte da una certa posizione, dopodichè si esegue la funzione spostalentamente() e dopo si aggiornano le posizioni.

La funzione spostalentamente prende due valori, posX e posY, posX è il valore iniziale e pos Y quello finale, se sono diversi la funzione sposta il servo di un passo alla volta fino a farli diventare uguali, se posX è minore di posY ci sarà un incremento altrimenti un decremento ad ogni ciclo, velox è il ritardo ad ogni ciclo e quindi la velocità (o meglio lentezza), per tempi minori di 1 ms è possibile usare delayMicroseconds() in microsecondi invece che di delay() in millisecondi.

Fate delle prove per sapere la posizione esatta di accensione e spegnimento del vostro interruttore, tenete presente che mentre caricate il programma cioè a 5V la corsa del servo sarà un po’ più ampia che a batteria o a tensioni inferiori, attenzione a non superare la fine corsa se no il servo sforzerà e consumerete parecchio.

Interruttore_luce_automatico13La cosa fondamentale per il funzionamento del dispositivo è la taratura, bisogna impedire alla luce della lampada di illuminare la fotoresistenza, altrimenti il dispositivo andrebbe in sleep con luce accesa, dovete fare una prova al buio con solo la luce della lampada accesa, il led rosso non deve lampeggiare ma il dispositivo deve poter muovere il servo, se necessario ombreggiate la parte superiore della fotoresistenza ma senza impedire alla luce naturale di illuminarla, e ritoccate il valore di luce_min in modo che sia il più basso possibile.

Interruttore_luce_automatico8Interruttore_luce_automatico7

Una soluzione migliore è quella di allungare i fili e spostare la fotoresistenza fuori da una finestra.

interruttore_luce_automatico9Siccome il sensore pir sente le variazioni di movimento, se state fermi per troppo tempo il sensore spegnerà le luci anche in vostra presenza, per ridurre questo effetto, regolate i trimmer della sensibilità e del timer del sensore pir al massimo o in base alle vostre esigenze.

Interruttore_luce_automatico12Di solito le batterie al litio hanno un circuito di protezione dalla sotto e sovraccarica, se la batteria dovesse scaricarsi o se ci fosse qualsiasi problema, si potrà sempre usare il comando manuale, volendo per ricaricare la batteria si potrebbe usare un pannellino solare indoor per esempio amorfo, oppure una normale ricarica da presa usb ed una resistenza per limitare la ricarica, tenete presente che batterie grandi hanno anche grandi autoscariche.

Interruttore_luce_automatico5Interruttore_luce_automatico4

Il consumo è quello del sensore pir (circa 50µA), della fotoresistenza (circa 20µA) e del microcontrollore in sleep (circa 6µA) per un totale di circa 72µA, qualcosa in meno di notte per via della fotoresistenza.

Interruttore_luce_automatico11

Il consumo energetico è di circa 0,072mAx24h=1,7mAh al giorno, circa 50mAh in un mese, circa 600mAh in un anno.

In più supponendo 60mA il servo per circa 20 secondi ogni sera, sono circa 60mAx(20/3600)h=0,33mAh aggiuntivi, circa 1,2mAh al giorno, in un mese 36mAh, circa 432mAh in un anno, quindi complessivamente circa 1000mAh in un anno.

E’ consigliabile inserire dei condensatori sull’alimentazione per aiutare a contenere i picchi di corrente assorbita.

Volendo è possibile usare i due led, per segnalare la tensione della batteria, in modo da sapere in anticipo se è carica o scarica ma questo lo vedremo meglio più avanti, eccovi comunque il codice, un saluto e alla prossima.

#include <JeeLib.h>
ISR(WDT_vect) { Sleepy::watchdogEvent(); }

byte ledr=7;
byte ledv=8;
int luce=0;
int luce_min=925;
byte pir_out=4;
byte pmos=0;
int pir;
byte vecchio_pir=HIGH;
int changed=0;
byte segnale1_out=5;
byte segnale2_out=6;
byte microVcc=1;
int vbatt=3900;
  
void setup() 
{
 pinMode(pmos,OUTPUT);
 pinMode(microVcc,OUTPUT);
 pinMode(pir_out,INPUT);
 pinMode(ledr,OUTPUT);
 pinMode(ledv,OUTPUT);
 pinMode(segnale1_out,OUTPUT);
 pinMode(segnale2_out,OUTPUT);
} 

void loop() 
{
  Sleepy::loseSomeTime(4000); 
  luce=analogRead(A0);      
  if(luce<luce_min)
  { 
    digitalWrite(ledr,LOW);
    digitalWrite(ledv,LOW);     
    pir=digitalRead(pir_out);
    
    if((pir!=vecchio_pir)&&(vecchio_pir==LOW)&&changed==0)
    { 
      vecchio_pir=pir;
      changed=1;
    }
    else if((pir!=vecchio_pir)&&(vecchio_pir==HIGH)&&changed==0)
    { 
      vecchio_pir=pir;
      changed=1;
    }
    else
    changed=0;
    
   if(changed==1)
   {  
      digitalWrite(microVcc,HIGH);
      digitalWrite(pmos,LOW);
      if(pir==LOW)
       { 
        digitalWrite(segnale1_out,LOW);
        digitalWrite(segnale2_out,HIGH);
        delay(500);
       }
       else if(pir==HIGH)
       {
        digitalWrite(segnale1_out,HIGH);
        digitalWrite(segnale2_out,LOW);
        delay(500);          
       } 
       digitalWrite(ledr,LOW);
       digitalWrite(ledv,LOW);
       digitalWrite(segnale1_out,LOW);
       digitalWrite(segnale2_out,LOW); 
       delay(1700);
       digitalWrite(microVcc,LOW);
       digitalWrite(pmos,HIGH);
    }
  }
  else
  {
   vbatt=readVcc();
   digitalWrite(segnale1_out,LOW);
   digitalWrite(segnale2_out,LOW);
   digitalWrite(pmos,HIGH);
   if(vbatt<3700)
   {
    digitalWrite(ledv,LOW);
    digitalWrite(ledr,HIGH);
   }
   else
   {
    digitalWrite(ledv,HIGH);
    digitalWrite(ledr,LOW);
   }
   Sleepy::loseSomeTime(50);
   digitalWrite(ledr,LOW);
   digitalWrite(ledv,LOW);
   Sleepy::loseSomeTime(10000);
  }
}

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring
  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both
  long result = (high<<8) | low;
  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

I commenti sono chiusi.