Reformatting lib files and am starting to add DOxygen comments
This commit is contained in:
parent
8c7e2d477c
commit
6fdbf1d258
5 changed files with 955 additions and 638 deletions
|
|
@ -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
|
|
@ -8,284 +8,333 @@
|
||||||
#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;
|
{
|
||||||
clearError();
|
config_ = config;
|
||||||
|
|
||||||
if (config_.sda < 0) {
|
|
||||||
config_.sda = OLED_SDA;
|
|
||||||
}
|
|
||||||
if (config_.scl < 0) {
|
|
||||||
config_.scl = OLED_SCL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config_.beginWire) {
|
|
||||||
wire_.begin(config_.sda, config_.scl);
|
|
||||||
}
|
|
||||||
|
|
||||||
DateTime dt{};
|
|
||||||
bool lowVoltage = false;
|
|
||||||
ready_ = readRtc(dt, lowVoltage);
|
|
||||||
if (!ready_) {
|
|
||||||
setError("RTC read failed");
|
|
||||||
valid_ = false;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
lowVoltage_ = lowVoltage;
|
|
||||||
lastRtc_ = dt;
|
|
||||||
valid_ = !lowVoltage && isValidDateTime(dt);
|
|
||||||
lastEpoch_ = valid_ ? toEpochSeconds(dt) : 0;
|
|
||||||
if (lowVoltage) {
|
|
||||||
setError("RTC low-voltage flag set");
|
|
||||||
} else if (!valid_) {
|
|
||||||
setError("RTC date/time invalid");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TBeamClock::update() {
|
|
||||||
DateTime dt{};
|
|
||||||
bool lowVoltage = false;
|
|
||||||
if (!readRtc(dt, lowVoltage)) {
|
|
||||||
ready_ = false;
|
|
||||||
valid_ = false;
|
|
||||||
setError("RTC read failed");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
ready_ = true;
|
|
||||||
lowVoltage_ = lowVoltage;
|
|
||||||
lastRtc_ = dt;
|
|
||||||
valid_ = !lowVoltage && isValidDateTime(dt);
|
|
||||||
lastEpoch_ = valid_ ? toEpochSeconds(dt) : 0;
|
|
||||||
if (valid_) {
|
|
||||||
clearError();
|
clearError();
|
||||||
} else if (lowVoltage) {
|
|
||||||
setError("RTC low-voltage flag set");
|
|
||||||
} else {
|
|
||||||
setError("RTC date/time invalid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TBeamClock::readRtc(DateTime& out, bool& lowVoltageFlag) const {
|
if (config_.sda < 0)
|
||||||
wire_.beginTransmission(config_.rtcAddress);
|
{
|
||||||
wire_.write(0x02);
|
config_.sda = OLED_SDA;
|
||||||
if (wire_.endTransmission(false) != 0) {
|
}
|
||||||
return false;
|
if (config_.scl < 0)
|
||||||
|
{
|
||||||
|
config_.scl = OLED_SCL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config_.beginWire)
|
||||||
|
{
|
||||||
|
wire_.begin(config_.sda, config_.scl);
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime dt{};
|
||||||
|
bool lowVoltage = false;
|
||||||
|
ready_ = readRtc(dt, lowVoltage);
|
||||||
|
if (!ready_)
|
||||||
|
{
|
||||||
|
setError("RTC read failed");
|
||||||
|
valid_ = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
lowVoltage_ = lowVoltage;
|
||||||
|
lastRtc_ = dt;
|
||||||
|
valid_ = !lowVoltage && isValidDateTime(dt);
|
||||||
|
lastEpoch_ = valid_ ? toEpochSeconds(dt) : 0;
|
||||||
|
if (lowVoltage)
|
||||||
|
{
|
||||||
|
setError("RTC low-voltage flag set");
|
||||||
|
}
|
||||||
|
else if (!valid_)
|
||||||
|
{
|
||||||
|
setError("RTC date/time invalid");
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t need = 7;
|
void TBeamClock::update()
|
||||||
const uint8_t got = wire_.requestFrom((int)config_.rtcAddress, (int)need);
|
{
|
||||||
if (got != need) {
|
DateTime dt{};
|
||||||
return false;
|
bool lowVoltage = false;
|
||||||
|
if (!readRtc(dt, lowVoltage))
|
||||||
|
{
|
||||||
|
ready_ = false;
|
||||||
|
valid_ = false;
|
||||||
|
setError("RTC read failed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ready_ = true;
|
||||||
|
lowVoltage_ = lowVoltage;
|
||||||
|
lastRtc_ = dt;
|
||||||
|
valid_ = !lowVoltage && isValidDateTime(dt);
|
||||||
|
lastEpoch_ = valid_ ? toEpochSeconds(dt) : 0;
|
||||||
|
if (valid_)
|
||||||
|
{
|
||||||
|
clearError();
|
||||||
|
}
|
||||||
|
else if (lowVoltage)
|
||||||
|
{
|
||||||
|
setError("RTC low-voltage flag set");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setError("RTC date/time invalid");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint8_t sec = wire_.read();
|
bool TBeamClock::readRtc(DateTime &out, bool &lowVoltageFlag) const
|
||||||
const uint8_t min = wire_.read();
|
{
|
||||||
const uint8_t hour = wire_.read();
|
wire_.beginTransmission(config_.rtcAddress);
|
||||||
const uint8_t day = wire_.read();
|
wire_.write(0x02);
|
||||||
const uint8_t weekday = wire_.read();
|
if (wire_.endTransmission(false) != 0)
|
||||||
const uint8_t month = wire_.read();
|
{
|
||||||
const uint8_t year = wire_.read();
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
lowVoltageFlag = (sec & 0x80U) != 0;
|
const uint8_t need = 7;
|
||||||
out.second = fromBcd(sec & 0x7FU);
|
const uint8_t got = wire_.requestFrom((int)config_.rtcAddress, (int)need);
|
||||||
out.minute = fromBcd(min & 0x7FU);
|
if (got != need)
|
||||||
out.hour = fromBcd(hour & 0x3FU);
|
{
|
||||||
out.day = fromBcd(day & 0x3FU);
|
return false;
|
||||||
out.weekday = fromBcd(weekday & 0x07U);
|
}
|
||||||
out.month = fromBcd(month & 0x1FU);
|
|
||||||
const uint8_t yy = fromBcd(year);
|
|
||||||
out.year = (month & 0x80U) ? (1900U + yy) : (2000U + yy);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TBeamClock::readValidRtc(DateTime& out, int64_t* epochOut) const {
|
const uint8_t sec = wire_.read();
|
||||||
bool lowVoltage = false;
|
const uint8_t min = wire_.read();
|
||||||
if (!readRtc(out, lowVoltage) || lowVoltage || !isValidDateTime(out)) {
|
const uint8_t hour = wire_.read();
|
||||||
return false;
|
const uint8_t day = wire_.read();
|
||||||
}
|
const uint8_t weekday = wire_.read();
|
||||||
if (epochOut) {
|
const uint8_t month = wire_.read();
|
||||||
*epochOut = toEpochSeconds(out);
|
const uint8_t year = wire_.read();
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TBeamClock::writeRtc(const DateTime& dt) const {
|
lowVoltageFlag = (sec & 0x80U) != 0;
|
||||||
if (!isValidDateTime(dt)) {
|
out.second = fromBcd(sec & 0x7FU);
|
||||||
return false;
|
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);
|
||||||
|
const uint8_t yy = fromBcd(year);
|
||||||
|
out.year = (month & 0x80U) ? (1900U + yy) : (2000U + yy);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
wire_.beginTransmission(config_.rtcAddress);
|
bool TBeamClock::readValidRtc(DateTime &out, int64_t *epochOut) const
|
||||||
wire_.write(0x02);
|
{
|
||||||
wire_.write(toBcd(dt.second) & 0x7FU);
|
bool lowVoltage = false;
|
||||||
wire_.write(toBcd(dt.minute) & 0x7FU);
|
if (!readRtc(out, lowVoltage) || lowVoltage || !isValidDateTime(out))
|
||||||
wire_.write(toBcd(dt.hour) & 0x3FU);
|
{
|
||||||
wire_.write(toBcd(dt.day) & 0x3FU);
|
return false;
|
||||||
wire_.write(toBcd(dt.weekday) & 0x07U);
|
}
|
||||||
|
if (epochOut)
|
||||||
uint8_t monthReg = toBcd(dt.month) & 0x1FU;
|
{
|
||||||
if (dt.year < 2000U) {
|
*epochOut = toEpochSeconds(out);
|
||||||
monthReg |= 0x80U;
|
}
|
||||||
}
|
return true;
|
||||||
wire_.write(monthReg);
|
|
||||||
wire_.write(toBcd((uint8_t)(dt.year % 100U)));
|
|
||||||
return wire_.endTransmission() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t TBeamClock::toEpochSeconds(const DateTime& dt) {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TBeamClock::fromEpochSeconds(int64_t seconds, DateTime& out) {
|
|
||||||
if (seconds < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int64_t days = seconds / 86400LL;
|
bool TBeamClock::writeRtc(const DateTime &dt) const
|
||||||
int64_t remainder = seconds % 86400LL;
|
{
|
||||||
if (remainder < 0) {
|
if (!isValidDateTime(dt))
|
||||||
remainder += 86400LL;
|
{
|
||||||
days -= 1;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
wire_.beginTransmission(config_.rtcAddress);
|
||||||
|
wire_.write(0x02);
|
||||||
|
wire_.write(toBcd(dt.second) & 0x7FU);
|
||||||
|
wire_.write(toBcd(dt.minute) & 0x7FU);
|
||||||
|
wire_.write(toBcd(dt.hour) & 0x3FU);
|
||||||
|
wire_.write(toBcd(dt.day) & 0x3FU);
|
||||||
|
wire_.write(toBcd(dt.weekday) & 0x07U);
|
||||||
|
|
||||||
|
uint8_t monthReg = toBcd(dt.month) & 0x1FU;
|
||||||
|
if (dt.year < 2000U)
|
||||||
|
{
|
||||||
|
monthReg |= 0x80U;
|
||||||
|
}
|
||||||
|
wire_.write(monthReg);
|
||||||
|
wire_.write(toBcd((uint8_t)(dt.year % 100U)));
|
||||||
|
return wire_.endTransmission() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.hour = (uint8_t)(remainder / 3600LL);
|
bool TBeamClock::isValidDateTime(const DateTime &dt)
|
||||||
remainder %= 3600LL;
|
{
|
||||||
out.minute = (uint8_t)(remainder / 60LL);
|
if (dt.year < 2000U || dt.year > 2099U)
|
||||||
out.second = (uint8_t)(remainder % 60LL);
|
return false;
|
||||||
|
if (dt.month < 1U || dt.month > 12U)
|
||||||
days += 719468;
|
return false;
|
||||||
const int era = (days >= 0 ? days : days - 146096) / 146097;
|
if (dt.day < 1U || dt.day > daysInMonth(dt.year, dt.month))
|
||||||
const unsigned doe = (unsigned)(days - era * 146097);
|
return false;
|
||||||
const unsigned yoe = (doe - doe / 1460U + doe / 36524U - doe / 146096U) / 365U;
|
if (dt.hour > 23U || dt.minute > 59U || dt.second > 59U)
|
||||||
int year = (int)yoe + era * 400;
|
return false;
|
||||||
const unsigned doy = doe - (365U * yoe + yoe / 4U - yoe / 100U);
|
return true;
|
||||||
const unsigned mp = (5U * doy + 2U) / 153U;
|
|
||||||
const unsigned day = doy - (153U * mp + 2U) / 5U + 1U;
|
|
||||||
const unsigned month = mp + (mp < 10U ? 3U : (unsigned)-9);
|
|
||||||
year += (month <= 2U);
|
|
||||||
|
|
||||||
out.year = (uint16_t)year;
|
|
||||||
out.month = (uint8_t)month;
|
|
||||||
out.day = (uint8_t)day;
|
|
||||||
out.weekday = 0;
|
|
||||||
return isValidDateTime(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TBeamClock::formatIsoUtc(const DateTime& dt, char* out, size_t outSize) {
|
|
||||||
snprintf(out,
|
|
||||||
outSize,
|
|
||||||
"%04u-%02u-%02uT%02u:%02u:%02uZ",
|
|
||||||
(unsigned)dt.year,
|
|
||||||
(unsigned)dt.month,
|
|
||||||
(unsigned)dt.day,
|
|
||||||
(unsigned)dt.hour,
|
|
||||||
(unsigned)dt.minute,
|
|
||||||
(unsigned)dt.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TBeamClock::formatCompactUtc(const DateTime& dt, char* out, size_t outSize) {
|
|
||||||
snprintf(out,
|
|
||||||
outSize,
|
|
||||||
"%04u%02u%02u_%02u%02u%02u",
|
|
||||||
(unsigned)dt.year,
|
|
||||||
(unsigned)dt.month,
|
|
||||||
(unsigned)dt.day,
|
|
||||||
(unsigned)dt.hour,
|
|
||||||
(unsigned)dt.minute,
|
|
||||||
(unsigned)dt.second);
|
|
||||||
}
|
|
||||||
|
|
||||||
void TBeamClock::makeRunId(const DateTime& dt, const char* boardId, char* out, size_t outSize) {
|
|
||||||
snprintf(out,
|
|
||||||
outSize,
|
|
||||||
"%04u%02u%02u_%02u%02u%02u_%s",
|
|
||||||
(unsigned)dt.year,
|
|
||||||
(unsigned)dt.month,
|
|
||||||
(unsigned)dt.day,
|
|
||||||
(unsigned)dt.hour,
|
|
||||||
(unsigned)dt.minute,
|
|
||||||
(unsigned)dt.second,
|
|
||||||
boardId ? boardId : "NODE");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TBeamClock::parseDateTime(const char* text, DateTime& out) {
|
|
||||||
if (!text) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
int y = 0;
|
|
||||||
int mo = 0;
|
|
||||||
int d = 0;
|
|
||||||
int h = 0;
|
|
||||||
int mi = 0;
|
|
||||||
int s = 0;
|
|
||||||
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) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out.year = (uint16_t)y;
|
int64_t TBeamClock::toEpochSeconds(const DateTime &dt)
|
||||||
out.month = (uint8_t)mo;
|
{
|
||||||
out.day = (uint8_t)d;
|
const int64_t days = daysFromCivil((int)dt.year, dt.month, dt.day);
|
||||||
out.hour = (uint8_t)h;
|
return days * 86400LL + (int64_t)dt.hour * 3600LL + (int64_t)dt.minute * 60LL + (int64_t)dt.second;
|
||||||
out.minute = (uint8_t)mi;
|
|
||||||
out.second = (uint8_t)s;
|
|
||||||
out.weekday = 0;
|
|
||||||
return isValidDateTime(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TBeamClock::toBcd(uint8_t value) {
|
|
||||||
return (uint8_t)(((value / 10U) << 4U) | (value % 10U));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t TBeamClock::fromBcd(uint8_t value) {
|
|
||||||
return (uint8_t)(((value >> 4U) * 10U) + (value & 0x0FU));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TBeamClock::isLeapYear(uint16_t year) {
|
|
||||||
return ((year % 4U) == 0U && (year % 100U) != 0U) || ((year % 400U) == 0U);
|
|
||||||
}
|
|
||||||
|
|
||||||
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};
|
|
||||||
if (month == 2U) {
|
|
||||||
return (uint8_t)(isLeapYear(year) ? 29U : 28U);
|
|
||||||
}
|
}
|
||||||
if (month >= 1U && month <= 12U) {
|
|
||||||
return kDays[month - 1U];
|
bool TBeamClock::fromEpochSeconds(int64_t seconds, DateTime &out)
|
||||||
|
{
|
||||||
|
if (seconds < 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t days = seconds / 86400LL;
|
||||||
|
int64_t remainder = seconds % 86400LL;
|
||||||
|
if (remainder < 0)
|
||||||
|
{
|
||||||
|
remainder += 86400LL;
|
||||||
|
days -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.hour = (uint8_t)(remainder / 3600LL);
|
||||||
|
remainder %= 3600LL;
|
||||||
|
out.minute = (uint8_t)(remainder / 60LL);
|
||||||
|
out.second = (uint8_t)(remainder % 60LL);
|
||||||
|
|
||||||
|
days += 719468;
|
||||||
|
const int era = (days >= 0 ? days : days - 146096) / 146097;
|
||||||
|
const unsigned doe = (unsigned)(days - era * 146097);
|
||||||
|
const unsigned yoe = (doe - doe / 1460U + doe / 36524U - doe / 146096U) / 365U;
|
||||||
|
int year = (int)yoe + era * 400;
|
||||||
|
const unsigned doy = doe - (365U * yoe + yoe / 4U - yoe / 100U);
|
||||||
|
const unsigned mp = (5U * doy + 2U) / 153U;
|
||||||
|
const unsigned day = doy - (153U * mp + 2U) / 5U + 1U;
|
||||||
|
const unsigned month = mp + (mp < 10U ? 3U : (unsigned)-9);
|
||||||
|
year += (month <= 2U);
|
||||||
|
|
||||||
|
out.year = (uint16_t)year;
|
||||||
|
out.month = (uint8_t)month;
|
||||||
|
out.day = (uint8_t)day;
|
||||||
|
out.weekday = 0;
|
||||||
|
return isValidDateTime(out);
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int64_t TBeamClock::daysFromCivil(int year, unsigned month, unsigned day) {
|
void TBeamClock::formatIsoUtc(const DateTime &dt, char *out, size_t outSize)
|
||||||
year -= (month <= 2U);
|
{
|
||||||
const int era = (year >= 0 ? year : year - 399) / 400;
|
snprintf(out,
|
||||||
const unsigned yoe = (unsigned)(year - era * 400);
|
outSize,
|
||||||
const unsigned doy = (153U * (month + (month > 2U ? (unsigned)-3 : 9U)) + 2U) / 5U + day - 1U;
|
"%04u-%02u-%02uT%02u:%02u:%02uZ",
|
||||||
const unsigned doe = yoe * 365U + yoe / 4U - yoe / 100U + doy;
|
(unsigned)dt.year,
|
||||||
return era * 146097 + (int)doe - 719468;
|
(unsigned)dt.month,
|
||||||
}
|
(unsigned)dt.day,
|
||||||
|
(unsigned)dt.hour,
|
||||||
|
(unsigned)dt.minute,
|
||||||
|
(unsigned)dt.second);
|
||||||
|
}
|
||||||
|
|
||||||
void TBeamClock::setError(const char* message) const {
|
void TBeamClock::formatCompactUtc(const DateTime &dt, char *out, size_t outSize)
|
||||||
strlcpy(lastError_, message ? message : "", sizeof(lastError_));
|
{
|
||||||
}
|
snprintf(out,
|
||||||
|
outSize,
|
||||||
|
"%04u%02u%02u_%02u%02u%02u",
|
||||||
|
(unsigned)dt.year,
|
||||||
|
(unsigned)dt.month,
|
||||||
|
(unsigned)dt.day,
|
||||||
|
(unsigned)dt.hour,
|
||||||
|
(unsigned)dt.minute,
|
||||||
|
(unsigned)dt.second);
|
||||||
|
}
|
||||||
|
|
||||||
void TBeamClock::clearError() const {
|
void TBeamClock::makeRunId(const DateTime &dt, const char *boardId, char *out, size_t outSize)
|
||||||
lastError_[0] = '\0';
|
{
|
||||||
}
|
snprintf(out,
|
||||||
|
outSize,
|
||||||
|
"%04u%02u%02u_%02u%02u%02u_%s",
|
||||||
|
(unsigned)dt.year,
|
||||||
|
(unsigned)dt.month,
|
||||||
|
(unsigned)dt.day,
|
||||||
|
(unsigned)dt.hour,
|
||||||
|
(unsigned)dt.minute,
|
||||||
|
(unsigned)dt.second,
|
||||||
|
boardId ? boardId : "NODE");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace tbeam
|
bool TBeamClock::parseDateTime(const char *text, DateTime &out)
|
||||||
|
{
|
||||||
|
if (!text)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int y = 0;
|
||||||
|
int mo = 0;
|
||||||
|
int d = 0;
|
||||||
|
int h = 0;
|
||||||
|
int mi = 0;
|
||||||
|
int s = 0;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.year = (uint16_t)y;
|
||||||
|
out.month = (uint8_t)mo;
|
||||||
|
out.day = (uint8_t)d;
|
||||||
|
out.hour = (uint8_t)h;
|
||||||
|
out.minute = (uint8_t)mi;
|
||||||
|
out.second = (uint8_t)s;
|
||||||
|
out.weekday = 0;
|
||||||
|
return isValidDateTime(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t TBeamClock::toBcd(uint8_t value)
|
||||||
|
{
|
||||||
|
return (uint8_t)(((value / 10U) << 4U) | (value % 10U));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t TBeamClock::fromBcd(uint8_t value)
|
||||||
|
{
|
||||||
|
return (uint8_t)(((value >> 4U) * 10U) + (value & 0x0FU));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TBeamClock::isLeapYear(uint16_t year)
|
||||||
|
{
|
||||||
|
return ((year % 4U) == 0U && (year % 100U) != 0U) || ((year % 400U) == 0U);
|
||||||
|
}
|
||||||
|
|
||||||
|
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};
|
||||||
|
if (month == 2U)
|
||||||
|
{
|
||||||
|
return (uint8_t)(isLeapYear(year) ? 29U : 28U);
|
||||||
|
}
|
||||||
|
if (month >= 1U && month <= 12U)
|
||||||
|
{
|
||||||
|
return kDays[month - 1U];
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t TBeamClock::daysFromCivil(int year, unsigned month, unsigned day)
|
||||||
|
{
|
||||||
|
year -= (month <= 2U);
|
||||||
|
const int era = (year >= 0 ? year : year - 399) / 400;
|
||||||
|
const unsigned yoe = (unsigned)(year - era * 400);
|
||||||
|
const unsigned doy = (153U * (month + (month > 2U ? (unsigned)-3 : 9U)) + 2U) / 5U + day - 1U;
|
||||||
|
const unsigned doe = yoe * 365U + yoe / 4U - yoe / 100U + doy;
|
||||||
|
return era * 146097 + (int)doe - 719468;
|
||||||
|
}
|
||||||
|
|
||||||
|
void TBeamClock::setError(const char *message) const
|
||||||
|
{
|
||||||
|
strlcpy(lastError_, message ? message : "", sizeof(lastError_));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TBeamClock::clearError() const
|
||||||
|
{
|
||||||
|
lastError_[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tbeam
|
||||||
|
|
|
||||||
|
|
@ -3,69 +3,73 @@
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
|
|
||||||
namespace tbeam {
|
namespace tbeam
|
||||||
|
{
|
||||||
|
|
||||||
struct DateTime {
|
struct DateTime
|
||||||
uint16_t year = 0;
|
{
|
||||||
uint8_t month = 0;
|
uint16_t year = 0;
|
||||||
uint8_t day = 0;
|
uint8_t month = 0;
|
||||||
uint8_t hour = 0;
|
uint8_t day = 0;
|
||||||
uint8_t minute = 0;
|
uint8_t hour = 0;
|
||||||
uint8_t second = 0;
|
uint8_t minute = 0;
|
||||||
uint8_t weekday = 0;
|
uint8_t second = 0;
|
||||||
};
|
uint8_t weekday = 0;
|
||||||
|
};
|
||||||
|
|
||||||
struct ClockConfig {
|
struct ClockConfig
|
||||||
uint8_t rtcAddress = 0x51;
|
{
|
||||||
int sda = -1;
|
uint8_t rtcAddress = 0x51;
|
||||||
int scl = -1;
|
int sda = -1;
|
||||||
bool beginWire = true;
|
int scl = -1;
|
||||||
};
|
bool beginWire = true;
|
||||||
|
};
|
||||||
|
|
||||||
class TBeamClock {
|
class TBeamClock
|
||||||
public:
|
{
|
||||||
explicit TBeamClock(TwoWire& wire = Wire1);
|
public:
|
||||||
|
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);
|
||||||
static uint8_t fromBcd(uint8_t value);
|
static uint8_t fromBcd(uint8_t value);
|
||||||
static bool isLeapYear(uint16_t year);
|
static bool isLeapYear(uint16_t year);
|
||||||
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;
|
||||||
bool lowVoltage_ = false;
|
bool lowVoltage_ = false;
|
||||||
DateTime lastRtc_{};
|
DateTime lastRtc_{};
|
||||||
int64_t lastEpoch_ = 0;
|
int64_t lastEpoch_ = 0;
|
||||||
mutable char lastError_[128] = {};
|
mutable char lastError_[128] = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace tbeam
|
} // namespace tbeam
|
||||||
|
|
|
||||||
|
|
@ -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;
|
{
|
||||||
storage_ = storage;
|
serial_ = &serial;
|
||||||
config_ = config;
|
storage_ = storage;
|
||||||
lastFlushMs_ = millis();
|
config_ = config;
|
||||||
return true;
|
lastFlushMs_ = millis();
|
||||||
}
|
return true;
|
||||||
|
|
||||||
void TBeamLogger::update() {
|
|
||||||
if (!config_.autoFlush || !storage_) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const uint32_t now = millis();
|
|
||||||
if ((uint32_t)(now - lastFlushMs_) >= config_.flushIntervalMs) {
|
void TBeamLogger::update()
|
||||||
storage_->flush();
|
{
|
||||||
lastFlushMs_ = now;
|
if (!config_.autoFlush || !storage_)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const uint32_t now = millis();
|
||||||
|
if ((uint32_t)(now - lastFlushMs_) >= config_.flushIntervalMs)
|
||||||
|
{
|
||||||
|
storage_->flush();
|
||||||
|
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) {
|
|
||||||
if (!storage_) {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
char path[128];
|
|
||||||
if (!storage_->makeUniqueLogPath(prefix, extension, path, sizeof(path))) {
|
bool TBeamLogger::openUniqueLog(const char *prefix, const char *extension)
|
||||||
return false;
|
{
|
||||||
|
if (!storage_)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char path[128];
|
||||||
|
if (!storage_->makeUniqueLogPath(prefix, extension, path, sizeof(path)))
|
||||||
|
{
|
||||||
|
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 {
|
|
||||||
return storage_ && storage_->ready() && storage_->isLogOpen();
|
|
||||||
}
|
|
||||||
|
|
||||||
void TBeamLogger::flush() {
|
|
||||||
if (storage_) {
|
|
||||||
storage_->flush();
|
|
||||||
}
|
}
|
||||||
if (serial_) {
|
|
||||||
serial_->flush();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void TBeamLogger::closeLog() {
|
bool TBeamLogger::storageReady() const
|
||||||
if (storage_) {
|
{
|
||||||
storage_->closeLog();
|
return storage_ && storage_->ready() && storage_->isLogOpen();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
size_t TBeamLogger::write(uint8_t value) {
|
void TBeamLogger::flush()
|
||||||
return write(&value, 1);
|
{
|
||||||
}
|
if (storage_)
|
||||||
|
{
|
||||||
size_t TBeamLogger::write(const uint8_t* buffer, size_t size) {
|
storage_->flush();
|
||||||
size_t serialWrote = 0;
|
}
|
||||||
size_t storageWrote = 0;
|
if (serial_)
|
||||||
if (config_.echoSerial && serial_) {
|
{
|
||||||
serialWrote = serial_->write(buffer, size);
|
serial_->flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (config_.echoStorage && storage_ && storage_->isLogOpen()) {
|
|
||||||
storageWrote = storage_->write(buffer, size);
|
|
||||||
}
|
|
||||||
return storageWrote > 0 ? storageWrote : serialWrote;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace tbeam
|
void TBeamLogger::closeLog()
|
||||||
|
{
|
||||||
|
if (storage_)
|
||||||
|
{
|
||||||
|
storage_->closeLog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TBeamLogger::write(uint8_t value)
|
||||||
|
{
|
||||||
|
return write(&value, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t TBeamLogger::write(const uint8_t *buffer, size_t size)
|
||||||
|
{
|
||||||
|
size_t serialWrote = 0;
|
||||||
|
size_t storageWrote = 0;
|
||||||
|
if (config_.echoSerial && serial_)
|
||||||
|
{
|
||||||
|
serialWrote = serial_->write(buffer, size);
|
||||||
|
}
|
||||||
|
if (config_.echoStorage && storage_ && storage_->isLogOpen())
|
||||||
|
{
|
||||||
|
storageWrote = storage_->write(buffer, size);
|
||||||
|
}
|
||||||
|
return storageWrote > 0 ? storageWrote : serialWrote;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace tbeam
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue