Beleuchtungssteuerung

Hallo,

ich hätte mal eine Frage zu einem kleinen Programm, dass ich geschrieben habe.

Vielleicht erst mal ein paar Infos zum Aufbau:
Am Arduino Uno hängen drei LEDs (Rot,Gelb,Grün) und zwei Taster (Taster A und B).
Wenn ich jetzt einen der beiden Taster betätige, leuchten die drei LEDs für ca. 10 Sekunden auf, und wenn ich nochmal einen der beiden Taster während dieser Zeit drücke, wird die Zeit resettet und die LEDs leuchten dann länger.

Soweit ist das alles so wie ich es wollte, aber ich scheitere jetzt daran, über einen weiteren Taster (TasterC) bzw. Schalter eine Dauerlichtfunktion zu bekommen.
Ich habe es schon mit if und while probiert und das funktioniert auch, nur funktionert der restliche Code dann nicht mehr so wie vorher.

Vielleicht kann sich mal jemand den Code ansehen und mir sagen, warum ich das nicht hinbekomme.

Schon mal vielen Dank für euere Hilfe.

int ledRot = 10;
int ledGelb = 11;
int ledGruen = 13;
int tasterAPin = 7;     // Taster-A-Pin 7
int tasterBPin = 8;     // Taster-B-Pin 8
int tasterCPin = 6;     // Taster-C-Pin 6
int interval = 10000; // Intervallzeit (10 Sekunden)
unsigned long Zeit; // Zeit-Variable

void setup(){
  pinMode(ledRot, OUTPUT);
  pinMode(ledGelb, OUTPUT);
  pinMode(ledGruen, OUTPUT);
  pinMode(tasterAPin, INPUT);
  pinMode(tasterBPin, INPUT);
  pinMode(tasterCPin, INPUT);
  Zeit = millis();
}

void loop()
 //while (digitalRead(tasterCPin)== HIGH)
 //{digitalWrite(ledRot, HIGH); // Rote LED auf High-Pegel (5V)
 //digitalWrite(ledGelb, HIGH); // Gelbe LED auf High-Pegel (5V)
 //digitalWrite(ledGruen, HIGH); // Grüne LED auf High-Pegel (5V)}
 //if(digitalRead(tasterCPin)== LOW)
 //digitalWrite(ledRot, LOW); // Rote LED auf Low-Pegel (5V)
 //digitalWrite(ledGelb, LOW); // Gelbe LED auf High-Pegel (5V)
 //digitalWrite(ledGruen, LOW); // Grüne LED auf High-Pegel (5V)}
 {
 if(digitalRead(tasterAPin)== HIGH)
 {
 Zeit = millis();
 digitalWrite(ledRot, HIGH); // Rote LED auf High-Pegel (5V)
 digitalWrite(ledGelb, HIGH); // Gelbe LED auf High-Pegel (5V)
 digitalWrite(ledGruen, HIGH); // Grüne LED auf High-Pegel (5V)
 Zeit = millis();
 }
 else if((millis() - Zeit) > interval)
 {
 digitalWrite(ledRot, LOW); // Rote LED auf Low-Pegel (5V)
 digitalWrite(ledGelb, LOW); // Gelbe LED auf High-Pegel (5V)
 digitalWrite(ledGruen, LOW); // Grüne LED auf High-Pegel (5V)
 Zeit = millis();
 }
 if(digitalRead(tasterBPin)== HIGH){
 digitalWrite(ledRot, HIGH); // Rote LED auf High-Pegel (5V)
 digitalWrite(ledGelb, HIGH); // Gelbe LED auf High-Pegel (5V)
 digitalWrite(ledGruen, HIGH); // Grüne LED auf High-Pegel (5V)
 Zeit = millis(); 
 }
 else if((millis() - Zeit) > interval)
 {
 digitalWrite(ledRot, LOW); // Rote LED auf High-Pegel (5V)
 digitalWrite(ledGelb, LOW); // Gelbe LED auf High-Pegel (5V)
 digitalWrite(ledGruen, LOW); // Gelbe LED auf High-Pegel (5V)
 Zeit = millis();
 }
}

Hier:

else if((millis() - Zeit) > interval)
 {
 digitalWrite(ledRot, LOW); // Rote LED auf High-Pegel (5V)
 digitalWrite(ledGelb, LOW); // Gelbe LED auf High-Pegel (5V)
 digitalWrite(ledGruen, LOW); // Gelbe LED auf High-Pegel (5V)
 Zeit = millis();
 }

schaltest Du die Leds nach dem Ablauf der Zeit IMMER aus. Das musst Du noch mit deiner 'Dauerfunktion verknüpfen. D.h. das if muss als zusätzlich Bedingung enthalten, dass die Dauerlichtfunktion nicht aktiv ist.

P.S. Kommentare sind gut und wichtig. Aber bei copy und paste sollten auch die Kommentare angepasst werden. Falsche Kommentare sind fast noch schlimmer als gar keine.

Ok, aber wie mache ich das jetzt genau ?
Wahrscheinlich in etwas so, allerdings kriege ich da ne Fehlermeldung:

 else if((millis() - Zeit) > interval)) && ((digitalRead(tasterCPin)== LOW))
 {
 digitalWrite(ledRot, LOW); // Rote LED auf Low-Pegel (5V)
 digitalWrite(ledGelb, LOW); // Gelbe LED auf Low-Pegel (5V)
 digitalWrite(ledGruen, LOW); // Grüne LED auf Low-Pegel (5V)
 Zeit = millis();

P.S. Mit den Kommentaren hast du völlig recht, werde ich in Zukunft besser drauf achten.

schau mal wie du die Klammern gesetzt hast :wink:

Ardu83:
Ich habe es schon mit if und while probiert und das funktioniert auch, nur funktionert der restliche Code dann nicht mehr so wie vorher.

Meine Empfehlung wäre eine Statemaschine, damit kannst du die verschiedenen Zustände besser abbilden.
Zwischenablage02.jpg

So habe ich das jetzt verstanden:
Du hast 3 Zustände: Aus, 10sek Leuchten, Dauerlicht.

Mit Taste A oder B wechselst du in den Modus "10sek", nochmal drücken startet den Modus neu, wenn der Timer abgelaufen ist, gehts zurück nach "Aus".
Mit Taste C wechselst du in den Modus "Dauer". (Frage: Wie kommt man da wieder raus?)

Also Code könnte das so aussehen:

const byte Aus = 0;    //   Namen für die Stati
const byte ZehnSek = 1;
const byte Dauer = 2;
byte Status = Aus;  // Statusvariable für Statemachine


void setup() {
  // bla bla
}

void loop() {
  // Tastenabfragen
  bool TasteA = digitalRead(tasterAPin);
  bool TasteB = digitalRead(tasterBPin);
  bool TasteC = digitalRead(tasterCPin);

  // Statemachine:
  switch (Status) {
    case Aus:
      digitalWrite(ledRot, LOW); // LEDs aus
      digitalWrite(ledGelb, LOW);
      digitalWrite(ledGruen, LOW);
      if (TasteA || TasteB) Status = ZehnSek;
      if (TasteC )          Status = Dauer;
      break;
    case ZehnSek:
      static unsigned long Startzeit;  // Merker für die Startzeit
      static bool neuAufruf = true;    // zum initialisieren der Zeit
      if (neuAufruf) {                 // bei neuem Aufruf Startzeit merken
        Startzeit = millis();
        neuAufruf = false;
      }
      digitalWrite(ledRot, HIGH); // LEDs an
      digitalWrite(ledGelb, HIGH);
      digitalWrite(ledGruen, HIGH);
      if (millis() - Startzeit > 10000) { // 10 Sek vorbei?
        Status = Aus;                   // dann Statuswechsel nach Aus
      }
      if (TasteA || TasteB) neuAufruf = true;  // nochmal A oder B: Startzeit Reset
      if (TasteC )          Status = Dauer;
      break;
    case Dauer:
      digitalWrite(ledRot, HIGH); // LEDs an
      digitalWrite(ledGelb, HIGH);
      digitalWrite(ledGruen, HIGH);
      break;
    default:
      Status = Aus;
      break;
  } // end Statemachine.
}

Vorteil: du kannst in jedem Status einzeln festlegen, wie auf welchen Input reagiert wird, und es wird auch bei vielen unterschiedlichen Stati nicht unübersichtlich.
Aber immer: Vorher genau überlegen und aufmalen, was genau passieren soll.

So, ich habe es jetzt geschafft, die Klammern richtig zu setzen.
Jetzt funktioniert das so, wie ich es mir gedacht habe.
Schon mal vielen Dank dafür.

Das mit der Statemaschine ist auch eine interessante Idee.
Habe deinen "Beispielcode" mal angepasst und er läuft schon mal sehr gut.
Allerdings besteht hier dann wirklich das Problem, das ich den Dauermodus nicht mehr
verlassen kann.
Könnte man das Problem so lösen, das man statt eines Tasters einen Schalter verwendet ?
Also wenn der Schalter auf "Ein" geschaltet wird, ist der Dauermodus aktiv und wenn er auf "Aus" geschaltet wird, verlässt man den Dauermodus wieder.
Falls das so funktionieren würde, gibt es eine möglichkeit, den Dauermodus auch Zeitgesteuert wieder zu verlassen ?

Ich stelle mir das so vor:

-Der Schalter für den Dauermodus wird auf "Ein" geschaltet
-Dauermodus wird aktiviert und erst wieder beendet wenn:

  1. Schalter wieder auf "Aus" geschaltet wird
    -oder-
  2. Die Zeit von ca. 1 Minute abgelaufen ist.

Ardu83:
Allerdings besteht hier dann wirklich das Problem, das ich den Dauermodus nicht mehr
verlassen kann.
Könnte man das Problem so lösen, das man statt eines Tasters einen Schalter verwendet ?
Also wenn der Schalter auf "Ein" geschaltet wird, ist der Dauermodus aktiv und wenn er auf "Aus" geschaltet wird, verlässt man den Dauermodus wieder.
Falls das so funktionieren würde, gibt es eine möglichkeit, den Dauermodus auch Zeitgesteuert wieder zu verlassen ?

Ich stelle mir das so vor:

-Der Schalter für den Dauermodus wird auf "Ein" geschaltet
-Dauermodus wird aktiviert und erst wieder beendet wenn:

  1. Schalter wieder auf "Aus" geschaltet wird
    -oder-
  2. Die Zeit von ca. 1 Minute abgelaufen ist.

Du kannst das alles machen. Alle diese Optionen sind möglich, und viele andere auch.

Überleg dir, was du WILLST, zeichne die entsprechenden Pfeile in die Skizze der Statemachine ein, und beschrifte sie.
Dann baust du genau diese Bedingung(en) in den Code ein.

Leider kann ich mich unter meinem bisherigen Usernamen (Ardu83) nicht mehr einloggen, deswegen poste ich mal auf diesem Weg.

Ich habe jetzt versucht, den Code soweit anzupassen, das ich folgendes Ergebnis bekomme:

-TasterC schaltet wie bisher auch in den Dauermodus
-Dann läuft eine Wartezeit von 60 Sekunden ab
-Die Wartezeit soll durch ein erneutes Drücken von TasteC unterbrochen werden können

Leider funktioniert das noch nicht so wie gedacht, die Zeilt von 60 Sekunden läuft nicht ab, die LEDs bleiben einfach immer an.
Einschalten des Dauermodus funktioniert, genauso wie das ausschalten, nur manchmal.

Wenn sich jemand mal den "case Dauer" genauer anschauen könnte und mir ein paar Tipps geben könnte, wäre ich sehr dankbar.

Hier mal der Code:

int ledRot = 10;
int ledGelb = 11;
int ledGruen = 13;
int tasterAPin = 7;     // Taster-A-Pin 7
int tasterBPin = 8;     // Taster-B-Pin 8
int tasterCPin = 6;     // Taster-C-Pin 6
const byte Aus = 0;    //   Namen für die Stati
const byte ZehnSek = 1;
const byte Dauer = 2;
byte Status = Aus;  // Statusvariable für Statemachine


void setup() {
 pinMode(ledRot, OUTPUT);
 pinMode(ledGelb, OUTPUT);
 pinMode(ledGruen, OUTPUT);
 pinMode(tasterAPin, INPUT);
 pinMode(tasterBPin, INPUT);
 pinMode(tasterCPin, INPUT);
}

void loop() {
  // Tastenabfragen
  bool TasteA = digitalRead(tasterAPin);
  bool TasteB = digitalRead(tasterBPin);
  bool TasteC = digitalRead(tasterCPin);

  // Statemachine:
  switch (Status) {
    case Aus:
      digitalWrite(ledRot, LOW); // LEDs aus
      digitalWrite(ledGelb, LOW);
      digitalWrite(ledGruen, LOW);
      if (TasteA || TasteB) Status = ZehnSek;
      if (TasteC )          Status = Dauer;
      break;
    case ZehnSek:
      static unsigned long Startzeit;  // Merker für die Startzeit
      static bool neuAufruf = true;    // zum initialisieren der Zeit
      if (neuAufruf) {                 // bei neuem Aufruf Startzeit merken
        Startzeit = millis();
        neuAufruf = false;
      }
      digitalWrite(ledRot, HIGH); // LEDs an
      digitalWrite(ledGelb, HIGH);
      digitalWrite(ledGruen, HIGH);
      if (millis() - Startzeit > 10000) { // 10 Sek vorbei?
        Status = Aus;                   // dann Statuswechsel nach Aus
      }
      if (TasteA || TasteB) neuAufruf = true;  // nochmal A oder B: Startzeit Reset
      if (TasteC )          Status = Dauer;
      break;
   case Dauer:
      static unsigned long Startzeit2;
      Startzeit2 = millis();
      digitalWrite(ledRot, HIGH); // LEDs an
      digitalWrite(ledGelb, HIGH);
      digitalWrite(ledGruen, HIGH);
      if (millis() - Startzeit2 > 60000) { // 60 Sek vorbei?
      Status = Aus;                     // dann Statuswechsel nach Aus
      }
      if (TasteC )          Status = Aus;
      break;
    default:
      Status = Aus;
      break;
  } // end Statemachine.
}

Du möchstest also dass nach 60 sek "Dauer" wieder verlassen wird.
Das ist ja das gleiche wie in "ZehnSek", nur eben länger.

Also vergleiche doch mal die Unterschiede.

Tip: in Dauer wird in jedem Durchlauf Startzeit2 neu gesetzt.

So ganz steige ich da noch nicht durch...

Ich will einfach nur, das in "Dauer" die Zeit von 60 Sekunden abläuft, kein Reset der Zeit durch erneutes Tasterdrücken.
In diesem Fall sollte es doch kein Problem sein, wenn in jedem Durchlauf Startzeit2 neu gesetzt wird - oder ?
Ein erneutes Drücken von Taster C soll dann die die Wartezeit von 60 Sekunden unterbrechen und dann auf den case Aus schalten.

Aber ich sehe wohl den Wald vor lauter Bäumen nicht.

Wie soll denn die Differenz millis()-Startzeit2 jemals > 60000 werden, wenn Du sie in jedem Durchlauf mit Startzeit2=millis() wieder zu 0 setzt?

Ein guter Tip - aber wenn ich diesen Teil - Startzeit2=millis() - weglasse, dann funktioniert das einmal, und dann schaltet TasterC die LEDs nur ein, wenn ich denn Taster gedrückt halte.
Und nicht wie vorher für 60 Sekunden.

Du musst die Startzeit setzen, wenn Du in diesen Zustand wechselst. Also jeweils hier:

      if (TasteC )          Status = Dauer;

wird zu:

      if (TasteC )  { Startzeit2=millis(); Status = Dauer ; }

Oder, du machst das wie oben in ZehnSek:

      static bool neuAufruf = true;    // zum initialisieren der Zeit
      if (neuAufruf) {                 // bei neuem Aufruf Startzeit merken
        Startzeit = millis();
        neuAufruf = false;
      }

Du fügst einen Programmteil ein, der immer nur beim ersten Mal durchlaufen wird.

Das Setzen beim Druck auf tasteC hat den Nachteil, dass es dann überalle eingesetzt werden muss, wo TasteC gedrückt wird, und dass PRogrammteile, die zum State dauer gehören, plötzlich wo anders stehen.
Das ist dem Beispiel nicht wichtig, aber bei größeren Statemachines verliert man da schnell den Überblick.

Aber nicht vergessen, das Flag beim Verlassen des Zustands 'Dauer' wieder auf 'true' zu setzen :wink:

Wenn man schon eine State-Machine hat, kann man dafür auch einen Zwischenzustand definieren, dann kann man sich das Flag sparen:

...
   if (TasteC )          Status = IniDauer;
...

case Inidauer:
   Startzeit = millis();
   Status = Dauer;
   break;
case Dauer:

Es gibt immer viele Wege die nach Rom führen .... :smiley: :smiley: