From bb0832ad4fc4dc0deb827c28c05d9ea21ad98e38 Mon Sep 17 00:00:00 2001 From: Emanuele Trabattoni Date: Thu, 24 Jul 2025 22:46:31 +0200 Subject: [PATCH] Application develop start --- include/config.h | 17 ++-- lib/GPIO/BUZZER_Driver.cpp | 2 +- lib/GPIO/LED_Driver.cpp | 2 +- lib/GPIO/LED_Driver.h | 11 +++ lib/RS485/RS485_Driver.cpp | 2 +- src/commands.cpp | 49 ++++++++++ src/commands.h | 63 +++++++++++++ src/devices.h | 19 ++++ src/digitalIO.cpp | 4 +- src/main.cpp | 187 ++++++++++++++++--------------------- src/mqtt.cpp | 2 +- src/mqtt.h | 2 - 12 files changed, 237 insertions(+), 123 deletions(-) create mode 100644 src/commands.cpp create mode 100644 src/commands.h create mode 100644 src/devices.h diff --git a/include/config.h b/include/config.h index 3444033..abf1a51 100644 --- a/include/config.h +++ b/include/config.h @@ -189,12 +189,12 @@ private: mqtt["clientName"] = m_mqttClientName; mqtt["retries"] = m_mqttRetries; auto publish = mqtt["publish"].to(); - for (auto v : m_mqttSubscribe) + for (auto v : m_mqttPublish) { publish[v.first] = v.second; } auto subscribe = mqtt["subscribe"].to(); - for (auto v : m_mqttPublish) + for (auto v : m_mqttSubscribe) { subscribe[v.first] = v.second; } @@ -275,7 +275,7 @@ private: public: // Globals - std::uint16_t m_globalLoopDelay = 1000; // in milliseconds + std::uint16_t m_globalLoopDelay = 5000; // in milliseconds // Ethernet std::string m_ethHostname = "ETcontroller_PRO"; @@ -308,9 +308,10 @@ public: std::string m_mqttClientName = "etcontrollerPRO"; std::map m_mqttSubscribe = { - {"commands", "test/etcontroller/commands"}}; + {"commands", "etcontroller/hw/commands"}}; std::map m_mqttPublish = { - {"heatpump", "test/etcontroller/heatpump"}, - {"temperature", "test/etcontroller/temperatures"}, - {"irrigation", "test/etcontroller/irrigation"}}; -}; \ No newline at end of file + {"answers", "etcontroller/hw/answers"}, + {"heatpump", "etcontroller/hw/heatpump"}, + {"temperatures", "etcontroller/hw/temperatures"}, + {"irrigation", "etcontroller/hw/irrigation"}}; +}; diff --git a/lib/GPIO/BUZZER_Driver.cpp b/lib/GPIO/BUZZER_Driver.cpp index befb4d8..abb444d 100644 --- a/lib/GPIO/BUZZER_Driver.cpp +++ b/lib/GPIO/BUZZER_Driver.cpp @@ -14,7 +14,7 @@ namespace drivers ledcAttach(c_buzzerPin, 1000, 8); m_bp.pin = c_buzzerPin; m_bp.beeperTask = NULL; - beep(50, NOTE_G); + beep(50, NOTE_C); } Buzzer::~Buzzer() diff --git a/lib/GPIO/LED_Driver.cpp b/lib/GPIO/LED_Driver.cpp index cd23eec..84a63e9 100644 --- a/lib/GPIO/LED_Driver.cpp +++ b/lib/GPIO/LED_Driver.cpp @@ -23,7 +23,7 @@ namespace drivers void Led::setColor(const color_t color) { blinkStop(); - rgbLedWrite(c_ledPin, color.r, color.g, color.b); + rgbLedWrite(c_ledPin, color.g, color.r, color.b); } void Led::blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color) diff --git a/lib/GPIO/LED_Driver.h b/lib/GPIO/LED_Driver.h index 6a86c26..5c5f185 100644 --- a/lib/GPIO/LED_Driver.h +++ b/lib/GPIO/LED_Driver.h @@ -20,6 +20,17 @@ namespace drivers uint8_t b; } color_t; + const color_t COLOR_RED = {255, 0, 0}; + const color_t COLOR_ORANGE = {255, 127, 0}; + const color_t COLOR_YELLOW = {255, 255, 0}; + const color_t COLOR_CHARTREUSE = {127, 255, 0}; + const color_t COLOR_GREEN = {0, 255, 0}; + const color_t COLOR_CYAN = {0, 255, 255}; + const color_t COLOR_SKYBLUE = {0, 127, 255}; + const color_t COLOR_BLUE = {0, 0, 255}; + const color_t COLOR_VIOLET = {127, 0, 255}; + const color_t COLOR_MAGENTA = {255, 0, 255}; + private: typedef struct { diff --git a/lib/RS485/RS485_Driver.cpp b/lib/RS485/RS485_Driver.cpp index 992d1f2..45296c5 100644 --- a/lib/RS485/RS485_Driver.cpp +++ b/lib/RS485/RS485_Driver.cpp @@ -87,7 +87,7 @@ namespace drivers auto now = millis(); if ((now - m_lastAccess) < c_minDelay) // fixed milliseconds delay between commands to different devices { - LOG_WARN("MODBUS access delay", (now - m_lastAccess), "device", device); + LOG_DEBUG("MODBUS access delay", (now - m_lastAccess), "device", device); delay(now - m_lastAccess); } m_lastDevice = device; diff --git a/src/commands.cpp b/src/commands.cpp new file mode 100644 index 0000000..380a8fe --- /dev/null +++ b/src/commands.cpp @@ -0,0 +1,49 @@ +#include + +namespace commands +{ + const ArduinoJson::JsonDocument Commands::setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) + { + ArduinoJson::JsonDocument response; + if (!params["level"].is()) + { + LOG_ERROR("setHPlimit incorrect parameters"); + return response; + } + const auto level = params["level"].as(); + if (!c_hpLimitsMap.contains(level)) + { + LOG_ERROR("setHPlimit invalid level", level.c_str()); + return response; + } + for (auto k : c_hpLimitsMap) + { + if (level == k.first && level != "UNLIMITED") + dev.io.digitalOutWrite(k.second, true); + else + dev.io.digitalOutWrite(k.second, false); + } + LOG_INFO("setHPlimit -> level", level.c_str()); + return response; + } + + const ArduinoJson::JsonDocument Commands::getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) + { + ArduinoJson::JsonDocument response; + const auto pinfo = dev.seneca.getAll(); + response["cmd"] = "getHPpower"; + auto values = response["params"].to(); + values["power"] = pinfo.pAct; + values["current"] = pinfo.a; + values["energy"] = pinfo.whPar; + LOG_INFO("getHPpower -> power", pinfo.pAct, "current", pinfo.a, "energy", pinfo.whPar); + return response; + } + + const ArduinoJson::JsonDocument Commands::setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms) + { + ArduinoJson::JsonDocument response; + LOG_WARN("setHeating not yet implemented"); + return response; + } +} \ No newline at end of file diff --git a/src/commands.h b/src/commands.h new file mode 100644 index 0000000..1a90542 --- /dev/null +++ b/src/commands.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include + +#include + +namespace commands +{ + enum RO + { + P1, + P2, + P3, + P4, + NC_1, + NC_2, + PUMP_HT, + GND_FLOOR, + FR_FLOOR, + IRR_PUMP, + Z1, + Z2, + Z3, + AUX, + RETURN, + NC_3, + NC_4 + }; + + 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}}; + + const std::map c_irrigationValveMap = {{"ricircolo", RO::RETURN}, + {"1", RO::Z1}, + {"2", RO::Z2}, + {"3", RO::Z3}, + {"rubinetti", RO::AUX}}; + + class Commands + { + Commands() = delete; + + public: + static const ArduinoJson::JsonDocument setHPlimit(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); + static const ArduinoJson::JsonDocument getHPpower(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); + static const ArduinoJson::JsonDocument setHeating(const devices_t &dev, const ArduinoJson::JsonDocument ¶ms); + }; + + static const std::map> commandMap = { + {"setHPlimit", Commands::setHPlimit}, + {"getHPpower", Commands::getHPpower}, + {"setHeating", Commands::setHeating}}; + +} \ No newline at end of file diff --git a/src/devices.h b/src/devices.h new file mode 100644 index 0000000..cf6decf --- /dev/null +++ b/src/devices.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +typedef struct +{ + drivers::PCF85063 &rtc; + drivers::R4DCB08 &tmp; + drivers::S50140 &seneca; + drivers::Buzzer &buzzer; + drivers::Led &led; + digitalIO &io; +} devices_t; diff --git a/src/digitalIO.cpp b/src/digitalIO.cpp index 00180de..5d9e0d5 100644 --- a/src/digitalIO.cpp +++ b/src/digitalIO.cpp @@ -76,7 +76,7 @@ const std::vector digitalIO::digitalOutReadPort() rv.reserve(getOutNum()); rv.insert(rv.begin(), locals.begin(), locals.end()); rv.insert(rv.end(), remotes.begin(), remotes.end()); - return std::move(rv); + return rv; } const bool digitalIO::digitalInRead(const uint8_t ch) @@ -104,7 +104,7 @@ const std::vector digitalIO::digitalInReadPort() rv.reserve(getInNum()); rv.insert(rv.begin(), locals.begin(), locals.end()); rv.insert(rv.end(), remotes.begin(), remotes.end()); - return std::move(rv); + return rv; } void digitalIO::reset() diff --git a/src/main.cpp b/src/main.cpp index efb4ff3..1b9f246 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,36 +1,23 @@ -#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG - #include #include #include #include -#include -#include -#include -#include -#include -#include - -#include +#include #include + +#include #include "utils.h" /////////////// GLOBALS /////////////// Config &conf = Config::getInstance(); /////////////// GLOBALS /////////////// -void testAction(const ArduinoJson::JsonDocument &doc) -{ - std::string message; - ArduinoJson::serializeJsonPretty(doc, message); - LOG_INFO("Received on testAction\n", message.c_str()); -} - void setup() { Serial.begin(9600); LOG_ATTACH_SERIAL(Serial); + LOG_SET_LEVEL(DebugLogLevel::LVL_INFO); conf.init(); // read the configuration from internal flash } @@ -54,44 +41,54 @@ void loop() auto io = digitalIO(i2c, bus, {conf.m_modbusRelayAddr}); // Initialize temperature sensors sensors = tmp.getNum(); + tmp.setCorrection(conf.m_tempCorrectionValues); LOG_INFO("Temperature sensors connected ->", sensors); + + // Create device structure to pass all devices in the callbacks as needed + devices_t devices(rtc, tmp, seneca, buzzer, led, io); //////////////// DEVICES //////////////// //////////////// NETWORK //////////////// auto mqtt = MQTTwrapper(); //////////////// NETWORK //////////////// - std::function mycallback = - [&io](const ArduinoJson::JsonDocument &doc) + //////////////// MQTT //////////////// + /////////////// CALLBACK ////////////// + std::function commandsCallback = + [&mqtt, &devices](const ArduinoJson::JsonDocument &doc) { - std::vector v1 = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}; - std::vector v2 = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}; - std::vector v0(io.getOutNum(), 0); - - LOG_INFO("SET Digital Outputs V1: ", printBoolVec(v1).c_str()); - io.digitalOutWritePort(v1); - delay(100); - LOG_INFO("GET Digital Outputs V1: ", printBoolVec(io.digitalOutReadPort()).c_str()); - delay(2000); - - LOG_INFO("SET Digital Outputs V2: ", printBoolVec(v2).c_str()); - io.digitalOutWritePort(v2); - delay(100); - LOG_INFO("GET Digital Outputs V2: ", printBoolVec(io.digitalOutReadPort()).c_str()); - delay(2000); - - LOG_INFO("GET Digital Inputs: ", printBoolVec(io.digitalInReadPort()).c_str()); - io.digitalOutWritePort(v0); + if (!doc["cmd"].is() || !doc["params"].is()) + { + LOG_ERROR("Invalid Json Command"); + return; + } + const std::string cmd = doc["cmd"].as(); + ArduinoJson::JsonDocument params = doc["params"]; + if (commands::commandMap.contains(cmd)) + { // call command from command map in this same thread (the MQTT thread) + LOG_INFO("Executing command", cmd.c_str()); + auto answer = std::move(commands::commandMap.at(cmd)(devices, params)); + if (answer.isNull()) + return; + mqtt.publish(conf.m_mqttPublish["answers"], answer); + } + else + { + LOG_ERROR("Unknown command", cmd.c_str()); + } }; //////////////// NETWORK //////////////// /////////////// CALLBACK //////////////// Network.onEvent( - [ð, &rtc, &mqtt, &buzzer, &led, &mycallback](arduino_event_id_t event, arduino_event_info_t info) -> void + [&](arduino_event_id_t event, arduino_event_info_t info) -> void { eth.onEvent(event, info); // Arduino Ethernet event handler if (!eth.isConnected()) + { + led.setColor(led.COLOR_RED); return; + } // Get RTC time at ethernet connection time_t ntpTime; uint8_t timeRetries(0); @@ -100,20 +97,20 @@ void loop() { if (eth.getNtpTime(ntpTime) && rtc.setDatetime(drivers::PCF85063::fromEpoch(ntpTime))) { - // buzzer.beep(250, NOTE_F); - led.setColor({255, 255, 0}); + buzzer.beep(250, NOTE_A); + led.setColor(led.COLOR_ORANGE); const drivers::PCF85063::datetime_t dt(drivers::PCF85063::fromEpoch(ntpTime)); - LOG_INFO("NTP Time: ", drivers::PCF85063::datetime2str(dt).c_str()); - delay(100); + LOG_INFO("NTP Time Update: ", drivers::PCF85063::datetime2str(dt).c_str()); + break; } - break; + delay(100); } while (mqttRetries++ < conf.m_mqttRetries) { if (mqtt.connect()) { - mqtt.subscribe("test/esp32-in", testAction); - mqtt.subscribe("test/esp32-functional", mycallback); + led.setColor(led.COLOR_GREEN); + mqtt.subscribe(conf.m_mqttSubscribe["commands"], commandsCallback); break; } delay(100); @@ -126,81 +123,57 @@ void loop() while (true) { - LOG_INFO("[", k++, "] Loop"); - const std::string timeStr(rtc.getTimeStr()); - LOG_INFO("Current Datetime", timeStr.c_str()); - ArduinoJson::JsonDocument ts; - ts["loopIterator"] = k; - ts["currentTime"] = timeStr; - mqtt.publish("test/esp32-out", ts); + LOG_INFO("[", k++, "] Loop - Current Datetime", timeStr.c_str()); - uint8_t i(0); - for (auto v : tmp.getTempAll()) { - LOG_INFO("Temperature channel", i++, "->", v); + ArduinoJson::JsonDocument poll; + poll["cmd"] = "POLL"; + auto params = poll["params"].to(); + params["time"] = timeStr; + params["number"] = k; + mqtt.publish(conf.m_mqttPublish["answers"], poll); + }; + + + { + ArduinoJson::JsonDocument ti; + auto tempinfo = tmp.getTempAll(); + ti["solar"] = tempinfo.at(0); + ti["acs"] = tempinfo.at(0); + ti["heating"] = tempinfo.at(0); + mqtt.publish(conf.m_mqttPublish["temperatures"], ti); + }; + + { + ArduinoJson::JsonDocument pi; + auto powerinfo = seneca.getAll(); + pi["power"] = powerinfo.pAct; + pi["current"] = powerinfo.a; + pi["energy"] = powerinfo.whPar; + pi["voltage"] = powerinfo.v; + mqtt.publish(conf.m_mqttPublish["heatpump"], pi); } - LOG_INFO("Read Red"); - if (io.digitalInRead(0)) // rosso + if (io.digitalInRead(0)) // ROSSO - Config Reset { - std::vector v1 = {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}; - std::vector v2 = {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}; - std::vector v0(16, 0); - - LOG_INFO("SET Digital Outputs V1: ", printBoolVec(v1).c_str()); - io.digitalOutWritePort(v1); - LOG_INFO("GET Digital Outputs V1: ", printBoolVec(io.digitalOutReadPort()).c_str()); - delay(2000); - - LOG_INFO("SET Digital Outputs V2: ", printBoolVec(v2).c_str()); - io.digitalOutWritePort(v2); - LOG_INFO("GET Digital Outputs V2: ", printBoolVec(io.digitalOutReadPort()).c_str()); - delay(2000); - - LOG_INFO("GET Digital Inputs: ", printBoolVec(io.digitalInReadPort()).c_str()); - delay(2000); - - io.digitalOutWritePort(v0); - delay(2000); - } - - LOG_INFO("Read Blue"); - if (io.digitalInRead(8)) // blu - { - if (!buzzing) - { - buzzing = true; - buzzer.beepRepeat(100, 1000, NOTE_C); - led.blinkColor(100, 500, {255, 0, 255}); - } - else - { - buzzer.beepStop(); - led.blinkAlternate(500, 500, {255, 255, 0}, {0, 255, 255}); - buzzing = false; - } - LOG_INFO("Buzzing -> ", buzzing ? "True" : "False"); - } - - LOG_INFO("Read Green"); - if (io.digitalInRead(9)) - { // verde + LOG_WARN("Config RESET!"); + buzzer.beep(450, NOTE_E); + delay(500); conf.resetConfig(); } - LOG_INFO("Read Yellow"); - if (io.digitalInRead(10)) - { // giallo + if (io.digitalInRead(1)) // GIALLO - Restart + { + LOG_WARN("RESTART!"); + buzzer.beep(450, NOTE_D); + delay(100); esp_restart(); } - drivers::S50140::powerinfo_t pinfo = seneca.getAll(); - LOG_INFO("Power Info ==> V:", pinfo.v, "- A:", pinfo.a, "- W:", pinfo.pAct, "- F:", pinfo.f, "- Wh_t:", pinfo.whTot, "- Wh_p:", pinfo.whPar); - - delay(conf.m_globalLoopDelay); + delay(conf.m_globalLoopDelay); // to avoid too fast loop } - + //////////////////////////////////////// ///////// MAIN LOOP INSIDE LOOP //////// //////////////////////////////////////// diff --git a/src/mqtt.cpp b/src/mqtt.cpp index 5e79fc4..2282988 100644 --- a/src/mqtt.cpp +++ b/src/mqtt.cpp @@ -1,6 +1,6 @@ #include -#define STACK_DEPTH 4096 +#define STACK_DEPTH 8192 #define PRIOTITY 2 MQTTwrapper::MQTTwrapper() : m_config(Config::getInstance()), m_tcp(NetworkClient()), m_client(PubSubClient(m_tcp)), m_loopHandle(NULL) diff --git a/src/mqtt.h b/src/mqtt.h index ad2df4f..5c4fbb9 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -1,7 +1,5 @@ #pragma once -#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG - #include #include #include