Velocità Incremento Ciclo For o funzione Millis?

Salve a tutti

Ho una domanda: l’incremento di variabile in un ciclo for ogni quanto viene eseguita (o con che frequenza)?

Ho necessità di temporizzare una macchina a stati, della serie:

entra nello stato A
stacci per 3 ore
entra nello stato B
stacci per 5 ore
entra nello stato C
stacci per 1 ora
torna allo stato A

E ho necessità di usare un controllo sul tempo. Conviene usare la fantomatica funzione millis()?

Assolutamente si, usa millis() l'incremento di una variabile dipende dal codice. Ora magari ci mette 1 ms, aggiungi una istruzione e cambia. Quindi non ha senso. Avresti tempistica che dipende da codice.

Dipende da cosa fai dentro al for... Più roba fai, più l'incremento è lento. Se non facessi niente, a parte l'inutilità, credo potresti ottenere ~8 MHz, visto che il ciclo dovrebbe ridursi a 2 istruzioni assembler.

Comunque temporizzare cose in questo modo non è cosa molto furba, per una serie di motivi, dovuti principalmente al fatto che il codice che il compilatore genera non è strettamente predicibile. Figuriamoci poi quando il compilatore cambia, ecc...

Se vuoi scrivere codice strettamente legato al timing conviene farlo direttamente in assembler, ma ha senso solo se richiede precisione di microsecondi o più. Per precisioni inferiori millis() può andare benissimo.

Grazie mille delle risposte!

Domanda:

La funzione millis è inizializzabile in ogni stato o deve essere inizializzata nel loop prima degli stati? Ma sopratutto...comincia a contare all'accensione di Arduino o al caricamento in scheda del codice?

millis() si inizializza da sola (o meglio: ci pensa il core). Tu devi solo leggerla quando ti serve, conta da ogni accensione.

Solo un'appunto ...

KKMeph: ... entra nello stato A stacci per 3 ore entra nello stato B stacci per 5 ore entra nello stato C stacci per 1 ora torna allo stato A ...

Se le "ore" indicate sono approssimative o non dipendono da un preciso orario, puoi usare millis, se invece ti serve una certa precisione, meglio un'RTC, magari con DS3231 o DS3234, che sono piu precisi ed hanno gia un'oscillatore TCXO incorporato ...

Ciao,

Se le "ore" indicate sono approssimative o non dipendono da un preciso orario, puoi usare millis, se invece ti serve una certa precisione, meglio un'RTC, magari con DS3231 o DS3234, che sono piu precisi ed hanno gia un'oscillatore TCXO incorporato ...

forse è un po' esagerato. Scusa Etemenanki, non volermene, lo dico solo per capire se si può ottenere una soluzione abbastanza precisa senza ricorrere a circuiteria esterna all'Arduino.

Io ho sempre pensato che l'RTC fosse indicato per poter conoscere con una certa precisione la data e l'ora esatta. In altre parole se fosse necessario accendere qualcosa alle 10.30, per esempio.

Nel caso specifico invece, mi pare che si voglia soltanto conteggiare il tempo trascorso indipendentemente dall'orario in cui questo avviene. Non mi pare che la precisione possa essere inficiata da una somma di errori.

Sto pensando ad un banalissimo codice di questo tipo:

now = millis();
if (now - lastMillis > 1000)
{
   sec++;
   lastMillis = now;
}

if (sec == 3600)
{
   Serial.println("è passata un'ora!!!");
   sec = 0;
}

Io direi che eventuali cause di imprecisione possono portare al fatto che la nostra ora duri 3598 oppure 3601 secondi ma non che a lungo andare gli errori si sommino creando un sequenza di imprecisione crescente tipo: 3650, 3700, 3750,..., 4000.

In questo caso, se la precisione richiesta non è estrema, ritengo che si possa evitare l'uso dell'RTC. Inoltre, se si sperimenta che l'errore è costante, l'if potrebbe anche diventare sec==3598 (per esempio...).

Cosa ne pensate?

Ciao. Vittorio.

Quello che intendevo io e' che, se la precisione dei tempi deve essere la maggiore possibile, "al secondo", allora l'RTC serve, perche' l'oscillatore di Arduino non e' precisissimo ... mentre invece, se i tempi non e' necessario che siano assolutamente precisi, si puo tranquillamente usare il conteggio con millis, senza bisogno di usare un'RTC ;)

Allora, grazie a tutti per il supporto.

L'idea di base, ripeto è usare una macchina a stati, entrare nella macchina a stati e da li, passare da uno stato all'altro in base al tempo trascorso. Tuttavia risulta più complicato di quello che sembra.

Inoltre, vorrei utilizzare il monitor seriale in modo che ogni 5 secondi mi dia lo stato del programma...ma utilizzando millis il loop non finisce finche le variabili non vengono raggiunte giusto? Cioè, se io entro in uno stato che, per esempio, dura 4 ore...alla fine dello sketch dove di solito inserisco il print non c'arrivo mai giusto?

Dovrei definire una funzione di tipo "print" e alla fine di ogni stato dichiararla in modo che venga vista a seriale o sbaglio?

Cioè, se io entro in uno stato che, per esempio, dura 4 ore...alla fine dello sketch dove di solito inserisco il print non c'arrivo mai giusto?

no... in effetti no. L'idea di utilizzare la millis ed una macchina a stati finiti è proprio quella di permettere ad Arduino di fare più cose contemporaneamente. Tutto dipende da come scrivi il codice alll'interno della loop.

Parti innanzitutto dalla considerazione che tutto quello che scrivi nella loop, a meno che tu non usi funzioni bloccanti (tipo la delay, appunto), viene eseguito migliaia di volte al secondo, in base alla velocità del microcontrollore ed al tempo impiegato per eseguire il tuo codice.

Il seguente codice di esempio, fa (più o meno...) quello che vuoi fare tu. Naturalmente prendilo solo come uno spunto da cui partire...

int elapsedSecondsState;
unsigned long lastMillisStates;
unsigned long lastMillisSerial;

byte state;

void setup()
{
    Serial.begin(9600);

    elapsedSecondsState = 0;
    state = 0;
    lastMillisStates = millis();
    lastMillisSerial = millis();
}

void loop()
{
    // in questo punto, per esempio, ci passi migliaia di volte al secondo

    unsigned long now = millis();

    if (now - lastMillisSerial > 5000)
    {
        Serial.println("..."); // questa è la print seriale che vuoi fare ogni 5 secondi
        lastMillisSerial = now;
    }

    if (now - lastMillisStates > 1000) 
    {
        elapsedSecondsState++;
        lastMillisStates = now;
    }

    switch (state)
    {
        case 0: // stato A

            // qui fai quello che fare quando sei nello stato A

            if (elapsedSecondsState >= 10800) // 10800 secondi = 3 ore
            {
                //passi nello stato 1 = B
                state = 1;

                // ed azzeri il tempo trascorso per cominciare 
                // a contare quello dello stato successivo
                elapsedSecondsState = 0;    
            }

            break;

        case 1: // stato B

            // qui fai quello che fare quando sei nello stato B

            if (elapsedSecondsState >= 18000) // 18000 secondi = 5 ore
            {
                //passi nello stato 2 = C
                state = 2;

                // ed azzeri il tempo trascorso per cominciare 
                // a contare quello dello stato successivo
                elapsedSecondsState = 0;    
            }

            break;

        case 2: // stato C

            // qui fai quello che fare quando sei nello stato C

            if (elapsedSecondsState >= 3600) // 3600 secondi = 1 ora
            {
                //torni nello stato 0 = A
                state = 0;

                // ed azzeri il tempo trascorso per cominciare 
                // a contare quello dello stato successivo
                elapsedSecondsState = 0;    
            }

            break;
    }
}

Il codice potrebbe essere scritto in maniera più elegante (e consumando meno memoria Flash) usando un array che memorizza il numero di secondi da trascorrere in ciascuno stato. Di questo se vuoi ne riparliamo in seguito se ti è chiaro questo primo esempio.

Ciao. Vittorio.

Grazie Mille!