DigitalIO driver with dynamic channel count
This commit is contained in:
@@ -39,7 +39,7 @@ namespace drivers
|
|||||||
uint8_t currState(0);
|
uint8_t currState(0);
|
||||||
uint8_t newState(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);
|
LOG_ERROR("Invalid write to output channel: [%d]", ch);
|
||||||
return false;
|
return false;
|
||||||
@@ -64,7 +64,7 @@ namespace drivers
|
|||||||
const bool TCA9554PWR::readOut(const uint8_t ch)
|
const bool TCA9554PWR::readOut(const uint8_t ch)
|
||||||
{
|
{
|
||||||
uint8_t currState(0);
|
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);
|
LOG_ERROR("Invalid read to output channel: [%d]", ch);
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -21,18 +21,18 @@ namespace drivers
|
|||||||
{
|
{
|
||||||
|
|
||||||
public:
|
public:
|
||||||
enum
|
typedef enum
|
||||||
{
|
{
|
||||||
OUT_PIN1,
|
DO1,
|
||||||
OUT_PIN2,
|
DO2,
|
||||||
OUT_PIN3,
|
DO3,
|
||||||
OUT_PIN4,
|
DO4,
|
||||||
OUT_PIN5,
|
DO5,
|
||||||
OUT_PIN6,
|
DO6,
|
||||||
OUT_PIN7,
|
DO7,
|
||||||
OUT_PIN8,
|
DO8,
|
||||||
OUT_PIN_MAX
|
DO_MAX
|
||||||
};
|
} channel_t;
|
||||||
|
|
||||||
TCA9554PWR(I2C &i2c, const uint8_t address);
|
TCA9554PWR(I2C &i2c, const uint8_t address);
|
||||||
~TCA9554PWR();
|
~TCA9554PWR();
|
||||||
|
|||||||
@@ -6,17 +6,6 @@
|
|||||||
#include <CRC16.h>
|
#include <CRC16.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#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
|
namespace drivers
|
||||||
{
|
{
|
||||||
class RS485
|
class RS485
|
||||||
@@ -34,7 +23,7 @@ namespace drivers
|
|||||||
HardwareSerial &m_serial;
|
HardwareSerial &m_serial;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MODBUS : public RS485
|
class MODBUS : private RS485
|
||||||
{
|
{
|
||||||
|
|
||||||
static const uint8_t RESP_HEADER_SIZE = 3;
|
static const uint8_t RESP_HEADER_SIZE = 3;
|
||||||
|
|||||||
124
src/digitalIO.cpp
Normal file
124
src/digitalIO.cpp
Normal file
@@ -0,0 +1,124 @@
|
|||||||
|
#include <digitalIO.h>
|
||||||
|
|
||||||
|
digitalIO::digitalIO(drivers::I2C &i2c, drivers::MODBUS &bus, std::vector<uint8_t> 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;
|
||||||
|
}
|
||||||
52
src/digitalIO.h
Normal file
52
src/digitalIO.h
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DebugLog.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <remoteIO.h>
|
||||||
|
#include <TCA9554PWR_Driver.h>
|
||||||
|
|
||||||
|
#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<uint8_t> 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<uint8_t> m_remoteAddrs;
|
||||||
|
drivers::TCA9554PWR m_localOuts;
|
||||||
|
std::vector<remoteIO> m_remotes;
|
||||||
|
};
|
||||||
28
src/main.cpp
28
src/main.cpp
@@ -1,15 +1,15 @@
|
|||||||
#define DEBUGLOG_DEFAULT_LOG_LEVEL_INFO
|
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
|
||||||
|
|
||||||
#include <DebugLog.h>
|
#include <DebugLog.h>
|
||||||
#include <DebugLogEnable.h>
|
#include <DebugLogEnable.h>
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <PubSubClient.h>
|
#include <PubSubClient.h>
|
||||||
|
|
||||||
#include <RS485_Driver.h>
|
|
||||||
#include <TCA9554PWR_Driver.h>
|
|
||||||
#include <PCF85063_Driver.h>
|
#include <PCF85063_Driver.h>
|
||||||
#include <ETH_Driver.h>
|
#include <ETH_Driver.h>
|
||||||
|
|
||||||
|
#include <digitalIO.h>
|
||||||
|
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
void callback(char *topic, uint8_t *payload, unsigned int length)
|
void callback(char *topic, uint8_t *payload, unsigned int length)
|
||||||
@@ -42,29 +42,28 @@ void loop()
|
|||||||
const uint8_t relayBoardAddr(0x01);
|
const uint8_t relayBoardAddr(0x01);
|
||||||
const uint8_t baseRegister(0x00);
|
const uint8_t baseRegister(0x00);
|
||||||
uint16_t k(0);
|
uint16_t k(0);
|
||||||
|
uint8_t ethRetries(0);
|
||||||
|
|
||||||
//////////////// DEVICES ////////////////
|
//////////////// DEVICES ////////////////
|
||||||
// Declared here to keep devices local to the main loop otherwise the kernel crashes //
|
// Declared here to keep devices local to the main loop otherwise the kernel crashes //
|
||||||
auto i2c = drivers::I2C();
|
auto i2c = drivers::I2C();
|
||||||
auto relays = drivers::TCA9554PWR(i2c, TCA9554_ADDRESS);
|
|
||||||
auto bus = drivers::MODBUS(9600, SERIAL_8N1);
|
auto bus = drivers::MODBUS(9600, SERIAL_8N1);
|
||||||
auto rtc = drivers::PCF85063(i2c, PCF85063_ADDRESS);
|
auto rtc = drivers::PCF85063(i2c, PCF85063_ADDRESS);
|
||||||
auto eth = drivers::Ethernet("waveshare-test");
|
auto eth = drivers::Ethernet("waveshare-test");
|
||||||
|
auto io = digitalIO(i2c, bus, {relayBoardAddr});
|
||||||
|
|
||||||
Network.onEvent([ð](arduino_event_id_t event, arduino_event_info_t info)
|
Network.onEvent([ð](arduino_event_id_t event, arduino_event_info_t info)
|
||||||
{ eth.onEvent(event, 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);
|
delay(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set RTC time at startup
|
// Get RTC time at startup
|
||||||
time_t ntpTime;
|
time_t ntpTime;
|
||||||
drivers::PCF85063::datetime_t dt;
|
drivers::PCF85063::datetime_t dt;
|
||||||
eth.getNtpTime(ntpTime);
|
|
||||||
rtc.setDatetime(drivers::PCF85063::fromEpoch(ntpTime));
|
|
||||||
|
|
||||||
// MQTT Test
|
// MQTT Test
|
||||||
NetworkClient tcp;
|
NetworkClient tcp;
|
||||||
@@ -101,10 +100,13 @@ void loop()
|
|||||||
results.clear();
|
results.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(500);
|
for (auto j(0); j < io.getOutNum(); j++)
|
||||||
LOG_INFO("====>Read Inputs");
|
{
|
||||||
bus.readInputs(relayBoardAddr, 0x00, 8, values);
|
//io.digitalIOWrite(j, true);
|
||||||
printBool("====>Inputs", values);
|
LOG_INFO("Input", j, io.digitalIORead(j) ? "True" : "False");
|
||||||
|
delay(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
delay(5000);
|
delay(5000);
|
||||||
}
|
}
|
||||||
|
|||||||
93
src/remoteIO.cpp
Normal file
93
src/remoteIO.cpp
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
#include <remoteIO.h>
|
||||||
|
|
||||||
|
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<uint16_t> 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<bool> 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<bool> 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<bool> 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<bool> &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<bool> 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<bool> &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);
|
||||||
|
}
|
||||||
37
src/remoteIO.h
Normal file
37
src/remoteIO.h
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <DebugLog.h>
|
||||||
|
#include <RS485_Driver.h>
|
||||||
|
|
||||||
|
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<bool> values);
|
||||||
|
|
||||||
|
const bool getOut(const channel_t ch, bool &value);
|
||||||
|
const bool getOutPort(std::vector<bool> &values);
|
||||||
|
|
||||||
|
const bool getIn(const channel_t input, bool &value);
|
||||||
|
const bool getInPort(std::vector<bool> &values);
|
||||||
|
|
||||||
|
void resetAll(const bool value);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_initialized;
|
||||||
|
drivers::MODBUS &m_bus;
|
||||||
|
const uint8_t m_address;
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user