224 lines
5.6 KiB
C++
224 lines
5.6 KiB
C++
// 20260214 ChatGPT
|
|
// $Id$
|
|
// $HeadURL$
|
|
|
|
#include <Arduino.h>
|
|
#include <Wire.h>
|
|
#include "tbeam_supreme_adapter.h"
|
|
|
|
#ifndef RTC_I2C_ADDR
|
|
#define RTC_I2C_ADDR 0x51
|
|
#endif
|
|
|
|
static XPowersLibInterface* g_pmu = nullptr;
|
|
static uint32_t g_logSeq = 0;
|
|
static uint32_t g_lastPrintMs = 0;
|
|
static String g_cmdBuf;
|
|
static bool g_lastWasCR = false;
|
|
|
|
struct RtcDateTime {
|
|
uint16_t year;
|
|
uint8_t month;
|
|
uint8_t day;
|
|
uint8_t hour;
|
|
uint8_t minute;
|
|
uint8_t second;
|
|
uint8_t weekday;
|
|
};
|
|
|
|
static uint8_t toBcd(uint8_t v) {
|
|
return ((v / 10U) << 4U) | (v % 10U);
|
|
}
|
|
|
|
static uint8_t fromBcd(uint8_t b) {
|
|
return ((b >> 4U) * 10U) + (b & 0x0FU);
|
|
}
|
|
|
|
static void logf(const char* fmt, ...) {
|
|
char msg[196];
|
|
va_list args;
|
|
va_start(args, fmt);
|
|
vsnprintf(msg, sizeof(msg), fmt, args);
|
|
va_end(args);
|
|
Serial.printf("[%10lu][%06lu] %s\r\n", (unsigned long)millis(), (unsigned long)g_logSeq++, msg);
|
|
}
|
|
|
|
static bool initPmuForRtc() {
|
|
return tbeam_supreme::initPmuForPeripherals(g_pmu, &Serial);
|
|
}
|
|
|
|
static bool rtcRead(RtcDateTime& out, bool& lowVoltageFlag) {
|
|
Wire1.beginTransmission(RTC_I2C_ADDR);
|
|
Wire1.write(0x02); // seconds register
|
|
if (Wire1.endTransmission(false) != 0) {
|
|
return false;
|
|
}
|
|
|
|
const uint8_t need = 7;
|
|
uint8_t got = Wire1.requestFrom((int)RTC_I2C_ADDR, (int)need);
|
|
if (got != need) {
|
|
return false;
|
|
}
|
|
|
|
uint8_t sec = Wire1.read();
|
|
uint8_t min = Wire1.read();
|
|
uint8_t hour = Wire1.read();
|
|
uint8_t day = Wire1.read();
|
|
uint8_t weekday = Wire1.read();
|
|
uint8_t month = Wire1.read();
|
|
uint8_t year = Wire1.read();
|
|
|
|
lowVoltageFlag = (sec & 0x80U) != 0;
|
|
out.second = fromBcd(sec & 0x7FU);
|
|
out.minute = fromBcd(min & 0x7FU);
|
|
out.hour = fromBcd(hour & 0x3FU);
|
|
out.day = fromBcd(day & 0x3FU);
|
|
out.weekday = fromBcd(weekday & 0x07U);
|
|
out.month = fromBcd(month & 0x1FU);
|
|
uint8_t yy = fromBcd(year);
|
|
bool century = (month & 0x80U) != 0;
|
|
out.year = century ? (1900U + yy) : (2000U + yy);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool rtcWrite(const RtcDateTime& in) {
|
|
bool century = in.year < 2000;
|
|
uint8_t yy = (uint8_t)(in.year % 100);
|
|
uint8_t monthReg = toBcd(in.month) & 0x1FU;
|
|
if (century) {
|
|
monthReg |= 0x80U;
|
|
}
|
|
|
|
Wire1.beginTransmission(RTC_I2C_ADDR);
|
|
Wire1.write(0x02); // seconds register
|
|
Wire1.write(toBcd(in.second) & 0x7FU);
|
|
Wire1.write(toBcd(in.minute) & 0x7FU);
|
|
Wire1.write(toBcd(in.hour) & 0x3FU);
|
|
Wire1.write(toBcd(in.day) & 0x3FU);
|
|
Wire1.write(toBcd(in.weekday) & 0x07U);
|
|
Wire1.write(monthReg);
|
|
Wire1.write(toBcd(yy));
|
|
return Wire1.endTransmission() == 0;
|
|
}
|
|
|
|
static bool parseSetCommand(const String& line, RtcDateTime& dt) {
|
|
int y, mo, d, h, mi, s;
|
|
if (sscanf(line.c_str(), "set %d-%d-%d %d:%d:%d", &y, &mo, &d, &h, &mi, &s) != 6) {
|
|
return false;
|
|
}
|
|
|
|
if (y < 1900 || y > 2099) return false;
|
|
if (mo < 1 || mo > 12) return false;
|
|
if (d < 1 || d > 31) return false;
|
|
if (h < 0 || h > 23) return false;
|
|
if (mi < 0 || mi > 59) return false;
|
|
if (s < 0 || s > 59) return false;
|
|
|
|
dt.year = (uint16_t)y;
|
|
dt.month = (uint8_t)mo;
|
|
dt.day = (uint8_t)d;
|
|
dt.hour = (uint8_t)h;
|
|
dt.minute = (uint8_t)mi;
|
|
dt.second = (uint8_t)s;
|
|
dt.weekday = 0; // Not critical for this persistence check.
|
|
return true;
|
|
}
|
|
|
|
static void printRtcNow(const char* label) {
|
|
RtcDateTime now{};
|
|
bool lowV = false;
|
|
if (!rtcRead(now, lowV)) {
|
|
logf("%s: RTC read failed", label);
|
|
return;
|
|
}
|
|
logf("%s: %04u-%02u-%02u %02u:%02u:%02u weekday=%u%s",
|
|
label,
|
|
(unsigned)now.year, (unsigned)now.month, (unsigned)now.day,
|
|
(unsigned)now.hour, (unsigned)now.minute, (unsigned)now.second,
|
|
(unsigned)now.weekday,
|
|
lowV ? " [LOW_VOLTAGE_FLAG]" : "");
|
|
}
|
|
|
|
static void handleCommand(const String& raw) {
|
|
String line = raw;
|
|
line.trim();
|
|
if (line.length() == 0) {
|
|
return;
|
|
}
|
|
|
|
if (line == "help") {
|
|
logf("Commands:");
|
|
logf(" show");
|
|
logf(" set YYYY-MM-DD HH:MM:SS");
|
|
logf(" help");
|
|
return;
|
|
}
|
|
if (line == "show") {
|
|
printRtcNow("RTC");
|
|
return;
|
|
}
|
|
|
|
RtcDateTime dt{};
|
|
if (parseSetCommand(line, dt)) {
|
|
if (rtcWrite(dt)) {
|
|
logf("RTC set succeeded");
|
|
printRtcNow("RTC");
|
|
} else {
|
|
logf("RTC set failed");
|
|
}
|
|
return;
|
|
}
|
|
|
|
logf("Unknown command: %s", line.c_str());
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200);
|
|
Serial.println("Sleeping for 5 seconds to allow Serial Monitor connection...");
|
|
delay(5000);
|
|
|
|
Serial.println("\r\n==================================================");
|
|
Serial.println("Exercise 06: RTC check (PCF8563)");
|
|
Serial.println("==================================================");
|
|
Serial.printf("RTC I2C: SDA1=%d SCL1=%d ADDR=0x%02X\r\n",
|
|
tbeam_supreme::i2cSda(), tbeam_supreme::i2cScl(), RTC_I2C_ADDR);
|
|
|
|
initPmuForRtc();
|
|
|
|
logf("Type 'help' for commands.");
|
|
logf("Power-off persistence test:");
|
|
logf(" 1) set time");
|
|
logf(" 2) power off for a few minutes");
|
|
logf(" 3) power on and run 'show' before any GPS sync");
|
|
printRtcNow("RTC startup");
|
|
}
|
|
|
|
void loop() {
|
|
while (Serial.available() > 0) {
|
|
char c = (char)Serial.read();
|
|
if (c == '\r' || c == '\n') {
|
|
// Handle CR, LF, and CRLF/LFCR cleanly as one line ending.
|
|
if ((c == '\n' && g_lastWasCR) || (c == '\r' && !g_lastWasCR && g_cmdBuf.length() == 0)) {
|
|
g_lastWasCR = (c == '\r');
|
|
continue;
|
|
}
|
|
handleCommand(g_cmdBuf);
|
|
g_cmdBuf = "";
|
|
g_lastWasCR = (c == '\r');
|
|
} else {
|
|
g_lastWasCR = false;
|
|
g_cmdBuf += c;
|
|
if (g_cmdBuf.length() > 120) {
|
|
g_cmdBuf = "";
|
|
logf("Input line too long, buffer cleared");
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32_t nowMs = millis();
|
|
if ((uint32_t)(nowMs - g_lastPrintMs) >= 10000) {
|
|
g_lastPrintMs = nowMs;
|
|
printRtcNow("RTC periodic");
|
|
}
|
|
}
|