diff --git a/library.json b/library.json index 5362956..0beb511 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "M5Unified": "*", "M5GFX": "*" }, - "version": "1.0.0", + "version": "1.1.0", "frameworks": "arduino", "platforms": "espressif32" } \ No newline at end of file diff --git a/library.properties b/library.properties index 8621365..badfdc4 100644 --- a/library.properties +++ b/library.properties @@ -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 diff --git a/src/M5StamPLC.cpp b/src/M5StamPLC.cpp index 6a00042..c76e713 100644 --- a/src/M5StamPLC.cpp +++ b/src/M5StamPLC.cpp @@ -38,6 +38,11 @@ void M5_STAMPLC::begin() } } +void M5_STAMPLC::update() +{ + M5.update(); +} + /* -------------------------------------------------------------------------- */ /* I2C */ /* -------------------------------------------------------------------------- */ @@ -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); @@ -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) @@ -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); } } @@ -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; diff --git a/src/M5StamPLC.h b/src/M5StamPLC.h index 484bf94..d743d9d 100644 --- a/src/M5StamPLC.h +++ b/src/M5StamPLC.h @@ -41,6 +41,7 @@ class M5_STAMPLC { } void begin(); + void update(); LGFX_Device& Display = M5.Display; LGFX_Device& Lcd = M5.Lcd; @@ -48,6 +49,9 @@ class M5_STAMPLC { 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 @@ -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(); diff --git a/src/utils/rx8130/rx8130.cpp b/src/utils/rx8130/rx8130.cpp index 9924875..3985803 100644 --- a/src/utils/rx8130/rx8130.cpp +++ b/src/utils/rx8130/rx8130.cpp @@ -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); +} diff --git a/src/utils/rx8130/rx8130.h b/src/utils/rx8130/rx8130.h index 9f2349f..5a4ec02 100644 --- a/src/utils/rx8130/rx8130.h +++ b/src/utils/rx8130/rx8130.h @@ -1,25 +1,25 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - */ -#pragma once -#include -#include -// https://download.epsondevice.com/td/pdf/app/RX8130CE_en.pdf -// https://github.com/alexreinert/piVCCU/blob/master/kernel/rtc-rx8130.c - -class RX8130_Class : public m5::I2C_Device { -public: - RX8130_Class(std::uint8_t i2c_addr = 0x32, std::uint32_t freq = 400000, m5::I2C_Class *i2c = &m5::In_I2C) - : I2C_Device(i2c_addr, freq, i2c) - { - } - - bool begin(); - - void setTime(struct tm *time); - void getTime(struct tm *time); - void clearIrqFlags(); - void disableIrq(); -}; +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include +#include +// https://download.epsondevice.com/td/pdf/app/RX8130CE_en.pdf +// https://github.com/alexreinert/piVCCU/blob/master/kernel/rtc-rx8130.c + +class RX8130_Class : public m5::I2C_Device { +public: + RX8130_Class(std::uint8_t i2c_addr = 0x32, std::uint32_t freq = 400000, m5::I2C_Class *i2c = &m5::In_I2C) + : I2C_Device(i2c_addr, freq, i2c) + { + } + + bool begin(); + + void setTime(struct tm *time); + void getTime(struct tm *time); + void clearIrqFlags(); + void disableIrq(); +};