diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2025-12-07 13:04:02 -0700 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2025-12-07 13:04:02 -0700 |
| commit | 42f4c42676d42619d530db27e7a2a0b76fbf9253 (patch) | |
| tree | 01ebd66f55aeb9a7c2113ebde3508a2cb1f01f5a | |
| parent | 3f8b6e6f34275bdba501e3b1cddab43331b6f569 (diff) | |
| download | esp32-ws2812b-42f4c42676d42619d530db27e7a2a0b76fbf9253.tar.gz esp32-ws2812b-42f4c42676d42619d530db27e7a2a0b76fbf9253.tar.bz2 esp32-ws2812b-42f4c42676d42619d530db27e7a2a0b76fbf9253.zip | |
Clean up how parameters work.
Formalize a poor-man's Parameter "typeclass" and create "instances".
This makes things way less ad-hoc.
| -rw-r--r-- | include/param.h | 27 | ||||
| -rw-r--r-- | include/sockbuf.h | 6 | ||||
| -rw-r--r-- | include/ws2812b_writer.h | 4 | ||||
| -rw-r--r-- | main/CMakeLists.txt | 3 | ||||
| -rw-r--r-- | main/http_server.c | 257 | ||||
| -rw-r--r-- | main/param/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | main/param/bool.c | 81 | ||||
| -rw-r--r-- | main/param/color_int.c | 96 | ||||
| -rw-r--r-- | main/param/int.c | 99 | ||||
| -rw-r--r-- | main/param/uint32_t.c | 43 | ||||
| -rw-r--r-- | main/param/uint8_t.c | 42 | ||||
| -rw-r--r-- | main/sockbuf.c | 29 | ||||
| -rw-r--r-- | main/tcp_server.c | 168 |
13 files changed, 467 insertions, 390 deletions
diff --git a/include/param.h b/include/param.h new file mode 100644 index 0000000..b66b1b1 --- /dev/null +++ b/include/param.h @@ -0,0 +1,27 @@ +#pragma once + +#include <stdint.h> +#include "sockbuf.h" + +typedef struct httpd_req httpd_req_t; + +typedef uint32_t color_int_t; + +#define DECL_PARAMETER_INSTANCE(ty) \ + void handle_option__##ty(const char* attr, sockbuf_t* sockbuf, ty* ptr); \ + size_t serialize_to_json__##ty( \ + char** out, size_t len, const char* attr, const char* display_name, \ + ty value); \ + void http_handle__##ty(httpd_req_t* req, ty* val); \ + void print__##ty(sockbuf_t* sockbuf, const char* attr, ty v); + +#define cmd_handle(ty) handle_option__##ty +#define http_handle(ty) http_handle__##ty +#define serialize_to_json(ty) serialize_to_json__##ty +#define print(ty) print__##ty + +DECL_PARAMETER_INSTANCE(uint8_t); +DECL_PARAMETER_INSTANCE(bool); +DECL_PARAMETER_INSTANCE(uint32_t); +DECL_PARAMETER_INSTANCE(color_int_t); +DECL_PARAMETER_INSTANCE(int); diff --git a/include/sockbuf.h b/include/sockbuf.h index 664e134..0a11c4f 100644 --- a/include/sockbuf.h +++ b/include/sockbuf.h @@ -3,6 +3,7 @@ #define SOCKBUF_H_ #include <inttypes.h> +#include <stdlib.h> #define SOCKBUF_BUFLEN 128 #define SOCKBUF_CLOSED 1 @@ -36,4 +37,9 @@ int peek_char(sockbuf_t* sockbuf); */ int get_char(sockbuf_t* sockbuf); +/* + * Reads a token from the sockbuf. Tokens are separated by spaces. + */ +char* read_token(sockbuf_t* sockbuf, char* into, size_t sz); + #endif /* SOCKBUF_H_ */ diff --git a/include/ws2812b_writer.h b/include/ws2812b_writer.h index b750da6..b0e53d8 100644 --- a/include/ws2812b_writer.h +++ b/include/ws2812b_writer.h @@ -3,15 +3,13 @@ #define WS2812B_WRITER_H_ #include "drv/ws2812b.h" +#include "param.h" #include "esp_err.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/task.h" - extern const int NUMBER_PARAMS; -typedef uint32_t color_int_t; - typedef struct { #define STATE_PARAM(t, n, ...) t n; #include "state_params.i" diff --git a/main/CMakeLists.txt b/main/CMakeLists.txt index 4f7b302..93e4859 100644 --- a/main/CMakeLists.txt +++ b/main/CMakeLists.txt @@ -2,6 +2,9 @@ file(GLOB_RECURSE MAIN_SRCS "${CMAKE_CURRENT_LIST_DIR}/*.c") # Avoid pulling in sources from the nested driver component, which has its own # CMakeLists and registration. list(FILTER MAIN_SRCS EXCLUDE REGEX ".*/drv/.*") +list(FILTER MAIN_SRCS EXCLUDE REGEX ".*/param/.*") +file(GLOB PARAM_SRCS "${CMAKE_CURRENT_LIST_DIR}/param/*.c") +list(APPEND MAIN_SRCS ${PARAM_SRCS}) # Derive a schema hash from the parameter definition so persisted state # automatically invalidates when the schema changes. diff --git a/main/http_server.c b/main/http_server.c index ec8a8aa..745bfa6 100644 --- a/main/http_server.c +++ b/main/http_server.c @@ -1,233 +1,39 @@ #include "http_server.h" -#include "freertos/semphr.h" + #include <stdlib.h> #include <string.h> -#include <strings.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; -} - -static void handle_set_color_int_t(httpd_req_t* req, color_int_t* val) -{ - char buf[128]; - int buf_len = httpd_req_get_url_query_len(req) + 1; +#include "freertos/semphr.h" +#include "param.h" - 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; - } +#define TAG "httpd" - 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) { - char* p = param; - if (*p == '#') { - p++; - } else if (!strncasecmp(p, "0x", 2)) { - p += 2; - } - char* end = NULL; - unsigned long parsed = strtoul(p, &end, 16); - if (end == p || *end != '\0' || parsed > 0xFFFFFFUL) { - ESP_LOGI(TAG, "Invalid request. Invalid color value."); - httpd_resp_set_status(req, HTTPD_400); - return; - } - *val = (color_int_t)parsed; - } - } - } - httpd_resp_set_status(req, HTTPD_204); -} - -#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", "*"); \ - if (xSemaphoreTake(params->state_lock, portMAX_DELAY) != \ - pdTRUE) { \ - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, \ - "lock failed"); \ - return ESP_FAIL; \ - } \ - handle_set_##typ(req, ¶ms->state.attr); \ - ws_state_save(¶ms->state); \ - xSemaphoreGive(params->state_lock); \ - 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, \ +#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", "*"); \ + if (xSemaphoreTake(params->state_lock, portMAX_DELAY) != pdTRUE) { \ + httpd_resp_send_err( \ + req, HTTPD_500_INTERNAL_SERVER_ERROR, "lock failed"); \ + return ESP_FAIL; \ + } \ + http_handle(typ)(req, ¶ms->state.attr); \ + ws_state_save(¶ms->state); \ + xSemaphoreGive(params->state_lock); \ + 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 size_t write_color_int_t_value( - char** out, size_t len, const char* attr, const char* dn, color_int_t value) -{ - size_t newlen = snprintf( - *out, - len, - "\"%s\":{\"type\":\"color\",\"value\":\"%06lx\",\"disp\":\"%s\"}", - attr, - (unsigned long)(value & 0xFFFFFF), - 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", "*"); @@ -252,13 +58,12 @@ static esp_err_t handle_get_params(httpd_req_t* req) 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, snapshot.attr); \ +#define STATE_PARAM(typ, attr, display, ...) \ + if (write_comma && len > 1) { \ + *(out_ptr++) = ','; \ + len--; \ + } \ + len = serialize_to_json(typ)(&out_ptr, len, #attr, display, snapshot.attr); \ write_comma = true; #include "state_params.i" #undef STATE_PARAM diff --git a/main/param/CMakeLists.txt b/main/param/CMakeLists.txt new file mode 100644 index 0000000..614a7e3 --- /dev/null +++ b/main/param/CMakeLists.txt @@ -0,0 +1,2 @@ +file(GLOB PARAM_SRCS "${CMAKE_CURRENT_LIST_DIR}/*.c") +set(PARAM_SRCS ${PARAM_SRCS} PARENT_SCOPE) diff --git a/main/param/bool.c b/main/param/bool.c new file mode 100644 index 0000000..46b6ebb --- /dev/null +++ b/main/param/bool.c @@ -0,0 +1,81 @@ +#include "param.h" +#include "http_server.h" + +void cmd_handle(bool)(const char* attr, sockbuf_t* sockbuf, bool* ptr) +{ + char buf_[32]; + char* op = read_token(sockbuf, buf_, sizeof(buf_)); + + bool curval = *ptr; + if (!strcmp(op, "on")) { + curval = 1; + } else if (!strcmp(op, "off")) { + curval = 0; + } else if (!strcmp(op, "toggle")) { + curval = !curval; + } else { + sockbuf_write(sockbuf, "Invalid value: '"); + sockbuf_write(sockbuf, op); + sockbuf_write(sockbuf, "'\n"); + return; + } + + *ptr = curval; + snprintf(buf_, sizeof(buf_), "%s = %s\n", attr, curval ? "on" : "off"); + sockbuf_write(sockbuf, buf_); +} + +size_t serialize_to_json(bool)( + 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; +} + +#define TAG "serialize" +void http_handle(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); +} + +void print(bool)(sockbuf_t* sockbuf, const char* attr, bool val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%s: %s :: bool\n", attr, val ? "on" : "off"); + sockbuf_write(sockbuf, buf); +} diff --git a/main/param/color_int.c b/main/param/color_int.c new file mode 100644 index 0000000..0b8b002 --- /dev/null +++ b/main/param/color_int.c @@ -0,0 +1,96 @@ +#include "param.h" +#include "http_server.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <strings.h> + +void cmd_handle(color_int_t)( + const char* attr, sockbuf_t* sockbuf, color_int_t* ptr) +{ + char buf_[32]; + char* value = read_token(sockbuf, buf_, sizeof(buf_)); + + if (!value) { + sockbuf_write(sockbuf, "Missing color value\n"); + return; + } + + char* p = value; + if (*p == '#') { + p++; + } else if (!strncasecmp(p, "0x", 2)) { + p += 2; + } + + char* end = NULL; + unsigned long parsed = strtoul(p, &end, 16); + if (end == p || *end != '\0' || parsed > 0xFFFFFFUL) { + sockbuf_write(sockbuf, "Invalid color; expected hex like RRGGBB\n"); + return; + } + + *ptr = (color_int_t)parsed; + snprintf(buf_, sizeof(buf_), "%s = 0x%06lx\n", attr, parsed); + sockbuf_write(sockbuf, buf_); +} + +size_t serialize_to_json(color_int_t)( + char** out, size_t len, const char* attr, const char* dn, color_int_t value) +{ + size_t newlen = snprintf( + *out, + len, + "\"%s\":{\"type\":\"color\",\"value\":\"#%06lx\",\"disp\":\"%s\"}", + attr, + (unsigned long)(value & 0xFFFFFF), + dn); + *out += newlen; + return len - newlen; +} + +#define TAG "param_color" +void http_handle(color_int_t)(httpd_req_t* req, color_int_t* 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) { + char* p = param; + if (*p == '#') { + p++; + } else if (!strncasecmp(p, "0x", 2)) { + p += 2; + } + char* end = NULL; + unsigned long parsed = strtoul(p, &end, 16); + if (end == p || *end != '\0' || parsed > 0xFFFFFFUL) { + ESP_LOGI(TAG, "Invalid request. Invalid color value."); + httpd_resp_set_status(req, HTTPD_400); + return; + } + *val = (color_int_t)parsed; + } + } + } + + httpd_resp_set_status(req, HTTPD_204); +} + +void print(color_int_t)( + sockbuf_t* sockbuf, const char* attr, color_int_t val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%s: 0x%06lx :: color\n", attr, (unsigned long)val); + sockbuf_write(sockbuf, buf); +} diff --git a/main/param/int.c b/main/param/int.c new file mode 100644 index 0000000..758180a --- /dev/null +++ b/main/param/int.c @@ -0,0 +1,99 @@ +#include "param.h" +#include "http_server.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void cmd_handle(int)(const char* attr, sockbuf_t* sockbuf, int* ptr) +{ + char op_[8]; + char buf_[32]; + + char* op = read_token(sockbuf, op_, sizeof(op_)); + char* value = read_token(sockbuf, buf_, sizeof(buf_)); + + int curval = *ptr; + int ivalue = value ? atoi(value) : 0; + if (!strcmp(op, "+=")) { + curval += ivalue; + } else if (!strcmp(op, "-=")) { + curval -= ivalue; + } else if (!strcmp(op, "=")) { + curval = ivalue; + } else { + sockbuf_write(sockbuf, "Unknown operation: '"); + sockbuf_write(sockbuf, op); + sockbuf_write(sockbuf, "'\n"); + return; + } + + snprintf(buf_, sizeof(buf_), "%s = %d\n", attr, curval); + *ptr = curval; + sockbuf_write(sockbuf, buf_); +} + +size_t serialize_to_json(int)( + 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; +} + +#define TAG "param_int" +void http_handle(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); +} + +void print(int)(sockbuf_t* sockbuf, const char* attr, int val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%s: %d :: int\n", attr, val); + sockbuf_write(sockbuf, buf); +} diff --git a/main/param/uint32_t.c b/main/param/uint32_t.c new file mode 100644 index 0000000..2c256c1 --- /dev/null +++ b/main/param/uint32_t.c @@ -0,0 +1,43 @@ +#include "param.h" +#include "http_server.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void cmd_handle(uint32_t)( + const char* attr, sockbuf_t* sockbuf, uint32_t* ptr) +{ + int tmp = (int)(*ptr); + cmd_handle(int)(attr, sockbuf, &tmp); + *ptr = (uint32_t)tmp; +} + +size_t serialize_to_json(uint32_t)( + 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; +} + +#define TAG "param_u32" +void http_handle(uint32_t)(httpd_req_t* req, uint32_t* val) +{ + int tmp = (int)(*val); + http_handle(int)(req, &tmp); + *val = (uint32_t)tmp; +} + +void print(uint32_t)(sockbuf_t* sockbuf, const char* attr, uint32_t val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%s: %lu :: uint32_t\n", attr, (unsigned long)val); + sockbuf_write(sockbuf, buf); +} diff --git a/main/param/uint8_t.c b/main/param/uint8_t.c new file mode 100644 index 0000000..6d8e01a --- /dev/null +++ b/main/param/uint8_t.c @@ -0,0 +1,42 @@ +#include "param.h" +#include "http_server.h" + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +void cmd_handle(uint8_t)(const char* attr, sockbuf_t* sockbuf, uint8_t* ptr) +{ + int tmp = (int)(*ptr); + cmd_handle(int)(attr, sockbuf, &tmp); + *ptr = (uint8_t)tmp; +} + +size_t serialize_to_json(uint8_t)( + 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; +} + +#define TAG "param_u8" +void http_handle(uint8_t)(httpd_req_t* req, uint8_t* val) +{ + int tmp = (int)(*val); + http_handle(int)(req, &tmp); + *val = (uint8_t)tmp; +} + +void print(uint8_t)(sockbuf_t* sockbuf, const char* attr, uint8_t val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%s: %u :: uint8_t\n", attr, (unsigned int)val); + sockbuf_write(sockbuf, buf); +} diff --git a/main/sockbuf.c b/main/sockbuf.c index 5b88393..8ae413c 100644 --- a/main/sockbuf.c +++ b/main/sockbuf.c @@ -1,5 +1,6 @@ #include "sockbuf.h" +#include <ctype.h> #include <string.h> #include <unistd.h> @@ -41,3 +42,31 @@ int get_char(sockbuf_t* sockbuf) return ret; } + +char* read_token(sockbuf_t* sockbuf, char* into, size_t sz) +{ + int cur = 0; + + int ch = get_char(sockbuf); + + while (ch >= 0 && isspace(ch)) { + ch = get_char(sockbuf); + } + + while (ch >= 0 && !isspace(ch)) { + if (sz > 1) { + into[cur++] = (char)ch; + sz--; + } + ch = get_char(sockbuf); + } + + if (ch < 0) { + return NULL; + } + + if (sz >= 1) { + into[cur] = 0; + } + return into; +} diff --git a/main/tcp_server.c b/main/tcp_server.c index a8c4db9..5899a36 100644 --- a/main/tcp_server.c +++ b/main/tcp_server.c @@ -5,6 +5,7 @@ #include "freertos/semphr.h" #include "lwip/sockets.h" +#include "param.h" #include "sockbuf.h" #include "station.h" @@ -21,125 +22,6 @@ static int strcmp_(const char* s1, const char* s2) return strcmp(s1, s2); } -static char* read_token(sockbuf_t* sockbuf, char* into, size_t sz) -{ - int cur = 0; - - int ch = get_char(sockbuf); - - while (ch >= 0 && isspace(ch)) { - ch = get_char(sockbuf); - } - - while (ch >= 0 && !isspace(ch)) { - if (sz > 1) { - into[cur++] = (char)ch; - sz--; - } - ch = get_char(sockbuf); - } - - if (ch < 0) { - return NULL; - } - - if (sz >= 1) { - into[cur] = 0; - } - return into; -} - -static void handle_int_option(const char* attr, sockbuf_t* sockbuf, int* ptr) -{ - char op_[8]; - char buf_[32]; - - char* op = read_token(sockbuf, op_, sizeof(op_)); - char* value = read_token(sockbuf, buf_, sizeof(buf_)); - - int curval = *ptr; - int ivalue = atoi(value); - if (!strcmp_(op, "+=")) { - curval += ivalue; - } else if (!strcmp_(op, "-=")) { - curval -= ivalue; - } else if (!strcmp_(op, "=")) { - curval = ivalue; - } else { - sockbuf_write(sockbuf, "Unknown operation: '"); - sockbuf_write(sockbuf, op); - sockbuf_write(sockbuf, "'\n"); - return; - } - - snprintf(buf_, sizeof(buf_), "%s = %d\n", attr, curval); - *ptr = curval; - sockbuf_write(sockbuf, buf_); -} - -static void handle_uint8_t_option( - const char* attr, sockbuf_t* sockbuf, uint8_t* ptr) -{ - int tmp = *ptr; - handle_int_option(attr, sockbuf, &tmp); - *ptr = tmp; -} - -static void handle_uint32_t_option( - const char* attr, sockbuf_t* sockbuf, uint32_t* ptr) -{ - int tmp = *ptr; - handle_int_option(attr, sockbuf, &tmp); - *ptr = tmp; -} - -static void handle_color_int_t_option( - const char* attr, sockbuf_t* sockbuf, color_int_t* ptr) -{ - char buf_[32]; - char* value = read_token(sockbuf, buf_, sizeof(buf_)); - - if (!value) { - sockbuf_write(sockbuf, "Missing color value\n"); - return; - } - - char* end = NULL; - unsigned long parsed = strtoul(value, &end, 16); - if (end == value || *end != '\0' || parsed > 0xFFFFFFUL) { - sockbuf_write(sockbuf, "Invalid color; expected hex like RRGGBB\n"); - return; - } - - *ptr = (color_int_t)parsed; - snprintf(buf_, sizeof(buf_), "%s = 0x%06lx\n", attr, parsed); - sockbuf_write(sockbuf, buf_); -} - -static void handle_bool_option(const char* attr, sockbuf_t* sockbuf, bool* ptr) -{ - char buf_[32]; - char* op = read_token(sockbuf, buf_, sizeof(buf_)); - - bool curval = *ptr; - if (!strcmp(op, "on")) { - curval = 1; - } else if (!strcmp(op, "off")) { - curval = 0; - } else if (!strcmp(op, "toggle")) { - curval = !curval; - } else { - sockbuf_write(sockbuf, "Invalid value: '"); - sockbuf_write(sockbuf, op); - sockbuf_write(sockbuf, "'\n"); - return; - } - - *ptr = curval; - snprintf(buf_, sizeof(buf_), "%s = %s\n", attr, curval ? "on" : "off"); - sockbuf_write(sockbuf, buf_); -} - static void handle_set_cmd( sockbuf_t* sockbuf, ws_params_t* ws_params, const char* key) { @@ -151,11 +33,11 @@ static void handle_set_cmd( bool handled = false; if (false) { } -#define STATE_PARAM(ty, attr, ...) \ - else if (!strcmp_(key, #attr)) \ - { \ - handle_##ty##_option(#attr, sockbuf, &ws_params->state.attr); \ - handled = true; \ +#define STATE_PARAM(ty, attr, ...) \ + else if (!strcmp_(key, #attr)) \ + { \ + cmd_handle(ty)(#attr, sockbuf, &ws_params->state.attr); \ + handled = true; \ } #include "state_params.i" #undef STATE_PARAM @@ -171,42 +53,6 @@ static void handle_set_cmd( xSemaphoreGive(ws_params->state_lock); } -static void print_uint32_t(sockbuf_t* sockbuf, const char* attr, uint32_t val) -{ - char buf[128]; - snprintf(buf, sizeof(buf), "%s: %ld :: uint32_t\n", attr, val); - sockbuf_write(sockbuf, buf); -} - -static void print_uint8_t(sockbuf_t* sockbuf, const char* attr, uint8_t val) -{ - char buf[128]; - snprintf(buf, sizeof(buf), "%s: %d :: uint8_t\n", attr, (int)val); - sockbuf_write(sockbuf, buf); -} - -static void print_bool(sockbuf_t* sockbuf, const char* attr, bool val) -{ - char buf[128]; - snprintf(buf, sizeof(buf), "%s: %s :: bool\n", attr, val ? "on" : "off"); - sockbuf_write(sockbuf, buf); -} - -static void print_int(sockbuf_t* sockbuf, const char* attr, int val) -{ - char buf[128]; - snprintf(buf, sizeof(buf), "%s: %d :: int\n", attr, val); - sockbuf_write(sockbuf, buf); -} - -static void print_color_int_t( - sockbuf_t* sockbuf, const char* attr, color_int_t val) -{ - char buf[128]; - snprintf(buf, sizeof(buf), "%s: 0x%06lx :: color\n", attr, (unsigned long)val); - sockbuf_write(sockbuf, buf); -} - static void handle_print_cmd(sockbuf_t* sockbuf, ws_params_t* ws_params) { ws_state_t snapshot; @@ -217,7 +63,7 @@ static void handle_print_cmd(sockbuf_t* sockbuf, ws_params_t* ws_params) snapshot = ws_params->state; xSemaphoreGive(ws_params->state_lock); -#define STATE_PARAM(ty, attr, ...) print_##ty(sockbuf, #attr, snapshot.attr); +#define STATE_PARAM(ty, attr, ...) print(ty)(sockbuf, #attr, snapshot.attr); #include "state_params.i" #undef STATE_PARAM } |