[Anfängerfrage] Toggeln von Bool-Variablen

Hi zusammen, ich bin, was die Programmiersprache des Arduinos angeht, ein absoluter Anfänger. Im Moment versuche ich eine kleine Uhr zusammenzuschustern (Ja, nicht unbedingt ein Anfängerprojekt, aber die Leber wächst mit ihren Aufgaben.). Dabei bin ich auf ein kleines (für mich unverständliches) Problem gestoßen. Der Plan ist es, hier, so lange ich die Taste drücke einen zähler(zaehler) hochzählen zu lassen. Bei überschreiten der 400 soll uhrflag getoggelt werden, also immer zwischen 0 und 1 schalten und der zähler zurück auf 0 gesetzt werden.
Soweit, so gut.

set_btn und uhrflag sind als bool definiert, zaehler als int
der Eingangspin ist auf INPUT_PULLUP

void loop() {
  set_btn=!digitalRead(2);
  if(set_btn){
    zaehler++; 
  }else{
    zaehler=0;
  }
   if(zaehler > 400){
    zaehler=0;
    uhrflag = !uhrflag;
    Serial.println(uhrflag);
  }

}

Wenn ich diesen Code benutze, sollte ich jedes mal wenn die 400 erreicht werden, sehen dass uhrflag schaltet. Komischerweise schaltet der aber jedes mal 2x. Laut Zeitstempel im Serialmonitor auch gleichzeitig. Der zaehler geht aber ganz regulär auf 0 und zählt dann wieder hoch bis auf 400.

Setze ich nun die Serial.print-Befehle hinter die If-abfrage funktioniert alles so wie es soll. Wo habe ich den Verständnisfehler? Werden Variablen beim senden an die Serielle Schnittstelle verändert?

Danke schon mal für eure Infos
-Matthias

edit: Anscheinend liegt der Fehler im weiteren Programm. wenn ich tatsächlich nur das oben gezeigte lade gehts komischerweise (musste allerdings einen delay einbauen). Daher hier einmal der komplette Quelltext (Bitte nicht lachen)

  #include "DS1307RTC.h"
  #include "LedControl.h"
  #include "Wire.h"
  LedControl lc=LedControl(12,11,10,1); //Max7213 Data-12; Clock-11; Load-10; Anzahl-1
  byte zsec=0; //Zehner Sekunden
  byte esec=0; //Einer Sekunden
  byte zmin=0;
  byte emin=0;
  byte ehour=0;
  byte zhour=0;
  byte sectic=0;
  int sensor=0;
  bool alarm_btn;
  bool alarm_btn_flag=0;
  bool alarm_on=0;
  bool uhrflag=0;
  bool set_btn=0;
  int set_btn_count=0;
  int zaehler;
void setup() {
  pinMode(6,INPUT_PULLUP); //Alarm an Pin 6
  pinMode(2,INPUT_PULLUP); //SET an Pin 2
  lc.shutdown(0,false);// turn off power saving, enables display
  lc.setIntensity(0,15);// sets brightness (0~15 possible values)
  lc.clearDisplay(0);// clear screen
  setSyncProvider(RTC.get);
  Serial.begin(9600);
}

void loop() {
//  alarm_btn=digitalRead(6);
  set_btn=!digitalRead(2);
/*  if (alarm_btn && !alarm_btn_flag)
    {
      alarm_btn_flag=1;
    }
  if(!alarm_btn && alarm_btn_flag)
    {
      alarm_on=!alarm_on;
      alarm_btn_flag=false;
    }
*/
  if(set_btn){
    zaehler++; 
  }else{
    zaehler=0;
  }
   if(zaehler > 400){
    zaehler=0;
    uhrflag = !uhrflag;
    Serial.println(uhrflag);
  }
//    Serial.print(zaehler);
//    Serial.print("_");
//    Serial.println(uhrflag);
  if (!uhrflag){
    Helligkeit();
    AnzeigeUhr();
  }
}
//******************************************
//Ende der Hauptroutine, ab hier Subroutinen



void AnzeigeUhr(){ //Routine zur Anzeige im Uhrenmodus
  esec=second();
  zsec=esec/10;
  esec=esec-(10*zsec);
  emin=minute();
  zmin=emin/10;
  emin=emin-(10*zmin);
  ehour=hour();
  zhour=ehour/10;
  ehour=ehour-(10*zhour);
  sectic=(esec%2*7)+1;
  lc.setDigit(0,4,sectic,alarm_on);
  lc.setDigit(0,3,emin,set_btn);
  lc.setDigit(0,2,zmin,0);
  lc.setDigit(0,1,ehour,0);
  lc.setDigit(0,0,zhour,0);
  //Serial.print(uhrflag);
  //Serial.print("-");
  //Serial.println(zaehler);
  }

void Helligkeit(){ //Routine zum Auslesen des Helligkeitssensor und anpassen der Anzeigenintensität
    sensor=analogRead(A3);
  switch(sensor){
    case 0 ... 200:
      lc.setIntensity(0,0);
    break;
    case 201 ... 300:
      lc.setIntensity(0,2);
    break;
    case 301 ... 400:
      lc.setIntensity(0,4);
    break;
    case 401 ... 500:
      lc.setIntensity(0,6);
    break;
    case 501 ... 600:
      lc.setIntensity(0,8);
    break;
    case 601 ... 700:
      lc.setIntensity(0,10);
    break;
    case 701 ... 800:
      lc.setIntensity(0,12);
    break;
    case 801 ... 1024:
      lc.setIntensity(0,15);
    break;
  }   
}

Hallo,

ich vermute:
1 Dein Taster prellt
2 du solltest nur bei einer Flanke zählen

Nachtrag ich hab falsch gelesen sorry

Heinz

Wie lange dauern, deiner Schätzung nach, 400 loop-Durchläufe?

Bei dir hängt es vermutlich an der Serial Geschwindigkeit, deren SendePuffer immer am Anschlag ist und deshalb bremst. (Bei 9600 wechselt uhrflagalle 2 ms, wenn du den Taster erstmal gedrückt hattest)

@rentner
Dass der Taster pellen könnte ist mir bewusst. Dadurch dass ich in der If-Abfrage den Zähler unter else wieder auf 0 setze, sollte das pellen wegfallen.

@Michael_x
Ich hatte bereits geschrieben dass ich bei dem Testprogramm einen delay eingebaut hatte (5ms). Ich konnte auch im regulären Programm (unter edit) den Zähler nachverfolgen. Durch die weitere Programmierung wird die Software soweit ausgebremst dass der Serielle Buffer nicht überläuft.

Setze ich nun die Serial.print-Befehle hinter die If-abfrage funktioniert alles so wie es soll. Wo habe ich den Verständnisfehler? Werden Variablen beim senden an die Serielle Schnittstelle verändert?

Nein, print verändert nicht den Ausgabewert.

Auch wenn du es glaubwürdig abstreitest, der übliche ungeplante Seiteneffekt ist das unterschiedliche Zeitverhalten bei vollem Sendepuffer. Wo der Verständnisfehler genau liegt, hab ich aber leider noch nicht gesehen.

Wenn die Logik nur mit delay richtig arbeitet, ist ein grundlegender Fehler in der Logik.
Alte Programmierer-Weisheit

michael_x:
Nein, print verändert nicht den Ausgabewert.

Auch wenn du es glaubwürdig abstreitest, der übliche ungeplante Seiteneffekt ist das unterschiedliche Zeitverhalten bei vollem Sendepuffer. Wo der Verständnisfehler genau liegt, hab ich aber leider noch nicht gesehen.

Wenn die Logik nur mit delay richtig arbeitet, ist ein grundlegender Fehler in der Logik.
Alte Programmierer-Weisheit

Dass Print nivht den Wert verändert war auch eher eine sarkastische Frage. Hätte ich vlt. kenntlich machen sollen.

Hätte es einen Einfluss wenn ich testweise sie Übertragungsgeschwindigkeit erhöhe? Oder was kann ich sonst machen um das zu verhindern? Ich könnte jetzt einen delay ans Ende der If abfrage setzen (bin ich eigentlich kein Freund von, aber zum testen ist es okay). Würde das was bringen? Wie gesagt, ich möchte gerne verstehen was da vorgeht, da ich noch ganz am Anfang stehe. Habe vorher viel mit bascom gearbeitet und das hier ist für mich schon eine recht große Umstellung.

Die ganze uhrflag Geschichte soll doch dazu dienen, die zwei Funktionen

   Helligkeit();
   AnzeigeUhr();

nur gelegentlich auszuführen,oder?
Dazu müsste  uhrflag  nur einen Zyklus lang gesezt werden, und nicht symmetrisch hin und her getoggelt werden. Und eine BlinkWithoutDelay-Logik wäre wohl sinnvoller als Zyklen zählen.

Nein, uhrflag soll dazu dienen, wenn ich die Taste (SET-Taste) lange drücke (daher der zähler), die Anzeige der Uhrzeit zu stoppen und in den Set- Modus zu wechseln (Der Programmteil kommt noch :stuck_out_tongue: ). Dabei bin ich halt auf o.G. Problem gestoßen, weil ich mich gewundert habe, dass die Uhrzeit nicht gestoppt hat. Bin dann halt auf Fehlersuche gegangen und fast verzweifelt, weil ich mir die Logik dahinter nicht erklären konnte.

Der Programmteil kommt noch

OK. Aber Zyklen zählen um Zeiten zu erkennen ist einfach Mist.

Nimm ne Lib, die kurze und lange Tastendrücke unterscheidet. Oder mach es zu Fuss mit millis(), aber nicht mit loop-Durchläufe zählen. Sowas tut man nur, wenn millis() kaputt ist.

Ne lib ist auch ne Idee (merkt man dass ich bei bascom immer alles "zu Fuß " gemacht habe? lach). Bin jetzt grade nicht am PC (arbeit), welche Lib wäre das denn, die ich nutzen könnte? Dann kann ich mich schon mal etwas einlesen.

OT: gibt es eigentlich eine IDE die nicht so extrem auf groß und Kleinschreibung achtet?

Das ist keine Eigenheit der IDE. Die Sprache C++ ist case sensitiv.

Gruß Tommy

Also muss ich mich dran gewöhnen. Ist ja noch ein langer Weg und ich stehe noch recht am Anfang :slight_smile:

Also muss ich mich dran gewöhnen.

Sorgfalt und Disziplin sind die Eltern eines, zuverlässigen und schönen, Programms.

Eine laxe Vorgehensweise, kann dir dein soziales Umfeld verzeihen.
C++ tut das nicht.

Auch wenn Windows nicht case-sensitiv ist, sollte man (zur Sicherheit) auch bei Dateinamen die "richtige" Schreibweise verwenden. Java (die Programmiersprache der IDE selbst) ist auch case-sensitiv.

Da gab es früher schonmal Probleme, über die jeder der zwischen Linux und Windows hin und her springt, milde lächelt.

\|/
 |

Ich sag ja, ich hab definitiv zu lange mit Basic rumgekrebst. Hab mir die Grundzüge bereits mit 12 auf dem C64 und dem Programmierhandbuch selber beigebracht und bisher hat das immer gereicht. Was mich beim Arduino bzw dessen Programmiersprache fasziniert ist, dass man nicht mehr alles zu Fuß machen muss. Und die Syntax werde ich mir schon irgendwie einprügeln.

Trotz alledem bin ich immer noch am rätseln warum mein Programm diesen Effekt zeigt. Werde nachher, wenn ich wieder im Keller bin mal den aktuellen Quellcode hochladen. Ich steige da immer noch nicht hinter.

Sorry, dass ich mich heute erst melde, ich hatte gestern zu viel zu tun als dass ich noch was am PC machen konnte. Hier nun der Quellcode und die Ausgabe aus dem Monitor. vielleicht kann das aufschluss geben.

#include "DS1307RTC.h"
  #include "LedControl.h"
  #include "Wire.h"
  LedControl lc=LedControl(12,11,10,1); //Max7213 Data-12; Clock-11; Load-10; Anzahl-1
  byte zsec=0; //Zehner Sekunden
  byte esec=0; //Einer Sekunden
  byte zmin=0;
  byte emin=0;
  byte ehour=0;
  byte zhour=0;
  byte sectic=0;
  int sensor=0;
  bool alarm_btn;
  bool alarm_btn_flag=0;
  bool alarm_on=0;
  bool uhrflag=0;
  bool set_btn=0;
  int set_btn_count=0;
  int zaehler;
void setup() {
  pinMode(6,INPUT_PULLUP); //Alarm an Pin 6
  pinMode(2,INPUT_PULLUP); //SET an Pin 2
  lc.shutdown(0,false);// turn off power saving, enables display
  lc.setIntensity(0,15);// sets brightness (0~15 possible values)
  lc.clearDisplay(0);// clear screen
  setSyncProvider(RTC.get);
  Serial.begin(9600);
}

void loop() {
//  alarm_btn=digitalRead(6);
  set_btn=!digitalRead(2);
/*  if (alarm_btn && !alarm_btn_flag)
    {
      alarm_btn_flag=1;
    }
  if(!alarm_btn && alarm_btn_flag)
    {
      alarm_on=!alarm_on;
      alarm_btn_flag=false;
    }
*/
  if(set_btn){
    zaehler++; 
  }else{
    zaehler=0;
  }
   if(zaehler > 400){
    zaehler=0;
    uhrflag = !uhrflag;
    Serial.println(uhrflag);
    delay(5);
  }
//    Serial.print(zaehler);
//    Serial.print("_");
//    Serial.println(uhrflag);
  if (!uhrflag){
    Helligkeit();
    AnzeigeUhr();
  }
}
//******************************************
//Ende der Hauptroutine, ab hier Subroutinen



void AnzeigeUhr(){ //Routine zur Anzeige im Uhrenmodus
  esec=second();
  zsec=esec/10;
  esec=esec-(10*zsec);
  emin=minute();
  zmin=emin/10;
  emin=emin-(10*zmin);
  ehour=hour();
  zhour=ehour/10;
  ehour=ehour-(10*zhour);
  sectic=(esec%2*7)+1;
  lc.setDigit(0,4,sectic,alarm_on);
  lc.setDigit(0,3,emin,set_btn);
  lc.setDigit(0,2,zmin,0);
  lc.setDigit(0,1,ehour,0);
  lc.setDigit(0,0,zhour,0);
  //Serial.print(uhrflag);
  //Serial.print("-");
  //Serial.println(zaehler);
  }

void Helligkeit(){ //Routine zum Auslesen des Helligkeitssensor und anpassen der Anzeigenintensität
    sensor=analogRead(A3);
  switch(sensor){
    case 0 ... 200:
      lc.setIntensity(0,0);
    break;
    case 201 ... 300:
      lc.setIntensity(0,2);
    break;
    case 301 ... 400:
      lc.setIntensity(0,4);
    break;
    case 401 ... 500:
      lc.setIntensity(0,6);
    break;
    case 501 ... 600:
      lc.setIntensity(0,8);
    break;
    case 601 ... 700:
      lc.setIntensity(0,10);
    break;
    case 701 ... 800:
      lc.setIntensity(0,12);
    break;
    case 801 ... 1024:
      lc.setIntensity(0,15);
    break;
  }   
}

und hier die Ausgabe im Monitor

18:17:05.828 -> 1
18:17:05.828 -> 0
18:17:06.583 -> 1
18:17:06.583 -> 0
18:17:07.379 -> 1
18:17:07.379 -> 0
18:17:08.126 -> 1
18:17:08.126 -> 0
18:17:08.878 -> 1
18:17:08.878 -> 0
18:17:09.629 -> 1
18:17:09.675 -> 0
18:17:10.413 -> 1
18:17:10.413 -> 0
18:17:11.160 -> 1
18:17:11.160 -> 0

Die Uhrzeit erfindet der SerialMonitor im PC dazu.
Bei 9600 sollten eigentlich zwei Zeilen um 2 ms unterschiedliche Zeitstempel haben.
Merkwürdig.

Und: Warum kommt ausgerechnet diese Meldung

18:17:09.675 -> 0

46ms später als sonst?

Zu deiner Zyklen-Zählerei sag ich nichts mehr.

michael_x:
Die Uhrzeit erfindet der SerialMonitor im PC dazu.
Bei 9600 sollten eigentlich zwei Zeilen um 2 ms unterschiedliche Zeitstempel haben.
Merkwürdig.

Und: Warum kommt ausgerechnet diese Meldung

18:17:09.675 -> 0

46ms später als sonst?

Zu deiner Zyklen-Zählerei sag ich nichts mehr.

Die Zyklenzählerei ist im moment nur dem Test geschuldet. Werde den Debounce über Bounce2 machen, damit teste ich grade etwas rum. Werde auch nicht bei jedem Loop die Anzeige aufrufen, da das totaler quatsch ist. werde dafür millis() verwenden und das unterprogramm nur etwa alle 100millis aufrufen.

Mich hat auch gewundert dass da quasi immer die selben Zeiten stehen. Wieso da zwischendurch mal ein unterschied ist, kann ich mir auch nicht erklären. das taucht aber unregelmäßig auf. Was mich halt wundert, ist, dass Serial.print-Befehl 2 mal ausgeführt wird. Ich habe jetzt mal testweise einen zweiten print eingebaut, der mir den Zähler ausgibt und "zaehler=0;" dahinter gesetzt. wieder das selbe. Es sieht so aus als ob die If-Schleife (hört sich falsch an) bis zu dem Print-befehl doppelt ausgeführt wird und erst im zweiten durchlauf der Zähler auf null gesetzt wird. Anscheinend bin ich einfach zu doof dafür, den Logikfehler zu finden.

Anscheinend bin ich einfach zu doof dafür, den Logikfehler zu finden.

Glaube ich nicht.
Da fehlt sicherlich nur ein kleiner "Aha Effekt".

Ich nenne die notwendige Methode: "In Nebenläufigkeiten denken"
(vielleicht hilft dir das ja)