#include #include #include #include #include #include "firehazard.h" #include "websrv.h" #include "esp_http_server.h" #include "utils.h" #include "files/files_enum.h" #include "version.h" #include "analog.h" static const char *TAG="websrv"; static httpd_handle_t s_hServer = NULL; // Embedded files (must also be listed in CMakeLists.txt as COMPONENT_EMBED_TXTFILES) efile(index_file, "index_html"); static struct tpl_kv_list build_index_replacements_kv(void) { // char name[TPL_KV_KEY_LEN]; struct tpl_kv_list kv = tpl_kv_init(); tpl_kv_add(&kv, "version", APP_VERSION); size_t pcap1 = 300; size_t pcap2 = 300; char *path1 = malloc(pcap1); char *path2 = malloc(pcap2); assert(path1); assert(path2); path1[0] = 0; path2[0] = 0; #define SCRATCH_SIZE 15 char scratch1[SCRATCH_SIZE]; char scratch2[SCRATCH_SIZE]; bool last_empty = true; // first is move bool suc; for (int i = 0; i < REG_HISTORY_LEN; i++) { float x = i*2.5f; if (reg_meas_history[i] == 0) { snprintf(scratch1, SCRATCH_SIZE, "M%.1f,0", x); snprintf(scratch2, SCRATCH_SIZE, "M%.1f,0", x); last_empty = true; } else { snprintf(scratch1, SCRATCH_SIZE, "%c%.1f,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_meas_history[i])); snprintf(scratch2, SCRATCH_SIZE, "%c%.1f,%d", last_empty ? 'M' : 'L', x, (int)(400 - reg_tset_history[i])); last_empty = false; } suc = append_realloc(&path1, &pcap1, scratch1); assert(suc); suc = append_realloc(&path2, &pcap2, scratch2); assert(suc); } tpl_kv_add_heapstr(&kv, "ser-act", path1); tpl_kv_add_heapstr(&kv, "ser-set", path2); bool ena = fire_enabled(); tpl_kv_add(&kv, "fire_dis_ck", ena ? "" : "selected"); tpl_kv_add(&kv, "fire_en_ck", ena ? "selected" : ""); tpl_kv_add_int(&kv, "timeshift", history_counter); snprintf(scratch1, SCRATCH_SIZE, "%.2f", analog_read()); tpl_kv_add(&kv, "temp", scratch1); snprintf(scratch1, SCRATCH_SIZE, "%.0f", fire_get_setpoint(false)); tpl_kv_add(&kv, "tset", scratch1); float kp, ki, kd; fire_get_tuning(&kp, &ki, &kd); snprintf(scratch1, SCRATCH_SIZE, "%.3f", kp); tpl_kv_add(&kv, "kp", scratch1); snprintf(scratch1, SCRATCH_SIZE, "%.3f", ki); tpl_kv_add(&kv, "ki", scratch1); snprintf(scratch1, SCRATCH_SIZE, "%.3f", kd); tpl_kv_add(&kv, "kd", scratch1); #undef SCRATCH_SIZE return kv; } /* Main page */ static esp_err_t handler_index(httpd_req_t *req) { struct tpl_kv_list kv = build_index_replacements_kv(); esp_err_t suc = httpd_send_template_file(req, FILE_INDEX_HTML, tpl_kv_replacer, &kv, 0); tpl_kv_free(&kv); return suc; } /* Update XHR for new index page data */ static esp_err_t handler_update(httpd_req_t *req) { struct tpl_kv_list kv = build_index_replacements_kv(); esp_err_t suc = tpl_kv_send_as_ascii_map(req, &kv); tpl_kv_free(&kv); return suc; } /* Set a param */ static esp_err_t handler_set(httpd_req_t *req) { char buf[200]; int n = httpd_req_recv(req, buf, 63); if (n < 0) { ESP_LOGW(TAG, "rx er"); goto err; } buf[n]=0; char val[20]; // select box 0/1 esp_err_t rv = httpd_query_key_value(buf, "fire", val, 20); if (rv == ESP_OK) { fire_enable(val[0] == '1'); } rv = httpd_query_key_value(buf, "tset", val, 20); if (rv == ESP_OK) { errno = 0; float f = atoff(val); if (!errno) { fire_setlevel(f); } } float kp, ki, kd; // sorry // Kp rv = httpd_query_key_value(buf, "kp", val, 20); if (rv == ESP_OK) { errno = 0; kp = atoff(val); if (!errno) { // Ki rv = httpd_query_key_value(buf, "ki", val, 20); if (rv == ESP_OK) { errno = 0; ki = atoff(val); if (!errno) { // Kd rv = httpd_query_key_value(buf, "kd", val, 20); if (rv == ESP_OK) { errno = 0; kd = atoff(val); if (!errno) { fire_set_tuning(kp, ki, kd); // save to NVS nvs_handle nvs; ESP_ERROR_CHECK(nvs_open("config", NVS_READWRITE, &nvs)); union uf32 ukp, uki, ukd; ukp.f = kp; uki.f = ki; ukd.f = kd; nvs_set_u32(nvs, "kp", ukp.u); nvs_set_u32(nvs, "ki", uki.u); nvs_set_u32(nvs, "kd", ukd.u); nvs_commit(nvs); nvs_close(nvs); } } } } } } httpd_resp_set_hdr(req, "Location", "/"); httpd_resp_set_status(req, "302 Found"); return httpd_resp_send(req, NULL, 0); err: return httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, NULL); } /* Request emulator reboot */ static esp_err_t handler_reboot(httpd_req_t *req) { httpd_resp_send(req, "" "" "" "" "Reboot requested. Reloading in 10 seconds.
" "Try now.", -1); ESP_LOGI(TAG, "Restarting ESP..."); esp_restart(); } /* An HTTP GET handler */ static esp_err_t handler_staticfiles(httpd_req_t *r) { const struct embedded_file_info *file; const char *fname = r->user_ctx; enum file_access_level access = FILE_ACCESS_PROTECTED; // wildcard files must be public if (fname == NULL) { fname = r->uri + 1; // URI always starts with slash, but we dont want a slash in the file name access = FILE_ACCESS_PUBLIC; } #ifdef USE_CAPTIVE_PORTAL // First check if this is a phone taken here by the captive portal esp_err_t rv = httpd_captive_redirect(r); if (rv != ESP_ERR_NOT_FOUND) return rv; #endif if (ESP_OK != www_get_static_file(fname, access, &file)) { ESP_LOGW(TAG, "File not found: %s", fname); return httpd_resp_send_404(r); } else { if (streq(file->mime, "text/html")) { // using the template func to allow includes return httpd_send_template_file_struct(r, file, /*replacer*/NULL, /*ctx*/NULL, /*opts*/0); } else { return httpd_send_static_file_struct(r, file, TPL_ESCAPE_NONE, 0); } } } static const httpd_uri_t routes[] = { { .uri = "/", .method = HTTP_GET, .handler = handler_index, }, { .uri = "/data", .method = HTTP_GET, .handler = handler_update, }, { .uri = "/set", .method = HTTP_POST, .handler = handler_set, }, { .uri = "/reboot", .method = HTTP_GET, .handler = handler_reboot, }, { .uri = "*", // any file except protected (e.g. not HTML, PEM etc) .method = HTTP_GET, .handler = handler_staticfiles, }, }; esp_err_t websrv_init(void) { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.stack_size = 6000; config.uri_match_fn = httpd_uri_match_wildcard; config.lru_purge_enable = true; ESP_LOGI(TAG, "Starting HTTP server on port: '%d'", config.server_port); esp_err_t suc = httpd_start(&s_hServer, &config); if (suc == ESP_OK) { ESP_LOGI(TAG, "HTTP server started"); for (int i = 0; i < sizeof(routes) / sizeof(httpd_uri_t); i++) { httpd_register_uri_handler(s_hServer, &routes[i]); } return ESP_OK; } ESP_LOGE(TAG, "Error starting server!"); return suc; }