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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/helpers/AutoDiscoverRTCClock.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "AutoDiscoverRTCClock.h"
#include "RTClib.h"
#include <Melopero_RV3028.h>
#include "RTC_RX8130CE.h"

static RTC_DS3231 rtc_3231;
static bool ds3231_success = false;
Expand All @@ -11,9 +12,13 @@ static bool rv3028_success = false;
static RTC_PCF8563 rtc_8563;
static bool rtc_8563_success = false;

static RTC_RX8130CE rtc_8130;
static bool rtc_8130_success = false;

#define DS3231_ADDRESS 0x68
#define RV3028_ADDRESS 0x52
#define PCF8563_ADDRESS 0x51
#define RX8130CE_ADDRESS 0x32

bool AutoDiscoverRTCClock::i2c_probe(TwoWire& wire, uint8_t addr) {
wire.beginTransmission(addr);
Expand All @@ -25,22 +30,32 @@ void AutoDiscoverRTCClock::begin(TwoWire& wire) {
if (i2c_probe(wire, DS3231_ADDRESS)) {
ds3231_success = rtc_3231.begin(&wire);
}

if (i2c_probe(wire, RV3028_ADDRESS)) {
rtc_rv3028.initI2C(wire);
rtc_rv3028.writeToRegister(0x35, 0x00);
rtc_rv3028.writeToRegister(0x37, 0xB4); // Direct Switching Mode (DSM): when VDD < VBACKUP, switchover occurs from VDD to VBACKUP
rtc_rv3028.set24HourMode(); // Set the device to use the 24hour format (default) instead of the 12 hour format
rtc_rv3028.writeToRegister(0x35, 0x00);
rtc_rv3028.writeToRegister(0x37, 0xB4); // Direct Switching Mode (DSM): when VDD < VBACKUP, switchover occurs from VDD to VBACKUP
rtc_rv3028.set24HourMode(); // Set the device to use the 24hour format (default) instead of the 12 hour format
rv3028_success = true;
}
if(i2c_probe(wire,PCF8563_ADDRESS)){

if (i2c_probe(wire, PCF8563_ADDRESS)) {
rtc_8563_success = rtc_8563.begin(&wire);
}

if (i2c_probe(wire, RX8130CE_ADDRESS)) {
MESH_DEBUG_PRINTLN("RX8130CE: Found");
rtc_8130.begin(&wire);
rtc_8130_success = true;
MESH_DEBUG_PRINTLN("RX8130CE: Initialized");
}
Comment on lines +46 to +51
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: begin() return value is ignored. Unlikely to fail since i2c_probe() already confirmed the device is present, but worth checking defensively to match the pattern of the other RTCs above.

Suggested change
if (i2c_probe(wire, RX8130CE_ADDRESS)) {
MESH_DEBUG_PRINTLN("RX8130CE: Found");
rtc_8130.begin(&wire);
rtc_8130_success = true;
MESH_DEBUG_PRINTLN("RX8130CE: Initialized");
}
if (i2c_probe(wire, RX8130CE_ADDRESS)) {
MESH_DEBUG_PRINTLN("RX8130CE: Found");
rtc_8130_success = rtc_8130.begin(&wire);
if (rtc_8130_success) {
MESH_DEBUG_PRINTLN("RX8130CE: Initialized");
}
}

}

uint32_t AutoDiscoverRTCClock::getCurrentTime() {
if (ds3231_success) {
return rtc_3231.now().unixtime();
}

if (rv3028_success) {
return DateTime(
rtc_rv3028.getYear(),
Expand All @@ -51,9 +66,16 @@ uint32_t AutoDiscoverRTCClock::getCurrentTime() {
rtc_rv3028.getSecond()
).unixtime();
}
if(rtc_8563_success){

if (rtc_8563_success) {
return rtc_8563.now().unixtime();
}

if (rtc_8130_success) {
MESH_DEBUG_PRINTLN("RX8130CE: Reading time");
return rtc_8130.now().unixtime();
}

return _fallback->getCurrentTime();
}

Expand All @@ -66,6 +88,9 @@ void AutoDiscoverRTCClock::setCurrentTime(uint32_t time) {
rtc_rv3028.setTime(dt.year(), dt.month(), weekday, dt.day(), dt.hour(), dt.minute(), dt.second());
} else if (rtc_8563_success) {
rtc_8563.adjust(DateTime(time));
} else if (rtc_8130_success) {
MESH_DEBUG_PRINTLN("RX8130CE: Setting time");
rtc_8130.adjust(DateTime(time));
} else {
_fallback->setCurrentTime(time);
}
Expand Down
197 changes: 197 additions & 0 deletions src/helpers/RTC_RX8130CE.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
#include "RTC_RX8130CE.h"
#include "RTClib.h"


bool RTC_RX8130CE::stop(bool stop) {
write_register(0x1E, stop ? 0x040 : 0x00);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
write_register(0x1E, stop ? 0x040 : 0x00);
write_register(0x1E, stop ? 0x40 : 0x00);

return true;
}

bool RTC_RX8130CE::begin(TwoWire *wire) {
if (i2c_dev) {
delete i2c_dev;
}

i2c_dev = new Adafruit_I2CDevice(this->_addr, wire);
if (!i2c_dev->begin()) {
return false;
}

Comment on lines +16 to +19
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Per the RX8130CE datasheet init flowchart (fig. 42, page 55), after a power loss the VLF flag (bit 1 of Flag Register 0x1D) indicates clock data may be unreliable. A dummy read followed by a VLF check should happen before the register configuration. See also ArtronShop/ArtronShop_RX8130CE#1

Suggested change
if (!i2c_dev->begin()) {
return false;
}
if (!i2c_dev->begin()) {
return false;
}
// Dummy read per init flowchart (page 55)
read_register(0x1D);
// Check VLF flag (bit 1 of Flag Register 0x1D) — indicates data loss
uint8_t flags = read_register(0x1D);
if (flags & 0x02) {
// VLF set: clock data unreliable after power loss, clear flag
write_register(0x1D, flags & ~0x02);
}

/*
* Digital offset register:
* [7] DET: 0 -> disabled
* [6:0] L7-L1: 0 -> no offset
*/
write_register(0x30, 0x00);

/*
* Extension Register register:
* [7:6] FSEL: 0 -> 0
* [5] USEL: 0 -> 0
* [4] TE: 0 ->
* [3] WADA: 0 -> 0
* [2-0] TSEL: 0 -> 0
*/
write_register(0x1C, 0x00);

/*
* Flag Register register:
* [7] VBLF: 0 -> 0
* [6] 0: 0 ->
* [5] UF: 0 ->
* [4] TF: 0 ->
* [3] AF: 0 -> 0
* [2] RSF: 0 -> 0
* [1] VLF: 0 -> 0
* [0] VBFF: 0 -> 0
*/
write_register(0x1D, 0x00);

/*
* Control Register0 register:
* [7] TEST: 0 -> 0
* [6] STOP: 0 ->
* [5] UIE: 0 ->
* [4] TIE: 0 ->
* [3] AIE: 0 -> 0
* [2] TSTP: 0 -> 0
* [1] TBKON: 0 -> 0
* [0] TBKE: 0 -> 0
*/
write_register(0x1E, 0x00);

/*
* Control Register1 register:
* [7-6] SMPTSEL: 0 -> 0
* [5] CHGEN: 0 ->
* [4] INIEN: 0 ->
* [3] 0: 0 ->
* [2] RSVSEL: 0 -> 0
* [1-0] BFVSEL: 0 -> 0
*/
write_register(0x1F, 0x00);

this->stop(false); // clear STOP bit

/*
* Function register:
* [7] 100TH: 0 -> disabled
* [6:5] Periodic interrupt: 0 -> no periodic interrupt
* [4] RTCM: 0 -> real-time clock mode
* [3] STOPM: 0 -> RTC stop is controlled by STOP bit only
* [2:0] Clock output frequency: 000 (Default value)
*/
write_register(0x28, 0x00);

// Battery switch register
write_register(0x26, 0x00); // enable battery switch feature

return true;
}

bool RTC_RX8130CE::setTime(struct tm *t) {
uint8_t buf[8];
buf[0] = 0x10;
buf[1] = bin2bcd(t->tm_sec) & 0x7F;
buf[2] = bin2bcd(t->tm_min) & 0x7F;
buf[3] = bin2bcd(t->tm_hour) & 0x3F;
buf[4] = bin2bcd(t->tm_wday) & 0x07;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RX8130CE WEEK register uses a bitmap format (bit 0 = Sun, bit 1 = Mon, ..., bit 6 = Sat), not BCD. For example Wednesday (tm_wday=3) should be 0x08 (bit 3), but bin2bcd(3) gives 0x03 (bits 0+1 = Sun+Mon). The ArtronShop library has the same bug - opened a fix upstream: ArtronShop/ArtronShop_RX8130CE#1

Suggested change
buf[4] = bin2bcd(t->tm_wday) & 0x07;
buf[4] = (1 << t->tm_wday) & 0x7F;

buf[5] = bin2bcd(t->tm_mday) & 0x3F;
buf[6] = bin2bcd(t->tm_mon + 1) & 0x1F;
buf[7] = bin2bcd((t->tm_year - 100));

this->stop(true);
i2c_dev->write(buf, sizeof(buf));
this->stop(false);

return true;
}

void RTC_RX8130CE::adjust(DateTime dt) {
struct tm *atv;
time_t utime;

utime = (time_t)dt.unixtime();
atv = gmtime(&utime);

this->setTime(atv);
}

DateTime RTC_RX8130CE::now() {
struct tm atv;
this->getTime(&atv);

return DateTime((uint32_t)mktime(&atv));
}

uint32_t RTC_RX8130CE::unixtime() {
struct tm atv;
this->getTime(&atv);

return (uint32_t)mktime(&atv);
}

bool RTC_RX8130CE::getTime(struct tm *t) {
uint8_t buff[7];

buff[0] = 0x10;

i2c_dev->write_then_read(buff, 1, buff, 7);

t->tm_sec = bcd2bin(buff[0] & 0x7F);
t->tm_min = bcd2bin(buff[1] & 0x7F);
t->tm_hour = bcd2bin(buff[2] & 0x3F);
t->tm_wday = bcd2bin(buff[3] & 0x07);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same bitmap issue as setTime() — the WEEK register is a bitmap, not BCD. This needs to find which bit is set and convert it back to an index. See ArtronShop/ArtronShop_RX8130CE#1

Suggested change
t->tm_wday = bcd2bin(buff[3] & 0x07);
// WEEK register is a bitmap (bit 0=Sun, bit 1=Mon, ..., bit 6=Sat)
uint8_t wday_bits = buff[3] & 0x7F;
t->tm_wday = 0;
while (wday_bits >>= 1) t->tm_wday++;

t->tm_mday = bcd2bin(buff[4] & 0x3F);
t->tm_mon = bcd2bin(buff[5] & 0x1F) - 1;
t->tm_year = bcd2bin(buff[6]) + 100;

return true;
}

bool RTC_RX8130CE::writeRAM(uint8_t address, uint8_t value) {
return this->writeRAM(address, &value, 1);
}

size_t RTC_RX8130CE::writeRAM(uint8_t address, uint8_t *value, size_t len) {
uint8_t buf[len + 1];

if (address > 3) {
return 0;
}

if ((address + len) > 3) {
len = 3 - address;
}

buf[0] = 0x20 + address;

for (int i = 1; i <= len + 1; i++) {
buf[i] = value[i - 1];
}
Comment on lines +169 to +171
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
for (int i = 1; i <= len + 1; i++) {
buf[i] = value[i - 1];
}
for (int i = 1; i <= len; i++) {
buf[i] = value[i - 1];
}


i2c_dev->write(buf, len + 1);

return len;
}

bool RTC_RX8130CE::readRAM(uint8_t address, uint8_t *value, size_t len) {
uint8_t real_address = 0x20 + address;

if (address > 3) { // Oversize of 64-bytes RAM
return false;
}

if ((address + len) > 3) { // Data size over RAM size
len = 3 - address;
}

i2c_dev->write_then_read(&real_address, 1, value, len);
return true;
}

uint8_t RTC_RX8130CE::readRAM(uint8_t address) {
uint8_t value = 0xFF;
this->readRAM(address, &value, 1);
return value;
}
33 changes: 33 additions & 0 deletions src/helpers/RTC_RX8130CE.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#ifndef __RTC_RX8130CE_H__
#define __RTC_RX8130CE_H__

#include <Arduino.h>
#include <Wire.h>
#include <time.h>
#include "RTClib.h"

class RTC_RX8130CE : RTC_I2C {
private:
const uint8_t _addr = 0x32;

bool stop(bool stop);

protected:

public:
bool begin(TwoWire *wire);
bool setTime(struct tm *t);
bool getTime(struct tm *t);
void adjust(DateTime t);

DateTime now();
uint32_t unixtime();

bool writeRAM(uint8_t address, uint8_t value);
size_t writeRAM(uint8_t address, uint8_t *value, size_t len);
bool readRAM(uint8_t address, uint8_t *value, size_t len);
uint8_t readRAM(uint8_t address);

};

#endif
66 changes: 66 additions & 0 deletions variants/muziworks_r1_neo/R1NeoBoard.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#include <Arduino.h>
#include <Wire.h>

#include "R1NeoBoard.h"

#ifdef NRF52_POWER_MANAGEMENT
// Static configuration for power management
// Values set in variant.h defines
const PowerMgtConfig power_config = {
.lpcomp_ain_channel = PWRMGT_LPCOMP_AIN,
.lpcomp_refsel = PWRMGT_LPCOMP_REFSEL,
.voltage_bootlock = PWRMGT_VOLTAGE_BOOTLOCK
};

void R1NeoBoard::initiateShutdown(uint8_t reason) {
// Disable LoRa module power before shutdown
MESH_DEBUG_PRINTLN("R1Neo: shutting down");
digitalWrite(SX126X_POWER_EN, LOW);

if (reason == SHUTDOWN_REASON_LOW_VOLTAGE ||
reason == SHUTDOWN_REASON_BOOT_PROTECT) {
configureVoltageWake(power_config.lpcomp_ain_channel, power_config.lpcomp_refsel);
}

enterSystemOff(reason);
}
#endif // NRF52_POWER_MANAGEMENT

void R1NeoBoard::begin() {
// R1 Neo peculiarity: tell DCDC converter to stay powered.
// Must be done as soon as practical during boot.

pinMode(PIN_DCDC_EN_MCU_HOLD, OUTPUT);
digitalWrite(PIN_DCDC_EN_MCU_HOLD, HIGH);

// R1 Neo peculiarity: Tell I/O Controller device is on
// Enables passthrough of buttons and LEDs

pinMode(PIN_SOFT_SHUTDOWN, OUTPUT);
digitalWrite(PIN_SOFT_SHUTDOWN, HIGH);

NRF52BoardDCDC::begin();

// button is active high and passed through from I/O controller
pinMode(PIN_USER_BTN, INPUT);

pinMode(PIN_BUZZER, OUTPUT);
digitalWrite(PIN_BUZZER, LOW);

// battery pins
pinMode(PIN_BAT_CHG, INPUT);
pinMode(PIN_VBAT_READ, INPUT);

Wire.setPins(PIN_WIRE_SDA, PIN_WIRE_SCL);

Wire.begin();

pinMode(SX126X_POWER_EN, OUTPUT);
#ifdef NRF52_POWER_MANAGEMENT
// Boot voltage protection check (may not return if voltage too low)
// We need to call this after we configure SX126X_POWER_EN as output but before we pull high
checkBootVoltage(&power_config);
#endif
digitalWrite(SX126X_POWER_EN, HIGH);
delay(10); // give sx1262 some time to power up
}
Loading
Loading