Using Timer1 as a watchdog timer.

Hi All,
Arduino type: MEGA 1280.

This is what I am trying to do:

I have a cyclic event occurring which is interrupting the processor via INT0. This event is derived from the mains on the secondary side of the power transformer which provides a pulse every 20mS, ie half wave rectified 50Hz mains filtered and voltage limited to drive Pin 2. This is working just fine. I see pulses on this pin at the expected frequency and the interrupt is just fine and I can count these pulses.

Now I am interested in detecting dropped cycles where the mains has "glitched". My power supply has sufficient storage capacity to withstand a considerable mains outage (about 5 seconds) yet maintain an adequate supply to the MEGA regulator.

What I had intended was to use a timer (eg Timer1) with a period of 21 millseconds (just over 1 cycle of 50Hz) where I had attached an interrupt routine executed when the Timer ran out. The plan was to use restart (eg Timer1.restart()) when the mains interrupt occurred. If the mains is running normally the timer is always restarted before it completes and the timer interrupt routine never executes. If the mains glitches due to a failed cycle and the time reaches 21 mS the Timer routine executes and I know that I have a missed cycle. The code fragment looks like this:

#include <TimerOne.h>

const int what_is_happening = 8;                // I have a LED on pin 8, normally off

void mainsinterrupt()
{
   Timer1.restart();                            // timer reset every mains pulse
}

void Mains_Cycle_Timeout()
{
 digitalWrite(what_is_happening, HIGH);        // turn the LED on when a cycle is skipped

  Timer1.restart();                            // and set up again for another missed cycle unless the mains returns
}


void setup()
{
  attachInterrupt(0, mainsinterrupt, FALLING);
  Timer1.initialize(21000);                    // set the timer up for just over one cycle
  Timer1.attachInterrupt(Mains_Cycle_Timeout);
  pinMode(what_is_happening, OUTPUT);          // configure the test output
  digitalWrite(what_is_happening, LOW);        // and make it low

  
}


void loop()
{
  // loop is redundant here - all the action in interrupts
}

However when I flick the mains off/on the LED remains stubbornly off. What am I missing here?

Thanks, Fred

If the pulses are every 20 milliseconds, regular as clockwork, it seems that it should be trivial to detect when a pulse is not 20 milliseconds (plus or minus a little) after the last one, without needing to use another timer.

Use millis() or micros() to record when this pulse arrives, in the ISR, after moving the time of the last pulse to another variable. Then, compute the difference. A difference of 0 is good. A difference of 20 means you missed a pulse. A difference of 40 means you missed two pulses.

Hi Paul, Not a bad workaround and with some adaption I might use it yet. There may be a problem in that there is no time of the next pulse unless it arrives but I guess if enough get lost my DC storage runs out and the DC fails anyway. However I was intending to take action if the pulse was missed and could do so very smartly from the timer interrupt execution without further processing.

I am still wondering why my code fragment does not work. I must be missing something fundamental about the way this works and knowing why will improve my and possibly others understanding.

Regards, Fred.

I can reproduce your problem, sort-of. When I tried this modified version the LED seemed always on, not off:

#include <TimerOne.h>

const int what_is_happening = 8;                // I have a LED on pin 8, normally off

void mainsinterrupt()
{
   Timer1.restart ();
   digitalWrite(what_is_happening, LOW);    
}

void Mains_Cycle_Timeout()
{
 digitalWrite(what_is_happening, HIGH);        // turn the LED on when a cycle is skipped
}

void setup()
{
  Timer1.initialize(21000);                    // set the timer up for just over one cycle
  Timer1.attachInterrupt(Mains_Cycle_Timeout);
  pinMode(what_is_happening, OUTPUT);          // configure the test output
  digitalWrite(what_is_happening, LOW);        // and make it low
  attachInterrupt(0, mainsinterrupt, FALLING);
  pinMode (2, INPUT_PULLUP);
}

void loop() { }

I added the input pullup, in case you disconnected a wire from D2, and forced the LED off in mainsinterrupt, otherwise it would stay one.

But this is what the logic analyzer told me:

So the interrupt does fire, but it only brings the LED low for a fraction of a second. Why would this be?

Well it appears to me that the Timer1 library has a couple of bugs. The version I have, at least, as this in it:

ISR(TIMER1_OVF_vect)          // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
  Timer1.isrCallback();
}
...
void TimerOne::attachInterrupt(void (*isr)(), long microseconds)
{
  if(microseconds > 0) setPeriod(microseconds);
  isrCallback = isr;                                       // register the user's callback with the real ISR
  TIMSK1 = _BV(TOIE1);                                     // sets the timer overflow interrupt enable bit
  sei();                                                   // ensures that interrupts are globally enabled
  start();
}

An overflow interrupt is not called for here. An overflow happens when the timer overflows, that is it reaches 65535 in the case of Timer 1, which is intended to let you count overflows.

What is required is a "Timer/Counter1, Output Compare A Match Interrupt Enable". That interrupts when the counter compares equal to the set value. So with these modifications to TimerOne.cpp:

ISR (TIMER1_COMPA_vect)    // <---- Timer A compare match ISR
{
  Timer1.isrCallback();
}
...
void TimerOne::attachInterrupt(void (*isr)(), long microseconds)
{
  if(microseconds > 0) setPeriod(microseconds);
  isrCallback = isr;                                       // register the user's callback with the real ISR
  TIMSK1 = _BV(OCIE1A);          // <------ compare match interrupt
  sei();                                                   // ensures that interrupts are globally enabled
  start();
}

Now the LED comes on if you stop the 50 Hz input, and goes off if you start it again.

Ahhhhh Nick, that's what I call a reply. I dips my lid in deference. Properly researched, well explained and at the root cause. Nice. Thanks for your considerable effort. I was wondering whether the library will be corrected as this is a nasty little bug if you want this functionality. In the way of it I have solved my problem somewhat less elegantly partly inspired by the solution of Paul S. When I get a spare moment I'll extract the core bits to a fragment and post it. It works reliably with the existing Timer1 interrupt and does not have the failing of requiring a pulse to determine that one or more has gone missing. But a workaround it is. Regards, Fred.

I promised I would post my solution. Well here it is:

#include <TimerOne.h>

// Note that a late cycle by 1mS in a 50Hz system corresponds to a system frequency of about 47.6 Hz by 
// which time every under-frequency protection device in the system will have operated
// and the power system will be black. 2mS late corresponds to about 45.5Hz. Disastrous number.
// This is intended to catch short power interruptions such as the outages caused 
// by storms or other transient problems not under-frequency.

// This idea will also work for 60Hz (or any other frequency, say 400Hz) by altering 
// the constants and Timer frequency based on the frequency you want to monitor

int mains_lost_cycles_counter, mains_checker;

void mainsinterrupt()                                            // when a mains interrupt ocurrs we end up here, usually 50 times per second
{
  mains_checker = 0;                                             // reset the time counter to zero
}

void Mains_Cycle_Timeout()                                      // We end up here every 1mS based on the interrupt set up in setup()
{
  mains_checker++;                                              // add 1 mS to the counter we reset above. This should never become greater than 20 as the next mains pulse resets it
  if (mains_checker  > 21)                                      // If it is greater than 21 then we have a problem with a cycle that should have happened but has not
  {
    mains_lost_cycles_counter++;                                // So bump the lost cycles counter by 1
    mains_checker = 0;                                          // and reset the counter to 0 ready for the next one. Perhaps this could be set to -2 for a more precise operation????
  }
}
// Note that this is not ideal as the timer drifts by 1 or 2 mS every cycle we do not have a reset. 
// I tried >= as the test but it detects good cycles as bad sometimes.
// So after maybe 50 or so lost cycles detected there will be a lost cycle missed
// However for my application all I am interested in that cycles are lost, an exact count is less important
// Beware if you want an exact lost cycle count.

void setup()
{
  Serial.begin(9600);
  
  attachInterrupt(0, mainsinterrupt, FALLING);                    // Set up the mains interrupt arbitrarily on the falling edge
  Timer1.initialize(1000);                                        // set up interrupt for 1kHz:-  1ms per interrupt. By making this faster you home in better on missing pulses 
  Timer1.attachInterrupt(Mains_Cycle_Timeout);
  
  mains_lost_cycles_counter = 0;                                  // no lost cycles yet
  mains_checker = 0;                                              // starting at 0 not ideal as it depends where in the mains cycle we are but it will do
  
}
  
void loop()
{
  Serial.print("Lost cycles =");
  Serial.println(mains_lost_cycles_counter);                      // horrid test line to show results
}

This idea works well enough and I will need to consider if I want to modify the timer library to use the other solution. Mine is unquestionably more processor intensive and less accurate I suspect. The trouble is that good enough is a mortal enemy of better.

Incidentally I must make it crystal clear that I meant no disparagement of the original author of the Timer1 library in my comment about it being a nasty bug. When it comes to software too much testing is never enough and even then the worst bugs lurk hidden in the boundary conditions. It is really hard to test for what a product or system should NOT do. I have the greatest respect for those who write libraries and more power to them.

Regards, Fred.