#include "aes67_web.h" #include "esp_log.h" #include "esp_system.h" #include "esp_timer.h" #define MAX_STATUS_CBS 16 #define MAX_URIS 32 #define MAX_EARLY_URIS 16 static const char *TAG = "web"; extern const char index_html_start[] asm("_binary_index_html_start"); extern const char index_html_end[] asm("_binary_index_html_end"); static httpd_handle_t s_httpd; static status_cb_t s_status_cbs[MAX_STATUS_CBS]; static int s_status_n; static httpd_uri_t s_early_uris[MAX_EARLY_URIS]; static int s_early_n; esp_err_t status_register(status_cb_t cb) { if (s_status_n >= MAX_STATUS_CBS) { return ESP_ERR_NO_MEM; } s_status_cbs[s_status_n++] = cb; return ESP_OK; } esp_err_t web_register_uri(const httpd_uri_t *uri) { if (s_httpd) { return httpd_register_uri_handler(s_httpd, uri); } if (s_early_n >= MAX_EARLY_URIS) { return ESP_ERR_NO_MEM; } s_early_uris[s_early_n++] = *uri; return ESP_OK; } esp_err_t web_send_json(httpd_req_t *req, const cJSON *json) { char *body = cJSON_PrintUnformatted(json); if (!body) { return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "out of memory"); } httpd_resp_set_type(req, "application/json"); httpd_resp_set_hdr(req, "Cache-Control", "no-store"); esp_err_t err = httpd_resp_sendstr(req, body); cJSON_free(body); return err; } static esp_err_t index_get(httpd_req_t *req) { httpd_resp_set_type(req, "text/html; charset=utf-8"); // EMBED_TXTFILES appends a NUL; don't send it. return httpd_resp_send(req, index_html_start, index_html_end - index_html_start - 1); } static esp_err_t status_get(httpd_req_t *req) { cJSON *st = cJSON_CreateObject(); for (int i = 0; i < s_status_n; i++) { s_status_cbs[i](st); } esp_err_t err = web_send_json(req, st); cJSON_Delete(st); return err; } static void reboot_cb(void *arg) { esp_restart(); } static esp_err_t reboot_post(httpd_req_t *req) { ESP_LOGW(TAG, "reboot requested via API"); httpd_resp_sendstr(req, ""); // Let the response go out before restarting. const esp_timer_create_args_t args = { .callback = reboot_cb, .name = "reboot" }; esp_timer_handle_t t; if (esp_timer_create(&args, &t) == ESP_OK) { esp_timer_start_once(t, 500 * 1000); } return ESP_OK; } esp_err_t aes67_web_start(void) { httpd_config_t cfg = HTTPD_DEFAULT_CONFIG(); cfg.max_uri_handlers = MAX_URIS; cfg.stack_size = 8192; cfg.lru_purge_enable = true; cfg.uri_match_fn = httpd_uri_match_wildcard; esp_err_t err = httpd_start(&s_httpd, &cfg); if (err != ESP_OK) { ESP_LOGE(TAG, "httpd start failed: %s", esp_err_to_name(err)); return err; } static const httpd_uri_t core[] = { { .uri = "/", .method = HTTP_GET, .handler = index_get }, { .uri = "/index.html", .method = HTTP_GET, .handler = index_get }, { .uri = "/api/status", .method = HTTP_GET, .handler = status_get }, { .uri = "/api/reboot", .method = HTTP_POST, .handler = reboot_post }, }; for (int i = 0; i < sizeof(core) / sizeof(core[0]); i++) { httpd_register_uri_handler(s_httpd, &core[i]); } for (int i = 0; i < s_early_n; i++) { httpd_register_uri_handler(s_httpd, &s_early_uris[i]); } ESP_LOGI(TAG, "httpd started, index.html %d bytes, %d project routes", (int)(index_html_end - index_html_start - 1), s_early_n); return ESP_OK; }