esp32 firmware for a toaster reflow oven WIP!!!!!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
reflower/main/web/websrv.c

293 lines
8.0 KiB

#include <esp_log.h>
#include <esp_err.h>
#include <fileserver/token_subs.h>
#include <httpd_utils/captive.h>
#include <errno.h>
#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, "<!DOCTYPE html><html><head>"
"<meta http-equiv=\"refresh\" content=\"10; url=/\">"
"</head>"
"<body>"
"Reboot requested. Reloading in 10 seconds.<br>"
"<a href=\"/\">Try now.</a>", -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;
}