CSV file save on SPIFF filesystem, 10MB on internal flash
This commit is contained in:
60
RotaxMonitor/src/datasave.cpp
Normal file
60
RotaxMonitor/src/datasave.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include "datasave.h"
|
||||
|
||||
void save_history(const PSRAMVector<ignitionBoxStatus> &history)
|
||||
{
|
||||
// Initialize SPIFFS
|
||||
if (!SPIFFS.begin(true))
|
||||
{
|
||||
LOG_ERROR("Failed to mount SPIFFS");
|
||||
LOG_ERROR("5 seconds to restart...");
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
esp_restart();
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_INFO("SPIFFS mounted successfully");
|
||||
return;
|
||||
}
|
||||
|
||||
std::ofstream ofs("/spiffs/ignA_history.csv", std::ios::out | std::ios::trunc);
|
||||
if (ofs.fail())
|
||||
{
|
||||
LOG_ERROR("Failed to open file for writing");
|
||||
return;
|
||||
}
|
||||
|
||||
//write csv header
|
||||
ofs << "TS,\
|
||||
EVENTS_12,DLY_12,STAT_12,V_12_1,V_12_2,V_12_3,V_12_4,IGNITION_MODE_12,\
|
||||
EVENTS_34,DLY_34,STAT_34,V_34_1,V_34_2,V_34_3,V_34_4,IGNITION_MODE_34,\
|
||||
ENGINE_RPM,ADC_READTIME,N_QUEUE_ERRORS";
|
||||
for (auto &entry : history)
|
||||
{
|
||||
ofs << std::to_string(entry.timestamp) << ","
|
||||
<< std::to_string(entry.coils12.n_events) << ","
|
||||
<< std::to_string(entry.coils12.spark_delay) << ","
|
||||
<< std::string(sparkStatusNames.at(entry.coils12.spark_status)) << ","
|
||||
<< std::to_string(entry.coils12.peak_p_in) << ","
|
||||
<< std::to_string(entry.coils12.peak_n_in) << ","
|
||||
<< std::to_string(entry.coils12.peak_p_out) << ","
|
||||
<< std::to_string(entry.coils12.peak_n_out) << ","
|
||||
<< std::string(softStartStatusNames.at(entry.coils12.sstart_status)) << ","
|
||||
<< std::to_string(entry.coils34.n_events) << ","
|
||||
<< std::to_string(entry.coils34.spark_delay) << ","
|
||||
<< std::string(sparkStatusNames.at(entry.coils34.spark_status)) << ","
|
||||
<< std::to_string(entry.coils34.peak_p_in) << ","
|
||||
<< std::to_string(entry.coils34.peak_n_in) << ","
|
||||
<< std::to_string(entry.coils34.peak_p_out) << ","
|
||||
<< std::to_string(entry.coils34.peak_n_out) << ","
|
||||
<< std::string(softStartStatusNames.at(entry.coils34.sstart_status)) << ","
|
||||
<< std::to_string(entry.eng_rpm) << ","
|
||||
<< std::to_string(entry.adc_read_time) << ","
|
||||
<< std::to_string(entry.n_queue_errors);
|
||||
ofs << std::endl;
|
||||
}
|
||||
auto written_bytes = ofs.tellp();
|
||||
ofs.flush();
|
||||
ofs.close();
|
||||
LOG_INFO("Ignition A history saved to SPIFFS, bytes written: ", (uint32_t)written_bytes);
|
||||
SPIFFS.end(); // unmount SPIFFS to ensure data is written and avoid corruption on next mount
|
||||
}
|
||||
24
RotaxMonitor/src/datasave.h
Normal file
24
RotaxMonitor/src/datasave.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
// System Includes
|
||||
#include <Arduino.h>
|
||||
#include <DebugLog.h>
|
||||
#include <SPIFFS.h>
|
||||
#include <string>
|
||||
#include <fstream>
|
||||
|
||||
// Project Includes
|
||||
#include "isr.h"
|
||||
#include "psvector.h"
|
||||
|
||||
const bool SAVE_HISTORY_TO_SPIFFS = true; // Set to true to enable saving history to SPIFFS, false to disable
|
||||
|
||||
static void saveHistoryTask(void *pvParameters)
|
||||
{
|
||||
auto *history = static_cast<PSRAMVector<ignitionBoxStatus> *>(pvParameters);
|
||||
save_history(*history);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void save_history(const PSRAMVector<ignitionBoxStatus> &history);
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
|
||||
#define CORE_0 0
|
||||
#define CORE_1 1
|
||||
#define TASK_STACK 4096 // in words
|
||||
#define TASK_PRIORITY (configMAX_PRIORITIES - 4) // highest priority after wifi tasks
|
||||
#define RT_TASK_STACK 4096 // in words
|
||||
#define RT_TASK_PRIORITY (configMAX_PRIORITIES - 4) // highest priority after wifi tasks
|
||||
|
||||
// =====================
|
||||
// Event Flags (bitmask)
|
||||
|
||||
@@ -9,6 +9,8 @@
|
||||
// Definitions
|
||||
#include <tasks.h>
|
||||
#include <devices.h>
|
||||
#include <psvector.h>
|
||||
#include <datasave.h>
|
||||
#include <ui.h>
|
||||
|
||||
// FreeRTOS directives
|
||||
@@ -18,13 +20,6 @@
|
||||
// #define CH_B_ENABLE
|
||||
#define TEST
|
||||
|
||||
void printTaskStats()
|
||||
{
|
||||
char buffer[1024];
|
||||
vTaskGetRunTimeStats(buffer);
|
||||
Serial.println(buffer);
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(921600);
|
||||
@@ -56,14 +51,22 @@ void loop()
|
||||
{
|
||||
// global variables
|
||||
bool running = true;
|
||||
static Devices dev;
|
||||
const uint32_t max_history = 1024;
|
||||
const uint32_t max_queue = 128;
|
||||
|
||||
PSRAMVector<ignitionBoxStatus> ignA_history_0(max_history);
|
||||
PSRAMVector<ignitionBoxStatus> ignA_history_1(max_history);
|
||||
auto &active_history = ignA_history_0;
|
||||
auto &writable_history = ignA_history_1;
|
||||
|
||||
// Resources Initialization
|
||||
static Devices dev;
|
||||
// Task handle
|
||||
static TaskHandle_t trigA_TaskHandle = NULL;
|
||||
static TaskHandle_t trigB_TaskHandle = NULL;
|
||||
|
||||
static QueueHandle_t rt_taskA_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
|
||||
static QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
|
||||
// Data Queue for real time task to main loop communication
|
||||
static QueueHandle_t rt_taskA_queue = xQueueCreate(max_queue, sizeof(ignitionBoxStatus));
|
||||
static QueueHandle_t rt_taskB_queue = xQueueCreate(max_queue, sizeof(ignitionBoxStatus));
|
||||
static rtTaskParams taskA_params{
|
||||
.rt_running = true,
|
||||
.dev = &dev,
|
||||
@@ -82,7 +85,7 @@ void loop()
|
||||
LOG_INFO("Task Variables OK");
|
||||
|
||||
#ifdef CH_B_ENABLE
|
||||
QueueHandle_t rt_taskB_queue = xQueueCreate(10, sizeof(ignitionBoxStatus));
|
||||
QueueHandle_t rt_taskB_queue = xQueueCreate(max_queue, sizeof(ignitionBoxStatus));
|
||||
rtTaskParams taskB_params{
|
||||
.rt_running = true,
|
||||
.dev = &dev,
|
||||
@@ -99,18 +102,19 @@ void loop()
|
||||
.rt_resets = rtTaskResets{.rst_io_12p = RST_EXT_B12P, .rst_io_12n = RST_EXT_B12N, .rst_io_34p = RST_EXT_B34P, .rst_io_34n = RST_EXT_B34N}};
|
||||
#endif
|
||||
|
||||
|
||||
// Spi ok flags
|
||||
bool spiA_ok = true;
|
||||
bool spiB_ok = true;
|
||||
|
||||
// Init 2 SPI interfaces
|
||||
SPIClass SPI_A(FSPI);
|
||||
spiA_ok = SPI_A.begin(SPI_A_SCK, SPI_A_MISO, SPI_A_MOSI);
|
||||
SPI_A.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1
|
||||
#ifndef TEST
|
||||
#ifndef TEST
|
||||
SPIClass SPI_B(HSPI);
|
||||
spiB_ok = SPI_B.begin(SPI_B_SCK, SPI_B_MISO, SPI_B_MOSI);
|
||||
SPI_B.setDataMode(SPI_MODE1); // ADS1256 requires SPI mode 1
|
||||
#endif
|
||||
#endif
|
||||
if (!spiA_ok || !spiB_ok)
|
||||
{
|
||||
LOG_ERROR("Unable to Initialize SPI Busses");
|
||||
@@ -141,9 +145,9 @@ void loop()
|
||||
ignA_task_success = xTaskCreatePinnedToCore(
|
||||
rtIgnitionTask,
|
||||
"rtIgnitionTask_boxA",
|
||||
TASK_STACK,
|
||||
RT_TASK_STACK,
|
||||
(void *)&taskA_params,
|
||||
TASK_PRIORITY,
|
||||
RT_TASK_PRIORITY,
|
||||
&trigA_TaskHandle,
|
||||
CORE_0);
|
||||
|
||||
@@ -153,16 +157,16 @@ void loop()
|
||||
ignB_task_success = xTaskCreatePinnedToCore(
|
||||
rtIgnitionTask,
|
||||
"rtIgnitionTask_boxB",
|
||||
TASK_STACK,
|
||||
RT_TASK_STACK,
|
||||
(void *)&taskB_params,
|
||||
TASK_PRIORITY, // priorità leggermente più alta
|
||||
RT_TASK_PRIORITY, // priorità leggermente più alta
|
||||
&trigB_TaskHandle,
|
||||
CORE_1);
|
||||
#endif
|
||||
|
||||
if ((ignA_task_success && ignB_task_success) != pdPASS)
|
||||
{
|
||||
LOG_ERROR("Unble to initialize ISR task");
|
||||
LOG_ERROR("Una ble to initialize ISR task");
|
||||
LOG_ERROR("5 seconds to restart...");
|
||||
vTaskDelay(pdMS_TO_TICKS(5000));
|
||||
esp_restart();
|
||||
@@ -173,14 +177,28 @@ void loop()
|
||||
////////////////////// MAIN LOOP //////////////////////
|
||||
clearScreen();
|
||||
setCursor(0, 0);
|
||||
uint32_t counter = 0;
|
||||
ignitionBoxStatus ignA;
|
||||
int64_t last = esp_timer_get_time();
|
||||
uint32_t missed_firings12 = 0;
|
||||
uint32_t missed_firings34 = 0;
|
||||
uint32_t counter = 0;
|
||||
|
||||
while (running)
|
||||
{
|
||||
if (counter == active_history.size())
|
||||
{
|
||||
counter = 0;
|
||||
std::swap(active_history, writable_history); // switch active and writable buffers
|
||||
if (SAVE_HISTORY_TO_SPIFFS)
|
||||
xTaskCreate(
|
||||
saveHistoryTask,
|
||||
"saveHistoryTask",
|
||||
RT_TASK_STACK / 2,
|
||||
&writable_history,
|
||||
RT_TASK_PRIORITY + 1, // higher priority to ensure it runs asap after buffer switch
|
||||
NULL);
|
||||
}
|
||||
|
||||
if (xQueueReceive(rt_taskA_queue, &ignA, pdMS_TO_TICKS(1000)) == pdTRUE)
|
||||
{
|
||||
if (ignA.coils12.spark_status == sparkStatus::SPARK_POS_FAIL || ignA.coils12.spark_status == sparkStatus::SPARK_NEG_FAIL)
|
||||
@@ -218,6 +236,8 @@ void loop()
|
||||
printField("Engine RPM", ignA.eng_rpm);
|
||||
printField("ADC Read Time", ignA.adc_read_time);
|
||||
printField("Queue Errors", ignA.n_queue_errors);
|
||||
|
||||
active_history[counter++ % active_history.size()] = ignA;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
30
RotaxMonitor/src/psvector.h
Normal file
30
RotaxMonitor/src/psvector.h
Normal file
@@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
// Allocator custom per PSRAM
|
||||
template <typename T>
|
||||
struct PSRAMAllocator {
|
||||
using value_type = T;
|
||||
|
||||
PSRAMAllocator() noexcept {}
|
||||
|
||||
template <typename U>
|
||||
PSRAMAllocator(const PSRAMAllocator<U>&) noexcept {}
|
||||
|
||||
T* allocate(std::size_t n) {
|
||||
void* ptr = heap_caps_malloc(n * sizeof(T), MALLOC_CAP_SPIRAM);
|
||||
if (!ptr) {
|
||||
throw std::bad_alloc();
|
||||
}
|
||||
return static_cast<T*>(ptr);
|
||||
}
|
||||
|
||||
void deallocate(T* p, std::size_t) noexcept {
|
||||
heap_caps_free(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
using PSRAMVector = std::vector<T, PSRAMAllocator<T>>;
|
||||
@@ -4,3 +4,10 @@
|
||||
#include <string>
|
||||
|
||||
std::string printBits(uint32_t value);
|
||||
|
||||
static void saveHistoryTask(void *pvParameters)
|
||||
{
|
||||
auto *history = static_cast<PSRAMVector<ignitionBoxStatus> *>(pvParameters);
|
||||
save_history(*history);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
Reference in New Issue
Block a user