Compare commits

...

3 Commits

3 changed files with 132 additions and 38 deletions

View File

@ -10,10 +10,10 @@
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <unistd.h> #include <unistd.h>
#include "shared.h"
#include "esp8266/gpio_register.h" #include "esp8266/gpio_register.h"
#include "esp_system.h" #include "esp_system.h"
#include "rom/ets_sys.h" #include "rom/ets_sys.h"
#include "shared.h"
#include "driver/gpio.h" #include "driver/gpio.h"
// pack the struct to match exactly 8 * 4 = 32bits // pack the struct to match exactly 8 * 4 = 32bits
@ -36,6 +36,14 @@ union led_t {
struct gradient_point_t { struct gradient_point_t {
union led_t led; // value of the led at this point union led_t led; // value of the led at this point
size_t offset; // offset (measured in leds) from the beginning size_t offset; // offset (measured in leds) from the beginning
short movement; // direction of movement over time
};
struct gradient_t {
struct gradient_point_t points[16]; // array of gradient points, support at most 16 points in a gradient
size_t points_len; // number of used gradient points
float duration; // amount of time to allow this gradient to last
// positive means an amount in second 0 or negative means indefinitely until further notice
}; };
// buffer that will be written out to the led strip over serial // buffer that will be written out to the led strip over serial
@ -43,6 +51,9 @@ uint32_t g_serial_out_buffer[62];
// 60-long slice of the out buffer that represents the first few leds // 60-long slice of the out buffer that represents the first few leds
union led_t* g_leds = ((union led_t*)g_serial_out_buffer + 1); union led_t* g_leds = ((union led_t*)g_serial_out_buffer + 1);
struct gradient_t g_default_gradient;
struct gradient_t g_current_gradient;
int g_leds_are_default = 1;
#define CLOCK 4 #define CLOCK 4
#define DATA 5 #define DATA 5
@ -139,20 +150,27 @@ void set_led_range(int start, int end, union led_t value) {
} }
static static
void leds_set_gradient(struct gradient_point_t* points, size_t points_len, int send) { void leds_set_default_gradient(const struct gradient_t* gradient) {
struct gradient_point_t from = points[0]; g_default_gradient = *gradient;
}
static
void leds_set_current_gradient(const struct gradient_t* gradient, int defer_send) {
struct gradient_point_t from = gradient->points[0];
struct gradient_point_t to; struct gradient_point_t to;
// set_led_range(0, points[0].offset, points[0].led); set_led_range(0, gradient->points[0].offset, gradient->points[0].led);
// set_led_range(points[points_len-1].offset, 60, points[points_len-1].led); set_led_range(gradient->points[gradient->points_len-1].offset, 60, gradient->points[gradient->points_len-1].led);
for(int i = 1; i < points_len; ++i) { g_current_gradient = *gradient;
to = points[i];
for(int i = 1; i < gradient->points_len; ++i) {
to = gradient->points[i];
lerp_points_between(from, to); lerp_points_between(from, to);
from = to; from = to;
} }
if(send) { if(!defer_send) {
send_leds(); send_leds();
} }
} }

View File

@ -7,6 +7,9 @@
// Where l is the number of points on a gradient. And each point of the gradient has a r* g* b* a* and t* where * is the index. // Where l is the number of points on a gradient. And each point of the gradient has a r* g* b* a* and t* where * is the index.
// r g and b are the red green and blue 8-bit colour components of a point on the gradient. A is the 5-bit global component of the led at that point. // r g and b are the red green and blue 8-bit colour components of a point on the gradient. A is the 5-bit global component of the led at that point.
// t is the offset from the start measured in leds. // t is the offset from the start measured in leds.
// Each point also has an optional &m argument for movement. &m should be either -1, +1 or 0 and represents the movement per frame or the point.
// &m arguments are only valid if the global &d variable is also defined.
// So if &d is defined (say, &d=3) a point on the gradient will have &r, &g, &b, &a, &t, and &m. And the global variables will be &l and &d
// //
/// ///
@ -27,75 +30,139 @@
static static
httpd_handle_t g_http_server = NULL; httpd_handle_t g_http_server = NULL;
struct parse_error_t {
const char* error;
};
// Parse a gradient query // Parse a gradient query
static static
void parse_leds_query(char* query_string, size_t query_size) { struct parse_error_t parse_leds_query(char* query_string, size_t query_size) {
char query_value[16]; char query_value[16];
char query_key[3]; char query_key[3];
size_t gradient_point_count = 0; struct gradient_t gradient;
// Fetch the &l length parameter.
// Interpret as a positive integer number of points on the gradient.
if(httpd_query_key_value(query_string, "l", query_value, sizeof(query_value)) == ESP_OK) { if(httpd_query_key_value(query_string, "l", query_value, sizeof(query_value)) == ESP_OK) {
gradient_point_count = atoi(query_value); gradient.points_len = max(0, atoi(query_value));
} else { } else {
return; leds_set_current_gradient(&g_default_gradient, 0);
return (struct parse_error_t) {
.error = "ERROR: Failed to find length parameter &l"
};
} }
struct gradient_point_t* points = malloc(gradient_point_count * sizeof(struct gradient_point_t)); // Get the &d 'duration' parameter from the query.
// Interpreted as a floating point number of seconds before returning to default state.
if(httpd_query_key_value(query_string, "d", query_value, sizeof(query_value)) == ESP_OK) {
gradient.duration = atof(query_string);
} else {
gradient.duration = 0;
}
for(int point = 0; point < gradient_point_count; ++point) { LOGLN("Reading %zu points of gradient query:", gradient.points_len);
LOGLN("duration: %f", gradient.duration);
// Get the gradient point components for every point that was promised by the &l parameter
for(int point = 0; point < gradient.points_len; ++point) {
// Get the r, g, and b components as 8 bit integers
sprintf(query_key, "r%d", point); sprintf(query_key, "r%d", point);
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) { if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) {
points[point].led.components.red = atoi(query_value); gradient.points[point].led.components.red = atoi(query_value);
} else {
return (struct parse_error_t) {
.error = "ERROR: Point missing red component &r."
};
} }
sprintf(query_key, "g%d", point); sprintf(query_key, "g%d", point);
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) { if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) {
points[point].led.components.green = atoi(query_value); gradient.points[point].led.components.green = atoi(query_value);
} else {
return (struct parse_error_t) {
.is_error = 1,
.error = "ERROR: Point missing green component &g."
};
} }
sprintf(query_key, "b%d", point); sprintf(query_key, "b%d", point);
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) { if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
points[point].led.components.blue = atoi(query_value); gradient.points[point].led.components.blue = atoi(query_value);
} else {
return (struct parse_error_t) {
.error = "ERROR: Point missing blue component &b."
};
} }
// Get the global variable, passed as alpha. Limited to 0-32 (a 5bit unsigned int)
// Make sure the most significant 3 bits are all ones
sprintf(query_key, "a%d", point); sprintf(query_key, "a%d", point);
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) { if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
points[point].led.components.global = GLOBAL(atoi(query_value)); gradient.points[point].led.components.global = GLOBAL((uint8_t)atoi(query_value));
} else {
return (struct parse_error_t) {
.error = "ERROR: Point missing alpha component &a."
};
} }
// Get the time of the gradient as a number ranging from 0 - 60.
// Interpreted as an integer offset from the first led to the last
sprintf(query_key, "t%d", point); sprintf(query_key, "t%d", point);
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) { if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
points[point].offset = atoi(query_value); gradient.points[point].offset = clamp(0, 60, atoi(query_value));
} else {
return (struct parse_error_t) {
.error = "ERROR: Point missing time component &t."
};
}
// Get the movement variable &m.
// Interpreted as an integer number from -1 to +1
sprintf(query_key, "m%d", point);
if(gradient.duration > 0 && httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
gradient.points[point].movement = clamp(-1, +1, atoi(query_value));
} else {
gradient.points[point].movement = 0;
} }
LOGLN("led %d:", point); // Log fetched fields
LOGLN(" r %d", points[point].led.components.red); LOGLN("led[%d]:", point);
LOGLN(" g %d", points[point].led.components.green); LOGLN(" r %d", gradient.points[point].led.components.red);
LOGLN(" b %d", points[point].led.components.blue); LOGLN(" g %d", gradient.points[point].led.components.green);
LOGLN(" global %d", points[point].led.components.global >> 3); LOGLN(" b %d", gradient.points[point].led.components.blue);
LOGLN(" t %d", (int)points[point].offset); LOGLN(" global %d", gradient.points[point].led.components.global >> 3);
LOGLN(" t %zu", gradient.points[point].offset);
} }
leds_set_gradient(points, gradient_point_count, 1); leds_set_current_gradient(&gradient, 0);
return (struct parse_error_t) { .error = NULL };
} }
// receives HTTP GET requests on root // receives HTTP GET requests on root
static static
esp_err_t on_http_get_root(httpd_req_t* request) { esp_err_t on_http_get_root(httpd_req_t* request) {
// Error mewsages for cases of good and bad
static const char* response_ok = "OK!";
const char* response_msg = response_ok;
LOGLN("POST received on '/'."); LOGLN("POST received on '/'.");
char* buffer; // buffer for query string
size_t buffer_len; char* query_buffer;
size_t query_length = httpd_req_get_url_query_len(request) + 1;
struct parse_error_t result = { .error = NULL };
buffer_len = httpd_req_get_url_query_len(request) + 1; if(query_length > 1) {
query_buffer = malloc(query_length * sizeof(char));
if(buffer_len > 1) { if(httpd_req_get_url_query_str(request, query_buffer, query_length) == ESP_OK) {
buffer = malloc(buffer_len * sizeof(char));
if(httpd_req_get_url_query_str(request, buffer, buffer_len) == ESP_OK) {
LOGLN("Received query."); LOGLN("Received query.");
parse_leds_query(buffer, buffer_len); result = parse_leds_query(query_buffer, query_length);
} }
} }
const char* response = "OK!"; // an error was returned, pass it on to the API caller
httpd_resp_send(request, response, strlen(response)); if(result.error != NULL) {
httpd_resp_set_status(request, "400 Bad Request");
response_msg = result.error;
}
httpd_resp_send(request, response_msg, strlen(response_msg));
return ESP_OK; return ESP_OK;
} }
@ -106,7 +173,7 @@ httpd_uri_t get_root_uri = {
.user_ctx = NULL .user_ctx = NULL
}; };
// Configure and enable the http server. // Configure server and enable the http handler.
static static
httpd_handle_t start_webserver(void) { httpd_handle_t start_webserver(void) {
httpd_handle_t server = NULL; httpd_handle_t server = NULL;

View File

@ -25,6 +25,15 @@ static const char* APP_TAG="CINEKID_LEDS";
printf("\n");\ printf("\n");\
} while(0) } while(0)
static inline
int min(int a, int b) { return a < b ? a : b; }
static inline
int max(int a, int b) { return a > b ? a : b; }
static inline
int clamp(int x, int mi, int ma) {
return max(mi, min(ma, x));
}
#define GLOBAL(__a) (uint8_t)(__a|0xE0) #define GLOBAL(__a) (uint8_t)(__a|0xE0)
#endif // !_shared_h #endif // !_shared_h