Problema con controllore serra

Ciao Ragazzi,
da circa un paio di mesi sto lavorado su un controllore di una piccola serra basato su Arduino UNO.
La parte hardware è già funzionante, la parte software mi crea un piccolo errore.
Arduino gestisce un timer luce e una ventola e stampa a video Temperatura e Umidita . Io vorrei che Arduino attivasse il fanRelay (ventola di aspirazione) sempre quando il lightRelay (luce) è accesa e quando la luce è spenta acceda la ventola solo se l'umidità sale sopra il 55%. Il programma come ho scritto io non funziona correttamente, perchè se l'umidità si abbassa mentre la lampada è accesa si spegno il fanRelay. Sapete dirmi dove sbaglio?
Altro problemino i secondi sullo schermo non scorrono correttamente. Poi se avete migliorie ditemi pure!

#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include "DHT.h"
#include <LiquidCrystal.h>
#define DHTPIN 8 
#define DHTTYPE DHT21 
DHT dht(DHTPIN, DHTTYPE);
#include <EEPROM.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// FSM states
#define STATE_OFF  0
#define STATE_ON   1

// Timer settings
#define START_TIME  1000
#define END_TIME    2200


RTC_DS1307 RTC;
// Var for Tmax e Tmin
byte tMIN;
byte tMAX;

// Var for Hmax e Hmin
byte hMIN;
byte hMAX;


int FanState; 
int lightState;

// Define Relay 
int lightRelay = 6;
int fanRelay = 7;

const int reset = 10;
int statoreset = 0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  RTC.begin();
  lcd.begin(20,4);
  dht.begin(); 
  tMIN=EEPROM.read(1);
  tMAX=EEPROM.read(2);
  hMIN=EEPROM.read(3);
  hMAX=EEPROM.read(4); 
  
  pinMode(reset, INPUT);
  digitalWrite(10, HIGH);
  // Set the pinmode
  pinMode(lightRelay, OUTPUT);
  pinMode(fanRelay, OUTPUT);
  digitalWrite(lightRelay, LOW);
  lightState = STATE_OFF;
  digitalWrite(fanRelay, LOW);
}
 
void loop() {
  {
    int h = dht.readHumidity();
    int t = dht.readTemperature();
    float h1 = dht.readHumidity();
    float t1 = dht.readTemperature();
    if (isnan(t) || isnan(h)) {
    Serial.println("FALIED to read from DHT");
}
else {
  lcd.setCursor(0,1);
  lcd.print("T:");
  lcd.print(t);
  lcd.print("C");
  lcd.setCursor(0,2);
  lcd.print("H:");
  lcd.print(h);
  lcd.print("%");
  
  Serial.print("T:");
  Serial.print(t1);
  Serial.println("");
  Serial.print("H:");
  Serial.print(h1);
    
  delay(100);
}

if (t > tMAX)
{tMAX = t;
EEPROM.write(2, t);}
// se la temperatura supera il dato MAX allora modifico il dato MAX
// scrivo questo dato anche nella eeprom
if (t < tMIN)
{tMIN = t;
EEPROM.write(1, t);}
// se la temperatura è minore del dato MIN allora modifico il dato MIN
// scrivo questo dato anche nella eeprom


if (h > hMAX)
{hMAX = h;
EEPROM.write(4, h);}
// se la temperatura supera il dato MAX allora modifico il dato MAX
// scrivo questo dato anche nella eeprom
if (h < hMIN)
{hMIN = h;
EEPROM.write(3, h);}
// se la temperatura è minore del dato MIN allora modifico il dato MIN
// scrivo questo dato anche nella eeprom
statoreset = digitalRead (reset);
if( statoreset == LOW){
  tMIN = t;
  tMAX = t;
  hMAX = h;
  hMIN = h;
}

lcd.setCursor(6,1);
lcd.print("T+:");
lcd.print(tMAX);
lcd.print("C");
lcd.setCursor(13,1);
lcd.print("T-:");
lcd.print(tMIN);
lcd.print("C");
lcd.setCursor(6,2);
lcd.print("H+:");
lcd.print(hMAX);
lcd.print("%");
lcd.setCursor(13,2);
lcd.print("H-:");
lcd.print(hMIN);
lcd.print("%");

statoreset = digitalRead (reset);
if( statoreset == LOW){
  tMIN = t;
  tMAX = t;
  hMAX = h;
  hMIN = h;
}


   if (h1>55.00){
   digitalWrite(fanRelay, HIGH);
   Serial.println("");
   Serial.println("Fan On");
   FanState = STATE_ON;
   lcd.setCursor(10,3);
   lcd.print("Fan ON");
}
  if (h1<45){
  digitalWrite(fanRelay, LOW);
  Serial.println("");
  Serial.println("Fan Off");
  FanState = STATE_OFF;
  lcd.setCursor(10,3);
  lcd.print("      ");
}

}
  DateTime now = RTC.now();
  int nowHourMinute = now.hour()*100 + now.minute(); 
   
  // FSM states
  switch(lightState) {
    
    case STATE_OFF:
      if(nowHourMinute > START_TIME && nowHourMinute < END_TIME) {
        Serial.print(now.hour(), DEC);
        Serial.print(':');
        Serial.print(now.minute(), DEC);
        digitalWrite(lightRelay, HIGH);
        digitalWrite(fanRelay, HIGH);
        Serial.println("Fan On");
        FanState = STATE_ON;
        lcd.setCursor(10,3);
        lcd.print("Fan ON");
        lcd.setCursor(0,3);
        lcd.print("Light ON");
        lightState = STATE_ON;
        
      }
      break;
    
    case STATE_ON:
      if(nowHourMinute > END_TIME) {
        Serial.print(now.hour(), DEC);
        Serial.print(':');
        Serial.print(now.minute(), DEC);       
        digitalWrite(lightRelay, LOW);
        digitalWrite(fanRelay, LOW);
        lcd.setCursor(0,3);
        FanState = STATE_OFF;
        lcd.print("        ");
        lightState = STATE_OFF;
        
      }    
      break;
  }
 
  
 
  lcd.setCursor(0,0);
  if (now.day()<10)
  {
  lcd.print("0");
  }
  lcd.print(now.day(), DEC);
  lcd.print('/');
  if (now.month()<10)
  {
  lcd.print("0");
  }
  lcd.print(now.month(), DEC);
  lcd.print('/');
  lcd.print(now.year(), DEC);
  lcd.print(' ');
  if (now.hour()<10)
  {
  lcd.print("0");
  }
  lcd.print(now.hour(), DEC);
  lcd.print(":");
  if (now.minute()<10)
  {
    lcd.print("0");
  }
  lcd.print(now.minute(), DEC);
  lcd.print(":");
  if (now.second()<10)
  {
    lcd.print("0");
  }
  lcd.print(now.second(), DEC);
  
  Serial.println("");
  Serial.print(now.day(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.year(), DEC);
  Serial.print(' ');
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();   
  Serial.println();
  Alarm.delay(100);
}

Ti invitiamo a presentarti qui: Re: Presentazioni nuovi iscritti, fatevi conoscere da tutti! (Part 1) - Generale - Arduino Forum
e a leggere il regolamento: [REGOLAMENTO] Come usare questa sezione del forum - Italiano - Arduino Forum

Sulle migliorie, io un appunto l'avrei. Hai fatto un switch() usando una variabile di stato a soli 2 stati per la luce, ma secondo me era meglio avere uno stato "generale" di tutto il sistema. E quindi molti più stati.

nid69ita:
Ti invitiamo a presentarti qui: Re: Presentazioni nuovi iscritti, fatevi conoscere da tutti! (Part 1) - Generale - Arduino Forum
e a leggere il regolamento: [REGOLAMENTO] Come usare questa sezione del forum - Italiano - Arduino Forum

Ciao ho appena effettuato la presentazione, ( pensavo di averla fatta in passato da quando mi ero iscritto al forum), la parte hardware funziona correttamente e i collegamenti sono stati effettuati correttamente, ho solo quell'errore sul programma.
Non ho capito la parte dove dici:

Sulle migliorie, io un appunto l'avrei. Hai fatto un switch() usando una variabile di stato a soli 2 stati per la luce, ma secondo me era meglio avere uno stato "generale" di tutto il sistema. E quindi molti più stati.

Potresti spiegarmi meglio?

Guidus93:

Sulle migliorie, io un appunto l'avrei. Hai fatto un switch() usando una variabile di stato a soli 2 stati per la luce, ma secondo me era meglio avere uno stato "generale" di tutto il sistema. E quindi molti più stati.

Potresti spiegarmi meglio?

Beh, ad esempio: "il lightRelay (luce) è accesa e quando la luce è spenta acceda la ventola solo se l'umidità sale sopra il 55%"
Tu non hai solo la luce accesa/spenta ma anche una ventola da attivare in base a umidità. Pensavo a più stati. E' solo una pensata, magari ti complica.
Esempio
LUCESPENTA_VENTOLAOFF (non fai altro mi pare ?)
LUCEACCESA_VENTOLAON
LUCEACCESA_VENTOLAOFF
Ripeto è solo una idea per estendere l'idea della gestione degli stati

l'errore sta nel fatto che se la luce è accesa, tu controlli solo il tempo

case STATE_ON:
      if(nowHourMinute > END_TIME) {
        Serial.print(now.hour(), DEC);
        Serial.print(':');
        Serial.print(now.minute(), DEC);       
        digitalWrite(lightRelay, LOW);
        digitalWrite(fanRelay, LOW);
        lcd.setCursor(0,3);
        FanState = STATE_OFF;
        lcd.print("        ");
        lightState = STATE_OFF;

per cui se durante l'accensione della lampada, il loop legge l'umidità e compie l'operazione che vuoi tu

 if (h1<45){
  digitalWrite(fanRelay, LOW);
  Serial.println("");
  Serial.println("Fan Off");
  FanState = STATE_OFF;
  lcd.setCursor(10,3);
  lcd.print("      ");

una soluzione potrebbe essere fare una procedura AttivaUscite(luce, ventilatore) i cui parametri li cambi nel loop, poi la richiami solo alla fine... oppure mettere il controllo che spegne la ventola all'interno del case "STATE_OFF", che è ancora più semplice

Ho modificato così ma quando la lampada è spenta il controllo umidità non funziona, se inserisco il controllo umidità nel loop si spegne la ventola anche quando la luce è accesa. I miei 3 stati sono:

  • Luce Accesa - Ventola Accesa
  • Luce Spenta - Ventola Spenta (se h<49)
  • Luce Spenta - Ventola Accesa (se h>52)

Poi persiste ancora il problema dello scorrimento dei secondi che sono sfasati, in più Arduino (a cui ho collegato un RTC DS1307) perde l'ora, cioè solitamente dopo una settimana si spunta (avanti solitamente di 5 minuti)

DateTime now = RTC.now();
  int nowHourMinute = now.hour()*100 + now.minute(); 
  int h2 = dht.readHumidity();
  // FSM states
  switch(lightState) {

  case STATE_OFF:
    if(nowHourMinute > START_TIME && nowHourMinute < END_TIME) {
      Serial.print(now.hour(), DEC);
      Serial.print(':');
      Serial.print(now.minute(), DEC);
      digitalWrite(lightRelay, HIGH);
      digitalWrite(fanRelay, HIGH);
      Serial.println("Fan On");
      FanState = STATE_ON;
      lcd.setCursor(10,3);
      lcd.print("Fan ON");
      lcd.setCursor(0,3);
      lcd.print("Light ON");
      lightState = STATE_ON;

    }
    break;

  case STATE_ON:
    if(nowHourMinute > END_TIME) {
      Serial.print(now.hour(), DEC);
      Serial.print(':');
      Serial.print(now.minute(), DEC);       
      digitalWrite(lightRelay, LOW);
      if (h2>51.00){
        digitalWrite(fanRelay, HIGH);
        lcd.setCursor(10,3);
        lcd.print("Fan ON");
      }
      if (h2<49.00){
        digitalWrite(fanRelay, LOW);
        lcd.setCursor(10,3);
        lcd.print("      ");
      }
      lcd.setCursor(0,3);
      lcd.print("        ");
      lightState = STATE_OFF;

    }    
    break;
  }

Dopo un po' di studio e prove pratiche sono arrivato alla soluzione:

/*
 Grow&Fun  
 v 1.0: Timer programmabile per luce e controlo ventola in base alla temperatura impostata
 v 1.1: Ora la ventola viene controllata in base all'umidità
 02/04/2014 
 da Guido Fabbrini
 */
#include <Wire.h>
#include <RTClib.h>
#include <Time.h>
#include <TimeAlarms.h>
#include "DHT.h"
#include <LiquidCrystal.h>
#define DHTPIN 8 
#define DHTTYPE DHT21 
DHT dht(DHTPIN, DHTTYPE);
#include <EEPROM.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

// Light states
#define STATE_OFF  0
#define STATE_ON   1

// Timer settings
#define START_TIME  0500
#define END_TIME    2300


RTC_DS1307 RTC;
// Var for Tmax e Tmin
byte tMIN;
byte tMAX;

// Var for Hmax e Hmin
byte hMIN;
byte hMAX;


int FanState; 
int lightState;

// Define Relay 
int lightRelay = 6;
int fanRelay = 7;

const int reset = 10;
int statoreset = 0;

void setup() {
  Serial.begin(9600);
  Wire.begin();
  RTC.begin();
  lcd.begin(20,4);
  dht.begin(); 
  tMIN=EEPROM.read(1);
  tMAX=EEPROM.read(2);
  hMIN=EEPROM.read(3);
  hMAX=EEPROM.read(4); 

  pinMode(reset, INPUT);
  digitalWrite(10, HIGH);
  // Set the pinmode
  pinMode(lightRelay, OUTPUT);
  pinMode(fanRelay, OUTPUT);
  digitalWrite(lightRelay, LOW);
  lightState = STATE_OFF;
  digitalWrite(fanRelay, LOW);
}

void loop() {
  {
    int h = dht.readHumidity();
    int t = dht.readTemperature();
    float h1 = dht.readHumidity();
    float t1 = dht.readTemperature();
    if (isnan(t) || isnan(h)) {
      Serial.println("FALIED to read from DHT");
    }
    else {
      lcd.setCursor(0,1);
      lcd.print("T:");
      lcd.print(t);
      lcd.print("C");
      lcd.setCursor(0,2);
      lcd.print("H:");
      lcd.print(h);
      lcd.print("%");

      Serial.print("T:");
      Serial.print(t1);
      Serial.println("");
      Serial.print("H:");
      Serial.print(h1);

      delay(100);
    }

    if (t > tMAX)
    {
      tMAX = t;
      EEPROM.write(2, t);
    }
    // se la temperatura supera il dato MAX allora modifico il dato MAX
    // scrivo questo dato anche nella eeprom
    if (t < tMIN)
    {
      tMIN = t;
      EEPROM.write(1, t);
    }
    // se la temperatura è minore del dato MIN allora modifico il dato MIN
    // scrivo questo dato anche nella eeprom


    if (h > hMAX)
    {
      hMAX = h;
      EEPROM.write(4, h);
    }
    // se la temperatura supera il dato MAX allora modifico il dato MAX
    // scrivo questo dato anche nella eeprom
    if (h < hMIN)
    {
      hMIN = h;
      EEPROM.write(3, h);
    }
    // se la temperatura è minore del dato MIN allora modifico il dato MIN
    // scrivo questo dato anche nella eeprom
    statoreset = digitalRead (reset);
    if( statoreset == LOW){
      tMIN = t;
      tMAX = t;
      hMAX = h;
      hMIN = h;
    }

    lcd.setCursor(6,1);
    lcd.print("T+:");
    lcd.print(tMAX);
    lcd.print("C");
    lcd.setCursor(13,1);
    lcd.print("T-:");
    lcd.print(tMIN);
    lcd.print("C");
    lcd.setCursor(6,2);
    lcd.print("H+:");
    lcd.print(hMAX);
    lcd.print("%");
    lcd.setCursor(13,2);
    lcd.print("H-:");
    lcd.print(hMIN);
    lcd.print("%");

    statoreset = digitalRead (reset);
    if( statoreset == LOW){
      tMIN = t;
      tMAX = t;
      hMAX = h;
      hMIN = h;
    }


  }
  DateTime now = RTC.now();
  int nowHourMinute = now.hour()*100 + now.minute(); 
  int h2 = dht.readHumidity();
  // FSM states
  switch(lightState) {

  case STATE_OFF:
    if(nowHourMinute > START_TIME && nowHourMinute < END_TIME) {
      Serial.print(now.hour(), DEC);
      Serial.print(':');
      Serial.print(now.minute(), DEC);
      digitalWrite(lightRelay, HIGH);
      digitalWrite(fanRelay, HIGH);
      Serial.println("\nFan On");
      Serial.println("\nLight On");
      FanState = STATE_ON;
      lcd.setCursor(10,3);
      lcd.print("Fan ON");
      lcd.setCursor(0,3);
      lcd.print("Light ON");
      lightState = STATE_ON;

    }
    break;

  case STATE_ON:
    if(nowHourMinute > END_TIME) {
      Serial.print(now.hour(), DEC);
      Serial.print(':');
      Serial.print(now.minute(), DEC);       
      digitalWrite(lightRelay, LOW);
      lcd.setCursor(0,3);
      lcd.print("        ");
      lightState = STATE_OFF;

    }    
    break;
  }
  switch(FanState) {

  case STATE_OFF:
    if(lightState = STATE_ON && h2>51) {
      digitalWrite(fanRelay, HIGH);
      Serial.println("\nFan On");
      FanState = STATE_ON;
      lcd.setCursor(10,3);
      lcd.print("Fan ON");

    }
    break;

  case STATE_ON:
    if(lightState= STATE_ON && h2<49) {
      digitalWrite(fanRelay, LOW);
      Serial.println("\nFan Off");
      lcd.setCursor(10,3);
      lcd.print("      ");
      FanState = STATE_OFF;
    }    
    break;
  }

  lcd.setCursor(0,0);
  if (now.day()<10)
  {
    lcd.print("0");
  }
  lcd.print(now.day(), DEC);
  lcd.print('/');
  if (now.month()<10)
  {
    lcd.print("0");
  }
  lcd.print(now.month(), DEC);
  lcd.print('/');
  lcd.print(now.year(), DEC);
  lcd.print(' ');
  if (now.hour()<10)
  {
    lcd.print("0");
  }
  lcd.print(now.hour(), DEC);
  lcd.print(":");
  if (now.minute()<10)
  {
    lcd.print("0");
  }
  lcd.print(now.minute(), DEC);
  lcd.print(":");
  if (now.second()<10)
  {
    lcd.print("0");
  }
  lcd.print(now.second(), DEC);

  Serial.println("");
  Serial.print(now.day(), DEC);
  Serial.print('/');
  Serial.print(now.month(), DEC);
  Serial.print('/');
  Serial.print(now.year(), DEC);
  Serial.print(' ');
  Serial.print(now.hour(), DEC);
  Serial.print(':');
  Serial.print(now.minute(), DEC);
  Serial.print(':');
  Serial.print(now.second(), DEC);
  Serial.println();   
  Serial.println();
  Alarm.delay(100);
}

L'ultima cosa che vorrei correggere è il problema del tempo:
i secondi non scorrono bene, e ogni settimana più o meno l'orologio è avanti di 5 minuti,
suggerimenti?

Suggerimenti? Sì, evita il grassetto.. e per il tempo convivici perché gli RTC tipo DS1307 hanno un'elevato margine di errore. Sono chip economici che accumulano diversi secondi al giorno, che a fine mese si traducono anche in minuti di differenza. Se vuoi un orario più preciso, devi acquistare RTC differenti, ci sono ad esempio quelli termocompensati che aggiustano il clock in base alla temperatura, ed hanno uno scarto minimo ma costano un botto. Devi solo rispondere a questa domanda: è accettabile avere uno scarto di 5 minuti al mese al costo di 2€ di chip oppure vuoi uno scarto di 5 secondi al mese al costo di 20€ di chip?

ciao
mi puoi indicare un chip con la precisione maggiore di un 1307

grazie
Stefano

Il ds3231... C'è anche il modulino pronto ChronoDot - Ultra-precise Real Time Clock [v3] : ID 255 : $17.50 : Adafruit Industries, Unique & fun DIY electronics and kits

Oppure il DS3234:

(accuratezza 2 ppm)

Allora per il modulo rtc ho capito e immaginavo che fosse il chip troppo econimico.Mentre per quanto riguarda il programma c'è ancora una piccola falla, quando l'H scende sotto il 49% con la lampada accesa si spegne la ventola. Dove devo correggere il programma?

Per l'ultimo dubbio... immagino di sì, dovrai ristudiarti gli incroci tra i diversi stati di lampada e ventola.

un suggerimento te l'avevo dato. se non vuoi controllare la ventola con la luce accesa ma tenerla sempre attiva, togli il controllo dal loop e fallo solo a lampada spenta...quindi
lampada ON ->ventilazione accesa
lampada OFF->controllo umidità