diff --git a/main/leds.h b/main/leds.h index be324d2..46ce6a8 100644 --- a/main/leds.h +++ b/main/leds.h @@ -7,6 +7,7 @@ #ifndef _potion_leds_h #define _potion_leds_h +#include #include #include #include @@ -16,6 +17,12 @@ #include "rom/ets_sys.h" #include "driver/gpio.h" +enum leds_send_state_t { + LEDS_SEND_WAITING, + LEDS_SEND_REQUESTED, + LEDS_SENDING, +}; + // pack the struct to match exactly 8 * 4 = 32bits struct __attribute__((__packed__)) led_components_t { // RGB component values @@ -47,13 +54,17 @@ struct gradient_t { }; // buffer that will be written out to the led strip over serial -uint32_t g_serial_out_buffer[62]; -// 60-long slice of the out buffer that represents the first few leds +uint32_t g_serial_out_buffer[122]; +// 120-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); struct gradient_t g_default_gradient; struct gradient_t g_current_gradient; int g_leds_are_default = 1; +enum leds_send_state_t g_leds_send_state = LEDS_SEND_WAITING; + +SemaphoreHandle_t g_led_mutex; // mutex governing access to data for leds + // use for all global data defined in this header #define CLOCK 4 #define DATA 5 @@ -73,31 +84,26 @@ void leds_config_gpio() { static void serial_write(int high) { - // set clock out to high, triggering a rising edge - gpio_set_level(CLOCK, 1); - // write bit to data out - gpio_set_level(DATA, high); + // set clock out to high, triggering a rising edge + gpio_set_level(CLOCK, 1); + // write bit to data out + gpio_set_level(DATA, high); - os_delay_us(2); - // set clock to low, triggering a falling edge, shifting the LEDs shift register - gpio_set_level(CLOCK, 0); + ets_delay_us(1); + + // set clock to low, triggering a falling edge, shifting the LEDs shift register + gpio_set_level(CLOCK, 0); } static void send_leds() { - LOGLN("entering send_leds()"); // index of the bit being written // fixed point number where the first 5 bits are the bit of a 32bit integger, and the rest is the integer int write_bit = 0; int write_next = 0; - - LOGLN("Setting clock low"); - gpio_set_level(CLOCK, 0); - LOGLN("Writing to leds"); - while(write_bit < sizeof(g_serial_out_buffer) * 8) { // fetch the bit being addressed write_next = 0x1 & (g_serial_out_buffer[write_bit >> 5] >> (32 - (write_bit & 0x1F))); @@ -160,7 +166,7 @@ void leds_set_current_gradient(const struct gradient_t* gradient, int defer_send struct gradient_point_t to; set_led_range(0, gradient->points[0].offset, gradient->points[0].led); - set_led_range(gradient->points[gradient->points_len-1].offset, 60, gradient->points[gradient->points_len-1].led); + set_led_range(gradient->points[gradient->points_len-1].offset, 120, gradient->points[gradient->points_len-1].led); g_current_gradient = *gradient; @@ -175,11 +181,71 @@ void leds_set_current_gradient(const struct gradient_t* gradient, int defer_send } } +void memswap(void* d, void* s, size_t n) { + void* tmp = malloc(n); + memcpy(tmp, s, n); + memcpy(s, d, n); + memcpy(d, tmp, n); + free(tmp); +} + +void leds_animate() { + for(size_t i = 0; i < g_current_gradient.points_len; ++i) { + struct gradient_point_t* point = g_current_gradient.points + i; + if(point->movement > 0) { + point->offset = min(point->offset + 1, 120); + LOGLN("move result %i", point->offset); + LOGLN("next %d", (point+1)->offset); + + if(point->offset > (point+1)->offset) { + memswap(point, point+1, sizeof(struct gradient_point_t)); + LOGLN("swap down"); + } + } else if(point->movement < 0) { + point->offset = max(0, point->offset - 1); + LOGLN("move result %i", point->offset); + LOGLN("next %d", (point-1)->offset); + + if(point->offset < (point-1)->offset) { + memswap(point, point-1, sizeof(struct gradient_point_t)); + LOGLN("swap up"); + } + } + } + leds_set_current_gradient(&g_current_gradient, 1); +} + +struct led_thread_data_t { + TaskHandle_t task; + TaskFunction_t func; +}; + +static void leds_thread(); + +static struct led_thread_data_t _thread_data = { + .task = 0, + .func = &leds_thread +}; + +static +void leds_thread() { + send_leds(); + for(;;) { + vTaskDelay(10); + xSemaphoreTake(g_led_mutex, portMAX_DELAY); + send_leds(); + leds_animate(); + xSemaphoreGive(g_led_mutex); + } +} + + + static void leds_init() { g_serial_out_buffer[0] = 0u; g_serial_out_buffer[61] = ~0u; - set_led_range(0, 60, + set_led_range(0, 120, (union led_t){.components = (struct led_components_t) { .red = 0, @@ -189,6 +255,11 @@ void leds_init() { }} ); + g_led_mutex = xSemaphoreCreateMutex(); + + xTaskCreate(_thread_data.func, "Leds", 1024, NULL, 1, &_thread_data.task); + + // initialize mutex for leds data leds_config_gpio(); } diff --git a/main/potion_party.c b/main/potion_party.c index 14ca94b..5e0ccdf 100644 --- a/main/potion_party.c +++ b/main/potion_party.c @@ -30,39 +30,51 @@ void TEST_leds() { } }; - set_led_range(0, 60, led); + set_led_range(0, 120, led); led.components.blue = 255; - set_led_range(30, 60, led); + set_led_range(60, 120, led); send_leds(); sleep(1); - struct gradient_point_t points[3]; - points[0].offset = 0; - points[0].led.components = (struct led_components_t) { - .global = GLOBAL(10), - .red = 200, + struct gradient_t gradient; + gradient.points_len = 4; + + gradient.points[0].offset = 0; + gradient.points[0].movement = 0; + gradient.points[0].led.components = (struct led_components_t) { + .global = GLOBAL(0), + .red = 0, .green = 0, - .blue = 40, + .blue = 0, }; - points[1].offset = 30; - points[1].led.components = (struct led_components_t){ + gradient.points[1].offset = 59; + gradient.points[1].movement = -1; + gradient.points[1].led.components = (struct led_components_t){ .global = GLOBAL(10), .red = 40, - .green = 30, + .green = 200, .blue = 40, }; - - points[2].offset = 60; - points[2].led.components = (struct led_components_t){ + gradient.points[2].offset = 61; + gradient.points[2].movement = 1; + gradient.points[2].led.components = (struct led_components_t){ .global = GLOBAL(10), - .red = 200, - .green = 255, - .blue = 255, + .red = 40, + .green = 200, + .blue = 40, + }; + gradient.points[3].offset = 120; + gradient.points[3].movement = 0; + gradient.points[3].led.components = (struct led_components_t){ + .global = GLOBAL(0), + .red = 0, + .green = 0, + .blue = 0, }; - leds_set_gradient(points, 3, 1); + leds_set_current_gradient(&gradient, 0); } void app_main(void) { diff --git a/main/server.h b/main/server.h index 9120d28..4f3418f 100644 --- a/main/server.h +++ b/main/server.h @@ -1,16 +1,14 @@ /// -// // server.h // Call server_init() to start up an http webserver. -// listens on '/' for GET queries with a url query format like +// 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 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 -// +// 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 @@ -99,11 +97,11 @@ struct parse_error_t parse_leds_query(char* query_string, size_t query_size) { .error = "ERROR: Point missing alpha component &a." }; } - // Get the time of the gradient as a number ranging from 0 - 60. + // 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, 60, atoi(query_value)); + gradient.points[point].offset = clamp(0, 120, atoi(query_value)); } else { return (struct parse_error_t) { .error = "ERROR: Point missing time component &t." @@ -124,10 +122,17 @@ struct parse_error_t parse_leds_query(char* query_string, size_t query_size) { 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); + // 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 }; }