Reformatting lib files and am starting to add DOxygen comments

This commit is contained in:
John Poole 2026-04-24 16:30:27 -07:00
commit 6fdbf1d258
5 changed files with 955 additions and 638 deletions

View file

@ -18,11 +18,27 @@
#define LORA_CR 5 #define LORA_CR 5
#endif #endif
/**
* This sketch is intended to be used as a quick test of the LoRa radio on the
* T-Beam Supreme board, to verify that the radio is functional and can be used
* in a USB-connected application.
* It will attempt to initialize the radio, and then repeatedly transmit a test
* frame and call startReceive() to verify that the radio is responsive.
* Note that this sketch is not intended to be a full test of the radio's
* functionality, but rather a quick check that the radio can be initialized
* and used without errors. If you are seeing -706 or -707 errors, it likely means
* that the radio is not starting up correctly, which can be caused by incorrect
* pin connections or power issues. If you are seeing other errors, it may indicate
* a different issue with the radio or the code.
*/
// SX1262 on T-Beam Supreme (tbeam-s3-core pinout) // SX1262 on T-Beam Supreme (tbeam-s3-core pinout)
SX1262 radio = new Module(LORA_CS, LORA_DIO1, LORA_RESET, LORA_BUSY); SX1262 radio = new Module(LORA_CS, LORA_DIO1, LORA_RESET, LORA_BUSY);
int state; // = radio.begin(915.0, 125.0, 7, 5, 0x12, 14); int state; // = radio.begin(915.0, 125.0, 7, 5, 0x12, 14);
/*
@brief Setup function. Initializes the radio and prints the result to the serial console.
*/
void setup() { void setup() {
Serial.begin(115200); Serial.begin(115200);
delay(2000); // give USB time to enumerate delay(2000); // give USB time to enumerate
@ -42,7 +58,10 @@ void setup() {
} }
/*
@brief Loop function. Transmits a test frame and calls startReceive() to verify that
the radio is responsive. Repeats every second.
*/
void loop() { void loop() {
static uint32_t counter = 0; static uint32_t counter = 0;
Serial.printf("alive %lu\n", counter++); Serial.printf("alive %lu\n", counter++);

File diff suppressed because it is too large Load diff

View file

@ -8,29 +8,35 @@
#define OLED_SCL 18 #define OLED_SCL 18
#endif #endif
namespace tbeam { namespace tbeam
{
TBeamClock::TBeamClock(TwoWire& wire) : wire_(wire) {} TBeamClock::TBeamClock(TwoWire &wire) : wire_(wire) {}
bool TBeamClock::begin(const ClockConfig& config) { bool TBeamClock::begin(const ClockConfig &config)
{
config_ = config; config_ = config;
clearError(); clearError();
if (config_.sda < 0) { if (config_.sda < 0)
{
config_.sda = OLED_SDA; config_.sda = OLED_SDA;
} }
if (config_.scl < 0) { if (config_.scl < 0)
{
config_.scl = OLED_SCL; config_.scl = OLED_SCL;
} }
if (config_.beginWire) { if (config_.beginWire)
{
wire_.begin(config_.sda, config_.scl); wire_.begin(config_.sda, config_.scl);
} }
DateTime dt{}; DateTime dt{};
bool lowVoltage = false; bool lowVoltage = false;
ready_ = readRtc(dt, lowVoltage); ready_ = readRtc(dt, lowVoltage);
if (!ready_) { if (!ready_)
{
setError("RTC read failed"); setError("RTC read failed");
valid_ = false; valid_ = false;
return false; return false;
@ -40,18 +46,23 @@ bool TBeamClock::begin(const ClockConfig& config) {
lastRtc_ = dt; lastRtc_ = dt;
valid_ = !lowVoltage && isValidDateTime(dt); valid_ = !lowVoltage && isValidDateTime(dt);
lastEpoch_ = valid_ ? toEpochSeconds(dt) : 0; lastEpoch_ = valid_ ? toEpochSeconds(dt) : 0;
if (lowVoltage) { if (lowVoltage)
{
setError("RTC low-voltage flag set"); setError("RTC low-voltage flag set");
} else if (!valid_) { }
else if (!valid_)
{
setError("RTC date/time invalid"); setError("RTC date/time invalid");
} }
return true; return true;
} }
void TBeamClock::update() { void TBeamClock::update()
{
DateTime dt{}; DateTime dt{};
bool lowVoltage = false; bool lowVoltage = false;
if (!readRtc(dt, lowVoltage)) { if (!readRtc(dt, lowVoltage))
{
ready_ = false; ready_ = false;
valid_ = false; valid_ = false;
setError("RTC read failed"); setError("RTC read failed");
@ -63,25 +74,33 @@ void TBeamClock::update() {
lastRtc_ = dt; lastRtc_ = dt;
valid_ = !lowVoltage && isValidDateTime(dt); valid_ = !lowVoltage && isValidDateTime(dt);
lastEpoch_ = valid_ ? toEpochSeconds(dt) : 0; lastEpoch_ = valid_ ? toEpochSeconds(dt) : 0;
if (valid_) { if (valid_)
{
clearError(); clearError();
} else if (lowVoltage) { }
else if (lowVoltage)
{
setError("RTC low-voltage flag set"); setError("RTC low-voltage flag set");
} else { }
else
{
setError("RTC date/time invalid"); setError("RTC date/time invalid");
} }
} }
bool TBeamClock::readRtc(DateTime& out, bool& lowVoltageFlag) const { bool TBeamClock::readRtc(DateTime &out, bool &lowVoltageFlag) const
{
wire_.beginTransmission(config_.rtcAddress); wire_.beginTransmission(config_.rtcAddress);
wire_.write(0x02); wire_.write(0x02);
if (wire_.endTransmission(false) != 0) { if (wire_.endTransmission(false) != 0)
{
return false; return false;
} }
const uint8_t need = 7; const uint8_t need = 7;
const uint8_t got = wire_.requestFrom((int)config_.rtcAddress, (int)need); const uint8_t got = wire_.requestFrom((int)config_.rtcAddress, (int)need);
if (got != need) { if (got != need)
{
return false; return false;
} }
@ -103,21 +122,26 @@ bool TBeamClock::readRtc(DateTime& out, bool& lowVoltageFlag) const {
const uint8_t yy = fromBcd(year); const uint8_t yy = fromBcd(year);
out.year = (month & 0x80U) ? (1900U + yy) : (2000U + yy); out.year = (month & 0x80U) ? (1900U + yy) : (2000U + yy);
return true; return true;
} }
bool TBeamClock::readValidRtc(DateTime& out, int64_t* epochOut) const { bool TBeamClock::readValidRtc(DateTime &out, int64_t *epochOut) const
{
bool lowVoltage = false; bool lowVoltage = false;
if (!readRtc(out, lowVoltage) || lowVoltage || !isValidDateTime(out)) { if (!readRtc(out, lowVoltage) || lowVoltage || !isValidDateTime(out))
{
return false; return false;
} }
if (epochOut) { if (epochOut)
{
*epochOut = toEpochSeconds(out); *epochOut = toEpochSeconds(out);
} }
return true; return true;
} }
bool TBeamClock::writeRtc(const DateTime& dt) const { bool TBeamClock::writeRtc(const DateTime &dt) const
if (!isValidDateTime(dt)) { {
if (!isValidDateTime(dt))
{
return false; return false;
} }
@ -130,35 +154,45 @@ bool TBeamClock::writeRtc(const DateTime& dt) const {
wire_.write(toBcd(dt.weekday) & 0x07U); wire_.write(toBcd(dt.weekday) & 0x07U);
uint8_t monthReg = toBcd(dt.month) & 0x1FU; uint8_t monthReg = toBcd(dt.month) & 0x1FU;
if (dt.year < 2000U) { if (dt.year < 2000U)
{
monthReg |= 0x80U; monthReg |= 0x80U;
} }
wire_.write(monthReg); wire_.write(monthReg);
wire_.write(toBcd((uint8_t)(dt.year % 100U))); wire_.write(toBcd((uint8_t)(dt.year % 100U)));
return wire_.endTransmission() == 0; return wire_.endTransmission() == 0;
} }
bool TBeamClock::isValidDateTime(const DateTime& dt) { bool TBeamClock::isValidDateTime(const DateTime &dt)
if (dt.year < 2000U || dt.year > 2099U) return false; {
if (dt.month < 1U || dt.month > 12U) return false; if (dt.year < 2000U || dt.year > 2099U)
if (dt.day < 1U || dt.day > daysInMonth(dt.year, dt.month)) return false; return false;
if (dt.hour > 23U || dt.minute > 59U || dt.second > 59U) return false; if (dt.month < 1U || dt.month > 12U)
return false;
if (dt.day < 1U || dt.day > daysInMonth(dt.year, dt.month))
return false;
if (dt.hour > 23U || dt.minute > 59U || dt.second > 59U)
return false;
return true; return true;
} }
int64_t TBeamClock::toEpochSeconds(const DateTime& dt) { int64_t TBeamClock::toEpochSeconds(const DateTime &dt)
{
const int64_t days = daysFromCivil((int)dt.year, dt.month, dt.day); const 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; return days * 86400LL + (int64_t)dt.hour * 3600LL + (int64_t)dt.minute * 60LL + (int64_t)dt.second;
} }
bool TBeamClock::fromEpochSeconds(int64_t seconds, DateTime& out) { bool TBeamClock::fromEpochSeconds(int64_t seconds, DateTime &out)
if (seconds < 0) { {
if (seconds < 0)
{
return false; return false;
} }
int64_t days = seconds / 86400LL; int64_t days = seconds / 86400LL;
int64_t remainder = seconds % 86400LL; int64_t remainder = seconds % 86400LL;
if (remainder < 0) { if (remainder < 0)
{
remainder += 86400LL; remainder += 86400LL;
days -= 1; days -= 1;
} }
@ -184,9 +218,10 @@ bool TBeamClock::fromEpochSeconds(int64_t seconds, DateTime& out) {
out.day = (uint8_t)day; out.day = (uint8_t)day;
out.weekday = 0; out.weekday = 0;
return isValidDateTime(out); return isValidDateTime(out);
} }
void TBeamClock::formatIsoUtc(const DateTime& dt, char* out, size_t outSize) { void TBeamClock::formatIsoUtc(const DateTime &dt, char *out, size_t outSize)
{
snprintf(out, snprintf(out,
outSize, outSize,
"%04u-%02u-%02uT%02u:%02u:%02uZ", "%04u-%02u-%02uT%02u:%02u:%02uZ",
@ -196,9 +231,10 @@ void TBeamClock::formatIsoUtc(const DateTime& dt, char* out, size_t outSize) {
(unsigned)dt.hour, (unsigned)dt.hour,
(unsigned)dt.minute, (unsigned)dt.minute,
(unsigned)dt.second); (unsigned)dt.second);
} }
void TBeamClock::formatCompactUtc(const DateTime& dt, char* out, size_t outSize) { void TBeamClock::formatCompactUtc(const DateTime &dt, char *out, size_t outSize)
{
snprintf(out, snprintf(out,
outSize, outSize,
"%04u%02u%02u_%02u%02u%02u", "%04u%02u%02u_%02u%02u%02u",
@ -208,9 +244,10 @@ void TBeamClock::formatCompactUtc(const DateTime& dt, char* out, size_t outSize)
(unsigned)dt.hour, (unsigned)dt.hour,
(unsigned)dt.minute, (unsigned)dt.minute,
(unsigned)dt.second); (unsigned)dt.second);
} }
void TBeamClock::makeRunId(const DateTime& dt, const char* boardId, char* out, size_t outSize) { void TBeamClock::makeRunId(const DateTime &dt, const char *boardId, char *out, size_t outSize)
{
snprintf(out, snprintf(out,
outSize, outSize,
"%04u%02u%02u_%02u%02u%02u_%s", "%04u%02u%02u_%02u%02u%02u_%s",
@ -221,10 +258,12 @@ void TBeamClock::makeRunId(const DateTime& dt, const char* boardId, char* out, s
(unsigned)dt.minute, (unsigned)dt.minute,
(unsigned)dt.second, (unsigned)dt.second,
boardId ? boardId : "NODE"); boardId ? boardId : "NODE");
} }
bool TBeamClock::parseDateTime(const char* text, DateTime& out) { bool TBeamClock::parseDateTime(const char *text, DateTime &out)
if (!text) { {
if (!text)
{
return false; return false;
} }
int y = 0; int y = 0;
@ -234,7 +273,8 @@ bool TBeamClock::parseDateTime(const char* text, DateTime& out) {
int mi = 0; int mi = 0;
int s = 0; int s = 0;
if (sscanf(text, "%d-%d-%d %d:%d:%d", &y, &mo, &d, &h, &mi, &s) != 6 && if (sscanf(text, "%d-%d-%d %d:%d:%d", &y, &mo, &d, &h, &mi, &s) != 6 &&
sscanf(text, "%d-%d-%dT%d:%d:%d", &y, &mo, &d, &h, &mi, &s) != 6) { sscanf(text, "%d-%d-%dT%d:%d:%d", &y, &mo, &d, &h, &mi, &s) != 6)
{
return false; return false;
} }
@ -246,46 +286,55 @@ bool TBeamClock::parseDateTime(const char* text, DateTime& out) {
out.second = (uint8_t)s; out.second = (uint8_t)s;
out.weekday = 0; out.weekday = 0;
return isValidDateTime(out); return isValidDateTime(out);
} }
uint8_t TBeamClock::toBcd(uint8_t value) { uint8_t TBeamClock::toBcd(uint8_t value)
{
return (uint8_t)(((value / 10U) << 4U) | (value % 10U)); return (uint8_t)(((value / 10U) << 4U) | (value % 10U));
} }
uint8_t TBeamClock::fromBcd(uint8_t value) { uint8_t TBeamClock::fromBcd(uint8_t value)
{
return (uint8_t)(((value >> 4U) * 10U) + (value & 0x0FU)); return (uint8_t)(((value >> 4U) * 10U) + (value & 0x0FU));
} }
bool TBeamClock::isLeapYear(uint16_t year) { bool TBeamClock::isLeapYear(uint16_t year)
{
return ((year % 4U) == 0U && (year % 100U) != 0U) || ((year % 400U) == 0U); return ((year % 4U) == 0U && (year % 100U) != 0U) || ((year % 400U) == 0U);
} }
uint8_t TBeamClock::daysInMonth(uint16_t year, uint8_t month) { uint8_t TBeamClock::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}; static const uint8_t kDays[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
if (month == 2U) { if (month == 2U)
{
return (uint8_t)(isLeapYear(year) ? 29U : 28U); return (uint8_t)(isLeapYear(year) ? 29U : 28U);
} }
if (month >= 1U && month <= 12U) { if (month >= 1U && month <= 12U)
{
return kDays[month - 1U]; return kDays[month - 1U];
} }
return 0; return 0;
} }
int64_t TBeamClock::daysFromCivil(int year, unsigned month, unsigned day) { int64_t TBeamClock::daysFromCivil(int year, unsigned month, unsigned day)
{
year -= (month <= 2U); year -= (month <= 2U);
const int era = (year >= 0 ? year : year - 399) / 400; const int era = (year >= 0 ? year : year - 399) / 400;
const unsigned yoe = (unsigned)(year - era * 400); const unsigned yoe = (unsigned)(year - era * 400);
const unsigned doy = (153U * (month + (month > 2U ? (unsigned)-3 : 9U)) + 2U) / 5U + day - 1U; const unsigned doy = (153U * (month + (month > 2U ? (unsigned)-3 : 9U)) + 2U) / 5U + day - 1U;
const unsigned doe = yoe * 365U + yoe / 4U - yoe / 100U + doy; const unsigned doe = yoe * 365U + yoe / 4U - yoe / 100U + doy;
return era * 146097 + (int)doe - 719468; return era * 146097 + (int)doe - 719468;
} }
void TBeamClock::setError(const char* message) const { void TBeamClock::setError(const char *message) const
{
strlcpy(lastError_, message ? message : "", sizeof(lastError_)); strlcpy(lastError_, message ? message : "", sizeof(lastError_));
} }
void TBeamClock::clearError() const { void TBeamClock::clearError() const
{
lastError_[0] = '\0'; lastError_[0] = '\0';
} }
} // namespace tbeam } // namespace tbeam

View file

@ -3,9 +3,11 @@
#include <Arduino.h> #include <Arduino.h>
#include <Wire.h> #include <Wire.h>
namespace tbeam { namespace tbeam
{
struct DateTime { struct DateTime
{
uint16_t year = 0; uint16_t year = 0;
uint8_t month = 0; uint8_t month = 0;
uint8_t day = 0; uint8_t day = 0;
@ -13,40 +15,42 @@ struct DateTime {
uint8_t minute = 0; uint8_t minute = 0;
uint8_t second = 0; uint8_t second = 0;
uint8_t weekday = 0; uint8_t weekday = 0;
}; };
struct ClockConfig { struct ClockConfig
{
uint8_t rtcAddress = 0x51; uint8_t rtcAddress = 0x51;
int sda = -1; int sda = -1;
int scl = -1; int scl = -1;
bool beginWire = true; bool beginWire = true;
}; };
class TBeamClock { class TBeamClock
{
public: public:
explicit TBeamClock(TwoWire& wire = Wire1); explicit TBeamClock(TwoWire &wire = Wire1);
bool begin(const ClockConfig& config = ClockConfig{}); bool begin(const ClockConfig &config = ClockConfig{});
void update(); void update();
bool readRtc(DateTime& out, bool& lowVoltageFlag) const; bool readRtc(DateTime &out, bool &lowVoltageFlag) const;
bool readValidRtc(DateTime& out, int64_t* epochOut = nullptr) const; bool readValidRtc(DateTime &out, int64_t *epochOut = nullptr) const;
bool writeRtc(const DateTime& dt) const; bool writeRtc(const DateTime &dt) const;
bool ready() const { return ready_; } bool ready() const { return ready_; }
bool valid() const { return valid_; } bool valid() const { return valid_; }
bool lowVoltage() const { return lowVoltage_; } bool lowVoltage() const { return lowVoltage_; }
const DateTime& lastRtc() const { return lastRtc_; } const DateTime &lastRtc() const { return lastRtc_; }
int64_t lastEpoch() const { return lastEpoch_; } int64_t lastEpoch() const { return lastEpoch_; }
const char* lastError() const { return lastError_; } const char *lastError() const { return lastError_; }
static bool isValidDateTime(const DateTime& dt); static bool isValidDateTime(const DateTime &dt);
static int64_t toEpochSeconds(const DateTime& dt); static int64_t toEpochSeconds(const DateTime &dt);
static bool fromEpochSeconds(int64_t seconds, DateTime& out); static bool fromEpochSeconds(int64_t seconds, DateTime &out);
static void formatIsoUtc(const DateTime& dt, char* out, size_t outSize); static void formatIsoUtc(const DateTime &dt, char *out, size_t outSize);
static void formatCompactUtc(const DateTime& dt, char* out, size_t outSize); static void formatCompactUtc(const DateTime &dt, char *out, size_t outSize);
static void makeRunId(const DateTime& dt, const char* boardId, char* out, size_t outSize); static void makeRunId(const DateTime &dt, const char *boardId, char *out, size_t outSize);
static bool parseDateTime(const char* text, DateTime& out); static bool parseDateTime(const char *text, DateTime &out);
private: private:
static uint8_t toBcd(uint8_t value); static uint8_t toBcd(uint8_t value);
@ -55,10 +59,10 @@ class TBeamClock {
static uint8_t daysInMonth(uint16_t year, uint8_t month); static uint8_t daysInMonth(uint16_t year, uint8_t month);
static int64_t daysFromCivil(int year, unsigned month, unsigned day); static int64_t daysFromCivil(int year, unsigned month, unsigned day);
void setError(const char* message) const; void setError(const char *message) const;
void clearError() const; void clearError() const;
TwoWire& wire_; TwoWire &wire_;
ClockConfig config_{}; ClockConfig config_{};
bool ready_ = false; bool ready_ = false;
bool valid_ = false; bool valid_ = false;
@ -66,6 +70,6 @@ class TBeamClock {
DateTime lastRtc_{}; DateTime lastRtc_{};
int64_t lastEpoch_ = 0; int64_t lastEpoch_ = 0;
mutable char lastError_[128] = {}; mutable char lastError_[128] = {};
}; };
} // namespace tbeam } // namespace tbeam

View file

@ -1,78 +1,98 @@
#include "TBeamLogger.h" #include "TBeamLogger.h"
namespace tbeam { namespace tbeam
{
bool TBeamLogger::begin(Print& serial, TBeamStorage* storage, const LoggerConfig& config) { bool TBeamLogger::begin(Print &serial, TBeamStorage *storage, const LoggerConfig &config)
{
serial_ = &serial; serial_ = &serial;
storage_ = storage; storage_ = storage;
config_ = config; config_ = config;
lastFlushMs_ = millis(); lastFlushMs_ = millis();
return true; return true;
} }
void TBeamLogger::update() { void TBeamLogger::update()
if (!config_.autoFlush || !storage_) { {
if (!config_.autoFlush || !storage_)
{
return; return;
} }
const uint32_t now = millis(); const uint32_t now = millis();
if ((uint32_t)(now - lastFlushMs_) >= config_.flushIntervalMs) { if ((uint32_t)(now - lastFlushMs_) >= config_.flushIntervalMs)
{
storage_->flush(); storage_->flush();
lastFlushMs_ = now; lastFlushMs_ = now;
} }
} }
bool TBeamLogger::openLog(const char* path) { bool TBeamLogger::openLog(const char *path)
{
return storage_ && storage_->openLog(path); return storage_ && storage_->openLog(path);
} }
bool TBeamLogger::openUniqueLog(const char* prefix, const char* extension) { bool TBeamLogger::openUniqueLog(const char *prefix, const char *extension)
if (!storage_) { {
if (!storage_)
{
return false; return false;
} }
char path[128]; char path[128];
if (!storage_->makeUniqueLogPath(prefix, extension, path, sizeof(path))) { if (!storage_->makeUniqueLogPath(prefix, extension, path, sizeof(path)))
{
return false; return false;
} }
return storage_->openLog(path); return storage_->openLog(path);
} }
const char* TBeamLogger::currentLogPath() const { const char *TBeamLogger::currentLogPath() const
{
return storage_ ? storage_->currentLogPath() : ""; return storage_ ? storage_->currentLogPath() : "";
} }
bool TBeamLogger::storageReady() const { bool TBeamLogger::storageReady() const
{
return storage_ && storage_->ready() && storage_->isLogOpen(); return storage_ && storage_->ready() && storage_->isLogOpen();
} }
void TBeamLogger::flush() { void TBeamLogger::flush()
if (storage_) { {
if (storage_)
{
storage_->flush(); storage_->flush();
} }
if (serial_) { if (serial_)
{
serial_->flush(); serial_->flush();
} }
} }
void TBeamLogger::closeLog() { void TBeamLogger::closeLog()
if (storage_) { {
if (storage_)
{
storage_->closeLog(); storage_->closeLog();
} }
} }
size_t TBeamLogger::write(uint8_t value) { size_t TBeamLogger::write(uint8_t value)
{
return write(&value, 1); return write(&value, 1);
} }
size_t TBeamLogger::write(const uint8_t* buffer, size_t size) { size_t TBeamLogger::write(const uint8_t *buffer, size_t size)
{
size_t serialWrote = 0; size_t serialWrote = 0;
size_t storageWrote = 0; size_t storageWrote = 0;
if (config_.echoSerial && serial_) { if (config_.echoSerial && serial_)
{
serialWrote = serial_->write(buffer, size); serialWrote = serial_->write(buffer, size);
} }
if (config_.echoStorage && storage_ && storage_->isLogOpen()) { if (config_.echoStorage && storage_ && storage_->isLogOpen())
{
storageWrote = storage_->write(buffer, size); storageWrote = storage_->write(buffer, size);
} }
return storageWrote > 0 ? storageWrote : serialWrote; return storageWrote > 0 ? storageWrote : serialWrote;
} }
} // namespace tbeam } // namespace tbeam