diff --git a/docs/commands.json b/docs/commands.json index 9dcc06d..9eebd0e 100644 --- a/docs/commands.json +++ b/docs/commands.json @@ -64,11 +64,11 @@ "cmd": "setIrrigation", "params": { "zone": [ - "Ricircolo", + "ricircolo", "1", "2", "3", - "Rubinetti" + "rubinetti" ], "timeOn": 120, "timePause": 2 diff --git a/include/config.h b/include/config.h index 8e1bf56..7c140f3 100644 --- a/include/config.h +++ b/include/config.h @@ -104,9 +104,6 @@ public: deserialize(); saveConfig(); }; // filesystem is unmounted here - LOG_WARN("setConfig will cause restart!"); - delay(5000); - esp_restart(); } void resetConfig() diff --git a/lib/RS485/RS485_Driver.cpp b/lib/RS485/RS485_Driver.cpp index 02ab562..bac9dc3 100644 --- a/lib/RS485/RS485_Driver.cpp +++ b/lib/RS485/RS485_Driver.cpp @@ -156,7 +156,7 @@ namespace drivers // Delay Bus Access between different devices if (device != m_lastDevice) { - LOG_WARN("MODBUS device change from ", printHex(m_lastDevice).c_str(), "to", printHex(device).c_str()); + LOG_DEBUG("MODBUS device change from ", printHex(m_lastDevice).c_str(), "to", printHex(device).c_str()); BUS_DELAY; m_lastDevice = device; } @@ -213,7 +213,7 @@ namespace drivers // Delay Bus Access between different devices if (device != m_lastDevice) { - LOG_WARN("MODBUS device change from ", printHex(m_lastDevice).c_str(), "to", printHex(device).c_str()); + LOG_DEBUG("MODBUS device change from ", printHex(m_lastDevice).c_str(), "to", printHex(device).c_str()); BUS_DELAY; m_lastDevice = device; } @@ -266,7 +266,7 @@ namespace drivers // Delay Bus Access between different devices if (device != m_lastDevice) { - LOG_WARN("MODBUS device change from ", printHex(m_lastDevice).c_str(), "to", printHex(device).c_str()); + LOG_DEBUG("MODBUS device change from ", printHex(m_lastDevice).c_str(), "to", printHex(device).c_str()); BUS_DELAY; m_lastDevice = device; } @@ -325,7 +325,7 @@ namespace drivers // Delay Bus Access between different devices if (device != m_lastDevice) { - LOG_WARN("MODBUS device change from ", printHex(m_lastDevice).c_str(), "to", printHex(device).c_str()); + LOG_DEBUG("MODBUS device change from ", printHex(m_lastDevice).c_str(), "to", printHex(device).c_str()); BUS_DELAY; m_lastDevice = device; } diff --git a/src/commands.cpp b/src/commands.cpp index 727f55b..267bfd9 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -3,21 +3,34 @@ namespace commands { + void restart(TimerHandle_t t) + { + esp_restart(); + } + // CONFIG // // CONFIG // const ArduinoJson::JsonDocument Commands::setConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) { ArduinoJson::JsonDocument response; auto &conf = Config::getInstance(); + std::string buf; + response["cmd"] = "setConfig"; auto values = response["values"].to(); if (params.isNull()) { values["status"] = "Invalid"; + return response; } - else + conf.setConfig(params); + values["status"] = "Valid"; + serializeJson(params, buf); + LOG_INFO("setConfig ->", buf.c_str()); + TimerHandle_t resetTimer(xTimerCreate("restartTimer", pdMS_TO_TICKS(5000), false, NULL, restart)); + LOG_WARN("setConfig will cause restart!"); + if (resetTimer) { - conf.setConfig(params); - values["status"] = "Valid"; + xTimerStart(resetTimer, 0); } return response; } @@ -25,8 +38,11 @@ namespace commands { ArduinoJson::JsonDocument response; auto &conf = Config::getInstance(); + std::string buf; response["cmd"] = "getConfig"; response["values"] = conf.getConfig(); + serializeJson(response["values"], buf); + LOG_INFO("getConfig ->", buf.c_str()); return response; } // CONFIG // @@ -85,14 +101,145 @@ namespace commands const ArduinoJson::JsonDocument Commands::setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) { ArduinoJson::JsonDocument response; - LOG_WARN("Comand not yet implemented"); + if (params.isNull()) + { + LOG_ERROR("setHeating incorrect paramaters"); + return response; + } + for (auto v : c_heatingValveMap) + { + if (params[v.first].isNull()) + continue; + if (params[v.first] == "ON") + { + dev.io.digitalOutWrite(v.second, true); + LOG_INFO("setHeating -> ", v.first.c_str(), "ON"); + } + else if (params[v.first] == "OFF") + { + dev.io.digitalOutWrite(v.second, false); + LOG_INFO("setHeating -> ", v.first.c_str(), "OFF"); + } + else + LOG_ERROR("setHeating invalid valve state"); + } return response; } + void resetZone(TimerHandle_t th) + { + devices_t *dev = (devices_t *)pvTimerGetTimerID(th); + const char *timerName = pcTimerGetName(th); + LOG_INFO("Reset irrigation zone -> ", timerName); + if (!c_irrigationValveMap.contains(timerName)) + { + LOG_ERROR("Irrigation timer name invalid"); + return; + } + dev->io.digitalOutWrite(c_irrigationValveMap.at(timerName), false); + c_irrigationTimerMap.at(timerName).second = NULL; // reset timer handle for this timer + xTimerDelete(th, 0); // delete the timer on expiry + } + + void resetWaterPump(TimerHandle_t th) + { + devices_t *dev = (devices_t *)pvTimerGetTimerID(th); + LOG_INFO("Shutdown irrigation pump"); + dev->io.digitalOutWrite(RO::IRR_PUMP, false); + s_irrigationPumpTimer = NULL; + xTimerDelete(th, 0); // delete the timer on expiry + } + const ArduinoJson::JsonDocument Commands::setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) { ArduinoJson::JsonDocument response; - LOG_WARN("Comand not yet implemented"); + auto &conf = Config::getInstance(); + response["cmd"] = "setIrrigation"; + if (params.isNull()) + { + LOG_ERROR("setIrrigation incorrect paramaters"); + return response; + } + const std::string zone(params["zone"].as()); + const uint16_t tOn(params["timeOn"].as()); + const uint16_t tPause(params["timePause"].as()); + + if (zone == "stop") + { // stop all zones and reset timers + LOG_INFO("setIrrigation stop all zones"); + for (auto &h : c_irrigationTimerMap) + { + const auto zoneName = h.first; + auto &timerHandle = h.second.second; // get the timer handle + if (timerHandle) // if handle is not null (not from a deleted timer) + { + if (xTimerIsTimerActive(timerHandle)) // stop the timer if active + { + LOG_INFO("setIrrigation stopping timer", zoneName.c_str()); + xTimerStop(timerHandle, 0); + xTimerDelete(timerHandle, pdMS_TO_TICKS(10)); // delete it + timerHandle = NULL; + } + } + LOG_INFO("setIrrigation closing ", zoneName.c_str()); + dev.io.digitalOutWrite(c_irrigationValveMap.at(zoneName), false); // shuto down the valve + } + if (s_irrigationPumpTimer) + { + xTimerChangePeriod(s_irrigationPumpTimer, pdMS_TO_TICKS(30 * 1000), 0); // shutdown the pump in 30s after the stop + xTimerReset(s_irrigationPumpTimer, 0); + } + response["values"]["status"] = "stop"; + return response; + } + + if (!c_irrigationValveMap.contains(zone) && (tOn == 0 || tPause == 0)) // verify if zone is a valid map key + { + LOG_ERROR("setIrrigation incorrect zone", zone.c_str(), " or time values", tOn, tPause); + response["values"]["status"] = "invalid"; + return response; + } + + // verify if timer was already started, zone is already on + const auto timerName = c_irrigationTimerMap.at(zone).first; + const auto zoneIoNumber = c_irrigationValveMap.at(zone); + auto &timerHandle = c_irrigationTimerMap.at(zone).second; + + if (timerHandle) + { // this timer was alteady started, ignore command + LOG_WARN("setIrrigation zone", timerName, "already started"); + response["values"]["status"] = "conflict"; + return response; + } + + const uint32_t pumpTime((tOn + 30) * 1000); + + if (!s_irrigationPumpTimer) // Pump has not yet started + { + s_irrigationPumpTimer = xTimerCreate("pumpTimer", pdMS_TO_TICKS(pumpTime), false, (void *)&dev, resetWaterPump); + dev.io.digitalOutWrite(RO::IRR_PUMP, true); + xTimerStart(s_irrigationPumpTimer, 0); // immediate start pump timer + LOG_INFO("setIrrigation pump time ", pumpTime); + } + else + { + const auto currentRemaining(xTimerGetExpiryTime(s_irrigationPumpTimer) - xTaskGetTickCount()); + const auto newRemaining(pumpTime); + const auto newPeriod = std::max(newRemaining, currentRemaining); + xTimerChangePeriod(s_irrigationPumpTimer, newPeriod, 0); // set new period based on timing of new zone + xTimerReset(s_irrigationPumpTimer, 0); // if timer was already started, restart + LOG_INFO("setIrrigation pump time reset", newRemaining); + } + + TimerHandle_t shTimer(xTimerCreate(timerName, pdMS_TO_TICKS(tOn * 1000), false, (void *)&dev, resetZone)); + if (shTimer) + { + dev.io.digitalOutWrite(zoneIoNumber, true); + xTimerStart(shTimer, pdMS_TO_TICKS(tPause * 1000)); + timerHandle = shTimer; + response["values"]["status"] = "ok"; + LOG_INFO("setIrrigation zone -> ", timerName, "tOn", tOn, "tPause", tPause); + } return response; } // SETTERS // @@ -114,13 +261,6 @@ namespace commands return response; } - const ArduinoJson::JsonDocument Commands::getHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) - { - ArduinoJson::JsonDocument response; - LOG_WARN("Comand not yet implemented"); - return response; - } - const ArduinoJson::JsonDocument Commands::getInputStatus(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) { ArduinoJson::JsonDocument response; diff --git a/src/commands.h b/src/commands.h index a44e3ed..b56c63d 100644 --- a/src/commands.h +++ b/src/commands.h @@ -18,10 +18,9 @@ namespace commands P3, P4, NC_1, - NC_2, - PUMP_HT, + FST_FLOOR, GND_FLOOR, - FR_FLOOR, + PUMP_HT, IRR_PUMP, Z1, Z2, @@ -33,21 +32,29 @@ namespace commands RO_MAX // unused to detect invalid values }; - const std::map c_hpLimitsMap = {{"P1", RO::P1}, - {"P2", RO::P2}, - {"P3", RO::P3}, - {"P4", RO::P4}, - {"UNLIMITED", RO::P1}}; + static const std::map c_hpLimitsMap = {{"P1", RO::P1}, + {"P2", RO::P2}, + {"P3", RO::P3}, + {"P4", RO::P4}, + {"UNLIMITED", RO::P1}}; - const std::map c_heatingValveMap = {{"pump", RO::PUMP_HT}, - {"first", RO::FR_FLOOR}, - {"ground", RO::GND_FLOOR}}; + static const std::map c_heatingValveMap = {{"pump", RO::PUMP_HT}, + {"first", RO::FST_FLOOR}, + {"ground", RO::GND_FLOOR}}; - const std::map c_irrigationValveMap = {{"ricircolo", RO::RETURN}, - {"1", RO::Z1}, - {"2", RO::Z2}, - {"3", RO::Z3}, - {"rubinetti", RO::AUX}}; + static const std::map c_irrigationValveMap = {{"ricircolo", RO::RETURN}, + {"zone1", RO::Z1}, + {"zone2", RO::Z2}, + {"zone3", RO::Z3}, + {"rubinetti", RO::AUX}}; + + static std::map> c_irrigationTimerMap = {{"ricircolo", {"ricircolo", NULL}}, + {"zone1", {"zone1", NULL}}, + {"zone2", {"zone2", NULL}}, + {"zone3", {"zone3", NULL}}, + {"rubinetti", {"rubinetti", NULL}}}; + + static TimerHandle_t s_irrigationPumpTimer = NULL; class Commands { @@ -57,7 +64,7 @@ namespace commands // CONFIG // static const ArduinoJson::JsonDocument setConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument getConfig(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); - + // CRONJOBS // static const ArduinoJson::JsonDocument setCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument getCronjob(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); @@ -67,10 +74,9 @@ namespace commands static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument setIrrigation(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); - + // GETTERS // static const ArduinoJson::JsonDocument getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); - static const ArduinoJson::JsonDocument getHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument getInputStatus(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument getOutputStatus(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); static const ArduinoJson::JsonDocument getTemperatures(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); @@ -87,7 +93,7 @@ namespace commands {"setCronjob", Commands::setCronjob}, {"getCronjob", Commands::getCronjob}, {"delCronjob", Commands::delCronjob}, - + {"setHPlimit", Commands::setHPlimit}, {"setHeating", Commands::setHeating}, {"setIrrigation", Commands::setIrrigation},