27 Aprile 2024

Benvenuti nel BLOG di Vincenzo

Il blog realizzato mattoncino su mattoncino…!

This is a solar-powered IOT smart irrigation system based on an ESP8266.

Questo è un sistema di irrigazione intelligente IOT a energia solare basato su un ESP8266.


L’intento primordiale di pensare a un tale progetto nasce da necessità dettate da circostanze storico-tecnologiche decisamente non a noi distanti, ma che piuttosto oggigiorno più che mai si intravedono insidiarsi con lestezza sconvolgente nella quotidianità umana. Mi riferisco in primis al bisogno di curare efficientemente uno spazio verde, e a quello di rendere indipendente un sistema di irrigazione nella sua completezza, sia in termini di consumo elettrico, sia di fabbisogno idrico. A questo proposito mi sono occupato della realizzazione di un impianto IOT (Internet Of Things) proprietario di irrigazione intelligente alimentato grazie al Sole, incessante e vigorosa fonte energetica.

COMPONENTISTICA

Servendomi dunque della comoda versatilità che i microcontrollori offrono, ho preferito selezionare un Wemos D1 Mini (Fig. 1.1) come cervello centrale del prototipo, basato sulla CPU dell’ESP8266 con Wi-Fi integrato a basso costo. Usando poi l’IDE (Integrated Development Environment, Fig. 1.2) di Arduino che già possiede l’SDK (Software Development Kit) che ha permesso la programmazione diretta del chip, ho avuto modo di scrivere nel linguaggio C++ l’intero algoritmo da far eseguire al microcontrollore.

Il sistema di sensoristica adottato in tal circostanze comprende: un sensore di temperatura e umidità ambientale (DHT11, Fig. 2.1), uno di umidità del suolo di tipo capacitativo e quindi quasi del tutto esente da corrosione data la mancanza di un contatto diretto che gli elettrodi hanno con il terreno (Fig. 2.2), un fotoresistore per valutare la luce ambientale e un misuratore di tensione per monitorare lo stato di produzione del pannello fotovoltaico (Fig. 2.3) e la carica della batteria (12v – 7,2Ah) essenziale per l’accumulo dell’energia eccedente da usare in condizioni di scarsa luminosità o assenza di luce. Da non tralasciare per giunta l’unico output della suddetta centralina, ossia il relè che aziona l’elettrovalvola adoperata (Fig. 2.4) all’immettere acqua nel sistema di tubazioni vero e proprio per irrigare l’orto solamente in precisi e mirati frangenti di tempo ed in determinate condizioni ambientali. (Circuito allegato in fondo all’articolo)

FUNZIONAMENTO

In fase di programmazione, inoltre, il prototipo è stato previsto con tre distinte modalità d’irrigazione: quella MANUALE dove l’utente da una web GUI (Graphical User Interface, Fig 3, pag. suc.) può scegliere con estrema specificità in che giorni, a che ora e per quanto tempo irrigare; quella SEMI-MANUALE che annette a quella precedente il vincolo di una soglia regolabile di umidità del suolo da considerare per evitare di sovra innaffiare il terreno e quello di considerare la presenza di precipitazioni; infine quella AUTOMATICA che cede piena gestione dei tempi e dei momenti giusti per irrigare alla centralina che pondera le proprie scelte in base a un algoritmo che innaffia il terreno cercando di mantenere il grado di umidità del suolo costante.

Al termine di ogni atto di irrigazione, viene direttamente spedito all’utente un report (Come il seguente) sull’effettivo incremento di umidità del terreno ottenutosi perché raggiunto il target, ponendo eventualmente l’utente dinanzi alla possibilità di optare per proseguire l’irrigazione.

IMPLEMENTAZIONI

L’aver presupposto l’utilizzo di un’interfaccia grafica ha quindi reso possibile il confronto di una non indifferente mole di dati, assiduamente raccolti dalla centralina, in grafici e permesso di effettuarne i relativi studi per implementare in futuro nuovi algoritmi di irrigazione integrando informazioni da molteplici altri sensori per innaffiare con ancor meno spreco d’acqua sfruttando al massimo le condizioni atmosferiche di umidità o pioggia, magari istruendo nel migliore dei piani un’intelligenza artificiale a riguardo. Tuttavia, si prospetta interessante lo sviluppo in futuro di un sistema di irrigazione per mezzo di acqua piovana raccolta in apposite cisterne al fine di usufruire dell’acqua corrente solo in casi eccezionali, il tutto reso possibile da un apparato di elettrovalvole e pompe rigorosamente alimentate a energia solare. Da notare che in un’implementazione del genere, il SOLE rappresenterebbe di fatto l’unica fonte energetica sfruttata poiché l’acqua necessaria sarebbe recuperata da precipitazioni generate dal “ciclo dell’acqua” mosso dal sole stesso.

Ergo, l’installazione di un siffatto sistema di irrigazione non può che aver apportato un evidente perfezionamento della qualità del trattamento idrico dell’orto e avvalendosi del Sole come fonte energetica si è riusciti a disimpegnarlo completamente dall’utilizzo di energia esterna prodotta a partire da dubbie fonti quasi sicuramente inquinanti.




SOFTWARE:

//--GARBADOL Project--    www.vincenzonet.it

#define BLYNK_TEMPLATE_ID "TMPLEX1Ex2Cg"
#define BLYNK_DEVICE_NAME "EspGarbadol"
#define BLYNK_AUTH_TOKEN "xm5O0PtHC7wdZMfM0e_dhG6MRiODF1ru"

// Comment this out to disable prints and save space
//#define BLYNK_PRINT Serial

#include <ESP8266WiFi.h>              //lib
#include <BlynkSimpleEsp8266.h>       //lib
char auth[] = BLYNK_AUTH_TOKEN;
char ssid[] = "Chromenet";
char pass[] = "@12345braucci";
BlynkTimer timer;

#include <DHT.h>                      //lib
#define DHTPIN 14         
#define DHTTYPE DHT22   // DHT 22, AM2302, AM2321
DHT dht(DHTPIN, DHTTYPE);
float h;
float t;

// 2/3x gain +/- 6.144V -- 1 bit = 0.1875mV (default) -- 15bit
#include <Adafruit_ADS1015.h>         //lib
Adafruit_ADS1115 ads(0x48);

#include <TimeLib.h>                  //lib
#include <WidgetRTC.h>                //lib
WidgetRTC rtc;

WidgetTerminal terminal(V0);

//************************LUX
int valoreLdr = 0;       // usata per i valori (0-32767)
float Vout = 0.0;        //Voltaggio uscita partitore
float Vin = 5.0;         // Vcc (5 Volts) voltaggio arduino
float R_nota = 9980.0;   // Valore misurato della resistenza nota (10Komm)

float valore;            // valore lux                           
float Ldr = 0.0;         // Valore calcolato della resistenza Ldr.
float Ldr_1 = 75000.0;   // Valore ldr illuminamento unitario
float Pend  = 0.66;      // Valore gamma fotoresistenza
double Lux = 0.0;        // Lux calcolati

//************************Capacitive_soil_moisture_sensor_v1.2
const float AirValue = 21000;    //
const float WaterValue = 12350;  //8724

#define solenoide 16
int modalita;
int soglia_min;
int soglia_int;
int orario;
int per_quanto;
int verif=0;
int verif_mod=1;
int verif_mod_2;
int autenticazione_giorni;


boolean lun;
boolean mar;
boolean mer;
boolean gio;
boolean ven;
boolean sab;
boolean dom;

int lettura_prima;
int lettura_dopo;
int differenza_letture;
int verif_man=0;

float vout_bat = 0.0;
float vin_bat = 0.0;
float R1_bat = 440000.0; 
float R2_bat = 220000.0;

unsigned long t0 = 0;
unsigned long caso_giorni_spegni = 0;
unsigned long dt;
unsigned long tp_save;

String currentTime(){
  return String(hour()) + ":" + minute() + ":" + second();
}
String currentDate(){
  return String(day()) + "/" + month() + "/" + year();
}

String insieme(){
  return currentTime() + " - " + currentDate();
}

float soilMoistureValue(){
  return ads.readADC_SingleEnded(2);
}
   
int soilmoisturepercent(){
  return round(100-(100*(constrain(soilMoistureValue(),WaterValue,AirValue)-WaterValue)/(AirValue-WaterValue)));
}

void standard()
{  
   valoreLdr = ads.readADC_SingleEnded(0);
   Vout = valoreLdr*0.0001875;             // Voltaggio partitore
   Ldr = ((R_nota * Vin/Vout)- R_nota);    // Resistenza fotodiodo
   Lux = pow((Ldr/Ldr_1), (1.0/-Pend));
   Blynk.virtualWrite(V1, round(Lux));
   Blynk.virtualWrite(V2, round(map(Vout,0,5,0,100)));
   Blynk.virtualWrite(V3, Vout);

   h = dht.readHumidity();
   t = dht.readTemperature();      
   Blynk.virtualWrite(V4, t);
   Blynk.virtualWrite(V5, h);

   Blynk.virtualWrite(V6, soilmoisturepercent());
   Blynk.virtualWrite(V7, soilMoistureValue()*0.0001875);

   vin_bat = ads.readADC_SingleEnded(3)*0.0001875;
   vout_bat = constrain(vin_bat / (R2_bat/(R1_bat+R2_bat)), 0, 15) + 0.3;
   Blynk.virtualWrite(V22, vout_bat);
}


void vita(boolean x){
  digitalWrite(solenoide, !x);
  if (x==HIGH && verif==0){
    terminal.print("Irrigazione ACCESA: "); terminal.print(insieme());
    terminal.flush();
    Blynk.logEvent("stato_irrigazione", String("L'irrigazione è stata ACCESA."));
    Blynk.virtualWrite(V9, x);
    verif=1; 
  }
  if (x==LOW && verif==1) {
    terminal.print("Irrigazione SPENTA: "); terminal.print(insieme());
    terminal.flush();//www.vincenzonet.it
    Blynk.logEvent("stato_irrigazione", String("L'irrigazione è stata SPENTA."));
    Blynk.virtualWrite(V9, x);
    verif=0;
  }
}

void verifica_irrig(){
 while (verif_mod){
  t0=millis();
  lettura_prima=soilmoisturepercent();
  verif_mod=0;
  }
  
 dt = (millis()-caso_giorni_spegni) - t0;
 if ( dt >= (per_quanto*60000)) {
  t0 = millis();
  vita(false);
  lettura_dopo=soilmoisturepercent();
  verif_mod=1;       
  verif_mod_2=1;
  Blynk.virtualWrite(V21, HIGH);
  String corpo = String("Irrigazione avvenuta con un incremento del ") + (lettura_dopo-lettura_prima) + "% di umidità del terreno. Valore Attuale: " + lettura_dopo + "%";
  Blynk.logEvent("avvenuta_irrigazione", corpo);
  }
 else{
  vita(true);
  }
}

void irrigazione(){
   
   if (modalita==1){
    if (soilmoisturepercent()<soglia_min){
      vita(true);
    }
    if (soilmoisturepercent()>=(soglia_min+soglia_int)){
      vita(false);
    }
    if (verif_man==1){
      verif_man=0;
      terminal.print("** Modalità MANUALE disattivata. **  "); terminal.print(insieme()); terminal.flush();
      Blynk.logEvent("modalit_manuale_dis", String("Modalità MANUALE disattivata."));
     }
   }
                      //BUG magari si potrebbe fare: if (lun==1 && weekday()==2 && hour()==orario && 0<minutes()<(per_quanto*60000)){
   if (modalita==0){      
     if (lun==1 && weekday()==2 && hour()==orario && soilmoisturepercent()<=soglia_min && verif_mod_2==0){
     verifica_irrig();   //www.vincenzonet.it
     }
     else if (mar==1 && weekday()==3 && hour()==orario && soilmoisturepercent()<=soglia_min && verif_mod_2==0){
     verifica_irrig(); 
     }
     else if (mer==1 && weekday()==4 && hour()==orario && soilmoisturepercent()<=soglia_min && verif_mod_2==0){
     verifica_irrig(); 
     }
     else if (gio==1 && weekday()==5 && hour()==orario && soilmoisturepercent()<=soglia_min && verif_mod_2==0){
     verifica_irrig(); 
     }
     else if (ven==1 && weekday()==6 && hour()==orario && soilmoisturepercent()<=soglia_min && verif_mod_2==0){
     verifica_irrig();
     }
     else if (sab==1 && weekday()==7 && hour()==orario && soilmoisturepercent()<=soglia_min && verif_mod_2==0){
     verifica_irrig(); 
     }
     else if (dom==1 && weekday()==1 && hour()==orario && soilmoisturepercent()<=soglia_min && verif_mod_2==0){
     verifica_irrig(); 
     }
     else {
      vita(false);
     }
     if (verif_man==1){
      verif_man=0;
      terminal.print("** Modalità MANUALE disattivata. **  "); terminal.print(insieme()); terminal.flush();
      Blynk.logEvent("modalit_manuale_dis", String("Modalità MANUALE disattivata."));
     }
   }

    if (modalita==2){
      if (lun==1 && weekday()==2 && hour()==orario && verif_mod_2==0){
     verifica_irrig();
     }
     else if (mar==1 && weekday()==3 && hour()==orario && verif_mod_2==0){
     verifica_irrig(); 
     }
     else if (mer==1 && weekday()==4 && hour()==orario && verif_mod_2==0){
     verifica_irrig(); 
     }
     else if (gio==1 && weekday()==5 && hour()==orario && verif_mod_2==0){
     verifica_irrig(); 
     }
     else if (ven==1 && weekday()==6 && hour()==orario && verif_mod_2==0){
     verifica_irrig();
     }
     else if (sab==1 && weekday()==7 && hour()==orario && verif_mod_2==0){
     verifica_irrig(); 
     }
     else if (dom==1 && weekday()==1 && hour()==orario && verif_mod_2==0){
     verifica_irrig(); 
     }
     else {
      vita(false);
     }
    }
    if (hour()==0 && minute()==0){
    verif_mod_2=0;
    Blynk.virtualWrite(V21, LOW);
    }
}

//IMPORTANTE

BLYNK_CONNECTED() 
{  rtc.begin();
   terminal.print(" "); 
   terminal.flush();
   terminal.print("----GARBADOL Project----");
   terminal.flush();
   terminal.print("Nuova connessione: "); terminal.print(insieme());
   terminal.flush();
   terminal.print("Parole ammesse: {Marco, Reset, Manuale, Mode}");
   terminal.flush();
   Blynk.syncVirtual(V8);
   Blynk.syncVirtual(V10);
   Blynk.syncVirtual(V11);
   autenticazione_giorni=1;
   Blynk.syncVirtual(V12);
   Blynk.syncVirtual(V13);
   Blynk.syncVirtual(V14);
   Blynk.syncVirtual(V15);
   Blynk.syncVirtual(V16);
   Blynk.syncVirtual(V17);
   Blynk.syncVirtual(V18);
   Blynk.syncVirtual(V19);
   Blynk.syncVirtual(V20);
   Blynk.syncVirtual(V21);
   autenticazione_giorni=0;
}

BLYNK_WRITE(V8)
{modalita = param.asInt();}

BLYNK_WRITE(V10)
{soglia_min = param.asInt();}

BLYNK_WRITE(V11)
{soglia_int = param.asInt();}

void terminale_act_giorni(String giorno, boolean opzione){
  if (autenticazione_giorni==0){
    if (opzione==HIGH){
      terminal.print("L'irrigazione avverrà di " + giorno + ": "); terminal.print(insieme()); 
     }//www.vincenzonet.it
     if(verif_mod==0){
      caso_giorni_spegni=millis()-tp_save;
     }
    else {
      terminal.print("L'irrigazione NON avverrà di " + giorno + ": "); terminal.print(insieme()); 
     if(verif_mod==0){
      tp_save=millis();
     }
     }
    terminal.flush(); 
  }
}

BLYNK_WRITE(V12)
{lun = param.asInt();
terminale_act_giorni("Lunedì", lun);
}

BLYNK_WRITE(V13)
{mar = param.asInt();
terminale_act_giorni("Martedì", mar);
}

BLYNK_WRITE(V14)
{mer = param.asInt();
terminale_act_giorni("Mercoledì", mer);
}

BLYNK_WRITE(V15)
{gio = param.asInt();
terminale_act_giorni("Giovedì", gio);
}

BLYNK_WRITE(V16)
{ven = param.asInt();
terminale_act_giorni("Venerdì", ven);
}

BLYNK_WRITE(V17)
{sab = param.asInt();
terminale_act_giorni("Sabato", sab);
}

BLYNK_WRITE(V18)
{dom = param.asInt();
terminale_act_giorni("Domenica", dom);
}

BLYNK_WRITE(V19)
{orario = param.asInt();}

BLYNK_WRITE(V20)
{per_quanto = param.asInt();}

BLYNK_WRITE(V0)
{
  if (String("Marco") == param.asStr() || String("marco") == param.asStr() || String("MARCO") == param.asStr()) {
    terminal.println("Polo") ;
  }
  else if (String("Manuale") == param.asStr() || String("manuale") == param.asStr() || String("MANUALE") == param.asStr()){
    modalita=2;
    terminal.print("** Sei entrato in modalità MANUALE! **  "); terminal.print(insieme()); terminal.flush();
    Blynk.logEvent("modalit_manuale_att", String("Sei entrato in modalità MANUALE!"));
    verif_man=1;
  }
  else if (String("reset") == param.asStr() || String("Reset") == param.asStr() || String("RESET") == param.asStr()){
   verif_mod_2=0;
   t0=millis();
   terminal.print("Avvenuto RESET del giorno in corso: "); terminal.print(insieme());
   terminal.flush();
   Blynk.logEvent("reset_giorno", String("Avvenuto RESET del giorno in corso."));
   Blynk.virtualWrite(V21, LOW);
  }
   else if (String("mode") == param.asStr() || String("Mode") == param.asStr() || String("MODE") == param.asStr()){
   if (modalita==1){
   terminal.print("Sei in Modalità AUTOMATICA.");    
   }
   else if (modalita==0){
   terminal.print("Sei in Modalità SEMI-AUTOMATICA.");      
   }
   else if (modalita==2){
   terminal.print("Sei in Modalità MANUALE.");      
   }
   terminal.flush();
   }
  else {
   terminal.print("Non capisco: ");
   terminal.write(param.getBuffer(), param.getLength());
  }
  terminal.flush();
}

BLYNK_WRITE(V21)
{verif_mod_2 = param.asInt();}

/******************************************************************/


void setup()
{
  Serial.begin(115200);
  Blynk.begin(auth, ssid, pass);
  //Blynk.begin(auth, ssid, pass, "blynk.cloud", 80);
  //Blynk.begin(auth, ssid, pass, IPAddress(192,168,1,100), 8080);
  setSyncInterval(60 * 60);
  timer.setInterval(60000L, standard);
  pinMode(solenoide, OUTPUT);
  ads.begin();
  dht.begin();

}

void loop()
{
  Blynk.run();
  timer.run();
  irrigazione();
}
//www.vincenzonet.it

Vincenzo D’Ambrosio Classe 2006