Arduino nano crashes - please advise

I would be grateful if someone could suggest why the attached code is crashing.
The code uses an application monitor with a watchdog interrupt to help to identify the issue but this is not producing useful information.

ApplicationMonitor.cpp (6.4 KB)
ApplicationMonitor.h (3.2 KB)
ImmersionHeaterController.h (283 Bytes)
ImmersionHeaterController.ino (17.1 KB)

What does "crashing" mean?

A schematic may be hepful.

what is code intended to do? how?

operates 6 relays, takes input from two DS18B20 temperature sensors and serial inputs via software serial

crashing means unresponsive until restart - have to disconnect power to restart
i don't have a schematic - the circuit is pretty basic - DS18B20 sensors each wired with 4k7 ohm resistor, relays wired to 12V 4 Channel 20a Relays

Please read the how to get the best from the forum posts.

Format the code uding the IDE autoformat tool and post the code as instructed in the post.

Please answer questions. We cant help without snswers to our questions.

putting a bit more effort into explaining what your program is expected to do and actually does , may encourage some to download your program files, not posted with code tags (the <code> icon) and ook thru your 1000+ lines of code to possibly help you with your problem

Without seeing the code and a schematic, it will be hard to help.

I advise you to produce one, it'll lower the number of posts asking for it.

diagram:

#include <Arduino.h>
//#include <NeoSWSerial.h>
//NeoSWSerial portOne(10, 11);
#include "ImmersionHeaterController.h"
#include <SoftwareSerial.h>
#include "ApplicationMonitor.h"
#include <OneWire.h>
#include <DallasTemperature.h>

Watchdog::CApplicationMonitor ApplicationMonitor;
int g_nIterations = 0;

//enum debugLevel : int {
//	off = 0, low = 1, med, high
//};

//enum updateMode : int {
//	zero = 0, down = 1, up = 2
//};

debugLevel debug = high;
constexpr int softwareBaudRate = 9600;
int countOfEnergisedRelays = 0;

constexpr int maxNumberOfRelays = 6;
constexpr int relayPin1 = 2;
constexpr int relayPin2 = 3;
constexpr int relayPin3 = 4;
constexpr int relayPin4 = 5;
constexpr int relayPin5 = 6;
constexpr int relayPin6 = 7;
constexpr int relayPinSocket = 8;

constexpr int delayAfterSendingMessage = 50;

unsigned long countOFAllBufferResets = 0;
unsigned long countOfAllMessagesCorrectlyBounded = 0;
unsigned long bufferResetMax = 400000000; //4294967040

unsigned long previousTimeIncPlus = 0;
unsigned long storedTimeIncPlus = 0;
unsigned long previousTimeIncMinus = 0;
unsigned long storedTimeIncMinus = 0;

unsigned long minimumSwitchingTime = 2000;

/**
 * variables for Serial Communication
 */
constexpr int RXpin = 10;
constexpr int TXpin = 11;
SoftwareSerial port(RXpin, TXpin);
constexpr byte numBytes = 8;
char latestInstruction[numBytes];
byte numReceived = 0;
boolean newData = false;
boolean newInstruction = false;
constexpr char startMarker = '<'; //<
constexpr char endMarker = '>'; //>
int pauseWhileSending = 50;

/**
 * variables for temperature measurement
 */
// temp sensors wire pins
constexpr int ONE_WIRE_LOWERT = 9;
constexpr int ONE_WIRE_UPPERT = 12;

// Setup a oneWire instance to communicate with any OneWire device
OneWire OneWireLowerT(ONE_WIRE_LOWERT);
OneWire OneWireUpperT(ONE_WIRE_UPPERT);

// Pass oneWire reference to DallasTemperature library
DallasTemperature TempSensorLower(&OneWireLowerT);
DallasTemperature TempSensorUpper(&OneWireUpperT);

//callibrations
constexpr float referenceHigh = 99.9;
constexpr float referenceLow = 0;
constexpr float tLowerRawHigh = 98.3;
constexpr float tLowerRawLow = 1.0;
constexpr float tLowerRawRange = tLowerRawHigh - tLowerRawLow;
constexpr float referenceRange = referenceHigh - referenceLow;
float tLowerCorrectedValue;
float tLowerRawValue;
constexpr float tUpperRawHigh = 98.3;
constexpr float tUpperRawLow = 0.7;
constexpr float tUpperRawRange = tUpperRawHigh - tUpperRawLow;
float tUpperCorrectedValue;
float tUpperRawValue;

/**
 * TODO this message is not used! Deprecate?
 */
void logError(String error) {
	Serial.print("ERROR! ");
	Serial.println(error);
	//debug = high;
}

/**
 * this function is to help with error detection of transmitted messages
 */
void countMessageNumber(boolean messageStarted) {
	if (messageStarted) {
		countOfAllMessagesCorrectlyBounded++;
//		if (debug >= high) {
//			Serial.print("countOfAllMessagesCorreclyBounded ");
//			Serial.println(countOfAllMessagesCorreclyBounded);
//		}
		if (countOfAllMessagesCorrectlyBounded > bufferResetMax) {
			Serial.println("count1 overflow! ");
			Serial.println("Reset both counts to zero");
			countOfAllMessagesCorrectlyBounded = 0;
			countOFAllBufferResets = 0;
		}
	}
}
/**
 * This requires 1000ms between message sends to avoid buffer filling up
 * If buffer does fill, it will get emptied (elsewhere) but ~5/10% messages are void because of buffer overruns
 */
void recvCharsWithStartEndMarkers() {
	static boolean recvInProgress = false;
	static byte ndx = 0;
	char receivedChar[numBytes];
	//set receivedChar to un-initialised
	memset(receivedChar, 0, sizeof(receivedChar));
	char charReadFromPort;
	boolean messageStarted = false;

	port.listen();
	unsigned long timeout = 10000;
	unsigned long startTimeWhile = millis();
	unsigned long currentTime = millis();

	while (port.available() > 0 && newData == false
			&& currentTime - startTimeWhile < timeout) {
		currentTime = millis();
		charReadFromPort = (char) port.read();
		if (recvInProgress == true) {
			if (charReadFromPort != endMarker) {
				receivedChar[ndx] = charReadFromPort;
				ndx++;
				//it is possible that the message sent is longer than numBytes. if so, the last characters will be discarded
				if (ndx >= numBytes - 1) {
					ndx = numBytes - 2;
				}
			} else {
				//countMessageNumber(messageStarted);
				receivedChar[ndx] = '\0'; // terminate the string
				recvInProgress = false;
				numReceived = ndx;  // save the number for use when printing
				ndx = 0;
				newData = true;
			}
		} else if (charReadFromPort == startMarker) {
			recvInProgress = true;
			messageStarted = true;
		}
	}

	if (newData) {
		strncpy(latestInstruction, receivedChar, numBytes);
		newInstruction = true;
		if (debug >= high) {
			Serial.print("latestInstruction ");
			Serial.println(latestInstruction);
		}

	} else {
		strncpy(latestInstruction, "nochnge", numBytes);
		newInstruction = false;
	}
	newData = false;
}

/**
 * this works for messages less than 8 characters
 * a delay is required: delayAfterSendingMessage
 * currently the messages invoke no action - this requires implementation on the other side
 */
void wrapCharMessage(char message[]) {
	port.print(startMarker);
	port.print(message);
	port.print(endMarker);
	delay(delayAfterSendingMessage);
}

/****
 * make sure to check if relay switches on high or low
 */
void adjustLoad(int relayID, boolean on) {
	uint8_t cond;

	//low switching
//	if (on)
//		cond = LOW;
//	else
//		cond = HIGH;

	//high switching
	if (on)
		cond = HIGH;
	else
		cond = LOW;

	switch (relayID) {
	case 1:
		digitalWrite(relayPin1, cond);
		break;
	case 2:
		digitalWrite(relayPin2, cond);
		break;
	case 3:
		digitalWrite(relayPin3, cond);
		break;
	case 4:
		digitalWrite(relayPin4, cond);
		break;
	case 5:
		digitalWrite(relayPin5, cond);
		break;
	case 6:
		digitalWrite(relayPin6, cond);
		break;
	default:
		break;
		//todo this should not happen
	}
}

void updateRelayCount(updateMode mode) {
	switch (mode) {
	case 0:
		countOfEnergisedRelays = 0;
		break;
	case 1:
		countOfEnergisedRelays--;
		break;
	case 2:
		countOfEnergisedRelays++;
		break;
	default:
		break;
		//send data to emonloadcontrol
	}
}

void configureSocket(boolean turnOn) {
	if (turnOn)
		digitalWrite(relayPinSocket, HIGH);
	else
		digitalWrite(relayPinSocket, LOW);
}

void configureLoads(int numberOfRelays, boolean turnOn) {
	switch (countOfEnergisedRelays) {
	case 0:
		if (turnOn) {
			adjustLoad(1, true);
			updateRelayCount(up);
		}
		break; /* optional */
	case 1:
	case 2:
	case 3:
	case 4:
	case 5:
		if (turnOn) {
			adjustLoad(6, true);
			updateRelayCount(up);
		} else {
			//work out if frequent switching is an issue
			for (int i = 0; i < numberOfRelays; i++) {
				adjustLoad(countOfEnergisedRelays, false);
				if (countOfEnergisedRelays != 0)
					updateRelayCount(down);
			}
		}
		break;
	case 6:
		if (turnOn) {
			//sending the messages invokes no action on the other side!
			//wrapCharMessage("IHfull");
		} else {
			//wrapCharMessage("IHNF");
			for (int i = 0; i < numberOfRelays; i++) {
				adjustLoad(countOfEnergisedRelays, false);
				if (countOfEnergisedRelays != 0)
					updateRelayCount(down);
			}
		}
		break;
	default:
		//logError("issue with configureRelays");
		Serial.print("count ");
		Serial.print(countOfEnergisedRelays);
		Serial.print(" numberOfRelays ");
		Serial.print(numberOfRelays);
		Serial.print(" turnOn(1) turnOff(0):");
		Serial.println(turnOn);
		adjustLoad(1, false);
		adjustLoad(2, false);
		adjustLoad(3, false);
		adjustLoad(4, false);
		adjustLoad(5, false);
		adjustLoad(6, false);
		updateRelayCount(zero);
		break;
	}
//	Serial.print("numberOfRelaysOn after call ");
//	Serial.println(numberOfRelaysOn);

}

void checkOverflowAndEmptyBuffer() {
	if (port.overflow()) {
		Serial.println("overflow!");
		int timeout = 0;
		//buffer size is 64 bytes, each character can represent a byte
		while (port.available() && timeout < 64) {
			port.read();
			timeout++;
		}
	}
}

/**
 * this is a fudge for buffer errors
 *
 */
void updateErrorCount() {
	countOFAllBufferResets++;
	if (countOFAllBufferResets > bufferResetMax) {
		Serial.println("Overflow1 Reset ");
		countOFAllBufferResets = 0;
		countOfAllMessagesCorrectlyBounded = 0;
	}
	if (countOfAllMessagesCorrectlyBounded > bufferResetMax) {
		Serial.println("overflow2! Reset");
		countOfAllMessagesCorrectlyBounded = 0;
	}
//	Serial.print("bufferErrorCount: ");
//	Serial.println(countOFAllBufferResets);
//	Serial.print(" countOfAllMessagesCorreclyBounded: ");
//	Serial.println(countOfAllMessagesCorreclyBounded);
	if (countOFAllBufferResets < bufferResetMax
			&& countOfAllMessagesCorrectlyBounded < bufferResetMax) {
		Serial.print("% errors ");
		if (countOfAllMessagesCorrectlyBounded > 0)
			Serial.println(
					(countOFAllBufferResets / countOfAllMessagesCorrectlyBounded)
							* 100);
	}
}

void addressIssues() {
	//updateErrorCount();
	checkOverflowAndEmptyBuffer();
}

void adjustLoadsAccordingToInstruction() {
	if (newInstruction) {
		if (strncmp(latestInstruction, "alloff", strlen("alloff")) == 0) {
			configureLoads(6, 0);
			configureSocket(false);
		} else if (strncmp(latestInstruction, "Sct+", strlen("Sct+")) == 0) {
			configureSocket(true);
		} else if (strncmp(latestInstruction, "SoC-", strlen("SoC-")) == 0) {
			configureSocket(false);
		} else if (strncmp(latestInstruction, "inc+", strlen("inc+")) == 0) {
			if (countOfEnergisedRelays < 6) {
				configureLoads(1, 1);
			}
		} else if (strncmp(latestInstruction, "inc-", strlen("inc-")) == 0) {
//			previousTimeIncMinus = storedTimeIncMinus;
//			storedTimeIncMinus = millis();
//				if (storedTimeIncMinus - storedTimeIncPlus
//						> minimumSwitchingTime) {
			//				}
			if (countOfEnergisedRelays > 0) {
				configureLoads(1, 0);

			}
		} else {
			if (debug >= med) {
				Serial.print("instruction not matched ");
				Serial.print("latestInstruction: ");
				Serial.println(latestInstruction);
			}
			addressIssues();
		}
	}
}

/**
 * NOTE: There is a issue with start-up of Arduino programs that control these relays.
 * These relays input controls are Active LOW, meaning that setting a pin LOW turns them ON.
 * To assure that no relays activate at Reset or Power-On until you want them to,
 * the initialization sequence in SETUP should be:
 * pinMode(Relay, OUTPUT);
 * digitalWrite(Relay, HIGH);
 *
 */
void initialiseRelays() {
	pinMode(relayPin1, OUTPUT);
	pinMode(relayPin2, OUTPUT);
	pinMode(relayPin3, OUTPUT);
	pinMode(relayPin4, OUTPUT);
	pinMode(relayPin5, OUTPUT);
	pinMode(relayPin6, OUTPUT);
	pinMode(relayPinSocket, OUTPUT);

	digitalWrite(relayPin1, LOW);
	digitalWrite(relayPin2, LOW);
	digitalWrite(relayPin3, LOW);
	digitalWrite(relayPin4, LOW);
	digitalWrite(relayPin5, LOW);
	digitalWrite(relayPin6, LOW);
	digitalWrite(relayPinSocket, LOW);

}

void blinkLed(int numBlinks) {
	for (int dl = 0; dl < numBlinks; dl++) {
		digitalWrite(LED_BUILTIN, HIGH);
		delay(100);
		digitalWrite(LED_BUILTIN, LOW);
		delay(100);
	}
}

void setup() {
	Serial.begin(9600);
	Serial.println("setting up");
	ApplicationMonitor.Dump(Serial);
	ApplicationMonitor.EnableWatchdog(
			Watchdog::CApplicationMonitor::Timeout_4s);

	//initialise temperature sensors
//	TempSensorLower.begin();
//	TempSensorUpper.begin();

	port.begin(softwareBaudRate);
	port.listen();
	initialiseRelays();
	blinkLed(5);
	Serial.println("<Arduino is ready eddy>");
}

void measureTempAndAdjustLoad() {
	// Send the command to get temperatures
	TempSensorLower.requestTemperatures();
	TempSensorUpper.requestTemperatures();
	tLowerRawValue = TempSensorLower.getTempCByIndex(0);
	tUpperRawValue = TempSensorUpper.getTempCByIndex(0);

	if (tLowerRawValue == -127.0 || tUpperRawValue == -127.0) {
		//this indicates issue with sensors
		Serial.print("temp sensors not functioning correctly ");
		//turn off  all loads
		configureLoads(6, 0);

		if (tLowerRawValue == -127.0) {
			Serial.print("temp lower issue ");
		}
		if (tUpperRawValue == -127.0) {
			Serial.print("temp lower issue ");
		}
	}

	tLowerCorrectedValue = (((tLowerRawValue - tLowerRawLow) * referenceRange)
			/ tLowerRawRange) + referenceLow;

	tUpperCorrectedValue = (((tUpperRawValue - tUpperRawLow) * referenceRange)
			/ tUpperRawRange) + referenceLow;

	//	Serial.print("tLowerRawValue: ");
//	Serial.println(tLowerRawValue);
//	Serial.print("tUpperRawValue: ");
//	Serial.println(tUpperRawValue);

//	Serial.print("tLowerCorrectedValue: ");
//	Serial.println(tLowerCorrectedValue);
//	Serial.print("tUpperCorrectedValue: ");
//	Serial.println(tUpperCorrectedValue);

	if (tUpperCorrectedValue > 90 || tLowerCorrectedValue > 85) {
		//turn off  all loads
		configureLoads(6, 0);
	}
}

void testConfigure() {
	int shortDelay = 1000;
	configureLoads(6, 0);
	delay(shortDelay);

	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);

	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);

	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);

	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);

	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);

	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 1);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);
	configureLoads(1, 0);
	delay(shortDelay);

	configureLoads(6, 0);

}
void cycleRelaysOnAndOff() {
	int shortDelay = 3000;
	int longDelay = 10000;
	delay(longDelay);

	Serial.println("all off");
	digitalWrite(relayPin1, LOW);
	digitalWrite(relayPin2, LOW);
	digitalWrite(relayPin3, LOW);
	digitalWrite(relayPin4, LOW);
	digitalWrite(relayPin5, LOW);
	digitalWrite(relayPin6, LOW);
	delay(shortDelay);

	Serial.println("1 on");
	digitalWrite(relayPin1, HIGH);
	delay(shortDelay);

	Serial.println("1 off");
	digitalWrite(relayPin1, LOW);
	delay(shortDelay);

	Serial.println("2 on");
	digitalWrite(relayPin2, HIGH);
	delay(shortDelay);

	Serial.println("2 off");
	digitalWrite(relayPin2, LOW);
	delay(shortDelay);

	Serial.println("3 on");
	digitalWrite(relayPin3, HIGH);
	delay(shortDelay);

	Serial.println("3 off");
	digitalWrite(relayPin3, LOW);
	delay(shortDelay);

	Serial.println("4 on");
	digitalWrite(relayPin4, HIGH);
	delay(shortDelay);

	Serial.println("4 off");
	digitalWrite(relayPin4, LOW);
	delay(shortDelay);

	Serial.println("5 on");
	digitalWrite(relayPin5, HIGH);
	delay(shortDelay);

	Serial.println("5 off");
	digitalWrite(relayPin5, LOW);
	delay(shortDelay);

	Serial.println("6 on");
	digitalWrite(relayPin6, HIGH);
	delay(shortDelay);

	Serial.println("6 off");
	digitalWrite(relayPin6, LOW);
	delay(shortDelay);

	Serial.println("1, 2, 3, 4, 5, 6 on");
	digitalWrite(relayPin1, HIGH);
	digitalWrite(relayPin2, HIGH);
	digitalWrite(relayPin3, HIGH);
	digitalWrite(relayPin4, HIGH);
	digitalWrite(relayPin5, HIGH);
	digitalWrite(relayPin6, HIGH);
	delay(shortDelay);

}

/**
 * 	check how often the instructions come from emonloadcontrol and adjust delay accordingly
 * 	otherwise loop goes round too quickly and messages are chopped up
 */
void loop() {
	ApplicationMonitor.IAmAlive();
	ApplicationMonitor.SetData(g_nIterations++);
	const boolean testingSystem = false;
	const boolean testConfigureRelays = false;
	const boolean cycleRelays = false;
	if (!testingSystem) {
		delay(200);
		recvCharsWithStartEndMarkers();
		adjustLoadsAccordingToInstruction();
		measureTempAndAdjustLoad();

	} else {
		if (testConfigureRelays)
			testConfigure();
		if (cycleRelays)
			cycleRelaysOnAndOff();
	}
}

The function of this is to take serial input from the RX pin via the SoftwareSerial class.
recvCharsWithStartEndMarkers() pulls messages from the buffer and acts accordingly
the relays are energised based on the messages recieved
there are two DS18B20 temperature sensors on different busses. I know that these can be on the same bus but I have decided to separate these. There may be an impact on available memory - I have explored this and there is still plenty of space between heap and stack.

diagram now updated with the RX and TX pins

so what condition occurs when there is a "crash", when the code stops working?

what is purpose of the following?

ISR(WDT_vect, ISR_NAKED)
{
  // Setup a pointer to the program counter. It goes in a register so we
  // don't mess up the stack.
  upStack = (uint8_t*)SP;

  // The stack pointer on the AVR micro points to the next available location
  // so we want to go back one location to get the first byte of the address
  // pushed onto the stack when the interrupt was triggered. There will be
  // PROGRAM_COUNTER_SIZE bytes there.
  ++upStack;

  // Newer versions of GCC don't like when naked functions call regular functions
  // so we use call an assembly gate function instead
  __asm__ __volatile__ (
    "call appMon_asm_gate \n"
  );
}

If you're pulling a lot of current to drive whatever the relays switch, be careful to keep the power wires away from the Nano.

I'd be inclined to have those relays switching LEDs with a separate power supply to start isolating the problem.

this code includes the interrupt service routine for the watch dog. It comes from

The relays are powered via a separate 12v power supply and switched via an optocoupler.

the symptom is that the nano becomes unresponsive. No serial output is produced and it seems like there is a crash. In order to restart the nano, the power has to be removed. The watchdog does report an issue via the serial port when the programme restarts but it is not possible to track the error back through the assembly file.

There can be large voltage spikes when you power up a switched item. That’s the sort of thing that sometimes causes a crash.

If you replace each item with an LED, you can gain some intelligence about what’s going on.

Since your application is not trivial, it's hard to spot the bug. Perhaps is just a typo somewhere or there a more serious issue (in the firmware on even in the hardware, like others said). It's an AVR, there is no such a "crash" like an exception trap. On the worst case the MCU will reset.

I'd suggest to do the following:

  1. add in the main loop a Serial.print(".") every second, like:
static unsigned long timeRef = 0;
if (millis() - timeRef > 1000)
{
    Serial.print(".");
    timeRef = millis();
}

in this way you can easily detect (especially together hint #2) if your application is stuck somewhere else the main loop.

  1. comment out, one by one, the calls in the main loop and check which one (if any) leads to the unexpected behavior. When you find (hopefully!) the offending one, go deeper and do the same with the instructions inside.

the system works for a few days. All relays operate as expected. The temperature sensors report temperature accurately. I'm not sure how replacing relays with LEDs would provide any more insight. The relay boad does have optocoupler isolation - simiarl to this one:

How can I determine if there are voltage spikes on swithcing and what could be done to address this?