2023-09-19 08:34:06 +00:00
|
|
|
///
|
|
|
|
//
|
|
|
|
// server.h
|
|
|
|
// Call server_init() to start up an http webserver.
|
2023-09-22 08:12:11 +00:00
|
|
|
// listens on '/' for GET queries with a url query format like
|
2023-09-19 08:34:06 +00:00
|
|
|
// ?l=*&r0=*&g0=*&b0=*&a0=*&t0=* ... &rl=*&gl=*&bl=*&al=*&tl=*
|
|
|
|
// 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.
|
|
|
|
// t is the offset from the start measured in leds.
|
2023-09-22 10:30:29 +00:00
|
|
|
// 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
|
2023-09-19 08:34:06 +00:00
|
|
|
//
|
|
|
|
///
|
|
|
|
|
2023-09-18 20:41:46 +00:00
|
|
|
#ifndef _potion_party_server_h
|
|
|
|
#define _potion_party_server_h
|
2023-09-15 06:21:00 +00:00
|
|
|
|
2023-09-18 20:41:46 +00:00
|
|
|
#include <stddef.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "shared.h"
|
|
|
|
#include "leds.h"
|
|
|
|
#include "esp_http_server.h"
|
|
|
|
#include "esp_system.h"
|
|
|
|
#include "esp_netif.h"
|
2023-09-21 10:01:02 +00:00
|
|
|
#include "sys_arch.h"
|
|
|
|
|
2023-09-18 20:41:46 +00:00
|
|
|
static
|
|
|
|
httpd_handle_t g_http_server = NULL;
|
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
struct parse_error_t {
|
|
|
|
const char* error;
|
|
|
|
};
|
|
|
|
|
2023-09-19 08:34:06 +00:00
|
|
|
// Parse a gradient query
|
2023-09-18 20:41:46 +00:00
|
|
|
static
|
2023-09-22 10:30:29 +00:00
|
|
|
struct parse_error_t parse_leds_query(char* query_string, size_t query_size) {
|
2023-09-18 20:41:46 +00:00
|
|
|
char query_value[16];
|
|
|
|
char query_key[3];
|
2023-09-22 10:30:29 +00:00
|
|
|
struct gradient_t gradient;
|
2023-09-18 20:41:46 +00:00
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
// Fetch the &l length parameter.
|
|
|
|
// Interpret as a positive integer number of points on the gradient.
|
2023-09-18 20:41:46 +00:00
|
|
|
if(httpd_query_key_value(query_string, "l", query_value, sizeof(query_value)) == ESP_OK) {
|
2023-09-22 10:30:29 +00:00
|
|
|
gradient.points_len = max(0, atoi(query_value));
|
|
|
|
} else {
|
|
|
|
leds_set_current_gradient(&g_default_gradient, 0);
|
|
|
|
return (struct parse_error_t) {
|
|
|
|
.error = "ERROR: Failed to find length parameter &l"
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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);
|
2023-09-18 20:41:46 +00:00
|
|
|
} else {
|
2023-09-22 10:30:29 +00:00
|
|
|
gradient.duration = 0;
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
LOGLN("Reading %zu points of gradient query:", gradient.points_len);
|
|
|
|
LOGLN("duration: %f", gradient.duration);
|
2023-09-18 20:41:46 +00:00
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
// 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
|
2023-09-18 20:41:46 +00:00
|
|
|
sprintf(query_key, "r%d", point);
|
|
|
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) {
|
2023-09-22 10:30:29 +00:00
|
|
|
gradient.points[point].led.components.red = atoi(query_value);
|
|
|
|
} else {
|
|
|
|
return (struct parse_error_t) {
|
|
|
|
.error = "ERROR: Point missing red component &r."
|
|
|
|
};
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
|
|
|
sprintf(query_key, "g%d", point);
|
|
|
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) {
|
2023-09-22 10:30:29 +00:00
|
|
|
gradient.points[point].led.components.green = atoi(query_value);
|
|
|
|
} else {
|
|
|
|
return (struct parse_error_t) {
|
|
|
|
.error = "ERROR: Point missing green component &g."
|
|
|
|
};
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
|
|
|
sprintf(query_key, "b%d", point);
|
|
|
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
2023-09-22 10:30:29 +00:00
|
|
|
gradient.points[point].led.components.blue = atoi(query_value);
|
|
|
|
} else {
|
|
|
|
return (struct parse_error_t) {
|
|
|
|
.error = "ERROR: Point missing blue component &b."
|
|
|
|
};
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
2023-09-22 10:30:29 +00:00
|
|
|
// 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
|
2023-09-18 20:41:46 +00:00
|
|
|
sprintf(query_key, "a%d", point);
|
|
|
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
2023-09-22 10:30:29 +00:00
|
|
|
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."
|
|
|
|
};
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
2023-09-22 10:30:29 +00:00
|
|
|
// 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
|
2023-09-18 20:41:46 +00:00
|
|
|
sprintf(query_key, "t%d", point);
|
|
|
|
if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) {
|
2023-09-22 10:30:29 +00:00
|
|
|
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;
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
// Log fetched fields
|
|
|
|
LOGLN("led[%d]:", point);
|
|
|
|
LOGLN(" r %d", gradient.points[point].led.components.red);
|
|
|
|
LOGLN(" g %d", gradient.points[point].led.components.green);
|
|
|
|
LOGLN(" b %d", gradient.points[point].led.components.blue);
|
|
|
|
LOGLN(" global %d", gradient.points[point].led.components.global >> 3);
|
|
|
|
LOGLN(" t %zu", gradient.points[point].offset);
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
leds_set_current_gradient(&gradient, 0);
|
|
|
|
return (struct parse_error_t) { .error = NULL };
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
|
|
|
|
2023-09-22 08:12:11 +00:00
|
|
|
// receives HTTP GET requests on root
|
2023-09-18 20:41:46 +00:00
|
|
|
static
|
2023-09-22 08:12:11 +00:00
|
|
|
esp_err_t on_http_get_root(httpd_req_t* request) {
|
2023-09-22 10:30:29 +00:00
|
|
|
// Error mewsages for cases of good and bad
|
|
|
|
static const char* response_ok = "OK!";
|
|
|
|
|
|
|
|
const char* response_msg = response_ok;
|
2023-09-18 20:41:46 +00:00
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
LOGLN("POST received on '/'.");
|
2023-09-18 20:41:46 +00:00
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
// buffer for query string
|
|
|
|
char* query_buffer;
|
|
|
|
size_t query_length = httpd_req_get_url_query_len(request) + 1;
|
|
|
|
struct parse_error_t result = { .error = NULL };
|
2023-09-18 20:41:46 +00:00
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
if(query_length > 1) {
|
|
|
|
query_buffer = malloc(query_length * sizeof(char));
|
|
|
|
if(httpd_req_get_url_query_str(request, query_buffer, query_length) == ESP_OK) {
|
2023-09-18 20:41:46 +00:00
|
|
|
LOGLN("Received query.");
|
2023-09-22 10:30:29 +00:00
|
|
|
result = parse_leds_query(query_buffer, query_length);
|
2023-09-18 20:41:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
// an error was returned, pass it on to the API caller
|
|
|
|
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));
|
2023-09-18 20:41:46 +00:00
|
|
|
|
|
|
|
return ESP_OK;
|
|
|
|
}
|
2023-09-22 08:12:11 +00:00
|
|
|
httpd_uri_t get_root_uri = {
|
2023-09-18 20:41:46 +00:00
|
|
|
.uri="/",
|
2023-09-22 08:12:11 +00:00
|
|
|
.method=HTTP_GET,
|
|
|
|
.handler=&on_http_get_root,
|
2023-09-18 20:41:46 +00:00
|
|
|
.user_ctx = NULL
|
|
|
|
};
|
|
|
|
|
2023-09-22 10:30:29 +00:00
|
|
|
// Configure server and enable the http handler.
|
2023-09-18 20:41:46 +00:00
|
|
|
static
|
|
|
|
httpd_handle_t start_webserver(void) {
|
|
|
|
httpd_handle_t server = NULL;
|
|
|
|
httpd_config_t server_config = HTTPD_DEFAULT_CONFIG();
|
|
|
|
|
|
|
|
LOGLN("Starting HTTPd server ':%d'.", server_config.server_port);
|
2023-09-22 08:12:11 +00:00
|
|
|
|
2023-09-18 20:41:46 +00:00
|
|
|
if(httpd_start(&server, &server_config) == ESP_OK) {
|
2023-09-22 08:12:11 +00:00
|
|
|
httpd_register_uri_handler(server, &get_root_uri);
|
2023-09-18 20:41:46 +00:00
|
|
|
return server;
|
|
|
|
}
|
|
|
|
|
|
|
|
LOGLN("Failed to start HTTPd server.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2023-09-19 08:34:06 +00:00
|
|
|
// Start an http server and store it's handle in g_http_server.
|
2023-09-18 20:41:46 +00:00
|
|
|
static
|
|
|
|
void server_init(void) {
|
|
|
|
g_http_server = start_webserver();
|
2023-09-15 06:21:00 +00:00
|
|
|
}
|
|
|
|
|
2023-09-18 20:41:46 +00:00
|
|
|
#endif // !_potion_party_server_h
|