RAK12002 A Real-Time Clock module that can help you map your terrain

In the previous post, we learned how to use the WisBlock GNSS Location Module to get and display GNSS data on an OLED, including the time. We were using the GNSS time to set up a Real-Time Clock, one I had on hand, by Mikroe, using our WisBlock Sensor Adapter Module. I have since received our WisBlock RTC module, so we can have another look at the code and adapt the Arduino sketch to lighten the load (we're replacing a large-sih module with a 10 x 10 mm small pill of a module!) and make things more streamlined.

At the heart of the module is an RV-3028 C7 Real-Time Clock Module, which is optimized for extreme low power consumption. It has a wide operating voltage range, 1.1 to 5.5 V, and uses the I²C bus interface at 400 kHz. It has a lofty 43 bytes (!!!) of non-volatile user memory, which could be used to save basic settings, without having to add an EEPROM. Perfect for IoT and low-power requirements.

Originally I started using the RV-3028 C7 Arduino Library, which has a rich API that reflects the capabilities of the RTC module. However, we recommend to use the Melopero library, rather, as its user EEPROM-related functions work, as opposed to RV-3028. More about that below.

One thing of note is the trickle charging circuit, which allows a battery or super-capacitor connected to the VBACKUP pin to be charged from the power supply connected to the VDD pin. We took full advantage of that: in the schematics below, you can see that we have 2 super-capacitors, C2 and C3, connected to VBAK. By default it uses C2 at 70 mF/3.3 V. C3, at 11 mF/3.3 V is reserved. The library has two functions for this:

enableTrickleCharge(uint8_t tcr = TCR_15K)
disableTrickleCharge()

Values for enableTrickleCharge are:

  • TCR_3K for 3k Ohm
  • TCR_5K for 5k Ohm
  • TCR_9K for 9k Ohm
  • TCR_15K for 15k Ohm

The Trickle Charger

In real-life tests, we were able to squeeze 7 days out of the C2 super-capacitor, no pill battery required! That's quite good for such a small module.

Time functions

General functions

  • initDevice();

Setting/Getting the time

  • getSecond();

  • getMinute();

  • getHour();

  • getWeekday();

  • getDate();

  • getMonth();

  • getYear();

  • getUnixTime();

  • set24HourMode();

  • set12HourMode();

  • is12HourMode();

  • isPM();

  • to12HourFormat();

  • to24HourFormat();

  • setTime();

Alarm-related

  • isDateModeForAlarm();
  • setDateModeForAlarm(flag);
  • enableAlarm();
  • disableAlarm();
  • enablePeriodicTimer();
  • disablePeriodicTimer();
  • enablePeriodicTimeUpdate();
  • disablePeriodicTimeUpdate();
  • clearInterruptFlags();

EEPROM-related

  • readFromRegister();
  • writeToRegister();
  • writeToRegisters();
  • andOrRegister();
  • useEEPROM();
  • isEEPROMBusy();
  • readEEPROMRegister();
  • writeEEPROMRegister();

Setting the time is a little less convenient in the Melopero library. You have to use setTime, and contrary to RV-3208, it doesn't return a boolean letting us know whether things went well. You can't set, apparently, the UNIX counter, just read it. Maybe this could be added to Melopero. Likewise, properly-working EEPROM functions could maybe be ported to RV-3208. So, depending on your use case, you might have to go for one or the other.

rtc.setTime(
  g_myGNSS.getSecond(), g_myGNSS.getMinute(), g_myGNSS.getHour(), g_myGNSS.getDay(),
  g_myGNSS.getTimeOfWeek(), g_myGNSS.getMonth(), g_myGNSS.getYear()
);

Checking the RTC time and comparing it with the GNSS time is easy:

if (rtc.getMinute() != g_myGNSS.getMinute() || g_myGNSS.getHour() != rtc.getHour()) {
  Serial.println("Readjusting RTC");
  rtc.setTime(
    g_myGNSS.getSecond(), g_myGNSS.getMinute(), g_myGNSS.getHour(), g_myGNSS.getDay(),
    g_myGNSS.getTimeOfWeek(), g_myGNSS.getMonth(), g_myGNSS.getYear()
  );
}

UNIX Time Counter

The UNIX Time Counter is a 32-bit counter, unsigned integer: it rolls over to 00000000h when reaching
FFFFFFFFh. The 4 bytes (UNIX Time 0 to UNIX Time 3) are fully readable and writable. The counter source clock is the digitally-offset, compensated 1 Hz tick.

The UNIX Time Counter is independent from the RTC, and has some pecularities.

During I2C write access with an access time smaller than 950 ms, the UNIX Time 0 to UNIX Time 3 registers are blocked. Unlike setting the clock and calendar registers, after I2C STOP, a 1-Hz tick can be lost. If the I2C write access takes longer than 950 ms the I2C bus interface is reset by the internal bus timeout function. It is recommended to make a reset of the prescaler before setting the UNIX time(*). The 32-bit UNIX counter value itself does not change during reset.

However, while the RV-3208 library has a bool setUNIX(uint32_t) function to set the UNIX time (and read it with uint32_t getUNIX()), Melopero only has a reading function, uint32_t getUnixTime()

(*) RESET BIT FUNCTION

Again, something that's lacking in the Melopero library, although it's a TODO:

//TODO:
//void reset();
// refactor code to use more andOrRegister instead of a read and a write

In RV-3208it looks like this:

// Bits in Control2 Register
[...]
#define CTRL2_RESET 0

[...]

void RV3028::reset() {
  setBit(RV3028_CTRL2, CTRL2_RESET);
}

Anyway, the documentation for the RTC module is here. Sample code is on GitHub.