RTC DS3231 impostare allarmi

La semplicità (di uso) dei sistemi attuali è solo complessità abilmente nascosta (sessanta anni fa si riteneva che solo uno scienziato potesse usare un computer, Grace Hopper non era d'accordo, ma questa è storia).

Gli esempi di cui parli faranno uso di qualche libreria che "nasconde" e facilita, ma facilita (e questo è sempre il limite del nascondere la complessità tramite astrazioni) solo se la si usa esattamente con la logica con cui è stata pensata.

Detto ciò non so più se il dubbio attuale riguarda le date nell'RTC o i valori dell'encoder da limitare :confused:

dalla timelib.h

typedef struct  { 
  uint8_t Second; 
  uint8_t Minute; 
  uint8_t Hour; 
  uint8_t Wday;   // day of week, sunday is day 1
  uint8_t Day;
  uint8_t Month; 
  uint8_t Year;   // offset from 1970; 
} 	tmElements_t, TimeElements, *tmElementsPtr_t;


//convenience macros to convert to and from tm years 
#define  tmYearToCalendar(Y) ((Y) + 1970)  // full four digit year 
#define  CalendarYrToTm(Y)   ((Y) - 1970)
#define  tmYearToY2k(Y)      ((Y) - 30)    // offset is from 2000
#define  y2kYearToTm(Y)      ((Y) + 30)

Ah, ecco, 'tmYearToY2k' è una define.

Se non sbaglio i valori dovrebbero essere questi, non so che cosa scriva nell'RTC per valori antecedenti il 2000, immagino faccia una sottrazione in modulo 100 in modo da ottenere alla fine nel registro dell'RTC le unità e decine dell'anno (quindi 70 e 99 per le prime due righe).

|----------|-------------|--------------|
|   Anno   |   tm.Year   |     RTC      |
|          |  Anno-1970  |  tm.Year-30  |
|----------|-------------|--------------|
|   1970   |      0      |      ??      |
|   1999   |     29      |      ??      |
|----------|-------------|--------------|
|   2000   |     30      |       0      |
|   2018   |     48      |      18      |
|   2069   |     99      |      69      |
|----------|-------------|--------------|

E tutta questa complicazione solo per poter gestire date di quasi cinquant'anni fa (che per un acquario non servono :roll_eyes: ), e impedendo tra l'altro di superare il 2069?

E la domanda finale... la funzione 'year()' che cosa ritorna? Il valore dell'RTC? Il valore di tm.Year? L'anno da 1970 a 2069?

Claudio_FF:
Ah, ecco, 'tmYearToY2k' è una define.

...

E tutta questa complicazione solo per poter gestire date di quasi cinquant'anni fa (che per un acquario non servono :roll_eyes: ), e impedendo tra l'altro di superare il 2069?

E la domanda finale... la funzione 'year()' che cosa ritorna? Il valore dell'RTC? Il valore di tm.Year? L'anno da 1970 a 2069?

Sono assolutamente d'accordo con te infatti mi sta venendo la malsana idea di lasciarlo così com'è con il bug ed amen.
Non ricordo bene chi mi abbia consigliato o dove abbia letto che conveniva usare le due librerie per gestire il mio problema. Sono certo che la mia "ciabatta" non avrà lunga vita. Se verrà usata per un decennio o poco più sarà anche tanto.
Mi chiedo quindi come faccio a limitare la regolazione della data in questi 15/20 anni?

A me sembra strano che la libreria ti richieda quel calcolo per l'anno.

come più sopra , questo è nella timelib.h

uint8_t Year; // offset from 1970;

ziopippo:
idea di lasciarlo così com'è con il bug ed amen

Che significa essere reperibili H24 per l'amico quando un registro deciderà di fare un rollover... cosa che facilmente potrebbe capitare alla mezzanotte del 31 dicembre di uno qualsiasi dei prossimi anni :slight_smile:

Mi chiedo quindi come faccio a limitare la regolazione della data in questi 15/20 anni?

Io prima mi chiederei come scrivere e rileggere con assoluta certezza i dati, poi penserei alla modifica.

zoomx:
A me sembra strano che la libreria ti richieda quel calcolo per l'anno.

Beh no, probabilmente la libreria (pensata per un universo che inizia con il primo del 1970 e termina con il capodanno del 2069) prevede di essere usata in un certo modo, ad esempio con un anno assoluto a 16 bit che convertirebbe "di nascosto" nella rappresentazione interna a 8 bit e nell'ulteriore rappresentazione nel registro dell'RTC.

Certo che se ci si mette a modificare direttamente la rappresentazione interna bisogna sapere esattamente come funziona (ok, qui è un ±1970, ma potrebbe essere qualsiasi cosa).

Patrick_M:
come più sopra , questo è nella timelib.h

Allora, io trovo questa timelib.h, e questa time.cpp.

Vedo che la funzione 'year' restituisce un int con l'anno assoluto, quindi da 1970 a 2069.

Non mi spiego le righe che ziopippo dice portano al risultato corretto:

tmpYear = year() - 1999;
tm.Year = tmpYear - 2018;

...perché non sappiamo ne quale valore assuma 'tmpYear' per un anno conosciuto, ne cosa è stato fisicamente scritto (e quindi riletto) nell'RTC, e inoltre con quelle sottrazioni e assegnamenti forse ci sono di mezzo dei rollover e troncamenti di bit (tmpYear è 8 o 16 bit? con segno o senza?).

Cioè, in apparenza dovrebbe essere semplicissimo, nel programma lavoro sempre e solo con un anno int (range 1970..2069) e lascio fare conversioni/scritture/letture alla lib, in pratica non ho capito che problema ci sia (anche perché quella lib non la so usare :confused: ).

Year ritorna un anno int 1970..2069
Vedo che setTime accetta sempre un anno int 1970..2069:

void setTime(int hr,int min,int sec,int dy, int mnth, int yr){
 // year can be given as full four digit year or two digts (2010 or 10 for 2010);  
 //it is converted to years since 1970
  if( yr > 99)
      yr = yr - 1970;
  else
      yr += 30;  
  tm.Year = yr;
  ....

(EDIT: anche se mi sembra che nello spezzone di codice sopra ci sia un errore... 70+30 dovrebbe dare 0 e non 100)

Quindi non dovrebbe esserci alcun problema a lavorare sempre e solo con un anno int 1970..2069

Claudio_FF:
...
Non mi spiego le righe che ziopippo dice portano al risultato corretto:

tmpYear = year() - 1999;

tm.Year = tmpYear - 2018;



...perché non sappiamo ne quale valore assuma 'tmpYear' per un anno conosciuto, ne cosa è stato fisicamente scritto (e quindi riletto) nell'RTC, e inoltre con quelle sottrazioni e assegnamenti forse ci sono di mezzo dei rollover e troncamenti di bit (tmpYear è 8 o 16 bit? con segno o senza?).

Cioè, in apparenza dovrebbe essere semplicissimo, nel programma lavoro sempre e solo con un anno int (range 1970..2069) e lascio fare conversioni/scritture/letture alla lib, in pratica non ho capito che problema ci sia (anche perché quella lib non la so usare :confused: ).

Year ritorna un anno int 1970..2069
Vedo che setTime accetta sempre un anno int 1970..2069:


void setTime(int hr,int min,int sec,int dy, int mnth, int yr){
// year can be given as full four digit year or two digts (2010 or 10 for 2010); 
//it is converted to years since 1970
  if( yr > 99)
      yr = yr - 1970;
  else
      yr += 30; 
  tm.Year = yr;
  ....


(EDIT: anche se mi sembra che nello spezzone di codice sopra ci sia un errore... 70+30 dovrebbe dare 0 e non 100)

Quindi non dovrebbe esserci alcun problema a lavorare sempre e solo con un anno int 1970..2069

Questa è la dichiarazione delle variabili:

char const * wdays[] = {"Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"}; // definisco i giorni della settimana
uint8_t tmpHours, tmpMinutes, tmpDurata, tmpDay, tmpMonth, tmpYear;
int stato, scelta, sceltaTemp = 0; //vabialibi per il case switch
bool TermoDay = false;
tmElements_t tm;

Il motivo per cui ho dovuto fare quelle strane sottrazioni non me lo spiego neanche io, soprattutto quella relativa al 2018. Ho semplicemente provato ad impostare la data attuale, ho visto cosa mi restituiva con

void digitalClockDisplay()
{
  int wd = weekday();
  // digital clock display of the time
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.print(' ');
  Serial.print(wdays[wd - 1]);
  Serial.print(", ");
  Serial.print(day());
  Serial.print('/');
  Serial.print(month());
  Serial.print('/');
  Serial.print(year());
  Serial.println();
  delay(1000); // wait one second between clock display
}

ed ho fatto gli opportuni "aggiustamenti" fino a che non mi restituiva una data pari a quella inserita.
Se ai bisogno che faccia delle precise verifiche dimmi pure :wink:

Patrick_M:
come più sopra , questo è nella timelib.h

uint8_t Year; // offset from 1970;

Ho capito dove ho sbagliato. Sono andato a vedere gli sketch che usano l'RTC e ho trovato che io uso la libreria RTClib di Adafruit (si trova nel library manager oppure su GitHub) che, ho visto, è a volte richiamata anche negli esempi Timelib.
Ricapitolando uso la RTClib per gestire l'RTC, e la Timelib per effettuare i calcoli sulle date e la TimeZone per rendere automatico il passaggio tra orari estivi e invernali e Timezione usa Timelib.
Se usi il solo RTC e quando mostri l'orario lo chiedi all'RTC allora puoi usare la sola RTClib.

Sull'ESP8266 hanno incluso il tutto nelle librerie di sistema per cui non uso più nessuna di queste librerie.

La libreria Adafruit ha il vantaggio di avere molte istruzioni in comune pur utillizzando RTC diversi come il DS1307, il DS3231 e il PCF8523 ma anche un RTC puramente software. Però vedo che non gestisce il settaggio degli allarmi ma gestisce l'emissione dell'onda quadra del DS3231 e del PCF8523.

Beh guarda, io non alcun problema ad usare una libreria piuttosto che un'altra.
Il mio obbiettivo è solo quello di farlo funzionare e, se possibile, cercare di automatizzare il più possibile la cosa senza intervento dell'utente in modo da non aver poi rotture di scatole :wink:

Ho visto su GitHub che l'impostazione degli allarmi è una cosa sentita, c'è uno che ha proposto una modifica per gli allarmi ma su PCF8523.

Ci sono librerie apposta per il DS3231 che gestiscono tutto.

Il cambio delle librerie la potrei anche fare ma sicuramente non mi metto a reimpostare tutti gli allarmi ora che ho quasi finito (con molta fatica e tanto tempo dedicato). Mi restano solo un paio di funzioni di implementare ed un controllo sulle temperature per ultimare tutto il progetto. Se posso invece risolvere il problema delle date lo farei molto volentieri. :wink:

ziopippo:
Questa è la dichiarazione delle variabili:

uint8_t tmpYear;

Il motivo per cui ho dovuto fare quelle strane sottrazioni non me lo spiego neanche io, soprattutto quella relativa al 2018.

Ok, tutto chiaro, più o meno, c'è ancora un errore nascosto da qualche parte ma adesso è chiaro.

Partiamo dal principio.

La funzione 'year()' restituisce un valore a 16 bit compreso tra 1970 2069.

La cosa più semplice sarebbe quindi usare una 'tmpYear' da 16 bit (una banale variabile tipo int) per manipolare l'anno (regolandolo quindi con l'encoder dal 1970 al 2069), e poi sottrarre 1970 quando la si vuole copiare nella 'tm.Year', perché 'tm.Year' è un offset rispetto al valore 1970. Tutto questo in tabella 1, semplice, lineare, nessuna possibilità di errore:

TABELLA 1

                    -1970
   Year()    tmpYear      tm.Year
------------------------------------
   1970       1970         0
   1971       1971         1
    .         .            .
    .         .            .
   1998       1998         28
   1999       1999         29
------------------------------------
   2000       2000         30
   2001       2001         31
   2018       2018         48
    .         .            .
    .         .            .
   2068       2068         98
   2069       2069         99

MA...
Tu hai usato una 'tmpYear' a 8 bit che non può contenere un valore superiore a 255, quindi servono due operazioni invece di una. Prima sottraiamo 2000 al valore restituito da 'year()', in modo da lavorare in 'tmpYear' a 8 bit con un valore compreso tra 0 e 69 per gli anni 2000..2069, ma anche un valore negativo in complemento a due per gli anni dal 1970 al 1999.

E' un problema? No. Ci basta regolare l'anno in 'tmpYear' solo da 0 a 69 senza sconfinare nei valori negativi e tutto funziona. Quando dobbiamo aggiornare 'tm.Year' dobbiamo anche aggiungere 30 per riportare i valori come 'tm.Year' richiede. Il tutto in tabella 2, anche questo funziona senza errori:

TABELLA 2

        -2000         +30
   Year()    tmpYear      tm.Year
------------------------------------
   1970       226 (-30)    0
   1971       227 (-29)    1
    .         .            .
    .         .            .
   1998       254 (-2)     28
   1999       255 (-1)     29
------------------------------------
   2000       0            30
   2001       1            31
   2018       18           48
    .         .            .
    .         .            .
   2068       68           98
   2069       69           99

MA...
Invece tu hai fatto così per tentativi (tabella 3):

TABELLA 3

         -1999        -2018
    Year()    tmpYear      tm.Year
-------------------------------------
    1970       227 (-29)    1
    1971       228 (-28)    2
     .         .            .
     .         .            .
    1998       255 (-1)     29
    1999       0            30
-------------------------------------
    2000       1            31
    2001       2            32
    2018       19           49
     .         .            .
    2068       69           99
    2069       70           100

Iniziamo con il lolloso 2018... sottrarre 2018 da una variabile 8 bit senza segno è del tutto equivalente a sommare 30, in pratica se scrivevi +30 era la stessa cosa (il fatto che siamo nel 2018 è una pura coincidenza :slight_smile: )

Di differente dalla tabella 2 rimane solo quel 1999... che sfalsa di uno i valori degli anni, per cui va sicuramente a "compensare" un errore (probabilmente un decremento) presente da qualche altra parte.

Risolto quell'errore si può quindi tranquillamente usare l'attuale procedimento (ma sottraendo 2000 come in tabella 2) regolando 'tmpYear' da 0 a 69.

Il caso è chiuso :slight_smile:

Grazie mille per le tue "indagini". Sono rientrato da poco dal lavoro e non ho la lucidità per comprendere le tue finezze.
Domani spero di tornare prima a casa e di riuscire a capire meglio quello che hai scritto. Nel caso ti chiederò ulteriori spiegazioni. :wink:
GRAZIE!!!

Claudio_FF:
Ok, tutto chiaro, più o meno, c'è ancora un errore nascosto da qualche parte ma adesso è chiaro.

Partiamo dal principio.

La funzione 'year()' restituisce un valore a 16 bit compreso tra 1970 2069.

La cosa più semplice sarebbe quindi usare una 'tmpYear' da 16 bit (una banale variabile tipo int) per manipolare l'anno (regolandolo quindi con l'encoder dal 1970 al 2069), e poi sottrarre 1970 quando la si vuole copiare nella 'tm.Year', perché 'tm.Year' è un offset rispetto al valore 1970. Tutto questo in tabella 1, semplice, lineare, nessuna possibilità di errore:
...

Il caso è chiuso :slight_smile:

Dunque, caro il mio Sherlock Holmes, hai dimenticato di dire anche elementare Watson! :grin:
Grazie alla tua tenacia e capacità di aver saputo spiegare l'arcano anche ad un asino (come me!)
Ho optato per ridichiarare tmpYear come int ed ho corretto il codice in questo modo:

void SetYear() // Modifico l'anno
{
  if (LCDML_BUTTON_checkUp()) // incremento i minuti
  {
    tmpYear = tmpYear < 99 ? tmpYear + 1 : tmpYear;
    lcd.setCursor(x, y);
    lcd.print(tmpYear + 1970);
    LCDML_BUTTON_resetUp();
  }
  else if (LCDML_BUTTON_checkDown()) // decremento i minuti
  {
    tmpYear = tmpYear > 30 ? tmpYear - 1 : tmpYear;
    lcd.setCursor(x, y);
    lcd.print(tmpYear + 1970);
    LCDML_BUTTON_resetDown();
  }
  else if (LCDML_BUTTON_checkEnter()) // termino
    scelta = 3;
  LCDML_BUTTON_resetEnter();
}

void SaveDateTime() // Memorizzo Data ed ora
{
  tm.Hour = tmpHours;             //set the tm structure to 23h31m30s on 13Feb2009
  tm.Minute = tmpMinutes;
  tm.Second = 0;

  tm.Day = tmpDay;
  tm.Month = tmpMonth;
  tm.Year = tmpYear;

  SyncRTC(); // Sincronizzo il RTC per recuperare la data corretta
  RTC.write(tm);            //set the RTC from the tm structure
  Serial.println(F("MEMORIZZO ED ESCO"));
  SyncRTC(); // Sincronizzo il RTC per aggiornare ora e data sul display
}

e... tutto FUNZIONA QUASI ALLA PERFEZIONE!
L'unico "problema" che resta è quello di non fare il controllo sugli anni bisestili e comunque sui mesi con data inferiore a 31 giorni. Impostando ovviamente tipo il 31 aprile salva, ovviamente, come data il 01 maggio.
Controllo che detto tra noi non mi ero ancora posto.
Ho completato tutto il progetto come lo avevo pensato. Non sono riuscito ad inserire delle componenti grafiche sul display utilizzando i custom char perchè ne fa già uso la libreria LCDMenuLib e non capisco il motivo per cui mi bypassa le definizioni che vorrei aggiungere. Ho anche chiesto aiuto sulla sezione dedicata in tedesco (scrivendo in inglese) ma nessuna risposta in merito.
Purtroppo (e per fortuna, per altri fronti) il mio tempo, causa lavoro, da dedicare a questo progetto è quasi finito quindi, se qualcuno sa darmi una soluzione facile ed indolore per gestire il problema della data < a 31 la implemento atrimenti "amen".

Grazie ancora per la vostra infinita pazienza!!! :kissing:

Sono ovviamente onorato di condividere l'intero progetto se qualcuno ne fa richiesta...

ziopippo:
se qualcuno sa darmi una soluzione facile ed indolore per gestire il problema della data < a 31

Basta dire al tuo amico che se sbaglia ad inserire il giorno i pesci muoiono.
Problema risolto 8)

Claudio_FF:
Basta dire al tuo amico che se sbaglia ad inserire il giorno i pesci muoiono.
Problema risolto 8)

Ok ci sto. Però dico che sei stato tu a suggerirmelo. :grin: