/// // server.h // Call server_init() to start up an http webserver. // Listens on '/' for GET queries with a url query format like // ?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. // Each point also has an optional &m 'movement' argument. &m should be either -1, +1 or 0 and represents the movement per frame or the point. // A point on the gradient can have an &m parameter, and has to have a &r, &g, &b, &a, and &t. // The whole gradient can have a duration &d parameter and must have a length &l /// #ifndef _potion_party_server_h #define _potion_party_server_h #include #include #include #include "shared.h" #include "leds.h" #include "esp_http_server.h" #include "esp_system.h" #include "esp_netif.h" #include "sys_arch.h" static httpd_handle_t g_http_server = NULL; struct parse_error_t { const char* error; }; // Parse a gradient query static struct parse_error_t parse_leds_query(char* query_string, size_t query_size) { char query_value[16]; char query_key[3]; 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) { 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); } else { gradient.duration = 0; } 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); if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) { 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); if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_size)) == ESP_OK) { gradient.points[point].led.components.green = atoi(query_value); } else { return (struct parse_error_t) { .error = "ERROR: Point missing green component &g." }; } sprintf(query_key, "b%d", point); if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) { 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); if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) { 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 - 120. // Interpreted as an integer offset from the first led to the last sprintf(query_key, "t%d", point); if(httpd_query_key_value(query_string, query_key, query_value, sizeof(query_value)) == ESP_OK) { gradient.points[point].offset = clamp(0, 120, 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; } // 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); // may show up as a compile error on modern computers, // x86_64 size_t is usually an unsigned long int, on the ESP8266 it is an unsigned int LOGLN(" t %u", gradient.points[point].offset); } xSemaphoreTake(g_led_mutex, portMAX_DELAY); leds_set_current_gradient(&gradient, 0); xSemaphoreGive(g_led_mutex); return (struct parse_error_t) { .error = NULL }; } // receives HTTP GET requests on root static 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 '/'."); // 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 }; 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) { LOGLN("Received query."); result = parse_leds_query(query_buffer, query_length); } } // 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)); return ESP_OK; } httpd_uri_t get_root_uri = { .uri="/", .method=HTTP_GET, .handler=&on_http_get_root, .user_ctx = NULL }; // Configure server and enable the http handler. 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); if(httpd_start(&server, &server_config) == ESP_OK) { httpd_register_uri_handler(server, &get_root_uri); return server; } LOGLN("Failed to start HTTPd server."); return NULL; } // Start an http server and store it's handle in g_http_server. static void server_init(void) { g_http_server = start_webserver(); } #endif // !_potion_party_server_h