Detect absence of external clock signal to reset all outputs to LOW

I have a small code that takes a clock signal input ( 50 - 1000 Hz) and divides it sequentially over 4 outputs, like a shift register, using the Arduino Nano. No output can stay HIGH for any length of time, i.e. in the absence of a clock signal. Any output being high for more than .5 seconds would cause overheating on the external circuit that is driven from this Arduino.
The external circuit which generates the clock signal has an output that goes HIGH during clock being present, but I cannot find a reliable way to use that, because so far in my code, it is a brute force reset of the whole code using another ISR.
I would really like a safe and elegant solution to my code problem.
Thanks.

// Slave unit using IR LED as input from Master unit. 

int clock = 3 ;
int PwrOn = 2 ;       
int powerReset = 12 ;            
int h = 4 ;
int k = 8 ;
int d = 0 ;

void setup() 
  {
    for (int i = 4; i<12; i=i+1)
      {
       pinMode (i, OUTPUT) ;          // Make pins 4-11 outputs 
       digitalWrite (i, LOW) ;        // Set all outputs to LOW.
       digitalWrite (powerReset, HIGH) ;  // Set reset output to high, set to low via runReset void
      }
    pinMode (clock, INPUT) ;          // Clock signal from IR LED
    pinMode (PwrOn, INPUT) ;          // Check for signal presence using AC to DC Detctor circuit
    attachInterrupt (digitalPinToInterrupt (clock), Shift, RISING) ;   // detect clock rising edge to run
                                                                   // shift (void) output to next pin
    attachInterrupt (digitalPinToInterrupt (PwrOn), runReset, LOW) ; //Reset program if PwrOn pin is high
                                              // wire pin 12 to pin RST, locations next to GND
  }

void loop() 
  {
    if  (digitalRead (PwrOn) == HIGH ) ;
    {
      Shift () ;
    }
   digitalWrite (h, LOW) ;  
   digitalWrite (k, LOW) ;  
  }

void Shift ()                       // Sequentially make the outputs go high
  {
    digitalWrite (h, LOW) ;         // Because of dual pwr supplies,
    digitalWrite (k, LOW) ;         // two outputs will be high at the same time

    delayMicroseconds(1);
    h = h + 1 ;
    k = k + 1 ;
  
    if ( h > 7 )
    {
      h = 4 ;
      k = 8 ;
    }
    digitalWrite (h, HIGH) ;
    digitalWrite (k, HIGH) ;
  }
void runReset ()
  {
    digitalWrite (powerReset, LOW) ;
  }

The safest way to do a software RESET is to use the WDT
Put
wdt_enable(WDTO_15MS);
in your ISR
It will reset the board in 15ms
No need for external connection to RESET pin

Or just monitor the PwrOn pin in loop() and call setup() when it goes LOW

if  (digitalRead (PwrOn) == HIGH );
{
  Shift ();
}
else
{
  setup();
}

However you also need to initialize your variables in setup

  • You will get bitten if you continue with this habit.

  • Use a regular variable, not the pin initializer variable.

1 Like

The ISR for the clock pulse could reset a timer variable (unsigned long), and the loop() could take action if the timer ever got N milliseconds behind millis().

Such a variable should be qualified as volatile, and access to it in the loop done with interrupts off whilst doing.

And like this for not getting bitten on the arm one day:

// global, use in setup too for you pin mode setting.
const int pins1[4] = {4, 5, 6, 7};
const int pins2[4] = {8, 9, 10, 11};

// later
void Shift ()                       // Sequentially make the outputs go high
{
  static int pinIndex;

  lastClockPukse = millis();  // millis doesn't run in an ISR, but you can read it

  digitalWrite (pins1[pinIndex], LOW);
  digitalWrite (pins2[pinIndex], LOW);

  delayMicroseconds(1);

  pinIndex++;
  if (pinIndex >= 4) pinIndex = 0;

  digitalWrite (pins1[pinIndex], HIGH);
  digitalWrite (pins2[pinIndex], HIGH);
}

This makes it easy to move the pins around. Generally relying on maths and a particular circuit arrangement makes life harder, and makes your code obscurer. Yes that is too a word.

a7

2 Likes

Have you looked at a hardware watchdog circuit?

But when the board is in reset then all the pins are set to be inputs and so float. The time between the reset being applied and the floating of the inputs is shorter than the minimum reset time needs to be for a full reset (see data sheet) so you can't do that.

As I read it he is trying to generate that signal rather than he already has one.

But maybe that is me misreading the post.

Thank you all for some valuable info and insight.
Another Arduino generates the "master" clock signal and also has an output enabling an "on" LED, so this is what i could use to enable the Nano slave unit to run. They are coupled via close-coupled (physically) IR links. Both Arduinos, master and slave, drive 2x4 power transistors sequentially, my first master circuit works great, so I wanted a slave unit doing the same as the master. If any of the outputs go high for too long, or even worse if any glitches in more than 1 output at a time happen due to the processor resetting or whatever, would blow the fuse (hopefully) and once the units are in place its hard to get to the fuses. So I need a very "safe" code running on the slave unit.
An extra hardware \Watchdog unit is not a good option for me.
I thought about Alto777 solution, but not sure that because of using ISRs, the timing will function reliably.
I am not sure about any outputs having glitches when resetting the whole code, or how long it would take for the reset process and then running the code again and set all outputs to LOW.

My idea adds one line of code to the ISR you are already relying on. So I'm not sure what you mean here.

Meanwhile, watch out for if statements like this

if  (digitalRead (PwrOn) == HIGH ) ;

which is complete, the { braced } code lines that follow it are unconditionally executed. Lose the semicolon.

That may be something the compiler would notice and warn you about, if you go to the IDE preferences and crank up all warnings.

With the ISR I rewrote after @LarryD's comments, your loop would be

Wait, I just see you are calling Shift(), the ISR

    attachInterrupt (digitalPinToInterrupt (clock), Shift, RISING) ;

from the loop() function. This is wrong.

So with that in mind, your loop would be

bool panic;

void loop() 
{
  noInterrupts();
  if (millis() - lastClockPukse > 500) panic - true;
  interrupts();

  if (panic) {

// loss of external clock, deal,with it however you must here

  }
}

I haven't seen specifications on how critical your timing is. You are using a Nano, on which of delayMicroseconds() we can say

This function works very accurately in the range 3 microseconds and up to 16383 . We cannot assure that delayMicroseconds will perform precisely for smaller delay-times. Larger delay times may actually delay for an extremely brief time.

So no matter what, it is probably time to call in the heavies and use the AVR counter timer peripherals. Not me.

Or.

The gross timing is off your external clock. The microsecond pulses could probably be done better with some straight ahead old fashioned NOP wheel spinning.

In any case, since those 1 us pulses come at the external clock rising edge, the ISR timing won't enter into anything else.

a7

LarryD, I am not sure about the problem using the variables h and k and give them starting values. I did not use them to initialize any pins, only to set pins sequentially HIGH or LOW.
Please explain where I go wrong.
Alto777, if the millis function returns a time value from program code running start, will this only work the first time code is running?

No. Are you familiar with ant use of millis() to time stuff?

This in the ISR keeps moving the timer variable up to the present. To the time at which the ISR executes:

  lastClockPukse = millis();

and this

  if (millis() - lastClockPukse > 500) panic - true;

tests if the last time that happened was more than 500 milliseconds ago.

It's all relative and will work forever.

Think of it like the ISR emptying a bucket of water, and the passage of time adding water.

If you keep emptying the bucket, it never overflows.

Should the bucket fail to be emptied, eventually it overflows, alerting you to the fact that it's been a minute (well, 500 milliseconds) since the last bucket emptying.

Put your finger on the code and walk yourself through it.

If you haven't come to grips with the basic pattern involved, google

arduino blink without delay

and read a few articles on it.

Or, as far as I can tell, there are all the pieces on this thread to do exactly what I describe, maybe you could just give it a whirl.

Please play with this simulation of a "missing pulse detector". Keep pressing the button or the lamp goes out:


Wokwi_badge Keep Alive


The two entirely equivalent lines of code are

    startTime = now;    // reset the timer

// and check if it is expired

  if (now - startTime >= actionDelay)

Look for the context in this short sketch that the above link simulates:

// https://forum.arduino.cc/t/how-to-delay-using-millis/1142938
// https://wokwi.com/projects/368911815269434369

// version 4. remove anydebouncing

const byte buttonPin = 2;
const byte ledPin = 3;

const unsigned long actionDelay = 3500; // action 3.5 seconds for testing - life too short

void setup() {
  Serial.begin(115200);
  Serial.println("\nWake up!\n");

  pinMode (buttonPin, INPUT_PULLUP);
  pinMode (ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
}

void loop() {
  static unsigned long startTime; // time at action

  unsigned long now = millis();   // fancier 'currentMillis' ooh!
  int buttonState = !digitalRead(buttonPin);  // button pulled up!

// when the button is pressed, reset the timer, turn ON the LED
  if (buttonState == HIGH) {
    startTime = now;
    digitalWrite(ledPin, HIGH);
  }

// when (if!) the timer expires, turn OFF the LED
  if (now - startTime >= actionDelay) {
    digitalWrite(ledPin, LOW);
  }
}

HTH

a7

  • Not wrong, just suggest you do it in a more standard way.
if  (digitalRead (PwrOn) == HIGH ) ;
  • And you know adding the ; at the end, basically makes the statement non functional.

It's part of the nano hardware, there is nothing extra to add and it's the only safe way to do a software reset

Thanks guys for all your help.
I have a good idea how to proceed from here, using your suggestions.
Noted about the ; not wanted after the IF statement.
I am going to make a small 4-channel detector circuit with an Arduino to let me know if more than 1 output goes high at any time and if so switch on the LED during any of the procedures as recommended by you guys.
Hans.

Sounds like fun , but a waste of time.

If you re-ordered the digitalWrite() statements, you would be able to examine the code and see that such an event is impossible.

Just turn off the one that is on before you turn in the next. Aside from things that are not a matter of code, this would suffice. Maybe it already is logically impossible, didn't look to see just now.

Naturally if it coming to be due to a system failure of some kind was a Very Bad Thing, you are in the realm of needing redundant safeguards.

For that matter, a hardware missing pulse detector may be a better approach or one that might be in there as second or even primary defense against one output getting stucked on; it would still only be a warning and if you aren't trusting your code, you wouldn't trust your remediation, so maybe the idea of your detector extra Arduino is not a waste of time. I struck that above.

Unless the hardware missing pulse detector had the ability to do the protective action itself.

Many systems with microprocessors are designed with failure of that part in mind, real empowered limit switches in heavy machinery, for example. Fuses, another simple example.

a7

Ok Alto777, I hear what you are saying.
I have modified my code for the sequential outputs so that I always keep them LOW, set one HIGH for one clock cycle, then LOW, then add a short delay, related in time to the frequency of the Clock, then proceed to the next one.
I was thinking of using a small Arduino to act as a missing pulse detector, with 4 software AND gates, where each AND gate has as one input one of the sequential outputs, and as other input the clock signal.
Will try this on my breadboard.
Hans

So now you have another piece of complicated hardware that can also fail.

2 Likes

That is overly complex. You can use an NE555 chip as a missing pulse detector very easily, it is one of the standard examples. It just involves discharging the capacitor with your constant pulses.

I recall from a pulse and digital college class so many years ago, there is a missing pulse detection circuit. Have you investigated such a circuit that could interface to an Arduino pin?