diff --git a/lib/GPIO/TCA9554PWR_Driver.cpp b/lib/GPIO/TCA9554PWR_Driver.cpp index 0bd354e..0c1d864 100644 --- a/lib/GPIO/TCA9554PWR_Driver.cpp +++ b/lib/GPIO/TCA9554PWR_Driver.cpp @@ -39,7 +39,7 @@ namespace drivers uint8_t currState(0); uint8_t newState(0); - if (ch < OUT_PIN1 || ch > OUT_PIN8) + if (ch < DO1 || ch > DO8) { LOG_ERROR("Invalid write to output channel: [%d]", ch); return false; @@ -64,7 +64,7 @@ namespace drivers const bool TCA9554PWR::readOut(const uint8_t ch) { uint8_t currState(0); - if (ch < OUT_PIN1 || ch > OUT_PIN8) + if (ch < DO1 || ch > DO8) { LOG_ERROR("Invalid read to output channel: [%d]", ch); return false; diff --git a/lib/GPIO/TCA9554PWR_Driver.h b/lib/GPIO/TCA9554PWR_Driver.h index 61961cf..5ea2da8 100644 --- a/lib/GPIO/TCA9554PWR_Driver.h +++ b/lib/GPIO/TCA9554PWR_Driver.h @@ -21,18 +21,18 @@ namespace drivers { public: - enum + typedef enum { - OUT_PIN1, - OUT_PIN2, - OUT_PIN3, - OUT_PIN4, - OUT_PIN5, - OUT_PIN6, - OUT_PIN7, - OUT_PIN8, - OUT_PIN_MAX - }; + DO1, + DO2, + DO3, + DO4, + DO5, + DO6, + DO7, + DO8, + DO_MAX + } channel_t; TCA9554PWR(I2C &i2c, const uint8_t address); ~TCA9554PWR(); diff --git a/lib/RS485/RS485_Driver.h b/lib/RS485/RS485_Driver.h index d68ea39..2156db1 100644 --- a/lib/RS485/RS485_Driver.h +++ b/lib/RS485/RS485_Driver.h @@ -6,17 +6,6 @@ #include #include -#define Extension_CH1 1 // Expansion Channel 1 -#define Extension_CH2 2 // Expansion Channel 2 -#define Extension_CH3 3 // Expansion Channel 3 -#define Extension_CH4 4 // Expansion Channel 4 -#define Extension_CH5 5 // Expansion Channel 5 -#define Extension_CH6 6 // Expansion Channel 6 -#define Extension_CH7 7 // Expansion Channel 7 -#define Extension_CH8 8 // Expansion Channel 8 -#define Extension_ALL_ON 9 // Expansion ALL ON -#define Extension_ALL_OFF 10 // Expansion ALL OFF - namespace drivers { class RS485 @@ -34,7 +23,7 @@ namespace drivers HardwareSerial &m_serial; }; - class MODBUS : public RS485 + class MODBUS : private RS485 { static const uint8_t RESP_HEADER_SIZE = 3; diff --git a/src/digitalIO.cpp b/src/digitalIO.cpp new file mode 100644 index 0000000..d42356a --- /dev/null +++ b/src/digitalIO.cpp @@ -0,0 +1,124 @@ +#include + +digitalIO::digitalIO(drivers::I2C &i2c, drivers::MODBUS &bus, std::vector remotes) : m_localOuts(drivers::TCA9554PWR(i2c, TCA9554_ADDRESS)), m_remoteAddrs(remotes) +{ + for (uint8_t i(DI1); i < DI_MAX; i++) + { + pinMode(i, INPUT_PULLUP); // set all local pins as digitalInput + } + + for (auto a : remotes) + { + m_remotes.emplace_back(remoteIO(a, bus)); + } +} + +digitalIO::~digitalIO() +{ +} + +void digitalIO::digitalIOWrite(const uint8_t ch, const bool value) +{ + if (ch < 0 || ch > getOutNum()) + { + LOG_ERROR("Invalid digitalIOWrite channel number", ch); + } + + if (ch < drivers::TCA9554PWR::DO_MAX) // write to i2c device for local outputs + { + digitalWriteLocal(ch, value); + } + else + { + digitalWriteRemote(ch - drivers::TCA9554PWR::DO_MAX, value); + } +} +const bool digitalIO::digitalIORead(const uint8_t ch) +{ + if (ch < 0 || ch > getInNum()) + { + LOG_ERROR("Invalid digitalIORead channel number", ch); + } + + if (ch < (DI_MAX - DI1)) // read from local inputs not as gpio numbers + { + return digitalReadLocal(ch); + } + else + { + return digitalReadRemote(ch - (DI_MAX - DI1)); + } +} + +void digitalIO::reset() +{ + // set all local and remote outputs to 0 + m_localOuts.setPort(0x00); + for (auto r: m_remotes) + r.resetAll(false); +} + +const uint8_t digitalIO::getOutNum() +{ + return drivers::TCA9554PWR::DO_MAX + m_remotes.size() * remoteIO::CH_MAX; +} + +const uint8_t digitalIO::getInNum() +{ + return DI_MAX + m_remotes.size() * remoteIO::CH_MAX; +} + +void digitalIO::digitalWriteLocal(const uint8_t ch, const bool value) +{ + uint8_t retries(0); + while (retries++ < maxRetries) + { + if (m_localOuts.setOut(ch, value)) + { + LOG_DEBUG("digitalWriteLocal channel", ch, " status", value ? "True" : "False"); + break; + } + LOG_ERROR("Failed digitalWriteLocal channel ", ch, " status", value ? "True" : "False"); + } +} + +void digitalIO::digitalWriteRemote(const uint8_t ch, const bool value) +{ + uint8_t retries(0); + const uint8_t selectedRemote(floor(ch / 8.0f)); + const uint8_t selectedChannel(ch % remoteIO::CH_MAX); + while (retries++ < maxRetries) + { + if (m_remotes[selectedRemote].setOut((remoteIO::channel_t)selectedChannel, value)) + { + LOG_DEBUG("digitalWriteRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + break; + } + LOG_ERROR("Failed digitalWriteRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + } +} + +const bool digitalIO::digitalReadLocal(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"); + return value; +} + +const bool digitalIO::digitalReadRemote(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) + { + if (m_remotes[selectedRemote].getIn((remoteIO::channel_t)selectedChannel, value)) + { + LOG_DEBUG("digitalReadRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + return value; + } + LOG_ERROR("Failed digitalReadRemote remote", selectedRemote, " channel ", selectedChannel, " status", value ? "True" : "False"); + } + return false; +} \ No newline at end of file diff --git a/src/digitalIO.h b/src/digitalIO.h new file mode 100644 index 0000000..d08b601 --- /dev/null +++ b/src/digitalIO.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +#define ON true +#define OFF false + +class digitalIO +{ + +private: + enum localInputs + { + DI1 = 4, // gpio for local inputs starts at 4 as per manufacturer documentation + DI2, + DI3, + DI4, + DI5, + DI6, + DI7, + DI8, + DI_MAX + }; + + const uint8_t 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 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); + + const bool digitalReadLocal(const uint8_t ch); + const bool digitalReadRemote(const uint8_t ch); + +private: + std::vector m_remoteAddrs; + drivers::TCA9554PWR m_localOuts; + std::vector m_remotes; +}; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 2d9c48f..0b175e0 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,15 +1,15 @@ -#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO +#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG #include #include #include #include -#include -#include #include #include +#include + #include "utils.h" void callback(char *topic, uint8_t *payload, unsigned int length) @@ -42,29 +42,28 @@ void loop() const uint8_t relayBoardAddr(0x01); const uint8_t baseRegister(0x00); uint16_t k(0); + uint8_t ethRetries(0); //////////////// DEVICES //////////////// // Declared here to keep devices local to the main loop otherwise the kernel crashes // auto i2c = drivers::I2C(); - auto relays = drivers::TCA9554PWR(i2c, TCA9554_ADDRESS); auto bus = drivers::MODBUS(9600, SERIAL_8N1); auto rtc = drivers::PCF85063(i2c, PCF85063_ADDRESS); auto eth = drivers::Ethernet("waveshare-test"); + auto io = digitalIO(i2c, bus, {relayBoardAddr}); Network.onEvent([ð](arduino_event_id_t event, arduino_event_info_t info) { eth.onEvent(event, info); }); - while (!eth.isConnected()) + while (!eth.isConnected() && ethRetries++ < 5) { - LOG_WARN("Waiting for Ethernet"); + LOG_WARN("Waiting for Ethernet retry", ethRetries); delay(1000); } - // Set RTC time at startup + // Get RTC time at startup time_t ntpTime; drivers::PCF85063::datetime_t dt; - eth.getNtpTime(ntpTime); - rtc.setDatetime(drivers::PCF85063::fromEpoch(ntpTime)); // MQTT Test NetworkClient tcp; @@ -101,10 +100,13 @@ void loop() results.clear(); } - delay(500); - LOG_INFO("====>Read Inputs"); - bus.readInputs(relayBoardAddr, 0x00, 8, values); - printBool("====>Inputs", values); + for (auto j(0); j < io.getOutNum(); j++) + { + //io.digitalIOWrite(j, true); + LOG_INFO("Input", j, io.digitalIORead(j) ? "True" : "False"); + delay(500); + } + delay(5000); } diff --git a/src/remoteIO.cpp b/src/remoteIO.cpp new file mode 100644 index 0000000..8da6d71 --- /dev/null +++ b/src/remoteIO.cpp @@ -0,0 +1,93 @@ +#include + +remoteIO::remoteIO(const uint8_t address, drivers::MODBUS &bus) : m_address(address), m_initialized(false), m_bus(bus) +{ + LOG_INFO("Initializing relay module"); + std::vector response; + 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; + resetAll(false); +} + +remoteIO::~remoteIO() +{ + m_initialized = false; + resetAll(false); +} + +const bool remoteIO::setOut(const channel_t ch, const bool value) +{ + if (!m_initialized) + return false; + LOG_DEBUG("Write Channel", ch, "->", value ? "True" : "False"); + return m_bus.writeCoil(m_address, REG_COILS + ch, value); +} + +const bool remoteIO::toggleOut(const channel_t ch) +{ + if (!m_initialized) + return false; + std::vector value; + if (!m_bus.readCoils(m_address, REG_COILS + ch, 1, value)) + return false; + LOG_DEBUG("Toggle Channel", ch, "->", !value.front() ? "True" : "False"); + return m_bus.writeCoil(m_address, REG_COILS + ch, !value.front()); +} + +const bool remoteIO::setOutPort(const std::vector values) +{ + if (!m_initialized) + return false; + LOG_DEBUG("Write Port", CH_MAX); + return m_bus.writeCoils(m_address, CH_MAX, values); +} + +const bool remoteIO::getOut(const channel_t ch, bool &value) +{ + if (!m_initialized) + return false; + std::vector values; + if (!m_bus.readCoils(m_address, REG_COILS + ch, 1, values)) + return false; + value = values.front(); + LOG_DEBUG("Read Channel", ch, "->", value ? "True" : "False"); + return true; +} + +const bool remoteIO::getOutPort(std::vector &values) +{ + if (!m_initialized) + return false; + LOG_DEBUG("Read Port", CH_MAX); + return m_bus.readCoils(m_address, REG_COILS, 8, values); +} + +const bool remoteIO::getIn(const channel_t input, bool &value) +{ + if (!m_initialized) + return false; + std::vector values; + if (!m_bus.readInputs(m_address, REG_INPUT + input, 1, values)) + return false; + value = values.front(); + LOG_DEBUG("Read Input", input, "->", values.front() ? "True" : "False"); + return true; +} + +const bool remoteIO::getInPort(std::vector &values) +{ + if (!m_initialized) + return false; + LOG_DEBUG("Read Inputs", CH_MAX); + return m_bus.readInputs(m_address, REG_INPUT, CH_MAX, values); +} + +void remoteIO::resetAll(const bool value) +{ + LOG_DEBUG("Reset All ->", value ? "True" : "False"); + m_bus.writeCoil(m_address, REG_ALLCOILS, value); +} \ No newline at end of file diff --git a/src/remoteIO.h b/src/remoteIO.h new file mode 100644 index 0000000..1c1cf66 --- /dev/null +++ b/src/remoteIO.h @@ -0,0 +1,37 @@ +#pragma once + +#include +#include + +class remoteIO +{ +public: + typedef enum {CH1, CH2, CH3, CH4, CH5, CH6, CH7, CH8, CH_MAX} channel_t; + +private: + const uint16_t REG_VERSION = 0x8000; + const uint16_t REG_COILS = 0x0000; + const uint16_t REG_INPUT = 0x0000; + const uint16_t REG_ALLCOILS = 0x00FF; + +public: + remoteIO(const uint8_t address, drivers::MODBUS &bus); + ~remoteIO(); + + const bool setOut(const channel_t ch, const bool value); + const bool toggleOut(const channel_t ch); + const bool setOutPort(const std::vector values); + + const bool getOut(const channel_t ch, bool &value); + const bool getOutPort(std::vector &values); + + const bool getIn(const channel_t input, bool &value); + const bool getInPort(std::vector &values); + + void resetAll(const bool value); + +private: + bool m_initialized; + drivers::MODBUS &m_bus; + const uint8_t m_address; +};