Probleme mit (Watchdog-) Interrupt mit attiny85

Hallo liebe Community!

Ich habe ein Problem mit einem meiner Codes.
Konkret geht es dabei eigentlich nur um ein Upgrade eines Codes, den ich bereits vor einem Jahr geschrieben habe und nun ein wenig erweitern wollte.
Letztes Jahr hab ich für mein Kind ein Nachtlicht mit einem attiny85 gebaut. Das hat auf einen angeschlossenen PIR-Sensor reagiert und sobald es an war, hat der Watchdog immer nachgesehen, ob der PIR immer noch meldet, ansonsten das Licht wieder ausgeschalten. Hat bislang prima funktioniert.

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

const byte LED = 0;
const byte SWITCH = 2;
const byte LUM = A3;
const byte LUMPOWER = 4;

byte ledPWM = 0;
const byte minPWM = 30;
const byte maxPWM = 255;
const int lumLimit = 200;
const int lumThreshold = 50;

ISR (PCINT0_vect) {
  // Do something interesting here
}

ISR (WDT_vect) {
  wdt_disable();
  digitalWrite(LUMPOWER, HIGH);
}

void setup () {
  resetWatchdog ();
  pinMode (LED, OUTPUT);
  pinMode (SWITCH, INPUT);
  pinMode (LUMPOWER, OUTPUT);

  for ( int flashCount = 0; flashCount < 3; flashCount++ ) {
    for (ledPWM; ledPWM<maxPWM; ledPWM++ ) {
       analogWrite(LED,ledPWM);
       delay(10);
    }
    for (ledPWM; ledPWM>0; ledPWM-- ) {
      analogWrite(LED,ledPWM);
      delay(10);
    }
    delay(250);
  }

  digitalWrite(LUMPOWER, HIGH);

  PCMSK  |= bit (PCINT2);
  GIFR   |= bit (PCIF);
  GIMSK  |= bit (PCIE);

}

void loop () {
  bool switchValue = digitalRead(SWITCH);
  int lumValue = analogRead(LUM);

  if ( switchValue == HIGH && lumValue <= lumLimit && ledPWM < maxPWM ) {
    ledPWM++;
    analogWrite(LED, ledPWM);
    delay(10);
  }
  else if ( ( switchValue == LOW || lumValue > ( lumLimit + lumThreshold ) ) && ledPWM > 0 ) {
    ledPWM--;
    analogWrite(LED, ledPWM);
    delay(10);
  }
  else if ( switchValue == LOW && ledPWM == 0 ) {
    digitalWrite(LUMPOWER, LOW);
    goToSleep();
    digitalWrite(LUMPOWER, HIGH);
  }
  else {
    goToWDTSleep();
  }
}

void goToSleep () {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  byte keepADCSRA = ADCSRA;
  ADCSRA = 0;
  power_all_disable ();
  noInterrupts ();
  sleep_enable ();
  interrupts ();
  sleep_cpu();
  sleep_disable();
  power_all_enable();
  ADCSRA = keepADCSRA;
}

void goToWDTSleep() {
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  byte keepADCSRA = ADCSRA;
  ADCSRA = 0;
  power_all_disable();
  noInterrupts ();
  resetWatchdog();
  sleep_enable();
  interrupts ();
  sleep_cpu ();
  sleep_disable();
  power_all_enable();
  ADCSRA = keepADCSRA;
}

void resetWatchdog () {
  MCUSR = 0;
  WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF);
  WDTCR = bit (WDIE) | bit (WDP2) | bit (WDP1);
  wdt_reset();
}

Jetzt wollte ich das ganze Licht ein wenig erweitern, damit es auch über einen Taster händisch eingeschalten werden kann und hab den Code entsprechend umgeschrieben. Also Taster als PCInterrupt mit hinein gehängt, entsprechende Modi eingerichtet und dann auch noch dem µC die Aufgabe übergeben, sich um die Länge der Einschaltdauer zu kümmern (da jetzt ein anderer PIR verwendet wird).

#include <avr/sleep.h>
#include <avr/power.h>
#include <avr/wdt.h>

const byte LED = 0;
const byte PIR = 2;

unsigned int pirTrigger = 0;
unsigned int pirTimeout = 30;

const byte LUM = A2;
const byte LUMPOWER = 3;

const int lumLimit = 200;
const int lumThreshold = 50;

const byte BUTTON = 1;

byte ledPWM = 0;
byte currentMode = 0;
unsigned long lastButtonPress = 0;

ISR (PCINT0_vect) {
  noInterrupts();
}

ISR (WDT_vect) {
  wdt_disable();
}

void setup() {
  resetWatchdog();

  pinMode (LED, OUTPUT);
  pinMode (PIR, INPUT);
  pinMode (LUM, INPUT);
  pinMode (LUMPOWER, OUTPUT);
  pinMode (BUTTON, INPUT_PULLUP);

  for ( int flashCount = 0; flashCount < 3; flashCount++ ) {
    while (ledPWM<255)
      rampUpDown(1,5);
    while (ledPWM>0)
      rampUpDown(0,5);
    digitalWrite(LED, LOW);
    delay(250);
  }

//  PCMSK  |= bit (PCINT1);
//  PCMSK  |= bit (PCINT2);
  PCMSK = 0b000000110;
  GIFR   |= bit (PCIF);    // clear any outstanding interrupts
  GIMSK  |= bit (PCIE);    // enable pin change interrupts
}

void loop() {

  if ( digitalRead(BUTTON) == LOW ) {
    if ( millis() - lastButtonPress > 500 ) {
      if ( currentMode < 2 )
        currentMode++;
      else
        currentMode = 0;
      lastButtonPress = millis();
    }
    if ( currentMode == 2 ) {
      digitalWrite(LUMPOWER, HIGH);
      for ( byte counter = 0; counter < 3; counter++ ) {
        while (ledPWM<255)
          rampUpDown(1,1);
        while (ledPWM>0)
          rampUpDown(0,1);
      }
    }
  }
  
  if ( currentMode == 0 ) {
    if ( ledPWM > 0 ) {
      rampUpDown(0,10);
    }
    else {
      goToSleep();
    }
  }

  if ( currentMode == 1 ) {
    if ( ledPWM < 255 ) {
      rampUpDown(1,10);
    }
    else {
      goToSleep();
    }
  }

  if ( currentMode == 2 ) {
    bool pirValue = LOW;
    if ( digitalRead(PIR) == HIGH ) {
      pirTrigger = 0;
    }
    if ( pirTrigger < pirTimeout ) {
      pirValue = HIGH;
      if ( ledPWM == 255 )
        pirTrigger++;
    }
    
    int lumValue = analogRead(LUM);
  
    if ( pirValue == HIGH && lumValue <= lumLimit && ledPWM < 255 )
      rampUpDown(1,10);
    else if ( ( pirValue == LOW || lumValue > ( lumLimit + lumThreshold ) ) && ledPWM > 0 )
      rampUpDown(0,10);
    else if ( pirValue == LOW && ledPWM == 0 ) {
      digitalWrite(LUMPOWER, LOW);
      goToSleep();
      digitalWrite(LUMPOWER, HIGH);
    }
    else {
      goToWDTSleep();
    }
  }
#endif
}  // end of loop

void goToSleep() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  byte keepADCSRA = ADCSRA;
  ADCSRA = 0;
  power_all_disable();
  noInterrupts();
  if ( useWDT == 1 ) resetWatchdog();
  sleep_enable();
  interrupts();
  sleep_cpu();
  sleep_disable();
  power_all_enable();
  ADCSRA = keepADCSRA;
}

void goToWDTSleep() {
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);
  byte keepADCSRA = ADCSRA;
  ADCSRA = 0;
  power_all_disable();
  noInterrupts();
  resetWatchdog();
  sleep_enable();
  interrupts();
  sleep_cpu();
  sleep_disable();
  power_all_enable();
  ADCSRA = keepADCSRA;
}

void resetWatchdog() {
  MCUSR = 0;
  WDTCR = bit (WDCE) | bit (WDE) | bit (WDIF);
  WDTCR = bit (WDIE) | bit (WDP2) | bit (WDP1);
  wdt_reset();
}

void rampUpDown(bool upDown, byte speed) {
  if ( upDown == 1 ) ledPWM++;
  else ledPWM--;
  analogWrite(LED,ledPWM);
  delay(speed);
}

Beim Einschalten läuft alles sauber. Die Startsequenz läuft durch, auch das ein- und ausschalten vom Licht tut mit dem Taster. Sobald aber der PIR (und somit auch der Watchdog) ins Spiel kommt, klappt es ein paar Mal mit dem Triggern und irgendwann (mal früher, mal später) resettet sich plötzlich der µC und ich komme einfach nicht dahinter, woran es liegt. Spiele ich die alte FW wieder drauf, verrichtet er seinen Dienst wie bisher auch.
Nachdem ich mittlerweile schon etliche Abende und Nächste damit verbracht hab, den Fehlerteufel ausfindig zu machen und es mir nicht gelungen ist, liegt meine Hoffnung bei der tollen Community, die wir hier haben - ob mir jemand weiterhelfen kann. :slight_smile:

Vielen Dank im Voraus!

Ich verstehe nicht, warum du es mit dem WD machst.

In der Loop immer nachsehen, ob der Bewegungsmelder noch "anschlägt". Wenn nicht dann verzögert ausschalten.

Ich habe das mit einer Schrankbeleuchtung gemacht und funktioniert super. Alles ohne WD.

Hallo Dieter!

Danke für deine Antwort.
Habe ich mir auch schon überlegt, aber das Ganze ist mobil und läuft mit einem Akku. Deshalb soll so wenig Saft wie möglich verbraucht werden.
Davon abgesehen hat es bereits funktioniert und so sehr es mich auch schon nervt (oder gerade deswegen) will ich nicht einen Schritt zurück machen und aufgeben. :wink:

0mega5:
Sobald aber der PIR (und somit auch der Watchdog) ins Spiel kommt, klappt es ein paar Mal mit dem Triggern und irgendwann (mal früher, mal später) resettet sich plötzlich der µC und ich komme einfach nicht dahinter, woran es liegt.

Wie ist das gemeint?
Sobald ein Watchdog ist Spiel kommt (also "auslöst"/"zuschlägt") kommt es immer zu einem Reset des µC. Das ist die Aufgabe eines Watchdog-Timers - einen Reset des µC auslösen.

uxomm:
Wie ist das gemeint?
Sobald ein Watchdog ist Spiel kommt (also "auslöst"/"zuschlägt") kommt es immer zu einem Reset des µC. Das ist die Aufgabe eines Watchdog-Timers - einen Reset des µC auslösen.

Nein, da muss ich dir widersprechen. Der Watchdog kann genauso ein Interrupt sein, wie ein externer oder PC Interrupt.

Auch Nick schreibt dazu auf seiner Seite:

External interrupts, pin-change interrupts, and the watchdog timer interrupt, can also be used to wake the processor up. This can be very handy, as in sleep mode the processor can be configured to use a lot less power (eg. around 10 microamps). A rising, falling, or low-level interrupt can be used to wake up a gadget (eg. if you press a button on it), or a "watchdog timer" interrupt might wake it up periodically (eg. to check the time or temperature).

0mega5:
Hallo Dieter!

Danke für deine Antwort.
Habe ich mir auch schon überlegt, aber das Ganze ist mobil und läuft mit einem Akku. Deshalb soll so wenig Saft wie möglich verbraucht werden.
Davon abgesehen hat es bereits funktioniert und so sehr es mich auch schon nervt (oder gerade deswegen) will ich nicht einen Schritt zurück machen und aufgeben. :wink:

Ok, wenn es stromsparend sein soll, macht es Sinn.
Dennoch würde ich die Abfrage des PIR in der Loop machen und per WD in die Funktion zu Abfrage springen. Dann kannst du dazu parallel den Taster zum WD schalten.