priorità di calcolo nel codice...

Buongiorno a tutti,
utilizzo arduino per “leggere” gli impulsi generati da un contatore di consumi: 1 impulso = 1W.
Ovviamente in caso di 3600 impulsi nell’arco di un’ora il mio consumo è di 3600Wh.
Per calcolare il consumo medio (non è istantaneo) conto quanti millisecondi ci sono tra due impulsi, divido per 3600 e moltiplico per 1000 (va beh… divido per 3.6 :slight_smile: ).
Tutto funziona senza problemi.
Ieri ho voluto introdurre un paio di sensori DS18B20 che a regime dovrebbero diventare almeno 6 sensori.
Ho quindi aggiunto la parte di codice ed il tutto funziona, ma… ma vedo delle incongruenze con i valori di consumo riscontrati.
Credo che il problema sia dovuto dal tempo impiegato dai DS18B20 per la misura che a 12bit dovrebbe se non erro essere di 750ms.

Qua sotto l’estratto dello sketch:

///////ONE WIRE////START

#include <OneWire.h>
#include <DallasTemperature.h>
#define ONE_WIRE_BUS_PIN 15
OneWire oneWire(ONE_WIRE_BUS_PIN);
DallasTemperature sensors(&oneWire);
DeviceAddress CAMERA = { 0x28, 0x0C, 0x1D, 0xBE, 0x05, 0x00, 0x00, 0x42 };
DeviceAddress SALA   = { 0x28, 0x0E, 0x8A, 0xBD, 0x05, 0x00, 0x00, 0x42 };
unsigned long tempoPrecedenteONEWIRE = millis();
float TEMP_CAMERA = 0;
float TEMP_SALA = 0;

///////ONE WIRE////END


///////PULSE CONSUMI////START

// Pin pulse
const int getpulsepin = 2;
// variable
int power = 0;
unsigned long startime = 0;
unsigned long duration = 0;
int state = 0;
int pinval = 0;
unsigned long tempoPrecedente = 0;
unsigned long tempoAllerta = millis();
float duration3 = 0;

///////PULSE CONSUMI////END


**********************************************************


void setup() {


  ///////ONE WIRE////START

  //Inizializzo i sensori
  sensors.begin();
  // setto la risoluzione a 12 bit (da 9 to 12 bits .. lower is faster)
  sensors.setResolution(CAMERA, 12);
  sensors.setResolution(SALA, 12);

  ///////ONE WIRE////END



  ///////PULSE CONSUMI////START

  pinMode(getpulsepin, INPUT); // pin del sensore
  digitalWrite (getpulsepin, HIGH);//abilito resistenza pull up

  ///////PULSE CONSUMI////END






}




void loop() {


  ///////ONE WIRE////START

  if (millis() - tempoPrecedenteONEWIRE > 2000) {
    tempoPrecedenteONEWIRE = millis();

    // Leggo le temperature
    sensors.requestTemperatures();
    TEMP_CAMERA = sensors.getTempC(CAMERA);
    TEMP_SALA = sensors.getTempC(SALA);

    if (TEMP_CAMERA != -127)  {
      SendToHS(8, TEMP_CAMERA);
    }

    if (TEMP_SALA != -127)  {
      SendToHS(9, TEMP_SALA);
    }

  }

  ///////ONE WIRE////END



  ///////PULSE CONSUMI////START

  pinval = digitalRead(getpulsepin);   // legge il pin del sensore

  if ((state == 1) & (pinval == 0)) { // punto di transizione
    duration = millis() - startime;       // calcola la durata della pulsazione
    startime = millis();            // setta il nuovo tempo di partenza
    power = 3600.0 / duration * 1000; // calcola consumo

    if (power <= 3000) {
      SendToHS(1, power);
      tempoAllerta = millis();
    }

    if (power > 3000) {
      if (millis() - tempoAllerta > 2000) {
        SendToHS(1, power);
      }
    }

    // int duration2 = duration; // /1000;
    duration3 = duration / 1000.0;  //duration in seconds

    SendToHS(2, duration3);

  }
  state = pinval;         // imposta lo state uguale alla lettura del pin


  if (millis() - tempoPrecedente > 5000) {
    tempoPrecedente = millis();
    int consumo_generale = analogRead(A0) * (100 / 1023.0 * 6);
    SendToHS(3, consumo_generale);
  }

  ///////PULSE CONSUMI////END

La lettura quindi delle sonde di temperatura mi crea un ritardo nella acquisizione degli impulsi.
Per ovviare al problema posso abbassare la risoluzione settandola da 12 a 9 ma il problema comunque persisterebbe.
Alternativa sarebbe quella di utilizzare un Arduino solo per la rilevazione della temperatura (che può essere anche ogni 5, 10 ,30 secondi…)

La mia domanda: c’è qualche modo per dare priorità al codice di lettura degli impulsi consumo e rilevare le temperature in un secondo tempo per non generare ritardi?

Seconda domanda: se utilizzassi un NodeMCU che lavora a 80MHz, potrei trarne beneficio? Immagino di no perché comunque il micro dovrebbe attendere comunque i 750ms per completare la lettura della temperatura.

P.S.: SendToHS è una mia funzione che invia i dati al serverino domotico.
P.S2: la parte di codice

    if (power > 3000) {
      if (millis() - tempoAllerta > 2000) {
        SendToHS(1, power);

Mi serve per filtrare degli spike che ogni tanto rilevo. Impulsi a meno di un secondo fanno si che il valore di consumo venga calcolato come superiore ai 3300Wh. Il sistema è programmato per avvisarmi se supero i 3300Wh…

Grazie

Cris

Certo, leggi l'impulso sfruttando un interrupt esterno. Dai un'occhiata alla funzione attachInterrupt() del reference.

SukkoPera:
Certo, leggi l'impulso sfruttando un interrupt esterno. Dai un'occhiata alla funzione attachInterrupt() del reference.

Grazie! Inizio a studiare la funzione che purtroppo non ho mai utilizzato e quindi potrebbe risultarmi difficile da comprendere all'inizio.
In ogni caso, tale funzione dovrebbe consentirmi di dare assoluta priorità al conteggio dei millisecondi che separano i due impulsi. Dico bene?

Certo, vedi qua per capire innanzitutto cos'è un interrupt: Interrupt - Wikipedia.

Il resto è banale ;).

Allora, ho dato un'occhiata agli interrupt ed ho capito (credo) come funzionano.

Il tutto "semplicemente" dovrebbe diventare una cosa simile (mi riferisco solo alla parte di calcolo consumi).

Da così:

//////PULSE CONSUMI////START

  pinval = digitalRead(getpulsepin);   // legge il pin del sensore

  if ((state == 1) & (pinval == 0)) { // punto di transizione
    duration = millis() - startime;       // calcola la durata della pulsazione
    startime = millis();            // setta il nuovo tempo di partenza
    power = 3600.0 / duration * 1000; // calcola consumo

A così:

//////PULSE CONSUMI////START

unsigned long tempo_trascorso = millis();

void setup(){
       
       attachInterrupt(0,calcola_tempo,FALLING);  //Il mio impulso va da VCC a GND
}
void calcola_tempo(){
   duration=millis()-tempo_trascorso;
   tempo_trascorso=millis();   
}
power = 3600.0 / duration * 1000; // calcola consumo

Per non appesantire troppo il programma e rallentare il resto, in "calcola_tempo" eseguo solo ed esclusivamente il calcolo che intercorre tra i due impulsi. Giusto?

Visto che il programma si deve occupare anche di gestire la connessione seriale tra arduino ed il serverino domotico, devo vedere se l'interrupt mi crea problemi...
Per quel che riguarda i sensori DS18B20, se un impulso avvenisse durante l'acquisizione della temperatura (con conseguente esecuzione dell'interrupt), perderei la lettura, giusto?

Ancora grazie!

Mi sembra corretto, l'unico appunto è che è meglio usare:

attachInterrupt(digitalPinToInterrupt(2),calcola_tempo,FALLING);

Per il resto, con una ISR così rapida, non dovresti perdere nessuna lettura, anche perché non credo che la lettura del DS18B20 richieda timing più di tanto precisi.

… mi sembra che l’interrupt, dopo l’interruzione del loop e l’esecuzione della sua routine, ritorni dal punto in cui aveva interrotto, quindi in teoria non dovresti perderla … mi sembra …

Certo che è così, altrimenti farebbe più male che bene ;).

E su Arduino non funzionerebbe niente, tra l'altro, visto che millis() sfrutta appunto un interrupt per il suo funzionamento interno. Lo stato di ogni programma sarebbe devastato ogni secondo!

Grazie!
Stasera provo. Potrei farlo da remoto in pausa pranzo ma se combino qualcosa è meglio essere "sul pezzo" :grin: :grin: :grin: :grinning:

SukkoPera:
E su Arduino non funzionerebbe niente, tra l'altro, visto che millis() sfrutta appunto un interrupt per il suo funzionamento interno.

Ecco, appunto, mi raccomando, ricordate che dentro una ISR gli interrupt sono disabilitati, il che significa che millis() viene congelato al valore che ha nel momento in cui si entra nella ISR e NON avanza (... non per nulla NON è possibile usare la delay() nelle ISR :grin:).

Questo, ovviamente, in funzione del programma, può essere o non essere un problema ...
... ma è bene ternerlo in mente :wink:

Guglielmo

Intanto:

digitalWrite (2, HIGH); //setto resistenza interna di pullUP

è meglio che sia:

pinMode (2, INPUT_PULLUP);

A parte questo, le variabili che usi all’interno della ISR devono essere dichiarate con l’attributo volatile.

Cavoli, ho rimosso il post perché sono un babbeo ma tu hai già risposto :slight_smile:

Per la simulazione mettevo manualmente a GND il pin con il cavetto che puntavo sulla beadboard.
Ovviamente generavo una marea di disturbi che venivano "letti" dall'interrupt.
Stupido me. Collegando la basettina test al generatore di impulsi tutto funziona correttamente.

Ti sfrutto per una info, perché vanno dichiarate volatile le variabili interne alla ISR?

Se non conosci il linguaggio assembly/macchina è difficile capire. Se vuoi te lo spiego, ma forse fai prima a googlare :).

perché vanno dichiarate volatile le variabili interne alla ISR?

Solo se sono comuni, non se vengono usate solo nella ISR

SukkoPera:
Se non conosci il linguaggio assembly/macchina è difficile capire. Se vuoi te lo spiego, ma forse fai prima a googlare :).

gogolo volentieri! Così finisco per trovare e leggermi altro :slight_smile:

Grazie!