diff --git a/LICENSE b/LICENSE index 0b0f6fd..3848fd4 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2019 lewis he +Copyright (c) 2023 Chris Dirks Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8a78e8c..ecf7291 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,31 @@ -pcf8563-RTC -===================================== -A library to interace esp chips with the NXP PCF8563 Real time clock (RTC) in the arduino (c++) Framework. +# pcf8563-RTC + +The `pcf8563-RTC` library is designed to interface ESP chips with the NXP PCF8563 Real-Time Clock (RTC) within the Arduino (C++) Framework. ## Features -- use multiple I2C Busses -> rtc.begin(Wire1); -- works with timezones (RTC is set to UTC) -https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv -- set System (ESP32) time from RTC (assumes RTC is set to UTC/GMT) -- set RTC time from System (epoch) -- Set RTC time using wifi example + +- Supports multiple I2C buses: `rtc.begin(Wire1);` +- Timezone support (RTC is set to UTC) + - [List of timezones](https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv) +- Sets the system (ESP32) time from the RTC (assumes RTC is set to UTC/GMT) +- Sets the RTC time from the system (epoch) +- Example of setting RTC time using WiFi ## Warnings -- not all functions are implemented -- not compatible with Lewis he's Library -- only tested with the esp32 -- under active development -### Setup -```c++ +- Not all functions are implemented +- Not compatible with Lewis' library +- Only tested with the ESP32 and S2 variants +- Currently under development + +## Example ESP32 Code + +```cpp #include "pcf8563.h" #include "time.h" + PCF8563_Class rtc; -const char *time_zone = "NZST-12NZDT,M9.5.0,M4.1.0/3"; // https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv +const char *time_zone = "NZST-12NZDT,M9.5.0,M4.1.0/3"; // [List of timezones](https://github.com/nayarsystems/posix_tz_db/blob/master/zones.csv) Wire.begin(); rtc.begin(Wire); @@ -29,27 +33,35 @@ setenv("TZ", time_zone, 1); tzset(); rtc.syncToSystem(); ``` -### Loop -```c++ + +```cpp struct tm timeinfo; getLocalTime(&timeinfo); Serial.print(&timeinfo, "%d/%m/%y %H:%M:%S"); ``` -## 🖼️ Schematic +## Schematic + ![Schematic](/images/schematic.png) -- uses a cr1220 coincell (though almost any 3v lithium coincell should work) -- to not discharge the battery too fast disable output clock and alarms -- Low backup current: typical 0.25 uA at 3.0 V (theoretical not tested) -- currently I'm testing with 4.7kohm pullups on sda and scl -- currently the crystal I'm using is a Seiko Epson Q13FC1350000400 (+-20ppm) -- in theory this setup will drift by a maximum of ~11mins per year (https://www.analog.com/en/design-center/interactive-design-tools/real-time-clock-calculator.html) -- in theory a 37mAh cr1220 battery will last ~14 years without any charging (incl battery self discharge) -- 3.3V -> diode -> resistor charging of a coincell from 3.3v is hotly debated as to wether is good, ok, or horificly bad idea. I have not had any issuses but your mileage may vary -- be careful with pcb placement (have the crystal as close to the RTC chip a possible and souround it with ground planes connected with vias) - -## Based on the awesome work of Lewis he -Origin created by Lewis he in 2019 -https://github.com/lewisxhe/PCF8563_Library - -MIT license + +- Uses a CR1220 coincell (though almost any 3V lithium coincell should work) +- To prevent battery discharge, disable output clock and alarms +- Low backup current: typically 0.25uA at 3.0V (theoretical, not tested) +- Currently testing with 4.7kohm pull-ups on SDA and SCL +- Currently using a Seiko Epson Q13FC1350000400 crystal (+-20ppm) +- In theory, this setup will drift by a maximum of ~11 minutes per year ([Real-Time Clock Calculator](https://www.analog.com/en/design-center/interactive-design-tools/real-time-clock-calculator.html)) +- In theory, a 37mAh CR1220 battery will last ~14 years without any charging (including battery self-discharge) +- Charging a coincell from 3.3V through a diode and resistor is a topic of debate. It is important to exercise caution and consider individual circumstances. Using a 200-ohm resistor should limit the current to a maximum of 12mA, while a 1k-ohm resistor should limit it to a maximum of 2.6mA. +- Be careful with PCB placement; ensure the crystal is placed as close to the RTC chip as possible and surrounded by ground planes connected with vias + +## Acknowledgments + +This library is based on the original work of Lewis He, created in 2019. + +[GitHub Repository](https://github.com/lewisxhe/PCF8563_Library) + +## License + +This library is licensed under the MIT License. See the [LICENSE](LICENSE) file for more information. + +Please note that not all functions are implemented, and the library is under active development. For more information on how to use the library, refer to the documentation and examples provided. \ No newline at end of file diff --git a/keywords.txt b/keywords.txt index 70ed6be..e857917 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,58 +1,51 @@ ####################################### -# Syntax Coloring Map For Arduino library for NXP-PCF8563 By lewis He -# github:https://github.com/lewisxhe +# Syntax Coloring Map ####################################### ####################################### # Datatypes (KEYWORD1) ####################################### -RTC_Date KEYWORD1 -RTC_Alarm KEYWORD1 -PCF8563_Class KEYWORD1 +RTC_Date KEYWORD1 +RTC_Alarm KEYWORD1 +PCF8563_Class KEYWORD1 ####################################### # Methods and Functions (KEYWORD2) ####################################### - -begin KEYWORD2 -setDateTime KEYWORD2 -getDateTime KEYWORD2 -getAlarm KEYWORD2 -enableAlarm KEYWORD2 -disableAlarm KEYWORD2 -alarmActive KEYWORD2 -resetAlarm KEYWORD2 -setAlarm KEYWORD2 -setAlarmByWeekDay KEYWORD2 -setAlarmByHours KEYWORD2 -setAlarmByDays KEYWORD2 -setAlarmByMinutes KEYWORD2 -isTimerEnable KEYWORD2 -isTimerActive KEYWORD2 -enableTimer KEYWORD2 -disableTimer KEYWORD2 -setTimer KEYWORD2 -clearTimer KEYWORD2 -enableCLK KEYWORD2 -disableCLK KEYWORD2 -formatDateTime KEYWORD2 -getDayOfWeek KEYWORD2 - -####################################### -# Instances (KEYWORD2) -####################################### - +begin KEYWORD2 +setDateTime KEYWORD2 +getDateTime KEYWORD2 +getAlarm KEYWORD2 +enableAlarm KEYWORD2 +disableAlarm KEYWORD2 +alarmActive KEYWORD2 +resetAlarm KEYWORD2 +setAlarm KEYWORD2 +setAlarmByWeekDay KEYWORD2 +setAlarmByHours KEYWORD2 +setAlarmByDays KEYWORD2 +setAlarmByMinutes KEYWORD2 +isTimerEnable KEYWORD2 +isTimerActive KEYWORD2 +enableTimer KEYWORD2 +disableTimer KEYWORD2 +setTimer KEYWORD2 +clearTimer KEYWORD2 +enableCLK KEYWORD2 +disableCLK KEYWORD2 +formatDateTime KEYWORD2 +getDayOfWeek KEYWORD2 ####################################### # Constants (LITERAL1) ####################################### -PCF8563_CLK_32_768KHZ LITERAL1 -PCF8563_CLK_1024KHZ LITERAL1 -PCF8563_CLK_32HZ LITERAL1 -PCF8563_CLK_1HZ LITERAL1 -PCF_TIMEFORMAT_HM LITERAL1 -PCF_TIMEFORMAT_HMS LITERAL1 -PCF_TIMEFORMAT_YYYY_MM_DD LITERAL1 -PCF_TIMEFORMAT_MM_DD_YYYY LITERAL1 -PCF_TIMEFORMAT_DD_MM_YYYY LITERAL1 -PCF_TIMEFORMAT_YYYY_MM_DD_H_M_S LITERAL1 \ No newline at end of file +PCF8563_CLK_32_768KHZ LITERAL1 +PCF8563_CLK_1024KHZ LITERAL1 +PCF8563_CLK_32HZ LITERAL1 +PCF8563_CLK_1HZ LITERAL1 +PCF_TIMEFORMAT_HM LITERAL1 +PCF_TIMEFORMAT_HMS LITERAL1 +PCF_TIMEFORMAT_YYYY_MM_DD LITERAL1 +PCF_TIMEFORMAT_MM_DD_YYYY LITERAL1 +PCF_TIMEFORMAT_DD_MM_YYYY LITERAL1 +PCF_TIMEFORMAT_YYYY_MM_DD_H_M_S LITERAL1 \ No newline at end of file diff --git a/library.json b/library.json index e84eb97..c681e85 100644 --- a/library.json +++ b/library.json @@ -1,12 +1,25 @@ { "name": "pcf8563-rtc", - "keywords": "rtc, clock", - "description": "A library for the PCF8563", - "repository": - { + "version": "1.2.0", + "description": "An Arduino library for interfacing ESP chips with PCF8563 Real Time Clock using the I2C protocol.", + "keywords": [ + "rtc", + "clock", + "pcf8563", + "nxt", + "i2c" + ], + "repository": { "type": "git", "url": "https://github.com/CDFER/pcf8563-RTC.git" }, + "author": { + "name": "Chris Dirks", + "url": "https://keastudios.co.nz/about.htm", + "maintainer": true + }, + "license": "Other", + "homepage": "https://github.com/CDFER/pcf8563-RTC", "frameworks": "arduino", - "platforms": "espressif32" -} + "platforms": "*" +} \ No newline at end of file diff --git a/library.properties b/library.properties index bce4858..b698b84 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=pcf8563-RTC -version=1.1.0 +version=1.2.0 author=Chris Dirks (@CDFER) maintainer= Chris Dirks (@CDFER) sentence=Arduino library for NXP PCF8563 RTC chip. diff --git a/pcf8563.cpp b/pcf8563.cpp index b6ad7ae..d552d12 100644 --- a/pcf8563.cpp +++ b/pcf8563.cpp @@ -32,243 +32,254 @@ github:https://github.com/lewisxhe/PCF8563_Library #include #include -int PCF8563_Class::begin(TwoWire &port, uint8_t addr) { - _i2cPort = &port; - _address = addr; - _i2cPort->beginTransmission(_address); - return (_i2cPort->endTransmission() == 0); -} - -void PCF8563_Class::check() { - RTC_Date now = getDateTime(); - RTC_Date compiled = RTC_Date(__DATE__, __TIME__); - - // Serial.printf("%d:%d:%d - %d:%d:%d\n", compiled.year, compiled.month, compiled.day, compiled.hour, compiled.minute, compiled.second); - if (now.year < compiled.year || - (now.year == compiled.year && now.month < compiled.month) || - (now.year == compiled.year && now.month == compiled.month && now.day < compiled.day)) { - setDateTime(compiled); +uint8_t PCF8563_Class::begin(TwoWire &port, int addr) { + _i2cPort = &port; // Set the I2C port to be used for communication + _address = addr; // Set the I2C address of the PCF8563 chip + _i2cPort->beginTransmission(_address); // Begin the I2C transmission to the PCF8563 chip + return _i2cPort->endTransmission(); // Check if the transmission was successful and return the result +} + +const char *PCF8563_Class::getErrorText(uint8_t errorCode) { + switch (errorCode) { + case 0: + return "Success"; + case 1: + return "I2C data too long to fit in transmit buffer"; + case 2: + return "I2C received NACK on transmit of address"; + case 3: + return "I2C received NACK on transmit of data"; + case 4: + return "I2C other error"; + case 5: + return "I2C timeout"; + case 6: + return "bytesReceived(%i) != bytesRequested(%i)"; + case 7: + return "Measurement out of range"; + default: + return "Unknown error"; } } void PCF8563_Class::setDateTime(RTC_Date date) { setDateTime(date.year, date.month, date.day, date.hour, date.minute, date.second); + // Call the setDateTime function with individual date and time components } uint32_t PCF8563_Class::getDayOfWeek(uint32_t day, uint32_t month, uint32_t year) { uint32_t val; - if (month < 3) { - month = 12u + month; - year--; + if (month < 3) { // Check if the month is January or February + month = 12u + month; // Adjust the month value accordingly + year--; // Decrease the year value by 1 } val = (day + (((month + 1u) * 26u) / 10u) + year + (year / 4u) + (6u * (year / 100u)) + (year / 400u)) % 7u; + // Calculate the day of the week using Zeller's Congruence formula if (0u == val) { - val = 7; + val = 7; // Adjust Sunday's value to 7 instead of 0 } - return (val - 1); -} - -void PCF8563_Class::setDateTime(uint16_t year, - uint8_t month, - uint8_t day, - uint8_t hour, - uint8_t minute, - uint8_t second) { - _data[0] = _dec_to_bcd(second) & (~PCF8563_VOL_LOW_MASK); - _data[1] = _dec_to_bcd(minute); - _data[2] = _dec_to_bcd(hour); - _data[3] = _dec_to_bcd(day); - _data[4] = getDayOfWeek(day, month, year); - _data[5] = _dec_to_bcd(month); - _data[6] = _dec_to_bcd(year % 100); + return (val - 1); // Subtract 1 to match the range 0-6 for days of the week +} + +void PCF8563_Class::setDateTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) { + _data[0] = _dec_to_bcd(second) & (~PCF8563_VOL_LOW_MASK); // Convert seconds to BCD and store in data array + _data[1] = _dec_to_bcd(minute); // Convert minutes to BCD and store in data array + _data[2] = _dec_to_bcd(hour); // Convert hours to BCD and store in data array + _data[3] = _dec_to_bcd(day); // Convert day to BCD and store in data array + _data[4] = getDayOfWeek(day, month, year); // Get the day of the week and store in data array + _data[5] = _dec_to_bcd(month); // Convert month to BCD and store in data array + _data[6] = _dec_to_bcd(year % 100); // Convert last two digits of the year to BCD and store in data array if ((2000 % year) == 2000) { - _data[5] &= (~PCF8563_CENTURY_MASK); + _data[5] &= (~PCF8563_CENTURY_MASK); // Clear the century bit if the year is 2000 } else { - _data[5] |= PCF8563_CENTURY_MASK; + _data[5] |= PCF8563_CENTURY_MASK; // Set the century bit if the year is not 2000 } - _writeByte(PCF8563_SEC_REG, 7, _data); + _writeByte(PCF8563_SEC_REG, 7, _data); // Write the date and time values to the PCF8563 chip } bool PCF8563_Class::isValid() { - _readByte(PCF8563_SEC_REG, 1, &_isValid); - return !(_isValid & (1 << 7)); + _readByte(PCF8563_SEC_REG, 1, &_isValid); // Read the seconds register to check validity + return !(_isValid & (1 << 7)); // Check the validity flag and return the result } - RTC_Date PCF8563_Class::getDateTime() { - uint16_t year; - uint8_t century = 0; - _readByte(PCF8563_SEC_REG, 7, _data); - _voltageLow = (_data[0] & PCF8563_VOL_LOW_MASK); - _data[0] = _bcd_to_dec(_data[0] & (~PCF8563_VOL_LOW_MASK)); - _data[1] = _bcd_to_dec(_data[1] & PCF8563_minuteS_MASK); - _data[2] = _bcd_to_dec(_data[2] & PCF8563_HOUR_MASK); - _data[3] = _bcd_to_dec(_data[3] & PCF8563_DAY_MASK); - _data[4] = _bcd_to_dec(_data[4] & PCF8563_WEEKDAY_MASK); - century = _data[5] & PCF8563_CENTURY_MASK; - _data[5] = _bcd_to_dec(_data[5] & PCF8563_MONTH_MASK); - year = _bcd_to_dec(_data[6]); + uint16_t year; // Variable to store the year + uint8_t century = 0; // Variable to store the century flag + _readByte(PCF8563_SEC_REG, 7, _data); // Read the date and time registers + _voltageLow = (_data[0] & PCF8563_VOL_LOW_MASK); // Check if voltage low flag is set + _data[0] = _bcd_to_dec(_data[0] & (~PCF8563_VOL_LOW_MASK)); // Convert seconds from BCD to decimal + _data[1] = _bcd_to_dec(_data[1] & PCF8563_minuteS_MASK); // Convert minutes from BCD to decimal + _data[2] = _bcd_to_dec(_data[2] & PCF8563_HOUR_MASK); // Convert hours from BCD to decimal + _data[3] = _bcd_to_dec(_data[3] & PCF8563_DAY_MASK); // Convert day from BCD to decimal + _data[4] = _bcd_to_dec(_data[4] & PCF8563_WEEKDAY_MASK); // Convert weekday from BCD to decimal + century = _data[5] & PCF8563_CENTURY_MASK; // Check the century flag + _data[5] = _bcd_to_dec(_data[5] & PCF8563_MONTH_MASK); // Convert month from BCD to decimal + year = _bcd_to_dec(_data[6]); // Convert year from BCD to decimal // century : 0 = 1900 , 1 = 2000 - year = century ? 1900 + year : 2000 + year; + year = century ? 1900 + year : 2000 + year; // Determine the actual year based on the century flag return RTC_Date( year, _data[5], _data[3], _data[2], _data[1], - _data[0]); + _data[0]); // Create and return RTC_Date object with the obtained date and time values } RTC_Alarm PCF8563_Class::getAlarm() { - _readByte(PCF8563_ALRM_MIN_REG, 4, _data); - _data[0] = _bcd_to_dec(_data[0] & (~PCF8563_minuteS_MASK)); - _data[1] = _bcd_to_dec(_data[1] & (~PCF8563_HOUR_MASK)); - _data[2] = _bcd_to_dec(_data[2] & (~PCF8563_DAY_MASK)); - _data[3] = _bcd_to_dec(_data[3] & (~PCF8563_WEEKDAY_MASK)); - return RTC_Alarm(_data[0], _data[1], _data[2], _data[3]); + _readByte(PCF8563_ALRM_MIN_REG, 4, _data); // Read the alarm registers + _data[0] = _bcd_to_dec(_data[0] & (~PCF8563_minuteS_MASK)); // Convert minutes from BCD to decimal + _data[1] = _bcd_to_dec(_data[1] & (~PCF8563_HOUR_MASK)); // Convert hours from BCD to decimal + _data[2] = _bcd_to_dec(_data[2] & (~PCF8563_DAY_MASK)); // Convert day from BCD to decimal + _data[3] = _bcd_to_dec(_data[3] & (~PCF8563_WEEKDAY_MASK)); // Convert weekday from BCD to decimal + return RTC_Alarm(_data[0], _data[1], _data[2], _data[3]); // Create and return RTC_Alarm object with the obtained alarm values } void PCF8563_Class::enableAlarm() { - _readByte(PCF8563_STAT2_REG, 1, _data); - _data[0] &= ~PCF8563_ALARM_AF; - _data[0] |= (PCF8563_TIMER_TF | PCF8563_ALARM_AIE); - _writeByte(PCF8563_STAT2_REG, 1, _data); + _readByte(PCF8563_STAT2_REG, 1, _data); // Read the status register 2 + _data[0] &= ~PCF8563_ALARM_AF; // Clear the alarm flag + _data[0] |= (PCF8563_TIMER_TF | PCF8563_ALARM_AIE); // Set the alarm enable and timer flag + _writeByte(PCF8563_STAT2_REG, 1, _data); // Write back the updated status register 2 } void PCF8563_Class::disableAlarm() { - _readByte(PCF8563_STAT2_REG, 1, _data); - _data[0] &= ~(PCF8563_ALARM_AF | PCF8563_ALARM_AIE); - _data[0] |= PCF8563_TIMER_TF; - _writeByte(PCF8563_STAT2_REG, 1, _data); + _readByte(PCF8563_STAT2_REG, 1, _data); // Read the status register 2 + _data[0] &= ~(PCF8563_ALARM_AF | PCF8563_ALARM_AIE); // Clear the alarm flag and alarm enable + _data[0] |= PCF8563_TIMER_TF; // Set the timer flag + _writeByte(PCF8563_STAT2_REG, 1, _data); // Write back the updated status register 2 } void PCF8563_Class::resetAlarm() { - _readByte(PCF8563_STAT2_REG, 1, _data); - _data[0] &= ~(PCF8563_ALARM_AF); - _data[0] |= PCF8563_TIMER_TF; - _writeByte(PCF8563_STAT2_REG, 1, _data); + _readByte(PCF8563_STAT2_REG, 1, _data); // Read the status register 2 + _data[0] &= ~(PCF8563_ALARM_AF); // Clear the alarm flag + _data[0] |= PCF8563_TIMER_TF; // Set the timer flag + _writeByte(PCF8563_STAT2_REG, 1, _data); // Write back the updated status register 2 } - bool PCF8563_Class::alarmActive() { - _readByte(PCF8563_STAT2_REG, 1, _data); - return (bool)(_data[0] & PCF8563_ALARM_AF); + _readByte(PCF8563_STAT2_REG, 1, _data); // Read the status register 2 + return (bool)(_data[0] & PCF8563_ALARM_AF); // Check if the alarm flag is set } void PCF8563_Class::setAlarm(RTC_Alarm alarm) { - setAlarm(alarm.minute, alarm.hour, alarm.day, alarm.weekday); + setAlarm(alarm.minute, alarm.hour, alarm.day, alarm.weekday); // Call the setAlarm function with individual alarm parameters } void PCF8563_Class::setAlarm(uint8_t hour, uint8_t minute, uint8_t day, uint8_t weekday) { if (minute != PCF8563_NO_ALARM) { - _data[0] = _dec_to_bcd(constrain(minute, 0, 59)); - _data[0] &= ~PCF8563_ALARM_ENABLE; + _data[0] = _dec_to_bcd(constrain(minute, 0, 59)); // Convert minutes to BCD and store in data[0] + _data[0] &= ~PCF8563_ALARM_ENABLE; // Clear the alarm enable bit } else { - _data[0] = PCF8563_ALARM_ENABLE; + _data[0] = PCF8563_ALARM_ENABLE; // Set the alarm enable bit } if (hour != PCF8563_NO_ALARM) { - _data[1] = _dec_to_bcd(constrain(hour, 0, 23)); - _data[1] &= ~PCF8563_ALARM_ENABLE; + _data[1] = _dec_to_bcd(constrain(hour, 0, 23)); // Convert hours to BCD and store in data[1] + _data[1] &= ~PCF8563_ALARM_ENABLE; // Clear the alarm enable bit } else { - _data[1] = PCF8563_ALARM_ENABLE; + _data[1] = PCF8563_ALARM_ENABLE; // Set the alarm enable bit } + if (day != PCF8563_NO_ALARM) { - _data[2] = _dec_to_bcd(constrain(day, 1, 31)); - _data[2] &= ~PCF8563_ALARM_ENABLE; + _data[2] = _dec_to_bcd(constrain(day, 1, 31)); // Convert day to BCD and store in data[2] + _data[2] &= ~PCF8563_ALARM_ENABLE; // Clear the alarm enable bit } else { - _data[2] = PCF8563_ALARM_ENABLE; + _data[2] = PCF8563_ALARM_ENABLE; // Set the alarm enable bit } + if (weekday != PCF8563_NO_ALARM) { - _data[3] = _dec_to_bcd(constrain(weekday, 0, 6)); - _data[3] &= ~PCF8563_ALARM_ENABLE; + _data[3] = _dec_to_bcd(constrain(weekday, 0, 6)); // Convert weekday to BCD and store in data[3] + _data[3] &= ~PCF8563_ALARM_ENABLE; // Clear the alarm enable bit } else { - _data[3] = PCF8563_ALARM_ENABLE; + _data[3] = PCF8563_ALARM_ENABLE; // Set the alarm enable bit } - _writeByte(PCF8563_ALRM_MIN_REG, 4, _data); + + _writeByte(PCF8563_ALRM_MIN_REG, 4, _data); // Write the alarm data to the alarm registers } void PCF8563_Class::setAlarmByMinutes(uint8_t minute) { - setAlarm(PCF8563_NO_ALARM, minute, PCF8563_NO_ALARM, PCF8563_NO_ALARM); + setAlarm(PCF8563_NO_ALARM, minute, PCF8563_NO_ALARM, PCF8563_NO_ALARM); // Set the alarm only for minutes } + void PCF8563_Class::setAlarmByDays(uint8_t day) { - setAlarm(PCF8563_NO_ALARM, PCF8563_NO_ALARM, day, PCF8563_NO_ALARM); + setAlarm(PCF8563_NO_ALARM, PCF8563_NO_ALARM, day, PCF8563_NO_ALARM); // Set the alarm only for days } + void PCF8563_Class::setAlarmByHours(uint8_t hour) { - setAlarm(hour, PCF8563_NO_ALARM, PCF8563_NO_ALARM, PCF8563_NO_ALARM); + setAlarm(hour, PCF8563_NO_ALARM, PCF8563_NO_ALARM, PCF8563_NO_ALARM); // Set the alarm only for hours } + void PCF8563_Class::setAlarmByWeekDay(uint8_t weekday) { - setAlarm(PCF8563_NO_ALARM, PCF8563_NO_ALARM, PCF8563_NO_ALARM, weekday); + setAlarm(PCF8563_NO_ALARM, PCF8563_NO_ALARM, PCF8563_NO_ALARM, weekday); // Set the alarm only for weekdays } bool PCF8563_Class::isTimerEnable() { - _readByte(PCF8563_STAT2_REG, 1, &_data[0]); - _readByte(PCF8563_TIMER1_REG, 1, &_data[1]); - if (_data[0] & PCF8563_TIMER_TIE) { - return _data[1] & PCF8563_TIMER_TE ? true : false; + _readByte(PCF8563_STAT2_REG, 1, &_data[0]); // Read the status register 2 + _readByte(PCF8563_TIMER1_REG, 1, &_data[1]); // Read the timer register + if (_data[0] & PCF8563_TIMER_TIE) { // Check if timer interrupt is enabled + return _data[1] & PCF8563_TIMER_TE ? true : false; // Check if timer is enabled } - return false; + return false; // Timer is not enabled } - bool PCF8563_Class::isTimerActive() { - _readByte(PCF8563_STAT2_REG, 1, _data); - return (bool)_data[0] & PCF8563_TIMER_TF; + _readByte(PCF8563_STAT2_REG, 1, _data); // Read the status register 2 + return (bool)_data[0] & PCF8563_TIMER_TF; // Check if the timer flag is set } void PCF8563_Class::enableTimer() { - _readByte(PCF8563_STAT2_REG, 1, &_data[0]); - _readByte(PCF8563_TIMER1_REG, 1, &_data[1]); - _data[0] &= ~PCF8563_TIMER_TF; - _data[0] |= (PCF8563_ALARM_AF | PCF8563_TIMER_TIE); - _data[1] |= PCF8563_TIMER_TE; - _writeByte(PCF8563_STAT2_REG, 1, &_data[0]); - _writeByte(PCF8563_TIMER1_REG, 1, &_data[1]); + _readByte(PCF8563_STAT2_REG, 1, &_data[0]); // Read the status register 2 + _readByte(PCF8563_TIMER1_REG, 1, &_data[1]); // Read the timer register + _data[0] &= ~PCF8563_TIMER_TF; // Clear the timer flag + _data[0] |= (PCF8563_ALARM_AF | PCF8563_TIMER_TIE); // Enable alarm interrupt and timer interrupt + _data[1] |= PCF8563_TIMER_TE; // Enable the timer + _writeByte(PCF8563_STAT2_REG, 1, &_data[0]); // Write the updated status register 2 + _writeByte(PCF8563_TIMER1_REG, 1, &_data[1]); // Write the updated timer register } void PCF8563_Class::disableTimer() { - _readByte(PCF8563_STAT2_REG, 1, _data); - _data[0] &= ~PCF8563_TIMER_TF; - _data[1] |= PCF8563_ALARM_AF; - _writeByte(PCF8563_STAT2_REG, 1, _data); + _readByte(PCF8563_STAT2_REG, 1, _data); // Read the status register 2 + _data[0] &= ~PCF8563_TIMER_TF; // Clear the timer flag + _data[1] |= PCF8563_ALARM_AF; // Set the alarm flag + _writeByte(PCF8563_STAT2_REG, 1, _data); // Write the updated status register 2 } void PCF8563_Class::setTimer(uint8_t val, uint8_t freq, bool enInterrupt) { - _readByte(PCF8563_STAT2_REG, 1, &_data[0]); - _readByte(PCF8563_TIMER1_REG, 1, &_data[1]); + _readByte(PCF8563_STAT2_REG, 1, &_data[0]); // Read the status register 2 + _readByte(PCF8563_TIMER1_REG, 1, &_data[1]); // Read the timer register if (enInterrupt) { - _data[0] |= 1 << 4; + _data[0] |= 1 << 4; // Set the interrupt enable bit } else { - _data[0] &= ~(1 << 4); + _data[0] &= ~(1 << 4); // Clear the interrupt enable bit } - _data[1] |= (freq & PCF8563_TIMER_TD10); - _data[2] = val; - _writeByte(PCF8563_STAT2_REG, 1, &_data[0]); - _writeByte(PCF8563_TIMER1_REG, 1, &_data[1]); - _writeByte(PCF8563_TIMER2_REG, 1, &_data[2]); + _data[1] |= (freq & PCF8563_TIMER_TD10); // Set the timer frequency + _data[2] = val; // Set the timer value + _writeByte(PCF8563_STAT2_REG, 1, &_data[0]); // Write the updated status register 2 + _writeByte(PCF8563_TIMER1_REG, 1, &_data[1]); // Write the updated timer register + _writeByte(PCF8563_TIMER2_REG, 1, &_data[2]); // Write the timer value } void PCF8563_Class::clearTimer() { - _readByte(PCF8563_STAT2_REG, 1, _data); - _data[0] &= ~(PCF8563_TIMER_TF | PCF8563_TIMER_TIE); - _data[0] |= PCF8563_ALARM_AF; - _data[1] = 0x00; - _writeByte(PCF8563_STAT2_REG, 1, &_data[0]); - _writeByte(PCF8563_TIMER1_REG, 1, &_data[1]); + _readByte(PCF8563_STAT2_REG, 1, _data); // Read the status register 2 + _data[0] &= ~(PCF8563_TIMER_TF | PCF8563_TIMER_TIE); // Clear the timer flag and timer interrupt enable bits + _data[0] |= PCF8563_ALARM_AF; // Set the alarm flag + _data[1] = 0x00; // Clear the timer value + _writeByte(PCF8563_STAT2_REG, 1, &_data[0]); // Write the updated status register 2 + _writeByte(PCF8563_TIMER1_REG, 1, &_data[1]); // Write the updated timer register } bool PCF8563_Class::enableCLK(uint8_t freq) { - if (freq >= PCF8563_CLK_MAX) return false; - _data[0] = freq | PCF8563_CLK_ENABLE; - _writeByte(PCF8563_SQW_REG, 1, _data); - return true; + if (freq >= PCF8563_CLK_MAX) return false; // Check if the frequency is valid + _data[0] = freq | PCF8563_CLK_ENABLE; // Set the frequency and enable the clock + _writeByte(PCF8563_SQW_REG, 1, _data); // Write the updated square wave register + return true; // Clock enabled successfully } void PCF8563_Class::disableCLK() { - _data[0] = 0x00; - _writeByte(PCF8563_SQW_REG, 1, _data); + _data[0] = 0x00; // Disable the clock + _writeByte(PCF8563_SQW_REG, 1, _data); // Write the updated square wave register } const char *PCF8563_Class::formatDateTime(uint8_t style) { - RTC_Date t = getDateTime(); + RTC_Date t = getDateTime(); // Get the current date and time switch (style) { case PCF_TIMEFORMAT_HM: snprintf(format, sizeof(format), "%d:%d", t.hour, t.minute); @@ -296,41 +307,50 @@ const char *PCF8563_Class::formatDateTime(uint8_t style) { } #ifdef ESP32 -void PCF8563_Class::syncToSystem() { - struct tm t_tm; - struct timeval val; - RTC_Date dt = getDateTime(); - t_tm.tm_hour = dt.hour; - t_tm.tm_min = dt.minute; - t_tm.tm_sec = dt.second; - t_tm.tm_year = dt.year - 1900; // Year, whose value starts from 1900 - t_tm.tm_mon = dt.month - 1; // Month (starting from January, 0 for January) - Value range is [0,11] - t_tm.tm_mday = dt.day; - val.tv_sec = mktime(&t_tm) - _timezone; // make epoch from UTC/GMT even when system timezone already set https://stackoverflow.com/questions/530519/stdmktime-and-timezone-info - val.tv_usec = 0; - - settimeofday(&val, NULL); // set system epoch +bool PCF8563_Class::syncToSystem() { + if (PCF8563_Class::isValid()) { + struct tm t_tm; + struct timeval val; + RTC_Date dt = getDateTime(); + t_tm.tm_hour = dt.hour; + t_tm.tm_min = dt.minute; + t_tm.tm_sec = dt.second; + t_tm.tm_year = dt.year - 1900; // Year, whose value starts from 1900 + t_tm.tm_mon = dt.month - 1; // Month (starting from January, 0 for January) - Value range is [0,11] + t_tm.tm_mday = dt.day; + + val.tv_sec = mktime(&t_tm) - _timezone; // make epoch from UTC/GMT even when system timezone already set + val.tv_usec = 0; + + settimeofday(&val, NULL); // set system epoch + return true; + } else { + ESP_LOGE("RTC Time is not Valid", "System Epoch Not Set"); + return false; + } } -void PCF8563_Class::syncToRtc() { +bool PCF8563_Class::syncToRtc() { time_t epoch; struct tm gmt; time(&epoch); - gmtime_r(&epoch, &gmt); - setDateTime(gmt.tm_year + 1900, gmt.tm_mon + 1, gmt.tm_mday, gmt.tm_hour, gmt.tm_min, gmt.tm_sec); + if (epoch > 0 && epoch < 4102444800) { // sanity check if epoch is between 1970 and 2100 + gmtime_r(&epoch, &gmt); + setDateTime(gmt.tm_year + 1900, gmt.tm_mon + 1, gmt.tm_mday, gmt.tm_hour, gmt.tm_min, gmt.tm_sec); + return true; + } else { + ESP_LOGE("ESP32 Time is not Valid", "RTC Time Not Set"); + return false; + } } + #endif RTC_Date::RTC_Date() : year(0), month(0), day(0), hour(0), minute(0), second(0) { } -RTC_Date::RTC_Date(uint16_t y, - uint8_t m, - uint8_t d, - uint8_t h, - uint8_t mm, - uint8_t s) : year(y), month(m), day(d), hour(h), minute(mm), second(s) { +RTC_Date::RTC_Date(uint16_t y, uint8_t m, uint8_t d, uint8_t h, uint8_t mm, uint8_t s) : year(y), month(m), day(d), hour(h), minute(mm), second(s) { } uint8_t RTC_Date::StringToUint8(const char *pString) { @@ -350,52 +370,7 @@ uint8_t RTC_Date::StringToUint8(const char *pString) { return value; } -RTC_Date::RTC_Date(const char *date, const char *time) { - // sample input: date = "Dec 26 2009", time = "12:34:56" - year = 2000 + StringToUint8(date + 9); - // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec - switch (date[0]) { - case 'J': - if (date[1] == 'a') - month = 1; - else if (date[2] == 'n') - month = 6; - else - month = 7; - break; - case 'F': - month = 2; - break; - case 'A': - month = date[1] == 'p' ? 4 : 8; - break; - case 'M': - month = date[2] == 'r' ? 3 : 5; - break; - case 'S': - month = 9; - break; - case 'O': - month = 10; - break; - case 'N': - month = 11; - break; - case 'D': - month = 12; - break; - } - day = StringToUint8(date + 4); - hour = StringToUint8(time); - minute = StringToUint8(time + 3); - second = StringToUint8(time + 6); -} - -RTC_Alarm::RTC_Alarm( - uint8_t m, - uint8_t h, - uint8_t d, - uint8_t w) : minute(m), hour(h), day(d), weekday(w) { +RTC_Alarm::RTC_Alarm(uint8_t m, uint8_t h, uint8_t d, uint8_t w) : minute(m), hour(h), day(d), weekday(w) { } uint8_t PCF8563_Class::status2() { @@ -405,4 +380,4 @@ uint8_t PCF8563_Class::status2() { bool RTC_Date::operator==(RTC_Date d) { return ((d.year == year) && (d.month == month) && (d.day == day) && (d.hour == hour) && (d.minute == minute)); -} +} \ No newline at end of file diff --git a/pcf8563.h b/pcf8563.h index 62b9d94..6f3afe3 100644 --- a/pcf8563.h +++ b/pcf8563.h @@ -32,7 +32,7 @@ github:https://github.com/lewisxhe/PCF8563_Library #include #include -#define PCF8563_SLAVE_ADDRESS (0x51) // 7-bit I2C Address +#define PCF8563_ADDRESS (0x51) // 7-bit I2C Address //! REG MAP #define PCF8563_STAT1_REG (0x00) @@ -86,161 +86,371 @@ enum { PCF_TIMEFORMAT_YYYY_MM_DD_H_M_S, }; +/** + * @class RTC_Date + * @brief Represents a date and time + */ class RTC_Date { public: + /** + * @brief Default constructor. + */ RTC_Date(); + + /** + * @brief Constructor that takes date and time as strings. + * @param date The date string in the format "YYYY-MM-DD". + * @param time The time string in the format "HH:MM:SS". + */ RTC_Date(const char *date, const char *time); - RTC_Date(uint16_t year, - uint8_t month, - uint8_t day, - uint8_t hour, - uint8_t minute, - uint8_t second); - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t minute; - uint8_t second; + + /** + * @brief Constructor that takes individual date and time components. + * @param year The year. + * @param month The month. + * @param day The day. + * @param hour The hour. + * @param minute The minute. + * @param second The second. + */ + RTC_Date(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); + + uint16_t year; /**< The year. */ + uint8_t month; /**< The month. */ + uint8_t day; /**< The day. */ + uint8_t hour; /**< The hour. */ + uint8_t minute; /**< The minute. */ + uint8_t second; /**< The second. */ + + /** + * @brief Equality comparison operator. + * @param d The RTC_Date object to compare with. + * @return true if the objects are equal, false otherwise. + */ bool operator==(RTC_Date d); private: + /** + * @brief Converts a string to an unsigned 8-bit integer. + * @param pString The string to convert. + * @return The converted unsigned 8-bit integer. + */ uint8_t StringToUint8(const char *pString); }; +/** + * @class RTC_Alarm + * @brief Represents an alarm configuration + */ class RTC_Alarm { public: + /** + * @brief Default constructor. + */ RTC_Alarm(void); - RTC_Alarm( - uint8_t minute, - uint8_t hour, - uint8_t day, - uint8_t weekday); - uint8_t minute; - uint8_t hour; - uint8_t day; - uint8_t weekday; + + /** + * @brief Constructor that takes individual alarm components. + * @param minute The minute of the alarm. + * @param hour The hour of the alarm. + * @param day The day of the alarm. + * @param weekday The weekday of the alarm. + */ + RTC_Alarm(uint8_t minute, uint8_t hour, uint8_t day, uint8_t weekday); + + uint8_t minute; /**< The minute of the alarm. */ + uint8_t hour; /**< The hour of the alarm. */ + uint8_t day; /**< The day of the alarm. */ + uint8_t weekday; /**< The weekday of the alarm. */ }; +/** + * @class PCF8563_Class + * @brief Represents the PCF8563 RTC module + */ class PCF8563_Class { public: - int begin(TwoWire &port = Wire, uint8_t addr = PCF8563_SLAVE_ADDRESS); + /** + * @brief Initializes the PCF8563 RTC module. + * @param port The I2C port to use. + * @param addr The I2C address of the PCF8563 module. + * @return 0 if successful, error code otherwise. + */ + uint8_t begin(TwoWire &port = Wire, int addr = PCF8563_ADDRESS); /** - * @brief check rtc date is ahead of the compile time + * @brief Converts an error code into descriptive text. * - * @note if __DATE__ is in the future compared to the time - * from the RTC, set the RTC to __DATE__, __TIME__ + * This function takes an error code and returns a corresponding descriptive text. It can be used to convert + * error codes returned by the RTC into human-readable error messages. * - * @see __DATE__, __TIME__ are compile date time variables generated by platformio + * @param errorCode The error code to convert. + * @return A pointer to a constant character array containing the descriptive text of the error. + * If the error code is not recognized, "Unknown error" is returned. * - * @return true if time is valid, false if compile data time used + * @note The error codes and their meanings are as follows: + * - 0: Success + * - 1: I2C data too long to fit in transmit buffer + * - 2: I2C received NACK on transmit of address + * - 3: I2C received NACK on transmit of data + * - 4: I2C other error + * - 5: I2C timeout + * - 6: bytesReceived(%i) != bytesRequested(%i) + * - 7: Measurement out of range */ - void check(); + const char *getErrorText(uint8_t errorCode); - void setDateTime(uint16_t year, - uint8_t month, - uint8_t day, - uint8_t hour, - uint8_t minute, - uint8_t second); + /** + * @brief Sets the date and time of the RTC. + * @param year The year. + * @param month The month. + * @param day The day. + * @param hour The hour. + * @param minute The minute. + * @param second The second. + */ + void setDateTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second); + /** + * @brief Sets the date and time of the RTC using an RTC_Date object. + * @param date The RTC_Date object containing the date and time. + */ void setDateTime(RTC_Date date); + + /** + * @brief Gets the current date and time from the RTC. + * @return The RTC_Date object representing the current date and time. + */ RTC_Date getDateTime(); + + /** + * @brief Gets the current alarm configuration from the RTC. + * @return The RTC_Alarm object representing the current alarm configuration. + */ RTC_Alarm getAlarm(); + + /** + * @brief Enables the alarm. + */ void enableAlarm(); + + /** + * @brief Disables the alarm. + */ void disableAlarm(); + + /** + * @brief Checks if the alarm is active. + * @return true if the alarm is active, false otherwise. + */ bool alarmActive(); + + /** + * @brief Resets the alarm. + */ void resetAlarm(); + + /** + * @brief Sets the alarm configuration. + * @param alarm The RTC_Alarm object representing the alarm configuration. + */ void setAlarm(RTC_Alarm alarm); + + /** + * @brief Sets the alarm configuration using individual alarm components. + * @param hour The hour of the alarm. + * @param minute The minute of the alarm. + * @param day The day of the alarm. + * @param weekday The weekday of the alarm. + */ void setAlarm(uint8_t hour, uint8_t minute, uint8_t day, uint8_t weekday); /** - * @brief can clock data be trusted? + * @brief Checks if the clock data can be trusted. * - * @note The PCF8563 has an on-chip voltage-low detector. - * When Vdd drops below ~0.9V, the valid flag (bit) in the - * VL_seconds register is set to indicate that the integrity of the - * clock information is no * longer guaranteed. The isValid() - * flag can be cleared by using the setDateTime() or syncToRTC() - * functions. + * The PCF8563 has an on-chip voltage-low detector. When Vdd drops below ~0.9V, the valid flag (bit) in the + * VL_seconds register is set to indicate that the integrity of the clock information is no longer guaranteed. + * The isValid() flag can be cleared by using the setDateTime() or syncToRTC() functions. * - * @return true if time is valid, false if integrity not guaranteed + * @return true if the time is valid, false if the integrity is not guaranteed. */ bool isValid(); + /** + * @brief Sets the alarm to trigger on a specific weekday. + * @param weekday The weekday on which the alarm should trigger. + */ void setAlarmByWeekDay(uint8_t weekday); + + /** + * @brief Sets the alarm to trigger at a specific hour. + * @param hour The hour at which the alarm should trigger. + */ void setAlarmByHours(uint8_t hour); + + /** + * @brief Sets the alarm to trigger on a specific day of the month. + * @param day The day of the month on which the alarm should trigger. + */ void setAlarmByDays(uint8_t day); + + /** + * @brief Sets the alarm to trigger at a specific minute. + * @param minute The minute at which the alarm should trigger. + */ void setAlarmByMinutes(uint8_t minute); + /** + * @brief Checks if the timer is enabled. + * @return true if the timer is enabled, false otherwise. + */ bool isTimerEnable(); + + /** + * @brief Checks if the timer is active. + * @return true if the timer is active, false otherwise. + */ bool isTimerActive(); + + /** + * @brief Enables the timer. + */ void enableTimer(); + + /** + * @brief Disables the timer. + */ void disableTimer(); + + /** + * @brief Sets the timer configuration. + * @param val The timer value. + * @param freq The timer frequency. + * @param enInterrupt Whether to enable the timer interrupt. + */ void setTimer(uint8_t val, uint8_t freq, bool enInterrupt); + + /** + * @brief Clears the timer. + */ void clearTimer(); + /** + * @brief Enables the CLK output with the specified frequency. + * @param freq The CLK output frequency. + * @return true if CLK output was enabled successfully, false otherwise. + */ bool enableCLK(uint8_t freq); + + /** + * @brief Disables the CLK output, reducing power consumption from 550nA to 250nA. + */ void disableCLK(); + #ifdef ESP32 /** - * @brief set System (ESP32) time from RTC (assumes RTC is set to UTC/GMT) + * @brief Sets the System (ESP32) time from the RTC if the RTC data is valid. + * + * @note This function creates a time struct from rtc.getDateTime() and then makes the epoch from this, + * assuming that the RTC outputs UTC/GMT. This function works with the system timezone already set. * - * @note creates a time struct from rtc.getDateTime(); then makes the - * epoch from this assuming that the rtc outputs UTC/GMT (this - * function works with system timezone already set) + * @return true if the time is valid, false if the integrity is not guaranteed. */ - void syncToSystem(); + bool syncToSystem(); /** - * @brief set RTC time from System (ESP) Time + * @brief Sets the RTC time from the System (ESP32) time if the ESP32 time is between 1970 and 2100. * - * @note uses system epoch (seconds since 1970) to create UTC Time struct - * (year, month, day, hours, etc) and then sets the RTC to this + * @note This function uses the system epoch (seconds since 1970) to create a UTC Time struct + * (year, month, day, hours, etc.) and then sets the RTC to this. + * + * @return true if the time was set successfully, false otherwise. */ - void syncToRtc(); + bool syncToRtc(); #endif + /** + * @brief Formats the date and time as a string. + * @param style The style of the formatted string (default: PCF_TIMEFORMAT_HMS). + * @return The formatted date and time string. + */ const char *formatDateTime(uint8_t style = PCF_TIMEFORMAT_HMS); + + /** + * @brief Gets the day of the week for a given date. + * @param day The day. + * @param month The month. + * @param year The year. + * @return The day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday). + */ uint32_t getDayOfWeek(uint32_t day, uint32_t month, uint32_t year); + + /** + * @brief Gets the status2 value of the PCF8563. + * @return The status2 value. + */ uint8_t status2(); private: + /** + * @brief Converts a binary-coded decimal (BCD) value to decimal. + * @param val The BCD value to convert. + * @return The decimal value. + */ uint8_t _bcd_to_dec(uint8_t val) { return ((val / 16 * 10) + (val % 16)); } + + /** + * @brief Converts a decimal value to binary-coded decimal (BCD). + * @param val The decimal value to convert. + * @return The BCD value. + */ uint8_t _dec_to_bcd(uint8_t val) { return ((val / 10 * 16) + (val % 10)); } - int _readByte(uint8_t reg, int nbytes, uint8_t *data) { + + /** + * @brief Reads bytes from the specified register. + * @param reg The register address to read from. + * @param nbytes The number of bytes to read. + * @param data Pointer to the buffer to store the read data. + * @return 0 if successful, non-zero if there was an error. + */ + uint8_t _readByte(uint8_t reg, int nbytes, uint8_t *data) { _i2cPort->beginTransmission(_address); _i2cPort->write(reg); // Adapt to HYM8563, no stop bit is sent after reading the sending register address _i2cPort->endTransmission(false); - _i2cPort->requestFrom(_address, nbytes, 1); // HYM8563 send stopbit + _i2cPort->requestFrom(_address, nbytes, 1); // HYM8563 send stop bit uint8_t index = 0; while (_i2cPort->available()) data[index++] = _i2cPort->read(); return 0; } - int _writeByte(uint8_t reg, uint8_t nbytes, uint8_t *data) { + + /** + * @brief Writes bytes to the specified register. + * @param reg The register address to write to. + * @param nbytes The number of bytes to write. + * @param data Pointer to the buffer containing the data to write. + * @return 0 if successful, non-zero if there was an error. + */ + uint8_t _writeByte(uint8_t reg, uint8_t nbytes, uint8_t *data) { _i2cPort->beginTransmission(_address); _i2cPort->write(reg); for (uint8_t i = 0; i < nbytes; i++) { _i2cPort->write(data[i]); } - _i2cPort->endTransmission(); - return 0; + return _i2cPort->endTransmission(); } - uint8_t _isValid = false; - int _address; - bool _init = false; - TwoWire *_i2cPort; - uint8_t _data[16]; - bool _voltageLow; - char format[128]; -}; + uint8_t _isValid = false; // Flag indicating if the clock data is valid + int _address; // I2C address of the RTC module + TwoWire *_i2cPort; // Pointer to the I2C port + uint8_t _data[16]; // Data buffer for reading from the RTC module + bool _voltageLow; // Flag indicating if the voltage is low + char format[128]; // Format string for date and time formatting +}; \ No newline at end of file