diff --git a/exercises/12_FiveTalk/src/main.cpp b/exercises/12_FiveTalk/src/main.cpp index 4a77047..3b1d749 100644 --- a/exercises/12_FiveTalk/src/main.cpp +++ b/exercises/12_FiveTalk/src/main.cpp @@ -76,7 +76,7 @@ static const uint32_t kNoGpsMessagePeriodMs = 1500; static const uint32_t kHealthCheckPeriodMs = 60000; static const uint32_t kSlotSeconds = 2; -static XPowersLibInterface *g_pmu = nullptr; +static XPowersLibInterface* g_pmu = nullptr; static StartupSdManager g_sd(Serial); static U8G2_SH1106_128X64_NONAME_F_HW_I2C g_oled(U8G2_R0, U8X8_PIN_NONE); static HardwareSerial g_gpsSerial(1); @@ -93,7 +93,6 @@ static uint32_t g_lastHealthCheckMs = 0; static int64_t g_lastDisciplineEpoch = -1; static int64_t g_lastTxEpochSecond = -1; static uint32_t g_txCount = 0; -static uint32_t g_txAttemptId = 0; static bool g_radioReady = false; static bool g_sessionReady = false; @@ -109,8 +108,7 @@ static File g_recvFile; static char g_gpsLine[128]; static size_t g_gpsLineLen = 0; -struct DateTime -{ +struct DateTime { uint16_t year; uint8_t month; uint8_t day; @@ -119,8 +117,7 @@ struct DateTime uint8_t second; }; -struct GpsState -{ +struct GpsState { bool sawAnySentence = false; bool hasValidUtc = false; bool hasValidPosition = false; @@ -136,14 +133,9 @@ struct GpsState static GpsState g_gps; -static void parsePayloadFields(const char *msg, - char *txIdOut, size_t txIdLen, - char *latOut, size_t latLen, - char *lonOut, size_t lonLen, - char *altOut, size_t altLen); +static void parsePayloadCoords(const char* msg, char* latOut, size_t latLen, char* lonOut, size_t lonLen, char* altOut, size_t altLen); -enum class AppPhase : uint8_t -{ +enum class AppPhase : uint8_t { WAIT_SD = 0, WAIT_DISCIPLINE, RUN @@ -151,13 +143,11 @@ enum class AppPhase : uint8_t static AppPhase g_phase = AppPhase::WAIT_SD; -static uint8_t bestSatelliteCount() -{ +static uint8_t bestSatelliteCount() { return (g_gps.satsUsed > g_gps.satsInView) ? g_gps.satsUsed : g_gps.satsInView; } -static void logf(const char *fmt, ...) -{ +static void logf(const char* fmt, ...) { char msg[256]; va_list args; va_start(args, fmt); @@ -166,67 +156,49 @@ static void logf(const char *fmt, ...) Serial.printf("[%10lu][%06lu] %s\r\n", (unsigned long)millis(), (unsigned long)g_logSeq++, msg); } -static void oledShowLines(const char *l1, - const char *l2 = nullptr, - const char *l3 = nullptr, - const char *l4 = nullptr, - const char *l5 = nullptr) -{ +static void oledShowLines(const char* l1, + const char* l2 = nullptr, + const char* l3 = nullptr, + const char* l4 = nullptr, + const char* l5 = nullptr) { g_oled.clearBuffer(); g_oled.setFont(u8g2_font_5x8_tf); - if (l1) - g_oled.drawUTF8(0, 12, l1); - if (l2) - g_oled.drawUTF8(0, 24, l2); - if (l3) - g_oled.drawUTF8(0, 36, l3); - if (l4) - g_oled.drawUTF8(0, 48, l4); - if (l5) - g_oled.drawUTF8(0, 60, l5); + if (l1) g_oled.drawUTF8(0, 12, l1); + if (l2) g_oled.drawUTF8(0, 24, l2); + if (l3) g_oled.drawUTF8(0, 36, l3); + if (l4) g_oled.drawUTF8(0, 48, l4); + if (l5) g_oled.drawUTF8(0, 60, l5); g_oled.sendBuffer(); } -static uint8_t toBcd(uint8_t v) -{ +static uint8_t toBcd(uint8_t v) { return (uint8_t)(((v / 10U) << 4U) | (v % 10U)); } -static uint8_t fromBcd(uint8_t b) -{ +static uint8_t fromBcd(uint8_t b) { return (uint8_t)(((b >> 4U) * 10U) + (b & 0x0FU)); } -static bool isLeapYear(uint16_t y) -{ +static bool isLeapYear(uint16_t y) { return ((y % 4U) == 0U && (y % 100U) != 0U) || ((y % 400U) == 0U); } -static uint8_t daysInMonth(uint16_t year, uint8_t month) -{ +static uint8_t daysInMonth(uint16_t year, uint8_t month) { static const uint8_t kDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; - if (month == 2) - return (uint8_t)(isLeapYear(year) ? 29 : 28); - if (month >= 1 && month <= 12) - return kDays[month - 1]; + if (month == 2) return (uint8_t)(isLeapYear(year) ? 29 : 28); + if (month >= 1 && month <= 12) return kDays[month - 1]; return 30; } -static bool isValidDateTime(const DateTime &dt) -{ - if (dt.year < 2000U || dt.year > 2099U) - return false; - if (dt.month < 1 || dt.month > 12) - return false; - if (dt.day < 1 || dt.day > daysInMonth(dt.year, dt.month)) - return false; - if (dt.hour > 23 || dt.minute > 59 || dt.second > 59) - return false; +static bool isValidDateTime(const DateTime& dt) { + if (dt.year < 2000U || dt.year > 2099U) return false; + if (dt.month < 1 || dt.month > 12) return false; + if (dt.day < 1 || dt.day > daysInMonth(dt.year, dt.month)) return false; + if (dt.hour > 23 || dt.minute > 59 || dt.second > 59) return false; return true; } -static int64_t daysFromCivil(int y, unsigned m, unsigned d) -{ +static int64_t daysFromCivil(int y, unsigned m, unsigned d) { y -= (m <= 2); const int era = (y >= 0 ? y : y - 399) / 400; const unsigned yoe = (unsigned)(y - era * 400); @@ -235,21 +207,17 @@ static int64_t daysFromCivil(int y, unsigned m, unsigned d) return era * 146097 + (int)doe - 719468; } -static int64_t toEpochSeconds(const DateTime &dt) -{ +static int64_t toEpochSeconds(const DateTime& dt) { int64_t days = daysFromCivil((int)dt.year, dt.month, dt.day); return days * 86400LL + (int64_t)dt.hour * 3600LL + (int64_t)dt.minute * 60LL + (int64_t)dt.second; } -static bool fromEpochSeconds(int64_t sec, DateTime &out) -{ - if (sec < 0) - return false; +static bool fromEpochSeconds(int64_t sec, DateTime& out) { + if (sec < 0) return false; int64_t days = sec / 86400LL; int64_t rem = sec % 86400LL; - if (rem < 0) - { + if (rem < 0) { rem += 86400LL; days -= 1; } @@ -276,17 +244,14 @@ static bool fromEpochSeconds(int64_t sec, DateTime &out) return isValidDateTime(out); } -static bool rtcRead(DateTime &out, bool &lowVoltageFlag) -{ +static bool rtcRead(DateTime& out, bool& lowVoltageFlag) { Wire1.beginTransmission(RTC_I2C_ADDR); Wire1.write(0x02); - if (Wire1.endTransmission(false) != 0) - return false; + 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; + if (got != need) return false; uint8_t sec = Wire1.read(); uint8_t min = Wire1.read(); @@ -309,8 +274,7 @@ static bool rtcRead(DateTime &out, bool &lowVoltageFlag) return true; } -static bool rtcWrite(const DateTime &dt) -{ +static bool rtcWrite(const DateTime& dt) { Wire1.beginTransmission(RTC_I2C_ADDR); Wire1.write(0x02); Wire1.write(toBcd(dt.second & 0x7FU)); @@ -320,27 +284,22 @@ static bool rtcWrite(const DateTime &dt) Wire1.write(0x00); uint8_t monthReg = toBcd(dt.month); - if (dt.year < 2000U) - monthReg |= 0x80U; + if (dt.year < 2000U) monthReg |= 0x80U; Wire1.write(monthReg); Wire1.write(toBcd((uint8_t)(dt.year % 100U))); return Wire1.endTransmission() == 0; } -static bool getCurrentUtc(DateTime &dt, int64_t &epoch) -{ +static bool getCurrentUtc(DateTime& dt, int64_t& epoch) { bool lowV = false; - if (!rtcRead(dt, lowV)) - return false; - if (lowV || !isValidDateTime(dt)) - return false; + if (!rtcRead(dt, lowV)) return false; + if (lowV || !isValidDateTime(dt)) return false; epoch = toEpochSeconds(dt); return true; } -static void formatUtcHuman(const DateTime &dt, char *out, size_t outLen) -{ +static void formatUtcHuman(const DateTime& dt, char* out, size_t outLen) { snprintf(out, outLen, "%04u-%02u-%02u %02u:%02u:%02u UTC", (unsigned)dt.year, (unsigned)dt.month, @@ -350,8 +309,7 @@ static void formatUtcHuman(const DateTime &dt, char *out, size_t outLen) (unsigned)dt.second); } -static void formatUtcCompact(const DateTime &dt, char *out, size_t outLen) -{ +static void formatUtcCompact(const DateTime& dt, char* out, size_t outLen) { snprintf(out, outLen, "%04u%02u%02u_%02u%02u%02u", @@ -363,47 +321,36 @@ static void formatUtcCompact(const DateTime &dt, char *out, size_t outLen) (unsigned)dt.second); } -static bool parseUInt2(const char *s, uint8_t &out) -{ - if (!s || !isdigit((unsigned char)s[0]) || !isdigit((unsigned char)s[1])) - return false; +static bool parseUInt2(const char* s, uint8_t& out) { + if (!s || !isdigit((unsigned char)s[0]) || !isdigit((unsigned char)s[1])) return false; out = (uint8_t)((s[0] - '0') * 10 + (s[1] - '0')); return true; } -static bool parseNmeaCoordToDecimal(const char *raw, const char *hemi, bool isLat, double &outDeg) -{ - if (!raw || !hemi || raw[0] == '\0' || hemi[0] == '\0') - return false; +static bool parseNmeaCoordToDecimal(const char* raw, const char* hemi, bool isLat, double& outDeg) { + if (!raw || !hemi || raw[0] == '\0' || hemi[0] == '\0') return false; int degDigits = isLat ? 2 : 3; size_t n = strlen(raw); - if (n <= (size_t)degDigits + 2) - return false; + if (n <= (size_t)degDigits + 2) return false; - for (int i = 0; i < degDigits; ++i) - { - if (!isdigit((unsigned char)raw[i])) - return false; + for (int i = 0; i < degDigits; ++i) { + if (!isdigit((unsigned char)raw[i])) return false; } char degBuf[4] = {0}; memcpy(degBuf, raw, degDigits); int deg = atoi(degBuf); - const char *minPtr = raw + degDigits; + const char* minPtr = raw + degDigits; double minutes = atof(minPtr); - if (minutes < 0.0 || minutes >= 60.0) - return false; + if (minutes < 0.0 || minutes >= 60.0) return false; double dec = (double)deg + (minutes / 60.0); char h = (char)toupper((unsigned char)hemi[0]); - if (h == 'S' || h == 'W') - { + if (h == 'S' || h == 'W') { dec = -dec; - } - else if (h != 'N' && h != 'E') - { + } else if (h != 'N' && h != 'E') { return false; } @@ -411,30 +358,24 @@ static bool parseNmeaCoordToDecimal(const char *raw, const char *hemi, bool isLa return true; } -static void parseRmc(char *fields[], int count) -{ - if (count <= 9) - return; +static void parseRmc(char* fields[], int count) { + if (count <= 9) return; - const char *utc = fields[1]; - const char *status = fields[2]; - const char *latRaw = (count > 3) ? fields[3] : nullptr; - const char *latHem = (count > 4) ? fields[4] : nullptr; - const char *lonRaw = (count > 5) ? fields[5] : nullptr; - const char *lonHem = (count > 6) ? fields[6] : nullptr; - const char *date = fields[9]; + const char* utc = fields[1]; + const char* status = fields[2]; + const char* latRaw = (count > 3) ? fields[3] : nullptr; + const char* latHem = (count > 4) ? fields[4] : nullptr; + const char* lonRaw = (count > 5) ? fields[5] : nullptr; + const char* lonHem = (count > 6) ? fields[6] : nullptr; + const char* date = fields[9]; - if (!status || status[0] != 'A') - return; - if (!utc || !date || strlen(utc) < 6 || strlen(date) < 6) - return; + if (!status || status[0] != 'A') return; + if (!utc || !date || strlen(utc) < 6 || strlen(date) < 6) return; uint8_t hh = 0, mm = 0, ss = 0; uint8_t dd = 0, mo = 0, yy = 0; - if (!parseUInt2(utc + 0, hh) || !parseUInt2(utc + 2, mm) || !parseUInt2(utc + 4, ss)) - return; - if (!parseUInt2(date + 0, dd) || !parseUInt2(date + 2, mo) || !parseUInt2(date + 4, yy)) - return; + if (!parseUInt2(utc + 0, hh) || !parseUInt2(utc + 2, mm) || !parseUInt2(utc + 4, ss)) return; + if (!parseUInt2(date + 0, dd) || !parseUInt2(date + 2, mo) || !parseUInt2(date + 4, yy)) return; g_gps.utc.hour = hh; g_gps.utc.minute = mm; @@ -448,28 +389,23 @@ static void parseRmc(char *fields[], int count) double lat = 0.0; double lon = 0.0; if (parseNmeaCoordToDecimal(latRaw, latHem, true, lat) && - parseNmeaCoordToDecimal(lonRaw, lonHem, false, lon)) - { + parseNmeaCoordToDecimal(lonRaw, lonHem, false, lon)) { g_gps.latitudeDeg = lat; g_gps.longitudeDeg = lon; g_gps.hasValidPosition = true; } } -static void parseGga(char *fields[], int count) -{ - if (count <= 7) - return; - const char *latRaw = (count > 2) ? fields[2] : nullptr; - const char *latHem = (count > 3) ? fields[3] : nullptr; - const char *lonRaw = (count > 4) ? fields[4] : nullptr; - const char *lonHem = (count > 5) ? fields[5] : nullptr; +static void parseGga(char* fields[], int count) { + if (count <= 7) return; + const char* latRaw = (count > 2) ? fields[2] : nullptr; + const char* latHem = (count > 3) ? fields[3] : nullptr; + const char* lonRaw = (count > 4) ? fields[4] : nullptr; + const char* lonHem = (count > 5) ? fields[5] : nullptr; int sats = atoi(fields[7]); - if (sats >= 0 && sats <= 255) - g_gps.satsUsed = (uint8_t)sats; + if (sats >= 0 && sats <= 255) g_gps.satsUsed = (uint8_t)sats; - if (count > 9 && fields[9] && fields[9][0] != '\0') - { + if (count > 9 && fields[9] && fields[9][0] != '\0') { g_gps.altitudeM = (float)atof(fields[9]); g_gps.hasValidAltitude = true; } @@ -477,70 +413,52 @@ static void parseGga(char *fields[], int count) double lat = 0.0; double lon = 0.0; if (parseNmeaCoordToDecimal(latRaw, latHem, true, lat) && - parseNmeaCoordToDecimal(lonRaw, lonHem, false, lon)) - { + parseNmeaCoordToDecimal(lonRaw, lonHem, false, lon)) { g_gps.latitudeDeg = lat; g_gps.longitudeDeg = lon; g_gps.hasValidPosition = true; } } -static void parseGsv(char *fields[], int count) -{ - if (count <= 3) - return; +static void parseGsv(char* fields[], int count) { + if (count <= 3) return; int sats = atoi(fields[3]); - if (sats >= 0 && sats <= 255) - g_gps.satsInView = (uint8_t)sats; + if (sats >= 0 && sats <= 255) g_gps.satsInView = (uint8_t)sats; } -static void processNmeaLine(char *line) -{ - if (!line || line[0] != '$') - return; +static void processNmeaLine(char* line) { + if (!line || line[0] != '$') return; g_gps.sawAnySentence = true; - char *star = strchr(line, '*'); - if (star) - *star = '\0'; + char* star = strchr(line, '*'); + if (star) *star = '\0'; - char *fields[24] = {0}; + char* fields[24] = {0}; int count = 0; - char *saveptr = nullptr; - char *tok = strtok_r(line, ",", &saveptr); - while (tok && count < 24) - { + char* saveptr = nullptr; + char* tok = strtok_r(line, ",", &saveptr); + while (tok && count < 24) { fields[count++] = tok; tok = strtok_r(nullptr, ",", &saveptr); } - if (count == 0 || !fields[0]) - return; + if (count == 0 || !fields[0]) return; - const char *header = fields[0]; + const char* header = fields[0]; size_t n = strlen(header); - if (n < 6) - return; + if (n < 6) return; - const char *type = header + (n - 3); - if (strcmp(type, "RMC") == 0) - parseRmc(fields, count); - else if (strcmp(type, "GGA") == 0) - parseGga(fields, count); - else if (strcmp(type, "GSV") == 0) - parseGsv(fields, count); + const char* type = header + (n - 3); + if (strcmp(type, "RMC") == 0) parseRmc(fields, count); + else if (strcmp(type, "GGA") == 0) parseGga(fields, count); + else if (strcmp(type, "GSV") == 0) parseGsv(fields, count); } -static void pollGpsSerial() -{ - while (g_gpsSerial.available() > 0) - { +static void pollGpsSerial() { + while (g_gpsSerial.available() > 0) { char c = (char)g_gpsSerial.read(); - if (c == '\r') - continue; - if (c == '\n') - { - if (g_gpsLineLen > 0) - { + if (c == '\r') continue; + if (c == '\n') { + if (g_gpsLineLen > 0) { g_gpsLine[g_gpsLineLen] = '\0'; processNmeaLine(g_gpsLine); g_gpsLineLen = 0; @@ -548,66 +466,51 @@ static void pollGpsSerial() continue; } - if (g_gpsLineLen + 1 < sizeof(g_gpsLine)) - g_gpsLine[g_gpsLineLen++] = c; - else - g_gpsLineLen = 0; + if (g_gpsLineLen + 1 < sizeof(g_gpsLine)) g_gpsLine[g_gpsLineLen++] = c; + else g_gpsLineLen = 0; } } -static bool gpsUtcIsFresh() -{ +static bool gpsUtcIsFresh() { return g_gps.hasValidUtc && ((uint32_t)(millis() - g_gps.lastUtcMs) <= 2000U); } -static IRAM_ATTR void onPpsEdge() -{ +static IRAM_ATTR void onPpsEdge() { g_ppsEdgeCount++; } -static bool waitForNextPps(uint32_t timeoutMs) -{ +static bool waitForNextPps(uint32_t timeoutMs) { uint32_t startEdges = g_ppsEdgeCount; uint32_t startMs = millis(); - while ((uint32_t)(millis() - startMs) < timeoutMs) - { + while ((uint32_t)(millis() - startMs) < timeoutMs) { pollGpsSerial(); g_sd.update(); - if (g_ppsEdgeCount != startEdges) - return true; + if (g_ppsEdgeCount != startEdges) return true; delay(2); } return false; } -static bool ensureGpsLogPathReady() -{ - if (!g_sd.isMounted()) - { +static bool ensureGpsLogPathReady() { + if (!g_sd.isMounted()) { g_gpsPathReady = false; return false; } - if (g_gpsPathReady) - return true; + if (g_gpsPathReady) return true; - if (!g_sd.ensureDirRecursive("/gps")) - return false; + if (!g_sd.ensureDirRecursive("/gps")) return false; File f = SD.open("/gps/discipline_rtc.log", FILE_APPEND); - if (!f) - return false; + if (!f) return false; f.close(); g_gpsPathReady = true; return true; } -static bool appendDisciplineLog(const DateTime &gpsUtc, int64_t rtcMinusGpsSeconds, bool hadPriorRtc) -{ - if (!ensureGpsLogPathReady()) - return false; +static bool appendDisciplineLog(const DateTime& gpsUtc, int64_t rtcMinusGpsSeconds, bool hadPriorRtc) { + if (!ensureGpsLogPathReady()) return false; File f = SD.open("/gps/discipline_rtc.log", FILE_APPEND); - if (!f) - return false; + if (!f) return false; char ts[24]; snprintf(ts, @@ -621,8 +524,7 @@ static bool appendDisciplineLog(const DateTime &gpsUtc, int64_t rtcMinusGpsSecon (unsigned)gpsUtc.second); char line[256]; - if (hadPriorRtc) - { + if (hadPriorRtc) { snprintf(line, sizeof(line), "%s\t set RTC to GPS for FiveTalk\trtc-gps drift=%+lld s; sats=%u; fw_build_utc=%s", @@ -630,9 +532,7 @@ static bool appendDisciplineLog(const DateTime &gpsUtc, int64_t rtcMinusGpsSecon (long long)rtcMinusGpsSeconds, (unsigned)bestSatelliteCount(), FW_BUILD_UTC); - } - else - { + } else { snprintf(line, sizeof(line), "%s\t set RTC to GPS for FiveTalk\trtc-gps drift=RTC_unset; sats=%u; fw_build_utc=%s", @@ -646,32 +546,25 @@ static bool appendDisciplineLog(const DateTime &gpsUtc, int64_t rtcMinusGpsSecon return wrote > 0; } -static bool disciplineRtcToGps() -{ - if (!gpsUtcIsFresh()) - return false; +static bool disciplineRtcToGps() { + if (!gpsUtcIsFresh()) return false; DateTime prior{}; bool lowV = false; bool havePriorRtc = rtcRead(prior, lowV) && !lowV && isValidDateTime(prior); DateTime gpsSnap = g_gps.utc; - if (!waitForNextPps(kPpsWaitTimeoutMs)) - return false; + if (!waitForNextPps(kPpsWaitTimeoutMs)) return false; int64_t snapEpoch = toEpochSeconds(gpsSnap); DateTime target{}; - if (!fromEpochSeconds(snapEpoch + 1, target)) - return false; - if (!rtcWrite(target)) - return false; + if (!fromEpochSeconds(snapEpoch + 1, target)) return false; + if (!rtcWrite(target)) return false; int64_t driftSec = 0; - if (havePriorRtc) - driftSec = toEpochSeconds(prior) - toEpochSeconds(target); + if (havePriorRtc) driftSec = toEpochSeconds(prior) - toEpochSeconds(target); - if (!appendDisciplineLog(target, driftSec, havePriorRtc)) - { + if (!appendDisciplineLog(target, driftSec, havePriorRtc)) { logf("WARN: Failed to append /gps/discipline_rtc.log"); } @@ -682,14 +575,11 @@ static bool disciplineRtcToGps() return true; } -static bool parseLogTimestampToken(const char *token, int64_t &epochOut) -{ - if (!token) - return false; +static bool parseLogTimestampToken(const char* token, int64_t& epochOut) { + if (!token) return false; unsigned y = 0, m = 0, d = 0, hh = 0, mm = 0, ss = 0; - if (sscanf(token, "%4u%2u%2u_%2u%2u%2u", &y, &m, &d, &hh, &mm, &ss) != 6) - return false; + if (sscanf(token, "%4u%2u%2u_%2u%2u%2u", &y, &m, &d, &hh, &mm, &ss) != 6) return false; DateTime dt{}; dt.year = (uint16_t)y; @@ -698,102 +588,81 @@ static bool parseLogTimestampToken(const char *token, int64_t &epochOut) dt.hour = (uint8_t)hh; dt.minute = (uint8_t)mm; dt.second = (uint8_t)ss; - if (!isValidDateTime(dt)) - return false; + if (!isValidDateTime(dt)) return false; epochOut = toEpochSeconds(dt); return true; } -static bool loadLastDisciplineEpoch(int64_t &epochOut) -{ +static bool loadLastDisciplineEpoch(int64_t& epochOut) { epochOut = -1; - if (!g_sd.isMounted()) - return false; - if (!SD.exists("/gps/discipline_rtc.log")) - return false; + if (!g_sd.isMounted()) return false; + if (!SD.exists("/gps/discipline_rtc.log")) return false; File f = SD.open("/gps/discipline_rtc.log", FILE_READ); - if (!f) - return false; + if (!f) return false; - while (f.available()) - { + while (f.available()) { String line = f.readStringUntil('\n'); line.trim(); - if (line.length() == 0) - continue; + if (line.length() == 0) continue; int sep = line.indexOf('\t'); String token = (sep >= 0) ? line.substring(0, sep) : line; char buf[32]; size_t n = token.length(); - if (n >= sizeof(buf)) - n = sizeof(buf) - 1; + if (n >= sizeof(buf)) n = sizeof(buf) - 1; memcpy(buf, token.c_str(), n); buf[n] = '\0'; int64_t parsed = -1; - if (parseLogTimestampToken(buf, parsed)) - epochOut = parsed; + if (parseLogTimestampToken(buf, parsed)) epochOut = parsed; } f.close(); return epochOut >= 0; } -static bool isDisciplineStale() -{ +static bool isDisciplineStale() { DateTime now{}; int64_t nowEpoch = 0; - if (!getCurrentUtc(now, nowEpoch)) - return true; + if (!getCurrentUtc(now, nowEpoch)) return true; int64_t lastEpoch = -1; - if (!loadLastDisciplineEpoch(lastEpoch)) - { - if (g_lastDisciplineEpoch < 0) - return true; + if (!loadLastDisciplineEpoch(lastEpoch)) { + if (g_lastDisciplineEpoch < 0) return true; lastEpoch = g_lastDisciplineEpoch; } g_lastDisciplineEpoch = lastEpoch; - if (lastEpoch < 0) - return true; + if (lastEpoch < 0) return true; int64_t age = nowEpoch - lastEpoch; return age < 0 || age > (int64_t)kDisciplineMaxAgeSec; } -static void readBattery(float &voltageV, bool &present) -{ +static void readBattery(float& voltageV, bool& present) { voltageV = -1.0f; present = false; - if (!g_pmu) - return; + if (!g_pmu) return; present = g_pmu->isBatteryConnect(); voltageV = g_pmu->getBattVoltage() / 1000.0f; } -static void closeSessionLogs() -{ - if (g_sentFile) - g_sentFile.close(); - if (g_recvFile) - g_recvFile.close(); +static void closeSessionLogs() { + if (g_sentFile) g_sentFile.close(); + if (g_recvFile) g_recvFile.close(); g_sessionReady = false; } -static bool openSessionLogs() -{ +static bool openSessionLogs() { closeSessionLogs(); DateTime now{}; int64_t nowEpoch = 0; - if (!getCurrentUtc(now, nowEpoch)) - { + if (!getCurrentUtc(now, nowEpoch)) { logf("Cannot open session logs: RTC unavailable"); return false; } @@ -804,8 +673,7 @@ static bool openSessionLogs() g_sentFile = SD.open(g_sentPath, FILE_APPEND); g_recvFile = SD.open(g_recvPath, FILE_APPEND); - if (!g_sentFile || !g_recvFile) - { + if (!g_sentFile || !g_recvFile) { logf("Failed to open session logs: %s | %s", g_sentPath, g_recvPath); closeSessionLogs(); return false; @@ -833,33 +701,22 @@ static bool openSessionLogs() return true; } -static void gpsFieldStrings(char *latOut, size_t latLen, char *lonOut, size_t lonLen, char *altOut, size_t altLen) -{ - if (latOut && latLen > 0) - latOut[0] = '\0'; - if (lonOut && lonLen > 0) - lonOut[0] = '\0'; - if (altOut && altLen > 0) - altOut[0] = '\0'; +static void gpsFieldStrings(char* latOut, size_t latLen, char* lonOut, size_t lonLen, char* altOut, size_t altLen) { + if (latOut && latLen > 0) latOut[0] = '\0'; + if (lonOut && lonLen > 0) lonOut[0] = '\0'; + if (altOut && altLen > 0) altOut[0] = '\0'; - if (g_gps.hasValidPosition) - { - if (latOut && latLen > 0) - snprintf(latOut, latLen, "%.6f", g_gps.latitudeDeg); - if (lonOut && lonLen > 0) - snprintf(lonOut, lonLen, "%.6f", g_gps.longitudeDeg); + if (g_gps.hasValidPosition) { + if (latOut && latLen > 0) snprintf(latOut, latLen, "%.6f", g_gps.latitudeDeg); + if (lonOut && lonLen > 0) snprintf(lonOut, lonLen, "%.6f", g_gps.longitudeDeg); } - if (g_gps.hasValidAltitude) - { - if (altOut && altLen > 0) - snprintf(altOut, altLen, "%.2f", g_gps.altitudeM); + if (g_gps.hasValidAltitude) { + if (altOut && altLen > 0) snprintf(altOut, altLen, "%.2f", g_gps.altitudeM); } } -static void writeSentLog(int64_t epoch, const DateTime &dt, uint32_t txId, const char *payload, bool txOk) -{ - if (!g_sessionReady || !g_sentFile) - return; +static void writeSentLog(int64_t epoch, const DateTime& dt) { + if (!g_sessionReady || !g_sentFile) return; float battV = -1.0f; bool battPresent = false; @@ -871,13 +728,11 @@ static void writeSentLog(int64_t epoch, const DateTime &dt, uint32_t txId, const char lat[24], lon[24], alt[24]; gpsFieldStrings(lat, sizeof(lat), lon, sizeof(lon), alt, sizeof(alt)); - g_sentFile.printf("epoch=%lld\tutc=%s\tunit=%s\tmsg=%s\ttx_id=%lu\ttx_ok=%u\tlat=%s\tlon=%s\talt_m=%s\ttx_count=%lu\tbatt_present=%u\tbatt_v=%.3f\n", + g_sentFile.printf("epoch=%lld\tutc=%s\tunit=%s\tmsg=%s\tlat=%s\tlon=%s\talt_m=%s\ttx_count=%lu\tbatt_present=%u\tbatt_v=%.3f\n", (long long)epoch, human, NODE_SHORT, - payload ? payload : "", - (unsigned long)txId, - txOk ? 1U : 0U, + NODE_SHORT, lat, lon, alt, @@ -887,10 +742,8 @@ static void writeSentLog(int64_t epoch, const DateTime &dt, uint32_t txId, const g_sentFile.flush(); } -static void writeRecvLog(int64_t epoch, const DateTime &dt, const char *msg, float rssi, float snr) -{ - if (!g_sessionReady || !g_recvFile) - return; +static void writeRecvLog(int64_t epoch, const DateTime& dt, const char* msg, float rssi, float snr) { + if (!g_sessionReady || !g_recvFile) return; float battV = -1.0f; bool battPresent = false; @@ -899,15 +752,14 @@ static void writeRecvLog(int64_t epoch, const DateTime &dt, const char *msg, flo char human[32]; formatUtcHuman(dt, human, sizeof(human)); - char txId[24], lat[24], lon[24], alt[24]; - parsePayloadFields(msg, txId, sizeof(txId), lat, sizeof(lat), lon, sizeof(lon), alt, sizeof(alt)); + char lat[24], lon[24], alt[24]; + parsePayloadCoords(msg, lat, sizeof(lat), lon, sizeof(lon), alt, sizeof(alt)); - g_recvFile.printf("epoch=%lld\tutc=%s\tunit=%s\trx_msg=%s\trx_tx_id=%s\trx_lat=%s\trx_lon=%s\trx_alt_m=%s\trssi=%.1f\tsnr=%.1f\tbatt_present=%u\tbatt_v=%.3f\n", + g_recvFile.printf("epoch=%lld\tutc=%s\tunit=%s\trx_msg=%s\trx_lat=%s\trx_lon=%s\trx_alt_m=%s\trssi=%.1f\tsnr=%.1f\tbatt_present=%u\tbatt_v=%.3f\n", (long long)epoch, human, NODE_SHORT, msg ? msg : "", - txId, lat, lon, alt, @@ -918,114 +770,57 @@ static void writeRecvLog(int64_t epoch, const DateTime &dt, const char *msg, flo g_recvFile.flush(); } -static void buildTxPayload(char *out, size_t outLen, uint32_t txId) -{ - if (!out || outLen == 0) - return; +static void buildTxPayload(char* out, size_t outLen) { + if (!out || outLen == 0) return; out[0] = '\0'; char lat[24], lon[24], alt[24]; gpsFieldStrings(lat, sizeof(lat), lon, sizeof(lon), alt, sizeof(alt)); - snprintf(out, outLen, "%s,%lu,%s,%s,%s", NODE_SHORT, (unsigned long)txId, lat, lon, alt); + snprintf(out, outLen, "%s,%s,%s,%s", NODE_SHORT, lat, lon, alt); } -static bool isAllDigits(const char *s) -{ - if (!s || s[0] == '\0') - return false; - for (size_t i = 0; s[i] != '\0'; ++i) - { - if (!isdigit((unsigned char)s[i])) - return false; - } - return true; -} - -static void parsePayloadFields(const char *msg, - char *txIdOut, size_t txIdLen, - char *latOut, size_t latLen, - char *lonOut, size_t lonLen, - char *altOut, size_t altLen) -{ - if (txIdOut && txIdLen > 0) - txIdOut[0] = '\0'; - if (latOut && latLen > 0) - latOut[0] = '\0'; - if (lonOut && lonLen > 0) - lonOut[0] = '\0'; - if (altOut && altLen > 0) - altOut[0] = '\0'; - if (!msg || msg[0] == '\0') - return; +static void parsePayloadCoords(const char* msg, char* latOut, size_t latLen, char* lonOut, size_t lonLen, char* altOut, size_t altLen) { + if (latOut && latLen > 0) latOut[0] = '\0'; + if (lonOut && lonLen > 0) lonOut[0] = '\0'; + if (altOut && altLen > 0) altOut[0] = '\0'; + if (!msg || msg[0] == '\0') return; char buf[128]; size_t n = strlen(msg); - if (n >= sizeof(buf)) - n = sizeof(buf) - 1; + if (n >= sizeof(buf)) n = sizeof(buf) - 1; memcpy(buf, msg, n); buf[n] = '\0'; - char *saveptr = nullptr; - char *token = strtok_r(buf, ",", &saveptr); // unit label + char* saveptr = nullptr; + char* token = strtok_r(buf, ",", &saveptr); // unit label (void)token; - token = strtok_r(nullptr, ",", &saveptr); // tx_id or lat (legacy) - if (!token) - return; + token = strtok_r(nullptr, ",", &saveptr); // lat + if (token && latOut && latLen > 0) snprintf(latOut, latLen, "%s", token); - if (isAllDigits(token)) - { - if (txIdOut && txIdLen > 0) - snprintf(txIdOut, txIdLen, "%s", token); + token = strtok_r(nullptr, ",", &saveptr); // lon + if (token && lonOut && lonLen > 0) snprintf(lonOut, lonLen, "%s", token); - token = strtok_r(nullptr, ",", &saveptr); // lat - if (token && latOut && latLen > 0) - snprintf(latOut, latLen, "%s", token); - - token = strtok_r(nullptr, ",", &saveptr); // lon - if (token && lonOut && lonLen > 0) - snprintf(lonOut, lonLen, "%s", token); - - token = strtok_r(nullptr, ",", &saveptr); // alt - if (token && altOut && altLen > 0) - snprintf(altOut, altLen, "%s", token); - } - else - { - // Backward compatibility: older payloads were "UNIT,lat,lon,alt". - if (latOut && latLen > 0) - snprintf(latOut, latLen, "%s", token); - - token = strtok_r(nullptr, ",", &saveptr); // lon - if (token && lonOut && lonLen > 0) - snprintf(lonOut, lonLen, "%s", token); - - token = strtok_r(nullptr, ",", &saveptr); // alt - if (token && altOut && altLen > 0) - snprintf(altOut, altLen, "%s", token); - } + token = strtok_r(nullptr, ",", &saveptr); // alt + if (token && altOut && altLen > 0) snprintf(altOut, altLen, "%s", token); } -static void onLoRaDio1Rise() -{ +static void onLoRaDio1Rise() { g_rxFlag = true; } -static bool initRadio() -{ +static bool initRadio() { SPI.begin(LORA_SCK, LORA_MISO, LORA_MOSI, LORA_CS); int state = g_radio.begin(915.0, 125.0, 7, 5, 0x12, 14); - if (state != RADIOLIB_ERR_NONE) - { + if (state != RADIOLIB_ERR_NONE) { logf("radio.begin failed code=%d", state); return false; } g_radio.setDio1Action(onLoRaDio1Rise); state = g_radio.startReceive(); - if (state != RADIOLIB_ERR_NONE) - { + if (state != RADIOLIB_ERR_NONE) { logf("radio.startReceive failed code=%d", state); return false; } @@ -1034,8 +829,7 @@ static bool initRadio() return true; } -static void showRxOnOled(const DateTime &dt, const char *msg) -{ +static void showRxOnOled(const DateTime& dt, const char* msg) { char hhmmss[16]; snprintf(hhmmss, sizeof(hhmmss), "%02u:%02u:%02u", (unsigned)dt.hour, (unsigned)dt.minute, (unsigned)dt.second); @@ -1044,40 +838,31 @@ static void showRxOnOled(const DateTime &dt, const char *msg) oledShowLines(hhmmss, line); } -static void runTxScheduler() -{ +static void runTxScheduler() { DateTime now{}; int64_t epoch = 0; - if (!getCurrentUtc(now, epoch)) - return; + if (!getCurrentUtc(now, epoch)) return; int slotSecond = NODE_SLOT_INDEX * (int)kSlotSeconds; int secInFrame = now.second % 10; - if (secInFrame != slotSecond) - return; + if (secInFrame != slotSecond) return; int64_t epochSecond = epoch; - if (epochSecond == g_lastTxEpochSecond) - return; + if (epochSecond == g_lastTxEpochSecond) return; g_lastTxEpochSecond = epochSecond; - uint32_t txId = ++g_txAttemptId; g_rxFlag = false; g_radio.clearDio1Action(); char payload[96]; - buildTxPayload(payload, sizeof(payload), txId); + buildTxPayload(payload, sizeof(payload)); int tx = g_radio.transmit(payload); - if (tx == RADIOLIB_ERR_NONE) - { + if (tx == RADIOLIB_ERR_NONE) { g_txCount++; - writeSentLog(epoch, now, txId, payload, true); - logf("TX %s tx_id=%lu success_count=%lu payload=%s", NODE_SHORT, (unsigned long)txId, (unsigned long)g_txCount, payload); - } - else - { - writeSentLog(epoch, now, txId, payload, false); + writeSentLog(epoch, now); + logf("TX %s count=%lu payload=%s", NODE_SHORT, (unsigned long)g_txCount, payload); + } else { logf("TX failed code=%d", tx); } @@ -1086,24 +871,20 @@ static void runTxScheduler() g_radio.startReceive(); } -static void runRxHandler() -{ - if (!g_rxFlag) - return; +static void runRxHandler() { + if (!g_rxFlag) return; g_rxFlag = false; String rx; int rc = g_radio.readData(rx); - if (rc != RADIOLIB_ERR_NONE) - { + if (rc != RADIOLIB_ERR_NONE) { g_radio.startReceive(); return; } DateTime now{}; int64_t epoch = 0; - if (getCurrentUtc(now, epoch)) - { + if (getCurrentUtc(now, epoch)) { writeRecvLog(epoch, now, rx.c_str(), g_radio.getRSSI(), g_radio.getSNR()); showRxOnOled(now, rx.c_str()); } @@ -1111,40 +892,31 @@ static void runRxHandler() g_radio.startReceive(); } -static void enterWaitSdState() -{ - if (g_phase == AppPhase::WAIT_SD) - return; +static void enterWaitSdState() { + if (g_phase == AppPhase::WAIT_SD) return; g_phase = AppPhase::WAIT_SD; closeSessionLogs(); logf("State -> WAIT_SD"); } -static void enterWaitDisciplineState() -{ - if (g_phase == AppPhase::WAIT_DISCIPLINE) - return; +static void enterWaitDisciplineState() { + if (g_phase == AppPhase::WAIT_DISCIPLINE) return; g_phase = AppPhase::WAIT_DISCIPLINE; closeSessionLogs(); logf("State -> WAIT_DISCIPLINE"); } -static void enterRunState() -{ - if (g_phase == AppPhase::RUN) - return; - if (!openSessionLogs()) - return; +static void enterRunState() { + if (g_phase == AppPhase::RUN) return; + if (!openSessionLogs()) return; g_lastTxEpochSecond = -1; g_lastHealthCheckMs = millis(); g_phase = AppPhase::RUN; logf("State -> RUN"); } -static void updateWaitSd() -{ - if (g_sd.isMounted()) - { +static void updateWaitSd() { + if (g_sd.isMounted()) { g_lastWarnMs = 0; g_gpsPathReady = false; enterWaitDisciplineState(); @@ -1152,70 +924,56 @@ static void updateWaitSd() } uint32_t now = millis(); - if ((uint32_t)(now - g_lastWarnMs) >= kSdMessagePeriodMs) - { + if ((uint32_t)(now - g_lastWarnMs) >= kSdMessagePeriodMs) { g_lastWarnMs = now; oledShowLines("Reinsert SD Card", NODE_SHORT, NODE_LABEL); } } -static void updateWaitDiscipline() -{ - if (!g_sd.isMounted()) - { +static void updateWaitDiscipline() { + if (!g_sd.isMounted()) { enterWaitSdState(); return; } - if (!isDisciplineStale()) - { + if (!isDisciplineStale()) { enterRunState(); return; } uint32_t now = millis(); - if ((uint32_t)(now - g_lastWarnMs) >= kNoGpsMessagePeriodMs) - { + if ((uint32_t)(now - g_lastWarnMs) >= kNoGpsMessagePeriodMs) { g_lastWarnMs = now; char satsLine[24]; snprintf(satsLine, sizeof(satsLine), "Satellites: %u", (unsigned)bestSatelliteCount()); oledShowLines("Take me outside", "Need GPS time sync", satsLine); } - if ((uint32_t)(now - g_lastDisciplineTryMs) < kDisciplineRetryMs) - return; + if ((uint32_t)(now - g_lastDisciplineTryMs) < kDisciplineRetryMs) return; g_lastDisciplineTryMs = now; - if (disciplineRtcToGps()) - { + if (disciplineRtcToGps()) { g_lastWarnMs = 0; enterRunState(); } } -static void updateRun() -{ +static void updateRun() { uint32_t now = millis(); - if (!g_sd.isMounted()) - { - if ((uint32_t)(now - g_lastWarnMs) >= kSdMessagePeriodMs) - { + if (!g_sd.isMounted()) { + if ((uint32_t)(now - g_lastWarnMs) >= kSdMessagePeriodMs) { g_lastWarnMs = now; oledShowLines("SD removed", "Logging paused", "LoRa continues"); } - } - else if (!g_sessionReady) - { + } else if (!g_sessionReady) { // Card came back while running. Resume append logging without pausing radio work. (void)openSessionLogs(); } - if ((uint32_t)(now - g_lastHealthCheckMs) >= kHealthCheckPeriodMs) - { + if ((uint32_t)(now - g_lastHealthCheckMs) >= kHealthCheckPeriodMs) { g_lastHealthCheckMs = now; - if (isDisciplineStale()) - { + if (isDisciplineStale()) { enterWaitDisciplineState(); return; } @@ -1225,8 +983,7 @@ static void updateRun() runRxHandler(); } -void setup() -{ +void setup() { Serial.begin(115200); delay(kSerialDelayMs); @@ -1234,8 +991,7 @@ void setup() Serial.println("Exercise 12: FiveTalk"); Serial.println("=================================================="); - if (!tbeam_supreme::initPmuForPeripherals(g_pmu, &Serial)) - { + if (!tbeam_supreme::initPmuForPeripherals(g_pmu, &Serial)) { logf("WARN: PMU init failed"); } @@ -1245,8 +1001,7 @@ void setup() oledShowLines("Exercise 12", "FiveTalk startup", NODE_SHORT, NODE_LABEL); SdWatcherConfig sdCfg{}; - if (!g_sd.begin(sdCfg, nullptr)) - { + if (!g_sd.begin(sdCfg, nullptr)) { logf("WARN: SD watcher begin failed"); } @@ -1262,61 +1017,52 @@ void setup() g_gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); g_radioReady = initRadio(); - if (!g_radioReady) - { + if (!g_radioReady) { oledShowLines("LoRa init failed", "Check radio pins"); } g_phase = g_sd.isMounted() ? AppPhase::WAIT_DISCIPLINE : AppPhase::WAIT_SD; } -void loop() -{ +void loop() { pollGpsSerial(); g_sd.update(); - if (g_sd.consumeMountedEvent()) - { + if (g_sd.consumeMountedEvent()) { logf("SD mounted"); g_gpsPathReady = false; - if (g_phase == AppPhase::RUN) - { + if (g_phase == AppPhase::RUN) { g_lastWarnMs = 0; - if (!g_sessionReady) - { + if (!g_sessionReady) { (void)openSessionLogs(); } } } - if (g_sd.consumeRemovedEvent()) - { + if (g_sd.consumeRemovedEvent()) { logf("SD removed"); g_gpsPathReady = false; - if (g_phase == AppPhase::RUN) - { + if (g_phase == AppPhase::RUN) { closeSessionLogs(); g_lastWarnMs = 0; oledShowLines("SD removed", "Logging paused", "LoRa continues"); } } - if (!g_radioReady) - { + if (!g_radioReady) { delay(50); return; } - switch (g_phase) - { - case AppPhase::WAIT_SD: - updateWaitSd(); - break; - case AppPhase::WAIT_DISCIPLINE: - updateWaitDiscipline(); - break; - case AppPhase::RUN: - updateRun(); - break; + switch (g_phase) { + case AppPhase::WAIT_SD: + updateWaitSd(); + break; + case AppPhase::WAIT_DISCIPLINE: + updateWaitDiscipline(); + break; + case AppPhase::RUN: + updateRun(); + break; } delay(5); diff --git a/tools/livetrack/fieldtest_map.html b/tools/livetrack/fieldtest_map.html deleted file mode 100644 index fbd48b6..0000000 --- a/tools/livetrack/fieldtest_map.html +++ /dev/null @@ -1,157 +0,0 @@ - - -
- -