Progetto serra domotica open source

Le condizioni in una serra sono in continua evoluzione, ciò comporta la necessità di un attento monitoraggio. La crescita delle colture è determinata dai fattori ambientali all'interno della serra, che devono essere tenuti costanti tutto il giorno. I benefici di un sistema completamente automatizzato sono molti. Ovviamente, ci sarà una riduzione del lavoro manuale, ma molto più importante, si avrà la certezza di un miglioramento della qualità dei prodotti e possibilità di avere un database nel quale saranno raccolte molte informazioni. Ciò può fare la differenza tra guadagnare o subire perdite.Una serra domotica permette oltre all'automazione dell'ambiente, l'automazione della somministrazione di fertilizzanti e l'automazione di irrigazione. Inoltre, monitorando i fattori ambientali in modo real-time c'è la possibilità di avere un intervento immediato, evitando problemi alle colture. Ultima e non trascurabile caratteristica è quella di avere la possibilità di controllare tutto da casa dal proprio PC o Smartphone.

1.         Dispositivi utilizzati

Facendo una prima overview del progetto è possibile dividere il tutto in tre parti fondamentali Master, Slave e Web Server, meglio descritte successivamente. Per quanto riguarda il Master è stato realizzato utilizzando un Arduino Uno, un modulo di trasmissione wireless 2.4GHz NRF24L01 e una shield wifi CC3000. Lo Slave invece utilizza lo stesso modulo di trasmissione NRF24L01, un sensore di umidità e temperatura DHT22 e la shield per il controllo di un motore brushed della Infineon.
Infine il Master comunica tramite wifi con un web Server dedicato al quale è possibile accedere tramite una app realizzata per Android.

1.1.    Arduino UNO

Attualmente il progetto è ancora in fase di prototipazione, pertanto si è deciso di iniziare a sviluppare il tutto con il classico Arduino Uno. Il prossimo step sarà di utilizzare per gli Slave degli Arduino Nano o Mini e per il Master un Arduino Galileo che abbiamo a disposizione. La scelta di utilizzare Galileo è dovuta sia al fatto di poter disporre di una elevata potenza della scheda, che per gestire più Slave è necessaria, sia al vantaggio di poter utilizzare Linux. Pertanto Il progetto presentato qui è un primo step di controllo domotico di una serra, che poi andrà inevitabilmente potenziato.

Figura 1 - Arduino UNO

Figura 1 - Arduino UNO

1.2.    Shield per controllo motore in DC Infineon

Grazie all’iniziativa di EMCelettronica ci è stato possibile utilizzare anche questa comodissima shield della Infineon per il controllo di un motore DC. In questa fase è stato utilizzato un motore recuperato da una vecchia stampante, quest’ultimo ci serve per aprire e chiudere una finestra della serra quando la temperatura è troppo elevata.

Figura 2 - Motor Shield Infineon

Figura 2 - Motor Shield Infineon

1.3.    Misura temperatura e umidità ambientale

Per il controllo dei parametri ambientali è stato utilizzato un DHT22, che ci permette di avere una misura sia dell’umidità e sia della temperatura. Parametri che poi saranno trasmessi al Master che li invierà al Web Server. Non è stata effettuata una vera taratura di questa misura, ma semplicemente un confronto tra i valori ottenuti dal sensore e quelli ottenuti da un termometro e da un igrostato, essendo questi molto simili non è stata effettuata alcuna compensazione.

Figura 3 - DHT22

Figura 3 - DHT22

1.4.    NRF24L01

Infine sono stati utilizzati due moduli NRF per la comunicazione wireless tra Master e Slave, per trasmettere le informazioni lette dal sensore (Slave to Master) e per trasmettere le nuove impostazioni fornite dall’utente (Master to Slave).
È stato formulato un semplice protocollo di comunicazione, per assicurare che i dati trasmessi fossero consegnati. Quanto detto avviene tramite un controllo effettuato sia dal Master che dallo Slave, ciò permette una elevata efficienza di comunicazione e comunque un basso costo computazionale.

Figura 4 - NFR24L01

Figura 4 - NFR24L01

2.         Master

Di seguito è riportato il Flow Chart del programma:

Figura 1 - Flow Chart Master

Figura 5 - Flow Chart Master

Il Master è sempre in ascolto, appena ci sono dati disponibili li acquisisce e li stampa a video. Intanto verifica se sono presenti nuovi settaggi delle impostazioni da inviare allo Slave.
NB: il master invia le nuove impostazioni solo quando lo Slave gli invia le letture del sensore. Infatti, secondo il nostro protocollo è sempre lo Slave ad avviare la comunicazione.
Se sono presenti nuovi settaggi in lista, ne calcola prima il CRC a 8 bit, poi allega in coda alle impostazioni anche quest’ultimo, che servirà allo Slave per verificare se i dati ricevuti sono corretti o meno.

Fatto ciò il Master aspetta un’altra risposta dallo Slave per confermare la corretta ricezione dei dati. Se ciò non fosse, i dati non vengono cancellati ma restano in memoria e saranno inviati successivamente al prossimo contatto da parte dello Slave.

Infine ritorna nello stato iniziale di ascolto, per ricominciare un nuovo ciclo.

La funzione che calcola il CRC a 8 bit è un algoritmo basato sulle formule di Dallas – Maxim.

2.1.    Comunicazione lato Master

Vediamo ora un po’ di codice, in questo paragrafo descriviamo i processi controllati dal Master:

  while(!Mirf.dataReady()){
    //Aspetto qualche informazione
  }
  Mirf.getData(data);

  Serial.print("RAW DATA: ");
  Serial.println((char*)data);
  char* command = strtok((char *)data, ";");
  int count = 0;
  while (command != 0){

    //Divido le informazioni
    switch (count){
    case 0:
      snprintf(name, sizeof(name), "%s", command);
      break;

    case 1:
      temp = atof(command)/10;
      break;

    case 2:
      hum = atof(command)/10;
      break;
    }

    command = strtok(0, ";");
    count++;
  }

Il while alla prima riga mette il Master in attesa, appena arriva un dato lo sketch prosegue. Con il Mirf.getData(data) acquisiamo il dato e lo salviamo in data, che è un vettore di tipo byte. Il dato ricevuto ha la seguente struttura “nome;temperatura;umidità”, lo salviamo in un puntatore a char e sfruttiamo alcune funzioni delle stringhe. Quella usata qua è stringtokenizer che ci permette di dividere il vettore ricevuto quando incontriamo appunto un token, nel nostro caso “;”. Il comando atof(x), ascii to float, ci permette di convertire x in un float. Il primo parametro ricevuto è il nome dello Slave che sta trasmettendo, gli altri due sono temperatura e umidità lette nella serra.

if(Serial.read() == 'k'){

  Serial.println("Inserisci umidità max");
   while (Serial.available() == 0);
   humax = Serial.parseFloat();
   Serial.flush();

   Serial.println("Inserisci umididà min");
   while (Serial.available() == 0);
   humin = Serial.parseFloat();
   Serial.flush();

  Serial.println("Inserisci la temperatura max");
   while (Serial.available() == 0);
   tumax = Serial.parseFloat();
   Serial.flush();

  Serial.println("Inserisci la temperatura min");
   while (Serial.available() == 0);
   tumin = Serial.parseFloat();

  Serial.print("Letto: ");
   Serial.println(tumin);
   Serial.flush();
}

E ancora:

Mirf.setTADDR((byte *)name);

   snprintf(threshold, sizeof(toSend), "%s;%d;%d;%d;%d;%d", name, (int)humax, (int)humin, (int)tumax, (int)tumin, (int)crc);
    Serial.print("Soglie: ");
   Serial.println(threshold);
   Mirf.send((byte *)threshold);

    //Imposto un timeout oltre il quale stoppa la trasmissione
    while(Mirf.isSending()){
    }
    Serial.println("Finished sending");
    delay(10);
    time = millis();
    while(!Mirf.dataReady()){
      if ( ( millis() - time ) > 1000 ) {
        Serial.println("Timeout Setting parametri");
        return;
      }
    }

Nella prima parte quando viene scritto il carattere “k” sulla seriale, il programma ci chiederà di inserire le nuove soglie minime e massime di umidità e temperatura che poi saranno inviate allo Slave.

Mentre nella seconda parte col comando snprintf(threshold, sizeof(toSend), "%s;%d;%d;%d;%d;%d", name, (int)humax, (int)humin, (int)tumax, (int)tumin, (int)crc) creiamo un vettore da inviare allo Slave, formato da il nome di chi trasmette, le soglie impostate e il CRC calcolato dal Master. Per comodità tutti parametri trasmessi sono trasformati in interi.
Nell’ultima parte è impostato un timeout per stoppare la trasmissione dopo un certo tempo.

2.2.    Processi Master

Un controllo che effettua il Master è che la trasmissione sia andata a buon fine per fare ciò calcola il CRC.
Generalmente per verificare la bontà dei dati ricevuti si ricorre alle somme di controllo, quelle più semplici sono quelle algebriche che però hanno alcuni limiti. Pertanto si utilizzano algoritmi appositi, conosciuti con l’acronimo di CRC (Cyclic Redundancy Check). Questo sfrutta una rete logica, all’interno della quale vengono introdotti i dati che scorrono in un registro. Durante lo scorrimento, vengono prelevati e combinati fra loro dei bit da determinate posizioni del registro: il risultato di questa operazione viene combinato con il bit in ingresso. In questo caso è stato utilizzato un CRC8, ovvero si ha in uscita una somma ad 8 bit. Il codice è il seguente:

//Calcolo il CRC-8, mi creo quindi un array di byte
byte bhmax = (byte)humax;
byte bhmin = (byte)humin;
byte btmax = (byte)tumax;
byte btmin = (byte)tumin;

byte crc32_str[4] = {
bhmax, bhmin, btmax, btmin        };

crc = CRC8(crc32_str);
Serial.println("CRC: ");
Serial.println(crc);

E ancora:

//Calcolo CRC-8 - algoritmo basato sulle formule di CRC-8 di Dallas/Maxim
byte CRC8(const byte *data) {
byte crc = 0x00;

while (*data) {
byte extract = *data++;
for (byte tempI = 8; tempI; tempI--) {
byte sum = (crc ^ extract) & 0x01;
crc >>= 1;
if (sum) {
crc ^= 0x8C;
}
extract >>= 1;
}
}
return crc;
}

3.         Slave

Di seguito è riportato il Flow Chart del programma:

Figura 2 - Flow Chart Slave

Figura 6 - Flow Chart Slave

Inizialmente vengono effettuate tutte le inizializzazioni sia per i sensori sia per la comunicazione. Inizia quindi, l’acquisizione di umidità e temperatura del DHT22, ed inizia il timer per l’invio dei dati misurati. Non avendo una necessita di real time inviamo i dati ogni 10 secondi.
Pertanto verrà avviata la conversazione con il Master, una volta inviati i parametri lo Slave si mette in ascolto per ricevere eventuali impostazioni. Se così fosse acquisisce questo pacchetto, ne estrae le informazioni di cui ha bisogno e si calcola il CRC e verifica se è uguale a quello ricevuto dal Master. Se così fosse risponde con “OK”, altrimenti da “Errore”.

Intanto il microcontrollore continua a verificare se i parametri letti siano o meno all’interno delle soglie massime e minime. Agendo di conseguenza sugli attuatori, che in questo caso sono un motore che apre e chiude una finestra, una pompa per l’irrigazione ed un riscaldatore.

3.1.    Processi Slave

Vediamo ora un po’ di codice, in questa paragrafo descriviamo i processi controllati dal Master:

//Gestisco l'umidificatore, il riscaldatore e la ventola in funzione delle soglie e dei valori letti
//Riscaldatore
if(myDHT22.getTemperatureC() >= tmax-1){
digitalWrite(RISCALDATORE, HIGH);
}
if(myDHT22.getTemperatureC() <= tmin+1){
digitalWrite(RISCALDATORE, LOW);
}

//Umidificatore
if((int)myDHT22.getHumidity() >= hmax-1){
digitalWrite(UMIDIFICATORE, HIGH);
}
if((int)myDHT22.getHumidity() <= hmin+1){
digitalWrite(UMIDIFICATORE, LOW);
}

//Aprire finestra
if(myDHT22.getTemperatureC() >= tmax+4){
duty_motor = 20;
analogWrite(IN_2, duty_motor);
delay(TCONST);
}
if(myDHT22.getTemperatureC() <= (tmax+tmin)/2 || finecorsa == LOW){
reset_ports();
duty_motor = 0;
analogWrite(IN_2, duty_motor);
delay(TCONST);
}

Controllo i parametri in serra e attivo o disattivo i vari attuatori, l'ultimo if mi attiva il motore con un duty del 20%. Dato che devo aprire e chiudere una finestra non ho bisogno di elevata velocità, ma di elevata coppia pertanto lavoro a duty fisso e basso.

Poi controllo che i dati vengano inviati ogni 10 secondi:

//Invio i parametri ogni 10 secondi
if(currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
Mirf.setTADDR((byte *)"server");
snprintf(toSend, sizeof(toSend), "%s;%d;%d", name, (int)humidity, (int)temperature);
Serial.println(toSend);
Mirf.send((byte *)toSend);

//Imposto un timeout oltre il quale stoppa la trasmissione
while(Mirf.isSending()){
}
Serial.println("Finished sending");
delay(10);

while(!Mirf.dataReady()){
if ( ( millis() - time ) > 1000 ) {
return;
}
}
}

Ed infine controllo che il CRC ricevuto sia uguale a quello che mi calcolo ora:

Mirf.getData(data);
char* command = strtok((char *)data, ";");
int count = 0;
while (command != 0){
//Divido le informazioni
switch (count){
case 0:
snprintf(name, sizeof(name), "%s", command);
break;

case 1:
hmax = atof(command)/10; //atof(char* ) mi converte un tipo char* in double
break;

case 2:
hmin = atof(command)/10;
break;

case 3:
tmax = atof(command)/10;
break;

case 4:
tmin = atof(command)/10;
break;

case 5:
crc_ric = atoi(command);
break;
}
command = strtok(0, ";");
count++;
}
//Calcolo il CRC-8, mi creo quindi un array di byte
/********************************************************************************/
byte bhmax = (byte)hmax;
byte bhmin = (byte)hmin;
byte btmax = (byte)tmax;
byte btmin = (byte)tmin;

byte crc32_str[4] = {bhmax, bhmin, btmax, btmin};

crc = CRC8(crc32_str);
Serial.println("CRC: ");
Serial.println(crc);
/********************************************************************************/

//Controllo i dati ricevuti
/********************************************************************************/
if((byte)crc_ric == crc){
Serial.println("CONTROLLO DATI OK!");
Mirf.setTADDR((byte *)name);
Mirf.send((byte *)"OK");
}
else {
Serial.println("CONTROLLO DATI ERRORE!");
Mirf.setTADDR((byte *)name);
Mirf.send((byte *)"Errore");
}
/********************************************************************************/

Utilizzando il CRC si riesce con due sole trasmissioni a validare la bontà dei dati ricevuti.

3.2.    Comunicazione lato Slave

La comunicazione lato Slave è molto semplice infatti quest'ultimo non fa altro che inviare le letture dei dati ogni 10 secondi e aspettare se il Master ha qualche impostazione da inviare.

//Invio i parametri ogni 10 secondi
if(currentMillis - previousMillis > interval) {
previousMillis = currentMillis;
Mirf.setTADDR((byte *)"server");
snprintf(toSend, sizeof(toSend), "%s;%d;%d", name, (int)humidity, (int)temperature);
Serial.println(toSend);
Mirf.send((byte *)toSend);

//Imposto un timeout oltre il quale stoppa la trasmissione
while(Mirf.isSending()){
}
Serial.println("Finished sending");
delay(10);

while(!Mirf.dataReady()){
if ( ( millis() - time ) > 1000 ) {
return;
}
}
}

while(!Mirf.dataReady()){
if ( ( millis() - time ) > 1000 ) {
return;
}
}

L'intera comunicazione dello Slave avviene in questa parte dello sketch.

4.         Server

La componente Server del nostro progetto si occupa di ricevere ed immagazzinare le rilevazioni temporaneamente memorizzate sul dispositivo Master e di comunicare a quest’ultimo eventuali cambi di configurazione inoltrati dall’utente tramite App Mobile (Cap. 5). Si compone di una componente software Java e di un database di tipo MySQL.

Il server viene lanciato su una macchina dedicata e si predispone a ricevere pacchetti in entrata sia dal Master che da App Mobile. All’arrivo di un package per prima cosa ne analizza l’integrità tramite controllo CRC, poi ne analizza il mittente: se proviene da App Utente, quindi si tratta di un set di comandi da inviare al Master, lo aggiunge alla coda dei pacchetti in uscita e il flusso continua; se invece si tratta di un pacchetto in arrivo dal Master, viene estratta la parte delle rilevazioni e viene salvata in due tabelle del DB, una contenente i dati in formato ROW per eventuali analisi e un’altra contenente i dati strutturati tramite un parser dedicato pronti per la visualizzazione. Viene inviata poi una risposta al Master che indica l’avvenuta ricezione e che può quindi procedere a cancellare i suoi dati temporanei. Al termine della memorizzazione viene anche controllato se nella coda di pacchetti in uscita è presente un set di comandi da inviare al Master che ha appena comunicato, in tal caso invece della risposta OK viene inoltrato al Master il set di comandi, aspettando poi un OK da quest’ultimo per concludere il processo di comunicazione.

4.1 Comunicazione Server

Il Server riceve e inoltra pacchetti su protocollo TCP/IP istanziando un oggetto ServerSocket, secondo il seguente esempio:


String clientSentence;
String capitalizedSentence;
ServerSocket welcomeSocket = new ServerSocket(6789);

while(true)
{
Socket connectionSocket = welcomeSocket.accept();
BufferedReader inFromClient =
new BufferedReader(new InputStreamReader(connectionSocket.getInputStream()));
clientSentence = inFromClient.readLine();
System.out.println("Received: " + clientSentence);

…………………………..
}

 

Dopo aver costruito la risposta, viene inoltrata al mittente nel seguente modo

DataOutputStream outToClient = new DataOutputStream(connectionSocket.getOutputStream());

outToClient.writeBytes(response);

 

 

5.         App Mobile

L’app mobile è l’interfaccia dell’utente per consultare i dati memorizzati sul Server ed effettuare eventuali cambi di configurazione al Master (soglie di umidità e temperatura, timeout, …..). Si compone di una app Android scritta in linguaggio Java.

L’App al suo avvio, mostra un menù tramite il quale è possibile visualizzare le rilevazioni memorizzate sul Server, indicando un range temporale o di sensori che si vuole visualizzare. Il risultato appare come grafico e mostra l’andamento temporale delle rilevazioni.

E’ inoltre possibile scegliere dal menu di effettuare il cambio dei parametri, in questo caso vengono mostrati a video le configurazioni attuali, al salvataggio viene inoltrato un pacchetto al Server che provvederà ad instradarlo al dispositivo Master.

5.1 Comunicazione App Mobile

La comunicazione della App Mobile verso il Server avviene su protocollo TCP/IP, con un meccanismo analogo a quello del server.

Conclusioni

Manca da sviluppare solo la comunicazione tra Master e Server.

L’idea che c’è dietro a questo progetto è quella di riportare l’agricoltura alla portata di tutti. Infatti disporre di un sistema di questo tipo, significa poter coltivare a casa tutto quello che si vuole riducendo di molto il tempo e la fatica da dedicarci.
Questo per ora è solo un prototipo semplice dell’idea che vogliamo sviluppare, ma la nostra intenzione era quella di trasmettere la nostra visione di agricoltura.

È possibile, con un sistema ben sviluppato risparmiare anche più del 30% delle risorse naturali, ciò consente sia di abbattere i costi e sia di ridurre l’impatto ambientale.

 

Antonio La Mura & Umberto Festa

Scarica subito una copia gratis

14 Commenti

  1. Avatar photo Maurizio 2 Dicembre 2015
    • Avatar photo Antonio.La Mura 8 Gennaio 2016
  2. Avatar photo morevoice 5 Gennaio 2016
    • Avatar photo Antonio.La Mura 8 Gennaio 2016
  3. Avatar photo Massimiliano.MeinardiMessi 16 Febbraio 2016
    • Avatar photo Antonio.La Mura 16 Febbraio 2016
      • Avatar photo Massimiliano.MeinardiMessi 16 Febbraio 2016
        • Avatar photo Antonio.La Mura 16 Febbraio 2016
          • Avatar photo Massimiliano.MeinardiMessi 16 Febbraio 2016
  4. Avatar photo emmecilab 11 Aprile 2016
  5. Avatar photo Antonio.La Mura 11 Aprile 2016
    • Avatar photo emmecilab 11 Aprile 2016
  6. Avatar photo emmecilab 16 Maggio 2016

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend