I've been reading and researching and just can't figure out how best to utilize a WDT in my code.
The code monitors pool water chemistry and send regular readings to Smartthings.
There are aspects to that that requires multiple seconds of time to elapse (through required delays, normal connectivity time requirements, and waiting periods between readings and between uploading).
I've tried putting in numerous WDT_resets, but it's not catching it.
A single pass through loop(), depending on the final waiting periods I choose, could be 60 minutes.
Is there a way for the system to reset itself if there's no activity/life after so long a period?
I tried to include the code using the CODE tags within this posting but it's more than 9000 characters so I have attached it as a text file. If there's an alternative way that is preferred or easier for the reader, please let me know.
I fully realize the code is novice-level (at best) and not too pretty.
I greatly appreciate the useful/educational criticism, so fire away.
I see two things immediately.
There are a few delay()s sprinkled about... of course they’ll hold things ip.
When you use millis(), the interval test you’re using will fail in the long term... use the subtraction method explained elsewhere to avoid the 50 day rollover issue.
I can’t see specifically where the loop goes off the tracker go wrong, but blocks of code that can take an unknown length of time - like calling out to the internet - should maybe have their own timeouts built in, while disabling the WDT, or making those functions asynchronous - so the send and response are handled in separate blocks of code based on ‘when’ they occur, not holding up the surrounding code.
I see two things immediately.
There are a few delay()s sprinkled about... of course they’ll hold things ip.
When you use millis(), the interval test you’re using will fail in the long term... use the subtraction method explained elsewhere to avoid the 50 day rollover issue.
I can’t see specifically where the loop goes off the tracker do long, but blocks of code that can take an unknown length of time - like calling out to the internet - should maybe have their own timeouts built in, while disabling the WDT, or making those functions asynchronous - so the send and response are handled in separate blocks of code based on ‘when’ they occur, not holding up the surrounding code.
Thank you for the analysis and suggestions.
Regarding the delays, my understanding is that the hardware needs them.
Could you please point out which millis() interval test will fail?
I can't say I have a clear understanding of how to include timeouts in the functions that call out to the internet if those functions require multiple seconds to accomplish, but I'm pondering it.
And I really have to think hard about how I would make functions asynchronous -- separate send and response tasks.
I've cleaned up loop() some:
void loop() {
//wdt_reset();
if ((millis() > time_now + updateFrequency) || millis() < 360000) { // "OR" to make initial reading
smartthing.run(); // run smartthings communications logic
time_now = millis();
for (int channel = 0; channel < TOTAL_CIRCUITS; channel++) { // loop through all the sensors FOR CONTINUOUS READINGS
delay(1000);
Wire.beginTransmission(channel_ids[channel]); // call the circuit by its ID number.
Wire.write('r'); // request a reading by sending 'r'
Wire.endTransmission(); // end the I2C data transmission.
delay(1000); // AS circuits need a 1 second before the reading is ready
sensor_bytes_received = 0; // reset data counter
memset(sensordata, 0, sizeof(sensordata)); // clear sensordata array;
Wire.requestFrom(channel_ids[channel], 48, 1); // call the circuit and request 48 bytes (this is more then we need).
code = Wire.read();
while (Wire.available()) { // are there bytes to receive?
in_char = Wire.read(); // receive a byte.
if (in_char == 0) { // null character indicates end of command
Wire.endTransmission(); // end the I2C data transmission.
break; // exit the while loop, we're done here
}
else {
sensordata[sensor_bytes_received] = in_char; // append this byte to the sensor data array.
sensor_bytes_received++;
}
}
switch (code) { // switch case based on what the response code is.
case 1: // decimal 1 means the command was successful.
if (channel_names[channel] == "TEMP")
{
DegreesC = atof(sensordata);
DegreesF = (DegreesC * 1.8) + 32;
String stMsg = "temperature1 " + String(DegreesF);
smartthing.send(stMsg);
tempdata = atof(sensordata);
}
else {
if (channel_names[channel] == "PH")
{
String stMsg = "voltage1 " + String(sensordata);
smartthing.send(stMsg);
phdata = atof(sensordata);
}
else {
if (channel_names[channel] == "ORP")
{
String stMsg = "voltage2 " + String(sensordata);
smartthing.send(stMsg);
orpdata = atof(sensordata);
}
else {
String stMsg = "voltage3 " + String(sensordata);
smartthing.send(stMsg);
whoknowsdata == atof(sensordata);
}
}
}
StartTime = millis();
Serial.print("Elapsed time at end of Case 1, b4 sendtolcd: ");
Serial.println(ElapsedTime);
sendtoLcd(phdata, orpdata, tempdata);
Serial.println("238 sendtolcd -- inside case 1");
updateGroveStreams(channel_names[channel]);
Count++;
break;
case 2: // decimal 2 means the command has failed.
Serial.println("command failed");
break;
case 254: // decimal 254 means the command has not yet been finished calculating.
Serial.println("circuit not ready");
break;
case 255: // decimal 255 means there is no further data to send.
Serial.println("no data");
break;
} //end switch
} //end for
} //end if millis update
CurrentTime = millis();
ElapsedTime = CurrentTime - StartTime;
/*
if (CurrentTime > StartTime) {
ElapsedTime = CurrentTime - StartTime;
}
else
ElapsedTime = 0;
*/
if ((PreviousElapsedTime / 60000) != (ElapsedTime / 60000)) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Min Since Read: ");
lcd.print(ElapsedTime / 60000);
sendtoLcd(phdata, orpdata, tempdata);
} //end elapsedtime if
PreviousElapsedTime = ElapsedTime;
}
will possibly miss an event when millis() rolls over, or if the program allows millis() to skip a tick.
if ((millis() - start_time) > updateInterval)
Will work better.
Note we are keeping a record of the start time, and comparing with the required interval
Take a look at the various millis() examples, which explain rollover in greater detail.
Using delay() to wait for a device to respond is common, but not best coding practice. Unfortunately, if the delay is necessary, the ‘right’ way to code it is with a local state-machine, handling that slow device asynchronously while the other code keeps on running.
A common example of this is dealing with a modem sending SMS messages, where AT responses can take a second or more to complete. You tell the modem to do something, go away to handle other stuff, and only when the modem responds to your command - handle that reply.
It’s a bit lengthy and complicated to post, but AFAIK, I’m the only person with async modem code here... when I get a chance to package it up, I’ll post a tutorial.