aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJosh Rahm <joshuarahm@gmail.com>2025-12-07 13:04:02 -0700
committerJosh Rahm <joshuarahm@gmail.com>2025-12-07 13:04:02 -0700
commit42f4c42676d42619d530db27e7a2a0b76fbf9253 (patch)
tree01ebd66f55aeb9a7c2113ebde3508a2cb1f01f5a
parent3f8b6e6f34275bdba501e3b1cddab43331b6f569 (diff)
downloadesp32-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.h27
-rw-r--r--include/sockbuf.h6
-rw-r--r--include/ws2812b_writer.h4
-rw-r--r--main/CMakeLists.txt3
-rw-r--r--main/http_server.c257
-rw-r--r--main/param/CMakeLists.txt2
-rw-r--r--main/param/bool.c81
-rw-r--r--main/param/color_int.c96
-rw-r--r--main/param/int.c99
-rw-r--r--main/param/uint32_t.c43
-rw-r--r--main/param/uint8_t.c42
-rw-r--r--main/sockbuf.c29
-rw-r--r--main/tcp_server.c168
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, &params->state.attr); \
- ws_state_save(&params->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, &params->state.attr); \
+ ws_state_save(&params->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
}