mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-09-01 21:08:19 +00:00
wire up RTC to out-of-the-box capable nodes
This commit is contained in:
@@ -444,6 +444,12 @@ target_sources(app PRIVATE
|
||||
helpers/fatal_reboot.c
|
||||
)
|
||||
|
||||
# Boot-time hardware-RTC auto-discovery (compact raw-I2C). Always compiled so
|
||||
# the zephcore_rtc_* symbols exist on every board; it self-stubs internally
|
||||
# when CONFIG_ZEPHCORE_RTC_AUTODISCOVER is off or no zephcore,rtc-i2c node is
|
||||
# present in DT.
|
||||
target_sources(app PRIVATE adapters/clock/ZephyrRTCDiscover.c)
|
||||
|
||||
# ========== Battery Curve Selection ==========
|
||||
# helpers/battery_curve.c is always compiled — it provides battery_curve_lookup()
|
||||
# and the weak battery_curve_default. A board-specific battery_curve.c is compiled
|
||||
|
||||
@@ -523,6 +523,20 @@ config ZEPHCORE_APC
|
||||
|
||||
endmenu
|
||||
|
||||
config ZEPHCORE_RTC_AUTODISCOVER
|
||||
bool "Auto-discover a hardware I2C RTC at boot"
|
||||
default y
|
||||
depends on I2C
|
||||
help
|
||||
Probe I2C RTC chips declared with the "zephcore,rtc-i2c" binding
|
||||
(DS3231 / PCF8563 / RV3028 / RX8130CE). Boards with a battery/cap-
|
||||
backed RTC opt in by including boards/common/rtc-i2c.dtsi; if a chip
|
||||
holds a valid time it is restored at boot — shown tagged "L" until the
|
||||
next external sync. On every GPS/app/CLI sync the time is written back
|
||||
so it survives power-off. Compact raw-I2C reader (no Zephyr RTC
|
||||
subsystem). Safe to leave on: a board with no rtc-i2c.dtsi include has
|
||||
no RTC nodes and compiles to nothing.
|
||||
|
||||
menu "GPS Configuration"
|
||||
|
||||
config ZEPHCORE_GPS_POLL_INTERVAL_SEC
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Compact raw-I2C hardware-RTC auto-discovery. See ZephyrRTCDiscover.h.
|
||||
*
|
||||
* Register layouts (sec/min/hour/.../month/year, all BCD) and the per-chip
|
||||
* power-loss flags are carried in devicetree via the "zephcore,rtc-i2c"
|
||||
* binding, so this reader is generic — adding a new chip is a DT node, not
|
||||
* code. Maps were taken from Zephyr's own drivers (rtc_pcf8563.c,
|
||||
* rtc_ds3231.c, rtc_rv3028.c, rtc_rx8130ce.c).
|
||||
*/
|
||||
|
||||
#include "ZephyrRTCDiscover.h"
|
||||
|
||||
#include <zephyr/kernel.h>
|
||||
#include <zephyr/device.h>
|
||||
#include <zephyr/drivers/i2c.h>
|
||||
#include <zephyr/logging/log.h>
|
||||
|
||||
LOG_MODULE_REGISTER(zephcore_rtc, CONFIG_ZEPHCORE_DATASTORE_LOG_LEVEL);
|
||||
|
||||
#define RTC_COMPAT zephcore_rtc_i2c
|
||||
|
||||
#if IS_ENABLED(CONFIG_ZEPHCORE_RTC_AUTODISCOVER) && DT_HAS_COMPAT_STATUS_OKAY(RTC_COMPAT)
|
||||
|
||||
/* Validity flag lives in the seconds byte itself (PCF8563 VL bit). */
|
||||
#define RTC_STATUS_IN_SECONDS 0xFF
|
||||
|
||||
struct rtc_desc {
|
||||
const struct device *bus;
|
||||
uint16_t addr;
|
||||
uint8_t time_reg; /* register of the seconds byte */
|
||||
uint8_t date_index; /* day-of-month offset in the 7-byte block */
|
||||
uint8_t status_reg; /* power-loss flag register, or RTC_STATUS_IN_SECONDS */
|
||||
uint8_t status_mask; /* "time unreliable" bit within status_reg */
|
||||
const char *name;
|
||||
};
|
||||
|
||||
#define RTC_DESC_ENTRY(node) \
|
||||
{ \
|
||||
.bus = DEVICE_DT_GET(DT_BUS(node)), \
|
||||
.addr = (uint16_t)DT_REG_ADDR(node), \
|
||||
.time_reg = (uint8_t)DT_PROP(node, time_reg), \
|
||||
.date_index = (uint8_t)DT_PROP(node, date_index), \
|
||||
.status_reg = (uint8_t)DT_PROP(node, status_reg), \
|
||||
.status_mask = (uint8_t)DT_PROP(node, status_mask), \
|
||||
.name = DT_NODE_FULL_NAME(node), \
|
||||
},
|
||||
|
||||
static const struct rtc_desc rtc_descs[] = {
|
||||
DT_FOREACH_STATUS_OKAY(RTC_COMPAT, RTC_DESC_ENTRY)
|
||||
};
|
||||
|
||||
/* Chip we'll read/write going forward (first one found present). */
|
||||
static const struct rtc_desc *s_active;
|
||||
static bool s_probed;
|
||||
|
||||
#define BCD2BIN(x) ((((x) >> 4) & 0x0F) * 10 + ((x) & 0x0F))
|
||||
#define BIN2BCD(x) ((((x) / 10) << 4) | ((x) % 10))
|
||||
|
||||
/* A byte is valid BCD if both nibbles are 0-9, and its decoded value fits the
|
||||
* field. Used to tell a real RTC apart from an unrelated I2C chip that happens
|
||||
* to share an address (e.g. an MPU-class IMU at 0x68, same as DS3231) — we must
|
||||
* never adopt and write time into such a device. */
|
||||
static bool bcd_field_ok(uint8_t v, unsigned max)
|
||||
{
|
||||
if ((v & 0x0F) > 9 || (v >> 4) > 9) {
|
||||
return false;
|
||||
}
|
||||
return BCD2BIN(v) <= max;
|
||||
}
|
||||
|
||||
/* Howard Hinnant's civil<->days algorithms (proleptic Gregorian, UTC). */
|
||||
static int64_t days_from_civil(int y, unsigned m, unsigned d)
|
||||
{
|
||||
y -= (m <= 2);
|
||||
int64_t era = (y >= 0 ? y : y - 399) / 400;
|
||||
unsigned yoe = (unsigned)(y - era * 400);
|
||||
unsigned doy = (153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1;
|
||||
unsigned doe = yoe * 365 + yoe / 4 - yoe / 100 + doy;
|
||||
return era * 146097 + (int)doe - 719468;
|
||||
}
|
||||
|
||||
static void civil_from_days(int64_t z, int *y, unsigned *m, unsigned *d)
|
||||
{
|
||||
z += 719468;
|
||||
int64_t era = (z >= 0 ? z : z - 146096) / 146097;
|
||||
unsigned doe = (unsigned)(z - era * 146097);
|
||||
unsigned yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
int yy = (int)yoe + (int)(era * 400);
|
||||
unsigned doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
unsigned mp = (5 * doy + 2) / 153;
|
||||
*d = doy - (153 * mp + 2) / 5 + 1;
|
||||
*m = mp < 10 ? mp + 3 : mp - 9;
|
||||
*y = yy + (*m <= 2);
|
||||
}
|
||||
|
||||
/* Read the chip's power-loss flag. true => held time is unreliable. */
|
||||
static bool rtc_time_unreliable(const struct rtc_desc *d, const uint8_t blk[7])
|
||||
{
|
||||
if (d->status_reg == RTC_STATUS_IN_SECONDS) {
|
||||
return (blk[0] & d->status_mask) != 0;
|
||||
}
|
||||
|
||||
uint8_t st;
|
||||
if (i2c_reg_read_byte(d->bus, d->addr, d->status_reg, &st) != 0) {
|
||||
return true; /* can't confirm => don't trust it */
|
||||
}
|
||||
return (st & d->status_mask) != 0;
|
||||
}
|
||||
|
||||
/* Probe all chips once; cache the first present one in s_active. If a present
|
||||
* chip holds a sane time, return it via epoch_out. */
|
||||
static bool rtc_probe(uint32_t *epoch_out)
|
||||
{
|
||||
for (size_t i = 0; i < ARRAY_SIZE(rtc_descs); i++) {
|
||||
const struct rtc_desc *d = &rtc_descs[i];
|
||||
uint8_t blk[7];
|
||||
|
||||
if (!device_is_ready(d->bus)) {
|
||||
continue;
|
||||
}
|
||||
if (i2c_burst_read(d->bus, d->addr, d->time_reg, blk, sizeof(blk)) != 0) {
|
||||
continue; /* no ACK => chip absent */
|
||||
}
|
||||
|
||||
/* Mask off flag/century bits, then require the block to be valid
|
||||
* BCD. This is what proves a real RTC lives here (vs. an unrelated
|
||||
* chip sharing the address) — only then is it safe to adopt and
|
||||
* later WRITE to. A factory-fresh RTC reads a clean 2000-01-01,
|
||||
* which is valid BCD (just stale), so it's still adopted. */
|
||||
uint8_t sb = blk[0] & 0x7F, mb = blk[1] & 0x7F, hb = blk[2] & 0x3F;
|
||||
uint8_t db = blk[d->date_index] & 0x3F, ob = blk[5] & 0x1F, yb = blk[6];
|
||||
|
||||
if (!bcd_field_ok(sb, 59) || !bcd_field_ok(mb, 59) ||
|
||||
!bcd_field_ok(hb, 23) || !bcd_field_ok(db, 31) ||
|
||||
!bcd_field_ok(ob, 12) || !bcd_field_ok(yb, 99) ||
|
||||
BCD2BIN(db) < 1 || BCD2BIN(ob) < 1) {
|
||||
continue; /* not a real RTC at this address — do not adopt */
|
||||
}
|
||||
|
||||
if (s_active == NULL) {
|
||||
s_active = d; /* confirmed RTC => our write-back target */
|
||||
}
|
||||
|
||||
if (rtc_time_unreliable(d, blk)) {
|
||||
LOG_WRN("%s present but time flagged unreliable", d->name);
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned sec = BCD2BIN(sb);
|
||||
unsigned min = BCD2BIN(mb);
|
||||
unsigned hour = BCD2BIN(hb);
|
||||
unsigned day = BCD2BIN(db);
|
||||
unsigned month = BCD2BIN(ob);
|
||||
unsigned year = 2000 + BCD2BIN(yb);
|
||||
|
||||
if (year < 2025) {
|
||||
LOG_INF("%s present, time not yet set (%04u-%02u-%02u)",
|
||||
d->name, year, month, day);
|
||||
continue; /* RTC adopted for write-back, but no valid time */
|
||||
}
|
||||
|
||||
int64_t e = days_from_civil((int)year, month, day) * 86400LL +
|
||||
hour * 3600 + min * 60 + sec;
|
||||
if (epoch_out) {
|
||||
*epoch_out = (uint32_t)e;
|
||||
}
|
||||
LOG_INF("RTC %s: restored %04u-%02u-%02u %02u:%02u:%02u UTC",
|
||||
d->name, year, month, day, hour, min, sec);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool zephcore_rtc_restore(uint32_t *epoch_out)
|
||||
{
|
||||
s_probed = true;
|
||||
return rtc_probe(epoch_out);
|
||||
}
|
||||
|
||||
void zephcore_rtc_save(uint32_t epoch)
|
||||
{
|
||||
if (!s_probed) {
|
||||
/* Restore wasn't run (unexpected) — discover now. */
|
||||
(void)rtc_probe(NULL);
|
||||
s_probed = true;
|
||||
}
|
||||
if (s_active == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const struct rtc_desc *d = s_active;
|
||||
int y;
|
||||
unsigned m, day;
|
||||
civil_from_days((int64_t)epoch / 86400, &y, &m, &day);
|
||||
unsigned rem = epoch % 86400;
|
||||
unsigned hour = rem / 3600;
|
||||
unsigned min = (rem % 3600) / 60;
|
||||
unsigned sec = rem % 60;
|
||||
unsigned dow = (unsigned)(((epoch / 86400) + 4) % 7); /* 1970-01-01 = Thu */
|
||||
|
||||
uint8_t blk[7];
|
||||
blk[0] = BIN2BCD(sec);
|
||||
blk[1] = BIN2BCD(min);
|
||||
blk[2] = BIN2BCD(hour);
|
||||
/* weekday occupies whichever of index 3/4 the date doesn't. */
|
||||
blk[d->date_index] = BIN2BCD(day);
|
||||
blk[d->date_index == 4 ? 3 : 4] = (uint8_t)dow;
|
||||
blk[5] = BIN2BCD(m);
|
||||
blk[6] = BIN2BCD((unsigned)(y % 100));
|
||||
|
||||
if (i2c_burst_write(d->bus, d->addr, d->time_reg, blk, sizeof(blk)) != 0) {
|
||||
LOG_WRN("RTC %s: time write failed", d->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear the power-loss flag (for chips whose flag is a separate reg;
|
||||
* the seconds-bit chips clear it implicitly when we wrote sec above). */
|
||||
if (d->status_reg != RTC_STATUS_IN_SECONDS) {
|
||||
uint8_t st;
|
||||
if (i2c_reg_read_byte(d->bus, d->addr, d->status_reg, &st) == 0) {
|
||||
(void)i2c_reg_write_byte(d->bus, d->addr, d->status_reg,
|
||||
st & (uint8_t)~d->status_mask);
|
||||
}
|
||||
}
|
||||
LOG_DBG("RTC %s: persisted time", d->name);
|
||||
}
|
||||
|
||||
#else /* no zephcore,rtc-i2c node in DT — link-compatible stubs */
|
||||
|
||||
bool zephcore_rtc_restore(uint32_t *epoch_out)
|
||||
{
|
||||
ARG_UNUSED(epoch_out);
|
||||
return false;
|
||||
}
|
||||
|
||||
void zephcore_rtc_save(uint32_t epoch)
|
||||
{
|
||||
ARG_UNUSED(epoch);
|
||||
}
|
||||
|
||||
#endif /* DT_HAS_COMPAT_STATUS_OKAY(zephcore_rtc_i2c) */
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Boot-time hardware-RTC auto-discovery (compact raw-I2C reader).
|
||||
*
|
||||
* Probes every I2C RTC chip declared with the "zephcore,rtc-i2c" binding
|
||||
* (boards/common/rtc-i2c.dtsi, opt-in per board). Chips that aren't physically
|
||||
* present fail the probe and are skipped — like the environment sensors.
|
||||
*
|
||||
* If a present chip holds a valid time, zephcore_rtc_restore() returns it so
|
||||
* the soft clock can be seeded at boot (shown tagged "L" — local). On every
|
||||
* authoritative sync (GPS/app/CLI) the caller writes it back via
|
||||
* zephcore_rtc_save() so time survives the next power-off.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Probe all declared RTC chips. If one is present and holds a sane time
|
||||
* (year >= 2025 and its power-loss flag is clear), store the Unix epoch in
|
||||
* *epoch_out and return true. The present chip (valid time or not) is
|
||||
* remembered as the write-back target. Returns false if none present or no
|
||||
* trustworthy time is held.
|
||||
*/
|
||||
bool zephcore_rtc_restore(uint32_t *epoch_out);
|
||||
|
||||
/*
|
||||
* Persist an authoritative epoch to the discovered RTC chip and clear its
|
||||
* power-loss flag. No-op if no RTC was discovered. Safe to call often, but
|
||||
* intended only for real syncs (GPS/app/CLI), not per-packet clock nudges.
|
||||
*/
|
||||
void zephcore_rtc_save(uint32_t epoch);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <adapters/ble/ZephyrBLE.h>
|
||||
#include <adapters/gps/ZephyrGPSManager.h>
|
||||
#include <helpers/time_sync.h>
|
||||
#include <adapters/clock/ZephyrRTCDiscover.h>
|
||||
#if IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_BUTTON) || IS_ENABLED(CONFIG_ZEPHCORE_UI_DESIGN_JOYSTICK)
|
||||
#include <ui_task.h>
|
||||
#define ZEPHCORE_HAS_UI_TASK 1
|
||||
@@ -2208,6 +2209,7 @@ bool CompanionMesh::handleProtocolFrame(const uint8_t *data, size_t len)
|
||||
if (secs >= curr) {
|
||||
getRTCClock()->setCurrentTime(secs);
|
||||
time_sync_report(TIME_SYNC_APP);
|
||||
zephcore_rtc_save(secs); /* persist to hardware RTC */
|
||||
sendPacketOk();
|
||||
} else {
|
||||
sendPacketError(ERR_ILLEGAL_ARG);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Common I2C Real-Time-Clock descriptors for ZephCore
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* OPT-IN: include this inside an &i2cN { ... } block ONLY on boards that
|
||||
* physically have a battery/cap-backed RTC. Kept separate from
|
||||
* sensors-i2c.dtsi because these declare fixed I2C addresses (0x68/0x51/
|
||||
* 0x52/0x32) that collide with common parts — an IMU/accelerometer sits at
|
||||
* 0x68 (same as DS3231). Including this on a board without an RTC would risk
|
||||
* a devicetree unit-address clash and a runtime mis-probe, so it is opt-in.
|
||||
*
|
||||
* Usage (board overlay/dts, alongside the sensors include):
|
||||
* &i2c0 {
|
||||
* #include "../../common/sensors-i2c.dtsi"
|
||||
* #include "../../common/rtc-i2c.dtsi"
|
||||
* };
|
||||
*
|
||||
* These bind NO Zephyr RTC driver — they're data-only descriptors the
|
||||
* boot-time auto-discovery (adapters/clock/ZephyrRTCDiscover.c) probes via
|
||||
* raw I2C to restore wall-clock time at boot. A chip that isn't actually
|
||||
* present fails the probe and is skipped. One chip per I2C address:
|
||||
*
|
||||
* Chip │ Addr │ Also covers (same regs)
|
||||
* ──────────┼──────┼─────────────────────────
|
||||
* DS3231 │ 0x68 │ DS1307, DS3232
|
||||
* PCF8563 │ 0x51 │ — (PCF85063A is 0x51 too but differs — override)
|
||||
* RV3028 │ 0x52 │ —
|
||||
* RX8130CE │ 0x32 │ — (RV8803 is 0x32 too but differs — override)
|
||||
*
|
||||
* status-reg 0xFF means the power-loss flag is in the seconds byte. A board
|
||||
* with a same-address variant should /delete-node/ the conflicting one and
|
||||
* add its own in the board overlay.
|
||||
*/
|
||||
|
||||
rtc_ds3231: rtc-ds3231@68 {
|
||||
compatible = "zephcore,rtc-i2c";
|
||||
reg = <0x68>;
|
||||
time-reg = <0x00>;
|
||||
date-index = <4>;
|
||||
status-reg = <0x0F> /* control/status */
|
||||
status-mask = <0x80> /* OSF — oscillator stopped */
|
||||
};
|
||||
|
||||
rtc_pcf8563: rtc-pcf8563@51 {
|
||||
compatible = "zephcore,rtc-i2c";
|
||||
reg = <0x51>;
|
||||
time-reg = <0x02>;
|
||||
date-index = <3>; /* PCF8563 puts date before weekday */
|
||||
status-reg = <0xFF> /* flag is in the seconds byte */
|
||||
status-mask = <0x80> /* VL — clock integrity not guaranteed */
|
||||
};
|
||||
|
||||
rtc_rv3028: rtc-rv3028@52 {
|
||||
compatible = "zephcore,rtc-i2c";
|
||||
reg = <0x52>;
|
||||
time-reg = <0x00>;
|
||||
date-index = <4>;
|
||||
status-reg = <0x0e> /* status */
|
||||
status-mask = <0x01> /* PORF — power-on reset */
|
||||
};
|
||||
|
||||
rtc_rx8130ce: rtc-rx8130ce@32 {
|
||||
compatible = "zephcore,rtc-i2c";
|
||||
reg = <0x32>;
|
||||
time-reg = <0x10>;
|
||||
date-index = <4>;
|
||||
status-reg = <0x1d> /* flag */
|
||||
status-mask = <0x02> /* VLF — voltage low */
|
||||
};
|
||||
@@ -103,3 +103,8 @@ ina219: ina219@40 {
|
||||
shunt-milliohm = <100>;
|
||||
lsb-microamp = <10>;
|
||||
};
|
||||
|
||||
/* Real-time clocks are NOT here — they live in the opt-in "rtc-i2c.dtsi"
|
||||
* (included only by boards that physically have a battery-backed RTC), so
|
||||
* their fixed addresses (0x68/0x51/0x52/0x32) can't collide with an IMU or
|
||||
* other peripheral on boards that don't. */
|
||||
|
||||
@@ -299,6 +299,9 @@
|
||||
* devices that aren't populated. T-Echo factory sensor is BME280 at
|
||||
* the alt address 0x77, added separately below. */
|
||||
#include "../../common/sensors-i2c.dtsi"
|
||||
/* T-Echo has an onboard PCF8563 RTC — opt in to boot-time time restore
|
||||
* (auto-detected at runtime among the supported chips). */
|
||||
#include "../../common/rtc-i2c.dtsi"
|
||||
};
|
||||
|
||||
/* T-Echo BME280 sits at I2C 0x77 (alt address), conflicting with the
|
||||
|
||||
@@ -287,6 +287,9 @@
|
||||
|
||||
/* All supported environment & power sensors — auto-detected at runtime */
|
||||
#include "../../common/sensors-i2c.dtsi"
|
||||
/* ThinkNode M1 has a battery-backed hardware RTC — opt in to boot-time
|
||||
* time restore (auto-detected at runtime among the supported chips). */
|
||||
#include "../../common/rtc-i2c.dtsi"
|
||||
};
|
||||
|
||||
/* ---- SPI2 for LoRa SX1262 ---- */
|
||||
|
||||
@@ -208,6 +208,9 @@
|
||||
|
||||
/* All supported environment & power sensors — auto-detected at runtime */
|
||||
#include "../../common/sensors-i2c.dtsi"
|
||||
/* ThinkNode M3 has an onboard PCF8563 RTC — opt in to boot-time time
|
||||
* restore (auto-detected at runtime among the supported chips). */
|
||||
#include "../../common/rtc-i2c.dtsi"
|
||||
};
|
||||
|
||||
/* SPI1 for LR1110 */
|
||||
|
||||
@@ -241,6 +241,9 @@
|
||||
|
||||
/* All supported environment & power sensors — auto-detected at runtime */
|
||||
#include "../../common/sensors-i2c.dtsi"
|
||||
/* ThinkNode M6 has an onboard PCF8563 RTC — opt in to boot-time time
|
||||
* restore (auto-detected at runtime among the supported chips). */
|
||||
#include "../../common/rtc-i2c.dtsi"
|
||||
};
|
||||
|
||||
/* SPI1 for SX1262 LoRa */
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Data-only descriptor for an I2C RTC chip read by ZephyrRTCDiscover
|
||||
# (adapters/clock/ZephyrRTCDiscover.c). This binds NO Zephyr RTC driver —
|
||||
# the chip is accessed with raw I2C using the register layout described by
|
||||
# the properties below. This keeps the boot-time RTC restore compact
|
||||
# (~2-3 KB total, "Path B") instead of pulling in the full RTC subsystem.
|
||||
#
|
||||
# All supported chips use a 7-byte BCD time block (sec, min, hour, then
|
||||
# weekday/date, month, year) starting at "time-reg". Only the day-of-month
|
||||
# byte offset varies between parts, captured by "date-index".
|
||||
|
||||
description: ZephCore data-only I2C RTC descriptor (raw-I2C, no Zephyr RTC driver)
|
||||
|
||||
compatible: "zephcore,rtc-i2c"
|
||||
|
||||
include: [i2c-device.yaml]
|
||||
|
||||
properties:
|
||||
time-reg:
|
||||
type: int
|
||||
required: true
|
||||
description: Register address of the seconds byte (start of the 7-byte BCD block).
|
||||
|
||||
date-index:
|
||||
type: int
|
||||
required: true
|
||||
description: >
|
||||
Byte offset of the day-of-month within the 7-byte block. 4 for chips
|
||||
ordered sec/min/hour/weekday/date/month/year (DS3231, RV3028, RX8130CE);
|
||||
3 for PCF8563 which swaps to sec/min/hour/date/weekday/month/year.
|
||||
|
||||
status-reg:
|
||||
type: int
|
||||
required: true
|
||||
description: >
|
||||
Register holding the "time invalid / power was lost" flag. Use 0xFF to
|
||||
indicate the flag lives in the seconds byte itself (PCF8563 VL bit).
|
||||
|
||||
status-mask:
|
||||
type: int
|
||||
required: true
|
||||
description: Bit mask within status-reg that means the held time is unreliable.
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "CommonCLI.h"
|
||||
#include "battery_curve.h"
|
||||
#include <helpers/time_sync.h>
|
||||
#include <adapters/clock/ZephyrRTCDiscover.h>
|
||||
#include <helpers/TxtDataHelpers.h>
|
||||
#include <helpers/AdvertDataHelpers.h>
|
||||
#include <adapters/board/ZephyrBoard.h>
|
||||
@@ -336,6 +337,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
if (sender_timestamp > curr) {
|
||||
getRTCClock()->setCurrentTime(sender_timestamp + 1);
|
||||
time_sync_report(TIME_SYNC_CLI);
|
||||
zephcore_rtc_save(sender_timestamp + 1); /* persist to hardware RTC */
|
||||
uint32_t now = getRTCClock()->getCurrentTime();
|
||||
time_t t = (time_t)now;
|
||||
struct tm *tm = gmtime(&t);
|
||||
@@ -356,6 +358,7 @@ void CommonCLI::handleCommand(uint32_t sender_timestamp, const char* command, ch
|
||||
if (secs > curr) {
|
||||
getRTCClock()->setCurrentTime(secs);
|
||||
time_sync_report(TIME_SYNC_CLI);
|
||||
zephcore_rtc_save(secs); /* persist to hardware RTC */
|
||||
time_t t = (time_t)secs;
|
||||
struct tm *tm = gmtime(&t);
|
||||
snprintf(reply, CLI_REPLY_SIZE, "OK - clock set: %02d:%02d - %d/%d/%d UTC",
|
||||
|
||||
@@ -21,6 +21,7 @@ LOG_MODULE_REGISTER(zephcore_main, CONFIG_ZEPHCORE_MAIN_LOG_LEVEL);
|
||||
|
||||
#include <ZephyrDataStore.h>
|
||||
#include <adapters/clock/ZephyrRTCClock.h>
|
||||
#include <adapters/clock/ZephyrRTCDiscover.h>
|
||||
#include <zephyr/bluetooth/bluetooth.h>
|
||||
#include <zephyr/drivers/hwinfo.h>
|
||||
#include <zephyr/sys/reboot.h>
|
||||
@@ -754,6 +755,7 @@ static void gps_fix_callback(double lat, double lon, int64_t utc_time)
|
||||
LOG_INF("GPS fix: RTC sync time=%lld", utc_time);
|
||||
rtc_clock.setCurrentTime((uint32_t)utc_time);
|
||||
time_sync_report(TIME_SYNC_GPS);
|
||||
zephcore_rtc_save((uint32_t)utc_time); /* persist to hardware RTC */
|
||||
}
|
||||
|
||||
#ifdef ZEPHCORE_LORA
|
||||
@@ -855,6 +857,16 @@ int main(void)
|
||||
/* Initialize sensor manager (GPS, environment sensors) */
|
||||
sensor_manager_init();
|
||||
|
||||
/* Restore wall-clock time from a battery-backed hardware RTC if one is
|
||||
* present on I2C. Shown tagged "L" (local) until the next GPS/app/CLI
|
||||
* sync; no-op on boards without an RTC. */
|
||||
{
|
||||
uint32_t rtc_epoch;
|
||||
if (zephcore_rtc_restore(&rtc_epoch)) {
|
||||
rtc_clock.setCurrentTime(rtc_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Initialize UI subsystem (buttons, buzzer, display).
|
||||
* If display is present, ui_init() handles display init + auto-off.
|
||||
* If no display, fall back to raw OLED sleep for power saving. */
|
||||
|
||||
@@ -51,6 +51,7 @@ extern "C" void bt_ctlr_assert_handle(char *file, uint32_t line)
|
||||
#include <app/RepeaterDataStore.h>
|
||||
#include <app/RepeaterMesh.h>
|
||||
#include <adapters/clock/ZephyrRTCClock.h>
|
||||
#include <adapters/clock/ZephyrRTCDiscover.h>
|
||||
#include <ZephyrSensorManager.h>
|
||||
|
||||
/* UI subsystem (display, buttons, buzzer) */
|
||||
@@ -271,6 +272,7 @@ static void gps_fix_callback(double lat, double lon, int64_t utc_time)
|
||||
if (utc_time > 0) {
|
||||
LOG_INF("GPS fix: RTC sync time=%lld", utc_time);
|
||||
rtc_clock.setCurrentTime((uint32_t)utc_time);
|
||||
zephcore_rtc_save((uint32_t)utc_time); /* persist to hardware RTC */
|
||||
}
|
||||
|
||||
int lat_deg = (int)lat;
|
||||
@@ -431,6 +433,15 @@ int main(void)
|
||||
/* Initialize sensor manager */
|
||||
sensor_manager_init();
|
||||
|
||||
/* Restore wall-clock time from a battery-backed hardware RTC if present
|
||||
* (shown tagged "L" until the next GPS/CLI sync; no-op if no RTC). */
|
||||
{
|
||||
uint32_t rtc_epoch;
|
||||
if (zephcore_rtc_restore(&rtc_epoch)) {
|
||||
rtc_clock.setCurrentTime(rtc_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set GPS to repeater mode: power off now, wake every 48h for time sync only.
|
||||
* This prevents GPS from draining power on boards that have it (e.g., Wio Tracker). */
|
||||
if (gps_is_available()) {
|
||||
|
||||
@@ -51,6 +51,7 @@ extern "C" void bt_ctlr_assert_handle(char *file, uint32_t line)
|
||||
#include <app/RepeaterDataStore.h>
|
||||
#include <app/RoomServerMesh.h>
|
||||
#include <adapters/clock/ZephyrRTCClock.h>
|
||||
#include <adapters/clock/ZephyrRTCDiscover.h>
|
||||
#include <ZephyrSensorManager.h>
|
||||
|
||||
/* UI subsystem (display, buttons, buzzer) */
|
||||
@@ -284,6 +285,7 @@ static void gps_fix_callback(double lat, double lon, int64_t utc_time)
|
||||
if (utc_time > 0) {
|
||||
LOG_INF("GPS fix: RTC sync time=%lld", utc_time);
|
||||
rtc_clock.setCurrentTime((uint32_t)utc_time);
|
||||
zephcore_rtc_save((uint32_t)utc_time); /* persist to hardware RTC */
|
||||
}
|
||||
|
||||
int lat_deg = (int)lat;
|
||||
@@ -449,6 +451,15 @@ int main(void)
|
||||
/* Initialize sensor manager */
|
||||
sensor_manager_init();
|
||||
|
||||
/* Restore wall-clock time from a battery-backed hardware RTC if present
|
||||
* (shown tagged "L" until the next GPS/CLI sync; no-op if no RTC). */
|
||||
{
|
||||
uint32_t rtc_epoch;
|
||||
if (zephcore_rtc_restore(&rtc_epoch)) {
|
||||
rtc_clock.setCurrentTime(rtc_epoch);
|
||||
}
|
||||
}
|
||||
|
||||
/* Set GPS to repeater mode: power off now, wake every 48h for time sync only.
|
||||
* This prevents GPS from draining power on boards that have it (e.g., Wio Tracker). */
|
||||
if (gps_is_available()) {
|
||||
|
||||
Reference in New Issue
Block a user