Hi
I have built a number of automated dosing units that add balancing chemicals to water. In 5 years, with about 45 units in place, I have had 3 that have frozen somewhere in the process. The LCD usually flickers and the backlight flashes on and off as well. Pressing the reset button fixes the issue and it does not happen again on the same machine. Not yet anyway.
As these are remote systems I started looking around for a solution.
I considered the internal WDT but found that only has a loop interval of 8 seconds.
When my unit is doing a dose it is taking about 10 minutes.
I chose to use a 555 timer and this works well... I think.
If I disconnect the 'Heartbeat' wire on Pin 17 then it resets the timer.
But I want to try and recreate a 'freeze' of some sort that will lock up the looping and other processes so that the heartbeat signal of the WDT code is stopped. Then I can see if the reset I have wired in and programmed actually does what I want.
I have attached my WDT schematic that I found on another site, and also the test code I am using. I won't post the doser code as it is pretty long and is not part of the question I am asking.
Is there code that I could add into the loop that will lock up the nano? I tried a while(1); command but that just stopped the looping. It did not stop the heartbeat from continuing.
Thanks
#include <MsTimer2.h>
int heartbeatPin = 17; // The Arduino will send the heartbeat at this output pin.
int heartbeatInterval = 4000; // Heartbeat will be sent to the 555 timer at this interval, in milliseconds.
const int LED = 13;
/*
Sends a heartbeat to the 555 timer chip.
*/
void heartbeat() {
Serial.println("Heartbeat sent!");
pinMode(heartbeatPin, OUTPUT);
digitalWrite(heartbeatPin, LOW);
delay(2000); // Pulse delay to get the 555 to recognize it.
digitalWrite(heartbeatPin, HIGH);
// Return to high impedance
pinMode(heartbeatPin, INPUT);
}
void setup() {
pinMode(LED, OUTPUT);
Serial.begin(9600);
Serial.println("*** Arduino reset ***");
// Setup and start the heartbeat interval
MsTimer2::set(heartbeatInterval, heartbeat);
MsTimer2::start();
}
void loop() {
// This runs the LED and sends serial lines to know what is happening. This is simply to consume time within the loop for a test.
digitalWrite(LED, HIGH);
Serial.println("LED On 1000!");
delay(1000); // wait for a second
digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW
Serial.println("LED Off!");
delay(1000); // wait for a second
digitalWrite(LED, HIGH);
Serial.println("LED On 5000!");
delay(5000); // wait for 5 seconds
digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW
Serial.println("LED Off!");
delay(1000); // wait for a second
digitalWrite(LED, HIGH);
Serial.println("LED On 10sec!");
delay(10000); // wait for 10 seconds
digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW
Serial.println("LED Off!");
delay(1000); // wait for a second
digitalWrite(LED, HIGH);
Serial.println("LED On 20 secs!");
delay(20000); // wait for 20 seconds
digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW
Serial.println("LED Off!");
delay(1000); // wait for a second
digitalWrite(LED, HIGH);
Serial.println("LED On 30 secs!");
delay(30000); // wait for 30 seconds
digitalWrite(LED, LOW); // turn the LED off by making the voltage LOW
Serial.println("LED Off!");
delay(1000); // wait for a second
}

If stopping the loop() is not what you want, then you must specifically identify what you think "lock up the Arduino" means to you.
Paul
Hi Paul
I am wanting to repeat the 'glitch' I have seen previously. Where the unit freezes, and only a reset fixes it.
If I just stop the loop the heartbeat continues outside the loop.
So is there some some code that will 'entangle' the whole of the nano's processes to the point where it locks or freezes all loops and non-loop processes.
Instead of sending a "heartbeat" to an external reset device, send one to reset the watchdog timer.
That is the way the watchdog is intended to be used.
It doesn't matter how long it takes to dose, you're not blocking for that whole time are you? If you've written decent code it should be able to pat the dog while it runs a pump.
In general I don't write the code. It is rather too complex for me to figure out some of it. I thought I would be ok with this one.
I got the impression from my reading that the internal WDT was part of the loop.
I 'think' my dosing process stops the looping. I may be wrong about that.
It would be easier if I could use the internal WDT. In choosing to use the 555 timer I will have to build an additional PCB that stacks onto my existing PCB.
Is there a way to post large codes? Mine exceeds the character limit here.
You can attach large bits of code to your post.
I 'think' my dosing process stops the looping.
Often the case with poorly written code.
Learn how to avoid doing that by studying this excellent tutorial on the "Blink Without Delay" example.
Delta_G:
while(1);
or
for(;;);
Delta_G:
while(1);
or
for(;;);
I have tried both and neither of these lock up the heartbeat, they only stop the LEDs looping.
StepperMotorCodetoLoad_555Trial_Forum.ino (16.2 KB)
Here is the full code.
jremington:
Often the case with poorly written code.
I Have checked and it does stop the loop until the process is over. The Heartbeat continues.
StepperMotorCodetoLoad_555Trial_Forum.ino (16.2 KB)
What Arduino are you using? Where did you get the MsTimer2 library?
The posted code violates basically all the empirical rules of reliable Arduino code. It uses Strings, which cause memory problems and unpredictable program crashes, as well as long delay() statements.
If you didn't write it, it may be nearly impossible to fix.
If I had to do a quick fix I might consider replacing the delay() calls with a function that does incremental delays while resetting the internal watchdog timer (which is not used at all).
jremington:
What Arduino are you using? Where did you get the MsTimer2 library?
The posted code violates basically all the rules of reliable Arduino code. It uses Strings, which cause memory problems and unpredictable program crashes, as well as long delay() statements.
If you didn't write it, it may be nearly impossible to fix.
Well that is a cheery assessment. 
I am using a nano.
The MsTimer2 library came from Arduino Playground - MsTimer2
I had the original code written by a guy on Upwork, and then had it 'cleaned up' by someone else on Upwork. I am biotechnologist, not a coder, so while I understand some of what the code is doing, I don't have the knowledge to write or fix it.
Delta_G:
If you want some good doser code I have some on my github page. It uses an LCD and a rotary encode and button to handle a menu. You can set dosing amounts broken into as many time periods as you want.
Thanks for pointing that out. With the use of SMS capability (using a FONA card), plus the use of stepper motors etc, I am not sure I personally could adapt that code to work for me.
Well that is a cheery assessment.
Sorry, but it is true. I'm surprised that the code has been as reliable as you state (only 3/45 failures in five years).
To get it fixed properly, consider posting on the Gigs and Collaborations forum section. You may be asked to pay for the help, but given lockdown in so many places, perhaps not.
Your use of MSTimer2 is not correct, but it may actually function as you want.
MsTimer2::set(heartbeatInterval, heartbeat);
heartbeat() is called in a TimerOverflow interrupt. Within that interrupt, you should not use Serial printing or delay() as they both rely on interrupts which are disabled within the execution of the timer interrupt. The Serial output is buffered and will be printed when the interrupt finishes. There will be no delay, so evidently the timing of the digitalWrite() is adequate for the 555.
void heartbeat() {
Serial.println("Heartbeat sent!");
pinMode(heartbeatPin, OUTPUT);
digitalWrite(heartbeatPin, LOW);
delay(2000); // Should be enough time to pulse to get the 555 to recognize it.
digitalWrite(heartbeatPin, HIGH);
// Return to high impedance
pinMode(heartbeatPin, INPUT);
}
There will be no delay, so evidently the timing of the digitalWrite() is adequate for the 555.
This comment got my attention, because I've always thought (and experienced at some point) that delay() never ends when called from within an interrupt.
But delay() is simply ignored when used with MsTimer2, as this simple example, taken from the MsTimer2 reference page shows. I've looked at the code for wiring.c and can't understand why delay is ignored. Does anyone else know? There is a strange call to yield(), which I haven't tracked down yet, and may be the clue.
// Toggle LED on pin 13 each second
#include <MsTimer2.h>
void flash() {
static boolean output = HIGH;
digitalWrite(13, output);
delay(2000); //including this line makes no difference at all!
output = !output;
}
void setup() {
pinMode(13, OUTPUT);
MsTimer2::set(500, flash); // 500ms period
MsTimer2::start();
}
void loop() {
}
WIRING.C delay()
void delay(unsigned long ms)
{
uint32_t start = micros();
while (ms > 0) {
yield();
while ( ms > 0 && (micros() - start) >= 1000) {
ms--;
start += 1000;
}
}
}
can't understand why delay is ignored
I think that micros() stops advancing with the disabling of the Timer0 overflow interrupt within the other isr.
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
cli();
m = timer0_overflow_count;
#if defined(TCNT0)
t = TCNT0;
#elif defined(TCNT0L)
t = TCNT0L;
#else
#error TIMER 0 not defined
#endif
#ifdef TIFR0
if ((TIFR0 & _BV(TOV0)) && (t < 255))
m++;
#else
if ((TIFR & _BV(TOV0)) && (t < 255))
m++;
#endif
SREG = oldSREG;
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}
That is what I thought, but delay(2000) seems to return immediately when the interrupts are off.
If micros() stops advancing, delay() should be stuck here:
while ( ms > 0 && (micros() - start) >= 1000)
Delta_G:
Feeling generous. And it only took a little while. Here's a non-blocking version of your code.
I don't have all the libraries, so I can't compile it. Please forgive any little typos.
Wow, thanks for doing that!! Very much appreciated.
I had an issue on compiling, I get the error "wdt_enable(WDTO_8s); // enable 8 second watchdog"
I had a look at forums and I tried using the zero and the O in case it was a typo. Neither works.
After some reading I finally found it was the _8s should have been _8S.
So now it is all loaded.