Zeitmessung Interrupt gesteuert

Da Du Deine Laufzeit mit Sicherheit nicht in Millisekunden auswerten willst, kannst Du mit millis auch einen Sekundenzähler bauen. Das dürfte ausreichend sein.

Beispiel:

uint32_t sekunden = 0;
uint32_t lastMillis;

// im loop
uint32_t aktMillis = millis();
if (aktMillis - lastMillis >= 1000) { // 1 Sekunde
  lastMillis = aktMillis;
  sekunden++;
}

Gruß Tommy

Hi

Was versprichst Du Dir vom Interrupt?
Selbst, wenn der Arduino im Land der träume verweilt und durch 'egal was' perInterrupt aufgeweckt wird, kann Er DIREKT mit Seinem Programm anfangen - prüfen, Was jetzt fällig ist (Brenner ging an oder aus?), die entsprechende Zeit an der RTC erfragen (immerhin auf die Sekunde genau) und daraus den Verbrauch berechnen.
Einen Schritt weiter schiebst Du den Datensatz auf die SD-Karte, am Jahres-Ende (oder im Sommer, wenn der Brenner Urlaub hat) nach Excel und Grafiken für's Wohnzimmer zaubern.

Mein Ansatz:
Nix Interrupt
sondern eine State-Maschine
Wenn Tiefschlaf, irgendwie den Arduino wieder wach bekommen (wechselnde Flanke oder HIGH/LOW an bestimmtem Pin - müsste nachlesen).

MfG

Danke für Eure vielen und schnellen Rückmeldungen !!

Die Überlegung, Interrupts einzusetzen ist wohl auf mein mangelndes Wissen über die Komplexität und Möglichkeiten zurückzuführen. Ich muss das so gewiss nicht machen.

Der Arduino schläft nicht, die Zeitmessung soll einen bereits funktionierenden Temperaturdatenlogger ergänzen - wenn sie dann mal funktioniert ...

Wo finde ich denn Hinweise, wie ich mit dem Überlauf von millis() umgehen kann ?

Was genau ist eine State-Maschine und wie bau ich sowas bzw. finde Hinweise, wie das geht ? Bitte gerne auch dazu Hinweise

Viele Grüße

WZ1:
Wo finde ich denn Hinweise, wie ich mit dem Überlauf von millis() umgehen kann ?

Den hast Du in #3 schon bekommen. Das Konstrukt ist überlauffest.

Gruß Tommy

Dazu kommt, dass 2 x 10000 Millisekunden, also 10 Sekunden-delay in der loop auch nicht wirklich was verloren haben. Um schnell auf etwas reagieren zu können brauchst du den Programmlauf nicht wirklich für bis zu 20 Sekunden zu blockieren.

LG Stefan

Euch allen ganz herzlichen Dank, ich hab´s mit einer Statusmaschine hinbekommen :slight_smile:

Dann solltest Du das Ergebnis hier vorstellen.

Gruß Tommy

Der Sketch sieht jetzt wie folgt aus:

#include <RTClib.h>

#define tleer if(t<10)Serial.print(" ");Serial.print(t);
#define tnull if(t<10)Serial.print("0");Serial.print(t);

int TriggerState;
unsigned long currentMillis;
unsigned long previousMillis = 0;
int t;

RTC_Millis rtc;

float SystemTimeStart;
float Time, Verbrauch;
boolean state = LOW;
boolean laststate = LOW;

void setup() {
  rtc.begin(DateTime(F(__DATE__), F(__TIME__)));
  pinMode(2, INPUT);

  //Serieller Monitor
  Serial.begin(9600);
}

void loop() {
  //Zeitmessung
  state = digitalRead(2);
  if (state == HIGH && laststate == LOW)  // Pegelwechsel LOW => HIGH
  {
    SystemTimeStart = millis();
    Serial.print("Start ");
    DateTime now = rtc.now(); // RTC einlesen
    t = now.day();            // Tag
    tleer
    Serial.print(".");
    t = now.month();          // Monat
    tnull
    Serial.print(".");
    Serial.print(now.year(), DEC); // Jahr
    Serial.print("   ");
    t = now.hour();           // Stunde
    tleer
    Serial.print(":");
    t = now.minute();         // Minute
    tnull
    Serial.print(":");
    t = now.second();         // Sekunde
    tnull
    Serial.println();
  } else if (state == LOW && laststate == HIGH)    // Pegelwechsel HIGH => LOW
  {
    Time = (millis() - SystemTimeStart) / 1000;
    DateTime now = rtc.now(); // RTC einlesen
    Serial.print("Ende  ");
    t = now.day();            // Tag
    tleer
    Serial.print(".");
    t = now.month();          // Monat
    tnull
    Serial.print(".");
    Serial.print(now.year(), DEC); // Jahr
    Serial.print("   ");
    t = now.hour();           // Stunde
    tleer
    Serial.print(":");
    t = now.minute();         // Minute
    tnull
    Serial.print(":");
    t = now.second();         // Sekunde
    tnull
    Serial.println();

    //Ausgabe der Übertragungsdauer bei fallender Taktflanke
    Serial.print("Laufzeit: ");
    Serial.print(Time, 2);
    Serial.println(" s");
    Verbrauch = Time * 2.2 / 3600; // spezfischer Verbrauch 2.2 l/h
    Serial.print("Verbrauch: ");
    Serial.println(Verbrauch, 4);
  }
  laststate = state;
}

Einziges Manko … Der Loop lässt sich wohl kaum in den der Temperaturmessung einbauen, Temperaturen werden alle 15 Sekunden gemessen. Oder kann ich etwa zwei unterschiedliche Loops verwenden ?

Viele Grüße

deine defines sind ja net und ersparen dir Schreibarbeit. Dennoch sind das Code-Duplikate in deinem Source.

Wenn du sprintf vermeiden willst, aber dennoch eine formatierte Ausgabe mit Vornullen willst, warum machst du dir nicht eine Funktion die du aufrufst wenn du eine Zahl mit Vornull brauchst?

sowas in der Art, ungeprüft

void printNo(byte myValue, byte option = 0)
{
  if(myValue < 10)  
  {
    if (option == 0) 
      Serial.print("0");       
    else
      Serial.print(" ");
   }
  Serial.print(myValue);
}

Aufrufe
printNo(t, 0); //… für die Ausgabe von t mit Vornull
printNo(t);// … macht das gleiche, der zweite Parameter ist optional und defaulted auf 0
printNo(t, 1); // … für die Ausgabe von t mit einem Space

klappt halt aktuell nur bis zweistellige Zahlen, aber ich finde das besser als deine zwei defines.

Das if(myValue<0) sollte wohl eher if(myValue<10) heißen.

Gruß Tommy

noiascas Vorschlag scheint tatsächlich noch verbesserungsbedürftig.

Unschön an dem #define ist hauptsächlich, dass die Verwendung nicht wie ein Funktionsaufruf aussieht.

#define mitNull(t) if ((t) < 10) Serial.write('0'); Serial.print(t)

wäre da schon hübscher.

Wenn es nur um zweistellige positive Zahlen für Uhrzeiten geht, würde ich aus dem #define eine inline-Funktion machen

inline void tnull(byte val) {if (val < 10) Serial.write('0'); Serial.print(val); }

Eine allgemeinere Funktion, die beliebig viele führende Zeichen (wahlweise ‘0’ , default Leerzeichen) davorsetzt, könnte etwa so aussehen:

void printNo(int myValue, byte digits, char fuell=' ') {

  if(myValue>=0)  { // müsste für negative Zahlen erweitert werden
    while (--digits >= 5) Serial.write(fuell);
    while (digits) { 
       switch(digits) {
         case 4: if (myValue < 10000) Serial.write(fuell); break;
         case 3: if (myValue < 1000) Serial.write(fuell); break;
         case 2: if (myValue < 100) Serial.write(fuell); break;
         case 1: if (myValue < 10) Serial.write(fuell); break;
       }
     digits--;
     }

  }
  Serial.print(myValue);
}

Für positive int16_t Werte rechtsbündig. Negative Zahlen oder bei zu kleinem Wert von digits werden unformatiert ausgegeben.

Wenn du sprintf vermeiden willst,

Warum sollte man das tun wollen?

combie:
Warum sollte man das tun wollen?

Wenn man RAM und FLASH für etwas anderes braucht?

Nur um eine Zahl 0 .. 59 in einen Text "00" .. "59" zu wandeln, kann sprintf viel zu viel.

Aber, wenn der Speicherplatz es erlaubt, wäre es am falschen Ende gespart. Da gebe ich dir Recht.
Dann bliebe noch "sportlicher Ehrgeiz" als Grund.

sehe ich ähnlich.
Wenn man sprintf sonst nicht braucht ist es für eine zweistellige Ausgabe zu mächtig.

hab mal rasch die angesprochenen Varianten reingeklopft.
Ich finde meinen Ansatz nach Tommys Ausbesserung nicht so verkehrt
(außer es schlagt wieder irgend ein Optimierer zu).

Werte auf auf einem Uno-Bootloader mit 1.8.10

v1_legacy(); // Flash 2590 / RAM 209
v2_noiasca(); // Flash 2532 / RAM 209
v3_michael(); // Flash 2548 / RAM 207
v4_michael(); // Flash 2548 / RAM 207
v5_sprintf(); // Flash 3836 / RAM 229

//https://forum.arduino.cc/index.php?topic=646046.0

#define tleer if(t<10)Serial.print(" ");Serial.print(t);
#define tnull if(t<10)Serial.print("0");Serial.print(t);

uint8_t tag = 0;
uint8_t monat = 0;
uint16_t jahr = 0;
uint8_t stunde = 0;
uint8_t minute = 0;
uint8_t sekunde = 0;

void setup()
{
  Serial.begin(115200);
  randomSeed(analogRead(A0));
  // Zeit simulieren
  tag = random(1,32);
  monat = random(1,12);
  jahr = random(1942, 2050);
  stunde = random(1,24);
  minute = random(1,60);
  sekunde = random(1,60);

  v1_legacy();      // Flash 2590 / RAM 209
  //v2_noiasca();     // Flash 2532 / RAM 209 
  //v3_michael();     // Flash 2548 / RAM 207
  //v4_michael();     // Flash 2548 / RAM 207
  //v5_sprintf();     // Flash 3836 / RAM 229
}

void loop() {

}

void v1_legacy()                    // 9.08.1988   12:41:39 
{
  int t;
  t = tag;            // Tag
  tleer
  Serial.print(".");
  t = monat;          // Monat
  tnull
  Serial.print(".");
  Serial.print(jahr, DEC); // Jahr
  Serial.print("   ");
  t = stunde;           // Stunde
  tleer
  Serial.print(":");
  t = minute;         // Minute
  tnull
  Serial.print(":");
  t = sekunde;         // Sekunde
  tnull
  Serial.println();
}

void printNo(byte myValue, byte option = 0)
{
  if (myValue < 10) // müsste für negative Zahlen erweitert werden
  {
    if (option == 0)
      Serial.print("0");
    else
      Serial.print(" ");
  }
  Serial.print(myValue);
}

void v2_noiasca()                    // 9.08.1988   12:41:39 //
{
  printNo(tag, 1);
  Serial.print(".");
  printNo(monat);
  Serial.print(".");
  Serial.print(jahr, DEC); // Jahr
  Serial.print("   ");
  printNo(stunde, 1);
  Serial.print(":");
  printNo(minute);
  Serial.print(":");
  printNo(sekunde);
  Serial.println();
}
//

inline void v3_tnull(byte val) {
  if (val < 10) Serial.write('0');
  Serial.print(val);
}

inline void v3_tleer(byte val) {
  if (val < 10) Serial.write(' ');
  Serial.print(val);
}

void v3_michael()
{
  v3_tleer(tag);
  Serial.print(".");
  v3_tnull(monat);
  Serial.print(".");
  Serial.print(jahr, DEC); // Jahr
  Serial.print("   ");
  v3_tleer(stunde);
  Serial.print(":");
  v3_tnull(minute);
  Serial.print(":");
  v3_tnull(sekunde);
  Serial.println();
}

void v4_printNo(int myValue, byte digits, char fuell = ' ')
{
  if (myValue >= 0)  { // müsste für negative Zahlen erweitert werden
    while (--digits >= 5) Serial.write(fuell);                              
    while (digits) {
      switch (digits) {
        case 4: if (myValue < 10000) Serial.write(fuell); break;
        case 3: if (myValue < 1000) Serial.write(fuell); break;
        case 2: if (myValue < 100) Serial.write(fuell); break;
        case 1: if (myValue < 10) Serial.write(fuell); break;
      }
      digits--;
    }
  }
  Serial.print(myValue);
}

void v4_michael()                    // 9.08.1988   12:41:39 //
{
  v4_printNo(tag, 2);
  Serial.print(".");
  v4_printNo(monat, 2, '0');
  Serial.print(".");
  Serial.print(jahr, DEC); // Jahr
  Serial.print("   ");
  v4_printNo(stunde, 2);
  Serial.print(":");
  v4_printNo(minute, 2, '0');
  Serial.print(":");
  v4_printNo(sekunde, 2, '0');
  Serial.println();
}

void v5_sprintf()
{
  char logString[22];
  sprintf(logString,"%2d.%02d.%02d   %2d:%02d:%02d",tag, monat, jahr, stunde, minute, sekunde);
  Serial.println(logString);
}

noiasca:
Wenn man sprintf sonst nicht braucht ist es für eine zweistellige Ausgabe zu mächtig.

Und wo ist das Problem? Die meisten Lib's können viel mehr, als man davon braucht. Trotzdem schreibt man nicht alles selbst.
Ich würde sowas immer zuerst mit sprintf machen. Erst wenn der Sketch an die Grenzen von Flash und/oder Ram kommt, muss man ans Optimieren denken. Und bei den meisten Programmen, die ich hier so sehe, kann man da erstmal an anderen Stellen anfangen.
Was bringt es mir, wenn mein fertiger Sketch nur 60% oder 70% des Flash braucht? Wegen dem ungenutzten Flash wird nichts besser.

Leute, große Klasse, wie Ihr Euch um mich bemüht !! Ganz herzlichen Dank dafür.
Muss allerdings gestehen, dass ich ein bisschen verwirrt bin von den ganz spezifischen Vorschlägen. Da bin ich einfach noch nicht tief genug drin um das alles zu verstehen.
So wie´s grad aussiht, hab ich eine funktionierende Zeitmessung hinbekommen. Ist an dieser Lösung was dabei, was irgendwann irgendwelche Störungen oder Probleme verursachen könnte ?

Dann war ja noch die Sache mit der schon vorhandenen Temperaturmessung, in die ich eigentlich die Zeitmessung einbauen wollte. Allerdings taktet die Temperaturmessung mit einem delay von 15 Sekunden, da passt jetzt meine Zeitmessung nicht so recht dazu. Sehr Ihr einen Lösungsansatz, den ich verfolgen könnte ? Oder wird´s darauf hinauslaufen, die Zeitmessung mit einem zweiten Arduino ganz unabhängig von der Temperaturmessung zu machen ?
Viele Grüße

Hi

Ist an dieser Lösung was dabei, was irgendwann irgendwelche Störungen oder Probleme verursachen könnte ?

Könnte?? Und dann noch IRGENDWANN??
... dann ist die Antwort: Ja

Dabei ist's egal, wie toll der Aufbau wird ... irgendwann ist lang und können kann immer passieren.

MfG

Das, was du suchst nennt sich wohl "Multitasking".
Wobei dann jede Task einen endlicher Automaten abbildet.

Ablaufsteuerung
Meine Standardantwort zu Ablaufsteuerungen:

Eine Stichworte Sammlung für Google Suchen:
Endlicher Automat,
State Machine,
Multitasking,
Coroutinen,
Ablaufsteuerung,
Schrittkette,
BlinkWithoutDelay,

Blink Without Delay
Der Wachmann

Intervall Macro
Multitasking Macros
INTERVAL Ersatzstoff
CooperativeTask

Combie der Hinweis mit dem Wachmann war genial. Ich hab´s hinbekommen. Ganz herzlichen Dank auch an all die anderen und Eure Unterstützung.
Hier der betreffende Codeausschnitt:

void Zeitmessung() {
  taskBegin();
  //Zeitmessung
  state = digitalRead(2);
  if (state == HIGH && laststate == LOW)  // Pegelwechsel LOW => HIGH
  {
    SystemTimeStart = millis();
    Serial.print("Start ");
    DateTime now = rtc.now(); // RTC einlesen
    t = now.day();            // Tag
    tleer
    Serial.print(".");
    t = now.month();          // Monat
    tnull
    Serial.print(".");
    Serial.print(now.year(), DEC); // Jahr
    Serial.print("   ");
    t = now.hour();           // Stunde
    tleer
    Serial.print(":");
    t = now.minute();         // Minute
    tnull
    Serial.print(":");
    t = now.second();         // Sekunde
    tnull
    Serial.println();
  } else if (state == LOW && laststate == HIGH)    // Pegelwechsel HIGH => LOW
  {
    Time = (millis() - SystemTimeStart) / 1000;
    DateTime now = rtc.now(); // RTC einlesen
    Serial.print("Ende  ");
    t = now.day();            // Tag
    tleer
    Serial.print(".");
    t = now.month();          // Monat
    tnull
    Serial.print(".");
    Serial.print(now.year(), DEC); // Jahr
    Serial.print("   ");
    t = now.hour();           // Stunde
    tleer
    Serial.print(":");
    t = now.minute();         // Minute
    tnull
    Serial.print(":");
    t = now.second();         // Sekunde
    tnull
    Serial.println();

    //Ausgabe der Übertragungsdauer bei fallender Taktflanke
    Serial.print("Laufzeit: ");
    Serial.print(Time, 2);
    Serial.println(" s");
    Verbrauch = Time * 2.2 / 3600; // spezfischer Verbrauch 2.2 l/h
    Serial.print("Verbrauch: ");
    Serial.print(Verbrauch, 6);
    Serial.println(" l");
  }
  laststate = state;
  taskEnd();
}

void loop() {
  //Serial.println("Start Temperaturmessung");
  ii = 0;
  milltempo = millis();
  if (millis() > Pause * 1000) {
    for (int i = 0; i < AMOUNT_OF_ONEWIRE_INSTANCES; i++)
      setupSensorTemperature(sensors[i], totalDevs[i], allAddrses[i], i + 1, &ii);
    Zeitmessung();
  } else {
    Zeitmessung();
  }

}                     // void loop() - end

Viele grüße