#include "LedControl.h" #include "driver/rmt.h" #include "esp32_digital_led_lib.h" #include "Arduino.h" #define HSV_SECTION_6 (0x20) #define HSV_SECTION_3 (0x40) static ColorRGB hsv2rgb(const ColorHSV &hsv) { // Convert hue, saturation and brightness ( HSV/HSB ) to RGB // "Dimming" is used on saturation and brightness to make // the output more visually linear. // Apply dimming curves uint8_t value = hsv.v; uint8_t saturation = hsv.s; // The brightness floor is minimum number that all of // R, G, and B will be set to. uint8_t invsat = 255 - saturation; uint8_t brightness_floor = (value * invsat) / 256; // The color amplitude is the maximum amount of R, G, and B // that will be added on top of the brightness_floor to // create the specific hue desired. uint8_t color_amplitude = value - brightness_floor; // Figure out which section of the hue wheel we're in, // and how far offset we are withing that section uint8_t section = hsv.h / HSV_SECTION_3; // 0..2 uint8_t offset = hsv.h % HSV_SECTION_3; // 0..63 uint8_t rampup = offset; // 0..63 uint8_t rampdown = (HSV_SECTION_3 - 1) - offset; // 63..0 // We now scale rampup and rampdown to a 0-255 range -- at least // in theory, but here's where architecture-specific decsions // come in to play: // To scale them up to 0-255, we'd want to multiply by 4. // But in the very next step, we multiply the ramps by other // values and then divide the resulting product by 256. // So which is faster? // ((ramp * 4) * othervalue) / 256 // or // ((ramp ) * othervalue) / 64 // It depends on your processor architecture. // On 8-bit AVR, the "/ 256" is just a one-cycle register move, // but the "/ 64" might be a multicycle shift process. So on AVR // it's faster do multiply the ramp values by four, and then // divide by 256. // On ARM, the "/ 256" and "/ 64" are one cycle each, so it's // faster to NOT multiply the ramp values by four, and just to // divide the resulting product by 64 (instead of 256). // Moral of the story: trust your profiler, not your insticts. // Since there's an AVR assembly version elsewhere, we'll // assume what we're on an architecture where any number of // bit shifts has roughly the same cost, and we'll remove the // redundant math at the source level: // // scale up to 255 range // //rampup *= 4; // 0..252 // //rampdown *= 4; // 0..252 // compute color-amplitude-scaled-down versions of rampup and rampdown uint8_t rampup_amp_adj = (rampup * color_amplitude) / (256 / 4); uint8_t rampdown_amp_adj = (rampdown * color_amplitude) / (256 / 4); // add brightness_floor offset to everything uint8_t rampup_adj_with_floor = rampup_amp_adj + brightness_floor; uint8_t rampdown_adj_with_floor = rampdown_amp_adj + brightness_floor; if (section) { if (section == 1) return ColorRGB{brightness_floor, rampdown_adj_with_floor, rampup_adj_with_floor}; else return ColorRGB{rampup_adj_with_floor, brightness_floor, rampdown_adj_with_floor}; } else return ColorRGB{rampdown_adj_with_floor, rampup_adj_with_floor, brightness_floor}; } LedStrip::LedStrip(int numLeds, int pin) : numLeds_(numLeds), pin_(pin) { cfg = {.rmtChannel = 0, .gpioNum = pin, .ledType = LED_SK6812W_V1, .brightLimit = 24, .numPixels = numLeds}; strands[0] = &cfg; } void LedStrip::begin() { digitalLeds_initDriver(); gpioSetup(cfg.gpioNum, OUTPUT, LOW); int rc = digitalLeds_addStrands(strands, 1); if (rc) Serial.println("LEDs: Error during init"); } void LedStrip::clear() { digitalLeds_resetPixels(strands, 1); } int LedStrip::normalizeLedIdx(int i) { while (i < 0) i += numLeds_; while (i >= numLeds_) i -= numLeds_; return i; } void LedStrip::setColor(int led, int r, int g, int b, int w) { strands[0]->pixels[led] = pixelFromRGBW(r, g, b, w); } void LedStrip::transmit() { digitalLeds_drawPixels(strands, 1); } void LedStrip::setAll(int r, int g, int b, int w) { for (int i = 0; i < numLeds_; ++i) setColor(i, r, g, b, w); } void LedStrip::setRange(int begin, int end, int r, int g, int b, int w) { for (int i = begin; i < min(end, numLeds_); ++i) setColor(i, r, g, b, w); } void LedStrip::setColor(int led, const ColorRGB &color) { setColor(led, color.r, color.g, color.b, 0); } void LedStrip::setColor(int led, const ColorHSV &color) { setColor(led, hsv2rgb(color)); }