Richiesta aiuto progetto controllo clima grow box automatizzata

Salve a tutti,
da poco mi sono avvicinato al mondo di Arduino, e dopo aver consultato migliaia di siti e blog, sono riuscito a realizzare il mio primo “progettino”, un’unità di controllo per una grow box.
Per ora leggo e scrivo sul un display 1602 I2C, i valori di temperatura e umidità da un dht11 (ho ordinato un dht22 che sostituirà il dht11), poi ho impostato 4 variabili float per definire un range di temperatura e umidità da mantenere nel grow box(Tmax,Tmin,Hmax,Hmin) e collegato una scheda con 4 rele che pilota un umidificatore, una stufetta e una ventola di estrazione aria. Fin qui funziona tutto bene!
Ora vorrei poter cambiare le variabili (Tmax,Tmin,Hmax,Hmin) tramite un menu setting sul display e dei pulsanti. Ho letto un po di articoli in rete, ma purtroppo non riesco a venirne a capo (forse è una cosa troppo complessa per le mie attuali competenze) ::slight_smile: Avete qualche consiglio da darmi (magari qualche progetto semplice da cui partire)? :thanks

questo è lo sketch del mio progettino

//Librerie
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>


//Costanti
#define DHTPIN 9     // pin dht
#define DHTTYPE DHT11   // Seleziona tipo di sensore dht11/22
#define relePinV 10
#define relePinH 11
#define relePinR 12
#define pinLedV 6
#define pinLedH 7
#define pinLedR 8

DHT dht(DHTPIN, DHTTYPE); // Inizializza sensore dht

LiquidCrystal_I2C lcd(0x3F,16,2);

//Variabili
float hum;  //Valore umidità
float temp; //Valore temperatura
float tempMin = 13;               //Tmin
float tempMax = 26;               //Tmax
float huMin = 45;                 //Hmin
float huMax = 75;                 //Hmax


void setup()
{
   dht.begin();
  lcd.begin();
  lcd.backlight();
  pinMode (relePinV, OUTPUT);
  pinMode (relePinH, OUTPUT);
  pinMode (relePinR, OUTPUT);
  pinMode (pinLedV,OUTPUT);
  pinMode (pinLedH,OUTPUT);
  pinMode (pinLedR,OUTPUT);
  digitalWrite(relePinV, HIGH);
  digitalWrite(relePinH, HIGH);
  digitalWrite(relePinR, HIGH);
}

void loop()
{
  
  delay(2000);
  //Legge e memorizza valori temperatura e umidità
  hum = dht.readHumidity();
  temp = dht.readTemperature();
 
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Temp: ");
  lcd.print(temp);
  lcd.print(" ");
  lcd.print((char)223);
  lcd.print("C");
  lcd.setCursor(0,1);
  lcd.print("Hum:  ");
  lcd.print(hum);
  lcd.print(" %");

  delay(2000); //Delay 2 sec.

  



if (temp>(tempMax) || hum>(huMax)) {// ventola aspirazione per temperatura o umidità alta
    digitalWrite(relePinV, LOW);
    digitalWrite(pinLedV,HIGH);     //led ventola
   } 
   
  else {
     digitalWrite(relePinV, HIGH);
     digitalWrite(pinLedV,LOW); 
      
}
if (temp<tempMin) {                 //riscaldamento
    digitalWrite(relePinR, LOW);
    digitalWrite(pinLedR,HIGH);     //led riscaldatore
   } 
  
  else {
     digitalWrite(relePinR, HIGH);
     digitalWrite(pinLedR,LOW);
}
if (hum<huMin) {                    //umidificatore
    digitalWrite(relePinH, LOW);
    digitalWrite(pinLedH,HIGH);     //led umidificatore
   }
   else {
     digitalWrite(relePinH, HIGH);
     digitalWrite(pinLedH,LOW);
   }

ciao…ti consiglio di mettere il codice all’interno dei tag code…il pulsante per farlo è il </> sopra le faccine…

ORSO2001:
ciao…ti consiglio di mettere il codice all’interno dei tag code…il pulsante per farlo è il </> sopra le faccine…

Fatto! Grazie del consiglio

...nel primo post non sono riuscito a concludere il mio discorso, ero richiesto da mia figlia... :smiley:

allora a livello hardware dovrai sicuramente aggiungere un pulsante +; un pulsante - ed un pulsante per navigare nei vari menu...quest'ultimo pulsante incrementerà il valore di una varibile per abilitare "sezioni" di codice che abiliteranno i due pulsanti + e - ed eseguiranno quello che vuoi.

Sempre priorità ai pargoli! ;D ;D
Comunque anche io avevo pensato ( e ho anche fatto qualche prova, con scarsi risultati) 4 pulsanti (+,-, enter, esc). Ma non ho idea di come implementarli nello sketch.

Se per implementarli intendi acquisirne i valori, basta che dopo che li hai dichiarati come ingressi (ed hai dichiarato 4 variabili per assegnargli i relativi stati) fai semplicemente 4 digitalRead all'inizio del loop (in questo modo lo stato viene letto ogni ciclo del loop) ... pero' ti consiglio di eliminare tutti i delay, sostituendoli con dei controlli di tempo basati su millis() ... perche' un delay blocca completamente il programma ... i tuoi delay(2000) bloccano tutto, inclusa la lettura dei pulsanti e l'esecuzione dello sketch, ogni volta per 2 secondi, e questo vuol dire che per tutto il tempo in cui il sistema e' bloccato, non risponde alla pressione dei pulsanti, ne legge valori, ne fa qualsiasi altra cosa ... :wink:

Allora io vorrei creare un menu tipo:

int Tmax = 26 


Schermata principale "pageHome":


temp: 23,00°
hum:  62,00%



Se premo il pulsante "conferma" sulla schermata principale entra nella schermata "page1":


         Tmax
enter    -    +    esc


se premo il pulsante "+" passa alla variabile successiva es.: "pageTmin"
se premo il pulsante "-" torna alla variabile precedente es.: "pageTmax"
se premo il pulsante esc torna sulla "page1" altrimenti "enter" per entrare in "pageTmax"  che mi visualizza la variabile (da incrementare o decrementare):





                26
 enter     -      +     esc



se premo il pulsante "+" la variabile Tmax incrementa di 1, se premo "-" decresce di 1
se premo il pulsante "esc" torna alla schermata di selezione variabile "page1"
per salvare il valore e tornare al menu premo "enter"

Solo che non ho nemmeno idea di come cominciare a scrivere il codice.

PS: per usare i millis al posto dei delay ci ho provato tempo fa ma non sono ancora in grado di usare questa funzione (devo studiare di piu :slight_smile: :slight_smile: :slight_smile: )

ciao…di seguito un esempio di come cambiare “saluto” in un display lcd premendo un pulsante impostato come INPUT_PULLUP…ovvio che libreria e inizializzazione e pin del bottone dovrai adattarli al tuo sketch.

#include <LiquidCrystal.h>

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

byte buttonPin = 2;
boolean pinState = false;
byte passo = 0;

void setup() {
  lcd.begin(16, 2);
  lcd.clear();
  pinMode(buttonPin, INPUT_PULLUP); // ATTENZIONE COLLEGARE IL PULSANTE A GND!

}

void loop() {

  if (digitalRead(buttonPin)) {
    pinState = false;
    if (passo == 2) {
      passo = 0;
    }
  }

  if (!digitalRead(buttonPin) && !pinState && passo == 0) {
    lcd.clear();
    lcd.write("ciao!");
    pinState = true;
    passo++;
  }
  if (!digitalRead(buttonPin) && !pinState && passo == 1) {
    lcd.clear();
    lcd.write("arrivederci!");
    pinState = true;
    passo++;
  }

}

ORSO2001:
ciao…di seguito un esempio di come cambiare “saluto” in un display lcd premendo un pulsante impostato come INPUT_PULLUP…ovvio che libreria e inizializzazione e pin del bottone dovrai adattarli al tuo sketch.

#include <LiquidCrystal.h>

LiquidCrystal lcd(7, 8, 9, 10, 11, 12);

byte buttonPin = 2;
boolean pinState = false;
byte passo = 0;

void setup() {
 lcd.begin(16, 2);
 lcd.clear();
 pinMode(buttonPin, INPUT_PULLUP); // ATTENZIONE COLLEGARE IL PULSANTE A GND!

}

void loop() {

if (digitalRead(buttonPin)) {
   pinState = false;
   if (passo == 2) {
     passo = 0;
   }
 }

if (!digitalRead(buttonPin) && !pinState && passo == 0) {
   lcd.clear();
   lcd.write(“ciao!”);
   pinState = true;
   passo++;
 }
 if (!digitalRead(buttonPin) && !pinState && passo == 1) {
   lcd.clear();
   lcd.write(“arrivederci!”);
   pinState = true;
   passo++;
 }

}

Forse sto tentando di fare il passo piu lungo della gamba. Mi sto rendendo conto che non sono ancora pronto per “progettare” certe funzioni. :frowning:
credo di dover abbandonare almeno per ora questo progetto, e continuare a studiare :smiling_imp:
Comunque infinitamente grazie per la vostra disponibilità ;D ;D ;D

diegohaze:
...
PS: per usare i millis al posto dei delay ci ho provato tempo fa ma non sono ancora in grado di usare questa funzione (devo studiare di piu :slight_smile: :slight_smile: :slight_smile: )

Non sono un programmatore, ma vediamo ... mi sembra che a te' i delay servano per eseguire alcune funzioni con un certo intervallo, se non ho capito male ... dichiara un unsigned long in piu per usarlo come variabile di confronto ed assegnagli il valore di millis() alla fine del setup, poi metti le operazioni da eseguire ad intervalli in un'if, e fai un confronto fra millis e quella variabile, eseguendo l'if solo quando il tempo e' passato (e resettando di nuovo la variabile dentro l'if, ovviamente)

Una cosa del genere (pseudocodice) ... diciamo che la tua variabile la chiami tempo, e che ti servono ritardi di 2 secondi ...

if(millis() - tempo >= 2000)
{
   ... tutte le tue operazioni ...;
   tempo=millis();
}

... in questo modo l'if viene eseguito solo ogni 2 secondi (per il resto del tempo il contenuto delle graffe viene ignorato), ma allo stesso tempo il loop continua a girare, quindi puo eseguire anche tutto il resto ... :wink:

Poi non e' che i delay siano sbagliati del tutto, per conto loro ... vanno bene in alcuni specifici casi, cioe' quando tu VUOI che in tutto quel tempo il programma non faccia assolutamente nient'altro (ad esempio, una macchina a stati finiti con esecuzioni condizionate a specifici periodi, dove mentre attende NON DEVE fare altre cose intenzionalmente) ... ma se nel frattempo deve leggere ingressi, pilotare uscite, o fare qualsiasi altra cosa, non si possono usare ...

Due piccole note ...

  1. anche il "pseudo-codice" sottostà al regolamento e .. va chiuso nei tag CODE :wink:

  2. per migliorare la precisione temporale quell'IF, supponendo una periodicità di 2 sec., si può scrivere in questo modo:

if(millis() - tempo >= 2000)
{
   ... tutte le tue operazioni ...;
   tempo += 2000;
}

Guglielmo

Fatto :smiley:

EDIT: a te la finestra del code si ridimensiona ? ... a me appare alta una decina di righe anche se ce ne scrivo dentro solo una ed il resto dello spazio e' vuoto ...

Per prima cosa GRAZIE per la vostra pazienza.
ho provato cambiando cosi:

//Librerie
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <Wire.h>


//Costanti
#define DHTPIN 9     // pin dht
#define DHTTYPE DHT11   // Seleziona tipo di sensore dht11/22
#define relePinV 10
#define relePinH 11
#define relePinR 12
#define pinLedV 6
#define pinLedH 7
#define pinLedR 8

DHT dht(DHTPIN, DHTTYPE); // Inizializza sensore dht

LiquidCrystal_I2C lcd(0x3F,16,2);

//Variabili
float hum;  //Valore umidità
float temp; //Valore temperatura
float tempMin = 13;               //Tmin
float tempMax = 26;               //Tmax
float huMin = 45;                 //Hmin
float huMax = 75;                 //Hmax
unsigned long tempo;

void setup()
{
   dht.begin();
  lcd.begin();
  lcd.backlight();
  pinMode (relePinV, OUTPUT);
  pinMode (relePinH, OUTPUT);
  pinMode (relePinR, OUTPUT);
  pinMode (pinLedV,OUTPUT);
  pinMode (pinLedH,OUTPUT);
  pinMode (pinLedR,OUTPUT);
  digitalWrite(relePinV, HIGH);
  digitalWrite(relePinH, HIGH);
  digitalWrite(relePinR, HIGH);
  tempo=2000;
}

void loop()
{
  

  if(millis() - tempo >= 2000)
{
  hum = dht.readHumidity();
  temp = dht.readTemperature();
 tempo += 2000;
}
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Temp: ");
  lcd.print(temp);
  lcd.print(" ");
  lcd.print((char)223);
  lcd.print("C");
  lcd.setCursor(0,1);
  lcd.print("Hum:  ");
  lcd.print(hum);
  lcd.print(" %");

 


  



if (temp>(tempMax) || hum>(huMax)) {// ventola aspirazione per temperatura o umidità alta
    digitalWrite(relePinV, LOW);
    digitalWrite(pinLedV,HIGH);     //led ventola
   } 
   
  else {
     digitalWrite(relePinV, HIGH);
     digitalWrite(pinLedV,LOW); 
      
}
if (temp<tempMin) {                 //riscaldamento
    digitalWrite(relePinR, LOW);
    digitalWrite(pinLedR,HIGH);     //led riscaldatore
   } 
  
  else {
     digitalWrite(relePinR, HIGH);
     digitalWrite(pinLedR,LOW);
}
if (hum<huMin) {                    //umidificatore
    digitalWrite(relePinH, LOW);
    digitalWrite(pinLedH,HIGH);     //led umidificatore
   }
   else {
     digitalWrite(relePinH, HIGH);
     digitalWrite(pinLedH,LOW);
   }
 
}

Carico lo sketch, funziona pero’ il display da un immagine tromolante per tue secondi, poi l’immagine si fissa un istante e ricomincia a tremare (suppungo sia dovuto alla pausa che la funzione millis() fa fare al dht)

la differenza del tuo primo sketch, con delay(), e quest'ultimo che hai postato, con millis(), è che nel primo aggironavi l'LCD ogni 4 secondi, delay(2000) all'inizio e delay(2000) alla fine, con millis() avendo lasciato nel loop i vari lcd.clear() etc etc...cerchi di aggironare il display su frazioni di secondo "minime"....

ORSO2001:
la differenza del tuo primo sketch, con delay(), e quest’ultimo che hai postato, con millis(), è che nel primo aggironavi l’LCD ogni 4 secondi, delay(2000) all’inizio e delay(2000) alla fine, con millis() avendo lasciato nel loop i vari lcd.clear() etc etc…cerchi di aggironare il display su frazioni di secondo “minime”…

Quindi meglio con il delay? ma lo schema è corretto?

come detto da Etemenanki di solito è preferibile l'utilizzo di millis() in quanto il programma continua a ciclare, quindi a verificare gli input dal campo o ad eseguire altre azioni, in certi casi meglio delay().

nel tuo caso, secondo me, meglio millis() per eseguire verifiche dal dht e magari un altra if sempre con millis() per stampare a video con un altra condizione da rispettare...e ti spiego perchè...millis(), non stoppando il programma, ti consente di eseguire azioni, tipo premere un pulsante, che viene verificato, con delay() non ci riesci sempre...e quindi puoi abilitare altri menu...ecco la seconda condizione da rispettare "nessun pulsante premuto"...e via dicendo menù 1, 2 , fino a conferma finale, od uscita, che riabilità la stampa LCD normale.

Si forse dovrei convertire tutto in millis (e capire meglio questa funzione) e poi creare un menu per il display.

Vorrei porvi un’altra domanda, volendo creare un menu per gestire le variabili tramite pulsanti e lcd (anche se vorrei poi arrivare ad usare il nextion che ho già acquistato), ho la necessità di salvare i valori delle variabili se vengono incrementate o decrementate in modo da poter ripartire da quei valori in caso di riavvio dell arduino. Devo ricorrere ad una eeprom esterna o posso usare un lettore di sd card? Giusto per capire già che direzione prendere :slight_smile:

arduino ha una sua EEPROM interna che può sopportare, se non sbaglio, 100.000 cicli tra scrittura e lettura…puoi utilizzare quella…c’è una libreria apposita già inclusa con l’IDE…#include <EEPROM.h>

la grandezza della EEPROM dipende,diciamo, dal modello di arduino.

ORSO2001:
arduino ha una sua EEPROM interna che può sopportare, se non sbaglio, 100.000 cicli tra scrittura e lettura…puoi utilizzare quella…c’è una libreria apposita già inclusa con l’IDE…#include <EEPROM.h>

la grandezza della EEPROM dipende,diciamo, dal modello di arduino.

Grazie mille!

diegohaze:
Quindi meglio con il delay? ...

No, meglio risolvere il problema in modo migliore :wink:

Se ti basta aggiornare il display ogni 2 secondi, metti anche i comandi del display nell'if (in questo modo e' vero che hai l'aggiornamento della visualizzazione solo ogni 2 secondi, quando leggi i sensori ... pero' e' anche vero che non ti serve a nulla aggiornarlo piu spesso, dato che i sensori, appunto, li leggi solo ogni 2 secondi ... ;))