Buongiorno, sono un novizio di arduino, ogni tanto scrivo qualceh piccolo programma per imparare ma questa volta ho preso un progetto già fatto su Thinkercad e leggermente modificatoper le mie esigenze.
Il progetto è un cronometro con funzione di loop e salvataggio precedenti valori, il codice funziona, ma mi sono accorto tradi che la precisione è molto distante dalle aspettative.
Sapevo che usando MILLIS non sarebbe stato preciso, ma qui si parla di circa un errore di metà del valore misurato (4:00 min del cronometro del cellulare il mio con arduino segna 2:22)
Non capisco se è un errore di codice o cosa, mi sembra veramente tanto come discrepanza.
Allego il codice, qualsiasi aiuto è ben acceto:
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Imposta il tuo indirizzo I2C (modifica se necessario)
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Pulsanti
const int start = 8;
const int pausa = 9;
const int partial = 10;
const int scroll_partial = 11;
// Stato
int x = 0;
int lcdclear = 0;
int Display = 0;
// Tempo
int cents = 0, seconds = 0, minutes = 0, hours = 0;
const int interval = 10;
unsigned long previousMillis = 0;
int c1, c2, s1, s2, m1, m2, h;
// Partials
int partial2[7];
int partial3[7];
int partial4[7];
int scrolling = 0;
bool lastPartialState = HIGH; // Stato precedente del tasto PARTIAL
void setup() {
pinMode(start, INPUT_PULLUP);
pinMode(pausa, INPUT_PULLUP);
pinMode(partial, INPUT_PULLUP);
pinMode(scroll_partial, INPUT_PULLUP);
lcd.init();
lcd.backlight(); // ⚠️ importante
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Press start");
}
void loop() {
if (x == 0) {
while (digitalRead(start) == HIGH) {};
x++;
}
if (lcdclear == 0) {
lcd.clear();
lcdclear++;
}
if (Display == 0) {
lcd.home();
lcd.print("Now: ");
Display++;
scrolling = 0;
}
chronometer();
pause();
f_partial();
}
void chronometer(void) {
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
cents++;
if (cents == 100) {
cents = 0;
seconds++;
if (seconds == 60) {
seconds = 0;
minutes++;
if (minutes == 60) {
minutes = 0;
hours++;
if (hours == 24) hours = 0;
}
}
}
int cent = cents;
int sec = seconds;
int minu = minutes;
h = hours;
c1 = cent % 10;
cent /= 10;
c2 = cent % 10;
s1 = sec % 10;
sec /= 10;
s2 = sec % 10;
m1 = minu % 10;
minu /= 10;
m2 = minu % 10;
lcd.setCursor(6, 0);
lcd.print(h);
lcd.print(':');
lcd.print(m2);
lcd.print(m1);
lcd.print(':');
lcd.print(s2);
lcd.print(s1);
lcd.print(':');
lcd.print(c2);
lcd.print(c1);
}
}
void pause(void) {
if (digitalRead(pausa) == HIGH)
return;
else if (digitalRead(pausa) == LOW) {
while (digitalRead(start) == HIGH) {
if (digitalRead(scroll_partial) == LOW)
scrollPartial();
}
}
}
void scrollPartial(void) {
while (digitalRead(scroll_partial) == LOW) {};
if (scrolling == 0) {
lcd.clear();
lcd.home();
lcd.print("PAR1:");
lcd.setCursor(6, 0);
lcd.print(h);
lcd.print(':');
lcd.print(m2);
lcd.print(m1);
lcd.print(':');
lcd.print(s2);
lcd.print(s1);
lcd.print(':');
lcd.print(c2);
lcd.print(c1);
lcd.setCursor(0, 1);
lcd.print("PAR2:");
lcd.setCursor(6, 1);
for (int i = 0; i < 7; i++) {
if (i == 1 || i == 3 || i == 5) lcd.print(':');
lcd.print(partial2[i]);
}
Display = 0;
lcdclear = 0;
cents = seconds = minutes = hours = 0;
scrolling++;
}
else if (scrolling == 1) {
lcd.clear();
lcd.home();
lcd.print("PAR3:");
lcd.setCursor(6, 0);
for (int i = 0; i < 7; i++) {
if (i == 1 || i == 3 || i == 5) lcd.print(':');
lcd.print(partial3[i]);
}
lcd.setCursor(0, 1);
lcd.print("PAR4:");
lcd.setCursor(6, 1);
for (int i = 0; i < 7; i++) {
if (i == 1 || i == 3 || i == 5) lcd.print(':');
lcd.print(partial4[i]);
}
Display = 0;
lcdclear = 0;
cents = seconds = minutes = hours = 0;
scrolling = 0;
}
}
void f_partial(void) {
bool currentState = digitalRead(partial);
if (lastPartialState == HIGH && currentState == LOW) {
// Fronte di discesa rilevato (pressione del tasto)
lcd.clear();
lcd.setCursor(0, 1);
lcd.print("Old: ");
lcd.setCursor(6, 1);
lcd.print(h);
lcd.print(':');
lcd.print(m2);
lcd.print(m1);
lcd.print(':');
lcd.print(s2);
lcd.print(s1);
lcd.print(':');
lcd.print(c2);
lcd.print(c1);
Display = 0;
cents = 0;
seconds = 0;
minutes = 0;
hours = 0;
// Shift dei partial salvati
for (int i = 0; i < 7; i++) {
partial4[i] = partial3[i];
partial3[i] = partial2[i];
}
partial2[0] = h;
partial2[1] = m2;
partial2[2] = m1;
partial2[3] = s2;
partial2[4] = s1;
partial2[5] = c2;
partial2[6] = c1;
}
lastPartialState = currentState; // Aggiorna lo stato precedente
}
Sicuramente millis() non è precisissimo ma non ha un errore così grande.
Analizzando grossolanamente il codice mi pare che si basi su un timer, che dovrebbe partire ogni 10 millisecondi (quindi 1 centesimo di secondo) e con questo incrementare un contatore di centesimi e, a cascata, secondi, minuti ed ore.
Questo in teoria sarebbe corretto, ma dipende strettamente dal fatto che quel timer debba poter scattare realmente 100 volte al secondo, ma se il codice "fa altro" rischia di andare ben oltre e quindi scattare molte meno volte del previsto.
Per dire le prime cose, bisogna evitare cicli do/while e delay() nel resto del codice è essenziale. E di "while" ne vedo vari in giro (fortunatamente non i "delay()"), anche se, come dicevo, non ho analizzato l'intero codice.
Qualche nota formalmente secondaria ma che potrebbe causare malfunzionamenti. Non capisco perché le variabili come "Display" e "lcdclear" tu le vada ad incrementare quando poi il confronto è sempre se siano a zero o meno. Trasformale in bool ed impostale a true o false quando serve, perché se sono delle "int" (inbtero con segno) e le incrementi continuamente, una volta che arrivano a 32767 e le incrementi diventa -32768 e prosegue da lì, e dopo 65535 cicli tornano a zero!
Per non parlare della variabile "x", il cui scopo non mi è affatto chiaro (ed anche questo viene incrementato senza limiti, e senza che venga mai reimpostato)...
Buongiorno e benvenuto nella sezione Italiana del forum,
cortesemente, come prima cosa, leggi attentamente il REGOLAMENTOdi detta sezione, (... e, per evitare future possibili discussioni/incomprensioni, prestando molta attenzione al punto 15), dopo di che, come da suddetto regolamento (punto 16.7), fai la tua presentazioneNELL'APPOSITA DISCUSSIONE (... quello che vedi in blu è un link, fai click su di esso per raggiungere la discussione) spiegando bene quali esperienze hai in elettronica e programmazione, affinché noi possiamo conoscere la tua esperienza ed esprimerci con termini adeguati.
Grazie,
Guglielmo
P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposita discussione, nel rispetto del succitato regolamento nessuno ti risponderà (eventuali risposte o tuoi ulteriori post, verrebbero temporaneamente nascosti), quindi ti consiglio di farla al più presto.
Come prima cosa, se vuoi un conteggio del tempo abbastanza preciso dovresti usare un timer hardware (il come dipende dal microcontrollore che stai usando) così da svincolarti dal resto del codice.
In seconda battuta, secondo me quando si parla di tempi e calcolatori elettronici è necessario abbandonare il sistema sessagesimale e ragionare in termini di ore/minuti/secondi/centesimi esclusivamente quando si tratta di "stampare" il dato finale su un diplay, a terminale etc etc.
Se ragioni in questo modo, la tua applicazione si semplifica drammaticamente:
un timer che incrementa una variabile ogni 10 millisecondi;
quando inizia l'evento da misurare salvi il valore del conteggio in una variabile;
quando finisce l'evento da misurare fai la differenza tra il conteggio attuale ed il valore salvato;
solo a questo punto trasformi il numero di "tick" ottenuto in un formato di tempo "human readable" hh:mm:ss.ccc.
Per fare prove va bene millis() o micros(), ma pensa seriamente di usare un timer hardware;
Sì. Nel mio, infatti, con un timer ogni 10ms incremento o decremento (a seconda che il conteggio sia in avanti o indietro) la variabile t100 (centesimi di secondo). Per il calcolo di ore, minuti, secondi e la visualizzazione impiega quasi 2,2ms.
Naturalmente, se vuoi un po' di precisione, devi usare un quarzo, non il risuonatore ceramico di Arduino Uno e simili. Con un po' di manualità con il saldatore, puoi sostituire il risuonatore ceramico con un quarzo.
Grazie a tutti, purtroppo il mio attuale livello di programazione non mi permette dis crivere un codice così da zero, e quindi speravo di trovare un progettino già fatto adattabile con poche modifiche.
Qui sono incappato subito nel mio "reato di presunzione" partendo di fatto da un codice già non ottimizzato.
Vediamo se riesco, ripatendo da zero, ad arrivare a qualcosa di usabile.
Per il momento vorrei rimanere con l'HW attuale (arduino UNO) e provare a lavorare con millis, e i suoi conosciuti limiti
Il valore ritornato dalla funzione millis sbaglia di circa cinque secondi all'ora, vuol dire un terzo di secondo in quattro minuti. Quando il tempo contato con millis si discosta macroscopicamente da questo valore, significa che si sta misurando il tempo in modo errato.
In questo caso, non solo ogni elaborazione completa media di loop deve durare meno di 10 ms, ma il calcolo del tempo:
if (currentMillis - previousMillis >= interval) {
previousMillis = currentMillis;
non tiene conto di quanto al momento del controllo si può aver già sforato i 10 ms previsti (il programma "crede" siano passati solo 10 ms, mentre in realtà possono esserne passati molti di più).
Sicuramente l'aggiornamento della variabile temporale va fatto così:
if (currentMillis - previousMillis >= interval) {
previousMillis += interval;
Ma rimane il fatto che la durata media del loop deve essere inferiore ai 10 ms.
Oltre al progetto funzionante di @Datman che usa lo stesso principio, dai un'occhiata anche a questo sketch di test dove ho cercato di rappresentare "il nocciolo" dell'algoritmo proposto (incremento contatore e conversione in tempo).
Esempio che usa micros() per incrementare il contatore:
Esempio che usa il Timer1 di un Arduino Uno per il contatore:
Volendo si potrebbe usare una Leonardo, od una Micro, la scheda, non la funzione!
Se ben ricordo loro hanno il quarzo, dato che devono gestire in modo nativo la comunicazione tramite USB, notoriamente schizzinosa con le tempistiche.