move files into firmware folder
Some checks failed
ESP-IDF Build / build (esp32, latest) (push) Failing after 19s
ESP-IDF Build / build (esp32, release-v5.4) (push) Failing after 15s
ESP-IDF Build / build (esp32, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32c3, latest) (push) Failing after 21s
ESP-IDF Build / build (esp32c3, release-v5.4) (push) Failing after 15s
ESP-IDF Build / build (esp32c3, release-v5.5) (push) Failing after 14s
ESP-IDF Build / build (esp32c5, latest) (push) Failing after 14s
ESP-IDF Build / build (esp32c5, release-v5.4) (push) Failing after 15s
ESP-IDF Build / build (esp32c5, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32c6, latest) (push) Failing after 18s
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Failing after 15s
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32h2, latest) (push) Failing after 15s
ESP-IDF Build / build (esp32h2, release-v5.4) (push) Failing after 14s
ESP-IDF Build / build (esp32h2, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32p4, latest) (push) Failing after 15s
ESP-IDF Build / build (esp32p4, release-v5.4) (push) Failing after 18s
ESP-IDF Build / build (esp32p4, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32s3, latest) (push) Failing after 14s
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Failing after 14s
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Failing after 15s
Some checks failed
ESP-IDF Build / build (esp32, latest) (push) Failing after 19s
ESP-IDF Build / build (esp32, release-v5.4) (push) Failing after 15s
ESP-IDF Build / build (esp32, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32c3, latest) (push) Failing after 21s
ESP-IDF Build / build (esp32c3, release-v5.4) (push) Failing after 15s
ESP-IDF Build / build (esp32c3, release-v5.5) (push) Failing after 14s
ESP-IDF Build / build (esp32c5, latest) (push) Failing after 14s
ESP-IDF Build / build (esp32c5, release-v5.4) (push) Failing after 15s
ESP-IDF Build / build (esp32c5, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32c6, latest) (push) Failing after 18s
ESP-IDF Build / build (esp32c6, release-v5.4) (push) Failing after 15s
ESP-IDF Build / build (esp32c6, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32h2, latest) (push) Failing after 15s
ESP-IDF Build / build (esp32h2, release-v5.4) (push) Failing after 14s
ESP-IDF Build / build (esp32h2, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32p4, latest) (push) Failing after 15s
ESP-IDF Build / build (esp32p4, release-v5.4) (push) Failing after 18s
ESP-IDF Build / build (esp32p4, release-v5.5) (push) Failing after 15s
ESP-IDF Build / build (esp32s3, latest) (push) Failing after 14s
ESP-IDF Build / build (esp32s3, release-v5.4) (push) Failing after 14s
ESP-IDF Build / build (esp32s3, release-v5.5) (push) Failing after 15s
Signed-off-by: Peter Siegmund <developer@mars3142.org>
This commit is contained in:
6
firmware/components/storage/CMakeLists.txt
Normal file
6
firmware/components/storage/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
idf_component_register(SRCS
|
||||
"storage.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES
|
||||
spiffs
|
||||
)
|
55
firmware/components/storage/include/storage.h
Normal file
55
firmware/components/storage/include/storage.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include <sys/types.h> // For ssize_t
|
||||
|
||||
/**
|
||||
* @brief Initializes the SPIFFS filesystem.
|
||||
* This function should be called once before any file operations.
|
||||
* @return ESP_OK on success, error code otherwise.
|
||||
*/
|
||||
esp_err_t storage_init(void);
|
||||
|
||||
/**
|
||||
* @brief Unregisters the SPIFFS filesystem.
|
||||
* This function should be called once when file operations are finished.
|
||||
*/
|
||||
void storage_uninit(void);
|
||||
|
||||
/**
|
||||
* @brief Reads a chunk of data from a file on SPIFFS.
|
||||
* This function maintains internal state (file pointer) to allow reading
|
||||
* the same file chunk by chunk across multiple calls.
|
||||
*
|
||||
* @param filename The path to the file to read (e.g., "/storage/my_file.txt").
|
||||
* Must be the same filename for consecutive calls to read the same file.
|
||||
* @param buffer Buffer to store the read data. Must be large enough for max_bytes.
|
||||
* @param max_bytes The maximum number of bytes to read in this call. Must be > 0.
|
||||
* @return The number of bytes read on success (>= 0).
|
||||
* Returns 0 when the end of the file is reached.
|
||||
* Returns a negative value on error:
|
||||
* -1: Invalid input parameters (filename, buffer is NULL, or max_bytes is 0).
|
||||
* -2: Failed to open the file (first call for this filename).
|
||||
* -3: Filename mismatch with the currently open file (subsequent call).
|
||||
* -4: Read error occurred.
|
||||
*/
|
||||
ssize_t storage_read(const char *filename, char *buffer, size_t max_bytes);
|
||||
|
||||
/**
|
||||
* @brief Reads a chunk of data from a file on SPIFFS, starting at a specific offset.
|
||||
* This function is stateless regarding an internally managed file pointer for sequential reads;
|
||||
* it opens, seeks, reads, and closes the file on each call.
|
||||
*
|
||||
* @param filename The path to the file to read (e.g., "/storage/my_file.txt").
|
||||
* @param buffer Buffer to store the read data. Must be large enough for nbytes.
|
||||
* @param offset The offset in the file to start reading from.
|
||||
* @param nbytes The maximum number of bytes to read in this call. Must be > 0.
|
||||
* @return The number of bytes read on success (>= 0).
|
||||
* Returns 0 when the end of the file is reached from the given offset.
|
||||
* Returns a negative value on error:
|
||||
* -1: Invalid input parameters (filename, buffer is NULL, nbytes is 0, or offset is negative).
|
||||
* -2: Failed to open the file.
|
||||
* -5: Seek error occurred.
|
||||
* -4: Read error occurred.
|
||||
*/
|
||||
ssize_t storage_read_at(const char *filename, char *buffer, off_t offset, size_t nbytes);
|
139
firmware/components/storage/storage.c
Normal file
139
firmware/components/storage/storage.c
Normal file
@@ -0,0 +1,139 @@
|
||||
#include "storage.h"
|
||||
|
||||
#include "esp_log.h"
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "esp_spiffs.h"
|
||||
|
||||
static const char *TAG = "storage";
|
||||
|
||||
static FILE *s_current_file = NULL;
|
||||
static char s_current_filename[256] = {0}; // Buffer to store the current filename
|
||||
|
||||
esp_err_t storage_init(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing SPIFFS");
|
||||
|
||||
esp_vfs_spiffs_conf_t conf = {
|
||||
.base_path = "/storage", // Path where the filesystem will be mounted
|
||||
.partition_label = "storage", // Partition label (must match partitions.csv)
|
||||
.max_files = 5, // Maximum number of files that can be open at the same time
|
||||
.format_if_mount_failed = true // Format partition if mount fails
|
||||
};
|
||||
|
||||
// Initialize and mount SPIFFS
|
||||
esp_err_t ret = esp_vfs_spiffs_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 SPIFFS partition");
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to initialize SPIFFS (%s)", esp_err_to_name(ret));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "SPIFFS mounted");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void storage_uninit(void)
|
||||
{
|
||||
if (s_current_file != NULL)
|
||||
{
|
||||
// Log before clearing s_current_filename, using its value.
|
||||
ESP_LOGW(TAG, "File '%s' was still open during uninit. Closing it.", s_current_filename);
|
||||
fclose(s_current_file); // Close the file
|
||||
s_current_file = NULL;
|
||||
s_current_filename[0] = '\0';
|
||||
}
|
||||
esp_vfs_spiffs_unregister("storage");
|
||||
ESP_LOGI(TAG, "SPIFFS unmounted");
|
||||
}
|
||||
|
||||
ssize_t storage_read(const char *filename, char *buffer, size_t max_bytes)
|
||||
{
|
||||
// Input validation
|
||||
if (filename == NULL || filename[0] == '\0' || buffer == NULL || max_bytes == 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Invalid input parameters for storage_read");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// If no file is currently open, open the requested one
|
||||
if (s_current_file == NULL)
|
||||
{
|
||||
s_current_file = fopen(filename, "r");
|
||||
if (s_current_file == NULL)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to open file for reading: %s", filename);
|
||||
s_current_filename[0] = '\0'; // Clear filename as open failed
|
||||
return -2; // Failed to open file
|
||||
}
|
||||
// Store the filename for subsequent calls
|
||||
strncpy(s_current_filename, filename, sizeof(s_current_filename) - 1);
|
||||
s_current_filename[sizeof(s_current_filename) - 1] = '\0';
|
||||
ESP_LOGI(TAG, "Opened file: %s", s_current_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If a file is open, ensure it's the same file requested
|
||||
if (strcmp(filename, s_current_filename) != 0)
|
||||
{
|
||||
ESP_LOGE(TAG, "Filename mismatch. Expected '%s', got '%s'. Close the current file first.",
|
||||
s_current_filename, filename);
|
||||
// Note: File remains open. Caller might need a separate close function
|
||||
// or ensure read loop finishes (returns 0 or error) before changing filename.
|
||||
return -3; // Filename mismatch
|
||||
}
|
||||
}
|
||||
|
||||
// Read a chunk
|
||||
size_t bytes_read = fread(buffer, 1, max_bytes, s_current_file);
|
||||
|
||||
// Check for read errors
|
||||
if (ferror(s_current_file))
|
||||
{
|
||||
ESP_LOGE(TAG, "Error reading file: %s", s_current_filename);
|
||||
fclose(s_current_file);
|
||||
s_current_file = NULL;
|
||||
s_current_filename[0] = '\0';
|
||||
return -4; // Read error
|
||||
}
|
||||
|
||||
// If fread returns 0 and it's not an error, it means EOF or an empty file.
|
||||
// The file should be closed only when no more bytes can be read (i.e., bytes_read == 0).
|
||||
if (bytes_read == 0) // Indicates EOF or empty file (and no ferror)
|
||||
{
|
||||
// Log before clearing s_current_filename.
|
||||
// feof(s_current_file) should be true if end of file was actually reached.
|
||||
if (feof(s_current_file))
|
||||
{
|
||||
ESP_LOGI(TAG, "EOF reached for file: %s. Closing file.", s_current_filename);
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGI(TAG, "Read 0 bytes from file: %s (possibly empty or already at EOF). Closing file.",
|
||||
s_current_filename);
|
||||
}
|
||||
fclose(s_current_file);
|
||||
s_current_file = NULL;
|
||||
s_current_filename[0] = '\0';
|
||||
// Return 0 as per contract: "Returns 0 when the end of the file is reached."
|
||||
}
|
||||
// If bytes_read > 0, return the number of bytes read.
|
||||
// The file remains open (even if EOF was hit during this read and bytes_read < max_bytes).
|
||||
// The next call to storage_read for this file will then result in fread returning 0,
|
||||
// which will then hit the (bytes_read == 0) condition above and close the file.
|
||||
return bytes_read;
|
||||
}
|
Reference in New Issue
Block a user