mirror of
https://github.com/HendrikRauh/dmx-interface.git
synced 2026-03-09 05:20:21 +00:00
add(storage): implement LittleFS initialization and static file handling
This commit is contained in:
parent
99cb820b0c
commit
cac63b10d7
8 changed files with 189 additions and 38 deletions
|
|
@ -11,10 +11,7 @@ include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
|
||||||
project(dmx-interface)
|
project(dmx-interface)
|
||||||
|
|
||||||
# Disable filesystem image creation for now
|
# Enable LittleFS filesystem image creation for web assets
|
||||||
# Can be enabled later when data/ folder is properly configured
|
if(COMMAND littlefs_create_partition_image)
|
||||||
if(FALSE)
|
littlefs_create_partition_image(storage ${CMAKE_SOURCE_DIR}/data FLASH_IN_PROJECT)
|
||||||
if(COMMAND littlefs_create_partition_image)
|
|
||||||
littlefs_create_partition_image(storage ${CMAKE_SOURCE_DIR}/data FLASH_IN_PROJECT)
|
|
||||||
endif()
|
|
||||||
endif()
|
endif()
|
||||||
|
|
|
||||||
6
components/storage/CMakeLists.txt
Normal file
6
components/storage/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
idf_component_register(
|
||||||
|
SRCS "src/storage.c"
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
REQUIRES joltwallet__littlefs
|
||||||
|
PRIV_REQUIRES vfs
|
||||||
|
)
|
||||||
29
components/storage/include/storage.h
Normal file
29
components/storage/include/storage.h
Normal 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 */
|
||||||
56
components/storage/src/storage.c
Normal file
56
components/storage/src/storage.c
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -2,7 +2,6 @@ idf_component_register(
|
||||||
SRCS "src/web_server.c"
|
SRCS "src/web_server.c"
|
||||||
"src/wifi.c"
|
"src/wifi.c"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
REQUIRES esp_http_server
|
REQUIRES esp_http_server storage
|
||||||
PRIV_REQUIRES freertos esp_wifi esp_event esp_netif nvs_flash
|
PRIV_REQUIRES freertos esp_wifi esp_event esp_netif nvs_flash
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
|
#include "storage.h"
|
||||||
|
|
||||||
static const char *TAG = "WEBSERVER";
|
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;
|
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>"
|
const char *dot = strrchr(filename, '.');
|
||||||
"<html>"
|
if (!dot)
|
||||||
"<head><title>DMX Interface</title></head>"
|
return "application/octet-stream";
|
||||||
"<body>"
|
|
||||||
"<h1>DMX Interface</h1>"
|
|
||||||
"<p>Web server is running!</p>"
|
|
||||||
"<p><a href=\"/api/health\">Check Health</a></p>"
|
|
||||||
"</body>"
|
|
||||||
"</html>";
|
|
||||||
|
|
||||||
httpd_resp_set_type(req, "text/html");
|
if (strcmp(dot, ".html") == 0)
|
||||||
httpd_resp_sendstr(req, html);
|
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +121,7 @@ static esp_err_t health_check_handler(httpd_req_t *req)
|
||||||
*/
|
*/
|
||||||
static void webserver_task(void *arg)
|
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");
|
ESP_LOGI(TAG, "Web server task started");
|
||||||
|
|
||||||
// Keep task alive - the server runs in the background
|
// 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;
|
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
|
// Use provided config or defaults
|
||||||
uint16_t port = WEBSERVER_DEFAULT_PORT;
|
uint16_t port = WEBSERVER_DEFAULT_PORT;
|
||||||
size_t max_handlers = WEBSERVER_DEFAULT_MAX_HANDLERS;
|
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;
|
http_config.uri_match_fn = httpd_uri_match_wildcard;
|
||||||
|
|
||||||
// Start HTTP server
|
// 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)
|
if (ret != ESP_OK)
|
||||||
{
|
{
|
||||||
ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(ret));
|
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);
|
httpd_register_uri_handler(s_server_handle, &health_uri);
|
||||||
|
|
||||||
// Root / index.html handler
|
// Wildcard handler for static files from LittleFS (must be last)
|
||||||
httpd_uri_t root_uri = {
|
httpd_uri_t file_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 = {
|
|
||||||
.uri = "/*",
|
.uri = "/*",
|
||||||
.method = HTTP_GET,
|
.method = HTTP_GET,
|
||||||
.handler = NULL, // Let httpd handle as 404
|
.handler = static_file_handler,
|
||||||
.user_ctx = NULL,
|
.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
|
// Create FreeRTOS task for the server
|
||||||
// This allows other tasks to continue running and makes the server async-ready
|
// This allows other tasks to continue running and makes the server async-ready
|
||||||
|
|
|
||||||
|
|
@ -17,5 +17,5 @@ direct_dependencies:
|
||||||
- idf
|
- idf
|
||||||
- joltwallet/littlefs
|
- joltwallet/littlefs
|
||||||
manifest_hash: ff4b0b01cddb86fe710ecb8fe90983fdab6a922a91a7dcfade112bc73ef373e8
|
manifest_hash: ff4b0b01cddb86fe710ecb8fe90983fdab6a922a91a7dcfade112bc73ef373e8
|
||||||
target: esp32c6
|
target: esp32s2
|
||||||
version: 2.0.0
|
version: 2.0.0
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
# Espressif IoT Development Framework (ESP-IDF) 5.5.2 Project Minimal Configuration
|
# Espressif IoT Development Framework (ESP-IDF) 5.5.2 Project Minimal Configuration
|
||||||
#
|
#
|
||||||
CONFIG_IDF_TARGET="esp32s2"
|
|
||||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_IDF_TARGET="esp32s2"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue