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

Why must the whole thing reset? Why not just write all the pins low when the clock is low and only call the function that writes them HIGH if the clock is HIGH? Read the clock signal in loop and use an if statement to decide which to do.

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?

If you want to reset the board when this line goes LOW, then why not just connect it directly to the reset pin on the board. That will hold the board in reset as long as it is LOW and only allow the board to run when it is HIGH.

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.

He said the thing generating the signal would give a HIGH when the clock was running. I meant hook that directly to the reset pin. When the Arduino resets it shouldn't have any effect on the pin state of that other piece of equipment.

You're talking about when you tie an Arduino pin to reset. But he said this other thing would give a signal.

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.

To me it says he's taking an external clock signal and manipulating it and wants to turn everything off if the external signal disappears. It says the "external circuit" has an output so I figured that was "external" to Arduino.

I guess we wait for OP.

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.

The pins all go tri-state while the reset is held. They all turn input. It happens really fast, too fast to reset a chip with its own output. You can add a pulldown to the outputs if there is a lot of capacitance holding them up too long. But that's something to measure first.

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.