Portale di visualizzazione prestazioni di impianto fotovoltaico

SPIEGAZIONE DEL METODO DI MISURA

Naturalmente sul Web non si può inviare l'informazione della potenza istantanea prodotta e consumata ogni secondo. Occorre quindi realizzare una gestione delle misure opportuna, per permetterne una comunicazione, una rappresentazione ed una conservazione ottimali.
La scelta di questo progetto è stata la seguente:

  • il microcontrollore Arduino realizza il calcolo dell'energia prodotta e consumata ogni minuto e lo trasmette al dispositivo Wifi serialmente
  • il dispositivo WiFi calcola l'energia prodotta e consumata ogni 5 minuti e le trasmette al portale Web
  • il portale web calcola l'energia prodotta e consumata ogni 15 minuti e li mostra nell'interfaccia grafica, realizzando anche il calcolo di parametri significativi, il riepilogo giornaliero, settimanale, mensile e annuale, nonchè il bilancio complessivo di energia acquistata e ceduta alla rete e la percentuale di autoconsumo rispetto alla componente acquistata dalla rete.

Di seguito trovate i codici di programmazione del microcontrollore Arduino

#include <SPI.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
constexpr uint8_t Sync = 2;
constexpr uint8_t SCLK = 4;
constexpr uint8_t DAT = 3;
constexpr uint8_t Led_Rosso = 8;
constexpr uint8_t Led_Verde = 9;
int sensorValue;
unsigned int Misura;
int cyccnt = 0;
int nm = 0;
byte stcur = 1;
int Volt_pin = A0;
int MaxVolt = 0;
long MedVolt = 0;
int DeVolt;
double Volt;
int Amp1_pin = A1; // corrente di produzione
int MinAmp1 = 1023;
int MaxAmp1 = 0;
int DeAmp1;
long MedAmp1 = 0;
double Amp1 = 0;
unsigned int WattSec1;
long WattMin1 = 0;
byte MaxVerso1;
byte AMax1 = 0;
int Amp2_pin=A2; // corrente di consumo
int MinAmp2 = 1023;
int MaxAmp2 = 0;
int DeAmp2;
long MedAmp2 = 0;
double Amp2 = 0;
unsigned int WattSec2;
long WattMin2 = 0;
byte MaxVerso2;
byte AMax2 = 0;
void setup()
{
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3C (for the 128x64)
  display.clearDisplay();
  display.setTextSize(2);
  display.setTextColor(WHITE);    
  pinMode(Sync, INPUT);
  pinMode(Led_Rosso, OUTPUT);
  pinMode(Led_Verde, OUTPUT);
  pinMode(SCLK, OUTPUT);
  pinMode(DAT, OUTPUT); 
  digitalWrite(SCLK, HIGH);
  digitalWrite(DAT, LOW);  
  digitalWrite(Led_Rosso, HIGH);
  digitalWrite(Led_Verde, HIGH);
  while(digitalRead(Sync) == LOW){
  }
}
void loop()
{
  if(stcur == 0){
    if (digitalRead(Sync) == HIGH){
      stcur = 1;
      cyccnt = cyccnt  + 1;
      DeVolt = MaxVolt;
      DeAmp1 = MaxAmp1 - MinAmp1;
      DeAmp2 = MaxAmp2 - MinAmp2;
      MedVolt = MedVolt + DeVolt;
      MedAmp1 = MedAmp1 + DeAmp1;
      MedAmp2 = MedAmp2 + DeAmp2;
      AMax1 = AMax1 + MaxVerso1;
      AMax2 = AMax2 + MaxVerso2;                  
      MaxVolt = 0; 
      MinAmp1 = 1023;
      MaxAmp1 = 0;
      MinAmp2 = 1023;
      MaxAmp2 = 0;
      if(cyccnt == 48){
        nm = nm + 1;     
        MedVolt = MedVolt + 2*DeVolt;
        MedAmp1 = MedAmp1 + 2*DeAmp1;
        MedAmp2 = MedAmp2 + 2*DeAmp2;                    
        Volt = 0.004711*double(MedVolt) + 35;
        Amp1 = 0.001025*double(MedAmp1) - 0.045;
        Amp2 = 0.0010255*double(MedAmp2) - 0.045;    
        if(Amp1 < 0) Amp1 = 0;
        if(Amp2 < 0) Amp2 = 0;         
        WattSec1 = int((Volt*Amp1) + 0.5);
        WattSec2 = int((Volt*Amp2) + 0.5);        
        display.clearDisplay();   
        display.setCursor(0,0);            
        display.print("V");
        display.print(int(Volt + 0.5));      
        if(AMax1 < 24) {
          display.println (" No P");          
        }
        else{        
          display.print(" P");   
          display.println(WattSec1);        
        }               
        if(AMax2 < 24) display.print(" -");
        else display.print(" +");        
        display.print("C");      
        display.print(WattSec2);          
        display.display();     
        MedVolt = 0;
        MedAmp1 = 0;
        MedAmp2 = 0; 
        if(WattSec1>WattSec2){ // Produzione > Consumo
          digitalWrite(Led_Rosso, HIGH);
          digitalWrite(Led_Verde, LOW);              
        }
        else { // Produzione <= Consumo
          digitalWrite(Led_Rosso, LOW);
          digitalWrite(Led_Verde, HIGH);          
        }        
        WattMin1 = WattMin1 + WattSec1;
        WattMin2 = WattMin2 + WattSec2;     
        AMax1 = 0;
        AMax2 = 0;          
        if(nm == 60){
          Misura = int(WattMin1/60);
          MeasureSend(Misura);       
          WattMin1 = 0;
          Misura = int(WattMin2/60);
          MeasureSend(Misura);                  
          WattMin2 = 0;
          nm = 0;
        } 
        cyccnt = 0;               
        while(digitalRead(Sync) == LOW){
        } 
        stcur = 1;
      }
    }
  }    
  else {
    if (digitalRead(Sync) == 0){
      stcur = 0;
    }
  }  
  sensorValue = analogRead(Volt_pin);
  if(sensorValue>MaxVolt) {
    MaxVolt=sensorValue;
  }    
  sensorValue = analogRead(Amp1_pin);
  if(sensorValue
    MinAmp1=sensorValue; 
  }
  if(sensorValue>MaxAmp1){
    MaxAmp1=sensorValue;
    MaxVerso1 = stcur;
  }    
  sensorValue = analogRead(Amp2_pin);
  if(sensorValue
    MinAmp2=sensorValue;  
  }
  if(sensorValue>MaxAmp2){
    MaxAmp2=sensorValue;
    MaxVerso2 = stcur;
  }
}                                                                                                                                                        
void MeasureSend(unsigned int mis){
    digitalWrite(SCLK, LOW);
    digitalWrite(DAT, HIGH);    // Start bit
    delayMicroseconds(50);
    for (byte i = 0; i < 16; i++) {
      digitalWrite(DAT, LOW);    // ith bit
      if(bitRead(mis, i) == 1){
        delayMicroseconds(50); // bit a 1        
      } 
      else {
        delayMicroseconds(25); // bit a 0          
      }     
      digitalWrite(DAT, HIGH);    // end bit 
      delayMicroseconds(25); // durata end bit   
    }   
    delayMicroseconds(50);     
    digitalWrite(SCLK, HIGH); 
}
                                                                                                                                                    

e del dispositivo WiFi.

#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266HTTPClient.h>
ESP8266WiFiMulti WiFiMulti;
long WattMin1; // Watt per minuto di Produzione
long WattMin2; // Watt per minuto di Consumo
long Misura1 = 0; // Produzione su nm minuti
long Misura2 = 0; // Consumo su nm minuti
long nm = 1;
long nc = 1;
String Url, Addr = "http://www.example.com/index.php?prd="; // inserire qui l'indirizzo del portale
void setup() {
  pinMode(D4, INPUT);
  pinMode(D3, INPUT);   
  pinMode(LED_BUILTIN, OUTPUT);
  digitalWrite(LED_BUILTIN, HIGH);
}
void loop() {
  if(digitalRead(D4) == LOW){
      WattMin1 = long(PowerReceive());
      WattMin2 = long(PowerReceive());   
      Misura1 = Misura1 + WattMin1;
      Misura2 = Misura2 + WattMin2;            
      nc = nc - 1;    
      if(nc < 1){
        Misura1 = Misura1/nm;
        Misura2 = Misura2/nm;
        nm = Connect();
        if(nm>5 || nm<1) nm = 1;                  
        Misura1 = 0;
        Misura2 = 0;
        nc = nm;
      }                     
  }
}
long Connect(){
/************************** 
 *  AREA DI TRASMISSIONE  *
 **************************/
        long wm;
        digitalWrite(LED_BUILTIN, LOW);   // Turn the LED on
        WiFi.mode(WIFI_STA);
        WiFiMulti.addAP("wireless", "password"); // Inserire qui nome_rete e password WIFI   
        if ((WiFiMulti.run() == WL_CONNECTED)) {
          HTTPClient http;
          Url = Addr + String(Misura1);
          Url = Url + "&cns=";
          Url = Url + String(Misura2);
          http.begin(Url); //HTTP
          int httpCode = http.GET();
          if (httpCode > 0) {
            if (httpCode == HTTP_CODE_OK) {
              String inf = http.getString();
              wm = atol(inf.c_str()); 
              Serial.println(wm);
            }
            else {
              wm = 1;
            }            
          } else {
            wm = 1;
          }
          http.end();
        }
        digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off 
        return wm;
}
unsigned int PowerReceive(){
    unsigned int mis, WattMin;  
    for(byte k=0; k<16; k++) {
      while(digitalRead(D3) == HIGH){}
      mis = 0;
      while(digitalRead(D3) == LOW){
        mis = mis + 1;          
      }
      if(mis > 75) bitSet(WattMin, k);
      else bitClear(WattMin, k);         
    }
    while(digitalRead(D4) == LOW){} 
    return WattMin;
}

CONSIDERAZIONI FINALI

Poichè la frequenza di rete non è precisa, le misure ogni cinque minuti tendono a disincronizzarsi dalla rappresentazione grafica del sito.
Per questo motivo, si è fatto in modo che la pagina web, che riceve i dati dal Power Controller, restituisca ad ogni misura il numero di misure che devono essere eseguite nell'intervallo successivo. Di norma il numero restituito è sempre 5, ma si aggiusta a correggere disincronizzazioni superiori al minuto.
L'impianto fotovoltaico, di cui si misura l'energia prodotta, è gestito da un Inverter SolarEdge. Questo Inverter scambia dati di produzione con un portale della casa-madre che non solo è consultabile dall'utente, ma permette attraverso API, un'interrogazione in Web Service dei dati registrati (con una frequenza di misura di 15 minuti). Quindi alla fine i dati di produzione registrati dal Power Controller sono stati sostituiti da quelli estratti dal portale SolarEdge (anche se le due misure avevano un'eccellente corrispondenza).
Il portale è visibile online all'indirizzo: InTeAS Home Energy.

REALIZZAZIONE DELL'APP

Pur avendo il sito una buona portabilità, essendo realizzato con moduli cosiddetti responsive, è molto semplice realizzare una versione app per Android (in formato apk), utilizzando App Inventor.
In Fig.12  è mostrata l'impostazione del progetto su App Inventor:

Fig.12