Need help, WDT locking up after several hours. Attiny84 chip

Hello, I’m a little rusty programming in C, and I’m new to Arduino, and this forum.

My project is controlling a RGB LED with the PMW outputs of a Attiny84 chip. The affect is a random colour will build in intensity, and then dim, the circuit will go into a sleep state for around 16 - 48 sec, then repeat the process.

The problem I see is after something like ten hours, the project stops working either by not producing a colour in the set time frame, or blinking different colours, or it will simply be on for several minutes. I assume this has to do with the WDT, but I don’t know that for sure.

The setup I have is a Arduino UNO setup as a ISP, to program the Attiny84.

Any help would be appreciated, This is my first post, let me know if I’m doing it correctly.

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



ISR(WDT_vect) 
  {
 wdt_disable();  // disable watchdog
  }

void myWatchdogEnable(const byte interval) 
  {  

  MCUSR = 0;                          // reset various flags
  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

byte adcsra_save = ADCSRA;

  wdt_reset();
  ADCSRA = 0;// AD control and status register
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
 sleep_enable();
  sleep_mode();    // now goes to Sleep and waits for the interrupt
  
  sleep_disable();
 
 ADCSRA = adcsra_save;
 
 //wdt_enable(WDTO_1S);
  } 

float RGB1[3];
float RGB2[3];
float INC[3];

int red, green, blue;

int RedPin = 8;
int GreenPin = 7;
int BluePin = 6;
int i;

float Randomkitty;
int Randsleep;
void setup() 
{ 
  // Serial.begin(9600);
 randomSeed(analogRead(0));
 
 RGB1[0] = 0;
 RGB1[1] = 0;
 RGB1[2] = 0;
 
 RGB2[0] = random(10,255);
 RGB2[1] = random(10,255);
 RGB2[2] = random(10,255);  
 
 
 for (int u=0; u<255; u++){///////////Start up RGB LED test
        analogWrite(RedPin, u);
        delay(3);
         }
 for (int d=255; d>0; d--){
        analogWrite(RedPin, d);
        delay(3);
         }
 for (int u=0; u<255; u++){
        analogWrite(BluePin, u);
        delay(3);
         }
 for (int d=255; d>0; d--){
        analogWrite(BluePin, d);
        delay(3);
         }
 for (int u=0; u<255; u++){
        analogWrite(GreenPin, u);
        delay(3);
         }
 for (int d=255; d>0; d--){
        analogWrite(GreenPin, d);
        delay(3);
         }

 delay(1000);
 
} 

void loop() 
{ 
  
 // wdt_enable(WDTO_1S);
  
 randomSeed(analogRead(0));
 Randomkitty = random(200,800)/100;
 
 for (int x=0; x<3; x++) {
   INC[x] = (RGB1[x] - RGB2[x]) / 255; } 
 
 for (int x=0; x<256; x++) {
   
   red = int(RGB1[0]);
   green = int(RGB1[1]);
   blue = int(RGB1[2]);

   analogWrite (RedPin, red);  
   analogWrite (GreenPin, green);   
   analogWrite (BluePin, blue);     
   delay(Randomkitty);   //Speed control of the transition
   
   RGB1[0] -= INC[0];
   RGB1[1] -= INC[1];    
   RGB1[2] -= INC[2];    
 }
 
 
   
   for (int y=0; y<256; y++) {
     
     red = int(RGB1[0]);
     green = int(RGB1[1]);
     blue = int(RGB1[2]);
   
     analogWrite (RedPin, red);  
     analogWrite (GreenPin, green);   
     analogWrite (BluePin, blue);   
     
     delay(Randomkitty);
   
   RGB1[0] += INC[0];
   RGB1[1] += INC[1];    
   RGB1[2] += INC[2];    
   }
   
 for (int x=0; x<3; x++) {
   RGB2[x] = random(556)-300;
   //RGB2[x] = random(756)-500;
   RGB2[x] = constrain(RGB2[x], 0, 255); 
   delay(30);
                          }

//Randsleep = random(75,450); // 10min to 60 min
Randsleep = random(40,120) /10; //16 sec to 48 sec 83days worst case 331days best 205days adv

  for (i = 0; i <=Randsleep; i++)
      {  
    myWatchdogEnable (0b100000);  // 4 seconds
      }
  
}

Manipulating the watchdog is a timed sequence. The manipulation has to be protected from interrupts. Your code lacks the necessary protection.

This looks like code from my Gammon Forum : Electronics : Microprocessors : Interrupts page. I had left out the interrupt protection, sorry. Page amended now.

Failure after a few hours is not that surprising. Eventually a timer interrupt will occur at an unfortunate moment in the timed sequence, the watchdog will not be enabled, and thus the code will be stuck in sleep mode.

Thank you Mr. Gammon for your reply, yes I think I must have gotten the code from your posting. Although I don't remember. The only reason I'm sceptical is because the contributions you added are too helpful for me to over look, but I must have. I've been reading it quite extensively, and find it very helpful for this project and other projects I have in the queue. Thank you for taking the time to explain the processes, and giving some good follow up examples. I find it very helpful.

Do you have any suggestions as to how I might resolve my problem?

Thanks

What Coding Badly said, "protect" turning the watchdog on, eg.

void myWatchdogEnable(const byte interval) 
  {  

  noInterrupts ();  // timed sequence

  MCUSR = 0;                          // reset various flags
  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

byte adcsra_save = ADCSRA;

  wdt_reset();
  ADCSRA = 0;// AD control and status register
  set_sleep_mode (SLEEP_MODE_PWR_DOWN);  
  sleep_enable();

  interrupts ();  // now we can sleep

  sleep_cpu();    // now goes to Sleep and waits for the interrupt
  
  sleep_disable();
 
  ADCSRA = adcsra_save;
 
 //wdt_enable(WDTO_1S);
  }

Note that sleep_mode() is changed to sleep_cpu().

That is because sleep_mode does this:

#define sleep_mode() \
do {                 \
    sleep_enable();  \
    sleep_cpu();     \
    sleep_disable(); \
} while (0)

We already have the enable and disable, so all we need is the actual sleep.

Sorry for my delayed response, but thank you again for the help. Still a little shaky on the interrupt protection, but it will sink in, eventually. I'm further along in my project, so thank you again.

These two lines of code reconfigure the watchdog...

  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

According to the datasheet, the second line of code has to execute within four clock cycles of the first line of code. The problem is that an interrupt could occur between the lines of code. If an interrupt does occur the second line of code cannot possibly execute within the four clock cycles.

  WDTCSR |= 0b00011000;               // see docs, set WDCE, WDE
  // Interrupt occurs here throwing off the timing
  WDTCSR =  0b01000000 | interval;    // set WDIE, and appropriate delay

The solution is to disable interrupts so the two lines are guaranteed to execute one right after the other. The two lines of code have to be protected from being interrupted.

Thank you for helping me out. I understand it now, it makes sense, I guess I just need some things spelled out for me. Thanks again.

You are welcome.