DigitalIO driver with dynamic channel count
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -6,17 +6,6 @@
|
||||
#include <CRC16.h>
|
||||
#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
|
||||
{
|
||||
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;
|
||||
|
||||
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 <DebugLogEnable.h>
|
||||
#include <Arduino.h>
|
||||
#include <PubSubClient.h>
|
||||
|
||||
#include <RS485_Driver.h>
|
||||
#include <TCA9554PWR_Driver.h>
|
||||
#include <PCF85063_Driver.h>
|
||||
#include <ETH_Driver.h>
|
||||
|
||||
#include <digitalIO.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
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