Tempo esecuzione loop

Buongiorno a tutti, vi spiego brevemente il mio progetto cosi potete aiutarmi meglio.
Voglio misurare la corrente che esce e entra nella batteria del mio impianto solare per poi calcolare gli Ah che caricano e scaricano la batteria. Per calcolare gli Ah mi serve la lettura del sensore e anche il tempo che scorre. Io con Arduino faccio diverse azioni come la lettura della porta seriale e l’attivazione di diversi relais quindi il tempo di esecuzione del loop non e mai uguale ed e questo il problema principale. Avendo una corrente che può variare nel tempo io pensavo di fare una lettura ogni secondo, solo che cosi facendo non so come tener conto della velocita delle operazioni che fa Arduino. La mia domanda e la seguente:
Come faccio a fare una misura ogni secondo indipendentemente dalle operazioni che faccio? Arduino può calcolare ogni volta quanto ci mette e fare un delay variabile?
Mi spiego meglio, se voglio una lettura ogni secondo e Arduino ci mette 300ms deve aspettare 700ms se invece, dato che deve fare altre operazioni ci mette 800ms a fare le operazioni deve aspettare solo 200ms e via cosi…

questo e il codice che ho realizzato per la lettura del seriale, ora dovrei implementare questo delay variabile e trovare un modo le leggere la velocita delle operazioni:

int Switch = 3;
int Schermo = 5;
int LuceEsterna = 7;
int Trasformatore = 8;
char Dati;
int StatoSeAcceso = 0;
char DatiSi = 'U';
int DelaySistema = 500;

void setup() {
  Serial.begin(9600);
  //-------------------------------
  pinMode(Trasformatore, OUTPUT);
  digitalWrite(Trasformatore, HIGH);
  //-------------------------------
  pinMode(LuceEsterna, OUTPUT);
  digitalWrite(LuceEsterna,HIGH);
  pinMode(Switch, OUTPUT);
  digitalWrite(Switch, HIGH);
  pinMode(Schermo,OUTPUT);
  digitalWrite(Schermo, HIGH);
}

void loop() { 
  if (Serial.available() > 0) {
    Dati = Serial.read();
    if(Dati == 'C'){
    Serial.print(DatiSi);
    }
    if(Dati == 'S'){//Acceso
    digitalWrite(Schermo, HIGH);
    DatiSi = 'Q';
    }
    if(Dati == 'R'){//Spento
    digitalWrite(Schermo, LOW);
    DatiSi = 'U';
    }
    //Accesa---------------------------- 
    if (Dati == 'N'){
      if(digitalRead(5)==HIGH){
      digitalWrite(Schermo, LOW);
      StatoSeAcceso == 1;
      }
    //----------------------------------
      digitalWrite(Trasformatore, LOW);
      delay(DelaySistema);
    //---------------------------------
      digitalWrite(Switch, LOW);
      if(StatoSeAcceso == 1){
      digitalWrite(Schermo, HIGH);
      StatoSeAcceso == 0; 
      }
    }
    //----------------------------------
    //Spenta----------------------------
    if (Dati == 'O') {//Spenta
      if(digitalRead(5)==HIGH){
      digitalWrite(Schermo, LOW);
      }
      digitalWrite(Switch, HIGH);
      delay(DelaySistema);
      //----------------------------------
      digitalWrite(Trasformatore, HIGH);
      //----------------------------------
      digitalWrite(Schermo, LOW); 
      DatiSi = 'U';
      StatoSeAcceso == 0;
    }
    //------------------------------------
    //LuceEsterna-------------------------
    if(Dati == 'A'){
      digitalWrite(LuceEsterna,LOW);
      delay(DelaySistema);
    }
    if(Dati == 'B'){
      digitalWrite(LuceEsterna,HIGH);
      delay(DelaySistema);
    }
}
}

Grazie mille a tutti :slight_smile:

puoi usare millis ()

void setup() {
  ...
}

void loop() {
  static unsigned long precedente = 0;

  ... // il programma

  if (millis() - precedente >= 1000) {  // 1000 ms = 1s
    // fare qualcosa ogni secondo
    ...
    precedente += 1000; // o precedente = millis();  (cf @fabpolli)
  }
}

ovviamente con ritardi di 500ms (DelaySistema) non è facile rispettare 1s…

Concordo con @J-M-L per l’uso di millis e per la necessità di rivedere sin da subito il modo di operare del codice, se per te è importante lo scorrere del tempo devi progettare il programma in modo tale che non vi siano punti bloccanti (Es. delay) ma che tutto sia sempre gestito in modo non bloccante con l’uso, ad esempio, di millis ovunque tu debba gestire un intervallo.
Non concordo invece su incrementare la variabile precedente di 1000 perché non è detto che si arrivi in quel punto ogni secondo preciso e in questo modo si va a sommare l’errore fino ad avere letture troppo ravvicinate, quando eseguo il controllo memorizzerei l’attuale valore di millis nella varibile in modo che il controllo successivo avvenga sempre dopo un secondo.
Ma anche questo sistema non è infallibile e presta il fianco a possibili errori di calcolo dovuti al non perfetto scorrere del tempo. Per essere certi che il controllo avvenga ogni secondo preciso per non sballare i calcoli occorrerebbe gestire la cosa tramite interrupt e con un segnale esterno che lo faccia scattare ogni secondo in modo preciso. Dentro l’interrupt poi va valutato se è possibile fare i calcoli necessari o se questo lo renderebbe troppo lento (l’ISR dell’interrupt DEVE essere il più veloce possibile) o se quello che mi serve per fare i calcoli non è disponibile dentro l’ISR di un interrupt.
In più nel codice ci sono vari errori nella valorizzazione di StatoSeAcceso che non va fatta con il doppio uguale che è un test e non un assegnazione

Tanto per parlè, ma anche in questo modo non ci sarebbe la certezza della precisione assoluta perché con un quarzo da 16Mhz non è scontato che sia possibile generare un interrupt preciso di 1000ms
Magari con un valore di prescaling del timer si ottiene 999ms e con quello subito successivo 1001ms (sono valori sparati a caso)
E occorre anche tenere in considerazione la tolleranza e la variazione della frequenza del quarzo in funzione della temperatura.

Certo che per l’applicazione in questione, non credo che la cosa poi faccia tutta 'sta gran differenza.

si è una scelta.
ti permette di recuperare un po 'di ritardo per la prossima volta e rimanere su un tempo di 1s

Altrimenti lo facciamo
precedente = millis (); // memorizza il momento presente

Si, certo infatti ho specificato con un segnale esterno che lo fa scattare. Concorto con te che la precisione assoluta non serva in questo progetto ma era per dare anche un’altra strada, anche un banale RTC compensato in temperatura potrebbe già essere più che accettabile con “generatore” d’interrupt.

1 Like

Potrebbe non essere necessario un tempo periodico preciso, se si calcola quanto è trascorso dal momento X moltiplicato per la corrente misurata si ottiene già l’integrazione valida per quel periodo. Cioè con un periodo più lungo risulteranno più Ah di quelli risultanti da un periodo breve, ma la sommatoria complessiva (l’integrale) viene corretta.

Grazie mille a tutti per le molteplici risposte, ho capito che come volevo fare io non è molto fattibile. Mi è venuta un’idea, magari non e fattibile, se compro uno di quei misuratori di tensione e corrente ad esempio questo.

Poi in qualche modo andare a leggere questi valori cosi da avere sistema esterno indipendente,
pensate sia possibile?

Si ma questo tempo devo misurarlo in qualche modo, come potrei fare?

Il problema è solo la prima lettura, che diciamo la scarti, semplicemente memorizzi il millis attuale.

Poi ad ogni lettura fai la differenza tra millis attuale e tempo salvato prima, e il millis attuale lo salvi nella variabile tempo per il prossimo giro.

Ah okay ho capito, cosi evito di usare gli interrupt poi se arriva una lettera nel seriale e deve fare qualcosa di specifico il tempo sarà maggiore o minore. Tanto per una misura del genere non mi serve una precisione altissima, già i misuratori che si comprano su aliexpress non sono precisissimi.
Provo a fare cosi, grazie mille a tutti