From a4930f91ced80d26758fae30f3e01eee7abe4345 Mon Sep 17 00:00:00 2001 From: Josh Rahm Date: Sun, 7 Dec 2025 16:59:51 -0700 Subject: Add a couple different parameter types. fraction_t: semantically represents a value 0.0-1.0. Internally this is represented as a uint8 with 255 being 1 and 0 being 0. ratio_t: represents a fracitonal number that can be larger than 1.0. --- main/param/fraction.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++ main/param/ratio.c | 139 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 278 insertions(+) create mode 100644 main/param/fraction.c create mode 100644 main/param/ratio.c (limited to 'main/param') diff --git a/main/param/fraction.c b/main/param/fraction.c new file mode 100644 index 0000000..adfc769 --- /dev/null +++ b/main/param/fraction.c @@ -0,0 +1,139 @@ +#include "param.h" +#include "http_server.h" + +#include +#include +#include +#include + +static inline uint8_t clamp_u8(int v) +{ + if (v < 0) return 0; + if (v > 255) return 255; + return (uint8_t)v; +} + +static int parse_fraction(const char* s, fraction_t* out) +{ + if (!s) return -1; + char* end = NULL; + float f = strtof(s, &end); + if (end == s) { + return -1; + } + *out = clamp_u8((int)lroundf(f * 255.0f)); + return 0; +} + +void cmd_handle(fraction_t)(const char* attr, sockbuf_t* sockbuf, fraction_t* ptr) +{ + char op_[8]; + char buf_[32]; + + char* op = read_token(sockbuf, op_, sizeof(op_)); + char* value = read_token(sockbuf, buf_, sizeof(buf_)); + + fraction_t curval = *ptr; + fraction_t parsed = 0; + + if (!strcmp(op, "+=") || !strcmp(op, "-=") || !strcmp(op, "=")) { + if (parse_fraction(value, &parsed) != 0) { + sockbuf_write(sockbuf, "Invalid value\n"); + return; + } + if (!strcmp(op, "+=")) { + curval = clamp_u8((int)curval + (int)parsed); + } else if (!strcmp(op, "-=")) { + curval = clamp_u8((int)curval - (int)parsed); + } else { + curval = parsed; + } + } else { + sockbuf_write(sockbuf, "Unknown operation: '"); + sockbuf_write(sockbuf, op); + sockbuf_write(sockbuf, "'\n"); + return; + } + + *ptr = curval; + snprintf(buf_, sizeof(buf_), "%s = %.3f\n", attr, curval / 255.0f); + sockbuf_write(sockbuf, buf_); +} + +size_t serialize_to_json(fraction_t)( + char** out, size_t len, const char* attr, const char* dn, fraction_t value) +{ + float fval = value / 255.0f; + size_t newlen = snprintf( + *out, + len, + "\"%s\":{\"type\":\"fraction\",\"value\":\"%.4f\",\"disp\":\"%s\"}", + attr, + fval, + dn); + *out += newlen; + return len - newlen; +} + +#define TAG "param_fraction" +void http_handle(fraction_t)(httpd_req_t* req, fraction_t* 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]; + fraction_t parsed; + if (httpd_query_key_value(buf, "add", param, sizeof(param)) == ESP_OK) { + if (parse_fraction(param, &parsed) != 0) { + httpd_resp_set_status(req, HTTPD_400); + return; + } + *val = clamp_u8((int)(*val) + (int)parsed); + } else if ( + httpd_query_key_value(buf, "set", param, sizeof(param)) == ESP_OK) { + if (parse_fraction(param, &parsed) != 0) { + httpd_resp_set_status(req, HTTPD_400); + return; + } + *val = parsed; + } else if ( + httpd_query_key_value(buf, "sub", param, sizeof(param)) == ESP_OK) { + if (parse_fraction(param, &parsed) != 0) { + httpd_resp_set_status(req, HTTPD_400); + return; + } + *val = clamp_u8((int)(*val) - (int)parsed); + } 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; + } + + httpd_resp_set_status(req, HTTPD_204); +} + +void print(fraction_t)(sockbuf_t* sockbuf, const char* attr, fraction_t val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%s: %.3f :: fraction\n", attr, val / 255.0f); + sockbuf_write(sockbuf, buf); +} diff --git a/main/param/ratio.c b/main/param/ratio.c new file mode 100644 index 0000000..bd7e3f4 --- /dev/null +++ b/main/param/ratio.c @@ -0,0 +1,139 @@ +#include "param.h" +#include "http_server.h" + +#include +#include +#include +#include + +static inline uint16_t clamp_u16(int v) +{ + if (v < 0) return 0; + if (v > 65535) return 65535; + return (uint16_t)v; +} + +static int parse_ratio(const char* s, ratio_t* out) +{ + if (!s) return -1; + char* end = NULL; + float f = strtof(s, &end); + if (end == s) { + return -1; + } + *out = clamp_u16((int)lroundf(f * 255.0f)); + return 0; +} + +void cmd_handle(ratio_t)(const char* attr, sockbuf_t* sockbuf, ratio_t* ptr) +{ + char op_[8]; + char buf_[32]; + + char* op = read_token(sockbuf, op_, sizeof(op_)); + char* value = read_token(sockbuf, buf_, sizeof(buf_)); + + ratio_t curval = *ptr; + ratio_t parsed = 0; + + if (!strcmp(op, "+=") || !strcmp(op, "-=") || !strcmp(op, "=")) { + if (parse_ratio(value, &parsed) != 0) { + sockbuf_write(sockbuf, "Invalid value\n"); + return; + } + if (!strcmp(op, "+=")) { + curval = clamp_u16((int)curval + (int)parsed); + } else if (!strcmp(op, "-=")) { + curval = clamp_u16((int)curval - (int)parsed); + } else { + curval = parsed; + } + } else { + sockbuf_write(sockbuf, "Unknown operation: '"); + sockbuf_write(sockbuf, op); + sockbuf_write(sockbuf, "'\n"); + return; + } + + *ptr = curval; + snprintf(buf_, sizeof(buf_), "%s = %.3f\n", attr, curval / 255.0f); + sockbuf_write(sockbuf, buf_); +} + +size_t serialize_to_json(ratio_t)( + char** out, size_t len, const char* attr, const char* dn, ratio_t value) +{ + float fval = value / 255.0f; + size_t newlen = snprintf( + *out, + len, + "\"%s\":{\"type\":\"ratio\",\"value\":\"%.4f\",\"disp\":\"%s\"}", + attr, + fval, + dn); + *out += newlen; + return len - newlen; +} + +#define TAG "param_ratio" +void http_handle(ratio_t)(httpd_req_t* req, ratio_t* 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]; + ratio_t parsed; + if (httpd_query_key_value(buf, "add", param, sizeof(param)) == ESP_OK) { + if (parse_ratio(param, &parsed) != 0) { + httpd_resp_set_status(req, HTTPD_400); + return; + } + *val = clamp_u16((int)(*val) + (int)parsed); + } else if ( + httpd_query_key_value(buf, "set", param, sizeof(param)) == ESP_OK) { + if (parse_ratio(param, &parsed) != 0) { + httpd_resp_set_status(req, HTTPD_400); + return; + } + *val = parsed; + } else if ( + httpd_query_key_value(buf, "sub", param, sizeof(param)) == ESP_OK) { + if (parse_ratio(param, &parsed) != 0) { + httpd_resp_set_status(req, HTTPD_400); + return; + } + *val = clamp_u16((int)(*val) - (int)parsed); + } 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; + } + + httpd_resp_set_status(req, HTTPD_204); +} + +void print(ratio_t)(sockbuf_t* sockbuf, const char* attr, ratio_t val) +{ + char buf[128]; + snprintf(buf, sizeof(buf), "%s: %.3f :: ratio\n", attr, val / 255.0f); + sockbuf_write(sockbuf, buf); +} -- cgit