Added STM32 platform for debugging and development

This commit is contained in:
Emanuele Trabattoni
2025-06-27 18:55:20 +02:00
parent 01db0e543f
commit 3d2d44c0bb
9 changed files with 171 additions and 94 deletions

View File

@@ -13,7 +13,7 @@ namespace drivers
{
if (m_i2c.write(m_address, reg, {val}))
return true;
log_e("Unable to write register: reg[%d], val[%d] ", reg, val);
LOG_ERROR("Unable to write register: reg[%d], val[%d] ", reg, val);
return false;
}
@@ -25,7 +25,7 @@ namespace drivers
val = data.back();
return true;
}
log_e("Unable to read register: reg[%d]");
LOG_ERROR("Unable to read register: reg[%d]");
return false;
}
@@ -34,9 +34,9 @@ namespace drivers
uint8_t currState(0);
uint8_t newState(0);
if (ch < EXIO_PIN1 || ch > EXIO_PIN8)
if (ch < OUT_PIN1 || ch > OUT_PIN8)
{
log_e("Invalid write to output channel: [%d]", ch);
LOG_ERROR("Invalid write to output channel: [%d]", ch);
return false;
}
if (!readPort(currState))
@@ -52,16 +52,16 @@ namespace drivers
{
if (writeRegister(TCA9554_OUTPUT_REG, state))
return true;
log_e("Unable to write IO port: state[%02x]", state);
LOG_ERROR("Unable to write IO port: state[%02x]", state);
return false;
}
const bool TCA9554PWR::readOut(const uint8_t ch)
{
uint8_t currState(0);
if (ch < EXIO_PIN1 || ch > EXIO_PIN8)
if (ch < OUT_PIN1 || ch > OUT_PIN8)
{
log_e("Invalid read to output channel: [%d]", ch);
LOG_ERROR("Invalid read to output channel: [%d]", ch);
return false;
}
if (!readPort(currState))
@@ -73,7 +73,7 @@ namespace drivers
{
if (readRegister(TCA9554_INPUT_REG, state))
return true;
log_e("Unable to read IO port: state[%02x]", state);
LOG_ERROR("Unable to read IO port: state[%02x]", state);
return false;
}

View File

@@ -15,27 +15,25 @@
#define Low 0x00
#define High 0x01
#define EXIO_PIN1 0
#define EXIO_PIN2 1
#define EXIO_PIN3 2
#define EXIO_PIN4 3
#define EXIO_PIN5 4
#define EXIO_PIN6 5
#define EXIO_PIN7 6
#define EXIO_PIN8 7
namespace drivers
{
class TCA9554PWR
{
I2C &m_i2c;
uint8_t m_address;
private:
const bool writeRegister(const uint8_t reg, const uint8_t val);
const bool readRegister(const uint8_t reg, uint8_t &val);
public:
enum
{
OUT_PIN1,
OUT_PIN2,
OUT_PIN3,
OUT_PIN4,
OUT_PIN5,
OUT_PIN6,
OUT_PIN7,
OUT_PIN8,
OUT_PIN_MAX
};
TCA9554PWR(I2C &i2c, const uint8_t address);
const bool setOut(const uint8_t channel, const bool state);
@@ -43,5 +41,11 @@ namespace drivers
const bool readOut(const uint8_t channel);
const bool readPort(uint8_t &state);
private:
I2C &m_i2c;
uint8_t m_address;
const bool writeRegister(const uint8_t reg, const uint8_t val);
const bool readRegister(const uint8_t reg, uint8_t &val);
};
}

View File

@@ -17,7 +17,7 @@ namespace drivers
const bool I2C::read(const uint8_t deviceAddr, const uint8_t deviceReg, const uint8_t len, std::vector<uint8_t> &data)
{
busy.try_lock();
//busy.try_lock();
Wire.beginTransmission(deviceAddr);
Wire.write(deviceReg);
switch (Wire.endTransmission(true))
@@ -25,19 +25,19 @@ namespace drivers
case 0:
break; // no error, break switch
case 1:
log_e("Data to long to fit in buffer: [%d]", len);
LOG_ERROR("Data to long to fit in buffer: [%d]", len);
case 2:
log_e("Received NAK on address transmit");
LOG_ERROR("Received NAK on address transmit");
case 3:
log_e("Received NAK on data transmit");
LOG_ERROR("Received NAK on data transmit");
case 4:
log_e("Unknown Error");
LOG_ERROR("Unknown Error");
return false;
}
const uint8_t nBytes = Wire.requestFrom(deviceAddr, len);
if (nBytes < len)
{
log_w("Received data is less than expected: len[%d], nBytes[%d]", len, nBytes);
LOG_ERROR("Received data is less than expected: len[%d], nBytes[%d]", len, nBytes);
}
data.clear();
data.resize(nBytes); // resize out buffer to received data len, no check if data len is correct
@@ -45,13 +45,13 @@ namespace drivers
{
data[i] = static_cast<uint8_t>(Wire.read());
}
busy.unlock();
//busy.unlock();
return true;
}
const bool I2C::write(const uint8_t deviceAddr, const uint8_t deviceReg, const std::vector<uint8_t> &data)
{
busy.lock();
//busy.lock();
Wire.beginTransmission(deviceAddr);
Wire.write(deviceReg);
for (auto d : data)
@@ -64,16 +64,16 @@ namespace drivers
case 0:
break; // no error, break switch
case 1:
log_e("Data to long to fit in buffer: [%d]", data.size());
LOG_ERROR("Data to long to fit in buffer: [%d]", data.size());
case 2:
log_e("Received NAK on address transmit");
LOG_ERROR("Received NAK on address transmit");
case 3:
log_e("Received NAK on data transmit");
LOG_ERROR("Received NAK on data transmit");
case 4:
log_e("Unknown Error");
LOG_ERROR("Unknown Error");
return false;
}
busy.unlock();
//busy.unlock();
return true;
}

View File

@@ -1,4 +1,7 @@
#pragma once
#include <DebugLog.h>
#include <Arduino.h>
#include <Wire.h>
#include <vector>
#include <mutex>
@@ -13,7 +16,7 @@ namespace drivers
{
private:
bool isInitialized = false;
std::mutex busy;
//std::mutex busy;
public:
I2C(void);

View File

@@ -1,7 +1,14 @@
#include "RS485_driver.h"
#include <algorithm>
#include <cstring>
#include <machine/endian.h>
#ifdef ESP32
#include <endian.h>
#else
#define be16toh(x) __bswap16(x)
#define htobe16(x) __bswap16(x)
#define htole16(x) x
#endif
uint8_t data[][8] = {
// ESP32-S3-POE-ETH-8DI-8RO Control Command (RS485 receiving data)
@@ -37,14 +44,23 @@ namespace drivers
//////////// RS485 ////////////
////////////////////////////////
#ifdef ESP32
RS485::RS485(const uint32_t baud, const SerialConfig conf)
{
log_i("Init serial port 1");
LOG_INFO("Init serial port 1");
m_serial = std::make_unique<HardwareSerial>(PORT); // RS485 is hardwired to serial port 1
m_serial->begin(baud, conf);
m_serial->setMode(UART_MODE_RS485_HALF_DUPLEX);
}
#else
RS485::RS485(const uint32_t baud)
{
LOG_INFO("Init serial port 1");
m_serial = std::make_unique<HardwareSerial>(PORT); // RS485 is hardwired to serial port 1
m_serial->begin(baud);
}
#endif
const bool RS485::write(const std::vector<uint8_t> data)
{
return data.size() == m_serial->write(data.data(), data.size());
@@ -75,17 +91,22 @@ namespace drivers
////////////////////////////////
//////////// MODBUS ////////////
////////////////////////////////
#ifdef ESP32
MODBUS::MODBUS(const uint32_t baud, const SerialConfig conf) : RS485::RS485(baud, conf)
{
log_i("Init MODBUS Master Mode");
LOG_INFO("Init MODBUS Master Mode");
}
#else
MODBUS::MODBUS(const uint32_t baud) : RS485::RS485(baud)
{
LOG_INFO("Init MODBUS Master Mode");
}
#endif
// Func 0x01
const bool MODBUS::readCoils(const uint8_t device, const uint16_t reg, const uint16_t num, std::vector<bool> &coils)
{
constexpr uint8_t func = 0x01;
log_d("Read coils: dev[%02x], reg[%04x], num[%d]", device, reg, num);
LOG_DEBUG("Read coils: dev[%02x], reg[%04x], num[%d]", device, reg, num);
return readBinary(func, device, reg, num, coils);
}
@@ -93,7 +114,7 @@ namespace drivers
const bool MODBUS::readInputs(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<bool> &inputs)
{
constexpr uint8_t func = 0x01;
log_d("Read multi inputs: dev[%02x], reg[%04x], num[%d]", device, reg, num);
LOG_DEBUG("Read multi inputs: dev[%02x], reg[%04x], num[%d]", device, reg, num);
return readBinary(func, device, reg, num, inputs);
}
@@ -101,7 +122,7 @@ namespace drivers
const bool MODBUS::readHoldingRegisters(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<uint16_t> &values)
{
constexpr uint8_t func = 0x03;
log_d("Read multi holding registers: dev[%02x], reg[%04x], num[%d]", device, reg, num);
LOG_DEBUG("Read multi holding registers: dev[%02x], reg[%04x], num[%d]", device, reg, num);
return readInteger(func, device, reg, num, values);
}
@@ -109,7 +130,7 @@ namespace drivers
const bool MODBUS::readInputRegisters(const uint8_t device, const uint16_t reg, const uint8_t num, std::vector<uint16_t> &values)
{
constexpr uint8_t func = 0x04;
log_d("Read multi input registers: dev[%02x], reg[%04x], num[%d]", device, reg, num);
LOG_DEBUG("Read multi input registers: dev[%02x], reg[%04x], num[%d]", device, reg, num);
return readInteger(func, device, reg, num, values);
}
@@ -117,7 +138,7 @@ namespace drivers
const bool MODBUS::writeCoil(const uint8_t device, const uint16_t coil, const bool value)
{
constexpr uint8_t func = 0x05;
log_d("Write single coil: dev[%02x], reg[%04x], val[...]", device, reg);
LOG_DEBUG("Write single coil: dev[%02x], reg[%04x], val[...]", device, coil);
return writeBinary(device, func, coil, 1, {value});
}
@@ -125,7 +146,7 @@ namespace drivers
const bool MODBUS::writeRegister(const uint8_t device, const uint16_t reg, const uint16_t value)
{
constexpr uint8_t func = 0x06;
log_d("Write single register: dev[%02x], reg[%04x], val[%04x]", device, reg, value);
LOG_DEBUG("Write single register: dev[%02x], reg[%04x], val[%04x]", device, reg, value);
return writeInteger(device, func, reg, 1, {value});
}
@@ -133,7 +154,7 @@ namespace drivers
const bool MODBUS::writeCoils(const uint8_t device, const uint16_t coils, const std::vector<bool> &values)
{
constexpr uint8_t func = 0x0F;
log_d("Write multi coils: dev[%02x], reg[%04x], val[...]", device, reg);
LOG_DEBUG("Write multi coils: dev[%02x], reg[%04x], val[...]", device, coils);
return writeBinary(device, func, coils, values.size(), values);
}
@@ -141,7 +162,7 @@ namespace drivers
const bool MODBUS::writeRegisters(const uint8_t device, const uint16_t reg, const std::vector<uint16_t> &values)
{
constexpr uint8_t func = 0x10;
log_d("Write multi registers: dev[%02x], reg[%04x], val[...]", device, reg);
LOG_DEBUG("Write multi registers: dev[%02x], reg[%04x], val[...]", device, reg);
return writeInteger(device, func, reg, values.size(), values);
}
@@ -153,22 +174,22 @@ namespace drivers
{
if (!write(singleRequest(device, func, reg, bits)))
{
log_e("Failed send readBinary command");
LOG_ERROR("Failed send readBinary command");
return false;
}
const uint16_t nRespDataBytes = 1 + (uint16_t)(bits / 8); // 1 bit for every coil, if not 8 mutiple padded with zeroes
const uint16_t nRespDataBytes = 1 + (uint16_t)(bits / 8); // 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)
std::vector<uint8_t> response;
if (!readN(expectedRespLen, response))
{
log_e("Failed receive readBinary response");
LOG_ERROR("Failed receive readBinary response");
return false;
}
// element 2 of response has the response data bytes expected
if (response.at(2) != nRespDataBytes)
{
log_e("Failed receive, data to short: bytes[%d], expected[%d]", response.at(2), nRespDataBytes);
LOG_ERROR("Failed receive, data to short: bytes[%d], expected[%d]", response.at(2), nRespDataBytes);
return false;
}
@@ -200,7 +221,7 @@ namespace drivers
{
if (!write(singleRequest(device, func, reg, num)))
{
log_e("Failed send readInteger command");
LOG_ERROR("Failed send readInteger command");
return false;
}
const uint16_t nRespDataBytes = num * sizeof(uint16_t);
@@ -208,14 +229,14 @@ namespace drivers
std::vector<uint8_t> response;
if (!readN(expectedRespLen, response))
{
log_e("Failed receive readInteger response");
LOG_ERROR("Failed receive readInteger response");
return false;
}
// element 2 of response has the response data bytes expected
if (response.at(2) != nRespDataBytes)
{
log_e("Failed receive, data to short: bytes[%d], expected[%d]", response.at(2), nRespDataBytes);
LOG_ERROR("Failed receive, data to short: bytes[%d], expected[%d]", response.at(2), nRespDataBytes);
return false;
}
@@ -260,7 +281,7 @@ namespace drivers
if (!write(multiRequest(device, func, reg, bits, bitsOut)))
{
log_e("Failed send writeBinary command");
LOG_ERROR("Failed send writeBinary command");
return false;
}
@@ -268,7 +289,7 @@ namespace drivers
std::vector<uint8_t> response;
if (!readN(expectedRespLen, response))
{
log_e("Failed receive writeBinary response");
LOG_ERROR("Failed receive writeBinary response");
return false;
}
@@ -283,7 +304,7 @@ namespace drivers
{
if (!write(multiRequest(device, func, reg, num, in)))
{
log_e("Failed send writeInteger command");
LOG_ERROR("Failed send writeInteger command");
return false;
}
@@ -291,7 +312,7 @@ namespace drivers
std::vector<uint8_t> response;
if (!readN(expectedRespLen, response))
{
log_e("Failed receive writeInteger response");
LOG_ERROR("Failed receive writeInteger response");
return false;
}
@@ -371,7 +392,7 @@ namespace drivers
// verify crc code
if (highByte(computedCrc) != crcHi || lowByte(computedCrc) != crcLo)
{
log_e("Failed verify CRC code: comp[%04x], rec[%04x]", computedCrc, 0xFFFF & ((crcHi << 8) | crcLo));
LOG_ERROR("Failed verify CRC code: comp[%04x], rec[%04x]", computedCrc, 0xFFFF & ((crcHi << 8) | crcLo));
return false;
}
return true;

View File

@@ -1,7 +1,8 @@
#pragma once
#include <HardwareSerial.h> // Reference the ESP32 built-in serial port library
#include <DebugLog.h>
#include <Arduino.h>
#include <HardwareSerial.h> // Reference the ESP32 built-in serial port library
#include <CRC16.h>
#include <memory>
@@ -23,8 +24,11 @@ namespace drivers
static const uint8_t PORT = 1;
public:
#ifdef ESP32
RS485(const uint32_t baud, const SerialConfig conf);
#else
RS485(const uint32_t baud);
#endif
const bool write(const std::vector<uint8_t> data);
const bool readAll(std::vector<uint8_t> &data);
const bool readN(const uint16_t nBytes, std::vector<uint8_t> &data);
@@ -61,7 +65,11 @@ namespace drivers
typedef uint16_t crc_t;
public:
#ifdef ESP32
MODBUS(const uint32_t baud, const SerialConfig conf);
#else
MODBUS(const uint32_t baud);
#endif
// Func 0x01
const bool readCoils(const uint8_t device, const uint16_t reg, const uint16_t num, std::vector<bool> &coils);

View File

@@ -18,16 +18,16 @@ namespace drivers
success &= m_i2c.write(m_address, RTC_CTRL_2_ADDR, {def_conf2});
}
if (!success)
log_e("RTC Init Failure");
LOG_ERROR("RTC Init Failure");
}
const bool PCF85063::reset(void)
{
log_i("RTC Reset Initiated");
LOG_INFO("RTC Reset Initiated");
const uint8_t cfg = RTC_CTRL_1_DEFAULT | RTC_CTRL_1_CAP_SEL | RTC_CTRL_1_SR;
if (m_i2c.write(m_address, RTC_CTRL_1_ADDR, {cfg}))
return true;
log_e("RTC Reset Failure");
LOG_ERROR("RTC Reset Failure");
return false;
}
@@ -39,7 +39,7 @@ namespace drivers
decToBcd(time.hour)};
if (m_i2c.write(m_address, RTC_SECOND_ADDR, buf))
return true;
log_e("RTC setTime failure");
LOG_ERROR("RTC setTime failure");
return false;
}
@@ -52,7 +52,7 @@ namespace drivers
decToBcd(date.year - YEAR_OFFSET)};
if (m_i2c.write(m_address, RTC_DAY_ADDR, buf))
return true;
log_e("RTC setDate failure");
LOG_ERROR("RTC setDate failure");
return false;
}
@@ -72,7 +72,7 @@ namespace drivers
datetime.year = bcdToDec(buf[3]) + YEAR_OFFSET;
return true;
}
log_e("RTC readDate Failure");
LOG_ERROR("RTC readDate Failure");
return false;
}
@@ -86,7 +86,7 @@ namespace drivers
datetime.hour = bcdToDec(buf[2] & 0x3F);
return true;
}
log_e("RTC readTime Failure");
LOG_ERROR("RTC readTime Failure");
return false;
}
@@ -110,7 +110,7 @@ namespace drivers
if (m_i2c.write(m_address, RTC_CTRL_2_ADDR, currStatus))
return true;
log_e("RTC enableAlarm failure");
LOG_ERROR("RTC enableAlarm failure");
return false;
}
@@ -125,7 +125,7 @@ namespace drivers
};
if (m_i2c.write(m_address, RTC_SECOND_ALARM, buf))
return true;
log_e("RTC setAlarm failure");
LOG_ERROR("RTC setAlarm failure");
return false;
}
@@ -141,7 +141,7 @@ namespace drivers
time.dotw = (uint8_t)bcdToDec(buf[4] & 0x07);
return true;
}
log_e("RTC readAlarm failure");
LOG_ERROR("RTC readAlarm failure");
return false;
}
@@ -153,7 +153,7 @@ namespace drivers
flags = buf.at(0);
return true;
}
log_e("RTC readAlarmFlags failure");
LOG_ERROR("RTC readAlarmFlags failure");
return false;
}

View File

@@ -17,7 +17,15 @@ lib_deps =
arduino-libraries/NTPClient@^3.2.1
knolleary/PubSubClient@^2.8
robtillaart/CRC@^1.0.3
hideakitai/DebugLog@^0.8.4
[env:native]
platform = native
test_framework = doctest
[env:nucleo_f401re]
platform = ststm32
board = nucleo_f401re
framework = arduino
lib_deps =
bblanchon/ArduinoJson@^7.4.2
arduino-libraries/NTPClient@^3.2.1
knolleary/PubSubClient@^2.8
robtillaart/CRC@^1.0.3
hideakitai/DebugLog@^0.8.4

View File

@@ -1,38 +1,71 @@
#define DEBUGLOG_DEFAULT_LOG_LEVEL_DEBUG
#include <DebugLog.h>
#include <Arduino.h>
#include <RS485_Driver.h>
#include <TCA9554PWR_Driver.h>
void setup() {
void setup()
{
bool success = true;
LOG_INFO("Create i2c driver");
auto i2c = drivers::I2C();
LOG_INFO("Create relay driver");
auto relays = drivers::TCA9554PWR(i2c, TCA9554_ADDRESS);
for (auto i(0); i < drivers::TCA9554PWR::OUT_PIN_MAX; i++)
{
LOG_INFO("Toggle relay [%d]=ON", i);
success &= relays.setOut(i, true);
delay(1000);
LOG_INFO("Toggle relay [%d]=OFF", i);
success &= relays.setOut(i, false);
delay(1000);
}
LOG_INFO("Toggle port [0x55]");
success &= relays.setPort(0x55);
delay(2000);
LOG_INFO("Toggle port [0xAA]");
success &= relays.setPort(0xAA);
delay(2000);
LOG_INFO("Create modbus driver");
#ifdef ESP32
auto bus = drivers::MODBUS(9600, SERIAL_8N1);
#else
auto bus = drivers::MODBUS(9600);
#endif
const uint8_t devAddress(0x01);
const uint8_t baseRegister(0x02);
log_i("Write single coil");
LOG_INFO("Write single coil");
success &= bus.writeCoil(devAddress, baseRegister, true);
success &=bus.writeCoil(devAddress, baseRegister, false);
success &= bus.writeCoil(devAddress, baseRegister, false);
log_i("Write multiple coils");
LOG_INFO("Write multiple coils");
const uint16_t coilsNum(32);
std::vector<bool> coilsValues(coilsNum, false);
bool v=true;
for (auto i(0); i < coilsNum; i++) {
bool v = true;
for (auto i(0); i < coilsNum; i++)
{
coilsValues[i] = v;
v=~v;
v = ~v;
}
success &=bus.writeCoils(devAddress, baseRegister, coilsValues);
success &= bus.writeCoils(devAddress, baseRegister, coilsValues);
log_i("Write single register");
success &=bus.writeRegister(devAddress, baseRegister, 0xAA);
log_i("Write multiple registers");
LOG_INFO("Write single register");
success &= bus.writeRegister(devAddress, baseRegister, 0xAA);
LOG_INFO("Write multiple registers");
const uint16_t regNum(16);
std::vector<uint16_t> regValues(regNum, 0);
for (uint16_t i(0); i < regNum; i++) {
regValues[i] = i*2;
for (uint16_t i(0); i < regNum; i++)
{
regValues[i] = i * 2;
}
}
void loop() {
void loop()
{
}