OTA fixes + enable when network connected and switch pressed

This commit is contained in:
Emanuele Trabattoni
2025-08-06 09:49:46 +02:00
parent 5bff567863
commit e37aa58398
7 changed files with 102 additions and 74 deletions

View File

@@ -1,3 +1,41 @@
#define DI_CONFRESET 0
#define DI_RESTART 1 enum RO // relay output channels
#define DI_OTAENABLE 7 {
P1 = 0,
P2 = 1,
P3 = 2,
P4 = 3,
RO_4 = 4,
FST_FLOOR = 5,
GND_FLOOR = 6,
PUMP_HT = 7,
PUMP_IRR = 8,
ZONE1 = 9,
ZONE2 = 10,
ZONE3 = 11,
DRIP = 12,
RETURN = 13,
RO_14 = 14,
RO_15 = 15,
RO_MAX = 16 // unused to detect invalid values
};
enum DI // digital input channels
{
CONFRESET = 0,
RESTART = 1,
DI_2 = 2,
DI_3 = 3,
DI_4 = 4,
DI_6 = 6,
OTAENABLE = 7,
PUMP_PRESSURE = 8,
RAIN = 9,
IRR_OVERRIDE = 10,
DI_11 = 11,
DI_12 = 12,
DI_13 = 13,
DI_14 = 14,
DI_15 = 15,
DI_MAX = 16
}; // unused to detect invalid values

View File

@@ -31,12 +31,11 @@ upload_port = 10.0.2.139
platform = ${env:esp32-s3-waveshare8.platform} platform = ${env:esp32-s3-waveshare8.platform}
board = ${env:esp32-s3-waveshare8.board} board = ${env:esp32-s3-waveshare8.board}
framework = ${env:esp32-s3-waveshare8.framework} framework = ${env:esp32-s3-waveshare8.framework}
lib_deps = lib_deps = ${env:esp32-s3-waveshare8.lib_deps}
bblanchon/ArduinoJson@^7.4.2
arduino-libraries/NTPClient@^3.2.1 board_build.filesystem = ffat
knolleary/PubSubClient@^2.8 board_build.partitions = fatfs_partition.csv ; se stai usando uno custom
robtillaart/CRC@^1.0.3
hideakitai/DebugLog@^0.8.4
build_type = debug build_type = debug
build_flags = build_flags =
-O0 -O0
@@ -47,5 +46,3 @@ build_flags =
-fno-tree-sra -fno-tree-sra
-fno-builtin -fno-builtin
board_build.filesystem = ffat
board_build.partitions = fatfs_partition.csv ; se stai usando uno custom

View File

@@ -264,7 +264,7 @@ namespace commands
{ {
devices_t *dev = (devices_t *)pvTimerGetTimerID(th); devices_t *dev = (devices_t *)pvTimerGetTimerID(th);
LOG_INFO("setIrrigation shutdown pump"); LOG_INFO("setIrrigation shutdown pump");
dev->io.digitalOutWrite(RO::IRR_PUMP, false); dev->io.digitalOutWrite(RO::PUMP_IRR, false);
s_irrigationPumpTimer = NULL; s_irrigationPumpTimer = NULL;
xTimerDelete(th, 0); // delete the timer on expiry xTimerDelete(th, 0); // delete the timer on expiry
} }
@@ -340,7 +340,7 @@ namespace commands
if (!s_irrigationPumpTimer) // Pump has not yet started if (!s_irrigationPumpTimer) // Pump has not yet started
{ {
s_irrigationPumpTimer = xTimerCreate("pumpTimer", pdMS_TO_TICKS(pumpTime), false, (void *)&dev, resetWaterPump); s_irrigationPumpTimer = xTimerCreate("pumpTimer", pdMS_TO_TICKS(pumpTime), false, (void *)&dev, resetWaterPump);
dev.io.digitalOutWrite(RO::IRR_PUMP, true); dev.io.digitalOutWrite(RO::PUMP_IRR, true);
xTimerStart(s_irrigationPumpTimer, 0); // immediate start pump timer xTimerStart(s_irrigationPumpTimer, 0); // immediate start pump timer
LOG_INFO("setIrrigation pump time", pumpTime); LOG_INFO("setIrrigation pump time", pumpTime);
} }
@@ -412,13 +412,15 @@ namespace commands
ArduinoJson::JsonDocument response; ArduinoJson::JsonDocument response;
response["cmd"] = "getInputStatus"; response["cmd"] = "getInputStatus";
const std::vector<bool> inStatus(dev.io.digitalInReadPort()); const std::vector<bool> inStatus(dev.io.digitalInReadPort());
if (inStatus.empty() || inStatus.size() != dev.io.getInNum()) { if (inStatus.empty() || inStatus.size() != dev.io.getInNum())
{
response["values"] = "invalid"; response["values"] = "invalid";
return response; return response;
} }
uint8_t i(0); uint8_t i(0);
for (auto s: inStatus){ for (auto s : inStatus)
const std::string k("DI"+std::to_string(i)); {
const std::string k("DI" + std::to_string(i));
response["values"][k.c_str()] = s; response["values"][k.c_str()] = s;
} }
LOG_INFO("getInputStatus ->", printBoolVec(inStatus).c_str()); LOG_INFO("getInputStatus ->", printBoolVec(inStatus).c_str());
@@ -429,13 +431,15 @@ namespace commands
ArduinoJson::JsonDocument response; ArduinoJson::JsonDocument response;
response["cmd"] = "getOutputStatus"; response["cmd"] = "getOutputStatus";
const std::vector<bool> inStatus(dev.io.digitalOutReadPort()); const std::vector<bool> inStatus(dev.io.digitalOutReadPort());
if (inStatus.empty() || inStatus.size() != dev.io.getOutNum()) { if (inStatus.empty() || inStatus.size() != dev.io.getOutNum())
{
response["values"] = "invalid"; response["values"] = "invalid";
return response; return response;
} }
uint8_t i(0); uint8_t i(0);
for (auto s: inStatus){ for (auto s : inStatus)
const std::string k("DO"+std::to_string(i)); {
const std::string k("DO" + std::to_string(i));
response["values"][k.c_str()] = s; response["values"][k.c_str()] = s;
} }
LOG_INFO("getOutputStatus ->", printBoolVec(inStatus).c_str()); LOG_INFO("getOutputStatus ->", printBoolVec(inStatus).c_str());

View File

@@ -8,30 +8,10 @@
#include <config.h> #include <config.h>
#include <devices.h> #include <devices.h>
#include <pinlist.h>
namespace commands namespace commands
{ {
enum RO // relay output channels
{
P1,
P2,
P3,
P4,
NC_1,
FST_FLOOR,
GND_FLOOR,
PUMP_HT,
IRR_PUMP,
Z1,
Z2,
Z3,
AUX,
RETURN,
NC_3,
NC_4,
RO_MAX // unused to detect invalid values
};
static const std::map<const std::string, uint8_t> c_hpLimitsMap = {{"P1", RO::P1}, static const std::map<const std::string, uint8_t> c_hpLimitsMap = {{"P1", RO::P1},
{"P2", RO::P2}, {"P2", RO::P2},
{"P3", RO::P3}, {"P3", RO::P3},
@@ -43,10 +23,10 @@ namespace commands
{"ground", RO::GND_FLOOR}}; {"ground", RO::GND_FLOOR}};
static const std::map<const std::string, uint8_t> c_irrigationValveMap = {{"ricircolo", RO::RETURN}, static const std::map<const std::string, uint8_t> c_irrigationValveMap = {{"ricircolo", RO::RETURN},
{"zone1", RO::Z1}, {"zone1", RO::ZONE1},
{"zone2", RO::Z2}, {"zone2", RO::ZONE2},
{"zone3", RO::Z3}, {"zone3", RO::ZONE3},
{"rubinetti", RO::AUX}}; {"rubinetti", RO::DRIP}};
static std::map<const std::string, std::pair<const char *, TimerHandle_t>> c_irrigationTimerMap = {{"ricircolo", {"ricircolo", NULL}}, static std::map<const std::string, std::pair<const char *, TimerHandle_t>> c_irrigationTimerMap = {{"ricircolo", {"ricircolo", NULL}},
{"zone1", {"zone1", NULL}}, {"zone1", {"zone1", NULL}},
@@ -97,28 +77,26 @@ namespace commands
}; };
static const std::map<const std::string, Command> s_commandMap = { static const std::map<const std::string, Command> s_commandMap = {
// TEST
{"setBuzz", Commands::setBuzz}, {"setBuzz", Commands::setBuzz},
// CONFIG
{"setConfig", Commands::setConfig}, {"setConfig", Commands::setConfig},
{"getConfig", Commands::getConfig}, {"getConfig", Commands::getConfig},
// CRONJOBS
{"loadCronJob", Commands::loadCronJob}, {"loadCronJob", Commands::loadCronJob},
{"setCronJob", Commands::setCronJob}, {"setCronJob", Commands::setCronJob},
{"getCronJob", Commands::getCronJob}, {"getCronJob", Commands::getCronJob},
{"delCronJob", Commands::delCronJob}, {"delCronJob", Commands::delCronJob},
{"storeCronJob", Commands::storeCronJob}, {"storeCronJob", Commands::storeCronJob},
// SETTERS
{"setHPlimit", Commands::setHPlimit}, {"setHPlimit", Commands::setHPlimit},
{"setHeating", Commands::setHeating}, {"setHeating", Commands::setHeating},
{"setIrrigation", Commands::setIrrigation}, {"setIrrigation", Commands::setIrrigation},
// GETTERS
{"getHPpower", Commands::getHPpower}, {"getHPpower", Commands::getHPpower},
{"setHeating", Commands::setHeating},
{"getInputStatus", Commands::getInputStatus}, {"getInputStatus", Commands::getInputStatus},
{"getOutputStatus", Commands::getOutputStatus}, {"getOutputStatus", Commands::getOutputStatus},
// NTP and Time
{"getTimeDrift", Commands::getTimeDrift}, {"getTimeDrift", Commands::getTimeDrift},
{"setTimeNTP", Commands::setTimeNTP}, {"setTimeNTP", Commands::setTimeNTP},
}; };

View File

@@ -55,14 +55,6 @@ void loop()
LOG_INFO("Temperature sensors connected ->", sensors); LOG_INFO("Temperature sensors connected ->", sensors);
// Initialize OTA updater if needed // Initialize OTA updater if needed
auto ota = OTA(devices); auto ota = OTA(devices);
if (io.digitalInRead(DI_OTAENABLE)) {
buzzer.beepRepeat(25,25, NOTE_A);
delay(1000);
if (io.digitalInRead(DI_OTAENABLE)) { // maintain keyPress for 1s
ota.begin();
}
buzzer.beep(100, NOTE_G);
}
//////////////// DEVICES //////////////// //////////////// DEVICES ////////////////
//////////////// MQTT //////////////// //////////////// MQTT ////////////////
@@ -134,6 +126,17 @@ void loop()
led.setColor(led.COLOR_RED); led.setColor(led.COLOR_RED);
return; return;
} }
if (io.digitalInRead(DI::OTAENABLE)) // Initialize OTA, BLUE
{
buzzer.beepRepeat(25, 25, NOTE_A);
delay(1000);
if (io.digitalInRead(DI::OTAENABLE))
{ // maintain keyPress for 1s
ota.begin();
}
buzzer.beep(100, NOTE_G);
delay(100);
}
// Get RTC time at ethernet connection // Get RTC time at ethernet connection
time_t ntpTime; time_t ntpTime;
uint8_t timeRetries(0); uint8_t timeRetries(0);
@@ -198,7 +201,7 @@ void loop()
mqtt.publish(conf.m_mqttPublish["temperatures"], ti); mqtt.publish(conf.m_mqttPublish["temperatures"], ti);
}; };
if (io.digitalInRead(DI_CONFRESET)) // ROSSO - Config Reset if (io.digitalInRead(DI::CONFRESET)) // ROSSO - Config Reset
{ {
LOG_WARN("Config RESET!"); LOG_WARN("Config RESET!");
buzzer.beep(450, NOTE_E); buzzer.beep(450, NOTE_E);
@@ -206,7 +209,7 @@ void loop()
conf.resetConfig(); conf.resetConfig();
} }
if (io.digitalInRead(DI_RESTART)) // GIALLO - Restart if (io.digitalInRead(DI::RESTART)) // GIALLO - Restart
{ {
LOG_WARN("RESTART!"); LOG_WARN("RESTART!");
buzzer.beep(450, NOTE_D); buzzer.beep(450, NOTE_D);

View File

@@ -3,7 +3,7 @@
#define STACK_DEPTH 4096 #define STACK_DEPTH 4096
#define TASK_PRIORITY 2 #define TASK_PRIORITY 2
OTA::OTA(const devices_t &dev) : m_dev(dev), m_taskHandle(NULL), m_updating(false) OTA::OTA(const devices_t &dev) : m_dev(dev), m_taskHandle(NULL), m_updating(false), m_prevPercent(0)
{ {
LOG_WARN("OTA begin, waiting for connection on [", dev.eth.localIP().toString().c_str(), "]"); LOG_WARN("OTA begin, waiting for connection on [", dev.eth.localIP().toString().c_str(), "]");
} }
@@ -26,15 +26,16 @@ void OTA::begin()
ArduinoOTA.onEnd(s_onEnd); ArduinoOTA.onEnd(s_onEnd);
ArduinoOTA.onProgress(s_onProgress); ArduinoOTA.onProgress(s_onProgress);
ArduinoOTA.onError(s_onError); ArduinoOTA.onError(s_onError);
if (!xTaskCreate(handle, "otaUpdate", STACK_DEPTH, this, TASK_PRIORITY, &m_taskHandle) != pdPASS) if (xTaskCreate(handle, "otaUpdate", STACK_DEPTH, this, TASK_PRIORITY, &m_taskHandle) != pdPASS)
{ {
m_taskHandle = NULL; m_taskHandle = NULL;
LOG_ERROR("OTA failed to create handle task"); LOG_ERROR("OTA failed to create handle task");
} }
ArduinoOTA.begin(); // start the OTA server ArduinoOTA.begin(); // start the OTA server
m_dev.led.blinkAlternate(100, 100, m_dev.led.COLOR_ORANGE, m_dev.led.COLOR_SKYBLUE);
m_dev.led.setEnforce(true); // take unique control of the LED m_dev.led.setEnforce(true); // take unique control of the LED
m_dev.led.blinkAlternate(50, 50, m_dev.led.COLOR_GREEN, m_dev.led.COLOR_YELLOW);
m_active = true; m_active = true;
LOG_WARN("OTA started");
return; return;
} }
@@ -64,7 +65,12 @@ bool OTA::isActive()
void OTA::onProgress(const uint32_t progress, const uint32_t total) void OTA::onProgress(const uint32_t progress, const uint32_t total)
{ {
LOG_INFO("OTA progress [", (progress * 100.0f) / total, "]%"); float percent = (progress * 100.0f) / total;
if (percent > m_prevPercent + 5.0f)
{
LOG_INFO("OTA progress [", percent, "]%");
m_prevPercent = percent;
}
} }
void OTA::onStart() void OTA::onStart()
{ {
@@ -73,6 +79,7 @@ void OTA::onStart()
m_dev.led.setEnforce(false); m_dev.led.setEnforce(false);
m_dev.led.blinkAlternate(25, 50, m_dev.led.COLOR_BLUE, m_dev.led.COLOR_OFF); m_dev.led.blinkAlternate(25, 50, m_dev.led.COLOR_BLUE, m_dev.led.COLOR_OFF);
m_dev.led.setEnforce(true); m_dev.led.setEnforce(true);
m_prevPercent = 0;
} }
void OTA::onEnd() void OTA::onEnd()
{ {
@@ -87,26 +94,26 @@ void OTA::onError(const ota_error_t err)
LOG_ERROR("OTA Error [", err, "]"); LOG_ERROR("OTA Error [", err, "]");
switch (err) switch (err)
{ {
case OTA_AUTH_ERROR: case OTA_AUTH_ERROR:
LOG_ERROR("OTA authentication error"); LOG_ERROR("OTA authentication error");
break; break;
case OTA_BEGIN_ERROR: case OTA_BEGIN_ERROR:
LOG_ERROR("OTA begin errror"); LOG_ERROR("OTA begin errror");
break; break;
case OTA_CONNECT_ERROR: case OTA_CONNECT_ERROR:
LOG_ERROR("OTA connection error"); LOG_ERROR("OTA connection error");
break; break;
case OTA_RECEIVE_ERROR: case OTA_RECEIVE_ERROR:
LOG_ERROR("OTA receive error"); LOG_ERROR("OTA receive error");
break; break;
case OTA_END_ERROR: case OTA_END_ERROR:
LOG_ERROR("OTA end error"); LOG_ERROR("OTA end error");
break; break;
default: default:
LOG_ERROR("OTA unknown error"); LOG_ERROR("OTA unknown error");
}; };
m_updating = false; m_updating = false;
end(); //end ota on error end(); // end ota on error
} }
void OTA::handle(void *params) void OTA::handle(void *params)

View File

@@ -30,6 +30,7 @@ private:
TaskHandle_t m_taskHandle; TaskHandle_t m_taskHandle;
bool m_updating; bool m_updating;
bool m_active; bool m_active;
float m_prevPercent;
private: // callbacks, do not init in code private: // callbacks, do not init in code
ArduinoOTAClass::THandlerFunction s_onStart = [this]() ArduinoOTAClass::THandlerFunction s_onStart = [this]()