Gestire tempi a cavallo del mese con DS3231

Ciao :slight_smile:
Stavo cercando spunti in rete, ma non ho trovato, chiedo consiglio...
Volevo fare un temporizzatore per irrigazione, utilizzando un modulo DS3231 e un nano...
Non riesco a pensare come fare a gestire il cambio mese per i mesi con 30 o 31 giorni; per un tempo giornaliero, non ci sarebbero problemi, basterebbe gestire l'ora, ma volevo, per esempio, annaffiare delle piante grandi, una volta ogni 2 giorni; ciò sono riuscito a pensarlo per il mese, ma quando arrivo a fine mese, come passo al successivo?
Spero di essermi spiegato bene :slight_smile:

Ciao, grazie

La cosa piú semplice é che fai un array con il numero di giorni per mesi. Con questo array puoi controllare quanti giorni sono passati per esempio dal 1. gennaio dell anno.

Ciao Uwe

thedrifter:
volevo, per esempio, annaffiare delle piante grandi, una volta ogni 2 giorni; ciò sono riuscito a pensarlo per il mese, ma quando arrivo a fine mese, come passo al successivo?

In genere le programmazioni sono settimanali, quindi non ci sarebbero problemi in quelle normali, certamente se si volesse annaffiare esattamente ogni 2 giorni non sarebbe realmente possibile visto che 7 non è divisibile per 2 :wink: e quindi si dovrebbe attivare 3 volte a settimana ma con 2 giorni consecutivi senza irrigazione, o 4 volte a settimana con 2 giorni consecutivi di annaffiamento (ma che pianta è, e perché necessiti esattamente di questa programmazione?)...

Dando per scontato che si voglia realmente mantenere questa programmazione ogni 2 giorni, senza stare ad impicciarsi con date, mesi, annibisestili e fasi lunari :wink: ti segnalo che la RTCLib ha anche questa funzione:

uint32_t unixtime(void) const;

Ti dà la data ed ora correnti nel formato Unix ossia il numero di secondi dal 1/1/1970.
Dividila per 86400 (il numero di secondi in un giorno) mettendola in una unsigned long, ed hai un valore intero pari al numero di giorni dal 1/1/1970 che memorizzi al primo evento.

Poi ogni giorno, analogamente a quanto si fa ad esempio con millis(), verifichi se la differenza tra il valore attuale e quello memorizzato è uguale a 2, il che significa che è ora di far partire quell'irrigazione, memorizzi il nuovo valore e prosegui.

Non so per le schedulazioni mensili tu come abbia fatto, ma anche qui se devi essere svincolato dalla classica programmazione settimanale puoi usare lo stesso metodo.

Ciao,
in effetti non volevo impelagarmi con gli array come dice uwefed, e non ho necessità di questa precisione come dice docdoc, posso fare anche programmazione settimanale, ma in testa non mi viene idea di come farlo.
Nello specifico userò 4 linee (elettrovalvole), 2 o 3 avranno apertura giornaliera, ma con durata differente, mentre una linea, che fa piante da frutto, dovrà aprire ogni 2 giorni (assolutamente non critico).
Utilizzando la funzione della RTCLib, mi sembra la soluzione migliore, molto veloce da farsi...
Proverò a buttare giù un codice queste sere :wink:
Grazie grazie

>thedrifter: in qualsiasi progetto dove devo "manipolare" il tempo ho sempre adottato lo unix time (lo trovi anche come Epoch) che trasforma tutto il tempo, nel numero di secondi passati a partire dal 01.01.1970 :slight_smile:

In questo modo, qualsiasi data/ora/minuti/secondi diventa semplicemente un "unsigned long" , ovvero un numero a 32 bit, che puoi usare per fare qualsiasi operazione ... sommare tempi, sottrarli, ecc. poi, la <time.h> che fa parte, come al solito di AVR libc (che ti rammento essere libreria automaticamente inclusa dall'IDE in ogni progetto che usa AVR), ti mette a disposizione tutte le funzioni per "giocare" con tale "tempi" :slight_smile:

Guglielmo

Ottimo, farò delle prove, vediamo cosa riesco a combinare :smiley:
Grazie mille

thedrifter:
Ciao,
non ho necessità di questa precisione come dice docdoc, posso fare anche programmazione settimanale, ma in testa non mi viene idea di come farlo.

Come fare cosa? Se hai una programmazione settimanale ti basterà stabilire in quali giorni attivare le elettrovalvole di ognuna delle tue 4 linee. Per fare questo puoi usare delle costanti (o sarebbe meglio memorizzarle su una SD, ma ne parleremo più avanti se sarà il caso) tipo:

// Programmazione settimanale delle zone non giornaliere
// I 7 valori rappresentano i giorni da Domenica a Sabato, 
// per ognuno 1=attiva 0=non attiva
// giorni dispari = Lun, Mer, Gio
const byte prog[7] = { 0, 1, 0, 1, 0, 1, 0 };

a quel punto leggi dall'RTC il giorno della settimana con la dayOfTheWeek() e con questa prendi l'indice dell'array per sapere se "oggi" tu debba annaffiare (qui ignoro il controllo dell'intervallo di orari ovviamente):

...
  int d = dayOfTheWeek();
  if ( prog[d] == 1 } {
    // Oggi devo annaffiare!
    // Verifico se è l'ora giusta...
    ...
  }
...

In effetti non pare difficile nemmeno così, non avevo pensato a questo modo, provo a buttare giù un programma.
Grazie

Ciao, rieccomi :slight_smile:
ho buttato giù un semplice programma, ma ho riscontrato subito problemi....
Tutto funziona se rimango nella stessa ora, ma se cambio ora, naturalmente mi si incasina il conteggio dei minuti, mi potreste suggerire un'idea? Non mi viene in mente nulla :frowning:
Posso anche rimanere nella stessa ora, non ho necessità di superare un'ora di irrigazione, era per capire la cosa.
Allego lo sketch
Grazie :slight_smile:

Irrigazione.ino (6.54 KB)

Generalmente dovresti cercare di spiegare meglio cosa intendi esattamente, magari con un esempio.

Comunque lo interpreto così: "se imposto ora1_on e min1_on alle 20:45 e ora1_off e min1_off alle 21:15 non attiva correttamente l'irrigazione". Ovvero questi parametri (tra l'altro metti insieme ore e minuti dello stesso evento che si legge meglio):

byte ora1_on = 20;         // ci salvo la programmaz oraria uscita 1
byte min1_on = 45;
byte ora1_off = 21;
byte min1_off = 15;

Quindi quando vai a controllare dato che fai:

      if (ora >= ora1_on && ora <= ora1_off)
      {
        if (minuti >= min1_on && minuti < min1_off)
        {
          digitalWrite(LINEA1,  HIGH);  // accendo linea 1 (piante grandi, non giornaliero)
        }
        else
        {
          digitalWrite(LINEA1,  LOW);   // spengo linea 1 (piante grandi, non giornaliero)
        }
      }
      else
      {
        digitalWrite(LINEA1,  LOW);     // spengo linea 1 (piante grandi, non giornaliero)
      }

di fatto è come scrivere:

      if (ora >= 20 && ora <= 21)
      {
        if (minuti >= 45 && minuti < 15)
        {
          digitalWrite(LINEA1,  HIGH);  // accendo linea 1 (piante grandi, non giornaliero)
        }
        else
...

ed ovviamente "minuti" non può mai essere maggiore di 45 e minore di 15.
Devi "accorpare" ore e minuti in un valore equivalente ai minuti totali, moltiplicando le ore per 60 e sommando questo ai minuti e confrontare questo con gli intervalli:

int orario(int h, int m) {
  return(h*60+m);
}

e quindi puoi fare una cosa del genere al posto di tutte quelle if():

      if ( orario(ora,minuti) >= orario(ora1_on, min1_on) 
              && orario(ora,minuti) < orario(ora1_off, min1_off))
        digitalWrite(LINEA1,  HIGH);  // accendo linea 1 (piante grandi, non giornaliero)
      else
        digitalWrite(LINEA1,  LOW);     // spengo linea 1 (piante grandi, non giornaliero)

Oppure usare una codifica numerica più semplice, moltiplicando le ore per 100 invece che 60 per cui puoi fare così semplificando anche la definizione degli orari:

...
int ora1_on = 2045;         // ci salvo la programmaz oraria uscita 1
int ora1_off = 2115;
...

e quindi memorizzando in una variabile l'ora corrente nello stesso formato (int adesso=ora*100+minuti;), puoi fare:

...
      if ( adesso >= ora1_on && adesso < ora1_off )
        digitalWrite(LINEA1,  HIGH);  // accendo linea 1 (piante grandi, non giornaliero)
      else
        digitalWrite(LINEA1,  LOW);     // spengo linea 1 (piante grandi, non giornaliero)
...

o in modo ancora più compatto:

...
      // linea 1 (piante grandi, non giornaliero)
      digitalWrite(LINEA1,  ( adesso >= ora1_on && adesso < ora1_off ));
...

A questo punto io trasformerei le variabili della programmazione in un array, per poi gestire tutte le zone con una for() invece di replicare lo stesso codice, arrivando infine a leggere la programmazione da una scheda SD così se vuoi cambiare gli orari non devi ricompilare e ricaricare tutto il codice...
Ma per ora non voglio incasinarti troppo la cosa, intanto fai funzionare questo codice, e se poi vorrai potremo anche vedere il resto :wink:

Ciao :smiley:
intanto complimenti, mi rendo conto di non sapere nulla sulla programmazione, ma il tempo ci fa crescere :wink:
Però allora, lo ritenevo complicato, ma è simile a quello che aveva detto Guglielmo, si potrebbe utilizzare i minuti di irrigazione, da salvare in una variabile nella programmazione, immettendo quindi ora di inizio e minuti di durata, all'ora programmata salvare il valore dell'unix time, poi verificare i minuti trascorsi; o sbaglio?
Comunque mi piace la versione compatta da te suggerita :smiley: questa:

digitalWrite(LINEA1,  ( adesso >= ora1_on && adesso < ora1_off ));

Non conoscevo questa sintassi, in pratica il digitalWrite viene messo a HIGH solo se la condizione fra parentesi è vera, e rimesso a LOW in caso sia falsa? La utilizzerò in altri miei programmi :smiley: Grazie.

Per quanto riguarda l'interfaccia utente, per ora farò così, impostando l'ora da programma, poi avevo intenzione di mettere un display in seriale, che con tutta l'interfaccia grafica, mi andrà semplicemente a cambiare le variabili int. Non mi interessa se in mancanza di alimentazione perderò la programmazione, in quel caso potrei usare la eprom del modulo RTC, ma vedremo :slight_smile:

Ora testo il tutto :slight_smile:
Grazie infinite

thedrifter:
Non conoscevo questa sintassi, in pratica il digitalWrite viene messo a HIGH solo se la condizione fra parentesi è vera, e rimesso a LOW in caso sia falsa?

Si, in realtà se l'espressione è falsa, il "false" è codificato come valore 0 che corrisponde a LOW, mentre qualsiasi valore diverso da zero è "true" che corrisponde a HIGH. Per cui l'espressione una volta valutata mi dà un risultato booleano che viene usato come parametro nella digitalWrite().
Per esempio, per invertire lo stato di un led messo sul pin 10, invece di fare:

if ( digitalRead(10) == HIGH ) {
  digitalWrite(10, LOW);
else
  digitalWrite(10, HIGH);
}

puoi anche scrivere:

digitalWrite(10, !digitalRead(10));

Non mi interessa se in mancanza di alimentazione perderò la programmazione, in quel caso potrei usare la eprom

Esattamente!