diff options
| author | Josh Rahm <joshuarahm@gmail.com> | 2025-12-07 16:59:51 -0700 |
|---|---|---|
| committer | Josh Rahm <joshuarahm@gmail.com> | 2025-12-07 16:59:51 -0700 |
| commit | a4930f91ced80d26758fae30f3e01eee7abe4345 (patch) | |
| tree | 7814e178c3a13b7f3bbc03dd5ad4d97af744baf8 /main | |
| parent | 44c7f7a675d2cdb0281322f38be3227ef4fb1df2 (diff) | |
| download | esp32-ws2812b-a4930f91ced80d26758fae30f3e01eee7abe4345.tar.gz esp32-ws2812b-a4930f91ced80d26758fae30f3e01eee7abe4345.tar.bz2 esp32-ws2812b-a4930f91ced80d26758fae30f3e01eee7abe4345.zip | |
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.
Diffstat (limited to 'main')
| -rw-r--r-- | main/param/fraction.c | 139 | ||||
| -rw-r--r-- | main/param/ratio.c | 139 | ||||
| -rw-r--r-- | main/ws2812b_writer.c | 10 |
3 files changed, 284 insertions, 4 deletions
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 <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +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 <math.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +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); +} diff --git a/main/ws2812b_writer.c b/main/ws2812b_writer.c index 90c7405..63caf30 100644 --- a/main/ws2812b_writer.c +++ b/main/ws2812b_writer.c @@ -155,7 +155,8 @@ struct rgba interpolate(struct rgba c1, struct rgba c2, uint8_t amt) void calculate_colors(const ws_state_t* state, ws2812b_buffer_t* buffer) { for (int i = 0; i < SIZE; ++i) { - int x = i * 255 / state->x_scale; + int x_scale = state->x_scale ? state->x_scale : 1; + int x = i * 255 / x_scale; if (!state->power) { ws2812b_buffer_set_rgb(buffer, i, 0, 0, 0); @@ -164,12 +165,13 @@ void calculate_colors(const ws_state_t* state, ws2812b_buffer_t* buffer) struct rgba c; int64_t real_time = state->time * FRAME_TIME_DELTA; - if (state->speed % 1000 == 0) { + int32_t speed_scaled = ((int32_t)state->speed * 1000) / 255; + if (speed_scaled % 1000 == 0) { // speed = 1000 is the "canonical" time speed. If speed is divisible by // 1000, then no interpolation is required. - c = get_rgba(x, (real_time * state->speed) / 1000, state); + c = get_rgba(x, (real_time * speed_scaled) / 1000, state); } else { - int64_t adj_time = real_time * state->speed; + int64_t adj_time = real_time * speed_scaled; int64_t bot = adj_time / 1000; int64_t top = adj_time / 1000 + FRAME_TIME_DELTA * (adj_time >= 0 ? 1 : -1); |