Files
SC-F001/main/filemgmt.c

329 lines
8.8 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* filemgmt.c
*
* Created on: Nov 20, 2025
* Author: Thad
*/
#include "filemgmt.h"
#include "endian.h"
#include "esp_log.h"
#include <stdio.h>
#include <inttypes.h>
#include <string.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_mac.h" // for MACSTR
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_err.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "nvs_flash.h"
#include "esp_vfs_fat.h"
#include "esp_littlefs.h"
#include "ftp.h"
#include "control_fsm.h"
#include "power_mgmt.h"
#include "rtc.h"
#include "sensors.h"
const char* partition_label = "storage";
const char *TAG = "MAIN";
const char *mount_point = "/root";
RTC_DATA_ATTR bool find_new_filename = true;
void start_new_log_file() {
find_new_filename = true;
}
EventGroupHandle_t xEventTask;
int FTP_TASK_FINISH_BIT = BIT2;
esp_err_t start_filesystem() {
ESP_LOGI(TAG, "Initializing LittleFS on Builtin SPI Flash Memory");
esp_vfs_littlefs_conf_t conf = {
.base_path = mount_point,
.partition_label = partition_label,
.format_if_mount_failed = true,
.dont_mount = false,
};
// Use settings defined above to initialize and mount LittleFS filesystem.
// Note: esp_vfs_littlefs_register is an all-in-one convenience function.
esp_err_t ret = esp_vfs_littlefs_register(&conf);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount or format filesystem");
} else if (ret == ESP_ERR_NOT_FOUND) {
ESP_LOGE(TAG, "Failed to find LittleFS partition");
} else {
ESP_LOGE(TAG, "Failed to initialize LittleFS (%s)", esp_err_to_name(ret));
}
return ret;
}
size_t total = 0, used = 0;
ret = esp_littlefs_info(conf.partition_label, &total, &used);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to get LittleFS partition information (%s)", esp_err_to_name(ret));
//esp_littlefs_format(conf.partition_label);
return ret;
}
ESP_LOGI(TAG, "Partition size: total: %d, used: %d", total, used);
ESP_LOGI(TAG, "Mount LittleFS on %s", mount_point);
return ret;
}
static void wifi_event_handler(void* arg, esp_event_base_t event_base, int32_t event_id, void* event_data)
{
if (event_id == WIFI_EVENT_AP_STACONNECTED) {
wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" join, AID=%d", MAC2STR(event->mac), event->aid);
} else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d", MAC2STR(event->mac), event->aid);
}
}
extern void ftp_task (void *pvParameters);
// Start WiFi AP and FTP Server
// filemgmt.c
esp_err_t start_ftp_server(void)
{
ESP_LOGI("FTP", "START");
close_current_log();
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
// THIS IS THE CORRECT WAY recreate the default AP netif every time
esp_netif_create_default_wifi_ap();
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
wifi_config_t wifi_config = {
.ap = {
.ssid = ESP_WIFI_AP_SSID,
.ssid_len = strlen(ESP_WIFI_AP_SSID),
.password = ESP_WIFI_AP_PASSWORD,
.max_connection = ESP_WIFI_AP_N_CONNECTIONS,
.authmode = strlen(ESP_WIFI_AP_PASSWORD) ? WIFI_AUTH_WPA_WPA2_PSK : WIFI_AUTH_OPEN
},
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
ESP_ERROR_CHECK(esp_wifi_start());
ESP_LOGI(TAG, "WiFi AP started IP 192.168.4.1");
xEventTask = xEventGroupCreate();
xTaskCreate(ftp_task, "FTP", 1024*6, NULL, 2, NULL);
return ESP_OK;
}
void stop_ftp_server(void)
{
ESP_LOGI("FTP", "OFF");
ftp_terminate();
vTaskDelay(pdMS_TO_TICKS(500)); // give task time to die
esp_wifi_stop();
esp_wifi_deinit();
// This single call does everything correctly:
// Destroy default Wi-Fi interface (handles AP)
esp_netif_t *ap_netif = esp_netif_get_handle_from_ifkey("WIFI_AP_DEF");
if (ap_netif) {
esp_netif_destroy_default_wifi(ap_netif); // Correct API for AP
}
esp_event_loop_delete_default();
esp_netif_deinit();
ESP_LOGI("FTP", "WiFi + FTP completely stopped safe to restart");
}
void stop_filesystem() {
esp_vfs_littlefs_unregister(partition_label);
ESP_LOGI(TAG, "LittleFS unmounted");
}
#define LOG_ENTRY_SIZE 32
#define MAX_LOG_FILES 999 // rotate after N files
#define ENTRIES_PER_FILE 8000 // ~256 KB per file → safe
static FILE* logfile = NULL;
RTC_DATA_ATTR char current_log_path[32] = "/root/log000.bin";
static uint32_t current_entry_count = 0;
// Helper: close current file safely
void close_current_log(void)
{
if (logfile) {
fflush(logfile);
fsync(fileno(logfile)); // CRITICAL: forces write to flash
fclose(logfile);
logfile = NULL;
current_entry_count = 0;
}
}
// Helper: open next log file (rotates 000→009)
bool open_next_log_file(void)
{
close_current_log();
if (find_new_filename) {
// Find next filename
for (int i = 0; i < MAX_LOG_FILES; i++) {
snprintf(current_log_path, sizeof(current_log_path), "/root/log%03d.bin", i);
struct stat st;
if (stat(current_log_path, &st) != 0) {
break; // file doesn't exist → use this one
}
if (i == MAX_LOG_FILES-1) {
ESP_LOGE("FILE", "RAN OUT OF FILES, GOING TO MAX");
// All files exist → overwrite oldest (log000.bin)
strcpy(current_log_path, "/root/log999.bin");
}
}
logfile = fopen(current_log_path, "ab"); // binary append
if (!logfile) {
ESP_LOGE("FILE", "Failed to open %s", current_log_path);
return false;
}
// Optional: truncate if too big (safety)
fseek(logfile, 0, SEEK_END);
if (ftell(logfile) > ENTRIES_PER_FILE * LOG_ENTRY_SIZE) {
fclose(logfile);
logfile = fopen(current_log_path, "wb"); // truncate
}
find_new_filename = false;
} else {
logfile = fopen(current_log_path, "ab");
}
ESP_LOGI("FILE", "Logging to %s", current_log_path);
return true;
}
void file_log() {
if (!logfile && !open_next_log_file()) {
return;
}
char entry[LOG_ENTRY_SIZE] = {0};
entry[0] = LOG_ENTRY_SIZE;
// pack 64-bit timestamp into bytes 1-8
uint64_t be_timestamp = htobe64(rtc_time_ms());
memcpy(&entry[1], &be_timestamp, 8);
// pack 32-bit voltages/currents into bytes 9-24
int32_t be_voltage = htobe32(get_battery_mV());
memcpy(&entry[9], &be_voltage, 4);
int32_t be_current1 = htobe32(get_bridge_mA(BRIDGE_DRIVE));
memcpy(&entry[13], &be_current1, 4);
int32_t be_current2 = htobe32(get_bridge_mA(BRIDGE_JACK));
memcpy(&entry[17], &be_current2, 4);
int32_t be_current3 = htobe32(get_bridge_mA(BRIDGE_AUX));
memcpy(&entry[21], &be_current3, 4);
int32_t be_counter = htobe32(get_sensor_counter(SENSOR_DRIVE));
memcpy(&entry[25], &be_counter, 4);
entry[29] = get_sensor(SENSOR_DRIVE);
entry[30] = get_sensor(SENSOR_JACK);
entry[31] = fsm_get_state();
ESP_LOGI("FILE", "LOGGING TO %s: 0x %02x %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x %02x %02x", current_log_path,
entry[0],
entry[1],
entry[2],
entry[3],
entry[4],
entry[5],
entry[6],
entry[7],
entry[8],
entry[9],
entry[10],
entry[11],
entry[12],
entry[13],
entry[14],
entry[15],
entry[16],
entry[17],
entry[18],
entry[19],
entry[20],
entry[21],
entry[22],
entry[23],
entry[24],
entry[25],
entry[26],
entry[27],
entry[28],
entry[29],
entry[30],
entry[31]);
/* SEND TO FILE */
size_t written = fwrite(entry, 1, LOG_ENTRY_SIZE, logfile);
if (written != LOG_ENTRY_SIZE) {
ESP_LOGE("FILE", "Partial write! Closing file.");
close_current_log();
return;
}
current_entry_count++;
// Periodic flush (every 50100 entries) + full sync every 500
if (current_entry_count % 100 == 0) {
fflush(logfile);
}
if (current_entry_count % 500 == 0) {
fsync(fileno(logfile));
}
// Rotate if getting large
if (current_entry_count >= ENTRIES_PER_FILE) {
ESP_LOGI("FILE", "Rotating log file");
open_next_log_file();
}
}