Hey guys! Recently I migrated my pool controller from a Wemos d1 (ESP8266 based) to an ESP32 devkit, because I wanted to add a couple more relays and not have to deal with the intricacies of some pins during bootup. The issue I'm having is that the device will show as online for a few hours after boot and then randomly go offline forever until reset and it stops tracking cloud variables. When this happens, the device is still perfectly pingable, so I know it's not out of wifi coverage. In fact, I can kick the device from a hotspot and it'll happily connect to another and resume being pinged, but still be offline in the Arduino cloud devices list. Yesterday I added code to make the onboard LED blink all the time, so I could verify that the loop is still running correctly. The device still works to the point I can do OTA uploads using the ArduinoOTA library when it's in this broken state and they work perfectly too, basically the only thing that stops working is the syncing of cloud variables. What could be causing this?
Here's the thing's code FWIW
#include "arduino_secrets.h"
/*
Sketch generated by the Arduino IoT Cloud Thing "Untitled 2"
https://create.arduino.cc/cloud/things/20837b3e-597f-4326-b399-05fd11dbbc1b
Arduino IoT Cloud Variables description
The following variables are automatically generated and updated when changes are made to the Thing
CloudSwitch n;
Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
which are called when their values are changed from the Dashboard.
These functions are generated with the Thing and added at the end of this sketch.
*/
/*
Sketch generated by the Arduino IoT Cloud Thing "Untitled 2"
https://create.arduino.cc/cloud/things/15981c16-9961-4d4c-8dda-89ac835b5d75
Arduino IoT Cloud Variables description
The following variables are automatically generated and updated when changes are made to the Thing
CloudSwitch filtroPileta;
CloudSwitch ionizadorPileta;
CloudSwitch llenadoPileta;
CloudSwitch lucesPileta;
CloudSwitch riegoFondo;
CloudSwitch riegoFrente;
CloudSwitch riegoMedio;
Variables which are marked as READ/WRITE in the Cloud Thing will also have functions
which are called when their values are changed from the Dashboard.
These functions are generated with the Thing and added at the end of this sketch.
*/
#include <WiFi.h>
#include <WebServer.h>
#include <WiFiUdp.h>
#include <WiFiClient.h>
#include <ArduinoOTA.h>
#include "thingProperties.h"
#define LED_BUILTIN 1
typedef struct {
std::string name;
int gpioPin;
unsigned long long limit;
unsigned long long elapsed;
bool needsPump;
bool needs24v;
bool needs12v;
bool on;
CloudSwitch *cloudSwitch;
} Device;
// Pin configuration
static const int kChannelCount = 10;
Device devices[kChannelCount] = {{
.name = "Riego medio",
.gpioPin = 26,
.limit = 15 * 1000 * 60,
.elapsed = 0,
.needsPump = true,
.needs24v = true,
.needs12v = false,
.on = false,
.cloudSwitch = &riegoMedio
},
{
.name = "Riego frente",
.gpioPin = 32,
.limit = 15 * 1000 * 60,
.elapsed = 0,
.needsPump = true,
.needs24v = true,
.needs12v = false,
.on = false,
.cloudSwitch = &riegoFrente
},{
.name = "Riego fondo",
.gpioPin = 25,
.limit = 15 * 1000 * 60,
.elapsed = 0,
.needsPump = true,
.needs24v = true,
.needs12v = false,
.on = false,
.cloudSwitch = &riegoFondo
},{
.name = "Llenado pileta",
.gpioPin = 23,
.limit = 30 * 1000 * 60,
.elapsed = 0,
.needsPump = true,
.needs24v = true,
.needs12v = false,
.on = false,
.cloudSwitch = &llenadoPileta
},{
.name = "Filtro pileta",
.gpioPin = 21,
.limit = 0,
.elapsed = 0,
.needsPump = false,
.needs24v = false,
.needs12v = false,
.on = false,
.cloudSwitch = &filtroPileta
},{
.name = "Luces pileta",
.gpioPin = 16,
.limit = 0,
.elapsed = 0,
.needsPump = false,
.needs24v = false,
.needs12v = true,
.on = false,
.cloudSwitch = &lucesPileta
},{
.name = "Ionizador pileta",
.gpioPin = 17,
.limit = 0,
.elapsed = 0,
.needsPump = false,
.needs24v = false,
.needs12v = true,
.on = false,
.cloudSwitch = &ionizadorPileta
},
{
.name = "12v",
.gpioPin = 18,
.limit = 0,
.elapsed = 0,
.needsPump = false,
.needs24v = false,
.needs12v = false,
.on = false,
.cloudSwitch = NULL
},
{
.name = "24v",
.gpioPin = 22,
.limit = 0,
.elapsed = 0,
.needsPump = false,
.needs24v = false,
.needs12v = false,
.on = false,
.cloudSwitch = NULL
},
{
.name = "pump",
.gpioPin = 19,
.limit = 0,
.elapsed = 0,
.needsPump = false,
.needs24v = false,
.needs12v = false,
.on = false,
.cloudSwitch = NULL
}};
const int deviceIndexPump = kChannelCount - 1;
const int deviceIndex24v = kChannelCount - 2;
const int deviceIndex12v = kChannelCount - 3;
unsigned long long lastTime = 0;
unsigned long long ledElapsed = 0;
void InitOTA()
{
// Port defaults to 8266
ArduinoOTA.setPort(8266);
// Hostname defaults to esp8266-[ChipID]
ArduinoOTA.setHostname("ESP_POOL");
// No authentication by default
// ArduinoOTA.setPassword("admin");
// Password can be set with it's md5 value as well
// MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
// ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
ArduinoOTA.onStart([]() {
String type;
if (ArduinoOTA.getCommand() == U_FLASH) {
type = "sketch";
} else { // U_SPIFFS
type = "filesystem";
}
// NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
Serial.println("Start updating " + type);
});
ArduinoOTA.onEnd([]() {
Serial.println("\nEnd");
});
ArduinoOTA.onProgress([](unsigned int progress, unsigned int total) {
Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
});
ArduinoOTA.onError([](ota_error_t error) {
Serial.printf("Error[%u]: ", error);
if (error == OTA_AUTH_ERROR) {
Serial.println("Auth Failed");
} else if (error == OTA_BEGIN_ERROR) {
Serial.println("Begin Failed");
} else if (error == OTA_CONNECT_ERROR) {
Serial.println("Connect Failed");
} else if (error == OTA_RECEIVE_ERROR) {
Serial.println("Receive Failed");
} else if (error == OTA_END_ERROR) {
Serial.println("End Failed");
}
});
ArduinoOTA.begin();
}
void setup() {
WiFi.mode(WIFI_STA);
//Serial.begin(115200);
for (int i = 0; i < kChannelCount; ++i) {
pinMode(devices[i].gpioPin, OUTPUT);
digitalWrite(devices[i].gpioPin, HIGH);
}
pinMode(LED_BUILTIN, OUTPUT);
// Defined in thingProperties.h
initProperties();
// Connect to Arduino IoT Cloud
ArduinoCloud.begin(ArduinoIoTPreferredConnection);
setDebugMessageLevel(2);
ArduinoCloud.printDebugInfo();
InitOTA();
lastTime = millis();
}
void loop() {
ArduinoOTA.handle();
ArduinoCloud.update();
const unsigned long long now = millis();
const unsigned long long dt = now - lastTime;
ledElapsed += dt;
if (ledElapsed % 2000 > 1000) {
digitalWrite(LED_BUILTIN, HIGH);
}
else{
digitalWrite(LED_BUILTIN, LOW);
}
// Track time limits and updates from cloud switches
for (int index = 0; index < kChannelCount; ++index) {
if (devices[index].cloudSwitch != NULL && *(devices[index].cloudSwitch) != devices[index].on) {
// Grab changes from cloud
action(index, *(devices[index].cloudSwitch));
}
if (devices[index].limit != 0 && devices[index].on) {
devices[index].elapsed += dt;
//Serial.printf("device %d %llu vs %llu\n", index, devices[index].elapsed, devices[index].limit);
if (devices[index].elapsed > devices[index].limit) {
if (devices[index].cloudSwitch != NULL) {
*(devices[index].cloudSwitch) = false;
}
action(index, false);
devices[index].elapsed = 0;
}
}
else {
devices[index].elapsed = 0;
}
}
// Figure out dependencies
bool needs12v = false;
bool needs24v = false;
bool needsPump = false;
for (int index = 0; index < kChannelCount; ++index) {
needs12v = needs12v || (devices[index].on && devices[index].needs12v);
needs24v = needs24v || (devices[index].on && devices[index].needs24v);
needsPump = needsPump || (devices[index].on && devices[index].needsPump);
}
action(deviceIndex12v, needs12v);
action(deviceIndex24v, needs24v);
action(deviceIndexPump, needsPump);
lastTime = now;
}
void action(int deviceIndex, bool state) {
if (state != devices[deviceIndex].on) {
devices[deviceIndex].on = state;
digitalWrite(devices[deviceIndex].gpioPin, devices[deviceIndex].on ? LOW : HIGH);
Serial.printf("Turning %s %d\n", devices[deviceIndex].name.c_str(), devices[deviceIndex].on);
}
}
void onRiegoMedioChange() {
}
void onRiegoFrenteChange() {
}
void onRiegoFondoChange() {
}
void onLlenadoPiletaChange() {
}
void onFiltroPiletaChange() {
}
void onLucesPiletaChange() {
}
void onIonizadorPiletaChange() {
}