diff --git a/.github/workflows/compile_examples.yml b/.github/workflows/compile_examples.yml index d44bd97..5226a0f 100644 --- a/.github/workflows/compile_examples.yml +++ b/.github/workflows/compile_examples.yml @@ -7,10 +7,22 @@ on: jobs: compile-examples: runs-on: ubuntu-latest - + + strategy: + fail-fast: false + + matrix: + board: + - fqbn: arduino:avr:uno + - fqbn: arduino:avr:nano + steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v6 - uses: arduino/compile-sketches@v1 with: + fqbn: ${{ matrix.board.fqbn }} + cli-compile-flags: | + - --build-property + - compiler.cpp.extra_flags=-std=gnu++17 libraries: | - - source-path: ./ + - source-path: . diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml new file mode 100644 index 0000000..138f440 --- /dev/null +++ b/.github/workflows/unit_tests.yml @@ -0,0 +1,25 @@ +name: Unit Tests + +on: + push: + branches: [ "**" ] + + pull_request: + branches: [ "**" ] + +jobs: + unit-tests: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@main + + - name: Configure CMake + run: cmake -B build -DCMAKE_BUILD_TYPE=Release + + - name: Build + run: cmake --build build + + - name: Run Tests + run: ctest --test-dir build --output-on-failure \ No newline at end of file diff --git a/.gitignore b/.gitignore index 06dfa92..7781b42 100644 --- a/.gitignore +++ b/.gitignore @@ -34,6 +34,12 @@ # PlatformIO files/folders .pio +# cmake +build + +# mac +.DS_Store + # vscode files /folders .vscode/.browse.c_cpp.db* .vscode/c_cpp_properties.json diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..70230a4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.14) +project(Ds3231_Tests CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Fetch GoogleTest +include(FetchContent) +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/v1.14.0.zip +) +set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) +FetchContent_MakeAvailable(googletest) + +enable_testing() +add_subdirectory(test) \ No newline at end of file diff --git a/README.md b/README.md index 8e9c59b..17adc96 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # DS3231-RTC Library [![Spell Check](https://github.com/hasenradball/DS3231-RTC/actions/workflows/spell_checker.yml/badge.svg)](https://github.com/hasenradball/DS3231-RTC/actions/workflows/spell_checker.yml) [![Compile Examples](https://github.com/hasenradball/DS3231-RTC/actions/workflows/compile_examples.yml/badge.svg)](https://github.com/hasenradball/DS3231-RTC/actions/workflows/compile_examples.yml) +[![Unit Tests](https://github.com/hasenradball/DS3231-RTC/actions/workflows/unit_tests.yml/badge.svg)](https://github.com/hasenradball/DS3231-RTC/actions/workflows/unit_tests.yml) The **great** C++ Library for the DS3231 real-time clock (RTC) module. @@ -17,17 +18,25 @@ This document explains the installation and usage of the Library with the Arduin You do have to install the Library in your Arduino IDE environment before you can use it. Installation instructions are provided, below. **REMARK**:
-This library was based on the master branch of [NorthernWidget/DS3231](https://github.com/NorthernWidget/DS3231) Library in Oct/2023. It was reworked and refractured with respect of the following main topics: +This library was based on the master branch of [NorthernWidget/DS3231](https://github.com/NorthernWidget/DS3231) Library in Oct/2023. It was maintained, reworked and refractured with respect of the following main topics: * using standardized functions of the `time.h` library. * introduce a `struct tm` which holds all relevant date and time values. * restructure comments, so that syntax highlighting works fine. * add a `strf_DateTime()` function with can be used to print a user specific(self defined) DateTime string easily. +* restructure of code with respect of clean code +* usage of Doxygen Documentation generation +* introduce Google Unit Testing + ## Contents -* [Summary](#summary) +* [Summary and How to Start](#summary-and-how-to-start) + * [Get Datetime from DS3231](#setup-and-get-datetime-from-ds3231) + * [Set the DS3231 Module with Date and Time](#setup-and-set-the-date-and-time-in-the-ds3231-module) + * [Use Date and Time](#use-date-and-time) * [About the DS3231](#about-the-ds3231-module) * [The DS3231 Battery Problem](#the-ds3231-battery-problem) +* [Unit Testing](#unit-tests) * [How to Install the Library](#installation) * [Functions Provided in the Library](#functions) * [Examples of Using the Library](#examples-of-use) @@ -35,52 +44,86 @@ This library was based on the master branch of [NorthernWidget/DS3231](https://g * [Contributing, Credits and License](#contributing) * [To-Do List](#to-do) -
-## Summary - -After installing the Library in your Arduino IDE, using it in a program starts with three, simple steps: - -
    -
  1. Import the Library into the program code:
  2. -
+## Summary and How to Start +### Setup and get DateTime from DS3231 +After installing the Library in your Arduino IDE, you can retrieve a DateTime from the DS3231 via: ``` +#include +#include #include -``` - -
    -
  1. Declare a DS3231 object, for example:
  2. -
+DS3231::RTClib myRTC; + +void setup () { + Serial.begin(57600); + Wire.begin(); + delay(500); + Serial.println("Nano Ready!"); +} + +void loop () { + + delay(1000); + + // get each second a timestamp + DS3231::DateTime now = myRTC.now(); + + Serial.print(now.getYear(), DEC); + Serial.print('/'); + Serial.print(now.getMonth(), DEC); + Serial.print('/'); + Serial.print(now.getDay(), DEC); + Serial.print(' '); + Serial.print(now.getHour(), DEC); + Serial.print(':'); + Serial.print(now.getMinute(), DEC); + Serial.print(':'); + Serial.print(now.getSecond(), DEC); + Serial.println(); + + Serial.print(" since midnight 1/1/1970 = "); + Serial.print(now.getUnixTime()); + Serial.print("s = "); + Serial.print(now.getUnixTime() / 86400L); + Serial.println("d"); +} +``` +or Serial for the **ESP8266** like: ``` -DS3231 myRTC; +Wire.begin(SDA, SCL); ``` -
    -
  1. Start the Wire library to enable I2C communications with the DS3231 hardware, typically in the setup() code block:
  2. -
+### Setup and set the Date and Time in the DS3231 module +The feed the DS3231 Module the easiest way is to set th Epoch (unix timestamp). - -``` -Wire.begin(); -``` -or for the **ESP8266** like: -``` -Wire.begin(SDA, SCL); ``` +#include +#include +#include -Then, Library functions are typically accessed through the DS3231 object. +// unix timestamp of: Tue Aug 16 2022 10:00:00 GMT+0000 +constexpr time_t timestamp{1660644000UL}; -For example, to read the current date of the month (1...31), depending on the month and the year: +DS3231::RTClib myRTC; +DS3231::DS3231 Clock; -``` -byte theDate = myRTC.getDate(); +void setup() { + Serial.begin(115200); + Wire.begin(); + Clock.begin(); + delay(500); + + // feed UnixTimeStamp + Clock.setEpoch(timestamp); +} ``` +### Use Date and Time The Library incorporates two other classes to assist with managing `date` and `time` data: * `DateTime` class enables a object for managing date and time data. @@ -92,7 +135,7 @@ The `DateTime` class can be instantiated by a specific date and time in three di year, month, day, hour, minute and second or - + * 2.) by a single, `time_t` unix timestamp.
* 3.) by giving a separate `const char *` string for Date and Time like:
@@ -158,7 +201,126 @@ See the corresponding link for the problem description in detail:
[back to top](#ds3231-rtc-library)
+## Unit Tests +The project contains unit tests. +These tests validate: + +* some helper functions +* getter functions + +### Run tests locally +The tests are based on GoogleTest and are built with CMake. + +Required tools: + +* CMake +* a C++17 compatible compiler + +#### Windows +With Visual Studio or Visual Studio Build Tools installed, run the commands in a Developer PowerShell: + +```powershell +# Generate Build System +cmake -B build -DCMAKE_BUILD_TYPE=Release +# Build a Project +cmake --build build +# Execute Tests +ctest --test-dir build --output-on-failure +or +./build/test/test_DS3231 --gtest_color=yes +``` + +#### Linux +Install CMake and a compiler toolchain, for example `g++` or `clang++`, and run: + +```bash +# Generate Build System +cmake -B build -DCMAKE_BUILD_TYPE=Release +# Build a Project +cmake --build build +# Execute Tests +ctest --test-dir build --output-on-failure +./build/test/test_DS3231 --gtest_color=yes +``` + +#### macOS +Install Xcode Command Line Tools and CMake, then run: + +```bash +# Generate Build System +cmake -B build -DCMAKE_BUILD_TYPE=Release +# Build a Project +cmake --build build +# Execute Tests +ctest --test-dir build --output-on-failure +./build/test/test_DS3231 --gtest_color=yes +``` +Example Output: +``` +[==========] Running 23 tests from 3 test suites. +[----------] Global test environment set-up. +[----------] 8 tests from DS3231Tools_BCD +[ RUN ] DS3231Tools_BCD.BinaryDecodedDecimalToDecimal_0 +[ OK ] DS3231Tools_BCD.BinaryDecodedDecimalToDecimal_0 (0 ms) +[ RUN ] DS3231Tools_BCD.BinaryDecodedDecimalToDecimal_9 +[ OK ] DS3231Tools_BCD.BinaryDecodedDecimalToDecimal_9 (0 ms) +[ RUN ] DS3231Tools_BCD.BinaryDecodedDecimalToDecimal_10 +[ OK ] DS3231Tools_BCD.BinaryDecodedDecimalToDecimal_10 (0 ms) +[ RUN ] DS3231Tools_BCD.BinaryDecodedDecimalToDecimal_59 +[ OK ] DS3231Tools_BCD.BinaryDecodedDecimalToDecimal_59 (0 ms) +[ RUN ] DS3231Tools_BCD.DecimalToBinaryDecodedDecimal_0 +[ OK ] DS3231Tools_BCD.DecimalToBinaryDecodedDecimal_0 (0 ms) +[ RUN ] DS3231Tools_BCD.DecimalToBinaryDecodedDecimal_9 +[ OK ] DS3231Tools_BCD.DecimalToBinaryDecodedDecimal_9 (0 ms) +[ RUN ] DS3231Tools_BCD.DecimalToBinaryDecodedDecimal_10 +[ OK ] DS3231Tools_BCD.DecimalToBinaryDecodedDecimal_10 (0 ms) +[ RUN ] DS3231Tools_BCD.DecimalToBinaryDecodedDecimal_59 +[ OK ] DS3231Tools_BCD.DecimalToBinaryDecodedDecimal_59 (0 ms) +[----------] 8 tests from DS3231Tools_BCD (0 ms total) + +[----------] 7 tests from DS3231Tools_leapYear +[ RUN ] DS3231Tools_leapYear.IsLeapYear_2023 +[ OK ] DS3231Tools_leapYear.IsLeapYear_2023 (0 ms) +[ RUN ] DS3231Tools_leapYear.IsLeapYear_2024 +[ OK ] DS3231Tools_leapYear.IsLeapYear_2024 (0 ms) +[ RUN ] DS3231Tools_leapYear.IsLeapYear_2032 +[ OK ] DS3231Tools_leapYear.IsLeapYear_2032 (0 ms) +[ RUN ] DS3231Tools_leapYear.IsLeapYear_1700 +[ OK ] DS3231Tools_leapYear.IsLeapYear_1700 (0 ms) +[ RUN ] DS3231Tools_leapYear.IsLeapYear_1800 +[ OK ] DS3231Tools_leapYear.IsLeapYear_1800 (0 ms) +[ RUN ] DS3231Tools_leapYear.IsLeapYear_1900 +[ OK ] DS3231Tools_leapYear.IsLeapYear_1900 (0 ms) +[ RUN ] DS3231Tools_leapYear.IsLeapYear_2000 +[ OK ] DS3231Tools_leapYear.IsLeapYear_2000 (0 ms) +[----------] 7 tests from DS3231Tools_leapYear (0 ms total) + +[----------] 8 tests from DS3231MockTest +[ RUN ] DS3231MockTest.GetSecondReadsSecondRegister +[ OK ] DS3231MockTest.GetSecondReadsSecondRegister (0 ms) +[ RUN ] DS3231MockTest.GetMinuteReadsMinuteRegister +[ OK ] DS3231MockTest.GetMinuteReadsMinuteRegister (0 ms) +[ RUN ] DS3231MockTest.GetHourReadsFlagsAndBcdValue +[ OK ] DS3231MockTest.GetHourReadsFlagsAndBcdValue (0 ms) +[ RUN ] DS3231MockTest.SetHourWritesUpdatedHourRegister +[ OK ] DS3231MockTest.SetHourWritesUpdatedHourRegister (0 ms) +[ RUN ] DS3231MockTest.GetDoWReadsDoWRegister +[ OK ] DS3231MockTest.GetDoWReadsDoWRegister (0 ms) +[ RUN ] DS3231MockTest.GetDayReadsDateRegister +[ OK ] DS3231MockTest.GetDayReadsDateRegister (0 ms) +[ RUN ] DS3231MockTest.GetMonthReadsMonthRegister +[ OK ] DS3231MockTest.GetMonthReadsMonthRegister (0 ms) +[ RUN ] DS3231MockTest.GetYearReadsYearRegister +[ OK ] DS3231MockTest.GetYearReadsYearRegister (0 ms) +[----------] 8 tests from DS3231MockTest (0 ms total) + +[----------] Global test environment tear-down +[==========] 23 tests from 3 test suites ran. (1 ms total) +[ PASSED ] 23 tests. +``` + +[back to top](#ds3231-rtc-library) ## Installation ### First Method @@ -324,6 +486,8 @@ See also [Working with the DS3231 libraries and interrupts](https://github.com/I [back to top](#ds3231-rtc-library)
+ + ## Contributing If you want to contribute to this project: @@ -353,7 +517,7 @@ Based on previous work by: ## License -DS3231 is licensed under [MIT License](https://github.com/hasenradball/DS3231-RTC/blob/master/LICENSE). +DS3231-RTC is licensed under [MIT License](https://github.com/hasenradball/DS3231-RTC/blob/master/LICENSE). [back to top](#ds3231-rtc-library)
diff --git a/docs/DS3231_datasheet.pdf b/docs/DS3231_datasheet.pdf new file mode 100755 index 0000000..4b51d64 Binary files /dev/null and b/docs/DS3231_datasheet.pdf differ diff --git a/examples/AdvanceAlarm/AdvanceAlarm.ino b/examples/AdvanceAlarm/AdvanceAlarm.ino index 5c96ed2..6332de5 100644 --- a/examples/AdvanceAlarm/AdvanceAlarm.ino +++ b/examples/AdvanceAlarm/AdvanceAlarm.ino @@ -24,14 +24,12 @@ Tested on: #include #include -// Interrupt frequency, in seconds -#define INT_FREQ 3UL // 3 seconds, characterized as unsigned long -// myRTC interrupt pin -#define CLINT 2 +// Interrupt frequency, in seconds +constexpr uint8_t INTERRUPT_FREQUENCY {3U}; +constexpr int RTC_INTERUPT_PIN {2}; -// Setup clock -DS3231 myRTC; +DS3231::DS3231 myRTC; // Variables for use in method parameter lists byte alarmDay; @@ -46,122 +44,121 @@ bool alarmPM; // Interrupt signaling byte volatile byte tick = 1; +void isr_TickTock() { + // interrupt signals to loop + tick = 1; + return; +} void setup() { - // Begin I2C communication - Wire.begin(); - - // Begin Serial communication - Serial.begin(9600); - while (!Serial); - - // Set the DS3231 clock mode to 24-hour - myRTC.setClockMode(false); // false = not using the alternate, 12-hour mode - - // Set the clock to an arbitrarily chosen time of - // 00:00:00 midnight the morning of January 1, 2020 - // using a suitable Unix-style timestamp - myRTC.setEpoch(1640995200); - - // Assign parameter values for Alarm 1 - alarmDay = myRTC.getDate(); - alarmHour = myRTC.getHour(alarmH12, alarmPM); - alarmMinute = myRTC.getMinute(); - alarmSecond = INT_FREQ; // initialize to the interval length - alarmBits = 0b00001110; // Alarm 1 when seconds match - alarmDayIsDay = false; // using date of month - - // Upload initial parameters of Alarm 1 - myRTC.turnOffAlarm(1); - myRTC.setA1Time( - alarmDay, alarmHour, alarmMinute, alarmSecond, - alarmBits, alarmDayIsDay, alarmH12, alarmPM); - // clear Alarm 1 flag after setting the alarm time - myRTC.checkIfAlarm(1); - // now it is safe to enable interrupt output - myRTC.turnOnAlarm(1); - - // When using interrupt with only one of the DS3231 alarms, as in this example, - // it may be possible to prevent the other alarm entirely, - // so it will not covertly block the outgoing interrupt signal. - - // Try to prevent Alarm 2 altogether by assigning a - // nonsensical alarm minute value that cannot match the clock time, - // and an alarmBits value to activate "when minutes match". - alarmMinute = 0xFF; // a value that will never match the time - alarmBits = 0b01100000; // Alarm 2 when minutes match, i.e., never - - // Upload the parameters to prevent Alarm 2 entirely - myRTC.setA2Time( - alarmDay, alarmHour, alarmMinute, - alarmBits, alarmDayIsDay, alarmH12, alarmPM); - // disable Alarm 2 interrupt - myRTC.turnOffAlarm(2); - // clear Alarm 2 flag - myRTC.checkIfAlarm(2); - - // NOTE: both of the alarm flags must be clear - // to enable output of a FALLING interrupt - - // attach clock interrupt - pinMode(CLINT, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(CLINT), isr_TickTock, FALLING); - - // Configure the LED for blinking - pinMode(LED_BUILTIN, OUTPUT); + // Begin I2C communication + Wire.begin(); + + // Begin Serial communication + Serial.begin(9600); + while (!Serial); + + myRTC.begin(); + + // Set the clock to an arbitrarily chosen time of + // 00:00:00 midnight the morning of January 1, 2020 + // using a suitable Unix-style timestamp + myRTC.setEpoch(1640995200); + + // Assign parameter values for Alarm 1 + alarmDay = myRTC.getDate(); + alarmHour = myRTC.getHour(alarmH12, alarmPM); + alarmMinute = myRTC.getMinute(); + alarmSecond = INTERRUPT_FREQUENCY; // initialize to the interval length + alarmBits = 0b00001110; // Alarm 1 when seconds match + alarmDayIsDay = false; // using date of month + + // Upload initial parameters of Alarm 1 + myRTC.turnOffAlarm(1); + myRTC.setA1Time( + alarmDay, alarmHour, alarmMinute, alarmSecond, + alarmBits, alarmDayIsDay, alarmH12, alarmPM); + // clear Alarm 1 flag after setting the alarm time + myRTC.checkIfAlarm(1); + // now it is safe to enable interrupt output + myRTC.turnOnAlarm(1); + + // When using interrupt with only one of the DS3231 alarms, as in this example, + // it may be possible to prevent the other alarm entirely, + // so it will not covertly block the outgoing interrupt signal. + + // Try to prevent Alarm 2 altogether by assigning a + // nonsensical alarm minute value that cannot match the clock time, + // and an alarmBits value to activate "when minutes match". + alarmMinute = 0xFF; // a value that will never match the time + alarmBits = 0b01100000; // Alarm 2 when minutes match, i.e., never + + // Upload the parameters to prevent Alarm 2 entirely + myRTC.setA2Time( + alarmDay, alarmHour, alarmMinute, + alarmBits, alarmDayIsDay, alarmH12, alarmPM); + // disable Alarm 2 interrupt + myRTC.turnOffAlarm(2); + // clear Alarm 2 flag + myRTC.checkIfAlarm(2); + + // NOTE: both of the alarm flags must be clear + // to enable output of a FALLING interrupt + + // attach clock interrupt + pinMode(RTC_INTERUPT_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(RTC_INTERUPT_PIN), isr_TickTock, FALLING); + + // Configure the LED for blinking + pinMode(LED_BUILTIN, OUTPUT); } void loop() { - // static variable to keep track of LED on/off state - static byte state = false; - - // Do when alarm interrupt received: - if (tick) { - // right away, capture the current time in a DateTime variable - // for later processing - DateTime alarmDT = RTClib::now(); - - // disable Alarm 1 interrupt - myRTC.turnOffAlarm(1); - - // Clear Alarm 1 flag - myRTC.checkIfAlarm(1); - - tick = 0; // reset the local interrupt-received flag - state = ~state; // reverse the state of the LED - digitalWrite(LED_BUILTIN, state); - - // optional serial output - Serial.print("Turning LED "); - Serial.print((state ? "ON" : "OFF")); - Serial.print(" at "); - Serial.print(alarmDT.getHour()); - Serial.print(":"); - Serial.print(alarmDT.getMinute()); - Serial.print(":"); - Serial.println(alarmDT.getSecond()); - - // extract the DateTime values as a timestamp - uint32_t nextAlarm = alarmDT.getUnixTime(); - // add the INT_FREQ number of seconds - nextAlarm += INT_FREQ; - // update the DateTime with the new timestamp - alarmDT = DateTime(nextAlarm); - - // upload the new time to Alarm 1 - myRTC.setA1Time( - alarmDT.getDay(), alarmDT.getHour(), alarmDT.getMinute(), alarmDT.getSecond(), - alarmBits, alarmDayIsDay, alarmH12, alarmPM); - - // enable Alarm 1 interrupts - myRTC.turnOnAlarm(1); + // static variable to keep track of LED on/off state + static byte state = false; + + // Do when alarm interrupt received: + if (tick) { + // right away, capture the current time in a DateTime variable + // for later processing + DS3231::DateTime alarmDT = DS3231::RTClib::now(); + + // disable Alarm 1 interrupt + myRTC.turnOffAlarm(1); + + // Clear Alarm 1 flag + myRTC.checkIfAlarm(1); + + tick = 0; // reset the local interrupt-received flag + state = ~state; // reverse the state of the LED + digitalWrite(LED_BUILTIN, state); + + // optional serial output + Serial.print("Turning LED "); + Serial.print((state ? "ON" : "OFF")); + Serial.print(" at "); + Serial.print(alarmDT.getHour()); + Serial.print(":"); + Serial.print(alarmDT.getMinute()); + Serial.print(":"); + Serial.println(alarmDT.getSecond()); + + // extract the DateTime values as a timestamp + uint32_t nextAlarm = alarmDT.getUnixTime(); + nextAlarm += INTERRUPT_FREQUENCY; + // update the DateTime with the new timestamp + alarmDT = DS3231::DateTime(nextAlarm); + + // upload the new time to Alarm 1 + myRTC.setA1Time( + alarmDT.getDay(), alarmDT.getHour(), alarmDT.getMinute(), alarmDT.getSecond(), + alarmBits, alarmDayIsDay, alarmH12, alarmPM); + + // enable Alarm 1 interrupts + myRTC.turnOnAlarm(1); // clear Alarm 1 flag again after enabling interrupts - myRTC.checkIfAlarm(1); - } + myRTC.checkIfAlarm(1); + } } -void isr_TickTock() { - // interrupt signals to loop - tick = 1; - return; -} + diff --git a/examples/AlarmInterrupt/AlarmInterrupt.ino b/examples/AlarmInterrupt/AlarmInterrupt.ino index 0642b99..3adb5ad 100644 --- a/examples/AlarmInterrupt/AlarmInterrupt.ino +++ b/examples/AlarmInterrupt/AlarmInterrupt.ino @@ -27,14 +27,14 @@ Added to this example: David Sparks, September 2022 */ -#include #include +#include // myRTC interrupt pin -#define CLINT 2 +#define CLOCK_INTERRUPT_PIN 2 // Setup clock -DS3231 myRTC; +DS3231::DS3231 myRTC; // Variables for use in method parameter lists byte alarmDay; @@ -49,91 +49,92 @@ bool alarmPM; // Interrupt signaling byte volatile byte tick = 1; +void isr_TickTock() { + // interrupt signals to loop + tick = 1; + return; +} + void setup() { - // Begin I2C communication - Wire.begin(); - - // Begin Serial communication - Serial.begin(9600); - while (!Serial); - Serial.println(); - Serial.println("Starting Serial"); - - // Assign parameter values for Alarm 1 - alarmDay = 0; - alarmHour = 0; - alarmMinute = 0; - alarmSecond = 0; - alarmBits = 0b00001111; // Alarm 1 every second - alarmDayIsDay = false; - alarmH12 = false; - alarmPM = false; - - // Set alarm 1 to fire at one-second intervals - myRTC.turnOffAlarm(1); - myRTC.setA1Time( - alarmDay, alarmHour, alarmMinute, alarmSecond, - alarmBits, alarmDayIsDay, alarmH12, alarmPM); - // enable Alarm 1 interrupts - myRTC.turnOnAlarm(1); - // clear Alarm 1 flag - myRTC.checkIfAlarm(1); - - // When using interrupt with only one of the DS3231 alarms, as in this example, - // it may be advisable to prevent the other alarm entirely, - // so it will not covertly block the outgoing interrupt signal. - - // Prevent Alarm 2 altogether by assigning a - // nonsensical alarm minute value that cannot match the clock time, - // and an alarmBits value to activate "when minutes match". - alarmMinute = 0xFF; // a value that will never match the time - alarmBits = 0b01100000; // Alarm 2 when minutes match, i.e., never - - // Upload the parameters to prevent Alarm 2 entirely - myRTC.setA2Time( - alarmDay, alarmHour, alarmMinute, - alarmBits, alarmDayIsDay, alarmH12, alarmPM); - // disable Alarm 2 interrupt - myRTC.turnOffAlarm(2); - // clear Alarm 2 flag - myRTC.checkIfAlarm(2); - - // NOTE: both of the alarm flags must be clear - // to enable output of a FALLING interrupt - - // attach clock interrupt - pinMode(CLINT, INPUT_PULLUP); - attachInterrupt(digitalPinToInterrupt(CLINT), isr_TickTock, FALLING); - - // Use builtin LED to blink - pinMode(LED_BUILTIN, OUTPUT); + // Begin I2C communication + Wire.begin(); + + // Begin Serial communication + Serial.begin(9600); + while (!Serial); + Serial.println(); + Serial.println("Starting Serial"); + myRTC.begin(); + + // Assign parameter values for Alarm 1 + alarmDay = 0; + alarmHour = 0; + alarmMinute = 0; + alarmSecond = 0; + alarmBits = 0b00001111; // Alarm 1 every second + alarmDayIsDay = false; + alarmH12 = false; + alarmPM = false; + + // Set alarm 1 to fire at one-second intervals + myRTC.turnOffAlarm(1); + myRTC.setA1Time( + alarmDay, alarmHour, alarmMinute, alarmSecond, + alarmBits, alarmDayIsDay, alarmH12, alarmPM); + // enable Alarm 1 interrupts + myRTC.turnOnAlarm(1); + // clear Alarm 1 flag + myRTC.checkIfAlarm(1); + + // When using interrupt with only one of the DS3231 alarms, as in this example, + // it may be advisable to prevent the other alarm entirely, + // so it will not covertly block the outgoing interrupt signal. + + // Prevent Alarm 2 altogether by assigning a + // nonsensical alarm minute value that cannot match the clock time, + // and an alarmBits value to activate "when minutes match". + alarmMinute = 0xFF; // a value that will never match the time + alarmBits = 0b01100000; // Alarm 2 when minutes match, i.e., never + + // Upload the parameters to prevent Alarm 2 entirely + myRTC.setA2Time( + alarmDay, alarmHour, alarmMinute, + alarmBits, alarmDayIsDay, alarmH12, alarmPM); + // disable Alarm 2 interrupt + myRTC.turnOffAlarm(2); + // clear Alarm 2 flag + myRTC.checkIfAlarm(2); + + // NOTE: both of the alarm flags must be clear + // to enable output of a FALLING interrupt + + // attach clock interrupt + pinMode(CLOCK_INTERRUPT_PIN, INPUT_PULLUP); + attachInterrupt(digitalPinToInterrupt(CLOCK_INTERRUPT_PIN), isr_TickTock, FALLING); + + // Use builtin LED to blink + pinMode(LED_BUILTIN, OUTPUT); } void loop() { - // static variable to keep track of LED on/off state - static byte state = false; + // static variable to keep track of LED on/off state + static byte state = false; - // if alarm went of, do alarm stuff - if (tick) { - tick = 0; - state = ~state; - digitalWrite(LED_BUILTIN, state); + // if alarm went of, do alarm stuff + if (tick) { + tick = 0; + state = ~state; + digitalWrite(LED_BUILTIN, state); - // optional serial output - Serial.print("Turning LED "); - Serial.println((state ? "ON" : "OFF")); + // optional serial output + Serial.print("Turning LED "); + Serial.println((state ? "ON" : "OFF")); - // Clear Alarm 1 flag - myRTC.checkIfAlarm(1); - } + // Clear Alarm 1 flag + myRTC.checkIfAlarm(1); + } - // Loop delay to emulate other running code - delay(10); + // Loop delay to emulate other running code + delay(10); } - -void isr_TickTock() { - // interrupt signals to loop - tick = 1; - return; -} diff --git a/examples/AlarmPolling/AlarmPolling.ino b/examples/AlarmPolling/AlarmPolling.ino index c5dc0f9..abaa352 100644 --- a/examples/AlarmPolling/AlarmPolling.ino +++ b/examples/AlarmPolling/AlarmPolling.ino @@ -14,37 +14,38 @@ Tested on: #include // Setup clock -DS3231 myRTC; +DS3231::DS3231 myRTC; void setup() { - // Begin I2C communication - Wire.begin(); - - // Setup alarm one to fire every second - myRTC.turnOffAlarm(1); - myRTC.setA1Time(0, 0, 0, 0, 0b01111111, false, false, false); - myRTC.turnOnAlarm(1); - myRTC.checkIfAlarm(1); - - // Use builtin LED to blink - pinMode(LED_BUILTIN, OUTPUT); - digitalWrite(LED_BUILTIN, HIGH); + // Begin I2C communication + Wire.begin(); + myRTC.begin(); + + // Setup alarm one to fire every second + myRTC.turnOffAlarm(1); + myRTC.setA1Time(0, 0, 0, 0, 0b01111111, false, false, false); + myRTC.turnOnAlarm(1); + myRTC.checkIfAlarm(1); + + // Use builtin LED to blink + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); } void loop() { - // static variable to keep track of LED on/off state - static byte state = false; - - // if alarm went of, do alarm stuff - // first call to checkIFAlarm does not clear alarm flag - if (myRTC.checkIfAlarm(1, false)) { - state = ~state; - digitalWrite(LED_BUILTIN, state); - // Clear alarm state - myRTC.checkIfAlarm(1, true); - } - - // Loop delay to emulate other running code - delay(10); + // static variable to keep track of LED on/off state + static byte state = false; + + // if alarm went of, do alarm stuff + // first call to checkIFAlarm does not clear alarm flag + if (myRTC.checkIfAlarm(1, false)) { + state = ~state; + digitalWrite(LED_BUILTIN, state); + // Clear alarm state + myRTC.checkIfAlarm(1, true); + } + + // Loop delay to emulate other running code + delay(10); } diff --git a/examples/DS3231_oscillator_test/DS3231_oscillator_test.ino b/examples/DS3231_oscillator_test/DS3231_oscillator_test.ino index 13979ba..766c6c4 100644 --- a/examples/DS3231_oscillator_test/DS3231_oscillator_test.ino +++ b/examples/DS3231_oscillator_test/DS3231_oscillator_test.ino @@ -10,29 +10,32 @@ working as they should. */ -#include #include +#include -DS3231 myRTC; -byte j; -bool on = false; +DS3231::DS3231 myRTC; +bool activate_32kHz = false; -void setup() { - // Start the I2C interface - Wire.begin(); - // Start the serial interface - Serial.begin(57600); +void setup() +{ + // Start the I2C interface + Wire.begin(); + // Start the serial interface + Serial.begin(57600); + myRTC.begin(); } -void loop() { - for (j=0;j<4;j++) { - // invert state of 32kHz oscillator. - on = !on; - myRTC.enable32kHz(on); - // Turn on oscillator pin, frequency j - myRTC.enableOscillator(true, false, j); - delay(4000); - } - // So... The 32kHz oscillator (pin 1) will turn on or off once each 2s, - // and the oscillator out pin (pin 3) will cycle through frequencies. +void loop() +{ + for (byte j = 0; j < 4; ++j) + { + // invert state of 32kHz oscillator. + activate_32kHz = !activate_32kHz; + myRTC.enable32kHz(activate_32kHz); + // Turn on oscillator pin, frequency j + myRTC.enableOscillator(true, false, j); + delay(4000); + } + // So... The 32kHz oscillator (pin 1) will turn on or off once each 2s, + // and the oscillator out pin (pin 3) will cycle through frequencies. } diff --git a/examples/DS3231_set/DS3231_set.ino b/examples/DS3231_set/DS3231_set.ino index d3afbc2..d8602bf 100644 --- a/examples/DS3231_set/DS3231_set.ino +++ b/examples/DS3231_set/DS3231_set.ino @@ -6,11 +6,10 @@ Eric Ayars Test of set-time routines for a DS3231 RTC */ - #include #include -DS3231 myRTC; +DS3231::DS3231 myRTC; byte year; byte month; @@ -20,94 +19,99 @@ byte hour; byte minute; byte second; -void getDateStuff(byte& year, byte& month, byte& date, byte& dOW, - byte& hour, byte& minute, byte& second) { - // Call this if you notice something coming in on - // the serial port. The stuff coming in should be in - // the order YYMMDDwHHMMSS, with an 'x' at the end. - boolean gotString = false; - char inChar; - byte temp1, temp2; - char inString[20]; - - byte j=0; - while (!gotString) { - if (Serial.available()) { - inChar = Serial.read(); - inString[j] = inChar; - j += 1; - if (inChar == 'x') { - gotString = true; - } - } - } - Serial.println(inString); - // Read year first - temp1 = (byte)inString[0] -48; - temp2 = (byte)inString[1] -48; - year = temp1*10 + temp2; - // now month - temp1 = (byte)inString[2] -48; - temp2 = (byte)inString[3] -48; - month = temp1*10 + temp2; - // now date - temp1 = (byte)inString[4] -48; - temp2 = (byte)inString[5] -48; - date = temp1*10 + temp2; - // now Day of Week - dOW = (byte)inString[6] - 48; - // now hour - temp1 = (byte)inString[7] -48; - temp2 = (byte)inString[8] -48; - hour = temp1*10 + temp2; - // now minute - temp1 = (byte)inString[9] -48; - temp2 = (byte)inString[10] -48; - minute = temp1*10 + temp2; - // now second - temp1 = (byte)inString[11] -48; - temp2 = (byte)inString[12] -48; - second = temp1*10 + temp2; +void getDateStuff(byte &year, byte &month, byte &date, byte &dOW, + byte &hour, byte &minute, byte &second) +{ + // Call this if you notice something coming in on + // the serial port. The stuff coming in should be in + // the order YYMMDDwHHMMSS, with an 'x' at the end. + boolean gotString = false; + char inChar; + byte temp1, temp2; + char inString[20]; + + byte j = 0; + while (!gotString) + { + if (Serial.available()) + { + inChar = Serial.read(); + inString[j] = inChar; + j += 1; + if (inChar == 'x') + { + gotString = true; + } + } + } + Serial.println(inString); + // Read year first + temp1 = (byte)inString[0] - 48; + temp2 = (byte)inString[1] - 48; + year = temp1 * 10 + temp2; + // now month + temp1 = (byte)inString[2] - 48; + temp2 = (byte)inString[3] - 48; + month = temp1 * 10 + temp2; + // now date + temp1 = (byte)inString[4] - 48; + temp2 = (byte)inString[5] - 48; + date = temp1 * 10 + temp2; + // now Day of Week + dOW = (byte)inString[6] - 48; + // now hour + temp1 = (byte)inString[7] - 48; + temp2 = (byte)inString[8] - 48; + hour = temp1 * 10 + temp2; + // now minute + temp1 = (byte)inString[9] - 48; + temp2 = (byte)inString[10] - 48; + minute = temp1 * 10 + temp2; + // now second + temp1 = (byte)inString[11] - 48; + temp2 = (byte)inString[12] - 48; + second = temp1 * 10 + temp2; } -void setup() { - // Start the serial port - Serial.begin(57600); - - // Start the I2C interface - Wire.begin(); +void setup() +{ + // Start the serial port + Serial.begin(57600); + + // Start the I2C interface + Wire.begin(); + myRTC.begin(); + } -void loop() { - - // If something is coming in on the serial line, it's - // a time correction so set the clock accordingly. - if (Serial.available()) { - getDateStuff(year, month, date, dOW, hour, minute, second); - - myRTC.setClockMode(false); // set to 24h - //setClockMode(true); // set to 12h - - myRTC.setYear(year); - myRTC.setMonth(month); - myRTC.setDate(date); - myRTC.setDoW(dOW); - myRTC.setHour(hour); - myRTC.setMinute(minute); - myRTC.setSecond(second); - - // Test of alarm functions - // set A1 to one minute past the time we just set the clock - // on current day of week. - myRTC.setA1Time(dOW, hour, minute+1, second, 0x0, true, - false, false); - // set A2 to two minutes past, on current day of month. - myRTC.setA2Time(date, hour, minute+2, 0x0, false, false, - false); - // Turn on both alarms, with external interrupt - myRTC.turnOnAlarm(1); - myRTC.turnOnAlarm(2); - - } - delay(1000); +void loop() +{ + + // If something is coming in on the serial line, it's + // a time correction so set the clock accordingly. + if (Serial.available()) + { + getDateStuff(year, month, date, dOW, hour, minute, second); + + myRTC.setYear(year); + myRTC.setMonth(month); + myRTC.setDate(date); + myRTC.setDoW(dOW); + myRTC.setHour(hour); + myRTC.setMinute(minute); + myRTC.setSecond(second); + + // Test of alarm functions + // set A1 to one minute past the time we just set the clock + // on current day of week. + myRTC.setA1Time(dOW, hour, minute + 1, second, 0x0, true, + false, false); + // set A2 to two minutes past, on current day of month. + myRTC.setA2Time(date, hour, minute + 2, 0x0, false, false, + false); + // Turn on both alarms, with external interrupt + myRTC.turnOnAlarm(1); + myRTC.turnOnAlarm(2); + } + delay(1000); } diff --git a/examples/DS3231_test/DS3231_test.ino b/examples/DS3231_test/DS3231_test.ino index 29d3ad8..395697a 100644 --- a/examples/DS3231_test/DS3231_test.ino +++ b/examples/DS3231_test/DS3231_test.ino @@ -13,7 +13,7 @@ working as they should. #include #include -DS3231 myRTC; +DS3231::DS3231 myRTC; bool century = false; bool h12Flag; bool pmFlag; @@ -21,137 +21,138 @@ byte alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits; bool alarmDy, alarmH12Flag, alarmPmFlag; void setup() { - // Start the I2C interface - Wire.begin(); + // Start the I2C interface + Wire.begin(); - // Start the serial interface - Serial.begin(57600); + // Start the serial interface + Serial.begin(57600); + myRTC.begin(); } void loop() { - // send what's going on to the serial monitor. - - // Start with the year - Serial.print("2"); - if (century) { // Won't need this for 89 years. - Serial.print("1"); - } else { - Serial.print("0"); - } - Serial.print(myRTC.getYear(), DEC); - Serial.print(' '); - - // then the month - Serial.print(myRTC.getMonth(century), DEC); - Serial.print(" "); + // send what's going on to the serial monitor. + + // Start with the year + Serial.print("2"); + if (century) { // Won't need this for 89 years. + Serial.print("1"); + } else { + Serial.print("0"); + } + Serial.print(myRTC.getYear(), DEC); + Serial.print(' '); + + // then the month + Serial.print(myRTC.getMonth(century), DEC); + Serial.print(" "); - // then the date - Serial.print(myRTC.getDate(), DEC); - Serial.print(" "); + // then the date + Serial.print(myRTC.getDate(), DEC); + Serial.print(" "); - // and the day of the week - Serial.print(myRTC.getDoW(), DEC); - Serial.print(" "); + // and the day of the week + Serial.print(myRTC.getDoW(), DEC); + Serial.print(" "); - // Finally the hour, minute, and second - Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC); - Serial.print(" "); - Serial.print(myRTC.getMinute(), DEC); - Serial.print(" "); - Serial.print(myRTC.getSecond(), DEC); + // Finally the hour, minute, and second + Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC); + Serial.print(" "); + Serial.print(myRTC.getMinute(), DEC); + Serial.print(" "); + Serial.print(myRTC.getSecond(), DEC); - // Add AM/PM indicator - if (h12Flag) { - if (pmFlag) { - Serial.print(" PM "); - } else { - Serial.print(" AM "); - } - } else { - Serial.print(" 24h "); - } + // Add AM/PM indicator + if (h12Flag) { + if (pmFlag) { + Serial.print(" PM "); + } else { + Serial.print(" AM "); + } + } else { + Serial.print(" 24h "); + } - // Display the temperature - Serial.print("T="); - Serial.print(myRTC.getTemperature(), 2); + // Display the temperature + Serial.print("T="); + Serial.print(myRTC.getTemperature(), 2); - // Tell whether the time is (likely to be) valid - if (myRTC.oscillatorCheck()) { - Serial.print(" O+"); - } else { - Serial.print(" O-"); - } + // Tell whether the time is (likely to be) valid + if (myRTC.oscillatorCheck()) { + Serial.print(" O+"); + } else { + Serial.print(" O-"); + } - // Indicate whether an alarm went off - if (myRTC.checkIfAlarm(1)) { - Serial.print(" A1!"); - } + // Indicate whether an alarm went off + if (myRTC.checkIfAlarm(1)) { + Serial.print(" A1!"); + } - if (myRTC.checkIfAlarm(2)) { - Serial.print(" A2!"); - } + if (myRTC.checkIfAlarm(2)) { + Serial.print(" A2!"); + } - // New line on display - Serial.println(); - - // Display Alarm 1 information - Serial.print("Alarm 1: "); - myRTC.getA1Time(alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits, alarmDy, alarmH12Flag, alarmPmFlag); - Serial.print(alarmDay, DEC); - if (alarmDy) { - Serial.print(" DoW"); - } else { - Serial.print(" Date"); - } - Serial.print(' '); - Serial.print(alarmHour, DEC); - Serial.print(' '); - Serial.print(alarmMinute, DEC); - Serial.print(' '); - Serial.print(alarmSecond, DEC); - Serial.print(' '); - if (alarmH12Flag) { - if (alarmPmFlag) { - Serial.print("pm "); - } else { - Serial.print("am "); - } - } - if (myRTC.checkAlarmEnabled(1)) { - Serial.print("enabled"); - } - Serial.println(); + // New line on display + Serial.println(); + + // Display Alarm 1 information + Serial.print("Alarm 1: "); + myRTC.getA1Time(alarmDay, alarmHour, alarmMinute, alarmSecond, alarmBits, alarmDy, alarmH12Flag, alarmPmFlag); + Serial.print(alarmDay, DEC); + if (alarmDy) { + Serial.print(" DoW"); + } else { + Serial.print(" Date"); + } + Serial.print(' '); + Serial.print(alarmHour, DEC); + Serial.print(' '); + Serial.print(alarmMinute, DEC); + Serial.print(' '); + Serial.print(alarmSecond, DEC); + Serial.print(' '); + if (alarmH12Flag) { + if (alarmPmFlag) { + Serial.print("pm "); + } else { + Serial.print("am "); + } + } + if (myRTC.checkAlarmEnabled(1)) { + Serial.print("enabled"); + } + Serial.println(); - // Display Alarm 2 information - Serial.print("Alarm 2: "); - myRTC.getA2Time(alarmDay, alarmHour, alarmMinute, alarmBits, alarmDy, alarmH12Flag, alarmPmFlag); - Serial.print(alarmDay, DEC); - if (alarmDy) { - Serial.print(" DoW"); - } else { - Serial.print(" Date"); - } - Serial.print(" "); - Serial.print(alarmHour, DEC); - Serial.print(" "); - Serial.print(alarmMinute, DEC); - Serial.print(" "); - if (alarmH12Flag) { - if (alarmPmFlag) { - Serial.print("pm"); - } else { - Serial.print("am"); - } - } - if (myRTC.checkAlarmEnabled(2)) { - Serial.print("enabled"); - } - - // display alarm bits - Serial.println(); - Serial.print("Alarm bits: "); - Serial.println(alarmBits, BIN); + // Display Alarm 2 information + Serial.print("Alarm 2: "); + myRTC.getA2Time(alarmDay, alarmHour, alarmMinute, alarmBits, alarmDy, alarmH12Flag, alarmPmFlag); + Serial.print(alarmDay, DEC); + if (alarmDy) { + Serial.print(" DoW"); + } else { + Serial.print(" Date"); + } + Serial.print(" "); + Serial.print(alarmHour, DEC); + Serial.print(" "); + Serial.print(alarmMinute, DEC); + Serial.print(" "); + if (alarmH12Flag) { + if (alarmPmFlag) { + Serial.print("pm"); + } else { + Serial.print("am"); + } + } + if (myRTC.checkAlarmEnabled(2)) { + Serial.print("enabled"); + } + + // display alarm bits + Serial.println(); + Serial.print("Alarm bits: "); + Serial.println(alarmBits, BIN); - Serial.println(); - delay(1000); + Serial.println(); + delay(1000); } diff --git a/examples/DateTime_Contructor_Test/DateTime_Contructor_Test.ino b/examples/DateTime_Contructor_Test/DateTime_Contructor_Test.ino index 04f79e9..9646d9d 100644 --- a/examples/DateTime_Contructor_Test/DateTime_Contructor_Test.ino +++ b/examples/DateTime_Contructor_Test/DateTime_Contructor_Test.ino @@ -1,165 +1,167 @@ #include +#include #include -void showTimeFormated(time_t t) { -#if defined (__AVR__) - t -= 946684800UL; + +void showTimeFormated(time_t t) +{ +#if defined(__AVR__) + t -= 946684800UL; #endif - char buffer[50]; - struct tm *ptm; - ptm = gmtime(&t); - const char * timeformat {"%a %F %X - weekday %w; CW %W"}; - strftime(buffer, sizeof(buffer), timeformat, ptm); - Serial.print(buffer); - Serial.print("\n"); + char buffer[50]; + struct tm *ptm; + ptm = gmtime(&t); + const char *timeformat{"%a %F %X - weekday %w; CW %W"}; + strftime(buffer, sizeof(buffer), timeformat, ptm); + Serial.print(buffer); + Serial.print("\n"); } -constexpr time_t tstmp {1660644000UL}; +constexpr time_t tstmp{1660644000UL}; -RTClib myRTC; -DS3231 Clock; +DS3231::RTClib myRTC; +DS3231::DS3231 Clock; -void setup () { - Serial.begin(115200); - while(!Serial){ - yield(); - } - Serial.println("\n\n\nDS3231 - DateTime Constructor Test()\n"); +void setup() +{ + Serial.begin(115200); + while (!Serial) + { + yield(); + } + Serial.println("\n\n\nDS3231 - DateTime Constructor Test()\n"); -#if defined (__AVR__) +#if defined(__AVR__) #warning using AVR platform - Serial.println("\n\nAVR Microcontroller Ready!\n\n"); - Wire.begin(); + Serial.println("\n\nAVR Microcontroller Ready!\n\n"); + Wire.begin(); -#elif defined (__SAMD21G18A__) +#elif defined(__SAMD21G18A__) #warning using SAMD21 platform - Serial.println("\n\nSAMD21 Microcontroller Ready!\n\n"); - Wire.begin(); + Serial.println("\n\nSAMD21 Microcontroller Ready!\n\n"); + Wire.begin(); -#elif defined (ESP8266) +#elif defined(ESP8266) #warning using espressif platform - Serial.println("\n\nESP8266 Microcontroller Ready!\n\n"); - // SDA = 0, SCL = 2 - Wire.begin(4U, 5U); + Serial.println("\n\nESP8266 Microcontroller Ready!\n\n"); + // SDA = 0, SCL = 2 + Wire.begin(4U, 5U); #endif - - // set the Ds3131 with a specific UnixTimestamp - // ==> Tue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33; yearday 227 - // ==> 1660644000 - - Serial.println("Input Data:"); - Serial.println("\tTue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33, yearday 227"); - Serial.print("\tUnixTimestamp - "); - Serial.println(tstmp); - - // feed UnixTimeStamp and don' t use localtime - Clock.setEpoch(tstmp, false); - // set to 24h - Clock.setClockMode(false); - - // Just for verification of DS3231 Data - // check now the data from ESP8266 and DS3231 - // get year - bool century = false; - bool h12Flag; - bool pmFlag; - - // hole die Werte direkt aus der Uhr(DS3231) - Serial.print("\n\n"); - Serial.println("Get Data from DS3231 clock:"); - Serial.print("\tDateTime of DS3231: "); - Serial.print(Clock.getYear(), DEC); - Serial.print("-"); - Serial.print(Clock.getMonth(century), DEC); - Serial.print("-"); - Serial.print(Clock.getDate(), DEC); - Serial.print(" "); - Serial.print(Clock.getHour(h12Flag, pmFlag), DEC); - Serial.print(":"); - Serial.print(Clock.getMinute(), DEC); - Serial.print(":"); - Serial.print(Clock.getSecond(), DEC); - Serial.print(" - weekday "); - Serial.print(Clock.getDoW(), DEC); - Serial.println(); - - Serial.flush(); - - // Hole die Zeit aus der DateTime Class - //auto tic{micros()}; - DateTime datetime = myRTC.now(); - //auto toc {micros()}; - //Serial.print("\n\nDateTime Class instantiation duration: "); - //Serial.print(toc-tic); - //Serial.println(" µs"); - - Serial.print("\n\n"); - Serial.println("Print data via myRTC.now() constructor"); - Serial.println("Get Data of Struct tm"); - Serial.print("\tDateTime of RTC: "); - Serial.print(datetime.getYear(), DEC); - Serial.print("-"); - Serial.print(datetime.getMonth(), DEC); - Serial.print("-"); - Serial.print(datetime.getDay(), DEC); - Serial.print(" "); - Serial.print(datetime.getHour(), DEC); - Serial.print(":"); - Serial.print(datetime.getMinute(), DEC); - Serial.print(":"); - Serial.print(datetime.getSecond(), DEC); - Serial.print(" - weekday "); - Serial.print(datetime.getWeekDay(), DEC); - Serial.print(" - yearday "); - Serial.print(datetime.getYearDay(), DEC); - Serial.println(); - Serial.print("\tUnixtime: "); - Serial.println(datetime.getUnixTime()); - Serial.print("\tY2k-Time: "); - Serial.println(datetime.getY2kTime()); - - - Serial.print("\n\nPrint Data via function of Struct tm:\n\t"); - showTimeFormated(tstmp); - - Serial.print("\nPrint Data via DataTime function :\n\t"); - char buffer[80]; - datetime.strf_DateTime(buffer, sizeof(buffer)); - Serial.println(buffer); - - //Serial.print("\nPrint __DATE__ and __TIME__:\n"); - //Serial.print(__DATE__); - //Serial.print(" "); - //Serial.println(__TIME__); - - //datetime = DateTime(__DATE__, __TIME__); - datetime = DateTime("Aug 16 2022", "10:00:00"); - Serial.print("\n\n"); - Serial.println("Print data via __Date, __TIME__ constructor"); - Serial.print("Data of Struct tm\n"); - Serial.print("\tDateTime of RTC: "); - Serial.print(datetime.getYear(), DEC); - Serial.print("-"); - Serial.print(datetime.getMonth(), DEC); - Serial.print("-"); - Serial.print(datetime.getDay(), DEC); - Serial.print(" "); - Serial.print(datetime.getHour(), DEC); - Serial.print(":"); - Serial.print(datetime.getMinute(), DEC); - Serial.print(":"); - Serial.print(datetime.getSecond(), DEC); - Serial.print(" - weekday "); - Serial.print(datetime.getWeekDay(), DEC); - Serial.print(" - yearday "); - Serial.print(datetime.getYearDay(), DEC); - Serial.println(); - Serial.print("\tUnixtime: "); - Serial.println(datetime.getUnixTime()); - Serial.print("\tY2k-Time: "); - Serial.println(datetime.getY2kTime()); - + Clock.begin(); + // set the Ds3131 with a specific UnixTimestamp + // ==> Tue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33; yearday 227 + // ==> 1660644000 + + Serial.println("Input Data:"); + Serial.println("\tTue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33, yearday 227"); + Serial.print("\tUnixTimestamp - "); + Serial.println(tstmp); + + // feed UnixTimeStamp and don' t use localtime + Clock.setEpoch(tstmp, false); + + // Just for verification of DS3231 Data + // check now the data from ESP8266 and DS3231 + // get year + bool century = false; + bool h12Flag; + bool pmFlag; + + // hole die Werte direkt aus der Uhr(DS3231) + Serial.print("\n\n"); + Serial.println("Get Data from DS3231 clock:"); + Serial.print("\tDateTime of DS3231: "); + Serial.print(Clock.getYear(), DEC); + Serial.print("-"); + Serial.print(Clock.getMonth(century), DEC); + Serial.print("-"); + Serial.print(Clock.getDate(), DEC); + Serial.print(" "); + Serial.print(Clock.getHour(h12Flag, pmFlag), DEC); + Serial.print(":"); + Serial.print(Clock.getMinute(), DEC); + Serial.print(":"); + Serial.print(Clock.getSecond(), DEC); + Serial.print(" - weekday "); + Serial.print(Clock.getDoW(), DEC); + Serial.println(); + + Serial.flush(); + + // Hole die Zeit aus der DateTime Class + // auto tic{micros()}; + DS3231::DateTime datetime = myRTC.now(); + // auto toc {micros()}; + // Serial.print("\n\nDateTime Class instantiation duration: "); + // Serial.print(toc-tic); + // Serial.println(" µs"); + + Serial.print("\n\n"); + Serial.println("Print data via myRTC.now() constructor"); + Serial.println("Get Data of Struct tm"); + Serial.print("\tDateTime of RTC: "); + Serial.print(datetime.getYear(), DEC); + Serial.print("-"); + Serial.print(datetime.getMonth(), DEC); + Serial.print("-"); + Serial.print(datetime.getDay(), DEC); + Serial.print(" "); + Serial.print(datetime.getHour(), DEC); + Serial.print(":"); + Serial.print(datetime.getMinute(), DEC); + Serial.print(":"); + Serial.print(datetime.getSecond(), DEC); + Serial.print(" - weekday "); + Serial.print(datetime.getWeekDay(), DEC); + Serial.print(" - yearday "); + Serial.print(datetime.getYearDay(), DEC); + Serial.println(); + Serial.print("\tUnixtime: "); + Serial.println(datetime.getUnixTime()); + Serial.print("\tY2k-Time: "); + Serial.println(datetime.getY2kTime()); + + Serial.print("\n\nPrint Data via function of Struct tm:\n\t"); + showTimeFormated(tstmp); + + Serial.print("\nPrint Data via DataTime function :\n\t"); + char buffer[80]; + datetime.strf_DateTime(buffer, sizeof(buffer)); + Serial.println(buffer); + + // Serial.print("\nPrint __DATE__ and __TIME__:\n"); + // Serial.print(__DATE__); + // Serial.print(" "); + // Serial.println(__TIME__); + + // datetime = DateTime(__DATE__, __TIME__); + datetime = DS3231::DateTime("Aug 16 2022", "10:00:00"); + Serial.print("\n\n"); + Serial.println("Print data via __Date, __TIME__ constructor"); + Serial.print("Data of Struct tm\n"); + Serial.print("\tDateTime of RTC: "); + Serial.print(datetime.getYear(), DEC); + Serial.print("-"); + Serial.print(datetime.getMonth(), DEC); + Serial.print("-"); + Serial.print(datetime.getDay(), DEC); + Serial.print(" "); + Serial.print(datetime.getHour(), DEC); + Serial.print(":"); + Serial.print(datetime.getMinute(), DEC); + Serial.print(":"); + Serial.print(datetime.getSecond(), DEC); + Serial.print(" - weekday "); + Serial.print(datetime.getWeekDay(), DEC); + Serial.print(" - yearday "); + Serial.print(datetime.getYearDay(), DEC); + Serial.println(); + Serial.print("\tUnixtime: "); + Serial.println(datetime.getUnixTime()); + Serial.print("\tY2k-Time: "); + Serial.println(datetime.getY2kTime()); } -void loop () { +void loop() +{ } \ No newline at end of file diff --git a/examples/echo_time/echo_time.ino b/examples/echo_time/echo_time.ino index 3e7b56e..e7af943 100644 --- a/examples/echo_time/echo_time.ino +++ b/examples/echo_time/echo_time.ino @@ -16,18 +16,19 @@ Andy Wickert #include #include -DS3231 myRTC; +DS3231::DS3231 myRTC; bool century = false; bool h12Flag; bool pmFlag; void setup() { - // Start the serial port - Serial.begin(57600); + // Start the serial port + Serial.begin(57600); - // Start the I2C interface - Wire.begin(); + // Start the I2C interface + Wire.begin(); + myRTC.begin(); for (int i=0; i<5; i++){ diff --git a/examples/getAXTimeTest/getAXTimeTest.ino b/examples/getAXTimeTest/getAXTimeTest.ino index c31da36..339d42e 100644 --- a/examples/getAXTimeTest/getAXTimeTest.ino +++ b/examples/getAXTimeTest/getAXTimeTest.ino @@ -19,61 +19,63 @@ Tested on: #include // Setup clock -DS3231 myRTC; +DS3231::DS3231 myRTC; void setup() { - // Begin I2C communication - Wire.begin(); - - // Begin Serial communication - Serial.begin(57600); - - // Setup alarm one to fire every minute - // No need to turn Alarm one on. - myRTC.turnOffAlarm(1); - myRTC.setA1Time(0, 0, 0, 0, 0b01111110, false, false, false); - myRTC.checkIfAlarm(1); - - // Setup alarm two to fire every minute - // No need to turn Alarm two on. - myRTC.turnOffAlarm(2); - myRTC.setA2Time(0, 0, 0, 0b01111110, false, false, false); - myRTC.checkIfAlarm(2); + // Begin I2C communication + Wire.begin(); + + // Begin Serial communication + Serial.begin(57600); + + myRTC.begin(); + + // Setup alarm one to fire every minute + // No need to turn Alarm one on. + myRTC.turnOffAlarm(1); + myRTC.setA1Time(0, 0, 0, 0, 0b01111110, false, false, false); + myRTC.checkIfAlarm(1); + + // Setup alarm two to fire every minute + // No need to turn Alarm two on. + myRTC.turnOffAlarm(2); + myRTC.setA2Time(0, 0, 0, 0b01111110, false, false, false); + myRTC.checkIfAlarm(2); } void loop() { - // Initialize AlarmBits - byte AlarmBits = 0x0; - Serial.print("Initialize AlarmBits: "); - Serial.println(AlarmBits, BIN); - - // Initialize Others - byte ADay, AHour, AMinute, ASecond; - bool ADy, Ah12, APM; - - // getA1Time (not clearing). - // Expected AlarmBits = 0x(0000)1110 - myRTC.getA1Time(ADay, AHour, AMinute, ASecond, AlarmBits, ADy, Ah12, APM); - Serial.print("getA1Time(): "); - Serial.println(AlarmBits, BIN); - - // getA2Time (not clearing). - // Expected AlarmBits = 0x01111110 - myRTC.getA2Time(ADay, AHour, AMinute, AlarmBits, ADy, Ah12, APM); - Serial.print("getA2Time(): "); - Serial.println(AlarmBits, BIN); - - // getA1Time (clearing). - // Expected AlarmBits = 0x(0000)1110 - myRTC.getA1Time(ADay, AHour, AMinute, ASecond, AlarmBits, ADy, Ah12, APM, true); - Serial.print("getA1Time(): "); - Serial.println(AlarmBits, BIN); - - // getA2Time (clearing). - // Expected AlarmBits = 0x01110000 - myRTC.getA2Time(ADay, AHour, AMinute, AlarmBits, ADy, Ah12, APM, true); - Serial.print("getA2Time(): "); - Serial.println(AlarmBits, BIN); - delay(5000); + // Initialize AlarmBits + byte AlarmBits = 0x0; + Serial.print("Initialize AlarmBits: "); + Serial.println(AlarmBits, BIN); + + // Initialize Others + byte ADay, AHour, AMinute, ASecond; + bool ADy, Ah12, APM; + + // getA1Time (not clearing). + // Expected AlarmBits = 0x(0000)1110 + myRTC.getA1Time(ADay, AHour, AMinute, ASecond, AlarmBits, ADy, Ah12, APM); + Serial.print("getA1Time(): "); + Serial.println(AlarmBits, BIN); + + // getA2Time (not clearing). + // Expected AlarmBits = 0x01111110 + myRTC.getA2Time(ADay, AHour, AMinute, AlarmBits, ADy, Ah12, APM); + Serial.print("getA2Time(): "); + Serial.println(AlarmBits, BIN); + + // getA1Time (clearing). + // Expected AlarmBits = 0x(0000)1110 + myRTC.getA1Time(ADay, AHour, AMinute, ASecond, AlarmBits, ADy, Ah12, APM, true); + Serial.print("getA1Time(): "); + Serial.println(AlarmBits, BIN); + + // getA2Time (clearing). + // Expected AlarmBits = 0x01110000 + myRTC.getA2Time(ADay, AHour, AMinute, AlarmBits, ADy, Ah12, APM, true); + Serial.print("getA2Time(): "); + Serial.println(AlarmBits, BIN); + delay(5000); } diff --git a/examples/now/now.ino b/examples/now/now.ino index 2925b89..1034d49 100644 --- a/examples/now/now.ino +++ b/examples/now/now.ino @@ -6,37 +6,37 @@ #include #include -RTClib myRTC; +DS3231::RTClib myRTC; void setup () { - Serial.begin(57600); - Wire.begin(); - delay(500); - Serial.println("Nano Ready!"); + Serial.begin(57600); + Wire.begin(); + delay(500); + Serial.println("Nano Ready!"); } void loop () { - - delay(1000); - - DateTime now = myRTC.now(); - - Serial.print(now.getYear(), DEC); - Serial.print('/'); - Serial.print(now.getMonth(), DEC); - Serial.print('/'); - Serial.print(now.getDay(), DEC); - Serial.print(' '); - Serial.print(now.getHour(), DEC); - Serial.print(':'); - Serial.print(now.getMinute(), DEC); - Serial.print(':'); - Serial.print(now.getSecond(), DEC); - Serial.println(); - - Serial.print(" since midnight 1/1/1970 = "); - Serial.print(now.getUnixTime()); - Serial.print("s = "); - Serial.print(now.getUnixTime() / 86400L); - Serial.println("d"); + + delay(1000); + + DS3231::DateTime now = myRTC.now(); + + Serial.print(now.getYear(), DEC); + Serial.print('/'); + Serial.print(now.getMonth(), DEC); + Serial.print('/'); + Serial.print(now.getDay(), DEC); + Serial.print(' '); + Serial.print(now.getHour(), DEC); + Serial.print(':'); + Serial.print(now.getMinute(), DEC); + Serial.print(':'); + Serial.print(now.getSecond(), DEC); + Serial.println(); + + Serial.print(" since midnight 1/1/1970 = "); + Serial.print(now.getUnixTime()); + Serial.print("s = "); + Serial.print(now.getUnixTime() / 86400L); + Serial.println("d"); } diff --git a/examples/setEpoch/setEpoch.ino b/examples/setEpoch/setEpoch.ino index 915146f..8a1eb82 100644 --- a/examples/setEpoch/setEpoch.ino +++ b/examples/setEpoch/setEpoch.ino @@ -1,120 +1,121 @@ #include #include -void showTimeFormated(time_t t) { -#if defined (__AVR__) - t -= 946684800UL; +void showTimeFormated(time_t t) +{ +#if defined(__AVR__) + t -= 946684800UL; #endif - char buffer[50]; - struct tm *ptm; - ptm = gmtime (&t); - const char * timeformat {"%a %F %X - weekday %w; CW %W"}; - strftime(buffer, sizeof(buffer), timeformat, ptm); - Serial.print(buffer); - Serial.print("\n"); + char buffer[50]; + struct tm *ptm; + ptm = gmtime(&t); + const char *timeformat{"%a %F %X - weekday %w; CW %W"}; + strftime(buffer, sizeof(buffer), timeformat, ptm); + Serial.print(buffer); + Serial.print("\n"); } // unix timestamp of: Tue Aug 16 2022 10:00:00 GMT+0000 -constexpr time_t tstmp {1660644000UL}; +constexpr time_t timestamp{1660644000UL}; -RTClib myRTC; -DS3231 Clock; +DS3231::RTClib myRTC; +DS3231::DS3231 Clock; -void setup () { - Serial.begin(115200); - Wire.begin(); - delay(500); - Serial.println("\n\n\nTest of DS3231 - setEpoch()\n\n\n"); +void setup() +{ + Serial.begin(115200); + Wire.begin(); + Clock.begin(); + delay(500); + Serial.println("\n\n\nTest of DS3231 - setEpoch()\n\n\n"); -#if defined (__AVR__) +#if defined(__AVR__) #warning using AVR platform - Serial.println("\n\nAVR Microcontroller Ready!\n\n"); - Wire.begin(); + Serial.println("\n\nAVR Microcontroller Ready!\n\n"); + Wire.begin(); -#elif defined (__SAMD21G18A__) +#elif defined(__SAMD21G18A__) #warning using SAMD21 platform - Serial.println("\n\nSAMD21 Microcontroller Ready!\n\n"); - Wire.begin(); + Serial.println("\n\nSAMD21 Microcontroller Ready!\n\n"); + Wire.begin(); -#elif defined (ESP8266) +#elif defined(ESP8266) #warning using espressif platform - Serial.println("\n\nESP8266 Microcontroller Ready!\n\n"); - // SDA, SCL - Wire.begin(SDA, SCL); + Serial.println("\n\nESP8266 Microcontroller Ready!\n\n"); + // SDA, SCL + Wire.begin(SDA, SCL); #endif - // set the DS3231 with a specific UnixTimestamp - // ==> Tue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33 - // ==> 1660644000 - - Serial.println("Tue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33"); - Serial.println("UnixTimestamp - 1660644000"); - - - // feed UnixTimeStamp and don' t use localtime - Clock.setEpoch(tstmp, false); - // set to 24h - Clock.setClockMode(false); - - // Just for verification of DS3231 Data - // check now the data from ESP8266 and DS3231 - // get year - bool century = false; - bool h12Flag; - bool pmFlag; - - // read directly from DS3231 module - Serial.print("\n\n"); - Serial.print(" DateTime of DS3231: "); - Serial.print(Clock.getYear(), DEC); - Serial.print("-"); - Serial.print(Clock.getMonth(century), DEC); - Serial.print("-"); - Serial.print(Clock.getDate(), DEC); - Serial.print(" "); - Serial.print(Clock.getHour(h12Flag, pmFlag), DEC); - Serial.print(":"); - Serial.print(Clock.getMinute(), DEC); - Serial.print(":"); - Serial.print(Clock.getSecond(), DEC); - Serial.print(" - weekday "); - Serial.print(Clock.getDoW(), DEC); - Serial.println(); - - // Read now from DateTime class - DateTime datetime = myRTC.now(); - Serial.print("\n\nData of Struct tm\n"); - Serial.print(" DateTime of RTC: "); - Serial.print(datetime.getYear(), DEC); - Serial.print("-"); - Serial.print(datetime.getMonth(), DEC); - Serial.print("-"); - Serial.print(datetime.getDay(), DEC); - Serial.print(" "); - Serial.print(datetime.getHour(), DEC); - Serial.print(":"); - Serial.print(datetime.getMinute(), DEC); - Serial.print(":"); - Serial.print(datetime.getSecond(), DEC); - Serial.print(" - weekday "); - Serial.print(datetime.getWeekDay(), DEC); - Serial.println(); - Serial.print(" Unixtime: "); - Serial.println(datetime.getUnixTime()); - Serial.print(" Y2k-Time: "); - Serial.println(datetime.getY2kTime()); - - Serial.print("\n\n Output of Struct tm: "); - // Use above defined function - showTimeFormated(tstmp); - - // Use smart print function from DateTime class - Serial.print("\nUse strf_DateTime function:\n "); - // provide a buffer - char buffer[80]; - datetime.strf_DateTime(buffer, sizeof(buffer)); - Serial.println(buffer); + // set the DS3231 with a specific UnixTimestamp + // ==> Tue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33 + // ==> 1660644000 + + Serial.println("Tue Aug 16 2022 10:00:00 GMT+0000 - weekday 2 (0 = Sunday); CW 33"); + Serial.println("UnixTimestamp - 1660644000"); + + // feed UnixTimeStamp and don' t use localtime + Clock.setEpoch(timestamp, false); + + // Just for verification of DS3231 Data + // check now the data from ESP8266 and DS3231 + // get year + bool century = false; + bool h12Flag; + bool pmFlag; + + // read directly from DS3231 module + Serial.print("\n\n"); + Serial.print(" DateTime of DS3231: "); + Serial.print(Clock.getYear(), DEC); + Serial.print("-"); + Serial.print(Clock.getMonth(century), DEC); + Serial.print("-"); + Serial.print(Clock.getDate(), DEC); + Serial.print(" "); + Serial.print(Clock.getHour(h12Flag, pmFlag), DEC); + Serial.print(":"); + Serial.print(Clock.getMinute(), DEC); + Serial.print(":"); + Serial.print(Clock.getSecond(), DEC); + Serial.print(" - weekday "); + Serial.print(Clock.getDoW(), DEC); + Serial.println(); + + // Read now from DateTime class + DS3231::DateTime datetime = myRTC.now(); + Serial.print("\n\nData of Struct tm\n"); + Serial.print(" DateTime of RTC: "); + Serial.print(datetime.getYear(), DEC); + Serial.print("-"); + Serial.print(datetime.getMonth(), DEC); + Serial.print("-"); + Serial.print(datetime.getDay(), DEC); + Serial.print(" "); + Serial.print(datetime.getHour(), DEC); + Serial.print(":"); + Serial.print(datetime.getMinute(), DEC); + Serial.print(":"); + Serial.print(datetime.getSecond(), DEC); + Serial.print(" - weekday "); + Serial.print(datetime.getWeekDay(), DEC); + Serial.println(); + Serial.print(" Unixtime: "); + Serial.println(datetime.getUnixTime()); + Serial.print(" Y2k-Time: "); + Serial.println(datetime.getY2kTime()); + + Serial.print("\n\n Output of Struct tm: "); + // Use above defined function + showTimeFormated(timestamp); + + // Use smart print function from DateTime class + Serial.print("\nUse strf_DateTime function:\n "); + // provide a buffer + char buffer[80]; + datetime.strf_DateTime(buffer, sizeof(buffer)); + Serial.println(buffer); } -void loop () { +void loop() +{ } diff --git a/examples/set_echo/set_echo.ino b/examples/set_echo/set_echo.ino index 0ecdb3b..833b091 100644 --- a/examples/set_echo/set_echo.ino +++ b/examples/set_echo/set_echo.ino @@ -21,7 +21,9 @@ Olivier Staquet #include #include -DS3231 myRTC; +void inputDateFromSerial(void); + +DS3231::DS3231 myRTC; byte year; byte month; @@ -41,25 +43,26 @@ bool pmFlag; * - Explain to the user how to use the program *****************************************************************************************************/ void setup() { - // Start the serial port - Serial.begin(57600); - - // Start the I2C interface - Wire.begin(); - - // Request the time correction on the Serial - delay(4000); - Serial.println("Format YYMMDDwhhmmssx"); - Serial.println("Where YY = Year (ex. 20 for 2020)"); - Serial.println(" MM = Month (ex. 04 for April)"); - Serial.println(" DD = Day of month (ex. 09 for 9th)"); - Serial.println(" w = Day of week from 1 to 7, 1 = Sunday (ex. 5 for Thursday)"); - Serial.println(" hh = hours in 24h format (ex. 09 for 9AM or 21 for 9PM)"); - Serial.println(" mm = minutes (ex. 02)"); - Serial.println(" ss = seconds (ex. 42)"); - Serial.println("Example for input : 2004095090242x"); - Serial.println("-----------------------------------------------------------------------------"); - Serial.println("Please enter the current time to set on DS3231 ended by 'x':"); + // Start the serial port + Serial.begin(57600); + + // Start the I2C interface + Wire.begin(); + myRTC.begin(); + + // Request the time correction on the Serial + delay(4000); + Serial.println("Format YYMMDDwhhmmssx"); + Serial.println("Where YY = Year (ex. 20 for 2020)"); + Serial.println(" MM = Month (ex. 04 for April)"); + Serial.println(" DD = Day of month (ex. 09 for 9th)"); + Serial.println(" w = Day of week from 1 to 7, 1 = Sunday (ex. 5 for Thursday)"); + Serial.println(" hh = hours in 24h format (ex. 09 for 9AM or 21 for 9PM)"); + Serial.println(" mm = minutes (ex. 02)"); + Serial.println(" ss = seconds (ex. 42)"); + Serial.println("Example for input : 2004095090242x"); + Serial.println("-----------------------------------------------------------------------------"); + Serial.println("Please enter the current time to set on DS3231 ended by 'x':"); } /***************************************************************************************************** @@ -69,41 +72,39 @@ void setup() { * - Echo the value from the DS3231 during 5 seconds *****************************************************************************************************/ void loop() { - // If something is coming in on the serial line, it's - // a time correction so set the clock accordingly. - if (Serial.available()) { - inputDateFromSerial(); - - myRTC.setClockMode(false); // set to 24h - - myRTC.setYear(year); - myRTC.setMonth(month); - myRTC.setDate(date); - myRTC.setDoW(dow); - myRTC.setHour(hour); - myRTC.setMinute(minute); - myRTC.setSecond(second); - - // Give time at next five seconds - for (uint8_t i = 0; i < 5; i++){ - delay(1000); - Serial.print(myRTC.getYear(), DEC); - Serial.print("-"); - Serial.print(myRTC.getMonth(century), DEC); - Serial.print("-"); - Serial.print(myRTC.getDate(), DEC); - Serial.print(" "); - Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC); //24-hr - Serial.print(":"); - Serial.print(myRTC.getMinute(), DEC); - Serial.print(":"); - Serial.println(myRTC.getSecond(), DEC); - } - - // Notify that we are ready for the next input - Serial.println("Please enter the current time to set on DS3231 ended by 'x':"); - } - delay(1000); + // If something is coming in on the serial line, it's + // a time correction so set the clock accordingly. + if (Serial.available()) { + inputDateFromSerial(); + + myRTC.setYear(year); + myRTC.setMonth(month); + myRTC.setDate(date); + myRTC.setDoW(dow); + myRTC.setHour(hour); + myRTC.setMinute(minute); + myRTC.setSecond(second); + + // Give time at next five seconds + for (uint8_t i = 0; i < 5; i++){ + delay(1000); + Serial.print(myRTC.getYear(), DEC); + Serial.print("-"); + Serial.print(myRTC.getMonth(century), DEC); + Serial.print("-"); + Serial.print(myRTC.getDate(), DEC); + Serial.print(" "); + Serial.print(myRTC.getHour(h12Flag, pmFlag), DEC); //24-hr + Serial.print(":"); + Serial.print(myRTC.getMinute(), DEC); + Serial.print(":"); + Serial.println(myRTC.getSecond(), DEC); + } + + // Notify that we are ready for the next input + Serial.println("Please enter the current time to set on DS3231 ended by 'x':"); + } + delay(1000); } /***************************************************************************************************** @@ -112,28 +113,28 @@ void loop() { * - Store the data in global variables *****************************************************************************************************/ void inputDateFromSerial() { - // Call this if you notice something coming in on - // the serial port. The stuff coming in should be in - // the order YYMMDDwHHMMSS, with an 'x' at the end. - boolean isStrComplete = false; - char inputChar; - byte temp1, temp2; - char inputStr[20]; - - uint8_t currentPos = 0; - while (!isStrComplete) { - if (Serial.available()) { - inputChar = Serial.read(); - inputStr[currentPos] = inputChar; - currentPos += 1; + // Call this if you notice something coming in on + // the serial port. The stuff coming in should be in + // the order YYMMDDwHHMMSS, with an 'x' at the end. + boolean isStrComplete = false; + char inputChar; + byte temp1, temp2; + char inputStr[20]; + + uint8_t currentPos = 0; + while (!isStrComplete) { + if (Serial.available()) { + inputChar = Serial.read(); + inputStr[currentPos] = inputChar; + currentPos += 1; // Check if string complete (end with "x") - if (inputChar == 'x') { - isStrComplete = true; - } - } - } - Serial.println(inputStr); + if (inputChar == 'x') { + isStrComplete = true; + } + } + } + Serial.println(inputStr); // Find the end of char "x" int posX = -1; @@ -147,36 +148,36 @@ void inputDateFromSerial() { // Consider 0 character in ASCII uint8_t zeroAscii = '0'; - // Read Year first - temp1 = (byte)inputStr[posX - 13] - zeroAscii; - temp2 = (byte)inputStr[posX - 12] - zeroAscii; - year = temp1 * 10 + temp2; - - // now month - temp1 = (byte)inputStr[posX - 11] - zeroAscii; - temp2 = (byte)inputStr[posX - 10] - zeroAscii; - month = temp1 * 10 + temp2; - - // now date - temp1 = (byte)inputStr[posX - 9] - zeroAscii; - temp2 = (byte)inputStr[posX - 8] - zeroAscii; - date = temp1 * 10 + temp2; - - // now Day of Week - dow = (byte)inputStr[posX - 7] - zeroAscii; - - // now Hour - temp1 = (byte)inputStr[posX - 6] - zeroAscii; - temp2 = (byte)inputStr[posX - 5] - zeroAscii; - hour = temp1 * 10 + temp2; - - // now Minute - temp1 = (byte)inputStr[posX - 4] - zeroAscii; - temp2 = (byte)inputStr[posX - 3] - zeroAscii; - minute = temp1 * 10 + temp2; - - // now Second - temp1 = (byte)inputStr[posX - 2] - zeroAscii; - temp2 = (byte)inputStr[posX - 1] - zeroAscii; - second = temp1 * 10 + temp2; + // Read Year first + temp1 = (byte)inputStr[posX - 13] - zeroAscii; + temp2 = (byte)inputStr[posX - 12] - zeroAscii; + year = temp1 * 10 + temp2; + + // now month + temp1 = (byte)inputStr[posX - 11] - zeroAscii; + temp2 = (byte)inputStr[posX - 10] - zeroAscii; + month = temp1 * 10 + temp2; + + // now date + temp1 = (byte)inputStr[posX - 9] - zeroAscii; + temp2 = (byte)inputStr[posX - 8] - zeroAscii; + date = temp1 * 10 + temp2; + + // now Day of Week + dow = (byte)inputStr[posX - 7] - zeroAscii; + + // now Hour + temp1 = (byte)inputStr[posX - 6] - zeroAscii; + temp2 = (byte)inputStr[posX - 5] - zeroAscii; + hour = temp1 * 10 + temp2; + + // now Minute + temp1 = (byte)inputStr[posX - 4] - zeroAscii; + temp2 = (byte)inputStr[posX - 3] - zeroAscii; + minute = temp1 * 10 + temp2; + + // now Second + temp1 = (byte)inputStr[posX - 2] - zeroAscii; + temp2 = (byte)inputStr[posX - 1] - zeroAscii; + second = temp1 * 10 + temp2; } diff --git a/keywords.txt b/keywords.txt index cdacdb7..52aec10 100644 --- a/keywords.txt +++ b/keywords.txt @@ -1,45 +1,58 @@ # Syntax Coloring Map For DS3231-RTC Library +# ================================= # Datatypes (KEYWORD1) -DS3231 KEYWORD1 -RTClib KEYWORD1 -DateTime KEYWORD1 +# ================================= +DS3231 KEYWORD1 +RTClib KEYWORD1 +DateTime KEYWORD1 +# ================================= # Methods and Functions (KEYWORD2) -now KEYWORD2 -getY2kTime KEYWORD2 -getUnixTime KEYWORD2 -strf_DateTime KEYWORD2 -getWeekDay KEYWORD2 -getDST KEYWORD2 -getSecond KEYWORD2 -getMinute KEYWORD2 -getHour KEYWORD2 -getDoW KEYWORD2 -getDate KEYWORD2 -getMonth KEYWORD2 -getYear KEYWORD2 -setSecond KEYWORD2 -setMinute KEYWORD2 -setHour KEYWORD2 -setDoW KEYWORD2 -setDate KEYWORD2 -setMonth KEYWORD2 -setYear KEYWORD2 -setClockMode KEYWORD2 -getTemperature KEYWORD2 -getA1Time KEYWORD2 -getA2Time KEYWORD2 -setA1Time KEYWORD2 -setA2Time KEYWORD2 -turnOnAlarm KEYWORD2 -turnOffAlarm KEYWORD2 +# ================================= +now KEYWORD2 +getY2kTime KEYWORD2 +getUnixTime KEYWORD2 +strf_DateTime KEYWORD2 +getWeekDay KEYWORD2 +getDST KEYWORD2 +getSecond KEYWORD2 +getMinute KEYWORD2 +getHour KEYWORD2 +getDoW KEYWORD2 +getDate KEYWORD2 +getDay KEYWORD2 +getMonth KEYWORD2 +getYear KEYWORD2 +getYearDay KEYWORD2 +setEpoch KEYWORD2 +setSecond KEYWORD2 +setMinute KEYWORD2 +setHour KEYWORD2 +setDoW KEYWORD2 +setDate KEYWORD2 +setMonth KEYWORD2 +setYear KEYWORD2 +set12hourMode KEYWORD2 +set24hourMode KEYWORD2 +begin KEYWORD2 +is24hourModeActive KEYWORD2 +getTemperature KEYWORD2 +getA1Time KEYWORD2 +getA2Time KEYWORD2 +setA1Time KEYWORD2 +setA2Time KEYWORD2 +turnOnAlarm KEYWORD2 +turnOffAlarm KEYWORD2 checkAlarmEnabled KEYWORD2 checkIfAlarm KEYWORD2 enableOscillator KEYWORD2 enable32kHz KEYWORD2 oscillatorCheck KEYWORD2 +# ================================= # Constants (LITERAL1) -UNIX_OFFSET LITERAL1 -NTP_OFFSET LITERAL1 +# ================================= +DS3231_Constants::DS3231_I2C_ADDRESS LITERAL1 +DS3231_Constants::UNIX_OFFSET LITERAL1 +DS3231_Constants::NTP_OFFSET LITERAL1 diff --git a/library.json b/library.json index c491e80..ab38ca8 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "DS3231-RTC", - "version": "1.1.0", + "version": "2.0.0", "repository": { "type": "git", diff --git a/library.properties b/library.properties index a459ea8..6a1e8e0 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=DS3231-RTC -version=1.1.0 +version=2.0.0 author=Frank Häfele maintainer=Frank Häfele sentence=C++ Library for the DS3231 real-time clock (RTC) module, ready to use on Arduino IDE and PlatformIO. diff --git a/src/DS3231-RTC.cpp b/src/DS3231-RTC.cpp old mode 100644 new mode 100755 index 8d503dd..3a8764b --- a/src/DS3231-RTC.cpp +++ b/src/DS3231-RTC.cpp @@ -1,895 +1,656 @@ -/* - DS3231.cpp: DS3231 Real-Time Clock library -*/ - -#include "DS3231-RTC.h" - -// These included for the DateTime class inclusion; will try to find a way to -// not need them in the future... -#if defined(__AVR__) -#include -#elif defined(ESP8266) -#include -#endif - -// ***************************************** -// Static Functions only used in this file -// ***************************************** - -static const uint8_t daysInMonth[] PROGMEM = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - -/** - * @brief function which calculates if a year is a leap year - * - * @param year - * @return true - * @return false - */ -static bool isleapYear(const int16_t year) { - // check if divisible by 4 - if(year % 4) { - return false; - } - // only check other, when first failed - return (year % 100 || year % 400 == 0); -} - -/** - * @brief calculate the days since January 1 (0...365) - * - * @param year e.g.: 2022 - * @param month 1...12 - * @param day 1...31 - * @return int16_t - */ -static int16_t calcYearDay(const int16_t year, const int8_t month, const int8_t day) { - uint16_t days = day - 1; - for (uint8_t i = 1; i < month; ++i) - days += pgm_read_byte(daysInMonth + i - 1); - if (month > 2 && isleapYear(year)) - ++days; - return days; -} - -// Slightly modified from JeeLabs / Ladyada -// Get all date/time at once to avoid rollover (e.g., minute/second don't match) -// Commented to avoid compiler warnings, but keeping in case we want this -// eventually -// static uint8_t bin2bcd (uint8_t val) { return val + 6 * (val / 10); } -static uint8_t bcd2bin (uint8_t val) { - return val - 6 * (val >> 4); -} - - -// ***************************************** -// Member functions for DateTime object -// ***************************************** -/** - * @brief Construct a new Date Time:: Date Time object - * - * @param timestamp - */ -DateTime::DateTime (time_t unix_timestamp) -: _unix_timestamp{unix_timestamp}, _y2k_timestamp{unix_timestamp - UNIX_OFFSET} -{ - gmtime_r(&_unix_timestamp, &_tm); -} - -/** - * @brief Construct a new Date Time:: Date Time object - * - * @param year year e.g. 2022 - * @param month months since January - [ 1...12 ] - * @param day day of the month - [ 1...31 ] - * @param hour hours since midnight - [ 0...23 ] - * @param min inutes after the hour - [ 0...59 ] - * @param sec seconds after the minute - [ 0...59 ] - * @param wday wdays since Sunday - [ 1...7 ] - * @param dst Daylight Saving Time flag - */ -DateTime::DateTime(int16_t year, int8_t month, int8_t day, int8_t hour, int8_t min, int8_t sec, int8_t wday, int16_t yday, int16_t dst) -{ - _tm.tm_sec = sec; - _tm.tm_min = min; - _tm.tm_hour = hour; - _tm.tm_mday = day; - _tm.tm_mon = month-1; - _tm.tm_year = year-1900; - _tm.tm_wday = wday-1; - _tm.tm_yday = yday; - _tm.tm_isdst = dst; - - set_timstamps(); -} - -/** - * @brief Construct a new Date Time:: Date Time object by givin the precompiler marcos - * as __DATE__ and __TIME__ - * - * @param date as Mmm dd yyyy (e.g. "Jan 14 2012") - * @param time as HH:MM:SS (e.g. "23:59:01") - */ -DateTime::DateTime(const char *date, const char *time) { - static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; - static char month_buff[4] = {'0','0','0','0'}; - int year, day; - sscanf(date, "%s %2d %4d", month_buff, &day, &year); - int month = (strstr(month_names, month_buff) - month_names) / 3 + 1; - _tm.tm_year = year-1900; - _tm.tm_mon = month-1; - _tm.tm_mday = day; - byte hour, min, sec; - sscanf(time, "%hhu:%hhu:%hhu", &hour, &min, &sec); - _tm.tm_hour = hour; - _tm.tm_min = min; - _tm.tm_sec = sec; - _tm.tm_yday = calcYearDay(year, month, day); - set_timstamps(); -} - -/** - * @brief Set the timestamps by using struct tm entries - * - */ -void DateTime::set_timstamps() { -#if defined (__AVR__) - _y2k_timestamp = mktime(&_tm); - _unix_timestamp = _y2k_timestamp + UNIX_OFFSET; -#else - _unix_timestamp = mktime(&_tm); - _y2k_timestamp = _unix_timestamp - UNIX_OFFSET; -#endif -} - -/** - * @brief function to format a DateTime string in an buffer based on the standard strftime function - * - * see: https://cplusplus.com/reference/ctime/strftime/ - * or: https://en.cppreference.com/w/cpp/chrono/c/strftime - * - * @param buffer buffer for time string - * @param buffersize size of buffer - * @param formatSpec define format see strftime - * @return size_t length of used buffer - */ -size_t DateTime::strf_DateTime(char *buffer, size_t buffersize, const char *formatSpec) { - size_t len {strftime(buffer, buffersize, formatSpec, &_tm)}; - return len; -} - -// ***************************************** -// Member functions for RTClib object -// ***************************************** - -DateTime RTClib::now(TwoWire & _Wire) { - // This is the first register address (Seconds) - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0); - // We'll read from here on for 7 bytes from registers: - // seconds, minutes, hours, day(1...7), date(1...31), month, year. - _Wire.endTransmission(); - - _Wire.requestFrom(CLOCK_ADDRESS, 7); - int8_t sec = bcd2bin(_Wire.read() & 0x7F); - int8_t min = bcd2bin(_Wire.read()); - int8_t hour = bcd2bin(_Wire.read()); - int8_t wday = bcd2bin(_Wire.read())-1; - int8_t day = bcd2bin(_Wire.read()); - int8_t month = bcd2bin(_Wire.read()); - int16_t year = bcd2bin(_Wire.read()) + 2000; - int16_t yday = calcYearDay(year, month, day); - int16_t dst = -1; - - // REMARK: add DST calculation if needed, but therefore timezone info is needed! - // use the complete set also yearday and dst for having a complete struct tm - return DateTime{year, month, day, hour, min, sec, wday, yday, dst}; -} - - -// ***************************************** -// Member functions for DS3231 object -// ***************************************** - -/** - * @brief Construct a new DS3231::DS3231 object - * initialize the internal _Wire with the Wire object - */ -DS3231::DS3231() : _Wire(Wire) { - // nothing to do for this constructor. -} - -/** - * @brief Construct a new DS3231::DS3231 object - * - * @param w reference of twoWire - */ -DS3231::DS3231(TwoWire &twowire) : _Wire(twowire) { -} - -/** -* @brief Get the second of the DS3231 module -* -* @return byte 0...59 -*/ -byte DS3231::getSecond() { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x00); - _Wire.endTransmission(); - return getRegisterValue(); -} - -/** - * @brief Get the minute of the DS3231 module - * - * @return byte 0...59 - */ -byte DS3231::getMinute() { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x01); - _Wire.endTransmission(); - return getRegisterValue(); -} - -/** - * @brief Get the hour of the DS3231 module - * - * @param h12 - * @param PM_time - * @return byte 1...12 / 0...23 - */ -byte DS3231::getHour(bool& h12, bool& PM_time) { - byte temp_buffer; - byte hour; - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x02); - _Wire.endTransmission(); - - _Wire.requestFrom(CLOCK_ADDRESS, 1); - temp_buffer = _Wire.read(); - h12 = temp_buffer & 0b01000000; - if (h12) { - PM_time = temp_buffer & 0b00100000; - hour = bcdToDec(temp_buffer & 0b00011111); - } - else { - hour = bcdToDec(temp_buffer & 0b00111111); - } - return hour; -} - - -/** - * @brief Get the DayOfWeek of the DS3231 module - * - * @return byte 1...7 - */ -byte DS3231::getDoW() { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x03); - _Wire.endTransmission(); - return getRegisterValue(); -} - -/** - * @brief Get the date of the DS3231 module - * - * @return byte 1...31 - */ -byte DS3231::getDate() { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x04); - _Wire.endTransmission(); - return getRegisterValue(); -} - -/** - * @brief Get the month and the century roll over bit of the DS3231 module - * - * @param century reference of century bit; toggles when value changes from 99 -> 00 - * @return byte - */ -byte DS3231::getMonth(bool ¢ury) { - byte temp_buffer; - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x05); - _Wire.endTransmission(); - - _Wire.requestFrom(CLOCK_ADDRESS, 1); - temp_buffer = _Wire.read(); - century = temp_buffer & 0b10000000; - return (bcdToDec(temp_buffer & 0b01111111)); -} - -/** - * @brief Get the Year of the DS3231 module - * - * @return byte 0...99 - */ -byte DS3231::getYear() { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x06); - _Wire.endTransmission(); - return getRegisterValue(); -} - -/** - * @brief Set the DS3231 module by a given epoch as unix-epoch - * epoch = UnixTime and starts at 01.01.1970 00:00:00 - * HINT: => the AVR time.h lib is based on the year 2000 - * - * @param epoch time_t timestamp of unix epoch - * @param flag_localtime flag if timestamp is based on local time - */ -void DS3231::setEpoch(time_t epoch, bool flag_localtime) { -#if defined (__AVR__) - epoch -= UNIX_OFFSET; -#endif - struct tm tmnow; - if (flag_localtime) { - localtime_r(&epoch, &tmnow); - } - else { - gmtime_r(&epoch, &tmnow); - } - setSecond(tmnow.tm_sec); - setMinute(tmnow.tm_min); - setHour(tmnow.tm_hour); - setDoW(tmnow.tm_wday + 1U); - setDate(tmnow.tm_mday); - setMonth(tmnow.tm_mon + 1U); - setYear(tmnow.tm_year - 100U); -} - -/** - * @brief Set the second of the DS3231 module - * This function also resets the Oscillator Stop Flag, which is set - * whenever power is interrupted. - * @param second 0...59 - */ -void DS3231::setSecond(byte second) { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x00); - _Wire.write(decToBcd(second)); - _Wire.endTransmission(); - // Clear OSF flag - byte temp_buffer = readControlByte(1); - writeControlByte((temp_buffer & 0b01111111), 1); -} - -/** - * @brief Set the Minute of the DS3231 module - * - * @param minute 0...59 - */ -void DS3231::setMinute(byte minute) { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x01); - _Wire.write(decToBcd(minute)); - _Wire.endTransmission(); -} - -/** - * @brief Sets the hour, without changing 12/24h mode. - * The hour must be in 24h format. - * - * @param hour 0...23 - */ -void DS3231::setHour(byte hour) { - bool h12; - byte temp_hour; - - // Start by figuring out what the 12/24 mode is - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x02); - _Wire.endTransmission(); - _Wire.requestFrom(CLOCK_ADDRESS, 1); - h12 = (_Wire.read() & 0b01000000); - // if h12 is true, it's 12h mode; false is 24h. - - if (h12) { - // 12 hour - bool am_pm = (hour > 11); - temp_hour = hour; - if (temp_hour > 11) { - temp_hour = temp_hour - 12; - } - if (temp_hour == 0) { - temp_hour = 12; - } - temp_hour = decToBcd(temp_hour) | (am_pm << 5) | 0b01000000; - } else { - // 24 hour - temp_hour = decToBcd(hour) & 0b10111111; - } - - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x02); - _Wire.write(temp_hour); - _Wire.endTransmission(); -} - -/** - * @brief Sets the Day of Week of the DS3231 module - * - * @param dayOfWeek 1...7 - */ -void DS3231::setDoW(byte dayOfWeek) { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x03); - _Wire.write(decToBcd(dayOfWeek)); - _Wire.endTransmission(); -} - -/** - * @brief Sets the Date of the DS3231 module - * - * @param date 1...31 - */ -void DS3231::setDate(byte date) { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x04); - _Wire.write(decToBcd(date)); - _Wire.endTransmission(); -} - -/** - * @brief Sets the Month of the DS3231 module - * - * @param month 1...12 - */ -void DS3231::setMonth(byte month) { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x05); - _Wire.write(decToBcd(month)); - _Wire.endTransmission(); -} - -/** - * @brief Sets the Year of the DS3231 module. - * - * @param year 0...99 - */ -void DS3231::setYear(byte year) { - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x06); - _Wire.write(decToBcd(year)); - _Wire.endTransmission(); -} - -/** - * @brief sets the clock mode to . - - * - * @param h12 12h (true) or 24h (false) - */ -void DS3231::setClockMode(bool h12) { - // One thing that bothers me about how I've written this is that - // if the read and right happen at the right hourly millisecond, - // the clock will be set back an hour. Not sure how to do it better, - // though, and as long as one doesn't set the mode frequently it's - // a very minimal risk. - // It's zero risk if you call this BEFORE setting the hour, since - // the setHour() function doesn't change this mode. - - byte temp_buffer; - - // Start by reading byte 0x02. - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x02); - _Wire.endTransmission(); - _Wire.requestFrom(CLOCK_ADDRESS, 1); - temp_buffer = _Wire.read(); - - // Set the flag to the requested value: - if (h12) { - temp_buffer = temp_buffer | 0b01000000; - } else { - temp_buffer = temp_buffer & 0b10111111; - } - - // Write the byte - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x02); - _Wire.write(temp_buffer); - _Wire.endTransmission(); -} - -/** - * @brief read the internal temperature sensor of the DS3231 module - * - * @return float temperature measured in DS3231 module - */ -float DS3231::getTemperature() { - byte tMSB, tLSB; - float temp3231; - - // temp registers (11h-12h) get updated automatically every 64s - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x11); - _Wire.endTransmission(); - _Wire.requestFrom(CLOCK_ADDRESS, 2); - - // Should I do more "if available" checks here? - if(_Wire.available()) { - //2's complement int portion - tMSB = _Wire.read(); - //fraction portion - tLSB = _Wire.read(); - - // Shift upper byte, add lower - int16_t itemp = ( tMSB << 8 | (tLSB & 0xC0) ); - // Scale and return - temp3231 = ( (float)itemp / 256.0 ); - } - else { - // Impossible temperature; error value - temp3231 = -9999; - } - - return temp3231; -} - -void DS3231::getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM) { - byte temp_buffer; - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x07); - _Wire.endTransmission(); - - _Wire.requestFrom(CLOCK_ADDRESS, 4); - - temp_buffer = _Wire.read(); // Get A1M1 and A1 Seconds - A1Second = bcdToDec(temp_buffer & 0b01111111); - // put A1M1 bit in position 0 of DS3231_AlarmBits. - AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>7; - - temp_buffer = _Wire.read(); // Get A1M2 and A1 minutes - A1Minute = bcdToDec(temp_buffer & 0b01111111); - // put A1M2 bit in position 1 of DS3231_AlarmBits. - AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>6; - - temp_buffer = _Wire.read(); // Get A1M3 and A1 Hour - // put A1M3 bit in position 2 of DS3231_AlarmBits. - AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>5; - // determine A1 12/24 mode - A1h12 = temp_buffer & 0b01000000; - if (A1h12) { - A1PM = temp_buffer & 0b00100000; // determine am/pm - A1Hour = bcdToDec(temp_buffer & 0b00011111); // 12-hour - } else { - A1Hour = bcdToDec(temp_buffer & 0b00111111); // 24-hour - } - - temp_buffer = _Wire.read(); // Get A1M4 and A1 Day/Date - // put A1M3 bit in position 3 of DS3231_AlarmBits. - AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>4; - // determine A1 day or date flag - A1Dy = (temp_buffer & 0b01000000)>>6; - if (A1Dy) { - // alarm is by day of week, not date. - A1Day = bcdToDec(temp_buffer & 0b00001111); - } else { - // alarm is by date, not day of week. - A1Day = bcdToDec(temp_buffer & 0b00111111); - } -} - -void DS3231::getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM, bool clearAlarmBits) { - if (clearAlarmBits) { - AlarmBits = 0x0; - } - getA1Time(A1Day, A1Hour, A1Minute, A1Second, AlarmBits, A1Dy, A1h12, A1PM); -} - -void DS3231::getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM) { - byte temp_buffer; - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x0b); - _Wire.endTransmission(); - - _Wire.requestFrom(CLOCK_ADDRESS, 3); - temp_buffer = _Wire.read(); // Get A2M2 and A2 Minutes - A2Minute = bcdToDec(temp_buffer & 0b01111111); - // put A2M2 bit in position 4 of DS3231_AlarmBits. - AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>3; - - temp_buffer = _Wire.read(); // Get A2M3 and A2 Hour - // put A2M3 bit in position 5 of DS3231_AlarmBits. - AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>2; - // determine A2 12/24 mode - A2h12 = temp_buffer & 0b01000000; - if (A2h12) { - A2PM = temp_buffer & 0b00100000; // determine am/pm - A2Hour = bcdToDec(temp_buffer & 0b00011111); // 12-hour - } else { - A2Hour = bcdToDec(temp_buffer & 0b00111111); // 24-hour - } - - temp_buffer = _Wire.read(); // Get A2M4 and A1 Day/Date - // put A2M4 bit in position 6 of DS3231_AlarmBits. - AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>1; - // determine A2 day or date flag - A2Dy = (temp_buffer & 0b01000000)>>6; - if (A2Dy) { - // alarm is by day of week, not date. - A2Day = bcdToDec(temp_buffer & 0b00001111); - } else { - // alarm is by date, not day of week. - A2Day = bcdToDec(temp_buffer & 0b00111111); - } -} - -void DS3231::getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM, bool clearAlarmBits) { - if (clearAlarmBits) { - AlarmBits = 0x0; - } - getA2Time(A2Day, A2Hour, A2Minute, AlarmBits, A2Dy, A2h12, A2PM); -} - -void DS3231::setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM) { - // Sets the alarm-1 date and time on the DS3231, using A1* information - byte temp_buffer; - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x07); // A1 starts at 07h - // Send A1 second and A1M1 - _Wire.write(decToBcd(A1Second) | ((AlarmBits & 0b00000001) << 7)); - // Send A1 Minute and A1M2 - _Wire.write(decToBcd(A1Minute) | ((AlarmBits & 0b00000010) << 6)); - // Figure out A1 hour - if (A1h12) { - // Start by converting existing time to h12 if it was given in 24h. - if (A1Hour > 12) { - // well, then, this obviously isn't a h12 time, is it? - A1Hour = A1Hour - 12; - A1PM = true; - } - if (A1PM) { - // Afternoon - // Convert the hour to BCD and add appropriate flags. - temp_buffer = decToBcd(A1Hour) | 0b01100000; - } else { - // Morning - // Convert the hour to BCD and add appropriate flags. - temp_buffer = decToBcd(A1Hour) | 0b01000000; - } - } else { - // Now for 24h - temp_buffer = decToBcd(A1Hour); - } - temp_buffer = temp_buffer | ((AlarmBits & 0b00000100)<<5); - // A1 hour is figured out, send it - _Wire.write(temp_buffer); - // Figure out A1 day/date and A1M4 - temp_buffer = ((AlarmBits & 0b00001000)<<4) | decToBcd(A1Day); - if (A1Dy) { - // Set A1 Day/Date flag (Otherwise it's zero) - temp_buffer = temp_buffer | 0b01000000; - } - _Wire.write(temp_buffer); - // All done! - _Wire.endTransmission(); -} - -void DS3231::setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM) { - // Sets the alarm-2 date and time on the DS3231, using A2* information - byte temp_buffer; - _Wire.beginTransmission(CLOCK_ADDRESS); - _Wire.write(0x0b); // A1 starts at 0bh - // Send A2 Minute and A2M2 - _Wire.write(decToBcd(A2Minute) | ((AlarmBits & 0b00010000) << 3)); - // Figure out A2 hour - if (A2h12) { - // Start by converting existing time to h12 if it was given in 24h. - if (A2Hour > 12) { - // well, then, this obviously isn't a h12 time, is it? - A2Hour = A2Hour - 12; - A2PM = true; - } - if (A2PM) { - // Afternoon - // Convert the hour to BCD and add appropriate flags. - temp_buffer = decToBcd(A2Hour) | 0b01100000; - } else { - // Morning - // Convert the hour to BCD and add appropriate flags. - temp_buffer = decToBcd(A2Hour) | 0b01000000; - } - } else { - // Now for 24h - temp_buffer = decToBcd(A2Hour); - } - // add in A2M3 bit - temp_buffer = temp_buffer | ((AlarmBits & 0b00100000)<<2); - // A2 hour is figured out, send it - _Wire.write(temp_buffer); - // Figure out A2 day/date and A2M4 - temp_buffer = ((AlarmBits & 0b01000000)<<1) | decToBcd(A2Day); - if (A2Dy) { - // Set A2 Day/Date flag (Otherwise it's zero) - temp_buffer = temp_buffer | 0b01000000; - } - _Wire.write(temp_buffer); - // All done! - _Wire.endTransmission(); -} - -void DS3231::turnOnAlarm(byte Alarm) { - // turns on alarm number "Alarm". Defaults to 2 if Alarm is not 1. - byte temp_buffer = readControlByte(0); - // modify control byte - if (Alarm == 1) { - temp_buffer = temp_buffer | 0b00000101; - } else { - temp_buffer = temp_buffer | 0b00000110; - } - writeControlByte(temp_buffer, 0); -} - -void DS3231::turnOffAlarm(byte Alarm) { - // turns off alarm number "Alarm". Defaults to 2 if Alarm is not 1. - // Leaves interrupt pin alone. - byte temp_buffer = readControlByte(0); - // modify control byte - if (Alarm == 1) { - temp_buffer = temp_buffer & 0b11111110; - } else { - temp_buffer = temp_buffer & 0b11111101; - } - writeControlByte(temp_buffer, 0); -} - -bool DS3231::checkAlarmEnabled(byte Alarm) { - // Checks whether the given alarm is enabled. - byte result = 0x0; - byte temp_buffer = readControlByte(0); - if (Alarm == 1) { - result = temp_buffer & 0b00000001; - } else { - result = temp_buffer & 0b00000010; - } - return result; -} - -bool DS3231::checkIfAlarm(byte Alarm) { - // Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly. - // Turns flag off, also. - // defaults to checking alarm 2, unless Alarm == 1. - byte result; - byte temp_buffer = readControlByte(1); - if (Alarm == 1) { - // Did alarm 1 go off? - result = temp_buffer & 0b00000001; - // clear flag - temp_buffer = temp_buffer & 0b11111110; - } else { - // Did alarm 2 go off? - result = temp_buffer & 0b00000010; - // clear flag - temp_buffer = temp_buffer & 0b11111101; - } - writeControlByte(temp_buffer, 1); - return result; -} - -bool DS3231::checkIfAlarm(byte Alarm, bool clearflag) { - // Checks whether alarm 1 or alarm 2 flag is on, returns T/F accordingly. - // Clears flag, if clearflag is set - // defaults to checking alarm 2, unless Alarm == 1. - byte result; - byte temp_buffer = readControlByte(1); - if (Alarm == 1) { - // Did alarm 1 go off? - result = temp_buffer & 0b00000001; - // clear flag - temp_buffer = temp_buffer & 0b11111110; - } else { - // Did alarm 2 go off? - result = temp_buffer & 0b00000010; - // clear flag - temp_buffer = temp_buffer & 0b11111101; - } - if (clearflag) { - writeControlByte(temp_buffer, 1); - } - return result; -} - -void DS3231::enableOscillator(bool TF, bool battery, byte frequency) { - // turns oscillator on or off. True is on, false is off. - // if battery is true, turns on even for battery-only operation, - // otherwise turns off if Vcc is off. - // frequency must be 0, 1, 2, or 3. - // 0 = 1 Hz - // 1 = 1.024 kHz - // 2 = 4.096 kHz - // 3 = 8.192 kHz (Default if frequency byte is out of range) - if (frequency > 3) frequency = 3; - // read control byte in, but zero out current state of RS2 and RS1. - byte temp_buffer = readControlByte(0) & 0b11100111; - if (battery) { - // turn on BBSQW flag - temp_buffer = temp_buffer | 0b01000000; - } else { - // turn off BBSQW flag - temp_buffer = temp_buffer & 0b10111111; - } - if (TF) { - // set ~EOSC to 0 and INTCN to zero. - temp_buffer = temp_buffer & 0b01111011; - } else { - // set ~EOSC to 1, leave INTCN as is. - temp_buffer = temp_buffer | 0b10000000; - } - // shift frequency into bits 3 and 4 and set. - frequency = frequency << 3; - temp_buffer = temp_buffer | frequency; - // And write the control bits - writeControlByte(temp_buffer, 0); -} - -void DS3231::enable32kHz(bool TF) { - // turn 32kHz pin on or off - byte temp_buffer = readControlByte(1); - if (TF) { - // turn on 32kHz pin - temp_buffer = temp_buffer | 0b00001000; - } else { - // turn off 32kHz pin - temp_buffer = temp_buffer & 0b11110111; - } - writeControlByte(temp_buffer, 1); -} - -bool DS3231::oscillatorCheck() { - // Returns false if the oscillator has been off for some reason. - // If this is the case, the time is probably not correct. - byte temp_buffer = readControlByte(1); - bool result = true; - if (temp_buffer & 0b10000000) { - // Oscillator Stop Flag (OSF) is set, so return false. - result = false; - } - return result; -} - -// ***************************************** -// Private Functions of DS3231 object -// ***************************************** - -byte DS3231::decToBcd(byte val) { -// Convert normal decimal numbers to binary coded decimal - return ( (val/10*16) + (val%10) ); -} - -byte DS3231::bcdToDec(byte val) { -// Convert binary coded decimal to normal decimal numbers - return ( (val/16*10) + (val%16) ); -} - -byte DS3231::readControlByte(bool which) { - // Read selected control byte - // first byte (0) is 0x0e, second (1) is 0x0f - _Wire.beginTransmission(CLOCK_ADDRESS); - if (which) { - // second control byte - _Wire.write(0x0f); - } else { - // first control byte - _Wire.write(0x0e); - } - _Wire.endTransmission(); - _Wire.requestFrom(CLOCK_ADDRESS, 1); - return _Wire.read(); -} - -void DS3231::writeControlByte(byte control, bool which) { - // Write the selected control byte. - // which=false -> 0x0e, true->0x0f. - _Wire.beginTransmission(CLOCK_ADDRESS); - if (which) { - _Wire.write(0x0f); - } else { - _Wire.write(0x0e); - } - _Wire.write(control); - _Wire.endTransmission(); -} +/** + * @file DS3231-RTC.cpp + * @author Frank Häfele + * @brief Real-Time clock library based on Arduino Framework + */ + +#include "DS3231-RTC.h" + +#include +#include +#include + + +// ***************************************** +// Static Functions only used in this file +// ***************************************** + +static void safe_gmtime(const time_t *timestamp, struct tm *timestruct) { +#if defined(_WIN32) + gmtime_s(timestruct, timestamp); +#else + gmtime_r(timestamp, timestruct); +#endif +} + +static void safe_localtime(const time_t *timestamp, struct tm *timestruct) { +#if defined(_WIN32) + localtime_s(timestruct, timestamp); +#else + localtime_r(timestamp, timestruct); +#endif +} + +#if DS3231_RTC_HAS_WIRE +DS3231::TwoWireAdapter::TwoWireAdapter(TwoWire *wire) +: _wire(wire) +{} + +void DS3231::TwoWireAdapter::beginTransmission(uint8_t address) { + _wire->beginTransmission(address); +} + +size_t DS3231::TwoWireAdapter::write(uint8_t value) { + return _wire->write(value); +} + +uint8_t DS3231::TwoWireAdapter::endTransmission() { + return _wire->endTransmission(); +} + +uint8_t DS3231::TwoWireAdapter::requestFrom(uint8_t address, uint8_t quantity) { + return _wire->requestFrom(address, quantity); +} + +int DS3231::TwoWireAdapter::read() { + return _wire->read(); +} + +int DS3231::TwoWireAdapter::available() { + return _wire->available(); +} + +void DS3231::TwoWireAdapter::begin() { + if (_wire) { + _wire->begin(); + } +} +#endif + +#pragma region DateTime +DS3231::DateTime::DateTime (time_t unix_timestamp) +: _unix_timestamp{unix_timestamp}, _y2k_timestamp{static_cast(unix_timestamp - UNIX_OFFSET)} +{ + safe_gmtime(&_unix_timestamp, &_tm); +} + +DS3231::DateTime::DateTime(int16_t year, int8_t month, int8_t day, int8_t hour, int8_t min, int8_t sec, int8_t wday, int16_t yday, int16_t dst) +{ + _tm.tm_sec = sec; + _tm.tm_min = min; + _tm.tm_hour = hour; + _tm.tm_mday = day; + _tm.tm_mon = month-1; + _tm.tm_year = year-1900; + _tm.tm_wday = wday-1; + _tm.tm_yday = yday; + _tm.tm_isdst = dst; + + set_timstamps(); +} + +DS3231::DateTime::DateTime(const char *date, const char *time) { + static const char month_names[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + static char month_buff[4] = {'0','0','0','0'}; + int year, day; +#if defined(_MSC_VER) + sscanf_s(date, "%3s %2d %4d", month_buff, static_cast(sizeof(month_buff)), &day, &year); +#else + sscanf(date, "%s %2d %4d", month_buff, &day, &year); +#endif + int month = static_cast((strstr(month_names, month_buff) - month_names) / 3 + 1); + _tm.tm_year = year-1900; + _tm.tm_mon = month-1; + _tm.tm_mday = day; + uint8_t hour, min, sec; +#if defined(_MSC_VER) + sscanf_s(time, "%hhu:%hhu:%hhu", &hour, &min, &sec); +#else + sscanf(time, "%hhu:%hhu:%hhu", &hour, &min, &sec); +#endif + _tm.tm_hour = hour; + _tm.tm_min = min; + _tm.tm_sec = sec; + _tm.tm_yday = DS3231_Tools::calcYearDay(year, month, day); + set_timstamps(); +} + +void DS3231::DateTime::set_timstamps() { +#if defined (__AVR__) + _y2k_timestamp = mktime(&_tm); + _unix_timestamp = _y2k_timestamp + UNIX_OFFSET; +#else + _unix_timestamp = mktime(&_tm); + _y2k_timestamp = _unix_timestamp - UNIX_OFFSET; +#endif +} + +size_t DS3231::DateTime::strf_DateTime(char *buffer, size_t buffersize, const char *formatSpec) { + size_t len {strftime(buffer, buffersize, formatSpec, &_tm)}; + return len; +} +#pragma endregion DateTime + +DS3231::DateTime DS3231::RTClib::now(BusInterface &bus) { + bus.beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS); + bus.write(0); + bus.endTransmission(); + + bus.requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 7); + int8_t sec = DS3231_Tools::bcdToDec(static_cast(bus.read()) & 0x7F); + int8_t min = DS3231_Tools::bcdToDec(static_cast(bus.read()) & 0x7F); + + // Read hour register and respect 12/24h mode (Bit 6) + uint8_t hour_byte = static_cast(bus.read()); + bool h12 = hour_byte & 0b01000000; + int8_t hour; + if (h12) { + // 12-hour mode: only use bits 4-0 for hour value + hour = DS3231_Tools::bcdToDec(hour_byte & 0b00011111); + } else { + // 24-hour mode: use bits 5-0 for hour value + hour = DS3231_Tools::bcdToDec(hour_byte & 0b00111111); + } + + int8_t wday = DS3231_Tools::bcdToDec(static_cast(bus.read()) & 0x07) - 1; + int8_t day = DS3231_Tools::bcdToDec(static_cast(bus.read()) & 0x3F); + int8_t month = DS3231_Tools::bcdToDec(static_cast(bus.read()) & 0x1F); + int16_t year = DS3231_Tools::bcdToDec(static_cast(bus.read())) + 2000; + int16_t yday = DS3231_Tools::calcYearDay(year, month, day); + int16_t dst = -1; + + return DateTime{year, month, day, hour, min, sec, wday, yday, dst}; +} + +#if DS3231_RTC_HAS_WIRE +DS3231::DateTime DS3231::RTClib::now(TwoWire &_Wire) { + TwoWireAdapter adapter(&_Wire); + return now(adapter); +} +#endif + +#pragma region DS3231 +#if DS3231_RTC_HAS_WIRE +DS3231::DS3231::DS3231() +: _wire_adapter(&Wire), _bus(&_wire_adapter) +{} + +DS3231::DS3231::DS3231(TwoWire &twowire) +: _wire_adapter(&twowire), _bus(&_wire_adapter) +{} +#endif + +DS3231::DS3231::DS3231(BusInterface &bus) +#if DS3231_RTC_HAS_WIRE +: _wire_adapter{nullptr}, _bus{&bus} +#else +: _bus{&bus} +#endif +{} + +uint8_t DS3231::DS3231::getSecond() { + selectRegister(0x00); + return getRegisterValue(); +} + +uint8_t DS3231::DS3231::getMinute() { + selectRegister(0x01); + return getRegisterValue(); +} + +uint8_t DS3231::DS3231::getHour(bool& h12, bool& PM_time) { + uint8_t temp_buffer = readRegisterRaw(0x02); + uint8_t hour; + + h12 = temp_buffer & 0b01000000; + if (h12) { + PM_time = temp_buffer & 0b00100000; + hour = DS3231_Tools::bcdToDec(temp_buffer & 0b00011111); + } + else { + PM_time = 0; + hour = DS3231_Tools::bcdToDec(temp_buffer & 0b00111111); + } + return hour; +} + +uint8_t DS3231::DS3231::getDoW() { + selectRegister(0x03); + return getRegisterValue(); +} + +uint8_t DS3231::DS3231::getDate() { + selectRegister(0x04); + return getRegisterValue(); +} + +uint8_t DS3231::DS3231::getMonth(bool ¢ury) { + uint8_t temp_buffer = readRegisterRaw(0x05); + century = temp_buffer & 0b10000000; + return DS3231_Tools::bcdToDec(temp_buffer & 0b01111111); +} + +uint8_t DS3231::DS3231::getYear() { + selectRegister(0x06); + return getRegisterValue(); +} + +void DS3231::DS3231::setEpoch(time_t epoch, bool flag_localtime) { +#if defined (__AVR__) + epoch -= UNIX_OFFSET; +#endif + struct tm tmnow; + if (flag_localtime) { + safe_localtime(&epoch, &tmnow); + } + else { + safe_gmtime(&epoch, &tmnow); + } + setSecond(static_cast(tmnow.tm_sec)); + setMinute(static_cast(tmnow.tm_min)); + setHour(static_cast(tmnow.tm_hour)); + setDoW(static_cast(tmnow.tm_wday + 1U)); + setDate(static_cast(tmnow.tm_mday)); + setMonth(static_cast(tmnow.tm_mon + 1U)); + setYear(static_cast(tmnow.tm_year - 100U)); +} + +void DS3231::DS3231::setSecond(uint8_t second) { + writeRegister(0x00, DS3231_Tools::decToBcd(second)); + uint8_t temp_buffer = readControlByte(1); + writeControlByte((temp_buffer & 0b01111111), 1); +} + +void DS3231::DS3231::setMinute(uint8_t minute) { + writeRegister(0x01, DS3231_Tools::decToBcd(minute)); +} + +void DS3231::DS3231::setHour(uint8_t hour) { + bool h12 = (readRegisterRaw(0x02) & 0b01000000); + uint8_t temp_hour; + + if (h12) { + bool am_pm = (hour > 11); + temp_hour = hour; + if (temp_hour > 11) { + temp_hour = temp_hour - 12; + } + if (temp_hour == 0) { + temp_hour = 12; + } + temp_hour = DS3231_Tools::decToBcd(temp_hour) | (am_pm << 5) | 0b01000000; + } else { + temp_hour = DS3231_Tools::decToBcd(hour) & 0b10111111; + } + writeRegister(0x02, temp_hour); +} + +void DS3231::DS3231::setDoW(uint8_t dayOfWeek) { + writeRegister(0x03, DS3231_Tools::decToBcd(dayOfWeek)); +} + +void DS3231::DS3231::setDate(uint8_t date) { + writeRegister(0x04, DS3231_Tools::decToBcd(date)); +} + +void DS3231::DS3231::setMonth(uint8_t month) { + writeRegister(0x05, DS3231_Tools::decToBcd(month)); +} + +void DS3231::DS3231::setYear(uint8_t year) { + writeRegister(0x06, DS3231_Tools::decToBcd(year)); +} + +void DS3231::DS3231::set12hourMode() { + uint8_t hour_byte = readRegisterRaw(0x02); + bool currently_24h = !(hour_byte & 0b01000000); // Bit 6 == 0 means 24h mode + + if (currently_24h) { + // Convert from 24h to 12h + uint8_t hour_24 = DS3231_Tools::bcdToDec(hour_byte & 0b00111111); + uint8_t hour_12; + bool is_pm = false; + + if (hour_24 == 0) { + hour_12 = 12; // 0:00 → 12:00 AM + is_pm = false; + } else if (hour_24 < 12) { + hour_12 = hour_24; // 1:00-11:00 → 1:00-11:00 AM + is_pm = false; + } else if (hour_24 == 12) { + hour_12 = 12; // 12:00 → 12:00 PM + is_pm = true; + } else { + hour_12 = hour_24 - 12; // 13:00-23:00 → 1:00-11:00 PM + is_pm = true; + } + + // Build new register value with 12h mode, PM flag, and new hour + uint8_t new_hour = DS3231_Tools::decToBcd(hour_12); + if (is_pm) { + new_hour |= 0b01100000; // Set Bit 6 (12h mode) and Bit 5 (PM) + } else { + new_hour |= 0b01000000; // Set Bit 6 (12h mode) only + } + + writeRegister(0x02, new_hour); + } + // If already in 12h mode, do nothing +} + +void DS3231::DS3231::set24hourMode() { + uint8_t hour_byte = readRegisterRaw(0x02); + bool currently_12h = (hour_byte & 0b01000000); // Bit 6 == 1 means 12h mode + + if (currently_12h) { + // Convert from 12h to 24h + bool is_pm = (hour_byte & 0b00100000); + uint8_t hour_12 = DS3231_Tools::bcdToDec(hour_byte & 0b00011111); + uint8_t hour_24; + + if (hour_12 == 12) { + hour_24 = is_pm ? 12 : 0; // 12:00 AM → 0:00, 12:00 PM → 12:00 + } else if (is_pm) { + hour_24 = hour_12 + 12; // 1:00-11:00 PM → 13:00-23:00 + } else { + hour_24 = hour_12; // 1:00-11:00 AM → 1:00-11:00 + } + + // Build new register value with 24h mode (Bit 6 = 0) + uint8_t new_hour = DS3231_Tools::decToBcd(hour_24) & 0b10111111; // Clear Bit 6 + + writeRegister(0x02, new_hour); + } + // If already in 24h mode, do nothing +} + +void DS3231::DS3231::begin() { +#if DS3231_RTC_HAS_WIRE + _wire_adapter.begin(); +#endif + set24hourMode(); +} + +bool DS3231::DS3231::is24hourModeActive() { + uint8_t hour_byte = readRegisterRaw(0x02); + bool currently_12h = (hour_byte & 0b01000000); // Bit 6 == 1 means 12h mode + return !currently_12h; +} + +float DS3231::DS3231::getTemperature() { + uint8_t tMSB, tLSB; + float temp3231; + + selectRegister(0x11); + bus().requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 2); + + if(bus().available()) { + tMSB = static_cast(bus().read()); + tLSB = static_cast(bus().read()); + + int16_t itemp = static_cast(tMSB << 8 | (tLSB & 0xC0)); + temp3231 = ((float)itemp / 256.0f); + } + else { + temp3231 = NAN; + } + return temp3231; +} + +void DS3231::DS3231::getA1Time(uint8_t &Day, uint8_t &Hour, uint8_t &Minute, uint8_t &Second, uint8_t &AlarmBits, bool &Dy, bool &h12, bool &PM) { + uint8_t temp_buffer; + selectRegister(0x07); + + bus().requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 4); + + temp_buffer = static_cast(bus().read()); + Second = DS3231_Tools::bcdToDec(temp_buffer & 0b01111111); + AlarmBits = AlarmBits | (temp_buffer & 0b10000000) >>7; + + temp_buffer = static_cast(bus().read()); + Minute = DS3231_Tools::bcdToDec(temp_buffer & 0b01111111); + AlarmBits = AlarmBits | (temp_buffer & 0b10000000) >>6; + + temp_buffer = static_cast(bus().read()); + AlarmBits = AlarmBits | (temp_buffer & 0b10000000) >>5; + h12 = temp_buffer & 0b01000000; + if (h12) { + PM = temp_buffer & 0b00100000; + Hour = DS3231_Tools::bcdToDec(temp_buffer & 0b00011111); + } else { + Hour = DS3231_Tools::bcdToDec(temp_buffer & 0b00111111); + } + + temp_buffer = static_cast(bus().read()); + AlarmBits = AlarmBits | (temp_buffer & 0b10000000) >>4; + Dy = (temp_buffer & 0b01000000) >>6; + if (Dy) { + Day = DS3231_Tools::bcdToDec(temp_buffer & 0b00001111); + } else { + Day = DS3231_Tools::bcdToDec(temp_buffer & 0b00111111); + } +} + +void DS3231::DS3231::getA1Time(uint8_t &Day, uint8_t &Hour, uint8_t &Minute, uint8_t &Second, uint8_t &AlarmBits, bool &Dy, bool &h12, bool &PM, bool clearAlarmBits) { + if (clearAlarmBits) { + AlarmBits = 0x0; + } + getA1Time(Day, Hour, Minute, Second, AlarmBits, Dy, h12, PM); +} + +void DS3231::DS3231::getA2Time(uint8_t &Day, uint8_t &Hour, uint8_t &Minute, uint8_t &AlarmBits, bool &Dy, bool &h12, bool &PM) { + uint8_t temp_buffer; + selectRegister(0x0b); + + bus().requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 3); + temp_buffer = static_cast(bus().read()); + Minute = DS3231_Tools::bcdToDec(temp_buffer & 0b01111111); + AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>3; + + temp_buffer = static_cast(bus().read()); + AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>2; + h12 = temp_buffer & 0b01000000; + if (h12) { + PM = temp_buffer & 0b00100000; + Hour = DS3231_Tools::bcdToDec(temp_buffer & 0b00011111); + } else { + Hour = DS3231_Tools::bcdToDec(temp_buffer & 0b00111111); + } + + temp_buffer = static_cast(bus().read()); + AlarmBits = AlarmBits | (temp_buffer & 0b10000000)>>1; + Dy = (temp_buffer & 0b01000000)>>6; + if (Dy) { + Day = DS3231_Tools::bcdToDec(temp_buffer & 0b00001111); + } else { + Day = DS3231_Tools::bcdToDec(temp_buffer & 0b00111111); + } +} + +void DS3231::DS3231::getA2Time(uint8_t &Day, uint8_t &Hour, uint8_t &Minute, uint8_t &AlarmBits, bool &Dy, bool &h12, bool &PM, bool clearAlarmBits) { + if (clearAlarmBits) { + AlarmBits = 0x0; + } + getA2Time(Day, Hour, Minute, AlarmBits, Dy, h12, PM); +} + +void DS3231::DS3231::setA1Time(uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second, uint8_t AlarmBits, bool Dy, bool h12, bool PM) { + uint8_t temp_buffer; + bus().beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS); + bus().write(0x07); + bus().write(DS3231_Tools::decToBcd(Second) | ((AlarmBits & 0b00000001) << 7)); + bus().write(DS3231_Tools::decToBcd(Minute) | ((AlarmBits & 0b00000010) << 6)); + + if (h12) { + if (Hour > 12) { + Hour = Hour - 12; + PM = true; + } + if (PM) { + temp_buffer = DS3231_Tools::decToBcd(Hour) | 0b01100000; + } else { + temp_buffer = DS3231_Tools::decToBcd(Hour) | 0b01000000; + } + } else { + temp_buffer = DS3231_Tools::decToBcd(Hour); + } + temp_buffer = temp_buffer | ((AlarmBits & 0b00000100)<<5); + bus().write(temp_buffer); + + temp_buffer = ((AlarmBits & 0b00001000)<<4) | DS3231_Tools::decToBcd(Day); + if (Dy) { + temp_buffer = temp_buffer | 0b01000000; + } + bus().write(temp_buffer); + bus().endTransmission(); +} + +void DS3231::DS3231::setA2Time(uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t AlarmBits, bool Dy, bool h12, bool PM) { + uint8_t temp_buffer; + bus().beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS); + bus().write(0x0b); + bus().write(DS3231_Tools::decToBcd(Minute) | ((AlarmBits & 0b00010000) << 3)); + + if (h12) { + if (Hour > 12) { + Hour = Hour - 12; + PM = true; + } + if (PM) { + temp_buffer = DS3231_Tools::decToBcd(Hour) | 0b01100000; + } else { + temp_buffer = DS3231_Tools::decToBcd(Hour) | 0b01000000; + } + } else { + temp_buffer = DS3231_Tools::decToBcd(Hour); + } + temp_buffer = temp_buffer | ((AlarmBits & 0b00100000)<<2); + bus().write(temp_buffer); + + temp_buffer = ((AlarmBits & 0b01000000)<<1) | DS3231_Tools::decToBcd(Day); + if (Dy) { + temp_buffer = temp_buffer | 0b01000000; + } + bus().write(temp_buffer); + bus().endTransmission(); +} + +void DS3231::DS3231::turnOnAlarm(uint8_t alarmNumber) { + uint8_t temp_buffer = readControlByte(0); + if (alarmNumber == 1) { + temp_buffer = temp_buffer | 0b00000101; + } else { + temp_buffer = temp_buffer | 0b00000110; + } + writeControlByte(temp_buffer, 0); +} + +void DS3231::DS3231::turnOffAlarm(uint8_t alarmNumber) { + uint8_t temp_buffer = readControlByte(0); + if (alarmNumber == 1) { + temp_buffer = temp_buffer & 0b11111110; + } else { + temp_buffer = temp_buffer & 0b11111101; + } + writeControlByte(temp_buffer, 0); +} + +bool DS3231::DS3231::checkAlarmEnabled(uint8_t alarmNumber) { + uint8_t result = 0x0; + uint8_t temp_buffer = readControlByte(0); + if (alarmNumber == 1) { + result = temp_buffer & 0b00000001; + } else { + result = temp_buffer & 0b00000010; + } + return result; +} + +bool DS3231::DS3231::checkIfAlarm(uint8_t alarmNumber, bool clearflag) { + uint8_t result; + uint8_t temp_buffer = readControlByte(1); + if (alarmNumber == 1) { + result = temp_buffer & 0b00000001; + temp_buffer = temp_buffer & 0b11111110; + } else { + result = temp_buffer & 0b00000010; + temp_buffer = temp_buffer & 0b11111101; + } + if (clearflag) { + writeControlByte(temp_buffer, 1); + } + return result; +} + +void DS3231::DS3231::enableOscillator(bool turnOn, bool onWithBattery, uint8_t frequency) { + if (frequency > 3) { + frequency = 3; + } + + uint8_t temp_buffer = readControlByte(0) & 0b11100111; + if (onWithBattery) { + temp_buffer = temp_buffer | 0b01000000; + } else { + temp_buffer = temp_buffer & 0b10111111; + } + if (turnOn) { + temp_buffer = temp_buffer & 0b01111011; + } else { + temp_buffer = temp_buffer | 0b10000000; + } + + frequency = frequency << 3; + temp_buffer = temp_buffer | frequency; + writeControlByte(temp_buffer, 0); +} + +void DS3231::DS3231::enable32kHz(bool activate32kHz) { + uint8_t temp_buffer = readControlByte(1); + if (activate32kHz) { + temp_buffer = temp_buffer | 0b00001000; + } else { + temp_buffer = temp_buffer & 0b11110111; + } + writeControlByte(temp_buffer, 1); +} + +bool DS3231::DS3231::oscillatorCheck() { + uint8_t temp_buffer = readControlByte(1); + bool result = true; + if (temp_buffer & 0b10000000) { + result = false; + } + return result; +} + +// ***************************************** +// Private Functions of DS3231 object +// ***************************************** + +void DS3231::DS3231::selectRegister(uint8_t register_addr) { + bus().beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS); + bus().write(register_addr); + bus().endTransmission(); +} + +uint8_t DS3231::DS3231::readRegisterRaw(uint8_t register_addr) { + selectRegister(register_addr); + bus().requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1); + return static_cast(bus().read()); +} + +void DS3231::DS3231::writeRegister(uint8_t register_addr, uint8_t value) { + bus().beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS); + bus().write(register_addr); + bus().write(value); + bus().endTransmission(); +} + +uint8_t DS3231::DS3231::readControlByte(bool which) { + return readRegisterRaw(which ? 0x0f : 0x0e); +} + +void DS3231::DS3231::writeControlByte(uint8_t control, bool which) { + writeRegister(which ? 0x0f : 0x0e, control); +} +#pragma endregion DS3231 \ No newline at end of file diff --git a/src/DS3231-RTC.h b/src/DS3231-RTC.h old mode 100644 new mode 100755 index c64bb90..c19b992 --- a/src/DS3231-RTC.h +++ b/src/DS3231-RTC.h @@ -1,256 +1,697 @@ -/* - * DS3231-RTC.h - * - * The great C++ Library for the DS3231 Real-Time Clock chip - * - */ - -#ifndef __DS3231_RTC_H__ -#define __DS3231_RTC_H__ - -#include -#include -#include - -#define CLOCK_ADDRESS 0x68 - -#if !defined (UNIX_OFFSET) -// SECONDS_FROM_1970_TO_2000 -// Difference between the Y2K and the UNIX epochs, in seconds. -// To convert a Y2K timestamp to UNIX... -#define UNIX_OFFSET 946684800UL -#endif - -#if !defined (NTP_OFFSET) -// SECONDS_FROM_1900_TO_2000 -// Difference between the Y2K and the NTP epochs, in seconds. -// To convert a Y2K timestamp to NTP... -#define NTP_OFFSET 3155673600UL -#endif - -// DateTime class restructured by using standardized time functions -class DateTime { - public: - DateTime (time_t unix_timestamp = 0); - - DateTime ( int16_t year, int8_t month, int8_t mday, - int8_t hour = 0, int8_t min = 0, int8_t sec = 0, - int8_t wday = 0, int16_t yday = 0, int16_t dst = -1); - - DateTime (const char *date, const char *time); - - int16_t getYear() const { return _tm.tm_year + 1900; } - int8_t getMonth() const { return _tm.tm_mon + 1; } - int8_t getDay() const { return _tm.tm_mday; } - int8_t getHour() const { return _tm.tm_hour; } - int8_t getMinute() const { return _tm.tm_min; } - int8_t getSecond() const { return _tm.tm_sec; } - int8_t getWeekDay() const { return _tm.tm_wday; } - int16_t getYearDay() const { return _tm.tm_yday; } - int16_t getDST() const { return _tm.tm_isdst; } - size_t strf_DateTime(char *buffer, size_t buffersize, const char *formatSpec = "%a %h %d %T %Y"); - - // time_t value as seconds since 1/1/2000 - time_t getY2kTime() const { return _y2k_timestamp; } - - // time_t value as seconds since 1/1/1970 - // THE ABOVE COMMENT IS CORRECT FOR LOCAL TIME; TO USE THIS COMMAND TO - // OBTAIN TRUE UNIX TIME SINCE EPOCH, YOU MUST CALL THIS COMMAND AFTER - // SETTING YOUR CLOCK TO UTC - time_t getUnixTime() const { return _unix_timestamp; } - - private: - void set_timstamps(); - - protected: - time_t _unix_timestamp; - time_t _y2k_timestamp; - struct tm _tm; -}; - -class RTClib { - public: - // Get date and time snapshot - static DateTime now(TwoWire &_Wire = Wire); -}; - -// Eric's original code is everything below this line -class DS3231 { - public: - // Constructor - DS3231(); - DS3231(TwoWire &twowire); - - TwoWire &_Wire; - - // ************************************ - // Time-retrieval functions - // ************************************ - // Get the second of the DS3231 module - byte getSecond(); - // Get the minute of the DS3231 module - byte getMinute(); - - // Get the hour of the DS3231 module, in addition, this function - // returns the values of the 12/24-hour flag and the AM/PM flag. - byte getHour(bool& h12, bool& PM_time); - - // Get the DayOfWeek of the DS3231 module - byte getDoW(); - - // Get the date of the DS3231 module - byte getDate(); - - // Get the month and the century roll over of the DS3231 module - byte getMonth(bool ¢ury); - - // Get the year of the DS3231 module - byte getYear(); - - - // ************************************ - // Time-setting functions - // ************************************ - // Note that none of these check for sensibility: You can set the - // date to July 42nd and strange things will probably result. - - // set epoch function gives the epoch as parameter and feeds the RTC - // epoch = UnixTime and starts at 01.01.1970 00:00:00 - void setEpoch(time_t epoch = 0, bool flag_localtime = false); - - // Set the Second of the DS3231 module - void setSecond(byte second); - // Set the minute of the DS3231 module - void setMinute(byte minute); - // Set the hour of the DS3231 module - void setHour(byte hour); - // Sets the Day of the Week (1...7) of the DS3231 module - void setDoW(byte dayOfWeek); - // Sets the Date of the DS3231 module - void setDate(byte date); - // Sets the Month of the DS3231 module - void setMonth(byte month); - // Sets the Year of the DS3231 module - void setYear(byte year); - // Sets the Hour format (12h/24h) of the DS3231 module - void setClockMode(bool h12); - - - // ************************************ - // Temperature function - // ************************************ - // get temperature of the DS3231 module - float getTemperature(); - - // Alarm functions - void getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM); - - /* Retrieves everything you could want to know about alarm - * one. - * A1Dy true makes the alarm go on A1Day = Day of Week, - * A1Dy false makes the alarm go on A1Day = Date of month. - * - * byte AlarmBits sets the behavior of the alarms: - * Dy A1M4 A1M3 A1M2 A1M1 Rate - * X 1 1 1 1 Once per second - * X 1 1 1 0 Alarm when seconds match - * X 1 1 0 0 Alarm when min, sec match - * X 1 0 0 0 Alarm when hour, min, sec match - * 0 0 0 0 0 Alarm when date, h, m, s match - * 1 0 0 0 0 Alarm when DoW, h, m, s match - * - * Dy A2M4 A2M3 A2M2 Rate - * X 1 1 1 Once per minute (at seconds = 00) - * X 1 1 0 Alarm when minutes match - * X 1 0 0 Alarm when hours and minutes match - * 0 0 0 0 Alarm when date, hour, min match - * 1 0 0 0 Alarm when DoW, hour, min match - * - * Note: byte AlarmBits is not explicitly cleared for the getAXTime methods to - * support sequential retrieval of both alarms with the same byte AlarmBits. - * Use the flag bool clearAlarmBits=True to explicitly clear byte AlarmBits on - * call to getAXTime. - */ - - // Same as getA1Time();, but A2 only goes on seconds == 00. - void getA2Time(byte& A2Day, byte& A2Hour, byte& A2Minute, byte& AlarmBits, bool& A2Dy, bool& A2h12, bool& A2PM); - - // Same as getA1Time();, but clears byte AlarmBits. - void getA1Time(byte& A1Day, byte& A1Hour, byte& A1Minute, byte& A1Second, byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM, bool clearAlarmBits); - - // Same as getA1Time();, but clears byte AlarmBits. - void getA2Time(byte& A1Day, byte& A1Hour, byte& A1Minute,byte& AlarmBits, bool& A1Dy, bool& A1h12, bool& A1PM, bool clearAlarmBits); - - // Set the details for Alarm 1 - void setA1Time(byte A1Day, byte A1Hour, byte A1Minute, byte A1Second, byte AlarmBits, bool A1Dy, bool A1h12, bool A1PM); - - // Set the details for Alarm 2 - void setA2Time(byte A2Day, byte A2Hour, byte A2Minute, byte AlarmBits, bool A2Dy, bool A2h12, bool A2PM); - - // Enables alarm 1 or 2 and the external interrupt pin. - // If Alarm != 1, it assumes Alarm == 2. - void turnOnAlarm(byte Alarm); - - // Disables alarm 1 or 2 (default is 2 if Alarm != 1); - // and leaves the interrupt pin alone. - void turnOffAlarm(byte Alarm); - - // Returns T/F to indicate whether the requested alarm is - // enabled. Defaults to 2 if Alarm != 1. - bool checkAlarmEnabled(byte Alarm); - - // Checks whether the indicated alarm (1 or 2, 2 default); - // has been activated. Always clears flag. - bool checkIfAlarm(byte Alarm); - - // Checks whether the indicated alarm (1 or 2, 2 default); - // has been activated. IF clearflag is set, clears alarm flag. - bool checkIfAlarm(byte Alarm, bool clearflag); - - - // ************************************ - // Oscillator functions - // ************************************ - - // turns oscillator on or off. True is on, false is off. - // if battery is true, turns on even for battery-only operation, - // otherwise turns off if Vcc is off. - // frequency must be 0, 1, 2, or 3. - // 0 = 1 Hz - // 1 = 1.024 kHz - // 2 = 4.096 kHz - // 3 = 8.192 kHz (Default if frequency byte is out of range); - void enableOscillator(bool TF, bool battery, byte frequency); - - // Turns the 32kHz output pin on (true); or off (false). - void enable32kHz(bool TF); - - // Checks the status of the OSF (Oscillator Stop Flag);. - // If this returns false, then the clock is probably not - // giving you the correct time. - // The OSF is cleared by function setSecond();. - bool oscillatorCheck(); - - - private: - // the getter functions retrieve current values of the registers. - byte getRegisterValue() { - _Wire.requestFrom(CLOCK_ADDRESS, 1); - return bcdToDec(_Wire.read()); - } - - // Convert normal decimal numbers to binary coded decimal - byte decToBcd(byte val); - // Convert binary coded decimal to normal decimal numbers - byte bcdToDec(byte val); - - - protected: - // Read selected control byte: (0); reads 0x0e, (1) reads 0x0f - byte readControlByte(bool which); - - // Write the selected control byte. - // which == false -> 0x0e, true->0x0f. - void writeControlByte(byte control, bool which); -}; -#endif +/** + * @file DS3231-RTC.h + * @author Frank Häfele + * @brief Real-Time clock library based on Arduino Framework + */ + +#pragma once + +#if __has_include() +#include +#else +#include +#include +#endif + +#if __has_include() +#include +#define DS3231_RTC_HAS_WIRE 1 +#else +#define DS3231_RTC_HAS_WIRE 0 +#endif + +#include +#include "DS3231-RTC_Tools.h" +#include "DS3231-RTC_Constants.h" + +namespace DS3231 { + class BusInterface { + public: + virtual ~BusInterface() = default; + virtual void beginTransmission(uint8_t address) = 0; + virtual size_t write(uint8_t value) = 0; + virtual uint8_t endTransmission() = 0; + virtual uint8_t requestFrom(uint8_t address, uint8_t quantity) = 0; + virtual int read() = 0; + virtual int available() = 0; + }; + +#if DS3231_RTC_HAS_WIRE + class TwoWireAdapter : public BusInterface { + public: + explicit TwoWireAdapter(TwoWire *wire = nullptr); + + void beginTransmission(uint8_t address) override; + size_t write(uint8_t value) override; + uint8_t endTransmission() override; + uint8_t requestFrom(uint8_t address, uint8_t quantity) override; + int read() override; + int available() override; + void begin(); + + private: + TwoWire *_wire; + }; +#endif + +#pragma region DateTime + class DateTime { + public: + /** + * @brief Construct a new Date Time:: Date Time object + * + * @param unix_timestamp for setup the date time members + */ + DateTime (time_t unix_timestamp = 0); + + /** + * @brief Construct a new Date Time object + * + * @param year year YYYY as e.g. 2022 + * @param month months since January - [ 1...12 ] + * @param mday day of the month - [ 1...31 ] + * @param hour hours since midnight - [ 0...23 ] + * @param min minutes after the hour - [ 0...59 ] + * @param sec seconds after the minute - [ 0...59 ] + * @param wday weekday since Sunday, Sunday is 1 - [ 1...7 ] + * @param yday yearday since first january [0...365] + * @param dst daylight saving time / summer time + */ + DateTime (int16_t year, int8_t month, int8_t mday, + int8_t hour = 0, int8_t min = 0, int8_t sec = 0, + int8_t wday = 0, int16_t yday = 0, int16_t dst = -1); + + /** + * @brief Construct a new Date Time:: Date Time object by givin the precompiler marcos + * as __DATE__ and __TIME__ + * + * @param date as Mmm dd yyyy (e.g. "Jan 14 2012") + * @param time as HH:MM:SS (e.g. "23:59:01") + */ + DateTime (const char *date, const char *time); + + /** + * @brief Get the Year value + * + * @return int16_t year as YYYY e.g. 2022 + */ + inline int16_t getYear() const { return _tm.tm_year + 1900; } + + /** + * @brief Get the Month value + * + * @return int8_t month 1...12 + */ + int8_t getMonth() const { return _tm.tm_mon + 1; } + + /** + * @brief Get the Day value + * + * @return int8_t day 1...31 + */ + int8_t getDay() const { return _tm.tm_mday; } + + /** + * @brief Get the Hour value + * + * @return int8_t hour as 0...23 + */ + int8_t getHour() const { return _tm.tm_hour; } + + /** + * @brief Get the Minute value + * + * @return int8_t minute as 0...59 + */ + int8_t getMinute() const { return _tm.tm_min; } + + /** + * @brief Get the Second value + * + * @return int8_t second as 0...60 + */ + int8_t getSecond() const { return _tm.tm_sec; } + + /** + * @brief Get the Week Day value + * + * @return int8_t days since sunday + */ + int8_t getWeekDay() const { return _tm.tm_wday; } + + /** + * @brief Get the Year Day value + * + * @return int16_t days since January 1 as 0...365 + */ + int16_t getYearDay() const { return _tm.tm_yday; } + + /** + * @brief get daylight saving time + * + * @retval >0: DST is active + * @retval 0: DST is not active + * @retval <0: no info avalilable + */ + int16_t getDST() const { return _tm.tm_isdst; } + + /** + * @brief function to format a DateTime string in an buffer based on the standard strftime function + * + * see: https://cplusplus.com/reference/ctime/strftime/ + * or: https://en.cppreference.com/w/cpp/chrono/c/strftime + * + * @param buffer buffer for time string + * @param buffersize size of buffer + * @param formatSpec define format see strftime + * @return size_t length of used buffer + */ + size_t strf_DateTime(char *buffer, size_t buffersize, const char *formatSpec = "%a %h %d %T %Y"); + + /** + * @brief time_t value as seconds from 1/1/2000 to now. + * Difference between the Y2K and the UNIX epochs, in seconds + * + * @return time_t seconds from 1/1/2000 to now + * + */ + time_t getY2kTime() const { return _y2k_timestamp; } + + /** + * @brief Get the Unix Time value. AKA EPOCH. + * THE ABOVE COMMENT IS CORRECT FOR LOCAL TIME; TO USE THIS COMMAND TO + * OBTAIN TRUE UNIX TIME SINCE EPOCH, YOU MUST CALL THIS COMMAND AFTER + * SETTING YOUR CLOCK TO UTC + * + * @return time_t epoch since 1/1/1970 + */ + time_t getUnixTime() const { return _unix_timestamp; } + + private: + /** + * @brief Set the timestamps for _y2k_timestamp and _unix_timestamp by struct tm entries + * + */ + void set_timstamps(); + + protected: + /** + * @brief internal unix timestamp from 1/1/1970 to now + * + */ + time_t _unix_timestamp; + + /** + * @brief internal y2k timestamp from 1/1/2000 to now + * + */ + time_t _y2k_timestamp; + + /** + * @brief internal struct tm + * + */ + struct tm _tm; + }; +#pragma endregion DateTime + + class RTClib { + public: + /** + * @brief get the actual timestamp snapshot + * + * @param bus + * @return DateTime + */ + static DateTime now(BusInterface &bus); + +#if DS3231_RTC_HAS_WIRE + static DateTime now(TwoWire &_Wire = Wire); +#endif + }; + +#pragma region DS3231 + class DS3231 { + public: +#if DS3231_RTC_HAS_WIRE + /** + * @brief Construct a new DS3231::DS3231 object + * initialize the internal _Wire with the Wire object + */ + DS3231(); + + /** + * @brief Construct a new DS3231::DS3231 object + * + * @param twowire reference of TwoWire object + */ + DS3231(TwoWire &twowire); +#endif + + /** + * @brief Construct a new DS3231::DS3231 object with an injected bus + * + * @param bus abstract bus implementation for production or tests + */ + explicit DS3231(BusInterface &bus); + + // ************************************ + // Time-retrieval functions + // ************************************ + /** + * @brief Get the second of the DS3231 module + * + * @return uint8_t 0...59 + */ + uint8_t getSecond(); + + /** + * @brief Get the minute of the DS3231 module + * + * @return uint8_t 0...59 + */ + uint8_t getMinute(); + + // Get the hour of the DS3231 module, + /** + * @brief Get the hour of the DS3231 module. in addition, this function + * returns the values of the 12/24-hour flag and the AM/PM flag. + * + * @param h12 reference of h12 - true when 12 h mode + * @param PM_time reference of pm time - true when pm + * @return uint8_t 1...12 / 0...23 + */ + uint8_t getHour(bool& h12, bool& PM_time); + + /** + * @brief Get the DayOfWeek of the DS3231 module + * + * @return uint8_t 1...7 + */ + uint8_t getDoW(); + + /** + * @brief Get the date/day of the DS3231 module + * + * @return uint8_t 1...31 + */ + uint8_t getDate(); + + /** + * @brief Get the month and the century roll over bit of the DS3231 module + * + * @param century reference of century bit; toggles when value changes from 99 -> 00 + * @return uint8_t value of month 1...12 + */ + uint8_t getMonth(bool ¢ury); + + /** + * @brief Get the Year of the DS3231 module + * + * @return uint8_t 0...99 + */ + uint8_t getYear(); + + + // ************************************ + // Time-setting functions + // ************************************ + // Note that none of these check for sensibility: You can set the + // date to July 42nd and strange things will probably result. + + // set epoch function gives the epoch as parameter and feeds the RTC + // epoch = UnixTime and starts at 01.01.1970 00:00:00 + + /** + * @brief Set the DS3231 by giving the Unix Epoch. + * Seconds since January 1st 1970 00:00:00. + * HINT: => the AVR time.h lib is based on the year 2000 + * + * @param epoch seconds since 01.01.1970 00:00:00 + * @param flag_localtime true if epoch represents local timestamp false otherwise + */ + void setEpoch(time_t epoch = 0, bool flag_localtime = false); + + /** + * @brief Set the second of the DS3231 module + * This function also resets the Oscillator Stop Flag, which is set + * whenever power is interrupted. + * @param second 0...59 + */ + void setSecond(uint8_t second); + + /** + * @brief Set the Minute of the DS3231 module + * + * @param minute 0...59 + */ + void setMinute(uint8_t minute); + + + /** + * @brief Sets the hour, without changing 12/24h mode. + * The hour must be in 24h format. + * + * @param hour 0...23 + */ + void setHour(uint8_t hour); + + + /** + * @brief Sets the Day of Week of the DS3231 module + * + * @param dayOfWeek 1...7 + */ + void setDoW(uint8_t dayOfWeek); + + /** + * @brief Sets the Date/Day of the DS3231 module + * + * @param date 1...31 + */ + void setDate(uint8_t date); + + // Sets the Month of the DS3231 module + /** + * @brief Sets the Month of the DS3231 module + * + * @param month 1...12 + */ + void setMonth(uint8_t month); + + /** + * @brief Sets the Year of the DS3231 module. + * + * @param year 0...99 + */ + void setYear(uint8_t year); + + /** + * @brief Sets the clock hour to 12h format of the DS3231 module + * + */ + void set12hourMode(); + + /** + * @brief Sets the clock hour to 24h format of the DS3231 module + * + */ + void set24hourMode(); + + /** + * @brief check if 24h hour mode is active + * + * @return true if 24 h mode is set + * @return false if 12 hour mode is set + */ + bool is24hourModeActive(); + + /** + * @brief Initialize the DS3231 instance after I2C bus is ready. + * + * This should be called after Wire.begin() when using the default + * TwoWire interface, and ensures the clock is placed in 24h mode. + */ + void begin(); + + // ************************************ + // Temperature Getter function + // ************************************ + // get temperature of the DS3231 module + /** + * @brief read the internal temperature sensor of the DS3231 module + * + * @return float temperature measured in DS3231 module + */ + float getTemperature(); + + // ************************************ + // Alarm Getter functions + // ************************************ + /* Retrieves everything you could want to know about alarm + * one. + * Dy true makes the alarm go on Day = Day of Week, + * Dy false makes the alarm go on Day = Date of month. + * + * uint8_t AlarmBits sets the behavior of the alarms: + * Dy A1M4 A1M3 A1M2 A1M1 Rate + * X 1 1 1 1 Once per second + * X 1 1 1 0 Alarm when seconds match + * X 1 1 0 0 Alarm when min, sec match + * X 1 0 0 0 Alarm when hour, min, sec match + * 0 0 0 0 0 Alarm when date, h, m, s match + * 1 0 0 0 0 Alarm when DoW, h, m, s match + * + * Dy A2M4 A2M3 A2M2 Rate + * X 1 1 1 Once per minute (at seconds = 00) + * X 1 1 0 Alarm when minutes match + * X 1 0 0 Alarm when hours and minutes match + * 0 0 0 0 Alarm when date, hour, min match + * 1 0 0 0 Alarm when DoW, hour, min match + * + * Note: uint8_t AlarmBits is not explicitly cleared for the getAXTime methods to + * support sequential retrieval of both alarms with the same uint8_t AlarmBits. + * Use the flag bool clearAlarmBits=True to explicitly clear uint8_t AlarmBits on + * call to getAXTime. + */ + + /** + * @brief get the time of alarm1 + * + * @param Day + * @param Hour + * @param Minute + * @param Second + * @param AlarmBits + * @param Dy + * @param h12 + * @param PM + */ + void getA1Time(uint8_t &Day, uint8_t &Hour, uint8_t &Minute, uint8_t &Second, uint8_t &AlarmBits, bool &Dy, bool &h12, bool &PM); + + /** + * @brief getA1Time();, but A2 only goes on seconds == 00. + * + * @param Day + * @param Hour + * @param Minute + * @param AlarmBits + * @param Dy + * @param h12 + * @param PM + */ + void getA2Time(uint8_t &Day, uint8_t &Hour, uint8_t &Minute, uint8_t &AlarmBits, bool &Dy, bool &h12, bool &PM); + + /** + * @brief Same as getA1Time();, but clears uint8_t AlarmBits. + * + * @param Day + * @param Hour + * @param Minute + * @param Second + * @param AlarmBits + * @param Dy + * @param h12 + * @param PM + * @param clearAlarmBits + */ + void getA1Time(uint8_t &Day, uint8_t &Hour, uint8_t &Minute, uint8_t &Second, uint8_t &AlarmBits, bool &Dy, bool &h12, bool &PM, bool clearAlarmBits); + + + + /** + * @brief Same as getA1Time();, but clears uint8_t AlarmBits. + * + * @param Day + * @param Hour + * @param Minute + * @param AlarmBits + * @param Dy + * @param h12 + * @param PM + * @param clearAlarmBits + */ + void getA2Time(uint8_t &Day, uint8_t &Hour, uint8_t &Minute, uint8_t &AlarmBits, bool &Dy, bool &h12, bool &PM, bool clearAlarmBits); + + /** + * @brief Set the details for Alarm 1 + * + * @param Day + * @param Hour + * @param Minute + * @param Second + * @param AlarmBits + * @param Dy + * @param h12 + * @param PM + */ + void setA1Time(uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t Second, uint8_t AlarmBits, bool Dy, bool h12, bool PM); + + /** + * @brief Set the details for Alarm 2 + * + * @param Day + * @param Hour + * @param Minute + * @param AlarmBits + * @param Dy + * @param h12 + * @param PM + */ + void setA2Time(uint8_t Day, uint8_t Hour, uint8_t Minute, uint8_t AlarmBits, bool Dy, bool h12, bool PM); + + /** + * @brief Enables alarm 1 or 2 and the external interrupt pin. + * If Alarm != 1, it assumes Alarm == 2. + * + * @param alarmNumber set 1 or 2 + */ + void turnOnAlarm(uint8_t alarmNumber); + + /** + * @brief Disables alarm 1 or 2 (default is 2 if Alarm != 1), + // and leaves the interrupt pin alone. + * + * @param alarmNumber + */ + void turnOffAlarm(uint8_t alarmNumber); + + /** + * @brief checks if alarm is enabled + * + * @param alarmNumber 1 or 2 + * @return true if alarm is enabled + * @return false if alarm is disabled + */ + bool checkAlarmEnabled(uint8_t alarmNumber); + + // Checks whether the indicated alarm (1 or 2, 2 default); + // has been activated. IF clearflag is set, clears alarm flag. + /** + * @brief check if alarm has triggered + * + * @param alarmNumber 1 or 2 + * @param clearflag clears the alarm flag (default) + * @return true alarm has triggered + * @return false has not triggered + */ + bool checkIfAlarm(uint8_t alarmNumber, bool clearflag = true); + + + // ************************************ + // Oscillator functions + // ************************************ + // turns oscillator on or off. True is on, false is off. + // if battery is true, turns on even for battery-only operation, + // otherwise turns off if Vcc is off. + // frequency must be 0, 1, 2, or 3. + // 0 = 1 Hz + // 1 = 1.024 kHz + // 2 = 4.096 kHz + // 3 = 8.192 kHz (Default if frequency uint8_t is out of range); + + /** + * @brief Turns oscillator ON or OFF. + * If battery is true, turns on even for battery-only operation, + * otherwise turns off if Vcc is off. + * frequency must be 0, 1, 2, or 3. + * 0 = 1 Hz + * 1 = 1.024 kHz + * 2 = 4.096 kHz + * 3 = 8.192 kHz (Default if frequency uint8_t is out of range); + * + * @param turnOn true to turn on false otherwise + * @param onWithBattery true for battery operation also + * @param frequency set 0, 1, 2 or 3 to select frequency + */ + void enableOscillator(bool turnOn, bool onWithBattery, uint8_t frequency); + + /** + * @brief Switch ON the 32kHz output pin (true); or off (false) + * + * @param activate32kHz + */ + void enable32kHz(bool activate32kHz); + + /** + * @brief Checks the status of the Oscillator Stop Flag (OSF). + * If this returns false, then the clock is probably not + * giving you the correct time. + * The OSF is cleared by function setSecond(). + * + * @return true if oscillator is running. + * @return false if oscillator has stopped + */ + bool oscillatorCheck(); + + + private: + /** + * @brief optional adapter used for Arduino TwoWire integration + * + */ +#if DS3231_RTC_HAS_WIRE + TwoWireAdapter _wire_adapter; +#endif + + /** + * @brief abstracted bus implementation used by this instance + * + */ + BusInterface *_bus; + + BusInterface &bus() { return *_bus; } + + /** + * @brief the getter functions retrieve current values of the registers. + * + * @return uint8_t register value + */ + uint8_t getRegisterValue() { + bus().requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1); + return DS3231_Tools::bcdToDec(static_cast(bus().read())); + } + + /** + * @brief write register address to bus from which to read from + * + * @param register_addr address of DS3231 register + */ + void selectRegister(uint8_t register_addr); + + /** + * @brief + * + * @param register_addr address of DS3231 register + * @return uint8_t + */ + uint8_t readRegisterRaw(uint8_t register_addr); + + /** + * @brief write value into DS3231 register + * + * @param register_addr address of DS3231 register + * @param value value to write in register + */ + void writeRegister(uint8_t register_addr, uint8_t value); + + + protected: + /** + * @brief Read selected control byte. + * + * @param which (0) reads 0x0e, (1) reads 0x0f + * @return uint8_t control byte + */ + uint8_t readControlByte(bool which); + + /** + * @brief write control byte + * + * @param control byte to write + * @param which (0) writes 0x0e, (1) writes 0x0f + */ + void writeControlByte(uint8_t control, bool which); + }; +#pragma endregion DS3231 +} diff --git a/src/DS3231-RTC_Constants.h b/src/DS3231-RTC_Constants.h new file mode 100644 index 0000000..08c922a --- /dev/null +++ b/src/DS3231-RTC_Constants.h @@ -0,0 +1,41 @@ +/** + * @file DS3231-RTC_Constants.h + * @author Frank Häfele + * @brief Constants for the DS3231-RTC lib + */ + +#pragma once + +#ifndef UNIX_OFFSET + /** + * @brief Seconds from 1/1/1970 to 1/1/2000. + * AKA Difference between the Y2K and the UNIX epochs, in seconds. + * To convert a Y2K timestamp to UNIX. + * + */ + constexpr unsigned long UNIX_OFFSET {946684800UL}; +#endif + +#ifndef NTP_OFFSET + /** + * @brief Seconds from 1/1/1990 to 1/1/2000. + * AKA Difference between the Y2K and the NTP epochs, in seconds. + * To convert a Y2K timestamp to NTP. + * + */ + constexpr unsigned long NTP_OFFSET {3155673600UL}; +#endif + +namespace DS3231_Constants { + /** + * @brief I2C Address of the DS3231 Module + * + */ + constexpr unsigned int DS3231_I2C_ADDRESS {0x68}; + + /** + * @brief constant array for days in month, used for calc days in year + * + */ + constexpr uint8_t daysInMonth[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; +} \ No newline at end of file diff --git a/src/DS3231-RTC_Tools.h b/src/DS3231-RTC_Tools.h new file mode 100644 index 0000000..eb0f72a --- /dev/null +++ b/src/DS3231-RTC_Tools.h @@ -0,0 +1,65 @@ +/** + * @file DS3231-RTC_Tools.h + * @author Frank Häfele + * @brief Tools for the DS3231-RTC lib + */ + +#pragma once +#include "DS3231-RTC_Constants.h" + +namespace DS3231_Tools { + /** + * @brief Convert value to binary coded decimal + * + * @param value decimal value + * @return uint8_t binary coded decimal value + */ + constexpr inline uint8_t decToBcd(uint8_t value) { + return value + 6 * (value / 10); + } + + /** + * @brief Convert binary coded decimal to decimal number + * @param value binary coded decimal value + * @return uint8_t decimal value + */ + constexpr inline uint8_t bcdToDec(uint8_t value) { + return value - 6 * (value >> 4); + } + + /** + * @brief function which calculates if a year is a leap year + * + * @param year + * @return true + * @return false + */ + constexpr inline bool isleapYear(const int16_t year) { + // check if divisible by 4 + if (year % 4) { + return false; + } + // only check OR (second condition), when first failed + return (year % 100 || year % 400 == 0); + } + + /** + * @brief calculate the days since January 1 (0...365) + * + * @param year e.g.: 2022 + * @param month 1...12 + * @param day 1...31 + * @return int16_t + */ + constexpr int16_t calcYearDay(const int16_t year, const int8_t month, + const int8_t day) { + uint16_t days = day - 1; + for (uint8_t i = 1; i < month; ++i) { + days += DS3231_Constants::daysInMonth[i-1]; + } + if (month > 2 && isleapYear(year)) { + ++days; + } + return days; + } +} // namespace DS3231_Tools diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100755 index 0000000..44c5410 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,16 @@ +add_executable(test_DS3231 + ../src/DS3231-RTC.cpp + test_DS3231.cpp +) + +target_include_directories(test_DS3231 PRIVATE + ../src +) + +target_link_libraries(test_DS3231 + GTest::gmock + GTest::gtest_main +) + +include(GoogleTest) +gtest_discover_tests(test_DS3231) \ No newline at end of file diff --git a/test/test_DS3231.cpp b/test/test_DS3231.cpp new file mode 100755 index 0000000..0591ad8 --- /dev/null +++ b/test/test_DS3231.cpp @@ -0,0 +1,176 @@ +#include +#include + +#include +#include "DS3231-RTC.h" +using ::testing::Return; + +TEST(DS3231Tools_BCD, BinaryDecodedDecimalToDecimal_0) { + EXPECT_EQ(DS3231_Tools::bcdToDec(0x0), 0U); +} + +TEST(DS3231Tools_BCD, BinaryDecodedDecimalToDecimal_9) { + EXPECT_EQ(DS3231_Tools::bcdToDec(0x9), 9U); +} + +TEST(DS3231Tools_BCD, BinaryDecodedDecimalToDecimal_10) { + EXPECT_EQ(DS3231_Tools::bcdToDec(0x10), 10U); +} + +TEST(DS3231Tools_BCD, BinaryDecodedDecimalToDecimal_59) { + EXPECT_EQ(DS3231_Tools::bcdToDec(0x59), 59U); +} + +TEST(DS3231Tools_BCD, DecimalToBinaryDecodedDecimal_0) { + EXPECT_EQ(DS3231_Tools::decToBcd(0U), 0x0); +} + +TEST(DS3231Tools_BCD, DecimalToBinaryDecodedDecimal_9) { + EXPECT_EQ(DS3231_Tools::decToBcd(9U), 0x9); +} + +TEST(DS3231Tools_BCD, DecimalToBinaryDecodedDecimal_10) { + EXPECT_EQ(DS3231_Tools::decToBcd(10U), 0x10); +} + +TEST(DS3231Tools_BCD, DecimalToBinaryDecodedDecimal_59) { + EXPECT_EQ(DS3231_Tools::decToBcd(59U), 0x59); +} + +TEST(DS3231Tools_leapYear, IsLeapYear_2023) { + EXPECT_FALSE(DS3231_Tools::isleapYear(2023)); +} + +TEST(DS3231Tools_leapYear, IsLeapYear_2024) { + EXPECT_TRUE(DS3231_Tools::isleapYear(2024)); +} + +TEST(DS3231Tools_leapYear, IsLeapYear_2032) { + EXPECT_TRUE(DS3231_Tools::isleapYear(2032)); +} + +TEST(DS3231Tools_leapYear, IsLeapYear_1700) { + EXPECT_FALSE(DS3231_Tools::isleapYear(1700)); +} + +TEST(DS3231Tools_leapYear, IsLeapYear_1800) { + EXPECT_FALSE(DS3231_Tools::isleapYear(1800)); +} + +TEST(DS3231Tools_leapYear, IsLeapYear_1900) { + EXPECT_FALSE(DS3231_Tools::isleapYear(1900)); +} + +TEST(DS3231Tools_leapYear, IsLeapYear_2000) { + EXPECT_TRUE(DS3231_Tools::isleapYear(2000)); +} + +class MockBus : public DS3231::BusInterface { + public: + MOCK_METHOD(void, beginTransmission, (uint8_t address), (override)); + MOCK_METHOD(size_t, write, (uint8_t value), (override)); + MOCK_METHOD(uint8_t, endTransmission, (), (override)); + MOCK_METHOD(uint8_t, requestFrom, (uint8_t address, uint8_t quantity), (override)); + MOCK_METHOD(int, read, (), (override)); + MOCK_METHOD(int, available, (), (override)); +}; + +class DS3231MockTest : public ::testing::Test { + protected: + MockBus bus; + DS3231::DS3231 rtc{bus}; +}; + +TEST_F(DS3231MockTest, GetSecondReadsSecondRegister) { + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x00)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + EXPECT_CALL(bus, requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1)).WillOnce(Return(1)); + EXPECT_CALL(bus, read()).WillOnce(Return(0x59)); + // DS3231 holds value in binary decoded decimal here 0x59 for 59 decimal + EXPECT_EQ(rtc.getSecond(), 59); +} + +TEST_F(DS3231MockTest, GetMinuteReadsMinuteRegister) { + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x01)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + EXPECT_CALL(bus, requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1)).WillOnce(Return(1)); + EXPECT_CALL(bus, read()).WillOnce(Return(0x42)); + + EXPECT_EQ(rtc.getMinute(), 42); +} + +TEST_F(DS3231MockTest, GetHourReadsFlagsAndBcdValue) { + bool h12 = false; + bool pm = false; + + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x02)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + EXPECT_CALL(bus, requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1)).WillOnce(Return(1)); + EXPECT_CALL(bus, read()).WillOnce(Return(0x63)); + + EXPECT_EQ(rtc.getHour(h12, pm), 3U); + EXPECT_TRUE(h12); + EXPECT_TRUE(pm); +} + +TEST_F(DS3231MockTest, SetHourWritesUpdatedHourRegister) { + ::testing::InSequence sequence; + + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x02)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + EXPECT_CALL(bus, requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1)).WillOnce(Return(1)); + EXPECT_CALL(bus, read()).WillOnce(Return(0x00)); + + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x02)).WillOnce(Return(1)); + EXPECT_CALL(bus, write(0x23)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + + rtc.setHour(23); +} + +TEST_F(DS3231MockTest, GetDoWReadsDoWRegister) { + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x03)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + EXPECT_CALL(bus, requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1)).WillOnce(Return(1)); + EXPECT_CALL(bus, read()).WillOnce(Return(0x03)); + + EXPECT_EQ(rtc.getDoW(), 3U); +} + +TEST_F(DS3231MockTest, GetDayReadsDateRegister) { + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x04)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + EXPECT_CALL(bus, requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1)).WillOnce(Return(1)); + EXPECT_CALL(bus, read()).WillOnce(Return(0x14)); + + EXPECT_EQ(rtc.getDate(), 14U); +} + +TEST_F(DS3231MockTest, GetMonthReadsMonthRegister) { + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x05)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + EXPECT_CALL(bus, requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1)).WillOnce(Return(1)); + EXPECT_CALL(bus, read()).WillOnce(Return(0x12)); + + bool century; + EXPECT_EQ(rtc.getMonth(century), 12U); +} + +TEST_F(DS3231MockTest, GetYearReadsYearRegister) { + EXPECT_CALL(bus, beginTransmission(DS3231_Constants::DS3231_I2C_ADDRESS)); + EXPECT_CALL(bus, write(0x06)).WillOnce(Return(1)); + EXPECT_CALL(bus, endTransmission()).WillOnce(Return(0)); + EXPECT_CALL(bus, requestFrom(DS3231_Constants::DS3231_I2C_ADDRESS, 1)).WillOnce(Return(1)); + // write Year 22 (bcd 0x22 => dec 22) + EXPECT_CALL(bus, read()).WillOnce(Return(0x22)); + + EXPECT_EQ(rtc.getYear(), 22U); +} \ No newline at end of file