If-Switch-Array-Millis-For

grazie, delle risposte,
sto' lavorando sulla creazione della data, giorno mese anno

mi potete tradurre in italiano questo comando:

long minutes  = time / MINS;    time -= minutes  * MINS;

nella variabile time ci sono i secondi, ma il resto e' una lingua che non parlo :slight_smile:

credevo fosse stato facile aggiungere la data ad un orologio :slight_smile:

ho iniziato a fare questo discorso:
1 ora = 60min
1 giorno = 24 ore
1 mese = ????????

se i mesi erano tutti di 30 giorni sarebbe stato facile, come si risolve ?

c'e' una libreria per la gestione della data ? tipo calendario ?

Intanto mettila così:

long minutes  = time / MINS;
time = time - (minutes  * MINS);

Dichiara minutes di tipo long che contiene i minuti come risultato di time/MINS
Poi da time sottrae il valore di minutes*MINS, che equivale a lasciare in time la parte decimale.

Cioè, mi spiego:
siccome minutes è un intero di tipo lungo, conterrà solo la parte intera della divisione.
Dopodiché da time verrà levata la parte intera, per lasciare solo i decimali. Come? Togliendo minutes*MINS.
E' una forma di conversione dalle date o dagli orari in formato decimale verso il formato temporale oo:mm:ss

ora e' molto piu' chiaro :slight_smile:

per il resto che fo ? mi compro un RTC ? non ci sono librerie che fungono da calendario ?

Testato:
credevo fosse stato facile aggiungere la data ad un orologio :slight_smile:

ho iniziato a fare questo discorso:
1 ora = 60min
1 giorno = 24 ore
1 mese = ????????

se i mesi erano tutti di 30 giorni sarebbe stato facile, come si risolve ?

c'e' una libreria per la gestione della data ? tipo calendario ?

Semplicemente ti fai un array con i giorni dei mesi:
mesi={31,28,31... ecc..}
Per i bisestili, li calcoli semplicemente ed aggiungi 1 giorno a febbraio nel caso di anno bisestile.

boolean bistestile(int anno) {
  if (((anno % 4) ==0) && ((anno % 100) != 0) or ((anno % 400) ==0)) {
    return true;
  } else {
    return false;
  }
}

La differenza tra IF e Switch case é anche che nelle IF puoi controllare piú condizioni e concatenarle in modo AND oppure OR.
Percui puoi usare Switch-case solo in particolari casi e non al posto del IF.
Ciao Uwe

quante cose da imparare :slight_smile:

il mio bell'orologio usa un array per passare avanti l'orario, io ne ho aggiunto un altro per la data, funziona, bravo mi sono detto.
prima di affrontare la questione dei mesi usando la strada spianata da Leo volevo creare l'IF per mandare a video 3 sec l'orario e 3 sec la data.

Debbo usare il blink without delay mi sono detto, lo modifico ed all'aspetto sembra gradevole.
Col cavolo che funziona,
Come si fa la copia di un array ?

  // IF senza delay per alternare Ora/Data
   
   if (millis() - previousMillis > interval) 
   {
    previousMillis = millis();
    
      if (alterna == ClockArray)
      alterna = DateArray;
    else
      alterna = ClockArray;
      
     // Display.
    DisplayNumberString( alterna );
    }

Gli array non puoi copiarli assegnando un array ad un altro array (credo non esistano metodi). Devi copiare i suoi elementi nell'array contenitore.

non funziona ancora

Vado a mangiare, magari a pancia piena penso meglio :slight_smile:

// IF senza delay per alternare Ora/Data
  int AlternaArray[6]={0,0,0,0,0,0};
  
   if (millis() - previousMillis > interval) 
   {
    previousMillis = millis();
    
      if (AlternaArray == ClockArray)
       {
       AlternaArray[6];
  DateArray[0] = upperDays;
  DateArray[1] = lowerDays;
  DateArray[2] = upperMonths;
  DateArray[3] = lowerMonths;
  DateArray[4] = upperYears;
  DateArray[5] = lowerYears;
  }
    else
     {
     AlternaArray[6];
       ClockArray[0] = upperHours;
  ClockArray[1] = lowerHours;
  ClockArray[2] = upperMins;
  ClockArray[3] = lowerMins;
  ClockArray[4] = upperSeconds;
  ClockArray[5] = lowerSeconds;
  
  
     // Display.
    DisplayNumberString( AlternaArray );
   }
}
}

allora, ci sono un pò di errori di base.
Se usi il nome di un array SENZA graffe, non stai utilizzando o confrontando TUTTI i suoi valori, ma l'indirizzo di memoria in cui è posizionato l'array (leggiti qualcosa sui puntatori in caso)

questo, a differenza di una copia, fa si che se ad uno dei due array vengono modificati dei valori, anche l'altro array avrà i valori modificati, perchè in realtà sono due "etichette" agli stessi dati, ovvero un'insieme di bit nella ram :slight_smile:

quindi dire:

 if (alterna == ClockArray)

equivale a dire: l'array alterna, ha (o meglio punta) lo stesso indirizzo di memoria dell'array ClockArray? sintatticamente è corretto da fare

alterna = DateArray;

vuol dire, ora alterna dovrebbe puntare all'indirizzo della prima cella di DateArray, e anche questo è corretto (ricorda che modificando alterna, anche i valori contenuti in DateArray risulteranno modificati e viceversa)

Quindi cosa c'è di sbagliato?
molto probabilmente hai dichiarato alterna come un array. gli array sono dei puntatori alla prima cella di memoria di una serie di celle di memoria nella ram (l'array dal punto di vista fisico, appunto). Cambiare questo indirizzo senza prima lanciare una free() su quella zona di memoria vuol dire che finchè il programma è in esecuzione o non termina la visibilità della variabile (savo malloc manuale ma è un'altra storia), quell'aria di memoria viene considerata "in uso" e non può essere riassegnata, è un errore di programmazione. La zona di memoria si dice garbage, e quando il garbage riempie tutta la ram sia il programma, che il sistema operativo possono crashare.
Dunque per evitare problemi, gli array hanno l'indirizzo puntato costante, se non erro. Quindi dobbiamo usare un puntatore.
alterna sarà un puntatore alla prima cella dell'array:

int *alterna;

l'if e l'assegnazione rimangono uguali, persino il display:

if (alterna == ClockArray)
      alterna = DateArray;
    else
      alterna = ClockArray;
// Display.
    DisplayNumberString( alterna );

come puoi vedere un puntatore può essere tranquillamente sostituito ad un array, l'unica differenza è che l'array si prende da solo una zona memoria, mentre col puntatore devi allocarla manualmente con una malloc(), ma in questo caso non serve perchè usi delle aree di memoria già allicate dai 2 array

spero sia chiaro e che la storia sull'indirizzo dell'array costante sia corretta, perchè non sono affatto sicuro :slight_smile:

tutto qui ?
mi mancava un asterisco ? :slight_smile:

grazie, avevo notato in altri codici quell'asterisco, credevo avesse il significato di carattere jolly :slight_smile:

funziona alla grande, il codice finale e' questo, lo posto per chi si imbattesse in questa discussione:

  // serve per cambio Ora/Data
  long previousMillis = 0;
  long interval = 3000; 
  int *alterna;
  // IF senza delay per alternare Ora/Data
   
   if (millis() - previousMillis > interval) 
   {
    previousMillis = millis();
    
      if (alterna == ClockArray)
      alterna = DateArray;
    else
      alterna = ClockArray;
      
     // Display.
    DisplayNumberString( alterna );
    }

ora devo affrontare la questone dellla gestione del mese, nel frattempo vi chiedo, il problema di overflow del millis, oltre a tutte le funzioni dell'orologio sara' presente anche su questa previousmillis che uso per lo switch, che succede dopo 2 mesi non switchera' piu' tra data e ora ?

Certamente.

ma allora mi chiedo, se millis e' una funzione, ed anche importante e molto usata, perche' non viene aggiornata in modo da gestire automaticamente questo problema ?

Chiedilo agli sviluppatori di Arduino :stuck_out_tongue:

guardate che funziona lo stesso, credo :slight_smile:

millis() - previousMillis > interval

dove interval è una costante, ricordate che sia millis() che previousMillis sono unsigned, quindi NON assumono valori negativi.

lavoriamo con valori da 0 a 63 (6bit), con 64 che è l'overflow, quindi a 64 si ritorna a 0

se previusMillis vale 50, il timer si resetta(a 0), abbiamo:
0 - 50, ovvero -50? no! non ci sono numeri negativi! lavoriamo a bit

000000 -
110010 =
001110 ovvero 14... e guarda caso, è proprio il tempo trascorso tra 50 e 0... 50+14=64, ovvero overflow e si riparte da 0

quindi l'overflow non è gestito da arduino perchè fondamentalmete non necessario. Certo, se parti da 50, si resetta, e ritorni a 50 il tempo trascorso è pari a 0. Quindi in realtà questa cosa ti permette di avere SEMPRE un valore di massimo tempo pari alla grandezza della variabile (pensa sewnza questo trucco cosa succede a 2 secondi dall'overflow... al massimo hai 2 secondi di autonomia!)

Puoi usare un trucco per andare oltre: ogni volta che millis si riazzera, aumenti una variabile. A questo punto il massimo valore memorizzabile è (valori unsigned long)*(valori variabile). con un int mi sa che arrivi tranquillamente fino a fine del sole :slight_smile:
ovviamente quando salvi un tempo, non prendi più solo la millis, ma ti salvi anche il numero di reset attuali. A questo punto si complica un poco la matematica: al valore 14 che prima abbiamo trovato, devi aggiungere il valore massimo dell'overflow moltiplicato per il numero di reset di differenza -1. Dato che è un valore non memorizzabile in una variabile standard, è un poco difficile da computare ma credo tu abbia capito cosa intendo, e spero non ti debba mai inbattere in calcoli del genere (che poi in realtà noi tra giorni, mesi e anni bisestili complichiamo la storia mooooolto di più) :slight_smile:

quindi se ho capito potremmo riassumere cosi',

se con millis mi faccio un orologio e quindi la uso per rimpilzare la variabile SECONDI il problema esiste, perche' quando SECONDI ripartira' da Zero l'orario sara' da correggere

mentre se uso millis per altre cose, ad esempio far lampeggiare un led, o nel mio caso switchare diversi dati perso un display non ci interessa l'azzeramento, perche' non ci interessa il valore in se' di millis.

L'unico rpoblema che vedo e' che ad esempio se io voglio far blincare il led 3sec on e 3sec off, se capita che l'overflow accade quando manca un secondo dallo spegnimento, ripartendo da zero sbagliera' i twempi del blink, ma sbagliera' solo la prima volta.

cioe' ogni due mese (unsigned long) capitera' che per un istante il led sbaglia il tempo di blincaggio, ma poi sara' regolare per altri due mesi ?

no, con un overflow della variabile tutta la matematica torna perfettamente e non sbaglia mai, a meno che non misuri tempi superiori al tempo di overflow(due mesi), allora devi usare il metodo della variabile esterna che conta gli overflow.

Se conti i secondi invece sbagli, perchè non esiste l'overflow a 60 in binario, al massimo ti avvicini usando 6bit che da 64, ma dato che la alu del atmega pensa in 8bit, sei comunque in una simulazione, quindi tanto vale gestire il tutto con if eccetera. (per le variabili che sono multiple del byte la matematica è più semplice, perchè lunica cosa di cui ti devi preoccupare è il riporto, la matematica avviene via hw spezzando la variabile nei byte)

sei insostituibile :slight_smile:

permettimi di ricapitolare il ricapitolo:

1- il problema dell'overflow di millis, seppur esistente, non influisce sull'uso che si fa della tecnica BlinkWithoutDelay, i led blinkeranno all'infinito senza sbagliare mai

2- non ha senso scriversi il codice di un orologio perche' ci si imbatte in:

  • questo problema di millis(),
  • nel rpoblema della gestione dei mesi,
  • nel problema della batteria di backup,
  • e non so se ce ne siano altri.

Quindi un bel RTC i2c e via

Testato:
2- non ha senso scriversi il codice di un orologio perche' ci si imbatte in:

  • questo problema di millis(),
  • nel rpoblema della gestione dei mesi,
  • nel problema della batteria di backup,
  • e non so se ce ne siano altri.

il problema millis può essere aggirato facilmente. per esempio gli RTC NON memorizzano data e ora, ma il numero di secondi trascordi dal 1/1/1900, il faomoso timestamp (da non confondere con lo unix time del 1970 e che non è lineare).
l'overflow di un unsigned long, usato per i secondi anzichè per i millisecondi vuol dire che ha un ordine di magnitudo in più:

un unsigned long conserva come valore massimo 2^32 = 4.2949673 * 10^9, ovvero 4.294.967.300, se sono millisecondi, si resettano ogni 4294967.3 secondi, ovvero 71582.788 minuti, ovvero 1193.0465 ore, ovvero 49.710271 giorni (circa un mese e mezzo)

se invece sono secondi... 71582788 minuti, o 1193046.5 ore, o 49710.271 giorni, circa 136 anni.....

a partire dai secondi, e con un paio di tabelle in memoria (la durata in giorni dei mesi secondo il calendario gregoriano, e un valore di riferimento che va da 0 a 3 per gli anni bisestili) è una baggianata estrarre giorno ora e data, si tratta di divisioni e resto delle stesse.

La batteria di backup è un problema anche per gli RTC, più che altro che ogni volta che riprogrammi l'atmega devi reinviare anche l'ora perchè cancelli la sua memoria.

PERCHÈ quindi NON si può fare?
questo me l'ha detto uwe le prime volte che bazzicavo nel forum, stupito dal fatto di usare un RTC quando si poteva benissimo fare una cosa simile con 2 calcoli, o al massimo per i più svogliati una libreria.

Il problema è che i quarzi o gli oscillatori che usiamo sono troppo poco precisi! Esistono ovviamente dei clock, ma se non ho capito bene sono intorno ai 30kHz, e non so se si possano settare i fuse o leggere il clock con degli interrupt, teoricamente è meno sbattimento usare un RTC esterno.

edit: avevo sbagliato il calcolo dei secondi conteggiando due volte le ore