LED geht "von alleine" an, obwohl dies nicht gewünscht ist

Hallo,

habe vor kurzem ein kleines Anfängerprojekt beendet.

Hauptfunktion:

Per Taster A und B soll eine LED "x" Minuten eingeschaltet werden können.

Ein Druck auf Taster B verlängert die Leuchtdauer der LED um X Minuten, wenn das Licht von Taster A eingeschaltet wurde (LED geht kurz aus und bleibt im Anschluss daran an).

Ein weiterer Druck auf Taster A, während Licht von Taster A eingeschaltet wurde, schaltet die LED aus.

Sekundärfunktionalität:

  1. Pro Taster individuell einstellbare Leuchtdauer
  2. Unendliche Prozessorlaufzeit
  3. Watchdog Timer (zur Sicherheit)

Mein Problem ist nun, dass sich die LED ohne das jmd. einen Taster gedrückt hätte ab und an von alleine einschaltet (ca. alle 2-3 Tage).

Trotz mehrfacher Durchsicht des Codes kann ich keine Fehler entdecken. Da ich mich jedoch noch immer zu den blutigen Anfängern zähle, möchte ich Programmierfehler in keinster Weise ausschließen.

Über sachdienliche Hinweise bin ich also sehr dankbar. Gerne auch Hinweise auf grundsätzliche Fehler- immer drauf. :wink:

Gruß Chris

#include <Bounce.h>
#include <avr/wdt.h>                                                                  // Watchdog Timer Library

/* Taster gegen Masse. */

int button_z = 2;
int button_c  =  3;
int led  =  4;

int urzustand_button_z;
int val_button_z;
unsigned long button_z_zaehler = 0;
Bounce button_z_Bouncer = Bounce(button_z,10 );                                       // 10 ms Bouncing für "button_z"
boolean druck_z = 0;

int urzustand_button_c;
int val_button_c;
unsigned long button_c_zaehler = 0;
Bounce button_c_Bouncer = Bounce(button_c,10 );                                       // 10 ms Bouncing für "button_c"
boolean druck_c = 0;

unsigned long druckzeitpunkt = 0;
int ledpause = 340;

unsigned long leuchtdauer_z = 300000;
unsigned long leuchtdauer_c = 300000;

int fall = 1; // 1                                                                    // Falldeklaration

/**************************************************************************************************************************************************************************************************************************************************************/

void setup()
{
  wdt_enable(WDTO_2S);                                                                // Watchdog Timer Initialisierung

  pinMode(button_z,  INPUT_PULLUP);

  pinMode(button_c,  INPUT_PULLUP);

  urzustand_button_z = HIGH;
  urzustand_button_c = HIGH;

  pinMode(led, OUTPUT);
  digitalWrite(led,LOW);

  delay(100);

  Serial.begin(9600);
  Serial.println("Schaltung betriebsbereit");
}

/**************************************************************************************************************************************************************************************************************************************************************/
 
void loop()
{
  
wdt_reset();                                                                           // Watchdog Timer "Rückmeldung"

int rollovers = millisRollover();                                                      // millis()-Überlaufbremse

switch(fall)
{
  
case 1:

button_z_Bouncer.update();
val_button_z = button_z_Bouncer.read();

if (val_button_z != urzustand_button_z)
{
  if (val_button_z == LOW)
  {
    digitalWrite(led,HIGH);
    druckzeitpunkt=millis();
    Serial.println("Taster Z wurde gedrückt- LED eingeschaltet");
    fall=2;
  }
}

urzustand_button_z = val_button_z;

button_c_Bouncer.update();
val_button_c = button_c_Bouncer.read();

if (val_button_c != urzustand_button_c)
{
  if (val_button_c == LOW)
  {
  digitalWrite(led,HIGH);
  druckzeitpunkt=millis();
  Serial.println("Taster C wurde gedrückt- LED eingeschaltet");
  fall=3;
  }
}

urzustand_button_c = val_button_c;

break;

case 2:

button_z_Bouncer.update();
val_button_z = button_z_Bouncer.read();

if (val_button_z != urzustand_button_z)
{
  if (val_button_z == LOW)
  {
    digitalWrite(led,LOW);
      Serial.println("Taster Z wurde erneut gedrückt- LED wurde wieder ausgeschaltet.");
      fall=1;
  }
}

urzustand_button_z = val_button_z;

button_c_Bouncer.update();
val_button_c = button_c_Bouncer.read();

if (val_button_c != urzustand_button_c)
{
  if (val_button_c == LOW)
  {
  digitalWrite(led,LOW);
  druckzeitpunkt=millis();
  digitalWrite(led,LOW);
  Serial.println("Taster C wurde gedrückt- LED wurde kurz ausgeschaltet..");
  delay(ledpause);
  digitalWrite(led,HIGH);
  Serial.println("LED eingeschaltet");
  fall=3;
  }
}

urzustand_button_c = val_button_c;

if(millis() - druckzeitpunkt > leuchtdauer_z)
{
  digitalWrite(led,LOW);
  Serial.println("LED wurde aufgrund Ueberschreitung der Leuchtdauer z ausgeschaltet");
  fall=1;
  break;
}

break;

case 3:

button_z_Bouncer.update();
val_button_z = button_z_Bouncer.read();

if (val_button_z != urzustand_button_z)
{
  if (val_button_z == LOW)
  {
    digitalWrite(led,LOW);
    druckzeitpunkt=millis();
    digitalWrite(led,LOW);
    Serial.println("Taster Z wurde gedrückt- LED wurde kurz ausgeschaltet..");
    delay(ledpause);
    digitalWrite(led,HIGH);
    Serial.println("LED eingeschaltet");
    fall=2;
  }
}

urzustand_button_z = val_button_z;

button_c_Bouncer.update();
val_button_c = button_c_Bouncer.read();

if (val_button_c != urzustand_button_c)
{
  if (val_button_c == LOW)
  {
    digitalWrite(led,LOW);
    Serial.println("Taster C wurde erneut gedrückt- LED wird wieder ausgeschaltet.");
    fall=1;
  }
}

urzustand_button_c = val_button_c;

if(millis() - druckzeitpunkt > leuchtdauer_c)
{
  digitalWrite(led,LOW);
  Serial.println("LED wurde aufgrund Ueberschreitung der Leuchtdauer c ausgeschaltet");
  fall=1;
  break;
}

break;

 }
}

int millisRollover()
{
  static int numRollovers=0; // variable that permanently holds the number of rollovers since startup
  static boolean readyToRoll = false; // tracks whether we've made it halfway to rollover
  unsigned long now = millis(); // the time right now
  unsigned long halfwayMillis = 2147483647; // this is halfway to the max millis value (17179868 for earlier versions of Arduino)

  if (now > halfwayMillis) { // as long as the value is greater than halfway to the max
    readyToRoll = true; // you are ready to roll over
  }

  if (readyToRoll == true && now < halfwayMillis) {
    // if we've previously made it to halfway
    // and the current millis() value is now _less_ than the halfway mark
    // then we have rolled over
    numRollovers++; // add one to the count the number of rollovers
    readyToRoll = false; // we're no longer past halfway
  } 
  return numRollovers;
}

Ohne jetzt den Code im Detail gelesen zu haben, tippe ich erst mal stark darauf, das ein Timer-Register "überläuft" und es dadurch zu einem "Rest" kommt.

Keine Ahnung.

Aber ein paar Anmerkungen:

  • Alle 2-3 Tage ist zu kurz für einen Überlauf von millis()
  • Zu was ist deine rollover- Akrobatik gut ? Wenn du mit allen Intervallen im Bereich eines uint32_t bleibst ( also < 49 Tage ),
    ist ein Überlauf vollkommen egal.
  • Was passiert denn genau, und welche der Test-Ausschriebe siehst du ?
  • Probleme löst man, indem man vermutlich irrelevantes (erstmal) weg lässt. ( z.B. den watchdog ?)

Ich tippe auf ein Ram-Problem.

Auch wenn es nicht so aussieht vebraucht

Serial.println("Taster C wurde erneut gedrückt- LED wird wieder ausgeschaltet.");

RAM da der Text vom Flash-Speicher, wo der Sketch abgespeichert ist, in das RAM gespeichert wird und dann erst auf die Serielle Schnittstelle ausgegeben wird. Mit der Zeit Ist das RAM verbraucht und der Arduino resetiert (im besten Fall).

Benutze die F() Syntax:
http://forum.arduino.cc/index.php?topic=127552.msg965539#msg965539

Bei ordentlicher Programmierung braucht es den Watchdog nicht und dieser bereitet generell mehr Probleme als er nützlich ist.

Grüße Uwe

Na, das ist doch was. :slight_smile:

Vielen Dank für die vielen Anregungen.

Werd mich zunächst einmal durch "weglassen" dem Problem nähren.

Gruß Chris

Hallo,

habe mittlerweile sämtliche "Serial.println-Zeilen" auskommentiert und sowohl den Watchdog als auch die Überlaufakrobatik entfernt.

Am Verhalten hat sich leider nichts geändert.

Kann jmd. einen weiteren Fehler im Code (s.o.) entdecken?

Wenn die LED ungewollt angegangen ist und ich drücke dann einen Taster, geht sie übrigens kurz aus und dann wieder an (und geht dann nach Ablauf von "leuchtdauer" wieder aus).

Gruß Chris

Bitte sende uns den Sketch wo Du alles auskommentiert und beseitigt hast und der noch nicht funktioniert.
Grüße Uwe

Chris72622:
Per Taster A und B soll eine LED "x" Minuten eingeschaltet werden können.

Ein Druck auf Taster B verlängert die Leuchtdauer der LED um X Minuten, wenn das Licht von Taster A eingeschaltet wurde (LED geht kurz aus und bleibt im Anschluss daran an).

Ein weiterer Druck auf Taster A, während Licht von Taster A eingeschaltet wurde, schaltet die LED aus.

So verstehe ich das:

Taster A ist ein manueller Einschalter für eine LED
Taster B ist ein manueller Einschalter für dieselbe LED
Nur Taster A kann als Ausschalter für die LED dienen, aber nur, falls das Einschalten ebenfalls durch Taster A erfolgte
Die Ausschaltung erfolgt ohne Betätigung eines Tasters auch nach einem Zeitablauf, und zwar je nachdem, über welchen Taster die LED eingeschaltet wurde:
Taster A ==> Ausschalten nach Zeitdauer A
Taster B ==> Ausschalten nach Zeitdauer B
Taster B kann nach dem Einschalten die Einschaltdauer noch verändern, und zwar wenn bei eingeschalteter LED der Taster B betätigt wird, fängt die Zeitdauer B neu an zu laufen, nach der die LED abgeschaltet wird

Rückfrage 1:
Habe ich das so richtig verstanden?

Chris72622:
Über sachdienliche Hinweise bin ich also sehr dankbar.

Dein Code ist viel zu unübersichtlich und zu lang und kaum gegliedert (z.B. in kurze, übersichtliche Funktionen).

EVA-Prinzip nicht beachtet: Keine Trennung von Eingabe-Verarbeitung-Ausgabe in Deinem Code erkennbar, alles ist spaghettimäßig miteinander verknüpft.

Ein Watchdog-Reset ist völlig ungeeignet, um fehlerhaft programmierte Programme zum Funktionieren zu bringen.

Rückfrage 2:
Ist Deine Schaltung auf einen Breadboard mit ausschließlich kurzen Leitungen (max. 20 cm zu den Tastern und LEDs) aufgebaut oder hast Du womöglich irgendwo lange Leitungen verlegt und Fehlfunktionen können möglicherweise nicht nur durch die Software, sondern auch durch aufgefangene Überspannungsimpulse auf den langen Leitungen verursacht sein?

jurs:
Habe ich das so richtig verstanden?

Ja. Taster A und Taster B können jeweils die LED ein- und ausschalten.

Wurde die LED jedoch mit A eingeschaltet und es wird B gedrückt, beginnt die Leuchtdauer erneut (leuchtdauer_z).

Wurde die LED mit B eingeschaltet und es wird A gedrückt, beginnt die Leuchtdauer erneut (leuchtdauer_c).

jurs:
Dein Code ist viel zu unübersichtlich und zu lang und kaum gegliedert (z.B. in kurze, übersichtliche Funktionen).

Hast Du mir hierfür evtl. ein Beispiel, damit ich das besser verstehen kann?

jurs:
EVA-Prinzip nicht beachtet: Keine Trennung von Eingabe-Verarbeitung-Ausgabe in Deinem Code erkennbar, alles ist spaghettimäßig miteinander verknüpft.

Auch hier bitte falls irgendwie möglich etwas genauer beschreiben wie Du das meinst.

jurs:
Ein Watchdog-Reset ist völlig ungeeignet, um fehlerhaft programmierte Programme zum Funktionieren zu bringen.

So war das auch nicht gedacht. Ich wollte vielmehr erreichen, dass sich der Prozessor von alleine neu startet, solle er sich aufhängen.

jurs:
Rückfrage 2:
Ist Deine Schaltung auf einen Breadboard mit ausschließlich kurzen Leitungen (max. 20 cm zu den Tastern und LEDs) aufgebaut oder hast Du womöglich irgendwo lange Leitungen verlegt und Fehlfunktionen können möglicherweise nicht nur durch die Software, sondern auch durch aufgefangene Überspannungsimpulse auf den langen Leitungen verursacht sein?

Ja, meine Schaltung ist nicht auf einem Breadboard aufgebaut, sondern beinhaltet folgenden Kabellängen:

Prozessor - Taster A: Ca. 10cm
Prozessor - Taster B: Ca. 230cm
Prozessor - LED: Ca. 250cm

Hier der von uwefed angefragte Code:

#include <Bounce.h>
//#include <avr/wdt.h>                                                                  // Watchdog Timer Library

/* Taster gegen Masse. */

int button_z = 2;
int button_c  =  3;
int led  =  4;

int urzustand_button_z;
int val_button_z;
unsigned long button_z_zaehler = 0;
Bounce button_z_Bouncer = Bounce(button_z,10 );                                       // 10 ms Bouncing für "button_z"
boolean druck_z = 0;

int urzustand_button_c;
int val_button_c;
unsigned long button_c_zaehler = 0;
Bounce button_c_Bouncer = Bounce(button_c,10 );                                       // 10 ms Bouncing für "button_c"
boolean druck_c = 0;

unsigned long druckzeitpunkt = 0;
int ledpause = 340;

unsigned long leuchtdauer_z = 300000;
unsigned long leuchtdauer_c = 300000;

int fall = 1; // 1                                                                    // Falldeklaration

/**************************************************************************************************************************************************************************************************************************************************************/

void setup()
{
//  wdt_enable(WDTO_2S);                                                                // Watchdog Timer Initialisierung

  pinMode(button_z,  INPUT_PULLUP);

  pinMode(button_c,  INPUT_PULLUP);

  urzustand_button_z = HIGH;
  urzustand_button_c = HIGH;

  pinMode(led, OUTPUT);
  delay(1000);
  digitalWrite(led,LOW);
  
//  Serial.begin(9600);
//  Serial.println("Schaltung betriebsbereit");
}

/**************************************************************************************************************************************************************************************************************************************************************/
 
void loop()
{
  
//wdt_reset();                                                                           // Watchdog Timer "Rückmeldung"

//int rollovers = millisRollover();                                                      // millis()-Überlaufbremse

switch(fall)
{
  
case 1:

button_z_Bouncer.update();
val_button_z = button_z_Bouncer.read();

if (val_button_z != urzustand_button_z)
{
  if (val_button_z == LOW)
  {
    digitalWrite(led,HIGH);
    druckzeitpunkt=millis();
//    Serial.println("Taster Z wurde gedrückt- LED eingeschaltet");
    fall=2;
  }
}

urzustand_button_z = val_button_z;

button_c_Bouncer.update();
val_button_c = button_c_Bouncer.read();

if (val_button_c != urzustand_button_c)
{
  if (val_button_c == LOW)
  {
  digitalWrite(led,HIGH);
  druckzeitpunkt=millis();
//  Serial.println("Taster C wurde gedrückt- LED eingeschaltet");
  fall=3;
  }
}

urzustand_button_c = val_button_c;

break;

case 2:

button_z_Bouncer.update();
val_button_z = button_z_Bouncer.read();

if (val_button_z != urzustand_button_z)
{
  if (val_button_z == LOW)
  {
    digitalWrite(led,LOW);
//      Serial.println("Taster Z wurde erneut gedrückt- LED wurde wieder ausgeschaltet.");
      fall=1;
  }
}

urzustand_button_z = val_button_z;

button_c_Bouncer.update();
val_button_c = button_c_Bouncer.read();

if (val_button_c != urzustand_button_c)
{
  if (val_button_c == LOW)
  {
  digitalWrite(led,LOW);
  druckzeitpunkt=millis();
  digitalWrite(led,LOW);
//  Serial.println("Taster C wurde gedrückt- LED wurde kurz ausgeschaltet..");
  delay(ledpause);
  digitalWrite(led,HIGH);
//  Serial.println("LED eingeschaltet");
  fall=3;
  }
}

urzustand_button_c = val_button_c;

if(millis() - druckzeitpunkt > leuchtdauer_z)
{
  digitalWrite(led,LOW);
//  Serial.println("LED wurde aufgrund Ueberschreitung der Leuchtdauer z ausgeschaltet");
  fall=1;
  break;
}

break;

case 3:

button_z_Bouncer.update();
val_button_z = button_z_Bouncer.read();

if (val_button_z != urzustand_button_z)
{
  if (val_button_z == LOW)
  {
    digitalWrite(led,LOW);
    druckzeitpunkt=millis();
    digitalWrite(led,LOW);
//    Serial.println("Taster Z wurde gedrückt- LED wurde kurz ausgeschaltet..");
    delay(ledpause);
    digitalWrite(led,HIGH);
//    Serial.println("LED eingeschaltet");
    fall=2;
  }
}

urzustand_button_z = val_button_z;

button_c_Bouncer.update();
val_button_c = button_c_Bouncer.read();

if (val_button_c != urzustand_button_c)
{
  if (val_button_c == LOW)
  {
    digitalWrite(led,LOW);
//    Serial.println("Taster C wurde erneut gedrückt- LED wird wieder ausgeschaltet.");
    fall=1;
  }
}

urzustand_button_c = val_button_c;

if(millis() - druckzeitpunkt > leuchtdauer_c)
{
  digitalWrite(led,LOW);
//  Serial.println("LED wurde aufgrund Ueberschreitung der Leuchtdauer c ausgeschaltet");
  fall=1;
  break;
}

break;

 }
}

/*int millisRollover()
{
  static int numRollovers=0; // variable that permanently holds the number of rollovers since startup
  static boolean readyToRoll = false; // tracks whether we've made it halfway to rollover
  unsigned long now = millis(); // the time right now
  unsigned long halfwayMillis = 2147483647; // this is halfway to the max millis value (17179868 for earlier versions of Arduino)

  if (now > halfwayMillis) { // as long as the value is greater than halfway to the max
    readyToRoll = true; // you are ready to roll over
  }

  if (readyToRoll == true && now < halfwayMillis) {
    // if we've previously made it to halfway
    // and the current millis() value is now _less_ than the halfway mark
    // then we have rolled over
    numRollovers++; // add one to the count the number of rollovers
    readyToRoll = false; // we're no longer past halfway
  } 
  return numRollovers;
}*/

Chris72622:
Prozessor - Taster B: Ca. 230cm

Bei dieser Kabellänge den Taster B mit dem internen PullUp zu beschalten ist einigermaßen fahrlässig. Die internen PullUp Widerstände sind sehr schwache PullUps in der Größenordnung von 20 bis 50 KOhm. Bei einer Antennenlänge von 2,30 m reichen schon elektrische Felder in der Nähe, um den Signalpegel auf der Leitung zum Schwabbeln zu bringen, sagen wir mal jemand schaltet zwei Meter daneben einen Staubsauger mit mangelhafter Funkentstörung ein oder aus.

Wie äußert sich denn die Fehlschaltung bei Dir? Das Licht geht nach 2-3 Tagen irgendwann an und überhaupt nicht mehr aus? Das wäre in jedem Fall ein Programmfehler. Auch bei erkannten Fehlschaltungen an Taster B, die durch elektrische Störungen wegen des schwachen internen PullUps an der langen Zuleitung verursacht werden, sollte das Licht ja nach Zeitablauf wieder ausschalten.

Also bei 2,3m Zuleitung zum Taster B würde ich schon eher einen PullUp von typischerweise 4,7 KOhm spendieren und nicht mehr den internen PullUp verwenden.

Wegen eines Programmbeispiels mache ich mir mal Gedanken. Vielleicht morgen.

jurs:
Bei dieser Kabellänge den Taster B mit dem internen PullUp zu beschalten ist einigermaßen fahrlässig. Die internen PullUp Widerstände sind sehr schwache PullUps in der Größenordnung von 20 bis 50 KOhm. Bei einer Antennenlänge von 2,30 m reichen schon elektrische Felder in der Nähe, um den Signalpegel auf der Leitung zum Schwabbeln zu bringen, sagen wir mal jemand schaltet zwei Meter daneben einen Staubsauger mit mangelhafter Funkentstörung ein oder aus. Also bei 2,3m Zuleitung zum Taster B würde ich schon eher einen PullUp von typischerweise 4,7 KOhm spendieren und nicht mehr den internen PullUp verwenden.

Klingt plausibel. Danke fürs "Augen öffnen". Hab da evtl. etwas zu schwäbisch gedacht, als ich beschloss an dieser Stelle keinen zusätzlichen Widerstand zu verbauen.

jurs:
Wie äußert sich denn die Fehlschaltung bei Dir? Das Licht geht nach 2-3 Tagen irgendwann an und überhaupt nicht mehr aus? Das wäre in jedem Fall ein Programmfehler. Auch bei erkannten Fehlschaltungen an Taster B, die durch elektrische Störungen wegen des schwachen internen PullUps an der langen Zuleitung verursacht werden, sollte das Licht ja nach Zeitablauf wieder ausschalten.

Wenn ich bemerkt habe, dass die LED ungewollt angegangen ist und ich dann Taster A drücke um sie auzuschalten, geht die LED kurz aus (int ledpause) und dann wieder an. Die LED zeigt somit das selbe Verhalten wie wenn sie von Taster B aus eingeschaltet worden wäre. Ob die LED ohne meinen Druck auf einen der beiden Taster nach drei Minuten von alleine wieder aus gehen würde, habe ich bisher nicht getestet, werde dies aber nachholen.

Kann also davon ausgegangen werden, dass die "Antennentheorie" zutrifft, wenn sich die LED beim bemerken eines fehlerhaften Einschaltens nach max. drei Minuten wieder "von alleine" ausschaltet?

Was aber, wenn sie nicht von alleine aus geht?

jurs:
Wegen eines Programmbeispiels mache ich mir mal Gedanken.

Wäre echt super. Wollte den Code über die einzelnen "case-Fälle" aus meiner Sicht extra ordentlich strukturieren und dachte, das wäre die übersichtlichste Lösung.

Gruß Chris

Chris72622:
Kann also davon ausgegangen werden, dass die "Antennentheorie" zutrifft, wenn sich die LED beim bemerken eines fehlerhaften Einschaltens nach max. drei Minuten wieder "von alleine" ausschaltet?

Kann sein, kann auch nicht sein: Im Endeffekt kann ja innerhalb der drei Minuten schon wieder ein neuer Fehlimpuls am Taster-B Kabel auftreten, der die Ausschaltdauer nochmal um drei Minuten verlängert. Ich stell mir gerade mal vor: Irgendwo im Umkreis von einigen Metern hat jemand ein eingeschaltetes Bügeleisen, und der Bimetall-Temperaturregler schaltet die Heizwicklung alle zwei Minuten ein und aus und jedesmal gibt es eine Störung auf das "Antennenkabel" zum Taster B.

Anbei mal ein Codevorschlag von mir.

Braucht eine Library weniger.
Braucht trotzdem weniger Codezeilen als Dein Sketch.
Watchdog ist nicht drin. Wenn Code fehlerfrei läuft, läuft er auch ohne Watchdog.
Und ich habe noch zusätzlich ein "Lebendigkeitsblinken" mit der Board-LED an Pin-13 realisiert, bei dem die Board-LED immer schön regelmäßig blinken sollte, wenn der Sketch einwandfrei läuft. Wenn der Code zum Beispiel wegen logischer Fehler in einer Endlosschleife landet, bei der die loop nicht mehr ausgeführt wird, würde man das am Ausblieben des Blinkens erkennen können.

Aber wie gesagt, erstmal würde ich dem Taster-Anschluss mit dem 2,30m langen Kabel einen 4.7 KOhm Pullup-Widerstand verpassen (im Sketch dann auf INPUT initialisieren). Der mit dem kurzen Kabel angeschlossene Taster kann problemlos mit dem internen PullUp betrieben werden.

Mein Codevorschlag (ich hoffe, ich habe die merkwürdige Schaltlogik verstanden):

#define BUTTON_Z 2
#define BUTTON_C 3
#define LEUCHTDAUER_C 300000L
#define LEUCHTDAUER_Z 250000L
#define LEDPAUSE 340 // LED Pause beim Erneuern der Schaltzeit
#define SCHALTLED 4  // Zu schaltende LED
#define BLINKLED 13  // LED für das Kontrollblinken

enum ledStates{OFF, ON_BY_C, ON_BY_Z};

int ledState=OFF;


void setup() {
  Serial.begin(9600);
  pinMode(SCHALTLED,OUTPUT);
  pinMode(BLINKLED,OUTPUT);
  pinMode(BUTTON_C, INPUT_PULLUP);
  pinMode(BUTTON_Z, INPUT_PULLUP);
  Serial.println("Programm gestartet.");
}

boolean button_C_pressed()
// Abfrage Stauswechsel HIGH nach LOW von Button C
{
  static boolean lastState=HIGH;
  boolean state=digitalRead(BUTTON_C);
  if (lastState==HIGH && state==LOW) // Pegelwechsel von HIGH nach LOW
  {
    lastState=state;
    Serial.println("Button C");
    delay(50); // entprellen 50ms
    return true;
  }  
  lastState=state;
  return false;
}

boolean button_Z_pressed()
// Abfrage Stauswechsel HIGH nach LOW von Button Z
{
  static boolean lastState=HIGH;
  boolean state=digitalRead(BUTTON_Z);
  if (lastState==HIGH && state==LOW) // Pegelwechsel von HIGH nach LOW
  {
    lastState=state;
    Serial.println("Button Z");
    delay(50); // entprellen 50ms
    return true;
  }  
  lastState=state;
  return false;
}

void ledOneShortblink()
{
  Serial.println("Taster erneuert Zeitablauf");
  digitalWrite(SCHALTLED,LOW);
  delay(LEDPAUSE);
  digitalWrite(SCHALTLED,HIGH);
}

void kontrollblinken()
// Blinkt die LED alle 512ms ein und aus, beim ständigen Aufrufen aus der Loop
{
  digitalWrite(BLINKLED,(millis()/512)%2);
}


unsigned long handleCButton()
// Rückgabewert: Neue Leuchtdauer, sonst 0
{
    if (ledState==OFF) 
    { // Einschalten mit Button C und neue Leuchtdauer setzen
      ledState=ON_BY_C;
      Serial.println("Eingeschaltet mit Taster C");
      return LEUCHTDAUER_C;
    }
    else if (ledState==ON_BY_C) 
    { // Ausschalten nur wenn auch mit C eingeschaltet wurde
      ledState=OFF;
      Serial.println("Ausgeschaltet mit Taster C");
      return 0;
    }  
    else  // Sonst blinken und neue Leuchtdauer festlegen
    {
      ledOneShortblink();
      Serial.print("Neue Leuchtdauer gesetzt mit Taster C: ");
      Serial.println(LEUCHTDAUER_C/1000);
      return LEUCHTDAUER_C; 
    }  
}

unsigned long handleZButton()
{
    if (ledState==OFF) 
    { // Einschalten mit Button Z und neue Leuchtdauer setzen
      ledState=ON_BY_Z;
      Serial.println("Eingeschaltet mit Taster Z");
      return LEUCHTDAUER_Z;
    }
    else
    {
      ledOneShortblink(); // einmal kurz blinken
      Serial.print("Neue Leuchtdauer gesetzt mit Taster Z: ");
      Serial.println(LEUCHTDAUER_Z/1000);
      return LEUCHTDAUER_Z;
    }
}
  

void loop() {
  static unsigned long leuchtStart, leuchtDauer, neueLeuchtdauer;
  int lastLedState=ledState;

  if (button_C_pressed())             // Abfrage Taster C
    neueLeuchtdauer=handleCButton();  // Verarbeitung Taster C
  else if (button_Z_pressed())        // Abfrage Taster Z
    neueLeuchtdauer=handleZButton();  // Verarbeitung Taster Z
  else neueLeuchtdauer=0;          
  // Verarbeitung einer eventuell neuen Leuchtdauer 
  if (neueLeuchtdauer>0)  // Es wurde eine neue Leuchtdauer gesetzt
  {  
    leuchtStart=millis();
    leuchtDauer=neueLeuchtdauer;
  }  
  // Ausschalten nach Zeitablauf
  if (millis()-leuchtStart > leuchtDauer)
  { // Leuchtdauer ist abgelaufen
    ledState=OFF;
    if (ledState!=lastLedState) Serial.println("Zeitablauf AUS");
  }
  /* AUSGABE DER ERGEBNISSE */
  digitalWrite(SCHALTLED, ledState); // hier und nur hier wird die LED geschaltet
  kontrollblinken();  // Hier blinkt die Board-LED als "Lebendigkeitsnachweis" für den Sketch
}

Vielen Dank für den Code, jurs.

Ich konnte bisher widerstehen ihn anstatt meinem Code aufzuspielen, zumal ich (noch) nicht verstehe, was enum bedeutet, da es hier nicht zu finden ist:

Meine Reihenfolge:

  1. Warten, bis der Fehler wieder auftritt und im Anschluss die nächsten drei Minuten beobachten.
  2. Einbau des zusätzlichen Widerstands.
  3. Erneut warten auf Fehler.
  4. Testweise Deinen Code aufspielen.

Gruß Chris

Enum steht für "Enumeration":

Bei "enum ledStates{OFF, ON_BY_C, ON_BY_Z};" weist der Compiler den Namen in den Klammern jeweils die Zahlen 0, 1 und 2 zu.

Daher kann man sowas machen: int ledState=OFF;

Und if (ledState==ON_BY_C) fragt letztlich nur auf den Integer-Wert dahinter ab, aber man hat halt aussagekräftige Namen für die Zustände

Man könnte auch sowas machen:
const int OFF = 0;
const int ON_BY_C = 1;

Enums haben da den Vorteil, dass der gültige Wertebereich genau durch die Anzahl der Elemente festgelegt ist

Man kann das auch so machen:

typedef enum 
{
    OFF, 
    ON_BY_C, 
    ON_BY_Z
} ledStates;

ledStates ledState = OFF;

d.h. eine Variable von diesem enum erzeugen statt sie einem Integer zuzuweisen. Dann ist auch der Wertebereich der Variable begrenzt und du kannst nicht einfach "ledState = 5" machen.

Chris72622:
enum ledStates{OFF, ON_BY_C, ON_BY_Z};

Das ist eine fortlaufende Nummerierung von "Konstanten mit Namen", sowas ähnliches wie:
#define OFF 0
#define ON_BY_C 1
#define ON_BY_Z 2

Nur eben als Einzeiler und einer weiteren Bezeichnung ("ledStates") dran, aus der ich erkennen kann, wofür ich diese Konstanten verwenden möchte. In dem Fall dafür, wie der aktuelle LED-Zustand gesetzt ist:

  • Die LED ist aus (OFF)
  • Die LED wurde von Taster-C eingeschaltet (ON_BY_C)
  • Die LED wurde von Taster-Z eingeschaltet (ON_BY_Z)

Hallo,

der Fehler kam eben wieder. Diesmal wartete ich.. und siehe da:

Die LED ging wieder aus.

In Verbindung mit der Tatsache, dass sich, wenn der Fehler in der Vergangenheit auftrat, beim Drücken von Taster C die LED so verhalten hat, wie wenn sie durch Taster Z eingeschaltet worden wäre, komme ich zu dem Schluss, dass Ihr vollkommen recht hattet mit Eurer Vermutung.

Ich werde also demnächst den besagten Widerstand einbauen, da die zu lange Taster Z-Leitung das Problem zu sein scheint.

Gruß Chris

jurs:
..erstmal würde ich dem Taster-Anschluss mit dem 2,30m langen Kabel einen 4.7 KOhm Pullup-Widerstand verpassen (im Sketch dann auf INPUT initialisieren). Der mit dem kurzen Kabel angeschlossene Taster kann problemlos mit dem internen PullUp betrieben werden.

Da ich nur einen 2,2kOhm Widerstand hatte, hab ich notgedrungen diesen verwendet.

Wenn ich nun im Sketch anstatt pinMode(button_z, INPUT_PULLUP) das hier schreibe..

pinMode(button_z, INPUT)

..blinkt die LED die ganze Zeit, sobald der Code hochgeladen ist. Ändere ich es wieder um auf "INPUT_PULLUP" scheint zunächst Mal wieder alles zu funktionieren.

Gruß Chris

Chris72622:

jurs:
..erstmal würde ich dem Taster-Anschluss mit dem 2,30m langen Kabel einen 4.7 KOhm Pullup-Widerstand verpassen (im Sketch dann auf INPUT initialisieren). Der mit dem kurzen Kabel angeschlossene Taster kann problemlos mit dem internen PullUp betrieben werden.

Da ich nur einen 2,2kOhm Widerstand hatte, hab ich notgedrungen diesen verwendet.

Wie verwendet?

Tatsächlich als PullUp verwendet?

Oder vielleicht als PullDown verwendet?

Je nach (korrekter) Schaltung des Widerstands ist der Pin bei unbetätigtem Taster HIGH und bei betätigtem Taster LOW.
Oder genau umgekehrt.

Und bei falsch angeschlossenem Widerstand ist der Pin vielleicht sogar "schwebend" wie wenn der Widerstand gar nicht da wäre.
So what?

Wenn Du den Widerstand als PullUp angeschlossen hast und mißt mit einem Multimeter zwischen GND und Pin die Spannung, dann muss bei unbetätigtem Taster die gemessene Spannung 5V betragen und bei gedrücktem Taster 0V.

                 PullUp
                 _______
I/O Pin O--+----|_______|---- +5V
           |
           |          /
           +---------/ O----- GND
                   Taster
                 PullDown
                 _______
I/O Pin O--+----|_______|---- GND
           |
           |          /
           +---------/ O----- +5V
                   Taster

Ich habe den Widerstand einfach zwischen Taster und Input-Pin des Arduinos eingelötet. Die andere Seite des Tasters geht gegen Masse.

Da ich das Ganze "freiflugmäßig" verlötet habe (also ohne Platine), habe ich nun das Problem, das ich beim Aufbau einer ordentlichen Pullup-Schaltung ja zwangsläufig eine Stelle häte, an der ich drei Kabel miteinander verbinden müsste (was mir bisher ja erspart blieb).. hm.

Dank Dir.

Gruß Chris

Chris72622:
Ich habe den Widerstand einfach zwischen Taster und Input-Pin des Arduinos eingelötet. Die andere Seite des Tasters geht gegen Masse.

Taster vom I/O Pin gegen Masse ist korrekt.
Aber der PullUp muß natürlich vom I/O Pin gegen +5V geschaltet sein.
Siehe Schaltungsskizze in meinem oben geänderten Posting.