Ambizioso progetto serra "carnivora"

Ciao a tutti

sto iniziando a muovere i primi passi con arduino, e la prima applicazione che vorrei realizzare riguarda il controllo di alcuni paramentri climatici di una serra che ho costruito.
tale serra ospita delle piante carnivore (le quali, per chi non lo sapesse, hanno bisogno sempre di 2 / 4 cm di acqua distillata nel sottovaso)

ora, i parametri che vorrei monitorare sono: temperatura ed umidità interna, temperatura ed umidità esterna (tramite dht11, che già possiedo), il livello di acqua di tre vasche da idrocoltura (con tre sensori ad ultrasuoni, che mi dovrebbero arrivare in settimana), poi la velocità del vento (con un anemometro che ho costruito io, tramite sensore reed - ovviamente montato sul tetto della serra e non dentro! :slight_smile: ) e mi piacerebbe che arduino mi desse in uscita i dati su un display lcd con due pulsanti per selezionare i diversi tipi di dati.
la questione della pulsantiera l'ho risolta senza occupare alcun pin digitale, ma solo uno di quelli analogici (ho collegato i tasti al pin di alimentazione a 5 V con in serie delle resistenze di diverso valore in modo che a seconda del tasto che schiaccio misuro differenti livelli di tensione)
il fatto è che mi piacerebbe poter "registrare" i valori dei vari parametri per creare delle cronologie (specialmente per quanto riguarda la ventosità e le temperature).
e il massimo sarebbe buttare tutto in rete (con una shield ethernet, che mi consentirebbe di fare entrambe le cose.

però purtroppo i conti non tornano, per quanto riguarda i pin digitali:
32pin (sensori ad ultrasuoni) + 6pin (display lcd) + 21pin (sensori di temperatura ed umidità) + 1pin (swith reed per l'anemometro) = 15

ora, se è vero che potrei pilotare il monitor LCD con soli 3 pin (da Arduino Playground - LCD3wires), e l'anemometro potrei collegarlo come ingresso analogico, con resistenza in serie (come per la pulsantiera), otterrei

32pin (ultrasuoni) + 3pin(display) + 21pin(t e h) = 11pin usati, esclusa la shield ethernet.

per il momento ho già interfacciato l'lcd (col metodo classico da sei cavi), la pulsantiera che mi fa cambiare dati visualizzati sul display, i due sensori di t e h, l'anemometro, mi mancano i sensori di livello.
l'ethernet shield la vorei integrare in futuro, ma non avrei pin disponibili dato che ora come ora utilizzerei al minimo 11 pin.
insomma, avete qualche altro consiglio per farmi risparmiare dei pin preziosi?? :slight_smile:

grazie, ciao!

tommaso

I pin analogici posso essere usati come ingressi o uscite digitali.
Esempio:

digitalWrite(A3, HIGH);

Per il display LCD procurati un convertitore seriale (LCD to I2C) così usi solo i 2 pin dell'I2C.
Per la Ethernet Shield devi tenere liberi i pin 10,11, 12 e 13 più il 4 per la SD. Questo con Arduino UNO.
Per l'anemometro potresti prendere un ATtiny e programmarlo per restituirti il dato via I2C. E lo attacchi in parallelo all'LCD I2C.

Con tutte le cose che vorresti ottenere, ti consiglio di passare ad Arduino MEGA, non tanto per la maggiore quantità di ingressi/uscite e porte seriali, ma per la maggiore quantità di RAM a disposizione per il programma.

Per la misura della temperatura / umidità ti consiglio di usare un DHT22 molto più preciso, mentre credo che il livello dell'acqua possa essere misurato più semplicemente con 3 ingressi analogici corrispondenti a livello Max, Med, Min

grazie mille, proprio quello che cercavo!!!

devo un po capire bene come usare gli ingressi analogici come digitali ma ho già trovato alcuni esempi qui sul forum..

@cyberhs: ho un arduino UNO che di fatto non ho ancora praticamente usato!! dici che non riesce a gestire questi dati (io li misurerei ogni 3, 4 o 5 secondi... dici che lo sovraccarico?

Cari amici

il mio progetto sta prendendo forma (a giorni arriveranno i sensori di livello ed un circuitino i2c da collegare direttamente al monitor, con quattro cavetti in uscita bell’e pronti)

riscontro già qualche problemino, però.

ho due tasti: con uno voglio cambiare le schermate per visualizzare rispettivamente i dati “interni” (di t e h), qualli esterni (t, h e velocità del vento) , infine i livelli delle tre vasche (da completare dato che non ho ancora i sensori).

il problema è che noto delle discrepanze abbastanza notevoli tra i dati riportati a display con quelli che otterrei a monitor seriale (avevo creato tre codici più piccoli che poi ho inito insieme in quello che vi sto per riportare)

il funzionamento del sensore t-h è anomalo, nel senso che a display ri valori di umidità vanno oltre 400 (!!!) e di t saltano tra 12 e 25.
ho provato a far girare l’anemometro a mano, e, mentre col pezzo di codice che avevo appositamente scritto mi misurava valori di 40 - 50 (a seconda della velocità con cui lo faccio girare), col codice che sto per riportare non arriva a dieci. cioè salta proprio dei conteggi (eppure il sistema di conteggio con reset ogni due secondi è il medesimo)

dove sbaglio?

#include <LiquidCrystal.h>
#include <dht11.h>
dht11 DHT;
#define DHT11_PIN 8
LiquidCrystal Display20x4(12, 11, 2, 3, 4, 5);

const int BUTTON = 9;

int reed = 0;
int conta =0;
int aperto=0;
unsigned long precedente = 0;
int analogPin = 0;
int val = 0;
int decine = 0;
int unita =0;
int sezione = 0;
int schiacciato =0;

void setup() {
  
  Serial.begin(9600);
  
  pinMode(BUTTON,INPUT);
  Display20x4.begin(20, 4);  
  Display20x4.clear();
  Display20x4.cursor();
  delay(500);
  Display20x4.noCursor();
  delay(500);
  Display20x4.cursor();
  delay(500);
  Display20x4.noCursor();
  delay(500);
  Display20x4.cursor();
  delay(500);
  Display20x4.noCursor();
  delay(500);
}

void loop() {
  
  val = analogRead(analogPin);
  int chk;
  chk = DHT.read(DHT11_PIN); 
  unsigned long attuale = millis();
  reed = digitalRead(BUTTON);



if( val>=800 && val<=900) {
   Display20x4.clear();  
   if (decine!=2) {
     if (schiacciato==0){
   decine = decine+1;
   schiacciato = 1;
   }
   }
   else {decine=0;} 
  }

if( val>=400 && val<=500) {
   Display20x4.clear();  
   if (unita!=3) {
     if (schiacciato==0){
   unita = unita+1;
   schiacciato = 1;
   }
   }
   else {unita=0;} 
  }

if(val<=100) {schiacciato = 0;}

sezione = decine*10+unita;



  if (reed == HIGH) {
        if (aperto == 1) {
            conta = conta + 1;
            aperto = 0;
          }
        } else {
      if (aperto == 0) {
      aperto = 1;
    }
  }


  if (sezione==0) {
      Display20x4.setCursor(0,0);
      Display20x4.print("Valori ISTANTANEI");
      Display20x4.setCursor(0,1);
      Display20x4.print("T interna=");
      Display20x4.setCursor(12,1);
      Display20x4.print(DHT.temperature);
      Display20x4.setCursor(17,1);
      Display20x4.print("[C]");
      Display20x4.setCursor(0,2);
      Display20x4.print("H interna=");
      Display20x4.setCursor(12,2);
      Display20x4.print(DHT.humidity);
      Display20x4.setCursor(17,2);
      Display20x4.print("[%]");
  } 
  
    if (sezione==1) {
      Display20x4.setCursor(0,0);
      Display20x4.print("Valori ISTANTANEI");
      Display20x4.setCursor(0,1);
      Display20x4.print("T esterna=");
      Display20x4.setCursor(12,1);
      Display20x4.print(DHT.temperature,1);
      Display20x4.setCursor(17,1);
      Display20x4.print("[C]");
      Display20x4.setCursor(0,2);
      Display20x4.print("H esterna=");
      Display20x4.setCursor(12,2);
      Display20x4.print(DHT.humidity,1);
      Display20x4.setCursor(17,2);
      Display20x4.print("[%]");
      Display20x4.setCursor(0,3);
      Display20x4.print("V vento  =");
      Display20x4.setCursor(12,3);
      Display20x4.print(conta);
      Display20x4.setCursor(15,3);
      Display20x4.print("[m/s]");
  } 
  
      if (sezione==2) {
      Display20x4.setCursor(0,0);
      Display20x4.print("Valori ISTANTANEI");
      Display20x4.setCursor(0,1);
      Display20x4.print("Liv vasca 1=");
      //Display20x4.setCursor(12,1);
      //Display20x4.print(DHT.temperature);
      Display20x4.setCursor(17,1);
      Display20x4.print("[%]");
      Display20x4.setCursor(0,2);
      Display20x4.print("liv vasca 2=");
      //Display20x4.setCursor(12,2);
      //Display20x4.print(DHT.humidity);
      Display20x4.setCursor(17,2);
      Display20x4.print("[%]");
      Display20x4.setCursor(0,3);
      Display20x4.print("Liv vasca 3=");
      //Display20x4.setCursor(12,3);
      //Display20x4.print(conta);
      Display20x4.setCursor(17,3);
      Display20x4.print("[%]");
  } 
  
  if (attuale-precedente>=2000){
    conta = 0;
    precedente = attuale;
}

}

grazie mille a tutti, ciao!

tommaso

nessuno mi può aiutare? :frowning:

forse il codice è un po incasinato? (non ho messo commenti...)

Devi capire che non abbiamo il tuo hardware per poter provare lo sketch né siamo nella tua testa per cui sappiamo la logica di funzionamento che hai in mente :wink:

Posso darti dei suggerimenti, ossia considera che non usando gli interrupt, nel caso in cui arrivi un segnale prima o dopo la lettura digitale del pin, tu quel segnale lo perdi. Forse è questo il tuo problema.

Hai ragione Leo72!

in effetti voi siete degli esperti ma non dei veggenti! :sweat_smile:

comunque ti spiego meglio: ho questo anemometro costituito in sostanza da una lberello rotante con magnete e uno switch reed che quindi si apre e chiuda al passaggio del magnete.

il reed lo faccio leggere ad arduino come interruttore (pin 9) e gli faccio contare il numero di volte che si chiude (variabile “conta”).
ho impostato un tempo di campionamento pari a 2000 ms (ultimo ciclo IF)

con l’uscita a monitor seriale nessun problema. qui sotto lo schema

const int BUTTON = 9;

int val = 0;
int conta =0;
int aperto=0;
unsigned long precedente = 0;



void setup() {
  
  Serial.begin(9600);
  
  pinMode(BUTTON,INPUT);

}

void loop() {
  

unsigned long attuale = millis();

  
  val = digitalRead(BUTTON);
  
  if (val == HIGH) {
    
    if (aperto == 1) {
      
      conta = conta + 1;
      
      aperto = 0;
      
    }
   
    
  } else {
    
        if (aperto == 0) {
      
      aperto = 1;
      
    }
  }


if (attuale-precedente==2000){

  Serial.println(conta);
  conta = 0;
  precedente = attuale;

}

}

ma se provo a fare lo stesso identico ragionamento con però l’uscita a LCD le cose cambiano!
nonostante usi praticamente lo stesso codice!!
qui sotto il codice

#include <LiquidCrystal.h>
 
LiquidCrystal Display20x4(12, 11, 2, 3, 4, 5);

const int BUTTON = 9;

int val = 0;
int conta =0;
int aperto=0;
unsigned long precedente = 0;


 
void setup()
{
  
  Serial.begin(9600);
  pinMode(BUTTON,INPUT);
  
  Display20x4.begin(20, 4);
  Display20x4.clear();
  Display20x4.cursor();
  delay(2000);
  Display20x4.noCursor();
 
}


void loop()
{
 
  Display20x4.setCursor(0,0);
  Display20x4.print("Anemometro");
 
  Display20x4.setCursor(0,2);
  Display20x4.print("V wind = ");
 
  Display20x4.setCursor(15,2);
  Display20x4.print("[m/s]");
    
  unsigned long attuale = millis();
  val = digitalRead(BUTTON);
  
  if (val == HIGH) {
    
    if (aperto == 1) {
      
      conta = conta + 1;
      
      aperto = 0;
      
    }
   
    
  } else {
    
        if (aperto == 0) {
      
      aperto = 1;
      
    }
  }


if (attuale-precedente>=2000){

  Display20x4.clear();    
  Display20x4.setCursor(11,2);
  Display20x4.print(conta);
  conta = 0;
  precedente = attuale;

}
  
}

prima di tutto nell’ultimo ciclo IF non posso impostare il tempo precisamente (infatti devo mettere >=2000 e non == 2000)
inoltre mi salta proprio degli input.
girando a mano, con l’uscita seriale arrivo a 20 /30. con l’uscita a LCD sempre girando a mano non arrivo a 10.

come mai?

Ad occhio, il problema è quello che ti ho illustrato. Finché leggi SOLO l'input, il ciclo loop di Arduino viene eseguito talmente velocemente che la frequenza di lettura è maggiore di quella di chiusura del reed. Nel momento in cui ti interfacci al display le cose cambiano trasticamente... in negativo. La spedizione dei dati al display richiede un certo tempo e tu durante quel frangente perdi delle letture.
Io almeno ipotizzo questo.

Per toglierti ogni dubbio, usa gli interrupt ed attacca ad uno di essi una piccola funzione che incrementi un contatore. Tu non dovrai far altro che leggere periodicamente quel contatore e calcolarti il periodo in base al tempo intercorso fra una lettura e la precedente.

Cerca sul forum, trovi tutto quel che ti occorre.
Qui la pagina dedicata agli interrupt:

ehi grazie leo72, in effetti deve essere proprio quello il problema…

per fare una prova, ho spostato il “blocco di scrittura a LCD” all’interno dell’ultimo IF (così invio i dati all’LCD una volta ogni 2 s e non di continuo) e in effetti funziona!!

#include <LiquidCrystal.h>
 
LiquidCrystal Display20x4(12, 11, 2, 3, 4, 5);

const int BUTTON = 9;

int val = 0;
int conta =0;
int aperto=0;
unsigned long precedente = 0;


 
void setup()
{
  
  Serial.begin(9600);
  pinMode(BUTTON,INPUT);
  
  Display20x4.begin(20, 4);
  Display20x4.clear();
  Display20x4.cursor();
  delay(2000);
  Display20x4.noCursor();
 
}


void loop()
{
 
  
    
  unsigned long attuale = millis();
  val = digitalRead(BUTTON);
  
  if (val == HIGH) {
    
    if (aperto == 1) {
      
      conta = conta + 1;
      
      aperto = 0;
      
    }
   
    
  } else {
    
        if (aperto == 0) {
      
      aperto = 1;
      
    }
  }


if (attuale-precedente>=2000){

  Display20x4.clear();  
  Display20x4.setCursor(0,0);
  Display20x4.print("Anemometro");
 
  Display20x4.setCursor(0,2);
  Display20x4.print("V wind = ");
 
  Display20x4.setCursor(15,2);
  Display20x4.print("[m/s]");  
  Display20x4.setCursor(11,2);
  Display20x4.print(conta);
  conta = 0;
  precedente = attuale;

}
  
}

tuttavia rimane sempre il problema del >= anzichè == nell’ultimo IF (ho provato ed in uscita non mi da nulla)
comunque ora mi leggo un po di piu sugli interrupt, così imparo qualcosa di nuovo!

Intendi questo if?

if (attuale-precedente>=2000){

Qual era il problema?

Riguardo al codice conviene tenerlo compatto con gli spazi sufficienti per separare i vari elementi, ad esempio le diverse funzioni o comandi che si vuole evidenziare, senza staccare le parentesi; altrimenti non si capisce dove inizia e termina un blocco.
Inoltre, è utile indentare il codice tramite la funzione di formattazione automatica presente nel menù strumenti dell’IDE.

#include <LiquidCrystal.h>

LiquidCrystal Display20x4(12, 11, 2, 3, 4, 5);

const int BUTTON = 9;
int val = 0;
int conta = 0;
int aperto = 0;
unsigned long precedente = 0;

void setup() {
  Serial.begin(9600);
  pinMode(BUTTON, INPUT);

  Display20x4.begin(20, 4);
  Display20x4.clear();
  Display20x4.cursor();
  delay(2000);
  Display20x4.noCursor();
}

void loop() {
  unsigned long attuale = millis();
  val = digitalRead(BUTTON);

  if (val == HIGH) {
    if (aperto == 1) {
      conta++;
      aperto = 0;
    }
  } else {
    if (aperto == 0) {
      aperto = 1;
    }
  }

  if (attuale - precedente >= 2000) {
    Display20x4.clear();
    Display20x4.setCursor(0, 0);
    Display20x4.print("Anemometro");
    Display20x4.setCursor(0, 2);
    Display20x4.print("V wind = ");
    Display20x4.setCursor(15, 2);
    Display20x4.print("[m/s]");
    Display20x4.setCursor(11, 2);
    Display20x4.print(conta);
    conta = 0;
    precedente = attuale;
  }
} // End Loop

tuttavia rimane sempre il problema del >= anzichè == nell’ultimo IF (ho provato ed in uscita non mi da nulla)
comunque ora mi leggo un po di piu sugli interrupt, così imparo qualcosa di nuovo!

la soluzione è piuttosto banale: se usi l’uguaglianza, la condizione viene soddisfatta SOLO se il confronto lo fai 2000ms dopo il primo. Se passano 2001ms la condizione non è soddisfatta :wink:

tapirinho:

tuttavia rimane sempre il problema del >= anzichè == nell'ultimo IF (ho provato ed in uscita non mi da nulla)
comunque ora mi leggo un po di piu sugli interrupt, così imparo qualcosa di nuovo!

la soluzione è piuttosto banale: se usi l'uguaglianza, la condizione viene soddisfatta SOLO se il confronto lo fai 2000ms dopo il primo. Se passano 2001ms la condizione non è soddisfatta :wink:

appunto!!
il fatto è che appesantendo il codice, di fatto il loop ci mette piu di 1 ms a chiudersi.
questo significa che, arrivato all'Nesimo loop, "attuale - precedente" misurerà (sempio) 1999 ms ma lo step successivo assume valore diverso da 2000, es 2002.
è per questo che non mi da nulla in uscita se metto l'uguaglianza stretta.

la domanda è: come fare, in questo caso , a rersettare la variabile ESATTAMENTE ogni 2000 ms? indipendentemente dalla pesantezza del codice...

io, nel mio progettino in cui misuro la corrente, "chiudo" le letture in un ciclo for. In questo modo dedico il micro alla lettura di quell'ingresso e sono sicuro di perdermi meno letture possibili.
Il mio suggerimento è di leggere ogni x secondi (10 magari) la velocità del vento, dedicando 2secondi a tale operazione. In questo modo hai un refresh sufficiente ed una precisione massima (questo senza considerare gli interrupt).

Non credo che sia possibile far fare l'operazione ogni 2000ms esatti, a meno di "bloccare" il loop se sei intorno ai 1900 e poi eseguire l'operazione X al raggiungimento dei 2000.

PS: ho creato uno sketch la cui unica operazione è stampare il valore di millis() sulla seriale, ecco il risultato:

1
2
4
7
10
14
18
22
26
30

ci mette 4ms solo per leggere la millis e inviarla sulla seriale: non credo tu riesca a fare un loop da 1ms :roll_eyes: