// 20260214 ChatGPT // $Id$ // $HeadURL$ #include #include #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"); } }