Upload files to "/"
This commit is contained in:
342
sd_logger.cpp
Normal file
342
sd_logger.cpp
Normal file
@@ -0,0 +1,342 @@
|
||||
// sd_logger.cpp - SD Card Logger Implementation with Batch Writing
|
||||
|
||||
#include <Arduino.h>
|
||||
#include "sd_logger.h"
|
||||
#include "task_config.h"
|
||||
#include "can_handler.h"
|
||||
#include "psram_buffer.h"
|
||||
|
||||
extern volatile uint32_t canErrorCount;
|
||||
|
||||
bool sdInitialized = false;
|
||||
uint64_t sdCardSize = 0;
|
||||
uint64_t sdFreeSpace = 0;
|
||||
|
||||
File currentLogFile;
|
||||
char currentLogFileName[64] = {0};
|
||||
uint32_t currentLogFileSize = 0;
|
||||
|
||||
static uint32_t fileSequenceNumber = 0;
|
||||
|
||||
static uint8_t* writeBuffer = nullptr;
|
||||
static size_t bufferOffset = 0;
|
||||
static uint32_t lastFlushTime = 0;
|
||||
static uint32_t framesInBuffer = 0;
|
||||
|
||||
bool initSDCard() {
|
||||
Serial.println("Initializing SD Card (SDIO 4-bit mode)...");
|
||||
|
||||
writeBuffer = getSDWriteBuffer();
|
||||
if (writeBuffer == nullptr) {
|
||||
if (psramFound()) {
|
||||
writeBuffer = (uint8_t*)ps_malloc(PSRAM_SD_BUFFER_SIZE);
|
||||
} else {
|
||||
writeBuffer = (uint8_t*)malloc(PSRAM_SD_BUFFER_SIZE);
|
||||
}
|
||||
if (writeBuffer == nullptr) {
|
||||
Serial.println("Failed to allocate SD write buffer!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
SD_MMC.setPins(SDIO_CLK_PIN, SDIO_CMD_PIN, SDIO_D0_PIN,
|
||||
SDIO_D1_PIN, SDIO_D2_PIN, SDIO_D3_PIN);
|
||||
|
||||
if (!SD_MMC.begin("/sdcard", false)) {
|
||||
Serial.println("SD Card mount failed!");
|
||||
sdInitialized = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
sdCardSize = SD_MMC.cardSize();
|
||||
sdFreeSpace = SD_MMC.totalBytes() - SD_MMC.usedBytes();
|
||||
|
||||
Serial.println("SD Card mounted successfully!");
|
||||
Serial.printf(" Card Size: %llu MB\n", sdCardSize / (1024 * 1024));
|
||||
Serial.printf(" Free Space: %llu MB\n", sdFreeSpace / (1024 * 1024));
|
||||
|
||||
sdInitialized = true;
|
||||
|
||||
createLogDirectories();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool createLogDirectories() {
|
||||
if (!sdInitialized) return false;
|
||||
|
||||
// Create logs directory
|
||||
if (!SD_MMC.exists(LOGS_DIR)) {
|
||||
if (SD_MMC.mkdir(LOGS_DIR)) {
|
||||
Serial.println("Created /logs directory");
|
||||
} else {
|
||||
Serial.println("Failed to create /logs directory");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Create config directory
|
||||
if (!SD_MMC.exists(CONFIG_DIR)) {
|
||||
if (SD_MMC.mkdir(CONFIG_DIR)) {
|
||||
Serial.println("Created /config directory");
|
||||
} else {
|
||||
Serial.println("Failed to create /config directory");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool startLogFile() {
|
||||
if (!sdInitialized) return false;
|
||||
|
||||
// Close current file if open
|
||||
closeLogFile();
|
||||
|
||||
// Generate filename with timestamp
|
||||
time_t now = time(NULL);
|
||||
struct tm* timeinfo = localtime(&now);
|
||||
|
||||
snprintf(currentLogFileName, sizeof(currentLogFileName),
|
||||
"%s/CAN_%04d%02d%02d_%02d%02d%02d_%03d.pcap",
|
||||
LOGS_DIR,
|
||||
timeinfo->tm_year + 1900,
|
||||
timeinfo->tm_mon + 1,
|
||||
timeinfo->tm_mday,
|
||||
timeinfo->tm_hour,
|
||||
timeinfo->tm_min,
|
||||
timeinfo->tm_sec,
|
||||
fileSequenceNumber++);
|
||||
|
||||
// Open file for writing
|
||||
currentLogFile = SD_MMC.open(currentLogFileName, FILE_WRITE);
|
||||
if (!currentLogFile) {
|
||||
Serial.printf("Failed to open log file: %s\n", currentLogFileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write PCAP global header
|
||||
PCAPGlobalHeader header;
|
||||
header.magicNumber = PCAP_MAGIC_NUMBER;
|
||||
header.versionMajor = 2;
|
||||
header.versionMinor = 4;
|
||||
header.thiszone = 0;
|
||||
header.sigfigs = 0;
|
||||
header.snaplen = 65535;
|
||||
header.network = PCAP_LINK_TYPE;
|
||||
|
||||
currentLogFile.write((uint8_t*)&header, sizeof(header));
|
||||
currentLogFileSize = sizeof(header);
|
||||
|
||||
Serial.printf("Started new log file: %s\n", currentLogFileName);
|
||||
return true;
|
||||
}
|
||||
|
||||
void closeLogFile() {
|
||||
flushWriteBuffer();
|
||||
if (currentLogFile) {
|
||||
currentLogFile.close();
|
||||
Serial.printf("Closed log file: %s\n", currentLogFileName);
|
||||
currentLogFileName[0] = '\0';
|
||||
currentLogFileSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool writeCANFrameToSD(const CanFrame& frame) {
|
||||
if (!sdInitialized || !currentLogFile) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (currentLogFileSize >= FILE_SPLIT_SIZE) {
|
||||
flushWriteBuffer();
|
||||
closeLogFile();
|
||||
if (!startLogFile()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
PCAPPacketHeader pktHeader;
|
||||
pktHeader.tsSec = (uint32_t)(frame.timestamp / 1000000ULL);
|
||||
pktHeader.tsUsec = (uint32_t)(frame.timestamp % 1000000ULL);
|
||||
|
||||
CANFDFrame canFrame;
|
||||
canFrame.canId = frame.id;
|
||||
canFrame.len = frame.len;
|
||||
canFrame.flags = frame.flags;
|
||||
canFrame.reserved0 = 0;
|
||||
canFrame.reserved1 = 0;
|
||||
memcpy(canFrame.data, frame.data, frame.len);
|
||||
|
||||
uint32_t frameSize = sizeof(CANFDFrame) - 64 + frame.len;
|
||||
pktHeader.inclLen = frameSize;
|
||||
pktHeader.origLen = frameSize;
|
||||
|
||||
size_t totalSize = sizeof(pktHeader) + frameSize;
|
||||
|
||||
if (bufferOffset + totalSize > PSRAM_SD_BUFFER_SIZE) {
|
||||
if (!flushWriteBuffer()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(100)) == pdTRUE) {
|
||||
memcpy(writeBuffer + bufferOffset, &pktHeader, sizeof(pktHeader));
|
||||
bufferOffset += sizeof(pktHeader);
|
||||
memcpy(writeBuffer + bufferOffset, &canFrame, frameSize);
|
||||
bufferOffset += frameSize;
|
||||
framesInBuffer++;
|
||||
currentLogFileSize += totalSize;
|
||||
|
||||
xSemaphoreGive(sdMutex);
|
||||
|
||||
uint32_t now = millis();
|
||||
if (now - lastFlushTime >= SD_FLUSH_INTERVAL_MS) {
|
||||
flushWriteBuffer();
|
||||
lastFlushTime = now;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool flushWriteBuffer() {
|
||||
if (bufferOffset == 0 || !currentLogFile) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(sdMutex, pdMS_TO_TICKS(500)) == pdTRUE) {
|
||||
size_t written = currentLogFile.write(writeBuffer, bufferOffset);
|
||||
currentLogFile.flush();
|
||||
|
||||
xSemaphoreGive(sdMutex);
|
||||
|
||||
if (written != bufferOffset) {
|
||||
Serial.printf("SD write incomplete: %d of %d bytes\n", written, bufferOffset);
|
||||
bufferOffset = 0;
|
||||
framesInBuffer = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
bufferOffset = 0;
|
||||
framesInBuffer = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void sdWriteTask(void *pvParameters) {
|
||||
Serial.println("SD Write Task started on Core 1");
|
||||
|
||||
CanFrame frame;
|
||||
uint32_t lastStatusTime = 0;
|
||||
uint32_t framesWritten = 0;
|
||||
uint32_t framesFromPSRAM = 0;
|
||||
|
||||
while (1) {
|
||||
bool gotFrame = false;
|
||||
|
||||
if (xQueueReceive(canRxQueue, &frame, pdMS_TO_TICKS(10)) == pdTRUE) {
|
||||
gotFrame = true;
|
||||
} else if (canFrameBuffer.available() > 0) {
|
||||
if (canFrameBuffer.pop(frame)) {
|
||||
gotFrame = true;
|
||||
framesFromPSRAM++;
|
||||
}
|
||||
}
|
||||
|
||||
if (gotFrame) {
|
||||
if (!writeCANFrameToSD(frame)) {
|
||||
canErrorCount++;
|
||||
} else {
|
||||
framesWritten++;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t now = millis();
|
||||
if (now - lastStatusTime > 10000) {
|
||||
Serial.printf("[SD] Written: %d, From PSRAM: %d, Buffer: %d, Q: %d, PSRAM: %d\n",
|
||||
framesWritten, framesFromPSRAM, bufferOffset,
|
||||
uxQueueMessagesWaiting(canRxQueue),
|
||||
canFrameBuffer.available());
|
||||
lastStatusTime = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t getSDCardSize() {
|
||||
return sdInitialized ? sdCardSize : 0;
|
||||
}
|
||||
|
||||
uint64_t getFreeSpace() {
|
||||
if (!sdInitialized) return 0;
|
||||
return SD_MMC.totalBytes() - SD_MMC.usedBytes();
|
||||
}
|
||||
|
||||
uint64_t getUsedSpace() {
|
||||
if (!sdInitialized) return 0;
|
||||
return SD_MMC.usedBytes();
|
||||
}
|
||||
|
||||
int listLogFiles(char* buffer, size_t bufferSize) {
|
||||
if (!sdInitialized) return 0;
|
||||
|
||||
File root = SD_MMC.open(LOGS_DIR);
|
||||
if (!root || !root.isDirectory()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
size_t offset = 0;
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file && offset < bufferSize - 128) {
|
||||
if (!file.isDirectory() && strstr(file.name(), ".pcap")) {
|
||||
int len = snprintf(buffer + offset, bufferSize - offset,
|
||||
"{\"name\":\"%s\",\"size\":%d},",
|
||||
file.name(), file.size());
|
||||
offset += len;
|
||||
count++;
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
|
||||
// Remove trailing comma
|
||||
if (offset > 0 && buffer[offset - 1] == ',') {
|
||||
buffer[offset - 1] = '\0';
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
bool getLogFileInfo(const char* filename, uint32_t& size, uint32_t& timestamp) {
|
||||
if (!sdInitialized) return false;
|
||||
|
||||
char path[128];
|
||||
snprintf(path, sizeof(path), "%s/%s", LOGS_DIR, filename);
|
||||
|
||||
File file = SD_MMC.open(path);
|
||||
if (!file) return false;
|
||||
|
||||
size = file.size();
|
||||
timestamp = file.getLastWrite();
|
||||
file.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool deleteLogFile(const char* filename) {
|
||||
if (!sdInitialized) return false;
|
||||
|
||||
char path[128];
|
||||
snprintf(path, sizeof(path), "%s/%s", LOGS_DIR, filename);
|
||||
|
||||
return SD_MMC.remove(path);
|
||||
}
|
||||
|
||||
const char* getCurrentLogFilename() {
|
||||
return currentLogFileName;
|
||||
}
|
||||
Reference in New Issue
Block a user