Get Adobe Flash player

Power Consumed Pulse Counter

User Rating:  / 0
PoorBest 

Link Articolo Originale: http://www.grix.it/viewer.php?page=8378

 

 

Semplice sistema per monitorare la potenza consumata mediante un contatore su guida DIN con Arduino + Ethernet

 

 

Descrizione
La domotica mi ha sempre affascinato e in questo periodo sto studiando una "rete" di sensori per il controllo remoto della mia abitazione. Come primo progetto ho deciso di monitorare tramite TCP-IP i consumi della rete elettrica in modo da poter effettuare una elaborazione dei dati su un computer Client Remoto. 

I Moderni Contatori su guida DIN sono provvisti secondo la "DIN 43-864" di una uscita optoisolata per acquisire i consumi mediante un datalogger. Tale uscita si presenta con due terminali denominati SO+ e SO-.

 

 

clicca per ingrandire
clicca per ingrandire

 

Questo è il circuito equivalente interno :

 

Le uscite non sono altro che i terminali Collettore ed Emettitore di un transistor lasciato in configurazione open-collector. Il contatore restituisce 2000 impulsi (eccitando il transistor) per ogni kWh letto. Per ottenere tali impulsi si dovrà collegare un resistore di pull-up sul terminale SO+ :

Con tale configurazione, quando il consumo di potenza dell'abitazione è minore di 4W (risoluzione minima del contatore) esso non invia impulsi sui pin SO, il transistor non è in conduzione e sul  pin DATA si avrà un valore logico alto pari a Vcc=5V :

 Nessun impulso sul pin SO ,consumo < 4W

 Quando il consumo è maggiore di 4W, il contatore incomincia a restituire una serie di impulsi. Ad ogni impulso entra in conduzione il transistor e sul pin data si avrà un valore logico basso:

 In presenza di impulsi sul pin SO ,consumo > 4W

La durata dell'impulso misurata è di 86 ms.

 Per determinare il consumo di potenza è necessario calcolare il tempo che intercorre tra un impulso e un altro.

2000 impulsi = 1KWh    =>  tempo che intercorre tra due impulsi: 3600 sec / 2000  = 1.8 sec

Ciò significa che se l'intervallo di tempo che intercorre tra un impulso e un altro è pari a 1.8 sec, allora la potenza consumata in quel periodo di tempo è pari ad 1KW.

Esempio: è stato collegato al contatore un carico di circa 800W (phon), il contatore ha restituito una serie di impulsi intervallati da 2.44 sec  

 

 (1.8 sec / 2.44 sec ) * 1000 = 737.7 W 

  

Acquisizione degli impulsi

Come sistema per acquisire gli impulsi ho scelto l'Arduino, per chi ancora non conosce la board vi rimando al mio primo articolo. Certo la board è un po' sprecata visto che sarà necessario un solo pin digitale però è la migliore soluzione che ho trovato, in quanto all'Arduino è possibile inserire una ulteriore board con modulo ethernet. Il contatore non si trova vicino al client che memorizzerà le letture su database e quindi la soluzione migliore è quella di un Arduino con modulo ethernet. Anche se utilizzato un solo pin per la letture della potenza, il "sistema" è molto espandibile.

 

clicca per ingrandire

 

Per determinare l'intervallo tra due impulsi viene utilizzato un interrupt con trigger sul fronte di discesa del segnale. Quando un primo impulso porta il segnale da H a L, viene richiamato l'interrupt0 dell'arduino che aziona un timer (in us). Quando viene ricevuto un secondo impulso l'interrupt disabilita il timer.  Una volta determinato l'intervallo viene calcolata la potenza consumata secondo la formula precedente.

Il Client per ottenere le misurazioni si connette all'Arduino mediante il protocollo Telnet.  Per avere un controllo giornaliero (24h/24h) è necessario un computer Client sempre attivo che consuma abbastanza potenza! Come client ho utilzzato una foxboard che tramite TCP-IP recupera i campioni dall'arduino, e li memorizza in un database per elaborare un grafico che verrà visualizzato in una pagina html. I campioni possono essere acquisiti da qualsiasi PC o sitema dotato di comunicazione TCP-IP e non è necessaria una foxboard! Ad esempio se disponete di un router con Linux OpenWrt con esso si possono elaborare i dati provenienti dall'arduino, senza lasciare il PC sempre acceso. Se non disponete di tutto ciò si può reaizzare un' applicazione per PC di tipo Real-Time che recupera i campioni e visualizza i consumi su un grafico in "tempo reale". 

 

 

 

Arduino Codice Sorgente

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 /*
 "Power Consumed Pulse Counter" is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.
 
 "Power Consumed Pulse Counter" is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with "Power Consumed Pulse Counter"; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
 Copyright Marco Antonini - (c) 2010 - Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.
 
 "Power Consumed Pulse Counter" v 1.1
 
     Connecting the Counter (SO+,SO-)
     
                       [SO+]  [SO-]-------[GND]
                         |
                         |     10k
  [Digital 2] ------o---///---- [Vcc=5V]
                    
 */
 
/* Software Version */
#define VERSION 1.2
 
#include <Ethernet.h>
#include <MsTimer2.h>
 
#define INTERRUPT_SO 0 // Digital 2 
 
/* OPTIONS */
#define WATCHDOG // comment this line to disable the watchdog
#define DEBUG // comment this line to disable serial debug
 
// Network Configuration.
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; //mac address
byte ip[] = { 10, 0, 1, 33 }; // ip
byte gateway[] = { 10, 0, 1, 1 }; // gateway
byte subnet[] = { 255, 255, 255, 0 }; // subnet
int server_port = 23;  // Telnet Server Port
 
 
#ifdef WATCHDOG // include WATCHDOG
#include <avr/io.h>
#include <avr/wdt.h>
#endif  
 
/* Volatile Interrupt Variable */
volatile boolean timer_status_so;  // status timer so: true=start , false=stop
volatile unsigned long timer_start_so; // time (us): timer start
volatile unsigned long timer_stop_so;  // time (us): timer stop
volatile unsigned long delta_pulse_so; // time pulse interval so (us)  
 
// init Telnet Server on port server_port
Server server(server_port);
 
 
/* Function init_eth0 */
void init_eth0(void) {
   Ethernet.begin(mac, ip, gateway, subnet);
   server.begin();
}
 
/* Setup */
void setup(void) {
 
#ifdef WATCHDOG // init WATCHDOG 2 sec
   wdt_enable(WDTO_2S);
#endif  
#ifdef DEBUG
   Serial.begin(9600); //Serial Debug baudrate
   debug_network_info();
#endif
   attachInterrupt(INTERRUPT_SO, pulse_so, FALLING); // interrupt pulse counter so, FALLING: HIGH --> LOW
   timer_status_so=true; // init status timer so
   init_eth0(); //init ethernet
   MsTimer2::set(7200000,init_eth0); //init ethernet every 2 hour
   MsTimer2::start();
}
 
/* Fuction Pulse Counter so:  pulse_so(void)
 * function invoked by interrupt 0
 */
void pulse_so(void) {
   if(timer_status_so == true ) { // control status timer
      timer_start_so=micros(); // start timer  
      timer_status_so=false; // update variable timer_status
    } 
    else { // timer_stop
      timer_stop_so=micros(); // stop timer
      timer_status_so=true; // update variable timer_status
      if(timer_stop_so > timer_start_so) // control micro() reset
         delta_pulse_so=timer_stop_so-timer_start_so; // calculating the time interval pulse      
    }
}
 
 
/* Loop */
void loop() {
  
#ifdef WATCHDOG //reset WATCHDOG
   wdt_reset();
#endif
 
   // reset the variable if the time of the pulses are greater than 450 seconds ( < 4W) 
   if((micros()-timer_start_so) > 450000000 && timer_status_so==true) 
      delta_pulse_so=0.0; 
 
   /* 2000 pulse = 1W*h => pulse interval = 1,8 sec 
      (1,8 / (delta_pulse / 10^6) )*1000 = W
    */   
   float w=1800000000.0/(float)delta_pulse_so;
   
   Client client = server.available(); // Client Telnet 
 
   /* Telnet console*/
   if (client) {
        char command = client.read();
        
        if(command == 'p' ) { //Show Power
   server.print("P: ");
   server.println(w);
        }        
        else if (command == 'e') //exit connection
  client.stop(); 
        else if (command == 'h') { //show help
          server.println("Command available:");
          server.println("                   'h' this help");
          server.println("                   'p' show power");
          server.println("                   'e' exit telnet");
        }
        /* ADD Sensor*/
        
    }
    /* Serial Debug */
    #ifdef DEBUG
       if(Serial.available()>0) {
         char c = Serial.read();
         if( c == 'r' ) { // command 'r' : Show Power 
             Serial.println("> Power Consumed");
             Serial.print("  P: ");
             Serial.print(w);
             Serial.println(" W");
          }
          else if ( c == 'h') { // Command 'h' : Show Help 
             Serial.println("> Help");
             Serial.println("  Usage:  'h' show help");
             Serial.println("          'r' show Power");
             Serial.println("          'n' show Network Info");
          }
          else if (c == 'n') { // Command 'n' : Show Network Info
            debug_network_info();
          }
          else { // Unknown Command 
            Serial.print("> '");
            Serial.print(c);
            Serial.print("'");
            Serial.println(" Unknown Command!");
          }
        }       
    #endif
}
 
 
/* Fuction debug Network info : debug_network_info(void) 
   Show the Network Settings
 */
 
#ifdef DEBUG
 
void debug_network_info(void) {
 
  if(Serial.available()>=0) {
     Serial.print("**** Power Consumed  Pulse Counter (v. ");
     Serial.print(VERSION,1);
     Serial.println(") ****");
     Serial.println("");
 
     /* Print Mac Address */
     Serial.print("* Mac:   "); 
     for(int i=0; i<6; i++) {
Serial.print(mac[i],HEX);
if(i!=5)
   Serial.print(":");
else
   Serial.println("");
     }
     /* Print IP */
     Serial.print("* Ip:    ");
     for(int i=0; i<4; i++) {
Serial.print((int)ip[i]);
if(i!=3)
   Serial.print(".");
else
   Serial.println("");
     }
     /* Print Netmask */
     Serial.print("* Mask:  ");
     for(int i=0; i<4; i++) {
Serial.print((int)subnet[i]);
if(i!=3)
   Serial.print(".");
else
   Serial.println("");
     }
     /* Print Gateway */
     Serial.print("* Gw:    ");
     for(int i=0; i<4; i++) {
Serial.print((int)gateway[i]);
if(i!=3)
   Serial.print(".");
else
   Serial.println("");
     }
     /* Print Telnet Server Port*/
     Serial.print("* Port:  ");
     Serial.println(server_port);
     Serial.println("");
     Serial.println("> send 'r' to show the Power Consumed.. ");     
     }
}
#endif
 

 
Per modificare le impostazioni di rete come il mac-address,ip,netmask,gateway basta modificare la sezione OPTIONS in cima al codice. Ho inserito una sezione di "debug" su seriale dove viene restituita una mini console con le attuali impostazioni del network:
 
 
Espansione
 
Porte utilizzate :
Arduino Pin Digitale 2  => Lettura impulsi
Arduino Pin Digitale 10,11,12,13  => ethernet
 
Con le porte libere rimaste (digitali 3,4,5,6,7,8,9 analogiche 0,1,2,3,4,5) è possibile aggiungere sensori per monitorare l'abitazione, come rilevatori di presenze, sensori di temperatura,sensori per fughe di gas.. Per fare ciò basta modificare la sezione "/* ADD Sensors... */" . Per visualizzare da remoto (Telnet) il nuovo sensore si dovrà aggiungere il comando:

server.println()

 
Esempio, aggiunta di un sensore analogico e lettura di una porta digitale:

/* ADD Sensors... */
else if (command == 'a') { //example, command 'a' => show ADC0
   int ad=analogRead(0);  //read ADC0
   server.print("AD0: ");
   server.print((float)0.0049*ad); //print value in volt
}
else if (command == 'd') { //example, command 'd' => show Digital 5
   server.print("D5: ");
   server.print(analogRead(5)); //print status digital 5
}

 
 
Installazione Contatore
 
 
           
clicca per ingrandire
 
                                                                
Per le informazioni riguardanti l'installazione del contatore, l'autore dell'articolo non si assume nessuna responsabilità.
L'installazione deve essere effettuata da un tecnico esperto!  
 
clicca per ingrandire
 
I collegamenti tra arduino e contatore sono banali, possono essere effettuati in "aria" con un paio di fili e un resistore da 10k. Poichè non sono amante di tali collegamenti "volanti", ho aggiunto un pezzetto di millefori che si incastra all'arduino lasciando libere le porte inutilizzate per eventuali future espansioni.
 
clicca per ingrandire
 
 
 
Risultati
 
 Una volta installato il "sistema", dal terminale di un computer client si deve lanciare il comando :
 

     telnet arduino_ip server_port

 
 Lanciato il comando verrà mostrata una "mini" console dove si potrà interagire con l'arduino mediante Telnet.
 La versione del codice di default accetta i comandi:
 "p"  per mostrare la potenza consumata
 "h"  per visualizzare i comandi disponibili
 "e"  per chiudere la connessione
 Se sono stati aggiunti sensori come nell'esempio del paragrafo "Espansione" allora è possibile digitare i comandi corrispondenti scelti (nell'esempio: "a" mostra lo stato della porta analogica0 e "d" mostra lo stato della porta digitale5 ).
 
 
 Per la memorizzazione su database dei campioni da parte del client remoto, ho realizzato questo semplice  programma in python:
 

 
"Power Consumed Pulse Counter"
#   
Marco Antonini - 2010 - Questo indirizzo email è protetto dagli spambots. È necessario abilitare JavaScript per vederlo.
 
import time
import sys
import telnetlib
from pysqlite2 import dbapi2 as sqlite
 
HOST = "10.0.1.33"
PORT = 23
arduino_tn = telnetlib.Telnet(HOST,PORT)
arduino_tn.write("pn")
arduino_tn.write("en")
power = arduino_tn.read_all()
power = power.replace("P: ","").replace("n","").replace("r","")
power = eval(power)
 
connection = sqlite.connect('/var/www/power_consumed.sqlite')
cursor = connection.cursor()
cursor.execute('CREATE TABLE IF NOT EXISTS samples (id INTEGER PRIMARY KEY AUTOINCREMENT, date TEXT, time TEXT, value FLOAT)')
cursor.execute('INSERT INTO samples VALUES (null, date("now","localtime"),time("now","localtime"),' + "%.2f" % power + ')')
connection.commit()
print power
 
 
 
 

 
Ogni volta che il programma viene eseguito, viene recuperato un campione di potenza misurata dall'arduino, e memorizzato su database con la data corrente dell'acquisizione.
Per ottenere i consumi di tutta la giornata, basta eseguire uno script che 
 
 
 
richiama questo programma
 
con frequenza ad esempio di 10 minuti.
   
 
 
Questi sono i risultati delle mie acquisizioni:   
 
 
 
 
 
 
 
 
 
 
                                                                                                               
clicca per ingrandire
clicca per ingrandire
clicca per ingrandire
clicca per ingrandire
  
  
 
 
 
 
 
 
 
 
 
 
E' 
 
interessante notora i picchi regolari del frigorifero (circa 130W ogni ora).  Il picco di 2,26kW nel secondo screenshot è il forno elettrico mentre il picco nel terzo screenshot è dovuto dalla lavastoviglie.
 
  
 
 
 
 
 
 
 
 
 
 
 
Video:   
 
 
  
 
 
 
 
                     
 
 
 
 
   
 
 
 
 
 

 

 

 

 

 

 

Download Codice Sorgente

 

Login Form

Elettronica Open Source