Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion library.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"M5Unified": "*",
"M5GFX": "*"
},
"version": "1.0.0",
"version": "1.1.0",
"frameworks": "arduino",
"platforms": "espressif32"
}
2 changes: 1 addition & 1 deletion library.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name=M5StamPLC
version=1.0.0
version=1.1.0
author=M5Stack
maintainer=M5Stack
sentence=M5StamPLC is a library for M5StamPLC
Expand Down
22 changes: 15 additions & 7 deletions src/M5StamPLC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ void M5_STAMPLC::begin()
}
}

void M5_STAMPLC::update()
{
M5.update();
}

/* -------------------------------------------------------------------------- */
/* I2C */
/* -------------------------------------------------------------------------- */
Expand All @@ -62,7 +67,7 @@ void M5_STAMPLC::io_expander_a_init()
ioe.setDirection(5, true);
ioe.setPullMode(5, false);
ioe.setHighImpedance(5, true);

ioe.setDirection(6, true);
ioe.setPullMode(6, false);
ioe.setHighImpedance(6, true);
Expand All @@ -73,7 +78,7 @@ void M5_STAMPLC::setBacklight(bool on)
auto& ioe = M5.getIOExpander(0);

ioe.setHighImpedance(7, !on);
ioe.digitalWrite(7, !on); // backlight is active low
ioe.digitalWrite(7, !on); // backlight is active low
}

void M5_STAMPLC::setStatusLight(const uint8_t& r, const uint8_t& g, const uint8_t& b)
Expand Down Expand Up @@ -200,12 +205,12 @@ void M5_STAMPLC::ina226_init()
ESP_LOGE(TAG, "ina226 init failed");
} else {
INA226_Class::config_t cfg;
cfg.sampling_rate = INA226_Class::Sampling::Rate16;
cfg.bus_conversion_time = INA226_Class::ConversionTime::US_1100;
cfg.sampling_rate = INA226_Class::Sampling::Rate16;
cfg.bus_conversion_time = INA226_Class::ConversionTime::US_1100;
cfg.shunt_conversion_time = INA226_Class::ConversionTime::US_1100;
cfg.mode = INA226_Class::Mode::ShuntAndBus;
cfg.shunt_res = 0.01f;
cfg.max_expected_current = 2.0f;
cfg.mode = INA226_Class::Mode::ShuntAndBus;
cfg.shunt_res = 0.01f;
cfg.max_expected_current = 2.0f;
INA226.config(cfg);
}
}
Expand Down Expand Up @@ -481,6 +486,9 @@ void M5_STAMPLC::can_init()
case 125000:
t_config = TWAI_TIMING_CONFIG_125KBITS();
break;
case 250000:
t_config = TWAI_TIMING_CONFIG_250KBITS();
break;
case 500000:
t_config = TWAI_TIMING_CONFIG_500KBITS();
break;
Expand Down
8 changes: 6 additions & 2 deletions src/M5StamPLC.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,17 @@ class M5_STAMPLC {
}

void begin();
void update();

LGFX_Device& Display = M5.Display;
LGFX_Device& Lcd = M5.Lcd;

LM75B_Class LM75B;
INA226_Class INA226;
RX8130_Class RX8130;
Button_Class& BtnA = M5.BtnA;
Button_Class& BtnB = M5.BtnB;
Button_Class& BtnC = M5.BtnC;

/**
* @brief Set Status Light
Expand Down Expand Up @@ -146,8 +150,8 @@ class M5_STAMPLC {
*/
void noTone();

private:
AW9523_Class* _io_expander_b = nullptr; // Controls plc relays, plc inputs
protected:
AW9523_Class* _io_expander_b = nullptr; // Controls plc relays, plc inputs
Config_t _config;

void i2c_init();
Expand Down
294 changes: 181 additions & 113 deletions src/utils/rx8130/rx8130.cpp
Original file line number Diff line number Diff line change
@@ -1,113 +1,181 @@
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include "rx8130.h"

// RX-8130 Register definitions
#define RX8130_REG_SEC 0x10
#define RX8130_REG_MIN 0x11
#define RX8130_REG_HOUR 0x12
#define RX8130_REG_WDAY 0x13
#define RX8130_REG_MDAY 0x14
#define RX8130_REG_MONTH 0x15
#define RX8130_REG_YEAR 0x16

#define RX8130_REG_ALMIN 0x17
#define RX8130_REG_ALHOUR 0x18
#define RX8130_REG_ALWDAY 0x19
#define RX8130_REG_TCOUNT0 0x1A
#define RX8130_REG_TCOUNT1 0x1B
#define RX8130_REG_EXT 0x1C
#define RX8130_REG_FLAG 0x1D
#define RX8130_REG_CTRL0 0x1E
#define RX8130_REG_CTRL1 0x1F

#define RX8130_REG_END 0x23

// Extension Register (1Ch) bit positions
#define RX8130_BIT_EXT_TSEL (7 << 0)
#define RX8130_BIT_EXT_WADA (1 << 3)
#define RX8130_BIT_EXT_TE (1 << 4)
#define RX8130_BIT_EXT_USEL (1 << 5)
#define RX8130_BIT_EXT_FSEL (3 << 6)

// Flag Register (1Dh) bit positions
#define RX8130_BIT_FLAG_VLF (1 << 1)
#define RX8130_BIT_FLAG_AF (1 << 3)
#define RX8130_BIT_FLAG_TF (1 << 4)
#define RX8130_BIT_FLAG_UF (1 << 5)

// Control 0 Register (1Еh) bit positions
#define RX8130_BIT_CTRL_TSTP (1 << 2)
#define RX8130_BIT_CTRL_AIE (1 << 3)
#define RX8130_BIT_CTRL_TIE (1 << 4)
#define RX8130_BIT_CTRL_UIE (1 << 5)
#define RX8130_BIT_CTRL_STOP (1 << 6)
#define RX8130_BIT_CTRL_TEST (1 << 7)

static uint8_t bcd2dec(uint8_t val)
{
return (val >> 4) * 10 + (val & 0x0f);
}

static uint8_t dec2bcd(uint8_t val)
{
return ((val / 10) << 4) + (val % 10);
}

bool RX8130_Class::begin()
{
bool result = _i2c->start(_addr, false, _freq) && _i2c->stop();
return result;
}

void RX8130_Class::setTime(struct tm *time)
{
uint8_t rbuf = 0;

time->tm_year -= 100;

// set STOP bit before changing clock/calendar
rbuf = readRegister8(RX8130_REG_CTRL0);
rbuf = rbuf | RX8130_BIT_CTRL_STOP;
writeRegister8(RX8130_REG_CTRL0, rbuf);

uint8_t date[7] = {dec2bcd(time->tm_sec), dec2bcd(time->tm_min), dec2bcd(time->tm_hour),
dec2bcd(time->tm_wday), dec2bcd(time->tm_mday), dec2bcd(time->tm_mon),
dec2bcd(time->tm_year % 100)};

writeRegister(RX8130_REG_SEC, date, 7);

// clear STOP bit after changing clock/calendar
rbuf = readRegister8(RX8130_REG_CTRL0);
rbuf = rbuf & ~RX8130_BIT_CTRL_STOP;
writeRegister8(RX8130_REG_CTRL0, rbuf);
}

void RX8130_Class::getTime(struct tm *time)
{
uint8_t date[7];
readRegister(RX8130_REG_SEC, date, 7);

time->tm_sec = bcd2dec(date[RX8130_REG_SEC - 0x10] & 0x7f);
time->tm_min = bcd2dec(date[RX8130_REG_MIN - 0x10] & 0x7f);
time->tm_hour = bcd2dec(date[RX8130_REG_HOUR - 0x10] & 0x3f); // only 24-hour clock
time->tm_mday = bcd2dec(date[RX8130_REG_MDAY - 0x10] & 0x3f);
time->tm_mon = bcd2dec(date[RX8130_REG_MONTH - 0x10] & 0x1f);
time->tm_year = bcd2dec(date[RX8130_REG_YEAR - 0x10]);
time->tm_wday = bcd2dec(date[RX8130_REG_WDAY - 0x10] & 0x7f);

time->tm_year += 100;
}

void RX8130_Class::clearIrqFlags()
{
writeRegister8(RX8130_REG_FLAG, 0);
}

void RX8130_Class::disableIrq()
{
writeRegister8(RX8130_REG_CTRL0, 0);
}
/*
* SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD
*
* SPDX-License-Identifier: MIT
*/
#include "rx8130.h"

// RX-8130 Register definitions
#define RX8130_REG_SEC 0x10
#define RX8130_REG_MIN 0x11
#define RX8130_REG_HOUR 0x12
#define RX8130_REG_WDAY 0x13
#define RX8130_REG_MDAY 0x14
#define RX8130_REG_MONTH 0x15
#define RX8130_REG_YEAR 0x16

#define RX8130_REG_ALMIN 0x17
#define RX8130_REG_ALHOUR 0x18
#define RX8130_REG_ALWDAY 0x19
#define RX8130_REG_TCOUNT0 0x1A
#define RX8130_REG_TCOUNT1 0x1B
#define RX8130_REG_EXT 0x1C
#define RX8130_REG_FLAG 0x1D
#define RX8130_REG_CTRL0 0x1E
#define RX8130_REG_CTRL1 0x1F

#define RX8130_REG_END 0x23

// Extension Register (1Ch) bit positions
#define RX8130_BIT_EXT_TSEL (7 << 0)
#define RX8130_BIT_EXT_WADA (1 << 3)
#define RX8130_BIT_EXT_TE (1 << 4)
#define RX8130_BIT_EXT_USEL (1 << 5)
#define RX8130_BIT_EXT_FSEL (3 << 6)

// Flag Register (1Dh) bit positions
#define RX8130_BIT_FLAG_VLF (1 << 1)
#define RX8130_BIT_FLAG_AF (1 << 3)
#define RX8130_BIT_FLAG_TF (1 << 4)
#define RX8130_BIT_FLAG_UF (1 << 5)

// Control 0 Register (1Еh) bit positions
#define RX8130_BIT_CTRL_TSTP (1 << 2)
#define RX8130_BIT_CTRL_AIE (1 << 3)
#define RX8130_BIT_CTRL_TIE (1 << 4)
#define RX8130_BIT_CTRL_UIE (1 << 5)
#define RX8130_BIT_CTRL_STOP (1 << 6)
#define RX8130_BIT_CTRL_TEST (1 << 7)

static uint8_t bcd2dec(uint8_t val)
{
return (val >> 4) * 10 + (val & 0x0f);
}

static uint8_t dec2bcd(uint8_t val)
{
return ((val / 10) << 4) + (val % 10);
}

// Helper function to check if a year is a leap year
static bool isLeapYear(int year)
{
return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0);
}

// Helper function to get the number of days in a given month
// Note: month is 1-12 for this function
static int getDaysInMonth(int month, int year)
{
static const int daysInMonth[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

if (month == 2 && isLeapYear(year)) {
return 29;
}
return daysInMonth[month - 1];
}

bool RX8130_Class::begin()
{
bool result = _i2c->start(_addr, false, _freq) && _i2c->stop();
return result;
}

void RX8130_Class::setTime(struct tm *time)
{
uint8_t rbuf = 0;

time->tm_year -= 100;

// set STOP bit before changing clock/calendar
rbuf = readRegister8(RX8130_REG_CTRL0);
rbuf = rbuf | RX8130_BIT_CTRL_STOP;
writeRegister8(RX8130_REG_CTRL0, rbuf);

uint8_t date[7] = {dec2bcd(time->tm_sec), dec2bcd(time->tm_min), dec2bcd(time->tm_hour),
dec2bcd(time->tm_wday), dec2bcd(time->tm_mday), dec2bcd(time->tm_mon + 1),
dec2bcd(time->tm_year % 100)};

writeRegister(RX8130_REG_SEC, date, 7);

// clear STOP bit after changing clock/calendar
rbuf = readRegister8(RX8130_REG_CTRL0);
rbuf = rbuf & ~RX8130_BIT_CTRL_STOP;
writeRegister8(RX8130_REG_CTRL0, rbuf);
}

void RX8130_Class::getTime(struct tm *time)
{
uint8_t date[7];
readRegister(RX8130_REG_SEC, date, 7);

time->tm_sec = bcd2dec(date[RX8130_REG_SEC - 0x10] & 0x7f);
time->tm_min = bcd2dec(date[RX8130_REG_MIN - 0x10] & 0x7f);
time->tm_hour = bcd2dec(date[RX8130_REG_HOUR - 0x10] & 0x3f); // only 24-hour clock
time->tm_mday = bcd2dec(date[RX8130_REG_MDAY - 0x10] & 0x3f);
time->tm_year = bcd2dec(date[RX8130_REG_YEAR - 0x10]);
time->tm_wday = bcd2dec(date[RX8130_REG_WDAY - 0x10] & 0x7f);

// Read month from RTC (1-12) and convert to tm_mon (0-11)
int rtc_month = bcd2dec(date[RX8130_REG_MONTH - 0x10] & 0x1f);
time->tm_mon = rtc_month - 1;
time->tm_year += 100;

// Fix date overflow issues - RX8130 doesn't handle month/day overflow automatically
// Only correct when we detect actual invalid dates
bool dateChanged = false;

// Check for invalid day (day > maximum days in current month)
int maxDaysInMonth = getDaysInMonth(rtc_month, time->tm_year + 1900);
if (time->tm_mday > maxDaysInMonth) {
time->tm_mday = 1;
rtc_month++;
if (rtc_month > 12) {
rtc_month = 1;
time->tm_year++;
}
time->tm_mon = rtc_month - 1;
dateChanged = true;
}

// Check for invalid month (month > 12 or month < 1)
else if (rtc_month > 12) {
rtc_month = 1;
time->tm_year++;
time->tm_mon = rtc_month - 1;
dateChanged = true;
} else if (rtc_month < 1) {
rtc_month = 12;
time->tm_year--;
time->tm_mon = rtc_month - 1;
dateChanged = true;
}

// If we corrected the date, update the RTC to prevent future issues
if (dateChanged) {
// Set STOP bit before changing clock/calendar
uint8_t rbuf = readRegister8(RX8130_REG_CTRL0);
rbuf = rbuf | RX8130_BIT_CTRL_STOP;
writeRegister8(RX8130_REG_CTRL0, rbuf);

// Only update date registers, preserve time
uint8_t dateRegs[4] = {dec2bcd(time->tm_wday), dec2bcd(time->tm_mday), dec2bcd(rtc_month),
dec2bcd((time->tm_year - 100) % 100)};
writeRegister(RX8130_REG_WDAY, dateRegs, 4);

// Clear STOP bit after changing clock/calendar
rbuf = readRegister8(RX8130_REG_CTRL0);
rbuf = rbuf & ~RX8130_BIT_CTRL_STOP;
writeRegister8(RX8130_REG_CTRL0, rbuf);
}
}

void RX8130_Class::clearIrqFlags()
{
writeRegister8(RX8130_REG_FLAG, 0);
}

void RX8130_Class::disableIrq()
{
writeRegister8(RX8130_REG_CTRL0, 0);
}
Loading