diff --git a/src/config.h b/include/config.h similarity index 100% rename from src/config.h rename to include/config.h diff --git a/lib/ETH/WS_ETH.cpp b/lib/ETH/WS_ETH.cpp deleted file mode 100644 index 794ce5f..0000000 --- a/lib/ETH/WS_ETH.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "WS_ETH.h" - -#include -#include - -static bool eth_connected = false; -static bool eth_connected_Old = false; -IPAddress ETH_ip; -// NTP setup -WiFiUDP udp; -NTPClient timeClient(udp, "pool.ntp.org", TZ*3600, 60000); // NTP server, time offset in seconds, update interval - -void onEvent(arduino_event_id_t event, arduino_event_info_t info) { - switch (event) { - case ARDUINO_EVENT_ETH_START: - printf("ETH Started\r\n"); - //set eth hostname here - ETH.setHostname("esp32-eth0"); - break; - case ARDUINO_EVENT_ETH_CONNECTED: printf("ETH Connected\r\n"); break; - case ARDUINO_EVENT_ETH_GOT_IP: printf("ETH Got IP: '%s'\n", esp_netif_get_desc(info.got_ip.esp_netif)); //printf("%s\r\n",ETH); - ETH_ip = ETH.localIP(); - printf("ETH Got IP: %d.%d.%d.%d\n", ETH_ip[0], ETH_ip[1], ETH_ip[2], ETH_ip[3]); -#if USE_TWO_ETH_PORTS - // printf("%d\r\n",ETH1); -#endif - eth_connected = true; - break; - case ARDUINO_EVENT_ETH_LOST_IP: - printf("ETH Lost IP\r\n"); - eth_connected = false; - break; - case ARDUINO_EVENT_ETH_DISCONNECTED: - printf("ETH Disconnected\r\n"); - eth_connected = false; - break; - case ARDUINO_EVENT_ETH_STOP: - printf("ETH Stopped\r\n"); - eth_connected = false; - break; - default: break; - } -} - -void testClient(const char *host, uint16_t port) { - printf("\nconnecting to \r\n");; - printf("%s\r\n",host); - - NetworkClient client; - if (!client.connect(host, port)) { - printf("connection failed\r\n"); - return; - } - client.printf("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", host); - while (client.connected() && !client.available()); - while (client.available()) { - printf("%c",(char)client.read()); - } - - printf("closing connection\n"); - client.stop(); -} - -void ETH_Init(void) { - printf("Ethernet Start\r\n"); - Network.onEvent(onEvent); - - SPI.begin(ETH_SPI_SCK, ETH_SPI_MISO, ETH_SPI_MOSI); - ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_CS, ETH_PHY_IRQ, ETH_PHY_RST, SPI); -#if USE_TWO_ETH_PORTS - ETH1.begin(ETH1_PHY_TYPE, ETH1_PHY_ADDR, ETH1_PHY_CS, ETH1_PHY_IRQ, ETH1_PHY_RST, SPI); -#endif - xTaskCreatePinnedToCore( - EthernetTask, - "EthernetTask", - 4096, - NULL, - 2, - NULL, - 0 - ); -} -void EthernetTask(void *parameter) { - while(1){ - if (eth_connected && !eth_connected_Old) { - eth_connected_Old = eth_connected; - //RGB_Open_Time(0, 60, 0,1000, 0); - printf("Network port connected!\r\n"); - Acquisition_time(); - } - else if(!eth_connected && eth_connected_Old){ - eth_connected_Old = eth_connected; - printf("Network port disconnected!\r\n"); - } - vTaskDelay(pdMS_TO_TICKS(100)); - } - vTaskDelete(NULL); -} -void Acquisition_time(void) { // Get the network time and set to DS3231 to be called after the WIFI connection is successful - timeClient.begin(); - timeClient.update(); - - time_t currentTime = timeClient.getEpochTime(); - while(currentTime < 1609459200) // Using the current timestamp to compare with a known larger value,1609459200 is a known larger timestamp value that corresponds to January 1, 2021 - { - timeClient.update(); - currentTime = timeClient.getEpochTime(); - printf("ETH - Online clock error!!!\r\n"); - } - struct tm *localTime = localtime(¤tTime); - //static datetime_t PCF85063_Time = {0}; - //PCF85063_Time.year = localTime->tm_year + 1900; - //PCF85063_Time.month = localTime->tm_mon + 1; - //PCF85063_Time.day = localTime->tm_mday; - //PCF85063_Time.dotw = localTime->tm_wday; - //PCF85063_Time.hour = localTime->tm_hour; - //PCF85063_Time.minute = localTime->tm_min; - //PCF85063_Time.second = localTime->tm_sec; - //PCF85063_Set_All(PCF85063_Time); -} diff --git a/lib/ETH/WS_ETH.h b/lib/ETH/WS_ETH.h deleted file mode 100644 index f0d0f5b..0000000 --- a/lib/ETH/WS_ETH.h +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once -#include -#include -#include - -// Set this to 1 to enable dual Ethernet support -#define USE_TWO_ETH_PORTS 0 - -#ifndef ETH_PHY_TYPE - #define ETH_PHY_TYPE ETH_PHY_W5500 - #define ETH_PHY_ADDR 1 - #define ETH_PHY_CS 16 - #define ETH_PHY_IRQ 12 - #define ETH_PHY_RST 39 -#endif - -// SPI pins -#define ETH_SPI_SCK 15 -#define ETH_SPI_MISO 14 -#define ETH_SPI_MOSI 13 - -#if USE_TWO_ETH_PORTS - // Second port on shared SPI bus - #ifndef ETH1_PHY_TYPE - #define ETH1_PHY_TYPE ETH_PHY_W5500 - #define ETH1_PHY_ADDR 1 - #define ETH1_PHY_CS 32 - #define ETH1_PHY_IRQ 33 - #define ETH1_PHY_RST 18 - #endif - ETHClass ETH1(1); -#endif - -#define TZ 1 // rome - -void ETH_Init(void); -void ETH_Loop(void); -void EthernetTask(void *parameter); - -void Acquisition_time(void); \ No newline at end of file diff --git a/lib/GPIO/BUZZER_Driver.cpp b/lib/GPIO/BUZZER_Driver.cpp index b9a8f7d..befb4d8 100644 --- a/lib/GPIO/BUZZER_Driver.cpp +++ b/lib/GPIO/BUZZER_Driver.cpp @@ -10,9 +10,9 @@ namespace drivers Buzzer::Buzzer() { LOG_INFO("Initializing Beeper"); - pinMode(buzzerPin, OUTPUT); - ledcAttach(buzzerPin, 1000, 8); - m_bp.pin = buzzerPin; + pinMode(c_buzzerPin, OUTPUT); + ledcAttach(c_buzzerPin, 1000, 8); + m_bp.pin = c_buzzerPin; m_bp.beeperTask = NULL; beep(50, NOTE_G); } @@ -20,8 +20,8 @@ namespace drivers Buzzer::~Buzzer() { beepStop(); - ledcDetach(buzzerPin); - pinMode(buzzerPin, INPUT); + ledcDetach(c_buzzerPin); + pinMode(c_buzzerPin, INPUT); } void Buzzer::beep(const uint16_t tBeep, const note_t note) @@ -57,11 +57,11 @@ namespace drivers while (true) { ledcWriteNote(bPar->pin, bPar->note, OCTAVE); // on with selected note - vTaskDelay(pdMS_TO_TICKS(bPar->tOn)); + delay(bPar->tOn); ledcWriteTone(bPar->pin, 0); // off if (bPar->tOff == 0) break; - vTaskDelay(pdMS_TO_TICKS(bPar->tOff)); + delay(bPar->tOff); } LOG_DEBUG("Beeper Task Ended"); bPar->beeperTask = NULL; diff --git a/lib/GPIO/BUZZER_Driver.h b/lib/GPIO/BUZZER_Driver.h index af6f86d..b0e7d21 100644 --- a/lib/GPIO/BUZZER_Driver.h +++ b/lib/GPIO/BUZZER_Driver.h @@ -10,7 +10,8 @@ namespace drivers class Buzzer { - const uint8_t buzzerPin = 46; // hardware assigned + const uint8_t c_buzzerPin = 46; // hardware assigned + typedef struct { note_t note; diff --git a/lib/GPIO/LED_Driver.cpp b/lib/GPIO/LED_Driver.cpp index 1d4a4bd..cd23eec 100644 --- a/lib/GPIO/LED_Driver.cpp +++ b/lib/GPIO/LED_Driver.cpp @@ -9,21 +9,21 @@ namespace drivers Led::Led() { LOG_INFO("Inizializing RGB Led"); - pinMode(ledPin, OUTPUT); - m_lp.pin = ledPin; + pinMode(c_ledPin, OUTPUT); + m_lp.pin = c_ledPin; m_lp.blinkTask = NULL; } Led::~Led() { setColor({0, 0, 0}); - pinMode(ledPin, INPUT); + pinMode(c_ledPin, INPUT); } void Led::setColor(const color_t color) { blinkStop(); - rgbLedWrite(ledPin, color.r, color.g, color.b); + rgbLedWrite(c_ledPin, color.r, color.g, color.b); } void Led::blinkColor(const uint16_t tOn, const uint16_t tOff, const color_t color) @@ -62,11 +62,11 @@ namespace drivers while (true) { rgbLedWrite(lPar->pin, lPar->color1.g, lPar->color1.r, lPar->color1.b); - vTaskDelay(pdMS_TO_TICKS(lPar->tOn)); + delay(lPar->tOn); rgbLedWrite(lPar->pin, lPar->color2.g, lPar->color2.r, lPar->color2.b); // off if (lPar->tOff == 0) break; - vTaskDelay(pdMS_TO_TICKS(lPar->tOff)); + delay(lPar->tOff); } LOG_DEBUG("Blinker Task Ended"); lPar->blinkTask = NULL; diff --git a/lib/GPIO/LED_Driver.h b/lib/GPIO/LED_Driver.h index ddcf3c9..6a86c26 100644 --- a/lib/GPIO/LED_Driver.h +++ b/lib/GPIO/LED_Driver.h @@ -10,7 +10,7 @@ namespace drivers class Led { - const uint8_t ledPin = 38; + const uint8_t c_ledPin = 38; public: typedef struct diff --git a/lib/GPIO/TCA9554PWR_Driver.cpp b/lib/GPIO/TCA9554PWR_Driver.cpp index 0c1d864..75cf5b5 100644 --- a/lib/GPIO/TCA9554PWR_Driver.cpp +++ b/lib/GPIO/TCA9554PWR_Driver.cpp @@ -53,6 +53,12 @@ namespace drivers return setPort(newState); } + const bool TCA9554PWR::toggleOut(const uint8_t channel) + { + bool value; + return readOut(channel, value) && setOut(channel, value); + } + const bool TCA9554PWR::setPort(const uint8_t state) { if (writeRegister(TCA9554_OUTPUT_REG, state)) @@ -61,7 +67,7 @@ namespace drivers return false; } - const bool TCA9554PWR::readOut(const uint8_t ch) + const bool TCA9554PWR::readOut(const uint8_t ch, bool &state) { uint8_t currState(0); if (ch < DO1 || ch > DO8) @@ -71,12 +77,13 @@ namespace drivers } if (!readPort(currState)) return false; - return (currState && (High >> ch)); + state = (currState && (High << ch)); + return true; } const bool TCA9554PWR::readPort(uint8_t &state) { - if (readRegister(TCA9554_INPUT_REG, state)) + if (readRegister(TCA9554_OUTPUT_REG, state)) return true; LOG_ERROR("Unable to read IO port: state[%02x]", state); return false; diff --git a/lib/GPIO/TCA9554PWR_Driver.h b/lib/GPIO/TCA9554PWR_Driver.h index 506e58a..ab4e2a6 100644 --- a/lib/GPIO/TCA9554PWR_Driver.h +++ b/lib/GPIO/TCA9554PWR_Driver.h @@ -42,9 +42,10 @@ namespace drivers ~TCA9554PWR(); const bool setOut(const uint8_t channel, const bool state); + const bool toggleOut(const uint8_t channel); const bool setPort(const uint8_t state); - const bool readOut(const uint8_t channel); + const bool readOut(const uint8_t channel, bool &state); const bool readPort(uint8_t &state); private: diff --git a/lib/RS485/RS485_Driver.cpp b/lib/RS485/RS485_Driver.cpp index a53bfd2..992d1f2 100644 --- a/lib/RS485/RS485_Driver.cpp +++ b/lib/RS485/RS485_Driver.cpp @@ -65,13 +65,40 @@ namespace drivers readAll(garbage); LOG_INFO("Init MODBUS Master Mode"); m_crc.reset(CRC16_MODBUS_POLYNOME, CRC16_MODBUS_INITIAL, CRC16_MODBUS_XOR_OUT, CRC16_MODBUS_REV_IN, CRC16_MAXIM_REV_OUT); + m_lastAccess = millis(); + m_lastDevice = 0; + } + + // Get transaction lock + std::unique_lock MODBUS::getLock() + { + return std::unique_lock(m_mutex); + } + + std::mutex &MODBUS::getMutex() + { + return m_mutex; + } + + void MODBUS::delayAccess(const uint8_t device) + { + if (device == m_lastDevice) + return; + 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); + delay(now - m_lastAccess); + } + m_lastDevice = device; + m_lastAccess = millis(); } // Func 0x01 const bool MODBUS::readCoils(const uint8_t device, const uint16_t reg, const uint16_t num, std::vector &coils) { constexpr uint8_t func = 0x01; - std::lock_guard lock(m_mutex); + delayAccess(device); LOG_DEBUG("Read coils: dev[", device, "], reg[", reg, "], num[", num, "]"); return readBinary(device, func, reg, num, coils); } @@ -80,7 +107,7 @@ namespace drivers const bool MODBUS::readInputs(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector &inputs) { constexpr uint8_t func = 0x02; - std::lock_guard lock(m_mutex); + delayAccess(device); LOG_DEBUG("Read multi inputs: dev[", device, "], reg[", reg, "], num[", num, "]"); return readBinary(device, func, reg, num, inputs); } @@ -89,7 +116,7 @@ namespace drivers const bool MODBUS::readHoldingRegisters(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector &values) { constexpr uint8_t func = 0x03; - std::lock_guard lock(m_mutex); + delayAccess(device); LOG_DEBUG("Read multi holding registers: dev[", device, "], reg[", reg, "], num[", num, "]"); return readInteger(device, func, reg, num, values); } @@ -98,7 +125,7 @@ namespace drivers const bool MODBUS::readInputRegisters(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector &values) { constexpr uint8_t func = 0x04; - std::lock_guard lock(m_mutex); + delayAccess(device); LOG_DEBUG("Read multi input registers: dev[", device, "], reg[", reg, "], num[", num, "]"); return readInteger(device, func, reg, num, values); } @@ -107,7 +134,7 @@ namespace drivers const bool MODBUS::writeCoil(const uint8_t device, const uint16_t coil, const bool value) { constexpr uint8_t func = 0x05; - std::lock_guard lock(m_mutex); + delayAccess(device); LOG_DEBUG("Write single coil: dev[", device, "], coil[", coil, "], value[", value ? "true" : "false", "]"); return writeBinary(device, func, coil, {value}); } @@ -116,7 +143,7 @@ namespace drivers const bool MODBUS::writeRegister(const uint8_t device, const uint16_t reg, const uint16_t value) { constexpr uint8_t func = 0x06; - std::lock_guard lock(m_mutex); + delayAccess(device); LOG_DEBUG("Write single register: dev[", device, "], reg[", reg, "], value[", value, "]"); return writeInteger(device, func, reg, {value}, false); } @@ -125,7 +152,7 @@ namespace drivers const bool MODBUS::writeCoils(const uint8_t device, const uint16_t coils, const std::vector &values) { constexpr uint8_t func = 0x0F; - std::lock_guard lock(m_mutex); + delayAccess(device); LOG_DEBUG("Write multi coils: dev[", device, "], start[", coils, "], num[", values.size(), "]"); return writeBinary(device, func, coils, values); } @@ -134,7 +161,7 @@ namespace drivers const bool MODBUS::writeRegisters(const uint8_t device, const uint16_t reg, const std::vector &values) { constexpr uint8_t func = 0x10; - std::lock_guard lock(m_mutex); + delayAccess(device); LOG_DEBUG("Write multi registers: dev[", device, "], start[", reg, "], num[", values.size(), "]"); return writeInteger(device, func, reg, values, true); } @@ -151,7 +178,7 @@ namespace drivers return false; } const uint16_t nRespDataBytes = (uint16_t)ceil(bits / 8.0f); // 1 bit for every coil, if not 8 mutiple padded with zeroes - const uint16_t expectedRespLen = (RESP_HEADER_SIZE + RESP_CRC_SIZE) + nRespDataBytes; // device + function + nbytes + data[] + crc(16b) + const uint16_t expectedRespLen = (c_respHeaderSize + c_respCrcSize) + nRespDataBytes; // device + function + nbytes + data[] + crc(16b) std::vector response; if (!readN(expectedRespLen, response)) { @@ -180,7 +207,7 @@ namespace drivers uint16_t bitNum(0); // get response data bytes excluding header and crc - const std::vector respData(response.begin() + RESP_HEADER_SIZE, response.end() - sizeof(crc_t)); + const std::vector respData(response.begin() + c_respHeaderSize, response.end() - sizeof(crc_t)); for (auto it = respData.begin(); it < respData.end(); it++) { for (uint8_t j(0); j < 8 && bitNum < bits; j++) @@ -201,7 +228,7 @@ namespace drivers return false; } const uint16_t nRespDataBytes = num * sizeof(uint16_t); - const uint16_t expectedRespLen = (RESP_HEADER_SIZE + sizeof(crc_t)) + nRespDataBytes; // device + function + nbytes + data[] + crc(16b) + const uint16_t expectedRespLen = (c_respHeaderSize + sizeof(crc_t)) + nRespDataBytes; // device + function + nbytes + data[] + crc(16b) std::vector response; if (!readN(expectedRespLen, response)) { @@ -228,7 +255,7 @@ namespace drivers out.clear(); out.reserve(nRespDataBytes / sizeof(uint16_t)); // get response data bytes excluding header and crc - const std::vector respData(response.begin() + RESP_HEADER_SIZE, response.end() - RESP_CRC_SIZE); + const std::vector respData(response.begin() + c_respHeaderSize, response.end() - c_respCrcSize); for (auto it = respData.begin(); it < respData.end(); it++) { const uint8_t lo(*it++); diff --git a/lib/RS485/RS485_Driver.h b/lib/RS485/RS485_Driver.h index ff410d5..275996f 100644 --- a/lib/RS485/RS485_Driver.h +++ b/lib/RS485/RS485_Driver.h @@ -13,10 +13,13 @@ namespace drivers { class RS485 { - static const uint8_t PORT = 1; + const uint8_t c_port = 1; public: RS485(const uint32_t baud, const SerialConfig conf); + RS485(const RS485 &) = delete; // remove copy constructors + RS485 &operator=(const RS485 &) = delete; + const bool write(const std::vector data); const bool readAll(std::vector &data); const bool readN(const uint16_t nBytes, std::vector &data); @@ -29,8 +32,9 @@ namespace drivers class MODBUS : private RS485 { - static const uint8_t RESP_HEADER_SIZE = 3; - static const uint8_t RESP_CRC_SIZE = 2; + const uint8_t c_respHeaderSize = 3; + const uint8_t c_respCrcSize = 2; + const uint32_t c_minDelay = 500; typedef struct { @@ -54,6 +58,12 @@ namespace drivers public: MODBUS(const uint32_t baud, const SerialConfig conf); + MODBUS(const MODBUS &) = delete; // remove copy constructors + MODBUS &operator=(const MODBUS &) = delete; + + // Get transaction lock + std::unique_lock getLock(); + std::mutex &getMutex(); // Func 0x01 const bool readCoils(const uint8_t device, const uint16_t reg, const uint16_t num, std::vector &coils); @@ -82,6 +92,9 @@ namespace drivers private: CRC16 m_crc; std::mutex m_mutex; + uint8_t m_lastDevice; + uint32_t m_lastAccess; + void delayAccess(const uint8_t device); const std::vector singleRequest(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t data); const std::vector multiRequest(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t qty, const std::vector &data); const bool readBinary(const uint8_t device, const uint8_t func, const uint16_t reg, const uint16_t bits, std::vector &out); diff --git a/lib/RTC/PCF85063_Driver.h b/lib/RTC/PCF85063_Driver.h index 361f62e..b527da6 100644 --- a/lib/RTC/PCF85063_Driver.h +++ b/lib/RTC/PCF85063_Driver.h @@ -63,38 +63,6 @@ #define RTC_TIMER_FLAG (0x08) -typedef struct -{ - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t dotw; - uint8_t hour; - uint8_t minute; - uint8_t second; -} datetime_t; - -const unsigned char MonthStr[12][4] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; -const unsigned char Week[7][4] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; - -extern datetime_t datetime; -void PCF85063_Init(void); -void PCF85063_Reset(void); -void PCF85063Task(void *parameter); - -void PCF85063_Set_Time(datetime_t time); -void PCF85063_Set_Date(datetime_t date); -void PCF85063_Set_All(datetime_t time); - -void PCF85063_Read_Time(datetime_t *time); - -void PCF85063_Enable_Alarm(void); -uint8_t PCF85063_Get_Alarm_Flag(void); -void PCF85063_Set_Alarm(datetime_t time); -void PCF85063_Read_Alarm(datetime_t *time); - -void datetime_to_str(char *datetime_str, datetime_t time); - namespace drivers { diff --git a/lib/SENECA/S50140_Driver.cpp b/lib/SENECA/S50140_Driver.cpp index 0531275..5152ef2 100644 --- a/lib/SENECA/S50140_Driver.cpp +++ b/lib/SENECA/S50140_Driver.cpp @@ -13,6 +13,7 @@ namespace drivers const S50140::powerinfo_t S50140::getAll() { powerinfo_t info{MAXFLOAT}; + std::lock_guard lock(m_bus.getMutex()); info.v = getV(); info.a = getA(); info.pAct = getPact(); @@ -65,16 +66,18 @@ namespace drivers void S50140::delayRequest() { auto now = millis(); - if ((now - m_lastRequest) < minDelay) + if ((now - m_lastRequest) < c_minDelay) { // minimum m_lastRequest between requests - vTaskDelay(pdMS_TO_TICKS(now - m_lastRequest)); + LOG_DEBUG("S50140 delay request", (now-m_lastRequest)); + delay(now - m_lastRequest); } - m_lastRequest = now; + m_lastRequest = millis(); } const uint8_t S50140::getRegset() { std::vector value; + std::lock_guard lock(m_bus.getMutex()); delayRequest(); m_bus.readHoldingRegisters(m_address, REG_Regset, 2, value); if (value.empty()) @@ -85,6 +88,7 @@ namespace drivers const uint16_t S50140::getCounterStatus() { std::vector value; + std::lock_guard lock(m_bus.getMutex()); delayRequest(); m_bus.readHoldingRegisters(m_address, REG_PartCount, 2, value); if (value.empty()) @@ -99,7 +103,8 @@ namespace drivers constexpr uint16_t resetAll = 0x0A03; constexpr uint16_t stopAll = 0x0A02; constexpr uint16_t startAll = 0x0A01; - while (retries++ < maxRetries) + std::lock_guard lock(m_bus.getMutex()); + while (retries++ < c_maxRetries) { bool ok(true); delayRequest(); @@ -123,10 +128,10 @@ namespace drivers uint8_t retries(0); std::vector values; - while (retries++ < maxRetries) + while (retries++ < c_maxRetries) { delayRequest(); - if (m_bus.readHoldingRegisters(m_address, reg, dataWords, values) && values.size() == dataWords) + if (m_bus.readHoldingRegisters(m_address, reg, c_dataWords, values) && values.size() == c_dataWords) { floatval_t fv; // potrebbe essere il contrario, vedremo fv.words.lo = values[0]; // magari va invertita ancora l'endianness diff --git a/lib/SENECA/S50140_Driver.h b/lib/SENECA/S50140_Driver.h index 810b37e..7d27770 100644 --- a/lib/SENECA/S50140_Driver.h +++ b/lib/SENECA/S50140_Driver.h @@ -11,9 +11,9 @@ namespace drivers class S50140 { private: - const uint8_t maxRetries = 5; - const uint8_t dataWords = 2; - const uint16_t minDelay = 500; + const uint8_t c_maxRetries = 5; + const uint8_t c_dataWords = 2; + const uint32_t c_minDelay = 500; const uint16_t REG_V = 0x100C; const uint16_t REG_A = 0x1016; @@ -80,6 +80,6 @@ namespace drivers private: const uint8_t m_address; drivers::MODBUS &m_bus; - uint64_t m_lastRequest; + uint32_t m_lastRequest; }; } \ No newline at end of file diff --git a/lib/TEMP/R4DCB08_Driver.cpp b/lib/TEMP/R4DCB08_Driver.cpp index 92d2ea0..6baca72 100644 --- a/lib/TEMP/R4DCB08_Driver.cpp +++ b/lib/TEMP/R4DCB08_Driver.cpp @@ -5,12 +5,24 @@ namespace drivers R4DCB08::R4DCB08(drivers::MODBUS &bus, const uint8_t address) : m_address(address), m_bus(bus), m_sensors(0) { m_sensors = getNum(); + m_lastRequest = millis(); } R4DCB08::~R4DCB08() { } + void R4DCB08::delayRequest() + { + auto now = millis(); + if ((now - m_lastRequest) < c_minDelay) + { // minimum m_lastRequest between requests + LOG_DEBUG("R4DCB08 delay request", (now-m_lastRequest)); + delay(now - m_lastRequest); + } + m_lastRequest = millis(); + } + const float R4DCB08::getTemp(const uint8_t ch) { uint8_t retries(0); @@ -20,15 +32,16 @@ namespace drivers LOG_ERROR("Invalid Temperature Channel number", ch); return MAXFLOAT; } - while (retries++ < maxRetries) + std::lock_guard lock(m_bus.getMutex()); + while (retries++ < c_maxRetries) { + delayRequest(); if (m_bus.readHoldingRegisters(m_address, REG_TEMP + ch, 1, rawT) && !rawT.empty()) { return rawT.front() / 10.0f; } LOG_ERROR("Failed to Read Temperature, device", m_address, "channel", ch); rawT.clear(); - delay(50); } return MAXFLOAT; } @@ -38,8 +51,10 @@ namespace drivers uint8_t retries(0); std::vector rawT; std::vector out; - while (retries++ < maxRetries) + std::lock_guard lock(m_bus.getMutex()); + while (retries++ < c_maxRetries) { + delayRequest(); if (m_bus.readHoldingRegisters(m_address, REG_TEMP, getNum(), rawT) && !rawT.empty()) { out.reserve(rawT.size()); @@ -51,7 +66,6 @@ namespace drivers } LOG_ERROR("Failed to Read All Temperature, device", m_address); rawT.clear(); - delay(50); } out.clear(); return out; @@ -62,19 +76,18 @@ namespace drivers uint8_t retries(0); uint8_t channel(0); corr.resize(getNum()); // max number of temperature correction values is equal to number of sensors - + std::lock_guard lock(m_bus.getMutex()); for (auto v : corr) - { // convert to decimal degreees to register value - while (retries++ < maxRetries) + { + while (retries++ < c_maxRetries) { - if (m_bus.writeRegister(m_address, REG_TEMPCORR + channel, v*10)) + delayRequest(); + if (m_bus.writeRegister(m_address, REG_TEMPCORR + channel, v * 10)) // convert to decimal degreees to register value { channel++; - delay(50); break; } LOG_ERROR("Failed to Set Temperature Correction, device", m_address); - delay(50); } } } @@ -86,20 +99,21 @@ namespace drivers std::vector out; rawV.reserve(getNum()); - while (retries++ < maxRetries) + std::lock_guard lock(m_bus.getMutex()); + while (retries++ < c_maxRetries) { + delayRequest(); if (m_bus.readHoldingRegisters(m_address, REG_TEMPCORR, getNum(), rawV)) { out.reserve(rawV.size()); for (auto v : rawV) { - out.push_back(v/10.0f); + out.push_back(v / 10.0f); } return out; } LOG_ERROR("Failed to Get Temperature Correction, device", m_address); rawV.clear(); - delay(50); } out.clear(); return out; @@ -112,8 +126,10 @@ namespace drivers uint8_t retries(0); uint8_t sensors(0); std::vector rawT; - while (retries++ < maxRetries) + std::lock_guard lock(m_bus.getMutex()); + while (retries++ < c_maxRetries) { + delayRequest(); if (m_bus.readHoldingRegisters(m_address, REG_TEMP, T_MAX, rawT)) { for (auto v : rawT) @@ -125,7 +141,6 @@ namespace drivers return m_sensors; } LOG_ERROR("Failed to Get Sensor Number, device", m_address); - delay(50); } LOG_ERROR("No Temperature Sensors Detected, device", m_address); return 0; diff --git a/lib/TEMP/R4DCB08_Driver.h b/lib/TEMP/R4DCB08_Driver.h index 0adee3d..401473c 100644 --- a/lib/TEMP/R4DCB08_Driver.h +++ b/lib/TEMP/R4DCB08_Driver.h @@ -25,7 +25,9 @@ namespace drivers T_MAX }; - const uint8_t maxRetries = 5; + private: + const uint8_t c_maxRetries = 5; + const uint32_t c_minDelay = 500; const uint16_t REG_TEMP = 0x0000; const uint16_t REG_TEMPCORR = 0x0008; @@ -41,9 +43,13 @@ namespace drivers const uint8_t getNum(); + private: + void delayRequest(); + private: const uint8_t m_address; uint8_t m_sensors; MODBUS &m_bus; + uint32_t m_lastRequest; }; } diff --git a/lib/utils/utils.cpp b/lib/utils/utils.cpp index fe50deb..d296446 100644 --- a/lib/utils/utils.cpp +++ b/lib/utils/utils.cpp @@ -1,5 +1,6 @@ #include "utils.h" + void printBytes(const char title[], const std::vector &b) { Serial0.flush(); @@ -18,20 +19,32 @@ void printBytes(const char title[], const std::vector &b) printf("%s: ", title); for (auto v : b) { - printf("0x%04x ", v); - } - printf("\n"); - Serial0.flush(); + printf("0x%04x ", v); + } + printf("\n"); + Serial0.flush(); } void printBool(const char title[], const std::vector &vals) { - Serial0.flush(); - printf("%s: ", title); - for (auto j(0); j < vals.size(); j++) - { - printf("%s ", vals.at(j) ? "True" : "False"); - } - printf("\n"); - Serial0.flush(); + Serial0.flush(); + printf("%s: ", title); + for (auto j(0); j < vals.size(); j++) + { + printf("%s ", vals.at(j) ? "True" : "False"); + } + printf("\n"); + Serial0.flush(); +} + +const std::string printBoolVec(const std::vector &vals) +{ + std::string buf; + buf.reserve(vals.size()+1); + buf.append("b"); + for (const auto v : vals) + { + buf.append(v ? "1" : "0"); + } + return buf; } diff --git a/lib/utils/utils.h b/lib/utils/utils.h index 1ac741a..6e04830 100644 --- a/lib/utils/utils.h +++ b/lib/utils/utils.h @@ -4,6 +4,7 @@ #include #include +#include #include ///////////// UTIL Functions ///////////////// @@ -13,3 +14,5 @@ void printBytes(const char title[], const std::vector &b); void printBytes(const char title[], const std::vector &b); void printBool(const char title[], const std::vector &vals); + +const std::string printBoolVec(const std::vector &vals); diff --git a/src/digitalIO.cpp b/src/digitalIO.cpp index d42356a..00180de 100644 --- a/src/digitalIO.cpp +++ b/src/digitalIO.cpp @@ -1,4 +1,5 @@ #include +#include digitalIO::digitalIO(drivers::I2C &i2c, drivers::MODBUS &bus, std::vector remotes) : m_localOuts(drivers::TCA9554PWR(i2c, TCA9554_ADDRESS)), m_remoteAddrs(remotes) { @@ -9,31 +10,76 @@ digitalIO::digitalIO(drivers::I2C &i2c, drivers::MODBUS &bus, std::vector inputs", getInNum(), "outputs", getOutNum()); } digitalIO::~digitalIO() { } -void digitalIO::digitalIOWrite(const uint8_t ch, const bool value) +void digitalIO::digitalOutWrite(const uint8_t ch, const bool value) { if (ch < 0 || ch > getOutNum()) { - LOG_ERROR("Invalid digitalIOWrite channel number", ch); + LOG_ERROR("Invalid digitalOutWrite channel number", ch); + return; } if (ch < drivers::TCA9554PWR::DO_MAX) // write to i2c device for local outputs { - digitalWriteLocal(ch, value); + writeLocal(ch, value); } else { - digitalWriteRemote(ch - drivers::TCA9554PWR::DO_MAX, value); + writeRemote(ch - drivers::TCA9554PWR::DO_MAX, value); } } -const bool digitalIO::digitalIORead(const uint8_t ch) + +void digitalIO::digitalOutWritePort(const std::vector &values) +{ + if (values.size() != getOutNum()) + { + LOG_ERROR("Invalid digitalOutWrite channel number", values.size()); + return; + } + const std::vector locals(values.begin(), values.begin() + drivers::TCA9554PWR::DO_MAX); + const std::vector remotes(values.begin() + drivers::TCA9554PWR::DO_MAX, values.end()); + writeLocalPort(locals); + writeRemotePort(remotes); +} + +const bool digitalIO::digitalOutRead(const uint8_t ch) +{ + if (ch < 0 || ch > getOutNum()) + { + LOG_ERROR("Invalid digitalOutRead channel number", ch); + return false; + } + + if (ch < drivers::TCA9554PWR::DO_MAX) // write to i2c device for local outputs + { + return readLocalIn(ch); + } + else + { + return readRemoteIn(ch - drivers::TCA9554PWR::DO_MAX); + } +} + +const std::vector digitalIO::digitalOutReadPort() +{ + const std::vector locals(readLocalOutPort()); + const std::vector remotes(readRemoteOutPort()); + std::vector rv; + rv.reserve(getOutNum()); + rv.insert(rv.begin(), locals.begin(), locals.end()); + rv.insert(rv.end(), remotes.begin(), remotes.end()); + return std::move(rv); +} + +const bool digitalIO::digitalInRead(const uint8_t ch) { if (ch < 0 || ch > getInNum()) { @@ -42,83 +88,281 @@ const bool digitalIO::digitalIORead(const uint8_t ch) if (ch < (DI_MAX - DI1)) // read from local inputs not as gpio numbers { - return digitalReadLocal(ch); + return readLocalIn(ch); } else { - return digitalReadRemote(ch - (DI_MAX - DI1)); + return readRemoteIn(ch - (DI_MAX - DI1)); } } +const std::vector digitalIO::digitalInReadPort() +{ + const std::vector locals(readLocalInPort()); + const std::vector remotes(readRemoteInPort()); + std::vector rv; + rv.reserve(getInNum()); + rv.insert(rv.begin(), locals.begin(), locals.end()); + rv.insert(rv.end(), remotes.begin(), remotes.end()); + return std::move(rv); +} + void digitalIO::reset() { // set all local and remote outputs to 0 m_localOuts.setPort(0x00); - for (auto r: m_remotes) + for (auto r : m_remotes) r.resetAll(false); } +const uint8_t digitalIO::getLocalInNum() +{ + return (DI_MAX - DI1); +} +const uint8_t digitalIO::getLocalOutNum() +{ + return drivers::TCA9554PWR::DO_MAX; +} +const uint8_t digitalIO::getRemoteInNum() +{ + return m_remotes.size() * remoteIO::CH_MAX; +} +const uint8_t digitalIO::getRemoteOutNum() +{ + + return m_remotes.size() * remoteIO::CH_MAX; +} + const uint8_t digitalIO::getOutNum() { - return drivers::TCA9554PWR::DO_MAX + m_remotes.size() * remoteIO::CH_MAX; + return getLocalOutNum() + getRemoteOutNum(); } const uint8_t digitalIO::getInNum() { - return DI_MAX + m_remotes.size() * remoteIO::CH_MAX; + return getLocalInNum() + getRemoteInNum(); } -void digitalIO::digitalWriteLocal(const uint8_t ch, const bool value) +void digitalIO::writeLocal(const uint8_t ch, const bool value) { uint8_t retries(0); - while (retries++ < maxRetries) + while (retries++ < c_maxRetries) { if (m_localOuts.setOut(ch, value)) { - LOG_DEBUG("digitalWriteLocal channel", ch, " status", value ? "True" : "False"); - break; + LOG_DEBUG("writeLocal channel", ch, " status", value ? "True" : "False"); + return; } - LOG_ERROR("Failed digitalWriteLocal channel ", ch, " status", value ? "True" : "False"); + LOG_ERROR("Failed writeLocal channel ", ch, " status", value ? "True" : "False"); } } -void digitalIO::digitalWriteRemote(const uint8_t ch, const bool value) +void digitalIO::writeLocalPort(const std::vector &values) { uint8_t retries(0); - const uint8_t selectedRemote(floor(ch / 8.0f)); + uint8_t decValue(0); + for (uint8_t i(0); i < 8; i++) // convert from bits to byte value + { + if (values[i]) + decValue |= High << i; + } + while (retries++ < c_maxRetries) + { + if (m_localOuts.setPort(decValue)) + { + LOG_DEBUG("writeLocalPort value", printBoolVec(values).c_str()); + return; + } + LOG_ERROR("Failed writeLocalPort value", printBoolVec(values).c_str()); + } +} + +void digitalIO::writeRemote(const uint8_t ch, const bool value) +{ + uint8_t retries(0); + const uint8_t selectedRemote(floor(ch / (float)remoteIO::CH_MAX)); const uint8_t selectedChannel(ch % remoteIO::CH_MAX); - while (retries++ < maxRetries) + while (retries++ < c_maxRetries) { if (m_remotes[selectedRemote].setOut((remoteIO::channel_t)selectedChannel, value)) { - LOG_DEBUG("digitalWriteRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); - break; + LOG_DEBUG("writeRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + return; } - LOG_ERROR("Failed digitalWriteRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + LOG_ERROR("Failed writeRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); } } -const bool digitalIO::digitalReadLocal(const uint8_t ch) +void digitalIO::writeRemotePort(const std::vector &values) +{ + uint8_t retries(0); + while (retries++ < c_maxRetries) + { + bool ok(true); + for (uint8_t i(0); i < values.size(); i += remoteIO::CH_MAX) + { + const uint8_t selectedRemote(floor(i / (float)remoteIO::CH_MAX)); + const std::vector currValues(values.begin() + i, values.begin() + i + remoteIO::CH_MAX); + ok &= m_remotes[selectedRemote].setOutPort(currValues); + if (ok) + { + LOG_DEBUG("writeRemotePort remote", selectedRemote, "values", printBoolVec(values).c_str()); + continue; + } + LOG_ERROR("Failed writeRemotePort remote", selectedRemote, "values", printBoolVec(values).c_str()); + break; + } + if (ok) + break; + } +} + +const bool digitalIO::readLocalIn(const uint8_t ch) { bool value = !digitalRead(ch + DI1); // base pin number in enum, inverted input - LOG_DEBUG("digitalReadLocal pin", (ch + DI1), " status", value ? "True" : "False"); + LOG_DEBUG("readLocalIn pin", (ch + DI1), " status", value ? "True" : "False"); return value; } -const bool digitalIO::digitalReadRemote(const uint8_t ch) +const bool digitalIO::readLocalOut(const uint8_t ch) +{ + bool value(false); + uint8_t retries(0); + while (retries++ < c_maxRetries) + { + if (m_localOuts.readOut(ch, value)) + { + LOG_DEBUG("readLocalOut pin", (ch), " status", value ? "True" : "False"); + return value; + } + LOG_ERROR("Failed readLocalOut channel", ch); + } + return false; +} + +const std::vector digitalIO::readLocalInPort() +{ + std::vector values(getLocalInNum()); + for (uint8_t i(0); i < values.size(); i++) + { + values[i] = readLocalIn(i); + } + LOG_DEBUG("readLocalInPort values", printBoolVec(values).c_str()); + return values; +} + +const std::vector digitalIO::readLocalOutPort() +{ + uint8_t retries(0); + uint8_t state(0); + std::vector values(getLocalOutNum()); + while (retries++ < c_maxRetries) + { + if (m_localOuts.readPort(state)) + { + for (uint8_t i(0); i < values.size(); i++) + { + values[i] = (state >> i) & High; + } + LOG_DEBUG("readLocalOutPort values", printBoolVec(values).c_str()); + return values; + } + LOG_ERROR("Failed readLocalOutPort"); + } + values.clear(); + return values; +} + +const bool digitalIO::readRemoteIn(const uint8_t ch) { uint8_t retries(0); const uint8_t selectedRemote(floor(ch / 8.0f)); const uint8_t selectedChannel(ch % remoteIO::CH_MAX); bool value; - while (retries++ < maxRetries) + while (retries++ < c_maxRetries) { if (m_remotes[selectedRemote].getIn((remoteIO::channel_t)selectedChannel, value)) { - LOG_DEBUG("digitalReadRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + LOG_DEBUG("readRemoteIn remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); return value; } - LOG_ERROR("Failed digitalReadRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + LOG_ERROR("Failed readRemoteIn remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); } return false; -} \ No newline at end of file +} + +const bool digitalIO::readRemoteOut(const uint8_t ch) +{ + uint8_t retries(0); + const uint8_t selectedRemote(floor(ch / (float)remoteIO::CH_MAX)); + const uint8_t selectedChannel(ch % remoteIO::CH_MAX); + bool value; + while (retries++ < c_maxRetries) + { + if (m_remotes[selectedRemote].getOut((remoteIO::channel_t)selectedChannel, value)) + { + LOG_DEBUG("readRemoteOut remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + return value; + } + LOG_ERROR("Failed readRemoteOut remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + } + return false; +} + +const std::vector digitalIO::readRemoteInPort() +{ + uint8_t retries(0); + std::vector values; + values.reserve(getRemoteInNum()); + while (retries++ < c_maxRetries) + { + bool ok(true); + for (uint8_t i(0); i < getRemoteInNum(); i += remoteIO::CH_MAX) + { + const uint8_t selectedRemote(floor(i / (float)remoteIO::CH_MAX)); + std::vector remVals(remoteIO::CH_MAX); + ok &= m_remotes[selectedRemote].getInPort(remVals); + if (ok) + { + values.insert(values.begin() + values.size(), remVals.begin(), remVals.end()); + LOG_DEBUG("readRemoteInPort remote", selectedRemote, "values", printBoolVec(remVals).c_str()); + continue; + } + LOG_ERROR("Failed readRemoteInPort remote", selectedRemote); + break; + } + if (ok) + return values; + } + values.clear(); + return values; +} + +const std::vector digitalIO::readRemoteOutPort() +{ + uint8_t retries(0); + std::vector values; + values.reserve(getRemoteOutNum()); + while (retries++ < c_maxRetries) + { + bool ok(true); + for (uint8_t i(0); i < getRemoteOutNum(); i += remoteIO::CH_MAX) + { + const uint8_t selectedRemote(floor(i / (float)remoteIO::CH_MAX)); + std::vector remVals(remoteIO::CH_MAX); + ok &= m_remotes[selectedRemote].getOutPort(remVals); + if (ok) + { + values.insert(values.begin() + values.size(), remVals.begin(), remVals.end()); + LOG_DEBUG("readRemoteOutPort remote", selectedRemote, "values", printBoolVec(remVals).c_str()); + continue; + } + LOG_ERROR("Failed readRemoteOutPort remote", selectedRemote); + break; + } + if (ok) + return values; + } + values.clear(); + return values; +} diff --git a/src/digitalIO.h b/src/digitalIO.h index 73b8c5c..1b29666 100644 --- a/src/digitalIO.h +++ b/src/digitalIO.h @@ -28,25 +28,44 @@ private: DI_MAX }; - const uint8_t maxRetries = 5; + const uint8_t c_maxRetries = 5; public: digitalIO(drivers::I2C &i2c, drivers::MODBUS &bus, std::vector remotes); ~digitalIO(); - void digitalIOWrite(const uint8_t ch, const bool value); - const bool digitalIORead(const uint8_t ch); + void digitalOutWrite(const uint8_t ch, const bool value); + void digitalOutWritePort(const std::vector &values); + const bool digitalOutRead(const uint8_t ch); + const std::vector digitalOutReadPort(); + + const bool digitalInRead(const uint8_t ch); + const std::vector digitalInReadPort(); + void reset(); const uint8_t getOutNum(); const uint8_t getInNum(); -private: - void digitalWriteLocal(const uint8_t ch, const bool value); - void digitalWriteRemote(const uint8_t ch, const bool value); + private: + const uint8_t getLocalInNum(); + const uint8_t getLocalOutNum(); + const uint8_t getRemoteInNum(); + const uint8_t getRemoteOutNum(); - const bool digitalReadLocal(const uint8_t ch); - const bool digitalReadRemote(const uint8_t ch); + void writeLocal(const uint8_t ch, const bool value); + void writeLocalPort(const std::vector &values); + void writeRemote(const uint8_t ch, const bool value); + void writeRemotePort(const std::vector &values); + + const bool readLocalIn(const uint8_t ch); + const bool readLocalOut(const uint8_t ch); + const std::vector readLocalInPort(); + const std::vector readLocalOutPort(); + const bool readRemoteIn(const uint8_t ch); + const bool readRemoteOut(const uint8_t ch); + const std::vector readRemoteInPort(); + const std::vector readRemoteOutPort(); private: std::vector m_remoteAddrs; diff --git a/src/main.cpp b/src/main.cpp index 7c05d02..efb4ff3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,11 +1,9 @@ #define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG +#include #include #include -#include -#include - #include #include #include @@ -15,43 +13,29 @@ #include #include - +#include #include "utils.h" /////////////// GLOBALS /////////////// -Config& conf = Config::getInstance(); +Config &conf = Config::getInstance(); /////////////// GLOBALS /////////////// -void callback(char *topic, uint8_t *payload, unsigned int length) +void testAction(const ArduinoJson::JsonDocument &doc) { - std::string pl; - pl.resize(length); - std::snprintf(pl.data(), length, "%s", payload); - LOG_INFO("Message: Topic [", topic, "], Payload [", pl.c_str(), "]"); + std::string message; + ArduinoJson::serializeJsonPretty(doc, message); + LOG_INFO("Received on testAction\n", message.c_str()); } -void myTask(void *mqtt) -{ - auto client = (PubSubClient *)(mqtt); - while (client->connected()) - { - client->loop(); - vTaskDelay(pdMS_TO_TICKS(100)); - } - LOG_ERROR("Mqtt Loop Ended, client disconnected"); - vTaskDelete(NULL); // delete the current task -}; - void setup() { Serial.begin(9600); LOG_ATTACH_SERIAL(Serial); - conf.init(); + conf.init(); // read the configuration from internal flash } void loop() { - const uint8_t baseRegister(0x00); uint16_t k(0); uint8_t sensors(0); bool buzzing(false); @@ -63,29 +47,47 @@ void loop() auto rtc = drivers::PCF85063(i2c, PCF85063_ADDRESS); auto eth = drivers::Ethernet(conf.m_ethHostname); auto tmp = drivers::R4DCB08(bus, conf.m_modbusTemperatureAddr); - delay(100); - auto io = digitalIO(i2c, bus, {conf.m_modbusRelayAddr}); - delay(100); auto seneca = drivers::S50140(bus, conf.m_modbusSenecaAddr); auto buzzer = drivers::Buzzer(); auto led = drivers::Led(); - //////////////// DEVICES //////////////// + delay(500); + auto io = digitalIO(i2c, bus, {conf.m_modbusRelayAddr}); // Initialize temperature sensors sensors = tmp.getNum(); LOG_INFO("Temperature sensors connected ->", sensors); + //////////////// DEVICES //////////////// //////////////// NETWORK //////////////// - // MQTT Test // - NetworkClient tcp; - PubSubClient mqtt(tcp); - mqtt.setServer(conf.m_mqttHost.c_str(), conf.m_mqttPort); - mqtt.setCallback(callback); + auto mqtt = MQTTwrapper(); //////////////// NETWORK //////////////// - - //////////////// NETWORK //////////////// - /////////////// CALLBACK //////////////// + + std::function mycallback = + [&io](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); + }; + + //////////////// NETWORK //////////////// + /////////////// CALLBACK //////////////// Network.onEvent( - [ð, &rtc, &mqtt, &buzzer, &led](arduino_event_id_t event, arduino_event_info_t info) -> void + [ð, &rtc, &mqtt, &buzzer, &led, &mycallback](arduino_event_id_t event, arduino_event_info_t info) -> void { eth.onEvent(event, info); // Arduino Ethernet event handler if (!eth.isConnected()) @@ -98,7 +100,7 @@ void loop() { if (eth.getNtpTime(ntpTime) && rtc.setDatetime(drivers::PCF85063::fromEpoch(ntpTime))) { - buzzer.beep(250, NOTE_F); + // buzzer.beep(250, NOTE_F); led.setColor({255, 255, 0}); const drivers::PCF85063::datetime_t dt(drivers::PCF85063::fromEpoch(ntpTime)); LOG_INFO("NTP Time: ", drivers::PCF85063::datetime2str(dt).c_str()); @@ -108,10 +110,10 @@ void loop() } while (mqttRetries++ < conf.m_mqttRetries) { - if (!mqtt.connected() && mqtt.connect(conf.m_mqttClientName.c_str())) + if (mqtt.connect()) { - mqtt.subscribe("test/esp32-in"); - xTaskCreatePinnedToCore(myTask, "mqttLoop", 4096, &mqtt, 2, NULL, 1); + mqtt.subscribe("test/esp32-in", testAction); + mqtt.subscribe("test/esp32-functional", mycallback); break; } delay(100); @@ -128,34 +130,43 @@ void loop() const std::string timeStr(rtc.getTimeStr()); LOG_INFO("Current Datetime", timeStr.c_str()); - mqtt.publish("test/esp32-out", ("[" + std::to_string(k) + "] -> " + timeStr).c_str()); + ArduinoJson::JsonDocument ts; + ts["loopIterator"] = k; + ts["currentTime"] = timeStr; + mqtt.publish("test/esp32-out", ts); uint8_t i(0); for (auto v : tmp.getTempAll()) { LOG_INFO("Temperature channel", i++, "->", v); } - i = 0; - delay(10); - for (auto v : tmp.getCorrection()) + + LOG_INFO("Read Red"); + if (io.digitalInRead(0)) // rosso { - LOG_INFO("Temperature correction channel", i++, "tc", v); + 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); } - delay(100); - 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); - - if (io.digitalIORead(0)) // rosso - { - uint8_t regset(seneca.getRegset()); - uint16_t countStat(seneca.getCounterStatus()); - LOG_INFO("Register Set: ", regset); - LOG_INFO("Counter Status: ", countStat); - seneca.resetPartialCounters(); - } - delay(100); - if (io.digitalIORead(8)) // blu + LOG_INFO("Read Blue"); + if (io.digitalInRead(8)) // blu { if (!buzzing) { @@ -171,15 +182,22 @@ void loop() } LOG_INFO("Buzzing -> ", buzzing ? "True" : "False"); } - - if(io.digitalIORead(9)) { // verde + + LOG_INFO("Read Green"); + if (io.digitalInRead(9)) + { // verde conf.resetConfig(); } - - if(io.digitalIORead(10)) { // giallo + + LOG_INFO("Read Yellow"); + if (io.digitalInRead(10)) + { // giallo 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); } diff --git a/src/mqtt.cpp b/src/mqtt.cpp new file mode 100644 index 0000000..5e79fc4 --- /dev/null +++ b/src/mqtt.cpp @@ -0,0 +1,176 @@ +#include + +#define STACK_DEPTH 4096 +#define PRIOTITY 2 + +MQTTwrapper::MQTTwrapper() : m_config(Config::getInstance()), m_tcp(NetworkClient()), m_client(PubSubClient(m_tcp)), m_loopHandle(NULL) +{ + m_client.setServer(m_config.m_mqttHost.c_str(), m_config.m_mqttPort); + m_client.setKeepAlive(15); + getInstance(this); +} + +MQTTwrapper::~MQTTwrapper() +{ + disconnect(); +} + +const bool MQTTwrapper::connect() +{ + if (!m_client.connect(m_config.m_mqttClientName.c_str())) + { + LOG_ERROR("MQTT unable to connect to host", m_config.m_mqttHost.c_str()); + return false; + } + LOG_INFO("MQTT client connected to", m_config.m_mqttHost.c_str()); + if (m_loopHandle == NULL) + { + xTaskCreate(clientLoop, "mqttLoop", STACK_DEPTH, this, PRIOTITY, &m_loopHandle); + m_client.setCallback(MQTTwrapper::callback); + } + return true; +} + +const bool MQTTwrapper::disconnect() +{ + m_client.disconnect(); + if (m_loopHandle) + { + vTaskDelete(m_loopHandle); // immediate terminate loop + m_loopHandle = NULL; + } + return true; +} + +const bool MQTTwrapper::subscribe(topic_t topic, action_t action) +{ + if (m_actionMap.contains(topic)) + { + LOG_WARN("MQTT was already subscribed to", topic.c_str()); + return true; + } + if (m_client.subscribe(topic.c_str())) + { + m_actionMap[topic] = action; + LOG_INFO("MQTT subscribed to", topic.c_str()); + return true; + } + LOG_ERROR("MQTT unable to subscribe to", topic.c_str()); + return false; +} + +const bool MQTTwrapper::unsubscribe(topic_t topic) +{ + if (!m_actionMap.contains(topic)) + { + LOG_WARN("MQTT was NOT subscribed to", topic.c_str()); + return false; + } + if (m_client.unsubscribe(topic.c_str())) + { + LOG_INFO("MQTT unsubscribed to", topic.c_str()); + m_actionMap.erase(topic); + return true; + } + LOG_ERROR("MQTT unable to unsubscribe to", topic.c_str()); + return false; +} + +const bool MQTTwrapper::connected() +{ + return m_loopHandle != NULL; +} + +const bool MQTTwrapper::publish(topic_t topic, const ArduinoJson::JsonDocument obj) +{ + std::string message; + if (!m_client.connected()) + { + LOG_ERROR("MQTT client not connected"); + return false; + } + if (!ArduinoJson::serializeJson(obj, message)) + { + LOG_ERROR("MQTT failed to serialize object"); + return false; + } + if (m_client.publish(topic.c_str(), message.c_str())) + { + LOG_DEBUG("MQTT published topic [", topic.c_str(), "] - message [", message.c_str(), "]"); + return true; + } + LOG_ERROR("MQTT failed to publish topic [", topic.c_str(), "] - message [", message.c_str(), "]"); + return false; +} + +void MQTTwrapper::callback(char *topic, uint8_t *payload, unsigned int length) +{ + std::string pl; + pl.resize(length + 1); + std::snprintf(pl.data(), length + 1, "%s", payload); + auto inst = getInstance(); + if (inst) + { + inst->onMessage(std::string(topic), pl); + return; + } + LOG_ERROR("MQTT no client instance set"); + return; +} + +void MQTTwrapper::onMessage(const std::string topic, const std::string message) +{ + ArduinoJson::JsonDocument obj; + LOG_DEBUG("MQTT received topic [", topic.c_str(), "] - message [", message.c_str(), "]"); + if (ArduinoJson::deserializeJson(obj, message) == ArduinoJson::DeserializationError::Ok) + { + m_actionMap[topic](obj); + return; + } + LOG_ERROR("MQTT failed to deserialize message\n", message.c_str()); + return; +} + +void MQTTwrapper::clientLoop(void *params) +{ + auto wrapper = (MQTTwrapper *)(params); + auto &client = wrapper->m_client; + auto &config = wrapper->m_config; + auto &stateMap = wrapper->stateMap; + const auto loopTime = config.m_mqttLoopTime; + const auto mqttRetries = config.m_mqttRetries; + const auto clientName = config.m_mqttClientName; + uint8_t connectAttempt(0); + LOG_INFO("MQTT starting client loop"); + while (connectAttempt++ < mqttRetries) + { + while (client.connected()) + { + client.loop(); + delay(loopTime); + } + if (client.state() != MQTT_CONNECTED) + { + LOG_ERROR("MQTT disconnect reason ", stateMap.at(client.state()).c_str()); + delay(loopTime * 50); + const bool ok = client.connect(clientName.c_str()); + LOG_WARN("MQTT reconnected", ok ? "True" : "False"); + if (ok) + { + for (auto &v : wrapper->m_actionMap) + { + const std::string &topic(v.first); + LOG_WARN("MQTT resubscribing to", topic.c_str()); + if (!wrapper->m_client.subscribe(topic.c_str())) + { + LOG_ERROR("Unable to resubscribe to", topic.c_str()); + } + } + connectAttempt = 0; + } + } + } + LOG_ERROR("MQTT client loop terminated, disconnected"); + wrapper->m_loopHandle = NULL; + vTaskDelete(NULL); // delete the current task +} diff --git a/src/mqtt.h b/src/mqtt.h new file mode 100644 index 0000000..ad2df4f --- /dev/null +++ b/src/mqtt.h @@ -0,0 +1,75 @@ +#pragma once + +#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG + +#include +#include +#include +#include +#include + +#include + +#include +#include + +typedef std::string topic_t; +typedef std::function action_t; // the actions receive a JsonObject containing the received message +typedef std::map action_map_t; + +class MQTTwrapper +{ + +private: + const std::map stateMap = { + {-4, "MQTT_CONNECTION_TIMEOUT"}, + {-3, "MQTT_CONNECTION_LOST"}, + {-2, "MQTT_CONNECT_FAILED"}, + {-1, "MQTT_DISCONNECTED"}, + {0, "MQTT_CONNECTED"}, + {1, "MQTT_CONNECT_BAD_PROTOCOL"}, + {2, "MQTT_CONNECT_BAD_CLIENT_ID"}, + {3, "MQTT_CONNECT_UNAVAILABLE"}, + {4, "MQTT_CONNECT_BAD_CREDENTIALS"}, + {5, "MQTT_CONNECT_UNAUTHORIZED"} + }; + +private: + static MQTTwrapper * + getInstance(MQTTwrapper *inst = nullptr) + { + static std::unique_ptr m_instance; + if (inst) + m_instance.reset(inst); + if (m_instance) + return m_instance.get(); + return nullptr; + } + +public: + MQTTwrapper(); + ~MQTTwrapper(); + + const bool connect(); + const bool disconnect(); + const bool connected(); + + const bool subscribe(topic_t topic, action_t action); + const bool unsubscribe(topic_t topic); + + const bool publish(topic_t topic, const ArduinoJson::JsonDocument obj); + +private: + static void callback(char *topic, uint8_t *payload, unsigned int length); // C-style callback only to invoke onMessage + void onMessage(const std::string topic, const std::string message); + + // infinite loop to call the client loop method in a taskHandle + static void clientLoop(void *params); + +private: + const Config &m_config; + action_map_t m_actionMap; + NetworkClient m_tcp; + PubSubClient m_client; + TaskHandle_t m_loopHandle; +}; diff --git a/src/remoteIO.cpp b/src/remoteIO.cpp index 8da6d71..66d69e8 100644 --- a/src/remoteIO.cpp +++ b/src/remoteIO.cpp @@ -4,12 +4,14 @@ remoteIO::remoteIO(const uint8_t address, drivers::MODBUS &bus) : m_address(addr { LOG_INFO("Initializing relay module"); std::vector response; + std::lock_guard lock(m_bus.getMutex()); if (!m_bus.readHoldingRegisters(m_address, REG_VERSION, 1, response)) { LOG_ERROR("Unable to inizialize relay module"); }; LOG_INFO("Software version", std::to_string(response.at(0) / 100.0f).c_str()); m_initialized = true; + m_lastRequest = millis(); resetAll(false); } @@ -19,10 +21,23 @@ remoteIO::~remoteIO() resetAll(false); } +void remoteIO::delayRequest() +{ + auto now = millis(); + if ((now - m_lastRequest) < c_minDelay) + { // minimum m_lastRequest between requests + LOG_DEBUG("remoteIO delay request", (now - m_lastRequest)); + delay(now - m_lastRequest); + } + m_lastRequest = millis(); +} + const bool remoteIO::setOut(const channel_t ch, const bool value) { if (!m_initialized) return false; + std::lock_guard lock(m_bus.getMutex()); + delayRequest(); LOG_DEBUG("Write Channel", ch, "->", value ? "True" : "False"); return m_bus.writeCoil(m_address, REG_COILS + ch, value); } @@ -31,6 +46,8 @@ const bool remoteIO::toggleOut(const channel_t ch) { if (!m_initialized) return false; + std::lock_guard lock(m_bus.getMutex()); + delayRequest(); std::vector value; if (!m_bus.readCoils(m_address, REG_COILS + ch, 1, value)) return false; @@ -42,14 +59,18 @@ const bool remoteIO::setOutPort(const std::vector values) { if (!m_initialized) return false; + std::lock_guard lock(m_bus.getMutex()); + delayRequest(); LOG_DEBUG("Write Port", CH_MAX); - return m_bus.writeCoils(m_address, CH_MAX, values); + return m_bus.writeCoils(m_address, REG_COILS, values); } const bool remoteIO::getOut(const channel_t ch, bool &value) { if (!m_initialized) return false; + std::lock_guard lock(m_bus.getMutex()); + delayRequest(); std::vector values; if (!m_bus.readCoils(m_address, REG_COILS + ch, 1, values)) return false; @@ -62,14 +83,18 @@ const bool remoteIO::getOutPort(std::vector &values) { if (!m_initialized) return false; + std::lock_guard lock(m_bus.getMutex()); + delayRequest(); LOG_DEBUG("Read Port", CH_MAX); - return m_bus.readCoils(m_address, REG_COILS, 8, values); + return m_bus.readCoils(m_address, REG_COILS, CH_MAX, values); } const bool remoteIO::getIn(const channel_t input, bool &value) { if (!m_initialized) return false; + std::lock_guard lock(m_bus.getMutex()); + delayRequest(); std::vector values; if (!m_bus.readInputs(m_address, REG_INPUT + input, 1, values)) return false; @@ -82,6 +107,8 @@ const bool remoteIO::getInPort(std::vector &values) { if (!m_initialized) return false; + std::lock_guard lock(m_bus.getMutex()); + delayRequest(); LOG_DEBUG("Read Inputs", CH_MAX); return m_bus.readInputs(m_address, REG_INPUT, CH_MAX, values); } diff --git a/src/remoteIO.h b/src/remoteIO.h index 1ee49b1..d31ca4b 100644 --- a/src/remoteIO.h +++ b/src/remoteIO.h @@ -8,9 +8,21 @@ class remoteIO { public: - typedef enum {CH1, CH2, CH3, CH4, CH5, CH6, CH7, CH8, CH_MAX} channel_t; + typedef enum + { + CH1, + CH2, + CH3, + CH4, + CH5, + CH6, + CH7, + CH8, + CH_MAX + } channel_t; private: + const uint32_t c_minDelay = 100; const uint16_t REG_VERSION = 0x8000; const uint16_t REG_COILS = 0x0000; const uint16_t REG_INPUT = 0x0000; @@ -32,8 +44,12 @@ public: void resetAll(const bool value); +private: + void delayRequest(); + private: bool m_initialized; drivers::MODBUS &m_bus; const uint8_t m_address; + uint32_t m_lastRequest; };