As I mentioned above, these devices remain idle and breathing 99% of the time, and are not actively publishing any information. I also figured I could go with SYSTEM_MODE(AUTOMATIC) and leave the cellular connection management to the device since I have no need to ever disconnect after startup. None of the devices have ever been stuck inside the loop because of Particle.publish, and they are quite responsive to cloud commands. Attached below is my code:
#include <Particle.h>
PRODUCT_VERSION(10);
const int relay = D4;
const int boardLED = D7;
const int address = 1000;
const int nameSaveAddress = 1001;
const int maxNameLength = 100;
String device_name = "N/A";
bool nameUpdate = false;
bool readNameAtStart = true;
const int isNameSavedAddress = 1002;
String wellStateVariable = "N/A";
char eeprom_device_name[maxNameLength];
SerialLogHandler logHandler;
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(AUTOMATIC);
bool startWell = false;
bool stopWell = false;
bool nameSet = true;
unsigned long lastStopTime = 0;
void subscriptionHandler(const char *topic, const char *data)
{
Log.info("Started subscriptionHandler");
String receivedName = String(data);
Log.info("Received Name: " + receivedName);
saveNameToEEPROM(receivedName);
device_name = receivedName;
Log.info("Confirming name save to EEPROM...");
readNameFromEEPROM();
int nameSaveIntTest = EEPROM.read(isNameSavedAddress);
if (nameSaveIntTest != 1)
{
Log.error("isNameSavedAddress was not equal to one... fixing...");
EEPROM.put(isNameSavedAddress, 1);
}
}
void setup()
{
pinMode(relay, OUTPUT);
pinMode(boardLED, OUTPUT);
if (EEPROM.read(address) == 1)
{
startWell = true;
}
else
{
stopWell = true;
}
readNameFromEEPROM();
if (EEPROM.read(isNameSavedAddress) != 1 || device_name == "N/A" || device_name == "DA" || device_name == "RU")
{
readNameAtStart = false;
nameUpdate = true;
EEPROM.put(isNameSavedAddress, 1);
}
Particle.function("webControl", controlWell);
Particle.function("onlineReset", remoteReset);
Particle.variable("state", wellStateVariable);
Particle.subscribe("particle/device/name", subscriptionHandler);
}
void loop()
{
if (Particle.connected() && nameUpdate)
{
nameUpdate = false;
Particle.publish("particle/device/name");
readNameFromEEPROM();
}
//Well Start Code: MAKE SURE TO FIX TIME DELAY AFTER TESTING
if (startWell && millis() - lastStopTime > 4 * 60 * 1000UL)
{
Log.info("Well On and name: " + device_name);
startWell = false;
digitalWrite(relay, HIGH);
digitalWrite(boardLED, HIGH);
int checkEeprom = EEPROM.read(address);
if (checkEeprom != 1)
{
EEPROM.put(address, 1);
}
if (Particle.connected())
{
String wellState = "Well Started";
String status = String::format("{\"status\": \"%s\", \"device_name\": \"%s\"}", wellState.c_str(), device_name.c_str());
Particle.publish("pushoverStatus", status, PRIVATE, WITH_ACK);
}
wellStateVariable = "On";
System.disableUpdates();
//Well Stop Code:
}
else if (stopWell)
{
Log.info("Well Off and name: " + device_name);
stopWell = false;
digitalWrite(relay, LOW);
digitalWrite(boardLED, LOW);
int checkEeprom = EEPROM.read(address);
if (checkEeprom != 0)
{
EEPROM.put(address, 0);
}
lastStopTime = millis();
if (Particle.connected())
{
String wellState = "Well Stopped";
String status = String::format("{\"status\": \"%s\", \"device_name\": \"%s\"}", wellState.c_str(), device_name.c_str());
Particle.publish("pushoverStatus", status, PRIVATE, WITH_ACK);
}
wellStateVariable = "Off";
System.enableUpdates();
}
if (readNameAtStart)
{
readNameFromEEPROM();
readNameAtStart = false;
}
}
//Online API Call Method:
int controlWell(String webInput)
{
if (webInput == "start")
{
if (millis() - lastStopTime < 4 * 60 * 1000UL)
{
unsigned long elapsedTime = millis() - lastStopTime;
unsigned long remainingTime = 4 * 60 * 1000UL - elapsedTime;
unsigned long seconds = remainingTime / 1000UL;
unsigned long minutes = seconds / 60UL;
seconds = seconds % 60UL;
String timeStr = String(minutes) + " minutes " + String(seconds) + " seconds";
String notReady = "Well start in: " + timeStr;
String notReadyFormat = String::format("{\"status\": \"%s\", \"device_name\": \"%s\"}", notReady.c_str(), device_name.c_str());
Particle.publish("pushoverStatus", notReadyFormat, PRIVATE, WITH_ACK);
startWell = true;
}
else
{
startWell = true;
}
return 1;
}
else if (webInput == "stop")
{
if (startWell)
{
String startCancel = "Well start cancelled";
String startCancelFormat = String::format("{\"status\": \"%s\", \"device_name\": \"%s\"}", startCancel.c_str(), device_name.c_str());
Particle.publish("pushoverStatus", startCancelFormat, PRIVATE, WITH_ACK);
startWell = false;
}
else
{
stopWell = true;
}
return 0;
}
else
{
return -1;
}
}
void saveNameToEEPROM(const String &data)
{
int nameLength = data.length();
if (nameLength >= maxNameLength)
{
Log.error("Name length exceeds maximum size. Not saving to EEPROM.");
return;
}
// Save the name to EEPROM, including the null terminator
data.toCharArray(eeprom_device_name, maxNameLength);
EEPROM.put(nameSaveAddress, eeprom_device_name);
Log.info("Name saved to EEPROM: " + data);
// Update the global String variable
device_name = data;
}
void readNameFromEEPROM()
{
// Read the name from EEPROM
EEPROM.get(nameSaveAddress, eeprom_device_name);
eeprom_device_name[maxNameLength - 1] = '\0'; // Ensure null-termination
// Check if the savedValue is valid (not an empty string)
if (eeprom_device_name[0] != '\0')
{
// Replace non-alphanumeric characters (excluding underscores) with 'A' until the null-terminator
for (int i = 0; i < maxNameLength; i++)
{
if (eeprom_device_name[i] == '\0')
{
break; // Stop replacing characters at the null-terminator
}
if (!isAlphaNumeric(eeprom_device_name[i]) && eeprom_device_name[i] != '_')
{
if (eeprom_device_name[0] == 'D')
{
eeprom_device_name[i] = 'A';
}
else if (eeprom_device_name[0] == 'R')
{
eeprom_device_name[i] = 'U';
}
}
}
// Update the global String variable
device_name = eeprom_device_name;
// Display the saved string
Log.info("Saved Name from EEPROM: " + device_name);
}
else
{
// If no valid data is found, set the default string
Log.error("No valid name found in EEPROM!");
// Update the global String variable
device_name = "N/A";
}
}
int remoteReset(String textInput)
{
if (textInput == "sr")
{
EEPROM.put(isNameSavedAddress, 1);
delay(1000);
System.reset();
return 0;
}
else if (textInput == "hr")
{
EEPROM.put(address, 0);
EEPROM.put(isNameSavedAddress, 0);
delay(1000);
System.reset();
return 1;
}
else if (textInput == "nr")
{
EEPROM.put(isNameSavedAddress, 0);
nameUpdate = true;
return 2;
}
else if (textInput == "nt")
{
readNameFromEEPROM();
return 3;
}
else
{
return -1;
}
}