Arduino Uhr: bricht nach 30 Sekunden ab

Hallo,
ich habe ein Programm geschrieben, dass mir alle 10 Sekunden etwas ausgibt. Allerdings bricht das Programm nach 30 Sekunden ab. Könnte mir jemand helfen?
Das ist mein Programm:

int i = 1;

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

}

void loop() {
  if(millis() > i * 5000)
  {
    Serial.println("===================================");
    Serial.println(millis());
    Serial.println(i * 5000);
    Serial.println(i);
    i++;
  }

  delay(50);
}

Und hier die Ausgabe:

===================================
5050
5000
1
===================================
10001
10000
2
===================================
15003
15000
3
===================================
20004
20000
4
===================================
25006
25000
5
===================================
30008
30000
6

Danke schonmal

Der signed Überlauf führt laut C++ Spezifikation in ein undefiniertes Verhalten.
Ist also verboten.

unsigned long i = 1;

Aber das hilft auch nur begrenzt, gegen eins der Probleme!
Denn das Verfahren ist grundsätzlich falsch!
Eine Multiplikation ist äquivalent zu einer mehrfachen Addition

Siehe: [Bericht] Der (Millis) Ueberlauf im Test

1 Like

Wozu eigentlich?

Das hat keine besonderer Bedeutung.

Verwende das Beispiel BlinkWithoutDelay der IDE als Vorlage, dann funktioniert das solange der µC Strom hat.

Stelle in den Voreinstellungen Compiler-Warnungen auf "Alle" und frage dich, was
warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
wohl bedeuten mag.
Dein Programm bricht übrigens nicht ab, sondern braucht nur ziemlich lang, da (i=7)*5000 erstmal -30.536 ist, was dann im Vergleich mit einem millis-Ergebnis (unsigned long) als 4.294.936.760 interpretiert wird. Nach 72.582 Minuten (1193 Stunden) wird der nächste Block kommen.

Mit der kleinen Änderung
if(millis() > i * 5000UL)
bekommst du übrigens alle 5 Sekunden eine Ausgabe wie du es gedacht hast. Kann sein, dass die beim Überlauf deines int i von -1 nach 0 und dann nach 1 etwas stolpert oder holpert, weil der millis() Überlauf zu einer anderen Zeit passiert.

Du hast zu wenig gewartet.
Das Programm bricht nicht ab nur daß es nachvollziebar keine Ausgaben mehr macht.
Versuch mal folgenden Sketch:

int i = 1;
unsigned long j;

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

}

void loop() {
  if(millis() > i * 5000UL)
  {    
    Serial.print("i: ");
    Serial.println(i);
    Serial.print("i*5000UL: ");
    Serial.println(i * 5000UL);
    Serial.print("i*5000: ");
    Serial.println(i * 5000);
    Serial.print("unsigned long (i*5000): ");
    j=(i * 5000);
    Serial.println(j);
    Serial.print("millis: ");
    Serial.println(millis());
    Serial.println("===================================");
  
 i++;
      }
  delay(50);
}

Grüße Uwe

Dieses Blink without Delay-Beispiel in der Arduino-IDE ist - nach meiner bescheidenen Meinung nach - in völlig unbescheiden Worten bewertet einfach nur Murks!

  • keine Demontration wie man functions benutzt
  • schlecht gewählte Variablennamen (previousMillis = currentMillis;
    Hä wie jetzt? Da zuvor wird aktuell ??? Warum das denn??
  • die Variablen müssen im code verteilt werden.

Das es das Standardbeispiel ist macht es einfach nur zu
einem weit verbeiteten ganz oft zitierten
schlechten Code-Beispiel.

Hier ist ein Democode der es - meiner bescheidenen Meinung nach - besser macht:


boolean TimePeriodIsOver (unsigned long &expireTime, unsigned long TimePeriod) {
  unsigned long currentMillis  = millis();
  if ( currentMillis - expireTime >= TimePeriod ) {
    expireTime = currentMillis; // set new expireTime
    return true;                // more time than TimePeriod has elapsed since last time if-condition was true
  }
  else return false;            // not expired
}

unsigned long meinZaehler = 0;            // verwende SELBST-erklärende Namen
unsigned long MyTestTimer = 0;  // Timer-variables MUST be of type unsigned long
const byte    OnBoard_LED = 13;

const unsigned long WarteZeitInMillisekunden = 10000;

void BlinkHeartBeatLED(int IO_Pin, int BlinkPeriod) {
  // zeigt an das das Programm läuft und die function loop() wiederhlt wird
  // wenn die onboard-LED NICHT mehr blinkt gibt es diese mögliche Ursachen:
  // - Strom weg
  // - Programm in einer SChleife fest.
  static unsigned long MyBlinkTimer;
  pinMode(IO_Pin, OUTPUT);

  if ( TimePeriodIsOver(MyBlinkTimer, BlinkPeriod) ) {
    digitalWrite(IO_Pin, !digitalRead(IO_Pin) );
  }
}


void EinmalSenden() {
  Serial.println( F("===================================" ) ); // F-Makro für Fixtexte um RAM zu sparen
  Serial.println(millis());
  Serial.println(meinZaehler * 5000);
  Serial.println(meinZaehler);
  meinZaehler++;
}

void setup() {
  Serial.begin(9600);
  Serial.println( F("Setup-Start") ); // F-Makro für Fixtexte um RAM zu sparen
}


void loop() {
  BlinkHeartBeatLED(OnBoard_LED, 500); // 500 Millisekunden aus / 500 Millisekunden an

  if ( TimePeriodIsOver(MyTestTimer, WarteZeitInMillisekunden) ) {
    EinmalSenden();
  }
}

vgs

Ich gebe dir insoweit Recht, dass beim BlinkWithoutDelay nicht delay() durch millis() ersetzt wird, sondern das Wesentliche daran die Statusvariable ist. Ob die nun previousMillis oder wie bei dir MyTestTimer oder MyBlinkTimer heißt, ist ja nun jedem freigestellt.

Auch hast du Recht, dass der große Vorteil der WithoutDelay-Herangehensweise darin liegt, dass man verschiedene Zeitabhängigkeiten unabhängig voneinander hinkriegt. Dafür ist natürlich ein Funktionsaufruf sehr sinnvoll. und die Tatsache, dass (wenn man es nicht objektorientiert macht) diese Funktion eine Referenz auf die jeweils verwendete Statusvariable braucht.
Dass diese Statusvariablen entweder global oder statisch sein können, sollte auch gezeigt werden.

Dass eine Demo nebenbei zeigt, wie früh ein int überläuft, ist allerdings deutlich kontraproduktiv nach allen didaktischen Regeln. Das merkt jeder Arduino-Anfänger selber, eher früher als später.

Neben der vorhandenen BlinkWithoutDelay-Demo als Basis wäre sicher ein weiterführendes Beispiel sinnvoll. Die Beispiele sind allerdings nicht als bunte Sammlung verschiedener Programmiertipps (und dann noch mehrere Tips in einer Demo) gedacht.

Ich wiederhole!

Das größere Drama ist, dass der int Überlauf unweigerlich in ein Undefiniertes Verhalten mündet.
Steht somit unter Höchststrafe!

Es kann also keine Beispiele geben. (aus denen man was lernen könnte)
Per Definition.

Na wenn du meinst.

Ich muss irgendwie völlig verquer im Kopf sein weil ich glaube das mit diesem Forum schon zehntausende Leuten etwas gelernt haben. Ein paar sogar etwas mit meinen Beispielen.
Vielleicht solltest du dich von dokuVorleseVerweigerer in "verstehVerweigerer" umbenennen? Aber das verstehst du wahrschlich nicht. Geschweige denn das du aus diesem Beispiel etwas lernen könntest. Vielleicht ist Beispiel-Lernen und Lernbeispiele einfach nix für dich.
grafik

Oder du probierst es doch mal mit (Doku)-Beispiele vorlesen?
Naja wieder was aus einem (Posting)Beispiel gelernt. Ich habe mich jedenfalls geradezu beispielhaft beim Schreiben amüsiert. Und Gute Laune soll (zum Beispiel) beim Lernen förderlich sein.

Ich verstehe, dass dir Kritik nicht schmeckt.
Und doch kann ein Beispiel, welches einen UB verursacht, meines bescheidenen Erachtens nach nicht "besser" sein.
Serial.println(meinZaehler * 5000);

Es verblüfft mich ein wenig, dass du mich so scharf angehst.
Auf eine fachliche Kritik so persönlich wirst.

Ja meine ich!
Aus Beispielen, welche in ein Undefiniertes Verhalten taumeln kann und sollte man nichts lernen.
Denn dann lernt man das falsche.

Also nochmal ganz klar:
Sicherlich kann man in Foren was lernen.
Aber dein Beispiel ist genau in dem Punkt "UB" Mist.
Entweder siehst du das ein, oder du lässt es.

Ich werde dich hier und jetzt damit alleine lassen.
Und wenn sich das wiederholt, kommt wieder diese fachlich korrekte Kritik.
Jedes mal, wenn ich das sehe.
Da bist du weder ein Einzelfall, noch sonst wie was besonderes.

Wenn die Leute hier schon was lernen sollen/können, dann auch bitte das korrekte.
Und nicht das falsche.

printed negative Zahlen. So what?
vgs

Ist das eine ernst gemeinte Frage?

Start
22:11:40.761 -> ===================================
22:11:40.761 -> 100
22:11:40.761 -> 0
22:11:40.761 -> 0
22:11:40.864 -> ===================================
22:11:40.864 -> 200
22:11:40.864 -> 5000
22:11:40.864 -> 1
22:11:40.968 -> ===================================
22:11:40.968 -> 300
22:11:40.968 -> 10000
22:11:40.968 -> 2
22:11:41.071 -> ===================================
22:11:41.071 -> 400
22:11:41.071 -> 15000
22:35:11.559 -> 32728
22:35:11.559 -> 14123
22:35:11.665 -> ===================================
22:35:11.665 -> 1412500
22:35:11.665 -> -27808
22:35:11.665 -> 14124
22:35:11.770 -> ===================================
22:35:11.770 -> 1412600
22:35:11.770 -> -22808
22:35:11.770 -> 14125
22:35:11.873 -> ===================================
22:35:11.873 -> 1412700
22:35:11.873 -> -17808
22:35:11.873 -> 1412

OK...

Das kann ich dir widerlegen!
(ohne Probleme)

Ein Beispiel für einen UNO, oder vergleichbaren:

byte feld[200];


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

    
  // alle Zellen vorbesetzen
  for(byte &data:feld) data = 0xFF;

  
  int result = 0; 
  for(byte data:feld) result += data;
  Serial.print(result); 
  
}

void loop(){}

Wenn der undefinierte Überlauf nur ein Implementationsdefinierter Überlauf wäre, dann würde der print(result) nicht 4294952760 anzeigen können!
Tut er aber!

Du würdest sicherlich -14536 erwarten.

Also was jetzt?

Ich wiederhole gerne noch mal:
Implemetation defined Behavior: Vorsicht damit! Aufmerksam sein.
Undefined Behavior: Die Hölle! Verboten.

1 Like

Sehr überzeugendes UB - Beispiel! Wie eine Methode print(int) auf einem Uno überhaupt die Zahl 4294952760 ausgeben kann, ist schon verstörend.
Wenn man genauer hinschaut, hat der Arduino sich bei

...
32385
32640
4294934655
...

"verschluckt"
Und behält diesen Wert bei folgenden += Operationen.

Witzigerweise bekommet man auch bei einer Variante wie
for(byte data:feld) Serial << (result += (byte)0xFF) << endl;
nicht auf negative Werte, aber auf 51000.

... und die Compiler-Warnung:
warning: iteration 128 invokes undefined behavior

Ja, als mir das begegnet ist, war ich auch etwas verstört.

Dann habe ich mich aber reingekniet und kann jetzt jeden einzelnen der dort auftretenden Effekte nachvollziehen.

Der Grund für die positiven Zahlen ist eigentlich recht einfach:
Da laut den mathematischen Gesetzen und Regeln, es so ist, dass eine Addition von 2 positiven Zahlen, niemals ein negatives Ergebnis liefern kann, darf der Optimizer alle Abhandlungen für die Ausgabe negativer Zahlen aus dem Code entfernen!
Und genau das tut er hier.

Es kann ja nie ein negatives Ergebnis kommen, denn der Überlauf ist per Definition verboten.

Also alles richtig so.
Vielleicht unerwartet, aber das korrekte Verhalten.

Eigentlich darf dir der Compiler/Arduino auch in einer solchen Situation das Wohnzimmer tapezieren, oder sich eine Pizza bestellen.
Das wäre genau so korrekt.

Es wäre verboten, sich beim Compiler-Programmierer zu beschweren. Aber darauf hoffen, dass das Zimmer tapeziert wird, sollte man besser auch nicht. :slight_smile:

Die Erwartungshaltung, ist die Mutter der Enttäuschung.

Wobei ich hoffe, dass der Groschen, jetzt auch beim StefanL38, gefallen ist.