ChaosDMX
Open-Source DMX-Interface
Loading...
Searching...
No Matches
web_server.c
Go to the documentation of this file.
1#define LOG_TAG "WEBSRV"
2
7
8#include "web_server.h"
9
10#include <ctype.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <string.h>
14
15#include "esp_err.h"
16#include "freertos/FreeRTOS.h"
17#include "freertos/task.h"
18#include "logger.h"
19#include "storage.h"
20
21// Default configuration values
25#define WEBSERVER_DEFAULT_PORT 80
29#define WEBSERVER_DEFAULT_MAX_HANDLERS 32
33#define WEBSERVER_DEFAULT_STACK_SIZE (8 * 1024)
37#define WEBSERVER_DEFAULT_TASK_PRIORITY 5
41
45static httpd_handle_t s_server_handle = NULL;
46
50static TaskHandle_t s_server_task_handle = NULL;
51
55static const char *get_mime_type(const char *filename) {
56 const char *dot = strrchr(filename, '.');
57 if (!dot)
58 return "application/octet-stream";
59
60 if (strcmp(dot, ".html") == 0)
61 return "text/html";
62 if (strcmp(dot, ".css") == 0)
63 return "text/css";
64 if (strcmp(dot, ".js") == 0)
65 return "application/javascript";
66 if (strcmp(dot, ".json") == 0)
67 return "application/json";
68 if (strcmp(dot, ".png") == 0)
69 return "image/png";
70 if (strcmp(dot, ".jpg") == 0 || strcmp(dot, ".jpeg") == 0)
71 return "image/jpeg";
72 if (strcmp(dot, ".gif") == 0)
73 return "image/gif";
74 if (strcmp(dot, ".svg") == 0)
75 return "image/svg+xml";
76 if (strcmp(dot, ".ico") == 0)
77 return "image/x-icon";
78 if (strcmp(dot, ".txt") == 0)
79 return "text/plain";
80 if (strcmp(dot, ".xml") == 0)
81 return "application/xml";
82 if (strcmp(dot, ".wav") == 0)
83 return "audio/wav";
84 if (strcmp(dot, ".mp3") == 0)
85 return "audio/mpeg";
86
87 return "application/octet-stream";
88}
89
93static esp_err_t static_file_handler(httpd_req_t *req) {
94 // Build the file path
95 char filepath[1024];
96 snprintf(filepath, sizeof(filepath), "%s%s", storage_get_mount_point(),
97 req->uri);
98
99 // Handle root path
100 if (strcmp(req->uri, "/") == 0) {
101 snprintf(filepath, sizeof(filepath), "%s/index.html",
103 }
104
105 FILE *f = fopen(filepath, "r");
106 if (!f) {
107 LOGW("File not found: %s", filepath);
108 httpd_resp_send_404(req);
109 return ESP_OK;
110 }
111
112 // Get MIME type and set content type
113 const char *mime_type = get_mime_type(filepath);
114 httpd_resp_set_type(req, mime_type);
115
116 // Send file in chunks
117 char buf[1024];
118 size_t read_len;
119 while ((read_len = fread(buf, 1, sizeof(buf), f)) > 0) {
120 if (httpd_resp_send_chunk(req, buf, read_len) != ESP_OK) {
121 LOGW("Failed to send data chunk for %s", filepath);
122 break;
123 }
124 }
125
126 fclose(f);
127 httpd_resp_send_chunk(req, NULL, 0); // Send end marker
128 return ESP_OK;
129}
130
134static esp_err_t health_check_handler(httpd_req_t *req) {
135 httpd_resp_set_type(req, "application/json");
136 httpd_resp_sendstr(req, "{\"status\":\"ok\"}");
137 return ESP_OK;
138}
139
144static void webserver_task(void *arg) {
145 (void)arg; // Unused parameter
146 LOGI("Web server task started");
147
148 // Keep task alive - the server runs in the background
149 while (s_server_handle != NULL) {
150 vTaskDelay(pdMS_TO_TICKS(10000)); // 10 second check interval
151 }
152
153 LOGI("Web server task ending");
154 vTaskDelete(NULL);
155}
156
166httpd_handle_t webserver_start(const webserver_config_t *config) {
167 if (s_server_handle != NULL) {
168 LOGW("Web server already running");
169 return s_server_handle;
170 }
171
172 // Initialize LittleFS
173 esp_err_t ret = storage_init();
174 if (ret != ESP_OK) {
175 LOGE("Failed to initialize storage");
176 return NULL;
177 }
178
179 // Use provided config or defaults
180 uint16_t port = WEBSERVER_DEFAULT_PORT;
181 size_t max_handlers = WEBSERVER_DEFAULT_MAX_HANDLERS;
182 size_t stack_size = WEBSERVER_DEFAULT_STACK_SIZE;
183 UBaseType_t task_priority = WEBSERVER_DEFAULT_TASK_PRIORITY;
184
185 if (config) {
186 port = config->port;
187 max_handlers = config->max_uri_handlers;
188 stack_size = config->stack_size;
189 task_priority = config->task_priority;
190 }
191
192 // Create HTTP server configuration
193 httpd_config_t http_config = HTTPD_DEFAULT_CONFIG();
194 http_config.server_port = port;
195 http_config.max_uri_handlers = max_handlers;
196 http_config.stack_size = stack_size;
197 http_config.uri_match_fn = httpd_uri_match_wildcard;
198
199 // Start HTTP server
200 ret = httpd_start(&s_server_handle, &http_config);
201 if (ret != ESP_OK) {
202 LOGE("Failed to start HTTP server: %s", esp_err_to_name(ret));
203 s_server_handle = NULL;
204 return NULL;
205 }
206
207 LOGI("HTTP server started on port %d", port);
208
209 // Register default handlers
210 // Health check endpoint
211 httpd_uri_t health_uri = {
212 .uri = "/api/health",
213 .method = HTTP_GET,
214 .handler = health_check_handler,
215 .user_ctx = NULL,
216 };
217 httpd_register_uri_handler(s_server_handle, &health_uri);
218
219 // Wildcard handler for static files from LittleFS (must be last)
220 httpd_uri_t file_uri = {
221 .uri = "/*",
222 .method = HTTP_GET,
223 .handler = static_file_handler,
224 .user_ctx = NULL,
225 };
226 httpd_register_uri_handler(s_server_handle, &file_uri);
227
228 // Create FreeRTOS task for the server
229 // This allows other tasks to continue running and makes the server
230 // async-ready
231 BaseType_t task_ret = xTaskCreate(webserver_task, "webserver", stack_size,
232 (void *)s_server_handle, task_priority,
234
235 if (task_ret != pdPASS) {
236 LOGE("Failed to create web server task");
237 httpd_stop(s_server_handle);
238 s_server_handle = NULL;
239 return NULL;
240 }
241
242 LOGI("Web server initialized successfully");
243 return s_server_handle;
244}
245
253void webserver_stop(httpd_handle_t server) {
254 if (server == NULL) {
255 return;
256 }
257
258 httpd_stop(server);
259 s_server_handle = NULL;
260
261 // Wait for task to finish
262 if (s_server_task_handle != NULL) {
263 vTaskDelay(pdMS_TO_TICKS(100));
265 }
266
267 LOGI("Web server stopped");
268}
269
278esp_err_t webserver_register_handler(httpd_handle_t server,
279 const httpd_uri_t *uri_handler) {
280 if (server == NULL || uri_handler == NULL) {
281 return ESP_ERR_INVALID_ARG;
282 }
283
284 esp_err_t ret = httpd_register_uri_handler(server, uri_handler);
285 if (ret == ESP_OK) {
286 LOGI("Registered handler: %s [%d]", uri_handler->uri, uri_handler->method);
287 } else {
288 LOGE("Failed to register handler %s: %s", uri_handler->uri,
289 esp_err_to_name(ret));
290 }
291
292 return ret;
293}
Project-wide logging macros based on ESP-IDF's logging library.
#define LOGW(...)
Log a message at Warning level.
Definition logger.h:40
#define LOGI(...)
Log a message at Info level.
Definition logger.h:42
#define LOGE(...)
Log a message at Error level.
Definition logger.h:38
esp_err_t storage_init(void)
Initialize and mount LittleFS filesystem.
Definition storage.c:15
const char * storage_get_mount_point(void)
Get the mount point for the LittleFS filesystem.
Definition storage.c:52
Web server configuration structure.
Definition web_server.h:18
#define WEBSERVER_DEFAULT_STACK_SIZE
Default maximum number of URI handlers.
Definition web_server.c:33
static esp_err_t static_file_handler(httpd_req_t *req)
HTTP handler for static files from LittleFS.
Definition web_server.c:93
static const char * get_mime_type(const char *filename)
Get MIME type based on file extension.
Definition web_server.c:55
esp_err_t webserver_register_handler(httpd_handle_t server, const httpd_uri_t *uri_handler)
Register a URI handler with the web server.
Definition web_server.c:278
static void webserver_task(void *arg)
FreeRTOS task function for the HTTP server. Allows non-blocking server operation and future extensibi...
Definition web_server.c:144
void webserver_stop(httpd_handle_t server)
Stop the web server and clean up resources.
Definition web_server.c:253
static TaskHandle_t s_server_task_handle
Handle for the FreeRTOS web server task.
Definition web_server.c:50
#define WEBSERVER_DEFAULT_PORT
Default port for the web server.
Definition web_server.c:25
httpd_handle_t webserver_start(const webserver_config_t *config)
Start the web server with the given configuration.
Definition web_server.c:166
static httpd_handle_t s_server_handle
Default task priority for the web server task.
Definition web_server.c:45
#define WEBSERVER_DEFAULT_TASK_PRIORITY
Default stack size for the web server task.
Definition web_server.c:37
#define WEBSERVER_DEFAULT_MAX_HANDLERS
Default port for the web server.
Definition web_server.c:29
static esp_err_t health_check_handler(httpd_req_t *req)
HTTP handler for API health check (GET /api/health).
Definition web_server.c:134
Simple HTTP web server component for ESP32 with async FreeRTOS support.