add(storage): implement LittleFS initialization and static file handling

This commit is contained in:
HendrikRauh 2026-03-05 19:50:59 +01:00
parent 99cb820b0c
commit cac63b10d7
8 changed files with 189 additions and 38 deletions

View file

@ -0,0 +1,6 @@
idf_component_register(
SRCS "src/storage.c"
INCLUDE_DIRS "include"
REQUIRES joltwallet__littlefs
PRIV_REQUIRES vfs
)

View file

@ -0,0 +1,29 @@
#ifndef STORAGE_H
#define STORAGE_H
#include "esp_err.h"
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Initialize and mount LittleFS filesystem
*
* @return ESP_OK on success, error code otherwise
*/
esp_err_t storage_init(void);
/**
* @brief Get the mount point for the LittleFS filesystem
*
* @return Pointer to the mount point string (e.g., "/data")
*/
const char *storage_get_mount_point(void);
#ifdef __cplusplus
}
#endif
#endif /* STORAGE_H */

View file

@ -0,0 +1,56 @@
#include "storage.h"
#include "esp_littlefs.h"
#include "esp_log.h"
#include "esp_vfs.h"
static const char *TAG = "STORAGE";
static const char *LITTLEFS_MOUNT_POINT = "/data";
esp_err_t storage_init(void)
{
esp_vfs_littlefs_conf_t conf = {
.base_path = LITTLEFS_MOUNT_POINT,
.partition_label = "storage",
.format_if_mount_failed = false,
.read_only = false,
};
esp_err_t ret = esp_vfs_littlefs_register(&conf);
if (ret != ESP_OK)
{
if (ret == ESP_FAIL)
{
ESP_LOGE(TAG, "Failed to mount LittleFS or format filesystem");
}
else if (ret == ESP_ERR_INVALID_STATE)
{
ESP_LOGE(TAG, "ESP_ERR_INVALID_STATE");
}
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_LOGI(TAG, "LittleFS mounted at %s. Total: %d bytes, Used: %d bytes",
LITTLEFS_MOUNT_POINT, total, used);
}
else
{
ESP_LOGE(TAG, "Failed to get LittleFS information");
}
return ESP_OK;
}
const char *storage_get_mount_point(void)
{
return LITTLEFS_MOUNT_POINT;
}

View file

@ -2,7 +2,6 @@ idf_component_register(
SRCS "src/web_server.c"
"src/wifi.c"
INCLUDE_DIRS "include"
REQUIRES esp_http_server
REQUIRES esp_http_server storage
PRIV_REQUIRES freertos esp_wifi esp_event esp_netif nvs_flash
)

View file

@ -9,6 +9,7 @@
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "storage.h"
static const char *TAG = "WEBSERVER";
@ -22,22 +23,85 @@ static httpd_handle_t s_server_handle = NULL;
static TaskHandle_t s_server_task_handle = NULL;
/**
* @brief HTTP handler for root (GET /)
* @brief Get MIME type based on file extension
*/
static esp_err_t root_handler(httpd_req_t *req)
static const char *get_mime_type(const char *filename)
{
const char *html = "<!DOCTYPE html>"
"<html>"
"<head><title>DMX Interface</title></head>"
"<body>"
"<h1>DMX Interface</h1>"
"<p>Web server is running!</p>"
"<p><a href=\"/api/health\">Check Health</a></p>"
"</body>"
"</html>";
const char *dot = strrchr(filename, '.');
if (!dot)
return "application/octet-stream";
httpd_resp_set_type(req, "text/html");
httpd_resp_sendstr(req, html);
if (strcmp(dot, ".html") == 0)
return "text/html";
if (strcmp(dot, ".css") == 0)
return "text/css";
if (strcmp(dot, ".js") == 0)
return "application/javascript";
if (strcmp(dot, ".json") == 0)
return "application/json";
if (strcmp(dot, ".png") == 0)
return "image/png";
if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
return "image/jpeg";
if (strcmp(dot, ".gif") == 0)
return "image/gif";
if (strcmp(dot, ".svg") == 0)
return "image/svg+xml";
if (strcmp(dot, ".ico") == 0)
return "image/x-icon";
if (strcmp(dot, ".txt") == 0)
return "text/plain";
if (strcmp(dot, ".xml") == 0)
return "application/xml";
if (strcmp(dot, ".wav") == 0)
return "audio/wav";
if (strcmp(dot, ".mp3") == 0)
return "audio/mpeg";
return "application/octet-stream";
}
/**
* @brief HTTP handler for static files from LittleFS
*/
static esp_err_t static_file_handler(httpd_req_t *req)
{
// Build the file path
char filepath[1024];
snprintf(filepath, sizeof(filepath), "%s%s", storage_get_mount_point(), req->uri);
// Handle root path
if (strcmp(req->uri, "/") == 0)
{
snprintf(filepath, sizeof(filepath), "%s/index.html", storage_get_mount_point());
}
FILE *f = fopen(filepath, "r");
if (!f)
{
ESP_LOGW(TAG, "File not found: %s", filepath);
httpd_resp_send_404(req);
return ESP_OK;
}
// Get MIME type and set content type
const char *mime_type = get_mime_type(filepath);
httpd_resp_set_type(req, mime_type);
// Send file in chunks
char buf[1024];
size_t read_len;
while ((read_len = fread(buf, 1, sizeof(buf), f)) > 0)
{
if (httpd_resp_send_chunk(req, buf, read_len) != ESP_OK)
{
ESP_LOGW(TAG, "Failed to send data chunk for %s", filepath);
break;
}
}
fclose(f);
httpd_resp_send_chunk(req, NULL, 0); // Send end marker
return ESP_OK;
}
@ -57,7 +121,7 @@ static esp_err_t health_check_handler(httpd_req_t *req)
*/
static void webserver_task(void *arg)
{
httpd_handle_t server = (httpd_handle_t)arg;
(void)arg; // Unused parameter
ESP_LOGI(TAG, "Web server task started");
// Keep task alive - the server runs in the background
@ -78,6 +142,14 @@ httpd_handle_t webserver_start(const webserver_config_t *config)
return s_server_handle;
}
// Initialize LittleFS
esp_err_t ret = storage_init();
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to initialize storage");
return NULL;
}
// Use provided config or defaults
uint16_t port = WEBSERVER_DEFAULT_PORT;
size_t max_handlers = WEBSERVER_DEFAULT_MAX_HANDLERS;
@ -100,7 +172,7 @@ httpd_handle_t webserver_start(const webserver_config_t *config)
http_config.uri_match_fn = httpd_uri_match_wildcard;
// Start HTTP server
esp_err_t ret = httpd_start(&s_server_handle, &http_config);
ret = httpd_start(&s_server_handle, &http_config);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(ret));
@ -120,23 +192,14 @@ httpd_handle_t webserver_start(const webserver_config_t *config)
};
httpd_register_uri_handler(s_server_handle, &health_uri);
// Root / index.html handler
httpd_uri_t root_uri = {
.uri = "/",
.method = HTTP_GET,
.handler = root_handler,
.user_ctx = NULL,
};
httpd_register_uri_handler(s_server_handle, &root_uri);
// Wildcard handler for 404 (must be last)
httpd_uri_t wildcard_uri = {
// Wildcard handler for static files from LittleFS (must be last)
httpd_uri_t file_uri = {
.uri = "/*",
.method = HTTP_GET,
.handler = NULL, // Let httpd handle as 404
.handler = static_file_handler,
.user_ctx = NULL,
};
// Don't register wildcard - just let httpd default to 404
httpd_register_uri_handler(s_server_handle, &file_uri);
// Create FreeRTOS task for the server
// This allows other tasks to continue running and makes the server async-ready