Files
wadamesh/variants/lilygo_tdeck/target.cpp
T
Kaj SchittecatandClaude Opus 5 17ce1ae018 DS3231 real-time clock support on the T-Deck (#378)
jrote1 is fitting a battery-backed DS3231 to the T-Deck's I2C bus so the device
keeps time across a full power-off without needing GPS or Wi-Fi. The RTC adapter
already had the hard parts, refusing an untrustworthy read and revalidating every
write, so this teaches it a third chip rather than adding a second path.

Three things differ from the soldered PCF parts and all three are handled by
normalising into the existing decoder rather than duplicating its rules:

- It lives at 0x68 rather than 0x51. That is also what makes it safe to add: it
  cannot be mistaken for either chip an existing board carries.
- Day-of-week and day-of-month are swapped in its calendar block.
- It reports "my time is not trustworthy" as OSF in a status register instead of
  in bit 7 of seconds, so that bit is folded into the one the decoder already
  refuses on. A module whose backup cell went flat is therefore rejected the
  same way a powered-off M9 is, rather than having its frozen calendar believed.

0x68 is a busy address, so identification requires a positive DS3231 trait
rather than an ACK: its temperature register pair, whose low byte is a quarter
degree in the top two bits and zero elsewhere, with a high byte in a range a
working chip could be at. A module that has never been set by us can also arrive
in 12-hour mode, which is converted rather than decoded as if it were 24-hour.

Transparent when no module is fitted, which is nearly every T-Deck: reads come
from the software clock, writes stop there, and the probe logs that nothing
answered. CAP_HARDWARE_RTC stays 0 for the board, since this is an aftermarket
addition rather than something a T-Deck has, so the cold-boot network time sync
stays on offer for everyone else.

Unverified on hardware: neither of us has the module wired yet. The host tests
for the shared decoder still pass.

Built on all nine S3 envs and both ESP32-P4 targets.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 01:24:59 +02:00

70 lines
2.6 KiB
C++

#include <Arduino.h>
#include "target.h"
#include <helpers/HardwareRtcClock.h>
TDeckBoard board;
#if defined(P_LORA_SCLK)
static SPIClass spi;
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY, spi);
#else
RADIO_CLASS radio = new Module(P_LORA_NSS, P_LORA_DIO_1, P_LORA_RESET, P_LORA_BUSY);
#endif
WRAPPER_CLASS radio_driver(radio, board);
SPIClass* tdeckSharedSPI() {
#if defined(P_LORA_SCLK)
return &spi; // already begun (SCLK/MISO/MOSI) by radio.std_init
#else
return nullptr;
#endif
}
ESP32RTCClock fallback_clock;
// The T-Deck ships with no battery-backed clock, but a DS3231 module on the I2C
// bus is a common self-fit (#378). The adapter is transparent when nothing is
// there: reads come from the software clock and writes stop at it, so a board
// without the module behaves exactly as before.
HardwareRtcClock hw_rtc(fallback_clock);
ClockFloorRTC rtc_clock(hw_rtc);
MicroNMEALocationProvider gps(Serial1, &rtc_clock);
EnvironmentSensorManager sensors(gps);
#ifdef DISPLAY_CLASS
DISPLAY_CLASS display;
MomentaryButton user_btn(PIN_USER_BTN, 1000, true);
#endif
bool radio_init() {
fallback_clock.begin();
Wire.begin(18, 8);
// A DS3231 fitted to this bus, if there is one. This is NOT the generic
// address probe the comment below warns about: it is a declared chip type at
// its own address with a positive identity proof, and a bus with nothing at
// 0x68 simply logs and carries on. CAP_HARDWARE_RTC stays 0 for the board,
// since the module is an aftermarket addition rather than something a T-Deck
// has, which keeps the cold-boot network time sync on offer for everyone else.
hw_rtc.begin(Wire, HardwareRtcClock::Chip::DS3231);
rtc_clock.noteHardwareClock(hw_rtc.present());
if (hw_rtc.adopted()) rtc_clock.noteHardwareTime();
// NO generic rtc_clock.begin(Wire) here (issue #383). This board has no declared
// battery-backed clock, and the call was doubly wrong: it ran BEFORE the bus
// was even begun, so "the probe found nothing" was never evidence of anything.
// A board either drives a documented chip through HardwareRtcClock (see the
// Pager and the M9) or has none — "the generic probe happened to be quiet" is
// not a capability test. CAP_HARDWARE_RTC is 0 here, which is what makes the
// opt-in cold-boot network time sync worth offering on this board.
#if defined(P_LORA_SCLK)
return radio.std_init(&spi);
#else
return radio.std_init();
#endif
}
mesh::LocalIdentity radio_new_identity() {
RadioNoiseListener rng(radio);
return mesh::LocalIdentity(&rng); // create new random identity
}