Programm arbeitet nach längerer Laufzeit nicht mehr richtig

Hallo,
zwei Taster am Mega schalten jeweils eine Lampe über ein Relais. Wenn man sich entfernt gehen die Lampen, gesteuert durch den Bewegungsmelder, nach 5 Minuten aus. Das funktioniert mit u.g. Programm wenn ich eine Zeit lang arbeite und mich dann entferne. Wenn ich jedoch lange Zeit arbeite (ca. ab einer Stunde)und mich dann entferne, geht das Licht nicht mehr von alleine aus. Während meiner "langen" Sitzung hat der Bewegungsmelder jedoch fast permanent geschaltet und den Zähler immer wieder neu gestartet.Ich finde den Fehler nicht :frowning: unsigned long ist doch mehr als ausreichend?
Hat da jemand eine Idee?

Danke!!

#include "Arduino.h"
#include <Bounce2.h>

int ledPin = 13;  // LED on Pin 13 of Arduino
int pirPin = 10; // Input for HC-S501
int sw1pin = 11; //Schalter 1 Lampe Mitte
int sw2pin = 12;  //Schalter 2 re und li Lampe
int rel1pin = 9; // Relais 1
int rel2pin = 8; // Relais 2
int pirValue; // Place to store read PIR Value

bool sw1_soll = 0;
int sw1_tast = 0;
bool sw1aktiv = false;

bool sw2_soll = 0;
int sw2_tast = 0;
bool sw2aktiv = false;

Bounce sw1 = Bounce();
Bounce sw2 = Bounce();

unsigned long vg_time1 = millis();
const unsigned long restlicht = 600000;

void swchange(int);

void setup() {

//Serial.begin(115200); // USB-Serielle SST
 pinMode(ledPin, OUTPUT);
 pinMode(pirPin, INPUT);
 pinMode(sw1pin,INPUT);
 pinMode(sw2pin,INPUT);
 pinMode(rel1pin,OUTPUT);
 pinMode(rel2pin,OUTPUT);

 digitalWrite(ledPin, LOW);
 digitalWrite(rel1pin, HIGH);
 digitalWrite(rel2pin, HIGH);

 sw1.attach(11);
 sw1.interval(50);

 sw2.attach(12);
 sw2.interval(50);
}

void loop() {
// Beide Lampen ausschalten wenn keine IR-Bewegung
if ((millis() - vg_time1) > restlicht) {
sw1_soll = LOW;
sw2_soll = LOW;
swchange(1);
swchange(2);
}

sw1.update();  // Taster 1
int sw1_tast = sw1.read();
if ((sw1_tast == HIGH) && (sw1aktiv== true)) {
if (sw1_soll == LOW)
sw1_soll = HIGH;
else
sw1_soll = LOW;
swchange(1);
sw1aktiv =false;
vg_time1 = millis();
}

if (sw1_tast == LOW) sw1aktiv = true;

sw2.update(); // Taster 2
int sw2_tast = sw2.read();
if ((sw2_tast == HIGH) && (sw2aktiv== true)) {
if (sw2_soll == LOW)
sw2_soll = HIGH;
else
sw2_soll = LOW;
swchange(2);
sw2aktiv =false;
vg_time1 = millis();
}

if (sw2_tast == LOW) sw2aktiv = true;

pirValue = digitalRead(pirPin); // Bewegung startet Lichttimer von vorne
if (pirValue == 1) {
digitalWrite(ledPin, HIGH);
delay(100);
digitalWrite(ledPin, LOW);
vg_time1 = millis();
}
}

void swchange(int sw){  // Relais schalten
if (sw == 1)
digitalWrite(rel1pin, !sw1_soll);
else
digitalWrite(rel2pin, !sw2_soll);
}

Hallo,

auf den ersten Blick kann ich keinen Fehler finden. Kontrolliere mal, was der PIR macht, wenn die Lampe nicht mehr abschaltet. Du könntest eine serielle Ausgaben einbauen, die dir Anzeigen, wann der PIR reagiert und ob die Schalter richtig arbeiten.

Hallo,
ok, danke, baue ich dann mal ein und lass mir alle Werte ausgeben.

Hallo,

ohne Code Formatierung ist das schlecht lesbar. Zudem nicht klar ist ob bei jeder if Abfrage wirklich nur eine Anweisung bearbeitet werden soll oder nicht doch alle. Viele unterschätzen die Wirkung der Klammern. Vielleicht steckt hier der Logikfehler drin. Kann ja sein. Weil einmal schreibste die Anweisung dahinter und dann wieder unten drunter. Anderen ist damit nicht klar ob das so sein soll oder ein versehen ist und damit die Wirkung des Code sich verändert. Das als Hinweis.

Für mein Empfinden ist der Code nicht lesbar.

Selbst nach dem ich ihn formatiert habe.......

Mir fehlen klare Schnittstellen.

Ich würde einzelne Klassen bauen.
Die LampenKlasse und die Bedienelemente in ein Array stopfen.
Denn ich mag keine durchnummerierten Variablen.
Und Codeduplizierungen erst recht nicht.

Nachrichten, würde ich verschicken.
Klar definierte Nachrichten.

z.B. könnten die Taster "TasterTrigger" Nachrichten schicken.
Die Lampe muss dann selber entscheiden, wie sie auf den Trigger reagiert. Der PIR könnte eine "ReTrigger" Nachricht senden. Auch hier weiß die Lampe, ob sie länger an bleiben soll, oder die Nachricht ignorieren muss.

So bekommt man einzelne kleine Funktionsbereiche, die jeweils auf eine Bildschirmseite passen. Das Verschafft Überblick.

So ist mir das zu verstreut!
So bekomme ich keinen gesamt Überblick.

Ja, auch ich sehe den Fehler so nicht.


// -------

pinMode(rel2pin, OUTPUT);

digitalWrite(ledPin, LOW);
  digitalWrite(rel1pin, HIGH);
  digitalWrite(rel2pin, HIGH);
//----

So rum durchläuft rel2pin eine kurze LOW Phase.
Das ist bestimmt nicht so beabsichtigt.

  digitalWrite(rel2pin, HIGH);
  pinMode(rel2pin, OUTPUT);

So rum wird das vermieden.

Sorry, ich hab es jetzt nochmals formatiert hier eingestellt

void loop() {
	// Beide Lampen ausschalten wenn keine IR-Bewegung
	if ((millis() - vg_time1) > restlicht) {
		sw1_soll = LOW;
		sw2_soll = LOW;
		swchange(1);
		swchange(2);
	}

	sw1.update();  // Taster 1
	int sw1_tast = sw1.read();
	if ((sw1_tast == HIGH) && (sw1aktiv== true)) {
		if (sw1_soll == LOW)
			sw1_soll = HIGH;
		else
			sw1_soll = LOW;
		swchange(1);
		sw1aktiv =false;
		vg_time1 = millis();
	}

	if (sw1_tast == LOW) sw1aktiv = true;

	sw2.update(); // Taster 2
	int sw2_tast = sw2.read();
	if ((sw2_tast == HIGH) && (sw2aktiv== true)) {
		if (sw2_soll == LOW)
			sw2_soll = HIGH;
		else
			sw2_soll = LOW;
		swchange(2);
		sw2aktiv =false;
		vg_time1 = millis();
	}

	if (sw2_tast == LOW) sw2aktiv = true;

	pirValue = digitalRead(pirPin); // Bewegung startet Lichttimer von vorne
	if (pirValue == 1) {
		digitalWrite(ledPin, HIGH);
		delay(100);
		digitalWrite(ledPin, LOW);
		vg_time1 = millis();
	}
}

void swchange(int sw){  // Relais schalten
	if (sw == 1)
		digitalWrite(rel1pin, !sw1_soll);
	else
		digitalWrite(rel2pin, !sw2_soll);
}

Hast du an deinen Tastern auch PullDown Widerstände verbaut?

Ich finde, man kann es mit Klassen usw. auch übertreiben!

Hier ist mal so ein Modell:

Bedingungen:
An den beiden Lampentastern muss ein PullDown montiert werden.
Die Lampen lassen sich nur einschalten, wenn wenn der Pir ausgelöst hat.
Die Lampentaster sollten sich also in Sichtentfernung des des Pir befinden.

class SimpleTimer
{
  private:
  unsigned long timeStamp = 0;
  bool abgelaufenStatus   = true;

  public:
  void start()
  {
    timeStamp         = millis();
    abgelaufenStatus  = false;
  }

  bool operator() (const unsigned long ablaufZeit)
  {
    return abgelaufenStatus?true:abgelaufenStatus=millis()-timeStamp>ablaufZeit;
  }
};

class EdgeTimer
{
  protected:
  SimpleTimer timer;
  unsigned long laufzeit = 0;

  public:
  EdgeTimer(const unsigned long laufzeit):laufzeit(laufzeit){}
  EdgeTimer(){}
  virtual bool operator()(const bool trigger);
  void setLaufzeit(const unsigned long _laufzeit)
  {
     laufzeit =  _laufzeit;
  }
};

class FallingEdgeTimer: public  EdgeTimer // abfallende Flanke wird verzoegert
{
  public:
  using EdgeTimer::EdgeTimer;
  
  virtual bool operator()(const bool trigger)
  {
    if(trigger) timer.start();
    return !timer(laufzeit);
  }
};

class RaisingEdgeTimer: public  EdgeTimer // steigende Flanke wird verzoegert
{
  public:
  using EdgeTimer::EdgeTimer;
  virtual bool operator()(const bool trigger)
  {
    if(!trigger) timer.start();
    return timer(laufzeit);
  }
};

class Entprell
{
  private:
  RaisingEdgeTimer  ton;
  FallingEdgeTimer  toff;
  
  
  public:
  Entprell(const unsigned long entprellzeit)
  {
    ton.setLaufzeit(entprellzeit);
    toff.setLaufzeit(entprellzeit);
  }
  
  bool operator()(const bool trigger)
  {
    return toff(ton(trigger));
  }
};

class Leuchte
{
  private:
  const byte pin; // invers, wg. Relais
  bool brennt = false;
  bool scharf = false; // reagiert auf tastendruck, wenn scharf gestellt

  public:
  Leuchte(const byte pin):pin(pin){}
  
  void init()
  {
    digitalWrite(pin,HIGH);
    pinMode(pin,OUTPUT);
  }
  
  void operator()(const bool tasterTrigger, const bool pir)
  {
    if(!tasterTrigger) scharf = true; // umschaltung, scharf stellen

    if(scharf && tasterTrigger) // per Taster ein aus schalten
    {
      scharf = false;
      brennt = !brennt;
    }

    if(brennt && !pir)  brennt = false; // per pir abschalten
    
    digitalWrite(pin,!brennt); // invers
  }
  
};

struct Leuchteinheit
{
   byte tasterpin;
   Leuchte   leuchte;
   Entprell  entprell;
   
   void run(const bool pir)
   {
     leuchte(entprell(digitalRead(tasterpin)),pir);
   }

   void init()
   {
     pinMode(tasterpin, INPUT);
     leuchte.init();
   }
};

const byte pirPin = 10;   // Input for HC-S501

const byte sw1pin  = 11;  //Schalter 1 Lampe Mitte
const byte sw2pin  = 12;  //Schalter 2 re und li Lampe
const byte rel1pin =  9;  // Relais 1
const byte rel2pin =  8;  // Relais 2

const unsigned long restlicht    = 600000UL; 
const unsigned long entprellzeit = 100UL;

FallingEdgeTimer pirAbhandlung(restlicht);

Leuchteinheit gruppe[] = {
                            {sw1pin,Leuchte(rel1pin),Entprell(entprellzeit)},
                            {sw2pin,Leuchte(rel2pin),Entprell(entprellzeit)},
                          };   


void setup() 
{
  pinMode(pirPin, INPUT);
  for(Leuchteinheit & l:gruppe) l.init();
}

void loop() 
{
  bool pir = pirAbhandlung(digitalRead(pirPin)); // incl. restlicht zeit
  for(Leuchteinheit & l:gruppe) l.run(pir);
}

Hallo,
vielen Dank für die grossartige Unterstützung.
Ja es sind 10k Pullup eingebaut - wenn der Taster nicht gedrückt ist liegt der Arduino-Pin über 10k an GND.

Program lässt sich übersetzen und hochladen.
Beide Taster haben korrekte Funktion und man kann die Lichter an und ausschalten - Licht geht jetzt aber nicht mehr automatisch aus.

Btw Eclipse Sloeber Meldungen:
Class 'FallingEdgeTimer' has virtual method 'operator ()' but non-virtual destructor
Class 'RaisingEdgeTimer' has virtual method 'operator ()' but non-virtual destructor

Ja, eigentlich wollte ich auch mit Klassen ran aber dann hätte das Program ein paar Wochen länger gebraucht da ich darin noch nicht so fit bin.

Das sind Warnungen. Die kannst du in dem Fall ignorieren

Virtuelle Destrukoren braucht man wenn ein Objekt einer abgeleiteten Klasse über einen Zeiger auf die Basis-Klasse zerstört werden kann. Ohne den wird nur der Destruktor der Basisklasse aufgerufen (wie eine normale nicht-virtuelle Methode). Nicht der der abgeleiteten Klasse. Das kann zu Speicherlecks führen.
Allerdings enthält die Klasse sowieso nichts was einen expliziten Destruktor braucht. Sauber ist es natürlich es richtig zu machen :slight_smile:

Ja es sind 10k Pullup eingebaut - wenn der Taster nicht gedrückt ist liegt der Arduino-Pin über 10k an GND.

Was denn jetzt?
Pullup oder Pulldown?

Einen solchen Destruktor brauchen wir hier nicht.
Aber wenn dir der Sinn danach steht, kannste ja einen hinzufügen.

Licht geht jetzt aber nicht mehr automatisch aus.

Das ist schade....

Wenn der Pir 10 Minuten nichts gesehen hat, geht das Licht aus.
(so hatte ich es geplant)
Edit: und auch nochmal getestet

Prüfe doch mal, über den seriellen Monitor, ob der Pir auch tatsächlich ausschaltet.

Gruß Tommy

Hallo,
ja da ist das Problem. Der IR prellt und schaltet dann zig Mal hintereinander. Die Zahl zeigt die vergangenen Millisekunden. Bei den Großen Zahlen hat es eine reale Bewegung vor dem Sensor gegeben.
An: 10707
An: 101
An: 100
An: 101
An: 100
An: 100
An: 101
An: 100
An: 99
An: 100
An: 99
An: 100
An: 101
An: 100
An: 100
An: 101
An: 100
An: 100
An: 100
An: 99
An: 100
An: 101
An: 100
An: 101
An: 100
An: 100
An: 101
An: 99
An: 99
An: 101
An: 100
An: 100
An: 101
An: 100
An: 100
An: 101
An: 100
An: 99
An: 100
An: 100
An: 100
An: 101
An: 100
An: 101
An: 100
An: 100
An: 100
An: 99
An: 99
An: 101
An: 100
An: 100
An: 101
An: 100
An: 100
An: 14092

Muss ich vom Programm her abfangen. Getestet habe ich mit meinem prozedualen Programm da ich das Programm mit den Klassen leider noch nicht so richtig durchstiegen habe.

Hi

Versuch, dem PIR einen Abblock-Kondensator oder einen kleinen Elko zu spendieren - vll. geht Dem - weswegen auch immer - sporadisch der Saft aus und Er fährt einen Selbsttest.
Wobei alle 100ms ist schon arg schnell hintereinander - meine PIR sind für mindestens 3 Sekunden ON, bevor überhaupt ein AUS möglich ist.
Bei mir allerdings ohne jegliche externe Zusatzbeschaltung, direkt vom µC bzw. Dessen Versorgung abgegriffen.
1x an einem ATtiny45, 1x an einem UNO mit kleinem Piezo (ein 'Weekender' - von Einem von Euch hier)

MfG

Pir entprellung, auf die Schnelle rein gebastelt

class SimpleTimer
{
  private:
  unsigned long timeStamp = 0;
  bool abgelaufenStatus   = true;

  public:
  void start()
  {
    timeStamp         = millis();
    abgelaufenStatus  = false;
  }

  bool operator() (const unsigned long ablaufZeit)
  {
    return abgelaufenStatus?true:abgelaufenStatus=millis()-timeStamp>ablaufZeit;
  }
};

class EdgeTimer
{
  protected:
  SimpleTimer timer;
  unsigned long laufzeit = 0;

  public:
  EdgeTimer(const unsigned long laufzeit):laufzeit(laufzeit){}
  EdgeTimer(){}
  virtual ~EdgeTimer(){};
  virtual bool operator()(const bool trigger);
  void setLaufzeit(const unsigned long _laufzeit)
  {
     laufzeit =  _laufzeit;
  }
};

class FallingEdgeTimer: public  EdgeTimer // abfallende Flanke wird verzoegert
{
  public:
  using EdgeTimer::EdgeTimer;
  
  virtual bool operator()(const bool trigger)
  {
    if(trigger) timer.start();
    return !timer(laufzeit);
  }
};

class RisingEdgeTimer: public  EdgeTimer // steigende Flanke wird verzoegert
{
  public:
  using EdgeTimer::EdgeTimer;
  virtual bool operator()(const bool trigger)
  {
    if(!trigger) timer.start();
    return timer(laufzeit);
  }
};

class Entprell
{
  private:
  RisingEdgeTimer  ton;
  FallingEdgeTimer  toff;
  
  public:
  Entprell(const unsigned long entprellzeit)
  {
    ton.setLaufzeit(entprellzeit);
    toff.setLaufzeit(entprellzeit);
  }
  
  bool operator()(const bool trigger)
  {
    return toff(ton(trigger));
  }
};

class Leuchte
{
  private:
  const byte pin; // invers, wg. Relais
  bool brennt = false;
  bool scharf = false; // reagiert auf tastendruck, wenn scharf gestellt

  public:
  Leuchte(const byte pin):pin(pin){}
  
  void init()
  {
    digitalWrite(pin,HIGH);
    pinMode(pin,OUTPUT);
  }
  
  void operator()(const bool tasterTrigger, const bool pir)
  {
    if(!tasterTrigger) scharf = true; // umschaltung, scharf stellen

    if(scharf && tasterTrigger) // per Taster ein aus schalten
    {
      scharf = false;
      brennt = !brennt;
    }

    if(brennt && !pir)  brennt = false; // per pir abschalten
    
    digitalWrite(pin,!brennt); // invers
  }
  
};

struct Leuchteinheit
{
   byte tasterpin;
   Leuchte   leuchte;
   Entprell  entprell;

   void operator()(const bool pir)
   {
     leuchte(entprell(digitalRead(tasterpin)),pir);
   }

   void init()
   {
     pinMode(tasterpin, INPUT);
     leuchte.init();
   }
};

const byte pirPin = 10;  // Input for HC-S501

const byte sw1pin  = 11;  //Schalter 1 Lampe Mitte
const byte sw2pin  = 12;  //Schalter 2 re und li Lampe
const byte rel1pin =  9;  // Relais 1
const byte rel2pin =  8;  // Relais 2

const unsigned long restlicht    = 600000UL;
const unsigned long entprellzeit = 100UL;
const unsigned long pirEntprellzeit = 200UL;

FallingEdgeTimer pirAbhandlung(restlicht);
RisingEdgeTimer  pirEntprellung(pirEntprellzeit);

Leuchteinheit gruppe[] = {
                            {sw1pin,Leuchte(rel1pin),Entprell(entprellzeit)},
                            {sw2pin,Leuchte(rel2pin),Entprell(entprellzeit)},
                          };   


void setup() 
{
  pinMode(pirPin, INPUT);

  for(Leuchteinheit & l:gruppe) l.init();
}

void loop() 
{
  bool pir = pirAbhandlung(pirEntprellung(digitalRead(pirPin))); // restlicht Verzoegerungszeit
  for(Leuchteinheit & l:gruppe) l(pir);
}

EDIT: Korrigiert

Bei den PIR, die in den Sets mitgeliefert werden (und so sieht der aus) gibt es 3 Möglichkeiten der Beeinflussung.

1 Jumper, ob der PIR retriggert oder zwingend ausschaltet
ein Poti für die Empfindlichkeit
ein Poti für die Haltezeit nach einer Erkennung

Sind die richtig eingestellt? Zur Einstellung besser die Plastikkappe abnehmen, damit er nur in eine Richtung schaut und nicht laufend den Bearbeiter ermittelt.

Gruß Tommy

Ja genau! Mir ist eingefallen, dass der IR eine Haltezeit von 2-3 Sekunden Sekunden hat. Die Routine läuft in der Zeit dann aber 20-30 Mal durch (delay(100)) und deshalb die vielen Ausgabezeilen. Egal wie man die Haltezeit runter drehen kann - die Routine kommt sicher x-Mal in der Zeit vorbei. Also Softwarelösung. Vielleicht klappts mit dem "combie-Klassenprogramm" das ist für mich aber jetzt nicht "von schlechten Eltern". Berücksichtigt die Entprellung die o.g. Erkenntnisse?

Die Kappe ist übrigens von Anfang an ab.

Das ist die Steuerung (Testaufbau). VDE-technisch ist das nicht toll - ich weis!

Das Klassenprogramm von combie läuft!!! Vielen Dank! Gibt meine Bettlektüre.

tischleuchte.jpg

Danke für die Blumen!

Bettlektüre ......
Ja... (von mir aus, gerne)

.... war zu schwere Bettlektüre :slight_smile: .... Programm ist leider zu heftig für mich Anfänger.