Files
DD3-LoRa-Bridge-MultiSender/src/sd_logger.cpp

129 lines
2.9 KiB
C++

#include "sd_logger.h"
#include "config.h"
#include <SD.h>
#include <SPI.h>
#include <time.h>
static bool g_sd_ready = false;
static SPIClass *g_sd_spi = nullptr;
static const char *fault_text(FaultType fault) {
switch (fault) {
case FaultType::MeterRead:
return "meter";
case FaultType::Decode:
return "decode";
case FaultType::LoraTx:
return "loratx";
default:
return "";
}
}
static bool ensure_dir(const String &path) {
if (SD.exists(path)) {
return true;
}
return SD.mkdir(path);
}
static String format_date_utc(uint32_t ts_utc) {
time_t t = static_cast<time_t>(ts_utc);
struct tm tm_utc;
gmtime_r(&t, &tm_utc);
char buf[16];
snprintf(buf, sizeof(buf), "%04d-%02d-%02d",
tm_utc.tm_year + 1900,
tm_utc.tm_mon + 1,
tm_utc.tm_mday);
return String(buf);
}
void sd_logger_init() {
if (!ENABLE_SD_LOGGING) {
g_sd_ready = false;
return;
}
if (!g_sd_spi) {
g_sd_spi = new SPIClass(HSPI);
}
g_sd_spi->begin(PIN_SD_SCK, PIN_SD_MISO, PIN_SD_MOSI, PIN_SD_CS);
g_sd_ready = SD.begin(PIN_SD_CS, *g_sd_spi);
if (SERIAL_DEBUG_MODE) {
if (g_sd_ready) {
uint8_t type = SD.cardType();
uint64_t size = SD.cardSize();
Serial.printf("sd: ok type=%u size=%llu\n", static_cast<unsigned>(type), static_cast<unsigned long long>(size));
} else {
Serial.println("sd: init failed");
}
}
}
bool sd_logger_is_ready() {
return g_sd_ready;
}
void sd_logger_log_sample(const MeterData &data, bool include_error_text) {
if (!g_sd_ready || data.ts_utc == 0) {
return;
}
String root_dir = "/dd3";
if (!ensure_dir(root_dir)) {
return;
}
String sender_dir = root_dir + "/" + String(data.device_id);
if (!ensure_dir(sender_dir)) {
return;
}
String filename = sender_dir + "/" + format_date_utc(data.ts_utc) + ".csv";
bool new_file = !SD.exists(filename);
File f = SD.open(filename, FILE_APPEND);
if (!f) {
return;
}
if (new_file) {
f.println("ts_utc,p_w,p1_w,p2_w,p3_w,e_kwh,bat_v,bat_pct,rssi,snr,err_m,err_d,err_tx,err_last");
}
f.print(data.ts_utc);
f.print(',');
f.print(data.total_power_w, 1);
f.print(',');
f.print(data.phase_power_w[0], 1);
f.print(',');
f.print(data.phase_power_w[1], 1);
f.print(',');
f.print(data.phase_power_w[2], 1);
f.print(',');
f.print(data.energy_total_kwh, 3);
f.print(',');
f.print(data.battery_voltage_v, 2);
f.print(',');
f.print(data.battery_percent);
f.print(',');
f.print(data.link_rssi_dbm);
f.print(',');
if (isnan(data.link_snr_db)) {
f.print("");
} else {
f.print(data.link_snr_db, 1);
}
f.print(',');
f.print(data.err_meter_read);
f.print(',');
f.print(data.err_decode);
f.print(',');
f.print(data.err_lora_tx);
f.print(',');
if (include_error_text && data.last_error != FaultType::None) {
f.print(fault_text(data.last_error));
}
f.println();
f.close();
}