Irrigazione automatica

karnhack: tralasciando per ora l'RTC e concentrandomi sulle funzioni delle cosiddette fasi... sempre che questo modo di procedere non mi incasini ancora di più le cose.

In un normale diagramma di flusso le fasi di funzionamento ci sarebbero comunque, solo che sarebbero "embeddate" e implicite nella la sequenza delle istruzioni invece di essere esplicite. Anche se non sembra in realtà con le fasi esplicite le cose si semplificano, ci si accorge di questo appena si vuole modificare qualcosa o aggiungere qualche funzionalità (soprattutto fare qualcosa in multitasking).

Per l'RTC... se scrivi una funzioncina 'leggi_rtc' fittizia che restituisce un orario fittizio a te comodo, il fatto di non usare l'RTC reale è ininfluente per il resto del programma. Poi basta modificare solo quella funzione per leggere l'RTC vero.

Ogni sforzo fatto per comprendere la struttura di questo progetto diventa strada spianata per qualsiasi altro progetto futuro.

Ora quello che c'è da dettagliare meglio è a cosa prestare attenzione in ogni fase, e quali azioni svolgere in quel caso.

Anche se non ho fatto chissà che sono già contento di non aver scopiazzato qua e la. Intanto allego i progressi e rinnovo la mia gratitudine per gli aiuti

#include 
#include 

#define POMPA 5 // definisce il pin della pompa
#define ELETTROVALVOLA 6
#define TROPPOPIENO A0 // ??? devo ancora capire come funziona il sensore del troppopieno (contatto aperto-chiuso) ???

int ACCESO = LOW; // sostituisce la logica del pin (LOW/HIGH) con il termine ACCESO/SPENTO per una migliore comprensione della fuzione
int SPENTO = HIGH;

RTC_DS1307 RTC;

void setup() {
  pinMode(POMPA, OUTPUT); // imposta il pin (POMPA) come output
  pinMode(ELETTROVALVOLA, OUTPUT);
  pinMode(TROPPOPIENO, INPUT);

  digitalWrite(POMPA, SPENTO); // all'avvio di arduino spegne la pompa
  digitalWrite(ELETTROVALVOLA, SPENTO);
  
/***************************RTC e WIRE**************************/
  Serial.begin(9600);
  Serial.println( "START" );
   
  Wire.begin();
  RTC.begin();

//  RTC.adjust(DateTime(2019, 05, 23, 12, 53, 00)); // decommentare per regolare ora e data (yyyy,mm,dd,hh,mm,ss)

  if (! RTC.isrunning()) {
    Serial.println("RTC non è attivo!");
    RTC.adjust(DateTime(__DATE__, __TIME__));
  }
}






void loop() {
  if ( RTC.isrunning()) {
    DateTime now = RTC.now();
    Serial.print(now.year(), DEC);
    Serial.print('/');
    Serial.print(now.month(), DEC);
    Serial.print('/');
    Serial.print(now.day(), DEC);
    Serial.print(' ');
    Serial.print(now.hour(), DEC);
    Serial.print(':');
    Serial.print(now.minute(), DEC);
    Serial.print(':');
    Serial.print(now.second(), DEC);
    Serial.println();
    }
}

Claudio_FF:
Per quanto riguarda il controllo dei periodi orari, meglio lavorare con i minuti, in questo modo basta evitare un periodo a cavallo della mezzanotte:

int adesso = now.hour()*60 + now.minute();  // tempi da 0 a 1439 minuti

int inizio = Pompa[0]*60 + Pompa[1];
int fine  = Pompa[2]*60 + Pompa[3];

Mi dite gentilmente a che servono i numeri nelle parentesi quadre?

Prova.ino (2.36 KB)

Mi dite gentilmente a che servono i numeri nelle parentesi quadre?

Siamo messi male. :) Si chiamano array (o vettori) link

int ACCESO = LOW;

Anche qui serve studiare i tipi di variabile. più corretto è,

const byte acceso = LOW;

Mentre per le macro (preprocessore C) corretto è,

#define ACCESO LOW

Ciao.

Maurotec: Siamo messi male. :)

Quoto in pieno! grazie per il link!

Ecco ora mi sono arenato… se switch è l’ora attuale, nel mio caso ADESSO, però ADESSO viene ricavato da

now.hour()*60 + now.minute();  // tempi da 0 a 1439 minuti

come faccio a prelevare anche il mese senza superare i 1439 minuti? c’è un operatore che non sia + e che mi permette di farlo? Allego il codice

Prova.ino (2.79 KB)

Per gli array la sintesi è questa:

-Una variabile è una scatoletta che può contenere un valore. -Un array è una fila di scatolette ciascuna identificata da un indice (che parte da 0 per la prima)

Ho scritto Pompa[n] perché nel codice del primo post gli orari di inizio e fine sono contenuti in un array di quattro posizioni:

int Pompa[] = {13, 29, 13, 30};

e per ottenere i valori bisogna indicare non solo la variabile array, ma anche quale delle sue scatolette:

       .----.----.----.----.
Pompa  | 13 | 29 | 13 | 30 |
       '----'----'----'----'
Indici   0    1    2    3

Per il resto il fatto che tu stia in qualche modo parlando di switch legato alla variabile 'adesso' e di sommare il mese mi fa temere qualcosa di brutto brutto, ma magari non è così :) Prima di pensare a come codificarla, non mi è chiaro come vorresti usare l'informazione del mese, perché il mese ce l'hai già in chiaro con now.month()

Te l’ho già detto che hai il dono della chiarezza? :slight_smile:

Ho capito anche io dopo aver fatto la domanda che stavo facendo una cavolata… poi aggiungendo il MESE col metodo a scatoletta (vettore, array) mi sono reso conto che posso verificare quella condizione in un altro modo.

int MESE[] = {3,5,7,9,11};  // (GEN 1, FEB 2, MAR 3, APR 4, MAG 5, GIU 6, LUG 7, AGO 8, SET 9, OTT 10, NOV 11, DIC 12)

Però ora mi sono arenato di nuovo con sto benedetto switch, in quanto arduino mi restituisce un errore incomprensibile tipo Prova:102:1: error: stray '\302' in program.

In pratica sto provando a fare così ma mi da errore:

switch (ADESSO)
    {
      case RIEMPIMENTO:
      // istruzioni a
      break;

      case RIPOSO:
      // istruzioni a
      break;

      case CONCIMAZIONE:
      // istruzioni a
      break;

      default:
      if ((ADESSO >= INIZIO) && (ADESSO < FINE))
      {
       digitalWrite(PinPOMPA, ACCESO);
      } 
      else 
      { 
       digitalWrite(PinPOMPA, SPENTO); 
      }
      break;
    }

Allego il codice nel caso vogliate dare un occhiata al casino che sto facendo :smiley:

Prova.ino (3.06 KB)

No scusa, nessun errore, avevo copiato ed incollato qualche carattere invisibile dal tuo esempio! FUNZIONA!!! :D :D

No no, l'errore c'è, ed è quello che temevo :)

Lo switch deve funzionare in base al valore della variabile di fase, non del valore orario... Di fatto con lo switch hai scritto l'equivalente esatto di questo if... un sacco di condizioni che non risulteranno mai vere, o meglio, risulteranno vere solo a mezzanotte, a mezzanotte e un minuto, a mezzanotte e due minuti ecc, mentre per tutto il resto del tempo viene eseguito solo l'else:

if (ADESSO == RIEMPIMENTO)
{
    // istruzioni a
}
else if (ADESSO == RIPOSO)
{
    // istruzioni a
}
else if (ADESSO == CONCIMAZIONE)
{
    // istruzioni a
}
else
{
    if ((ADESSO >= INIZIO) && (ADESSO < FINE))
    {
        digitalWrite(PinPOMPA, ACCESO);
    }
    else
    {
        digitalWrite(PinPOMPA, SPENTO);
    }
}

Di solito nella parte default dello switch non c'è bisogno di scrivere nulla. Avviene tutto nei case (o nelle funzioni richiamate dai case). Ed è all'interno dei case che si controlla il valore dell'orario decidendo cosa fare.

      case RIPOSO:  // attendo l'orario, e in quel momento passo a fase irrigazione
          if (in_orario())
          {
              accendi_pompa();
              fase = IRRIGAZIONE;
          }
      break;

      case IRRIGAZIONE:  // attendo la fine orario, e in quel momento passo a riempimento
          if (!in_orario())
          {
              spegni_pompa();
              apri_valvola();
              fase = RIEMPIMENTO;
          }
      break;

Se scrivi tante piccole funzioncine (come accendi_pompa, in_orario ecc) hai l'intera logica scritta in italiano.

In maiuscolo andrebbero scritte solo le costanti, in modo da distinguerle dalle variabili minuscole.

Quanta fretta ma dove corri… https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=2ahUKEwju47j4ibfiAhVQyKQKHRtABHUQyCkwAHoECAoQBQ&url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DYoBuv7HR-Fw&usg=AOvVaw34Sz9Sb-XmZdDvDzkl9Xsd

Freno a mano tirato su.
Esistono delle convenzioni che se rispettate permettono al programmatore di ricavare informazioni in un solo colpo di occhio. Ad esempio se per convenzione scriviamo le macro tutte in maiuscolo e le variabili in minuscolo abbiamo il seguente vantaggio, cioè osservando una piccola porzione di codice di un programma che conta 10000 righe sappiamo che PH_START è un macro, mentre phStatus è una variabile. Ovviamente possiamo modificare il valore di una variabile ma non di una macro.

Stessa potenza c’è nell’uso del tipo di variabili, come dire se uso il tipo int vuole dire che mi serve contenga anche valori negativi. Quando so a priori che la variabile non conterrà mai valori negativi il tipo corretto è byte, uint8_t, uint16_t, uint32_t, boolean ecc, in sostanza tutti tipi senza segno.

No no, l’errore c’è, ed è quello che temevo :slight_smile:

Che vuoi fare l’ebbrezza di avere scritto qualcosa che funziona senza copiare da alla testa. :slight_smile:

PS: lo so, la domanda è; da dove spuntano fuori uint8_t, uint16_t ecc?

Ciao.

Maurotec:
Che vuoi fare l’ebbrezza di avere scritto qualcosa che funziona senza copiare da alla testa. :slight_smile:

E’ stato breve ma intenso, il momento di gloria intendo…
Già non funziona più, appena ho tirato fuori il codice da default:

Non capisco una cosa però, a parte quello che mi avete detto. Lo switch richiede che il case sia dichiarato… si ma in che modo? facendo delle prove avevo messo dei numeri a caso e funzionava lo stesso:

#define RIEMPIMENTO 0 // perchè 0?
#define RIPOSO 1 // perchè 1?
#define CONCIMAZIONE 2 // perchè 2?
#define IRRIGAZIONE 3

Prova.ino (3.15 KB)

karnhack:
facendo delle prove avevo messo dei numeri a caso e funzionava lo stesso:

Dal post #11: «il valore associato ad ogni fase è naturalmente una convenzione»
Dal post #15: «Tra l’altro i valori sono del tutto convenzionali»

Al punto che dandogli dei nomi con ‘define’ o con ‘const byte’ ci possiamo dimenticare dei valori… purché siano tutti diversi l’uno dall’altro.

Claudio_FF: Dal post #11: «il valore associato ad ogni fase è naturalmente una convenzione» Dal post #15: «Tra l'altro i valori sono del tutto convenzionali»

Mi fai paura, ma sei umano o un BOT? :o Che memoria! :open_mouth:

Claudio_FF: Lo switch deve funzionare in base al valore della variabile di fase, non del valore orario... Di fatto con lo switch hai scritto l'equivalente esatto di questo if... un sacco di condizioni che non risulteranno mai vere, o meglio, risulteranno vere solo a mezzanotte, a mezzanotte e un minuto, a mezzanotte e due minuti ecc, mentre per tutto il resto del tempo viene eseguito solo l'else [/code]

Ok quindi se ho capito bene la tua indicazione il problema sta in switch (ADESSO) Quindi dovrei lavorare a cascata? cioè partire dalla fase1, verificarla e poi passare alla fase 2 ecc.

Dunque in poche parole dovrei partire dalla fase RIEMPIMENTO e interrogare la valvola se aperta o chiusa? è aperta, bene allora riempi e passa alla fase 2 RIPOSO. Nella fase RIPOSO vado a interrogare l'RTC in attesa che la data o l'ora coincidano oppure solo l'orario per le operazioni quotidiane, etc. etc. fino alle fasi successive.

Ma quindi la X di "switch (x)" non deve essere un fattore comune a tutte le fasi? è questo che non capisco... La X può essere legata solo alla lettura del sensore troppopieno della fase RIEMPIMENTO? Non so se mi sono spiegato.

karnhack: Quindi dovrei lavorare a cascata? cioè partire dalla fase1, verificarla e poi passare alla fase 2 ecc.

Si, in ogni situazione esegui solo un pezzo del programma, quello relativo alla situazione corrente identificata dalla variabile fase. Quando ci sono le condizioni, ad esempio quando nella fase riempimento trovi che il sensore pieno si è chiuso, modifichi le uscite e passi alla situazione/fase seguente (seguente in senso logico, non necessariamente di numero). Al prossimo giro del programma verrà eseguito il nuovo pezzo di codice relativo alla nuova fase e così via.

Nella fase RIPOSO vado a interrogare l'RTC in attesa che la data o l'ora coincidano oppure solo l'orario per le operazioni quotidiane, etc. etc. fino alle fasi successive.

Esatto.

La variabile 'x' degli esempi del post #15 contiene il numero della fase corrente, e serve per eseguire solo il pezzo di codice di quella fase. Che poi lo fai con switch o con if poco importa.

ci sto sbattendo la testa da un po ma non ne esco, forse ho dichiarato male le fasi

byte faseAttuale=RIEMPIMENTO;
int fase=0;

fatto sta che non riesco a passare da una fase all’altra. Per il momento stavo provando a passare dal RIEMPIMENTO ad IRRIGAZIONE giusto per capire, ma non va

switch (faseAttuale)
    {
      case RIEMPIMENTO:
      if(analogRead(pinTroppopieno) >= 1000) {
        digitalWrite(pinElettrovalvola,ACCESO);
      }
      else
      {
        digitalWrite(pinElettrovalvola,SPENTO);
        fase = IRRIGAZIONE;
      }
      delay(1000);
      //fase = IRRIGAZIONE; // ho provato anche qui
      break;

      case RIPOSO:  
       // istruzioni a  
      break;

      case CONCIMAZIONE:
      // istruzioni a
      break;

      case IRRIGAZIONE:
       digitalWrite(pinPompa,ACCESO);
      break;
      
      default:
      break;
    }
    }
}

Prova.ino (3.35 KB)

Hai dichiarato due diverse variabili.
Lo switch (cioè quale caso viene eseguito) è controllato da ‘faseAttuale’ che rimane invariata, mentre tu modifichi ‘fase’ che non viene usata da nessuna parte.

La logica all’interno dei case può essere più semplice:

Per aggiungere la concimazione basta aggiungere un else if nella fase riposo, che avvia peristalsi e agitatore, e passa alla concimazione.

Pero non mi spiego come mai una cosa che funzionava ieri non funziona oggi…
La lettura di A0 ieri oscillava tra 1023 e 1010 senza nessun voltaggio, oggi invece oscilla da tra i 310 e i 270 sempre a vuoto. Se invece gli do 5V simulando il contatto dell’acqua oscilla tra 0 e 1023. Ho bruciato qualcosa? ho provato anche un altro pin e fa uguale

case RIEMPIMENTO:
        if (analogRead(pinTroppopieno) >= 1000) {
          digitalWrite(pinElettrovalvola, ACCESO);
          
        }
        else
        {
          digitalWrite(pinElettrovalvola, SPENTO);
          
        }
        delay(500);
        fase = RIPOSO;
        break;

Oltre al mistero citato sopra, ho un altro problema, cioè appena viene fatta la lettura di A0 avviene la condizione pinTroppopieno >= 1000) e passa immediatamente all’altra fase senza attendere la condizione inversa, quindi in poche parole l’elettrovalvola rimane accesa. Ho provato ad inserire un delay senza successo, ho provato anche millis ma va in errore.

Prova.ino (3.36 KB)

karnhack: La lettura di A0 ieri oscillava tra 1023 e 1010 senza nessun voltaggio, oggi invece oscilla da tra i 310 e i 270 sempre a vuoto.

Nessun pin di ingresso deve restare scollegato, altrimenti leggendolo ottieni valori casuali. Il perché del ieri così e oggi pomì sta nel fatto che quell'ingresso ieri aveva accumulato più cariche elettrostatiche, e non essendo collegato a niente non potevano essere "smaltite" da nessuna parte.

appena viene fatta la lettura di A0 avviene la condizione pinTroppopieno >= 1000) e passa immediatamente all'altra fase senza attendere la condizione inversa

Certo, perché è esattamente quello che gli hai detto di fare, infatti la modifica alla variabile fase avviene al di fuori di ogni condizione, quindi sempre e subito alla prima esecuzione di quel case, invece dovrebbe avvenire solo nella condizione voluta.

Ho provato ad inserire un delay senza successo, ho provato anche millis ma va in errore.

I tentativi alla cieca non portano da nessuna parte. Se vuoi che un programma faccia quello che vuoi bisogna imparare a pensare con la stessa logica di esecuzione di Arduino... fai finta di essere Arduino ed esegui tu il programma che hai scritto. Se tra la tua esecuzione mentale simulata e quella di Arduino qualcosa non coincide, vuol dire che c'è qualcosa di non compreso ed è inutile andare avanti se prima non ci si chiarisce le idee. Purtroppo non c'è altra strada (a parte farsi scrivere i programmi dagli altri).