From 05e3f6bb7293c771629c7b3599392c8f335e7dd8 Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Thu, 1 Dec 2022 21:29:34 -0700 Subject: Add an HTTP Server. This server allows the user to control the parameters of the state for displaying the lights. --- main/http_server.c | 222 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 222 insertions(+) create mode 100644 main/http_server.c (limited to 'main/http_server.c') diff --git a/main/http_server.c b/main/http_server.c new file mode 100644 index 0000000..e4c426c --- /dev/null +++ b/main/http_server.c @@ -0,0 +1,222 @@ +#include "http_server.h" + +#define TAG "httpd" + +static void handle_set_int(httpd_req_t* req, int* val) +{ + char buf[128]; + int buf_len = httpd_req_get_url_query_len(req) + 1; + esp_err_t err; + + if (buf_len > 1) { + if (buf_len > sizeof(buf)) { + ESP_LOGI(TAG, "Invalid request. Query string too long."); + httpd_resp_set_status(req, HTTPD_400); + return; + } + + if ((err = httpd_req_get_url_query_str(req, buf, buf_len)) == ESP_OK) { + char param[32]; + if (httpd_query_key_value(buf, "add", param, sizeof(param)) == ESP_OK) { + *val += atoi(param); + } else if (httpd_query_key_value(buf, "set", param, sizeof(param)) == ESP_OK) { + *val = atoi(param); + } else if (httpd_query_key_value(buf, "sub", param, sizeof(param)) == ESP_OK) { + *val -= atoi(param); + } else { + ESP_LOGI(TAG, "No valid parameters."); + httpd_resp_set_status(req, HTTPD_400); + return; + } + } else { + ESP_LOGI(TAG, "Unable to get URL query string. [%d]", err); + httpd_resp_set_status(req, HTTPD_500); + return; + } + } else { + ESP_LOGI(TAG, "No query string provided?"); + httpd_resp_set_status(req, HTTPD_400); + return; + } + + ESP_LOGI(TAG, "Now equals %d", *val); + httpd_resp_set_status(req, HTTPD_204); +} + +static void handle_set_bool(httpd_req_t* req, bool* val) +{ + char buf[128]; + int buf_len = httpd_req_get_url_query_len(req) + 1; + + if (buf_len > 1) { + if (buf_len > sizeof(buf)) { + ESP_LOGI(TAG, "Invalid request. Query string too long."); + httpd_resp_set_status(req, HTTPD_400); + return; + } + + if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) { + char param[32]; + if (httpd_query_key_value(buf, "set", param, sizeof(param)) == ESP_OK) { + if (!strcmp(param, "on")) { + *val = true; + } else if (!strcmp(param, "off")) { + *val = false; + } else if (!strcmp(param, "toggle")) { + *val = !*val; + } else { + ESP_LOGI(TAG, "Invalid request. Invalid param value."); + httpd_resp_set_status(req, HTTPD_400); + return; + } + } + } + } + + httpd_resp_set_status(req, HTTPD_204); +} + +static void handle_set_uint8_t(httpd_req_t* req, uint8_t* val) +{ + int tmp = *val; + handle_set_int(req, &tmp); + *val = tmp; +} + +static void handle_set_uint32_t(httpd_req_t* req, uint32_t* val) +{ + int tmp = *val; + handle_set_int(req, &tmp); + *val = tmp; +} + +#define STATE_PARAM(typ, attr, ...) \ + static esp_err_t handle_set_ ## attr (httpd_req_t *req) { \ + ESP_LOGI(TAG, "Handling [%s] (%s)", #attr, req->uri); \ + ws_params_t* params = (ws_params_t*) req->user_ctx; \ + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); \ + handle_set_ ## typ (req, ¶ms->state. attr); \ + httpd_resp_send(req, NULL, 0); \ + return ESP_OK; \ + } \ + static httpd_uri_t http_ ## attr ## _handler = { \ + .uri = "/" #attr, \ + .method = HTTP_POST, \ + .handler = handle_set_ ## attr, \ + .user_ctx = NULL \ + }; +#include "state_params.i" +#undef STATE_PARAM + +static size_t write_uint32_t_value( + char** out, size_t len, const char* attr, const char* dn, uint32_t value) +{ + size_t newlen = snprintf(*out, len, "\"%s\":{\"type\":\"uint32_t\",\"value\":\"%u\",\"disp\":\"%s\"}", + attr, (unsigned int) value, dn); + *out += newlen; + return len - newlen; +} + +static size_t write_uint8_t_value( + char** out, size_t len, const char* attr, const char* dn, uint8_t value) +{ + size_t newlen = snprintf(*out, len, "\"%s\":{\"type\":\"uint8_t\",\"value\":\"%u\",\"disp\":\"%s\"}", + attr, (unsigned int)value, dn); + *out += newlen; + return len - newlen; +} + +static size_t write_int_value( + char** out, size_t len, const char* attr, const char* dn, int value) +{ + size_t newlen = snprintf(*out, len, "\"%s\":{\"type\":\"int\",\"value\":\"%d\",\"disp\":\"%s\"}", + attr, value, dn); + *out += newlen; + return len - newlen; +} + +static size_t write_bool_value( + char** out, size_t len, const char* attr, const char* dn, bool value) +{ + size_t newlen = snprintf(*out, len, "\"%s\":{\"type\":\"bool\",\"value\":\"%s\",\"disp\":\"%s\"}", + attr, value ? "on" : "off", dn); + *out += newlen; + return len - newlen; +} + +static esp_err_t handle_get_params(httpd_req_t* req) +{ + httpd_resp_set_hdr(req, "Access-Control-Allow-Origin", "*"); \ + size_t len = NUMBER_PARAMS * 128; + char out_buffer[len + 1]; + out_buffer[len] = 0; + + char* out_ptr = out_buffer; + + ws_params_t* ws_params = (ws_params_t*) req->user_ctx; + bool write_comma = false; + httpd_resp_set_type(req, "application/json"); + if (len > 1) { + *out_ptr = '{'; + out_ptr ++; + len --; + } +#define STATE_PARAM(typ, attr, display, ...) \ + if (write_comma && len > 1) { \ + *(out_ptr++) = ','; \ + len --; \ + } \ + len = write_ ## typ ## _value( \ + &out_ptr, len, #attr, display, ws_params->state. attr); \ + write_comma = true; +#include "state_params.i" +#undef STATE_PARAM + if (len > 3) { + *(out_ptr++) = '}'; len --; + *(out_ptr++) = '\r'; len --; + *(out_ptr++) = '\n'; len --; + } + if (len > 1) { + *(out_ptr++) = 0; + len --; + } + + httpd_resp_set_status(req, HTTPD_200); + httpd_resp_send(req, out_buffer, strlen(out_buffer)); + + return ESP_OK; +} + +static httpd_uri_t http_params_get_handler = { + .uri = "/params", + .method = HTTP_GET, + .handler = handle_get_params, + .user_ctx = NULL +}; + +httpd_handle_t start_webserver(ws_params_t* params) +{ + httpd_handle_t server = NULL; + httpd_config_t config = HTTPD_DEFAULT_CONFIG(); + config.lru_purge_enable = true; + config.max_uri_handlers = NUMBER_PARAMS + 1; + + ESP_LOGI(TAG, "Starting httpd server on port: '%d'", config.server_port); + if (httpd_start(&server, &config) == ESP_OK) { + + http_params_get_handler.user_ctx = params; + httpd_register_uri_handler(server, &http_params_get_handler); + +#define STATE_PARAM(typ, attr, ...) \ + ESP_LOGI(TAG, "Registering URI handler [%s]", #attr); \ + http_ ## attr ## _handler.user_ctx = params; \ + httpd_register_uri_handler(server, & http_ ## attr ## _handler); +#include "state_params.i" +#undef STATE_PARAM + + return server; + } + + ESP_LOGI(TAG, "Error starting server!"); + return NULL; +} -- cgit