#ifndef _potion_leds_h #define _potion_leds_h #include #include #include #include "rom/ets_sys.h" #include "shared.h" #include "driver/gpio.h" // pack the struct to match exactly 8 * 4 = 32bits struct __attribute__((__packed__)) led_components_t { uint8_t global; // global baseline brightness, highest 3 bits should always be ones // RGB component values uint8_t red; uint8_t green; uint8_t blue; }; // union of components and their representation as a u32 // allows for easier sending of data over serial union led_t { struct led_components_t components; uint32_t bits; }; // point on a gradient struct gradient_point_t { union led_t led; // value of the led at this point size_t offset; // offset (measured in leds) from the beginning }; // buffer that will be written out to the led strip over serial // for leds to work, the uint32_t g_serial_out_buffer[62]; // 60-long slice of the out buffer that represents the first few leds union led_t* g_leds = (uint32_t*)(g_serial_out_buffer + 1); #define CLOCK 4 #define DATA 5 static void send_leds() { LOGLN("Writing to leds"); // index of the bit being written // the first 5 bits represent the bit of the byte represented by the rest of the int int write_bit = 0; int write_next = 0; gpio_set_level(CLOCK, 0); while(write_bit < sizeof(g_serial_out_buffer) * 8) { // fetch the bit being adressed write_next = 0x1 & (g_serial_out_buffer[write_bit >> 5] >> (write_bit & 0x1F)); // set clock out to high, triggering a rising edge gpio_set_level(CLOCK, 1); // write bit to data out gpio_set_level(DATA, write_next); os_delay_us(10); // set clock to low, triggering a falling edge gpio_set_level(CLOCK, 0); os_delay_us(10); write_bit++; } } static void leds_init() { g_serial_out_buffer[0] = 0; g_serial_out_buffer[61] = 0xFFFFFFFF; for(int i = 0; i < 60; ++i) { g_leds[i].components = (struct led_components_t) { .red = 255, .green = 10, .blue = 255, .global = 0xE0 | 0 }; } gpio_config_t config = { .intr_type = GPIO_INTR_DISABLE, .mode = GPIO_MODE_OUTPUT, .pin_bit_mask = 0x18030, .pull_up_en = 0, .pull_down_en = 0, }; gpio_config(&config); } static inline uint8_t lerp_uint8(uint8_t a, uint8_t b, float t) { int dir = b - a; return a + t * dir; } static inline void lerp_led(union led_t* out, const union led_t* from, const union led_t* to, float t) { out->components.red = lerp_uint8(from->components.red, to->components.red, t); out->components.green = lerp_uint8(from->components.green, to->components.green, t); out->components.blue = lerp_uint8(from->components.blue, to->components.blue, t); uint8_t glob_from = from->components.global & 0x1F; uint8_t glob_to = to->components.global & 0x1F; out->components.global = 0xE0 | lerp_uint8(glob_from, glob_to, t); } static inline void lerp_points_between(const struct gradient_point_t from, const struct gradient_point_t to) { const int dif = to.offset - from.offset; float t = 0.f; for(int led = from.offset; led < to.offset; ++led) { t = (float)(led - from.offset) / (float)dif; lerp_led(g_leds + led, &from.led, &to.led, t); } } static void leds_set_gradient(struct gradient_point_t* points, size_t points_len) { struct gradient_point_t from = points[0]; struct gradient_point_t to; for(int i = 1; i < points_len; ++i) { to = points[i]; lerp_points_between(from, to); from = to; } send_leds(); } #endif // !_leds_h