| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428 |
- /*
- xplg_ws2812.ino - ws2812 led string support for Sonoff-Tasmota
- Copyright (C) 2018 Heiko Krupp and Theo Arends
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- #ifdef USE_WS2812
- /*********************************************************************************************\
- * WS2812 RGB / RGBW Leds using NeopixelBus library
- \*********************************************************************************************/
- #include <NeoPixelBus.h>
- #ifdef USE_WS2812_DMA
- #if (USE_WS2812_CTYPE == NEO_GRB)
- NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> *strip = NULL;
- #elif (USE_WS2812_CTYPE == NEO_BRG)
- NeoPixelBus<NeoBrgFeature, Neo800KbpsMethod> *strip = NULL;
- #elif (USE_WS2812_CTYPE == NEO_RBG)
- NeoPixelBus<NeoRbgFeature, Neo800KbpsMethod> *strip = NULL;
- #elif (USE_WS2812_CTYPE == NEO_RGBW)
- NeoPixelBus<NeoRgbwFeature, Neo800KbpsMethod> *strip = NULL;
- #elif (USE_WS2812_CTYPE == NEO_GRBW)
- NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod> *strip = NULL;
- #else // USE_WS2812_CTYPE
- NeoPixelBus<NeoRgbFeature, Neo800KbpsMethod> *strip = NULL;
- #endif // USE_WS2812_CTYPE
- #else // USE_WS2812_DMA
- #if (USE_WS2812_CTYPE == NEO_GRB)
- NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
- #elif (USE_WS2812_CTYPE == NEO_BRG)
- NeoPixelBus<NeoBrgFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
- #elif (USE_WS2812_CTYPE == NEO_RBG)
- NeoPixelBus<NeoRbgFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
- #elif (USE_WS2812_CTYPE == NEO_RGBW)
- NeoPixelBus<NeoRgbwFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
- #elif (USE_WS2812_CTYPE == NEO_GRBW)
- NeoPixelBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
- #else // USE_WS2812_CTYPE
- NeoPixelBus<NeoRgbFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
- #endif // USE_WS2812_CTYPE
- #endif // USE_WS2812_DMA
- struct WsColor {
- uint8_t red, green, blue;
- };
- struct ColorScheme {
- WsColor* colors;
- uint8_t count;
- };
- WsColor kIncandescent[2] = { 255,140,20, 0,0,0 };
- WsColor kRgb[3] = { 255,0,0, 0,255,0, 0,0,255 };
- WsColor kChristmas[2] = { 255,0,0, 0,255,0 };
- WsColor kHanukkah[2] = { 0,0,255, 255,255,255 };
- WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 };
- WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 };
- WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 };
- ColorScheme kSchemes[WS2812_SCHEMES] = {
- kIncandescent, 2,
- kRgb, 3,
- kChristmas, 2,
- kHanukkah, 2,
- kwanzaa, 3,
- kRainbow, 7,
- kFire, 3 };
- uint8_t kWidth[5] = {
- 1, // Small
- 2, // Medium
- 4, // Large
- 8, // Largest
- 255 }; // All
- uint8_t kWsRepeat[5] = {
- 8, // Small
- 6, // Medium
- 4, // Large
- 2, // Largest
- 1 }; // All
- uint8_t ws_show_next = 1;
- bool ws_suspend_update = false;
- /********************************************************************************************/
- void Ws2812StripShow(void)
- {
- #if (USE_WS2812_CTYPE > NEO_3LED)
- RgbwColor c;
- #else
- RgbColor c;
- #endif
- if (Settings.light_correction) {
- for (uint16_t i = 0; i < Settings.light_pixels; i++) {
- c = strip->GetPixelColor(i);
- c.R = ledTable[c.R];
- c.G = ledTable[c.G];
- c.B = ledTable[c.B];
- #if (USE_WS2812_CTYPE > NEO_3LED)
- c.W = ledTable[c.W];
- #endif
- strip->SetPixelColor(i, c);
- }
- }
- strip->Show();
- }
- int mod(int a, int b)
- {
- int ret = a % b;
- if (ret < 0) ret += b;
- return ret;
- }
- void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset)
- {
- #if (USE_WS2812_CTYPE > NEO_3LED)
- RgbwColor color;
- #else
- RgbColor color;
- #endif
- uint16_t mod_position = mod(position, (int)Settings.light_pixels);
- color = strip->GetPixelColor(mod_position);
- float dimmer = 100 / (float)Settings.light_dimmer;
- color.R = tmin(color.R + ((hand_color.red / dimmer) * offset), 255);
- color.G = tmin(color.G + ((hand_color.green / dimmer) * offset), 255);
- color.B = tmin(color.B + ((hand_color.blue / dimmer) * offset), 255);
- strip->SetPixelColor(mod_position, color);
- }
- void Ws2812UpdateHand(int position, uint8_t index)
- {
- position = (position + Settings.light_rotation) % Settings.light_pixels;
- if (Settings.flag.ws_clock_reverse) position = Settings.light_pixels -position;
- WsColor hand_color = { Settings.ws_color[index][WS_RED], Settings.ws_color[index][WS_GREEN], Settings.ws_color[index][WS_BLUE] };
- Ws2812UpdatePixelColor(position, hand_color, 1);
- uint8_t range = 1;
- if (index < WS_MARKER) range = ((Settings.ws_width[index] -1) / 2) +1;
- for (uint8_t h = 1; h < range; h++) {
- float offset = (float)(range - h) / (float)range;
- Ws2812UpdatePixelColor(position -h, hand_color, offset);
- Ws2812UpdatePixelColor(position +h, hand_color, offset);
- }
- }
- void Ws2812Clock(void)
- {
- strip->ClearTo(0); // Reset strip
- int clksize = 60000 / (int)Settings.light_pixels;
- Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND);
- Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE);
- Ws2812UpdateHand(((RtcTime.hour % 12) * (5000 / clksize)) + ((RtcTime.minute * 1000) / (12 * clksize)), WS_HOUR);
- if (Settings.ws_color[WS_MARKER][WS_RED] + Settings.ws_color[WS_MARKER][WS_GREEN] + Settings.ws_color[WS_MARKER][WS_BLUE]) {
- for (byte i = 0; i < 12; i++) {
- Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER);
- }
- }
- Ws2812StripShow();
- }
- void Ws2812GradientColor(uint8_t schemenr, struct WsColor* mColor, uint16_t range, uint16_t gradRange, uint16_t i)
- {
- /*
- * Compute the color of a pixel at position i using a gradient of the color scheme.
- * This function is used internally by the gradient function.
- */
- ColorScheme scheme = kSchemes[schemenr];
- uint16_t curRange = i / range;
- uint16_t rangeIndex = i % range;
- uint16_t colorIndex = rangeIndex / gradRange;
- uint16_t start = colorIndex;
- uint16_t end = colorIndex +1;
- if (curRange % 2 != 0) {
- start = (scheme.count -1) - start;
- end = (scheme.count -1) - end;
- }
- float dimmer = 100 / (float)Settings.light_dimmer;
- float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer;
- float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer;
- float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer;
- mColor->red = (uint8_t)fmyRed;
- mColor->green = (uint8_t)fmyGrn;
- mColor->blue = (uint8_t)fmyBlu;
- }
- void Ws2812Gradient(uint8_t schemenr)
- {
- /*
- * This routine courtesy Tony DiCola (Adafruit)
- * Display a gradient of colors for the current color scheme.
- * Repeat is the number of repetitions of the gradient (pick a multiple of 2 for smooth looping of the gradient).
- */
- #if (USE_WS2812_CTYPE > NEO_3LED)
- RgbwColor c;
- c.W = 0;
- #else
- RgbColor c;
- #endif
- ColorScheme scheme = kSchemes[schemenr];
- if (scheme.count < 2) return;
- uint8_t repeat = kWsRepeat[Settings.light_width]; // number of scheme.count per ledcount
- uint16_t range = (uint16_t)ceil((float)Settings.light_pixels / (float)repeat);
- uint16_t gradRange = (uint16_t)ceil((float)range / (float)(scheme.count - 1));
- uint16_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10);
- uint16_t offset = speed > 0 ? strip_timer_counter / speed : 0;
- WsColor oldColor, currentColor;
- Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset);
- currentColor = oldColor;
- for (uint16_t i = 0; i < Settings.light_pixels; i++) {
- if (kWsRepeat[Settings.light_width] > 1) {
- Ws2812GradientColor(schemenr, ¤tColor, range, gradRange, i +offset);
- }
- if (Settings.light_speed > 0) {
- // Blend old and current color based on time for smooth movement.
- c.R = map(strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red);
- c.G = map(strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green);
- c.B = map(strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue);
- }
- else {
- // No animation, just use the current color.
- c.R = currentColor.red;
- c.G = currentColor.green;
- c.B = currentColor.blue;
- }
- strip->SetPixelColor(i, c);
- oldColor = currentColor;
- }
- Ws2812StripShow();
- }
- void Ws2812Bars(uint8_t schemenr)
- {
- /*
- * This routine courtesy Tony DiCola (Adafruit)
- * Display solid bars of color for the current color scheme.
- * Width is the width of each bar in pixels/lights.
- */
- #if (USE_WS2812_CTYPE > NEO_3LED)
- RgbwColor c;
- c.W = 0;
- #else
- RgbColor c;
- #endif
- uint16_t i;
- ColorScheme scheme = kSchemes[schemenr];
- uint16_t maxSize = Settings.light_pixels / scheme.count;
- if (kWidth[Settings.light_width] > maxSize) maxSize = 0;
- uint16_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10);
- uint8_t offset = speed > 0 ? strip_timer_counter / speed : 0;
- WsColor mcolor[scheme.count];
- memcpy(mcolor, scheme.colors, sizeof(mcolor));
- float dimmer = 100 / (float)Settings.light_dimmer;
- for (i = 0; i < scheme.count; i++) {
- float fmyRed = (float)mcolor[i].red / dimmer;
- float fmyGrn = (float)mcolor[i].green / dimmer;
- float fmyBlu = (float)mcolor[i].blue / dimmer;
- mcolor[i].red = (uint8_t)fmyRed;
- mcolor[i].green = (uint8_t)fmyGrn;
- mcolor[i].blue = (uint8_t)fmyBlu;
- }
- uint8_t colorIndex = offset % scheme.count;
- for (i = 0; i < Settings.light_pixels; i++) {
- if (maxSize) colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width];
- c.R = mcolor[colorIndex].red;
- c.G = mcolor[colorIndex].green;
- c.B = mcolor[colorIndex].blue;
- strip->SetPixelColor(i, c);
- }
- Ws2812StripShow();
- }
- /*********************************************************************************************\
- * Public
- \*********************************************************************************************/
- void Ws2812Init(void)
- {
- #ifdef USE_WS2812_DMA
- #if (USE_WS2812_CTYPE == NEO_GRB)
- strip = new NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
- #elif (USE_WS2812_CTYPE == NEO_BRG)
- strip = new NeoPixelBus<NeoBrgFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
- #elif (USE_WS2812_CTYPE == NEO_RBG)
- strip = new NeoPixelBus<NeoRbgFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
- #elif (USE_WS2812_CTYPE == NEO_RGBW)
- strip = new NeoPixelBus<NeoRgbwFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
- #elif (USE_WS2812_CTYPE == NEO_GRBW)
- strip = new NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
- #else // USE_WS2812_CTYPE
- strip = new NeoPixelBus<NeoRgbFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
- #endif // USE_WS2812_CTYPE
- #else // USE_WS2812_DMA
- #if (USE_WS2812_CTYPE == NEO_GRB)
- strip = new NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
- #elif (USE_WS2812_CTYPE == NEO_BRG)
- strip = new NeoPixelBus<NeoBrgFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
- #elif (USE_WS2812_CTYPE == NEO_RBG)
- strip = new NeoPixelBus<NeoRbgFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
- #elif (USE_WS2812_CTYPE == NEO_RGBW)
- strip = new NeoPixelBus<NeoRgbwFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
- #elif (USE_WS2812_CTYPE == NEO_GRBW)
- strip = new NeoPixelBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
- #else // USE_WS2812_CTYPE
- strip = new NeoPixelBus<NeoRgbFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
- #endif // USE_WS2812_CTYPE
- #endif // USE_WS2812_DMA
- strip->Begin();
- Ws2812Clear();
- }
- void Ws2812Clear(void)
- {
- strip->ClearTo(0);
- strip->Show();
- ws_show_next = 1;
- }
- void Ws2812SetColor(uint16_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white)
- {
- #if (USE_WS2812_CTYPE > NEO_3LED)
- RgbwColor lcolor;
- lcolor.W = white;
- #else
- RgbColor lcolor;
- #endif
- lcolor.R = red;
- lcolor.G = green;
- lcolor.B = blue;
- if (led) {
- strip->SetPixelColor(led -1, lcolor); // Led 1 is strip Led 0 -> substract offset 1
- } else {
- // strip->ClearTo(lcolor); // Set WS2812_MAX_LEDS pixels
- for (uint16_t i = 0; i < Settings.light_pixels; i++) {
- strip->SetPixelColor(i, lcolor);
- }
- }
- if (!ws_suspend_update) {
- strip->Show();
- ws_show_next = 1;
- }
- }
- void Ws2812ForceSuspend (void) {
- ws_suspend_update = true;
- }
- void Ws2812ForceUpdate (void) {
- ws_suspend_update = false;
- strip->Show();
- ws_show_next = 1;
- }
- char* Ws2812GetColor(uint16_t led, char* scolor)
- {
- uint8_t sl_ledcolor[4];
- #if (USE_WS2812_CTYPE > NEO_3LED)
- RgbwColor lcolor = strip->GetPixelColor(led -1);
- sl_ledcolor[3] = lcolor.W;
- #else
- RgbColor lcolor = strip->GetPixelColor(led -1);
- #endif
- sl_ledcolor[0] = lcolor.R;
- sl_ledcolor[1] = lcolor.G;
- sl_ledcolor[2] = lcolor.B;
- scolor[0] = '\0';
- for (byte i = 0; i < light_subtype; i++) {
- if (Settings.flag.decimal_text) {
- snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]);
- } else {
- snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, sl_ledcolor[i]);
- }
- }
- return scolor;
- }
- void Ws2812ShowScheme(uint8_t scheme)
- {
- switch (scheme) {
- case 0: // Clock
- if ((1 == state_250mS) || (ws_show_next)) {
- Ws2812Clock();
- ws_show_next = 0;
- }
- break;
- default:
- if (1 == Settings.light_fade) {
- Ws2812Gradient(scheme -1);
- } else {
- Ws2812Bars(scheme -1);
- }
- ws_show_next = 1;
- break;
- }
- }
- #endif // USE_WS2812
|