diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2022-12-01 21:29:34 -0700 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2022-12-01 21:29:34 -0700 |
commit | 05e3f6bb7293c771629c7b3599392c8f335e7dd8 (patch) | |
tree | c2a5cd4d27d7673b3a58071c0656f87c92ee9ff3 | |
parent | 8d379bf60e50960f6c855cd6b9c83956a082f73d (diff) | |
download | esp32-ws2812b-05e3f6bb7293c771629c7b3599392c8f335e7dd8.tar.gz esp32-ws2812b-05e3f6bb7293c771629c7b3599392c8f335e7dd8.tar.bz2 esp32-ws2812b-05e3f6bb7293c771629c7b3599392c8f335e7dd8.zip |
Add an HTTP Server.
This server allows the user to control the parameters of the state for
displaying the lights.
-rw-r--r-- | include/http_server.h | 11 | ||||
-rw-r--r-- | include/state_params.i | 21 | ||||
-rw-r--r-- | include/ws2812b_writer.h | 4 | ||||
-rw-r--r-- | main/CMakeLists.txt | 1 | ||||
-rw-r--r-- | main/drv/CMakeLists.txt | 1 | ||||
-rw-r--r-- | main/http_server.c | 222 | ||||
-rw-r--r-- | main/main.c | 10 | ||||
-rw-r--r-- | main/tcp_server.c | 8 | ||||
-rw-r--r-- | main/ws2812b_writer.c | 54 |
9 files changed, 299 insertions, 33 deletions
diff --git a/include/http_server.h b/include/http_server.h new file mode 100644 index 0000000..578b561 --- /dev/null +++ b/include/http_server.h @@ -0,0 +1,11 @@ +#pragma once +#ifndef HTTP_SERVER_H_ +#define HTTP_SERVER_H_ + +#include <esp_log.h> +#include <esp_http_server.h> +#include "ws2812b_writer.h" + +httpd_handle_t start_webserver(ws_params_t* params); + +#endif /* HTTP_SERVER_H_ */ diff --git a/include/state_params.i b/include/state_params.i index 447b72b..15a5b43 100644 --- a/include/state_params.i +++ b/include/state_params.i @@ -1,9 +1,12 @@ -STATE_PARAM(uint32_t, time) -STATE_PARAM(int, timetick) -STATE_PARAM(uint8_t, brightness) -STATE_PARAM(uint8_t, off) -STATE_PARAM(uint8_t, n_snow) -STATE_PARAM(uint8_t, n_red) -STATE_PARAM(bool, sleep) -STATE_PARAM(bool, power) -STATE_PARAM(bool, cool) +// List of parameters. +// type name display name, default value + +STATE_PARAM(uint32_t, time, "Current Time", 0) +STATE_PARAM(int, timetick, "Speed", 100) +STATE_PARAM(uint8_t, brightness, "Brightness", 50) +STATE_PARAM(uint8_t, n_snow, "Snow Effect", 10) +STATE_PARAM(uint8_t, n_red, "Desaturation", 32) +STATE_PARAM(int, x_scale, "X Scale", 255) +STATE_PARAM(bool, power, "Power", true) +STATE_PARAM(bool, cool, "Cool", false) +STATE_PARAM(bool, invert, "Invert", false) diff --git a/include/ws2812b_writer.h b/include/ws2812b_writer.h index 89b4f7a..e8fa84f 100644 --- a/include/ws2812b_writer.h +++ b/include/ws2812b_writer.h @@ -6,11 +6,13 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" +extern const int NUMBER_PARAMS; + typedef struct { ws2812b_t* drv; struct { -#define STATE_PARAM(t, n) t n; +#define STATE_PARAM(t, n, ...) t n; #include "state_params.i" #undef STATE_PARAM } state; diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 9ecb402..5bc9356 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -4,4 +4,5 @@ idf_component_register(SRCS "ws2812b_writer.c" "tcp_server.c" "sockbuf.c" + "http_server.c" INCLUDE_DIRS "../include") diff --git a/main/drv/CMakeLists.txt b/main/drv/CMakeLists.txt index b56f1b0..3d9018f 100644 --- a/main/drv/CMakeLists.txt +++ b/main/drv/CMakeLists.txt @@ -1,2 +1,3 @@ idf_component_register(SRCS "ws2812b.c" + REQUIRES driver INCLUDE_DIRS "../../include") 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; +} diff --git a/main/main.c b/main/main.c index 9cd4f0f..82fff1e 100644 --- a/main/main.c +++ b/main/main.c @@ -6,6 +6,8 @@ #include "freertos/task.h" #include "sdkconfig.h" +#include "station.h" +#include "http_server.h" #include "ws2812b_writer.h" #include "tcp_server.h" @@ -91,7 +93,7 @@ void app_main(void) printf("miso: %d\n", PIN_NUM_MISO); printf("mosi: %d\n", PIN_NUM_MOSI); printf("sclk: %d\n", PIN_NUM_CLK); - printf("Tick period ms: %d\n", portTICK_PERIOD_MS); + printf("Tick period ms: %d\n", (int) portTICK_PERIOD_MS); error = spi_bus_initialize(HSPI_HOST, &cfg, SPI_DMA_CH_AUTO); printf("Bus Init\n"); @@ -105,7 +107,13 @@ void app_main(void) printf("Configuration complete!!\n"); + ESP_LOGI("main", "Connection to AP"); + wifi_init_station("Wort", "JoshIsBau5"); + ESP_LOGI("main", "Complete!"); + xTaskCreate(time_keeper, "time_keeper", 1024, &ws_params, 1, NULL); xTaskCreate(ws2812b_write_task, "ws2812b_writer", 4096, &ws_params, 1, NULL); xTaskCreate(tcp_server, "tcp_server", 4096, &ws_params, 2, NULL); + + start_webserver(&ws_params); } diff --git a/main/tcp_server.c b/main/tcp_server.c index 434f014..2ee7e51 100644 --- a/main/tcp_server.c +++ b/main/tcp_server.c @@ -130,7 +130,7 @@ static void handle_set_cmd( { if (false) {} -#define STATE_PARAM(ty, attr) \ +#define STATE_PARAM(ty, attr, ...) \ else if (!strcmp_(key, #attr)) { \ handle_ ## ty ## _option(#attr, sockbuf, &ws_params->state. attr); \ } @@ -147,7 +147,7 @@ static void handle_set_cmd( static void print_uint32_t(sockbuf_t* sockbuf, const char* attr, uint32_t val) { char buf[128]; - snprintf(buf, sizeof(buf), "%s: %d :: uint32_t\n", attr, val); + snprintf(buf, sizeof(buf), "%s: %ld :: uint32_t\n", attr, val); sockbuf_write(sockbuf, buf); } @@ -174,7 +174,7 @@ static void print_int(sockbuf_t* sockbuf, const char* attr, int val) static void handle_print_cmd(sockbuf_t* sockbuf, ws_params_t* ws_params) { -#define STATE_PARAM(ty, attr) \ +#define STATE_PARAM(ty, attr, ...) \ print_ ## ty ( sockbuf, #attr, ws_params->state. attr ); #include "state_params.i" #undef STATE_PARAM @@ -215,8 +215,6 @@ portTASK_FUNCTION(tcp_server, params) { ws_params_t* ws_params = (ws_params_t*) params; - wifi_init_station("Wort", "JoshIsBau5"); - int s = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); diff --git a/main/ws2812b_writer.c b/main/ws2812b_writer.c index f7176f1..36fa666 100644 --- a/main/ws2812b_writer.c +++ b/main/ws2812b_writer.c @@ -3,6 +3,12 @@ #include <stdlib.h> #include <string.h> +const int NUMBER_PARAMS = +#define STATE_PARAM(...) +1 +#include "state_params.i" +#undef STATE_PARAM +; + #define SIZE 300 /* Returns round(sin(2πn / 256) * 127.5 + 127.5). */ @@ -21,11 +27,11 @@ uint8_t amp(uint8_t in, uint8_t n); void reset_parameters(ws_params_t* params) { memset(¶ms->state, 0, sizeof(params->state)); - params->state.brightness = 32; - params->state.off = 8; - params->state.timetick = 100; - params->state.power = 1; - params->state.n_snow = 2; + +#define STATE_PARAM(type, attr, displ, def, ...) \ + params->state. attr = def; +#include "state_params.i" +#undef STATE_PARAM } void calculate_colors( @@ -33,14 +39,18 @@ void calculate_colors( ws2812b_buffer_t* buffer) { uint32_t red = 0, green = 0, blue = 0; + int time; for (int i = 0; i < SIZE; ++i) { + int x = i * 255 / ws_params->state.x_scale; + time = ws_params->state.time; + if (!ws_params->state.power) { ws2812b_buffer_set_rgb(buffer, i, 0, 0, 0); continue; } - red = byte_scale(byte_sin(ws_params->state.time / 1000 + i * 4), 255 - ws_params->state.n_red) + + red = byte_scale(byte_sin(time / 1000 + x * 4), 255 - ws_params->state.n_red) + ws_params->state.n_red; green = 255 - red; @@ -56,11 +66,11 @@ void calculate_colors( if (ws_params->state.n_snow) { uint32_t white[] = { /* Parallax "snow" */ - ws_params->state.time / 179 + i * 8, - ws_params->state.time / 193 + i * 12, - ws_params->state.time / 211 + i * 16, - (ws_params->state.time) / 233 + i * 8, - // (ws_params->state.time + 128) / 233 + i * 8, + time / 179 + x * 8, + time / 193 + x * 12, + time / 211 + x * 16, + time / 233 + x * 8, + // (ws_params->state.time + 128) / 233 + x * 8, }; for (int i = 0; i < sizeof(white) / sizeof(uint32_t); ++i) { @@ -73,12 +83,22 @@ void calculate_colors( } } - ws2812b_buffer_set_rgb( - buffer, - i, - byte_scale(min(red + whitesum, 255), ws_params->state.brightness), - byte_scale(min(green + whitesum, 255), ws_params->state.brightness), - byte_scale(min(blue + whitesum, 255), ws_params->state.brightness)); + uint8_t brightness = ws_params->state.brightness; + red = min(red + whitesum, 255); + green = min(green + whitesum, 255); + blue = min(blue + whitesum, 255); + + if (ws_params->state.invert) { + red = 255 - red; + green = 255 - green; + blue = 255 - blue; + } + + red = byte_scale(red, brightness); + green = byte_scale(green, brightness); + blue = byte_scale(blue, brightness); + + ws2812b_buffer_set_rgb(buffer, i, red, green, blue); } } |