xplg_ws2812.ino 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428
  1. /*
  2. xplg_ws2812.ino - ws2812 led string support for Sonoff-Tasmota
  3. Copyright (C) 2018 Heiko Krupp and Theo Arends
  4. This program is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. #ifdef USE_WS2812
  16. /*********************************************************************************************\
  17. * WS2812 RGB / RGBW Leds using NeopixelBus library
  18. \*********************************************************************************************/
  19. #include <NeoPixelBus.h>
  20. #ifdef USE_WS2812_DMA
  21. #if (USE_WS2812_CTYPE == NEO_GRB)
  22. NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod> *strip = NULL;
  23. #elif (USE_WS2812_CTYPE == NEO_BRG)
  24. NeoPixelBus<NeoBrgFeature, Neo800KbpsMethod> *strip = NULL;
  25. #elif (USE_WS2812_CTYPE == NEO_RBG)
  26. NeoPixelBus<NeoRbgFeature, Neo800KbpsMethod> *strip = NULL;
  27. #elif (USE_WS2812_CTYPE == NEO_RGBW)
  28. NeoPixelBus<NeoRgbwFeature, Neo800KbpsMethod> *strip = NULL;
  29. #elif (USE_WS2812_CTYPE == NEO_GRBW)
  30. NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod> *strip = NULL;
  31. #else // USE_WS2812_CTYPE
  32. NeoPixelBus<NeoRgbFeature, Neo800KbpsMethod> *strip = NULL;
  33. #endif // USE_WS2812_CTYPE
  34. #else // USE_WS2812_DMA
  35. #if (USE_WS2812_CTYPE == NEO_GRB)
  36. NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
  37. #elif (USE_WS2812_CTYPE == NEO_BRG)
  38. NeoPixelBus<NeoBrgFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
  39. #elif (USE_WS2812_CTYPE == NEO_RBG)
  40. NeoPixelBus<NeoRbgFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
  41. #elif (USE_WS2812_CTYPE == NEO_RGBW)
  42. NeoPixelBus<NeoRgbwFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
  43. #elif (USE_WS2812_CTYPE == NEO_GRBW)
  44. NeoPixelBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
  45. #else // USE_WS2812_CTYPE
  46. NeoPixelBus<NeoRgbFeature, NeoEsp8266BitBang800KbpsMethod> *strip = NULL;
  47. #endif // USE_WS2812_CTYPE
  48. #endif // USE_WS2812_DMA
  49. struct WsColor {
  50. uint8_t red, green, blue;
  51. };
  52. struct ColorScheme {
  53. WsColor* colors;
  54. uint8_t count;
  55. };
  56. WsColor kIncandescent[2] = { 255,140,20, 0,0,0 };
  57. WsColor kRgb[3] = { 255,0,0, 0,255,0, 0,0,255 };
  58. WsColor kChristmas[2] = { 255,0,0, 0,255,0 };
  59. WsColor kHanukkah[2] = { 0,0,255, 255,255,255 };
  60. WsColor kwanzaa[3] = { 255,0,0, 0,0,0, 0,255,0 };
  61. WsColor kRainbow[7] = { 255,0,0, 255,128,0, 255,255,0, 0,255,0, 0,0,255, 128,0,255, 255,0,255 };
  62. WsColor kFire[3] = { 255,0,0, 255,102,0, 255,192,0 };
  63. ColorScheme kSchemes[WS2812_SCHEMES] = {
  64. kIncandescent, 2,
  65. kRgb, 3,
  66. kChristmas, 2,
  67. kHanukkah, 2,
  68. kwanzaa, 3,
  69. kRainbow, 7,
  70. kFire, 3 };
  71. uint8_t kWidth[5] = {
  72. 1, // Small
  73. 2, // Medium
  74. 4, // Large
  75. 8, // Largest
  76. 255 }; // All
  77. uint8_t kWsRepeat[5] = {
  78. 8, // Small
  79. 6, // Medium
  80. 4, // Large
  81. 2, // Largest
  82. 1 }; // All
  83. uint8_t ws_show_next = 1;
  84. bool ws_suspend_update = false;
  85. /********************************************************************************************/
  86. void Ws2812StripShow(void)
  87. {
  88. #if (USE_WS2812_CTYPE > NEO_3LED)
  89. RgbwColor c;
  90. #else
  91. RgbColor c;
  92. #endif
  93. if (Settings.light_correction) {
  94. for (uint16_t i = 0; i < Settings.light_pixels; i++) {
  95. c = strip->GetPixelColor(i);
  96. c.R = ledTable[c.R];
  97. c.G = ledTable[c.G];
  98. c.B = ledTable[c.B];
  99. #if (USE_WS2812_CTYPE > NEO_3LED)
  100. c.W = ledTable[c.W];
  101. #endif
  102. strip->SetPixelColor(i, c);
  103. }
  104. }
  105. strip->Show();
  106. }
  107. int mod(int a, int b)
  108. {
  109. int ret = a % b;
  110. if (ret < 0) ret += b;
  111. return ret;
  112. }
  113. void Ws2812UpdatePixelColor(int position, struct WsColor hand_color, float offset)
  114. {
  115. #if (USE_WS2812_CTYPE > NEO_3LED)
  116. RgbwColor color;
  117. #else
  118. RgbColor color;
  119. #endif
  120. uint16_t mod_position = mod(position, (int)Settings.light_pixels);
  121. color = strip->GetPixelColor(mod_position);
  122. float dimmer = 100 / (float)Settings.light_dimmer;
  123. color.R = tmin(color.R + ((hand_color.red / dimmer) * offset), 255);
  124. color.G = tmin(color.G + ((hand_color.green / dimmer) * offset), 255);
  125. color.B = tmin(color.B + ((hand_color.blue / dimmer) * offset), 255);
  126. strip->SetPixelColor(mod_position, color);
  127. }
  128. void Ws2812UpdateHand(int position, uint8_t index)
  129. {
  130. position = (position + Settings.light_rotation) % Settings.light_pixels;
  131. if (Settings.flag.ws_clock_reverse) position = Settings.light_pixels -position;
  132. WsColor hand_color = { Settings.ws_color[index][WS_RED], Settings.ws_color[index][WS_GREEN], Settings.ws_color[index][WS_BLUE] };
  133. Ws2812UpdatePixelColor(position, hand_color, 1);
  134. uint8_t range = 1;
  135. if (index < WS_MARKER) range = ((Settings.ws_width[index] -1) / 2) +1;
  136. for (uint8_t h = 1; h < range; h++) {
  137. float offset = (float)(range - h) / (float)range;
  138. Ws2812UpdatePixelColor(position -h, hand_color, offset);
  139. Ws2812UpdatePixelColor(position +h, hand_color, offset);
  140. }
  141. }
  142. void Ws2812Clock(void)
  143. {
  144. strip->ClearTo(0); // Reset strip
  145. int clksize = 60000 / (int)Settings.light_pixels;
  146. Ws2812UpdateHand((RtcTime.second * 1000) / clksize, WS_SECOND);
  147. Ws2812UpdateHand((RtcTime.minute * 1000) / clksize, WS_MINUTE);
  148. Ws2812UpdateHand(((RtcTime.hour % 12) * (5000 / clksize)) + ((RtcTime.minute * 1000) / (12 * clksize)), WS_HOUR);
  149. if (Settings.ws_color[WS_MARKER][WS_RED] + Settings.ws_color[WS_MARKER][WS_GREEN] + Settings.ws_color[WS_MARKER][WS_BLUE]) {
  150. for (byte i = 0; i < 12; i++) {
  151. Ws2812UpdateHand((i * 5000) / clksize, WS_MARKER);
  152. }
  153. }
  154. Ws2812StripShow();
  155. }
  156. void Ws2812GradientColor(uint8_t schemenr, struct WsColor* mColor, uint16_t range, uint16_t gradRange, uint16_t i)
  157. {
  158. /*
  159. * Compute the color of a pixel at position i using a gradient of the color scheme.
  160. * This function is used internally by the gradient function.
  161. */
  162. ColorScheme scheme = kSchemes[schemenr];
  163. uint16_t curRange = i / range;
  164. uint16_t rangeIndex = i % range;
  165. uint16_t colorIndex = rangeIndex / gradRange;
  166. uint16_t start = colorIndex;
  167. uint16_t end = colorIndex +1;
  168. if (curRange % 2 != 0) {
  169. start = (scheme.count -1) - start;
  170. end = (scheme.count -1) - end;
  171. }
  172. float dimmer = 100 / (float)Settings.light_dimmer;
  173. float fmyRed = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].red, scheme.colors[end].red) / dimmer;
  174. float fmyGrn = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].green, scheme.colors[end].green) / dimmer;
  175. float fmyBlu = (float)map(rangeIndex % gradRange, 0, gradRange, scheme.colors[start].blue, scheme.colors[end].blue) / dimmer;
  176. mColor->red = (uint8_t)fmyRed;
  177. mColor->green = (uint8_t)fmyGrn;
  178. mColor->blue = (uint8_t)fmyBlu;
  179. }
  180. void Ws2812Gradient(uint8_t schemenr)
  181. {
  182. /*
  183. * This routine courtesy Tony DiCola (Adafruit)
  184. * Display a gradient of colors for the current color scheme.
  185. * Repeat is the number of repetitions of the gradient (pick a multiple of 2 for smooth looping of the gradient).
  186. */
  187. #if (USE_WS2812_CTYPE > NEO_3LED)
  188. RgbwColor c;
  189. c.W = 0;
  190. #else
  191. RgbColor c;
  192. #endif
  193. ColorScheme scheme = kSchemes[schemenr];
  194. if (scheme.count < 2) return;
  195. uint8_t repeat = kWsRepeat[Settings.light_width]; // number of scheme.count per ledcount
  196. uint16_t range = (uint16_t)ceil((float)Settings.light_pixels / (float)repeat);
  197. uint16_t gradRange = (uint16_t)ceil((float)range / (float)(scheme.count - 1));
  198. uint16_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10);
  199. uint16_t offset = speed > 0 ? strip_timer_counter / speed : 0;
  200. WsColor oldColor, currentColor;
  201. Ws2812GradientColor(schemenr, &oldColor, range, gradRange, offset);
  202. currentColor = oldColor;
  203. for (uint16_t i = 0; i < Settings.light_pixels; i++) {
  204. if (kWsRepeat[Settings.light_width] > 1) {
  205. Ws2812GradientColor(schemenr, &currentColor, range, gradRange, i +offset);
  206. }
  207. if (Settings.light_speed > 0) {
  208. // Blend old and current color based on time for smooth movement.
  209. c.R = map(strip_timer_counter % speed, 0, speed, oldColor.red, currentColor.red);
  210. c.G = map(strip_timer_counter % speed, 0, speed, oldColor.green, currentColor.green);
  211. c.B = map(strip_timer_counter % speed, 0, speed, oldColor.blue, currentColor.blue);
  212. }
  213. else {
  214. // No animation, just use the current color.
  215. c.R = currentColor.red;
  216. c.G = currentColor.green;
  217. c.B = currentColor.blue;
  218. }
  219. strip->SetPixelColor(i, c);
  220. oldColor = currentColor;
  221. }
  222. Ws2812StripShow();
  223. }
  224. void Ws2812Bars(uint8_t schemenr)
  225. {
  226. /*
  227. * This routine courtesy Tony DiCola (Adafruit)
  228. * Display solid bars of color for the current color scheme.
  229. * Width is the width of each bar in pixels/lights.
  230. */
  231. #if (USE_WS2812_CTYPE > NEO_3LED)
  232. RgbwColor c;
  233. c.W = 0;
  234. #else
  235. RgbColor c;
  236. #endif
  237. uint16_t i;
  238. ColorScheme scheme = kSchemes[schemenr];
  239. uint16_t maxSize = Settings.light_pixels / scheme.count;
  240. if (kWidth[Settings.light_width] > maxSize) maxSize = 0;
  241. uint16_t speed = ((Settings.light_speed * 2) -1) * (STATES / 10);
  242. uint8_t offset = speed > 0 ? strip_timer_counter / speed : 0;
  243. WsColor mcolor[scheme.count];
  244. memcpy(mcolor, scheme.colors, sizeof(mcolor));
  245. float dimmer = 100 / (float)Settings.light_dimmer;
  246. for (i = 0; i < scheme.count; i++) {
  247. float fmyRed = (float)mcolor[i].red / dimmer;
  248. float fmyGrn = (float)mcolor[i].green / dimmer;
  249. float fmyBlu = (float)mcolor[i].blue / dimmer;
  250. mcolor[i].red = (uint8_t)fmyRed;
  251. mcolor[i].green = (uint8_t)fmyGrn;
  252. mcolor[i].blue = (uint8_t)fmyBlu;
  253. }
  254. uint8_t colorIndex = offset % scheme.count;
  255. for (i = 0; i < Settings.light_pixels; i++) {
  256. if (maxSize) colorIndex = ((i + offset) % (scheme.count * kWidth[Settings.light_width])) / kWidth[Settings.light_width];
  257. c.R = mcolor[colorIndex].red;
  258. c.G = mcolor[colorIndex].green;
  259. c.B = mcolor[colorIndex].blue;
  260. strip->SetPixelColor(i, c);
  261. }
  262. Ws2812StripShow();
  263. }
  264. /*********************************************************************************************\
  265. * Public
  266. \*********************************************************************************************/
  267. void Ws2812Init(void)
  268. {
  269. #ifdef USE_WS2812_DMA
  270. #if (USE_WS2812_CTYPE == NEO_GRB)
  271. strip = new NeoPixelBus<NeoGrbFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
  272. #elif (USE_WS2812_CTYPE == NEO_BRG)
  273. strip = new NeoPixelBus<NeoBrgFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
  274. #elif (USE_WS2812_CTYPE == NEO_RBG)
  275. strip = new NeoPixelBus<NeoRbgFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
  276. #elif (USE_WS2812_CTYPE == NEO_RGBW)
  277. strip = new NeoPixelBus<NeoRgbwFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
  278. #elif (USE_WS2812_CTYPE == NEO_GRBW)
  279. strip = new NeoPixelBus<NeoGrbwFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
  280. #else // USE_WS2812_CTYPE
  281. strip = new NeoPixelBus<NeoRgbFeature, Neo800KbpsMethod>(WS2812_MAX_LEDS); // For Esp8266, the Pin is omitted and it uses GPIO3 due to DMA hardware use.
  282. #endif // USE_WS2812_CTYPE
  283. #else // USE_WS2812_DMA
  284. #if (USE_WS2812_CTYPE == NEO_GRB)
  285. strip = new NeoPixelBus<NeoGrbFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
  286. #elif (USE_WS2812_CTYPE == NEO_BRG)
  287. strip = new NeoPixelBus<NeoBrgFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
  288. #elif (USE_WS2812_CTYPE == NEO_RBG)
  289. strip = new NeoPixelBus<NeoRbgFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
  290. #elif (USE_WS2812_CTYPE == NEO_RGBW)
  291. strip = new NeoPixelBus<NeoRgbwFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
  292. #elif (USE_WS2812_CTYPE == NEO_GRBW)
  293. strip = new NeoPixelBus<NeoGrbwFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
  294. #else // USE_WS2812_CTYPE
  295. strip = new NeoPixelBus<NeoRgbFeature, NeoEsp8266BitBang800KbpsMethod>(WS2812_MAX_LEDS, pin[GPIO_WS2812]);
  296. #endif // USE_WS2812_CTYPE
  297. #endif // USE_WS2812_DMA
  298. strip->Begin();
  299. Ws2812Clear();
  300. }
  301. void Ws2812Clear(void)
  302. {
  303. strip->ClearTo(0);
  304. strip->Show();
  305. ws_show_next = 1;
  306. }
  307. void Ws2812SetColor(uint16_t led, uint8_t red, uint8_t green, uint8_t blue, uint8_t white)
  308. {
  309. #if (USE_WS2812_CTYPE > NEO_3LED)
  310. RgbwColor lcolor;
  311. lcolor.W = white;
  312. #else
  313. RgbColor lcolor;
  314. #endif
  315. lcolor.R = red;
  316. lcolor.G = green;
  317. lcolor.B = blue;
  318. if (led) {
  319. strip->SetPixelColor(led -1, lcolor); // Led 1 is strip Led 0 -> substract offset 1
  320. } else {
  321. // strip->ClearTo(lcolor); // Set WS2812_MAX_LEDS pixels
  322. for (uint16_t i = 0; i < Settings.light_pixels; i++) {
  323. strip->SetPixelColor(i, lcolor);
  324. }
  325. }
  326. if (!ws_suspend_update) {
  327. strip->Show();
  328. ws_show_next = 1;
  329. }
  330. }
  331. void Ws2812ForceSuspend (void) {
  332. ws_suspend_update = true;
  333. }
  334. void Ws2812ForceUpdate (void) {
  335. ws_suspend_update = false;
  336. strip->Show();
  337. ws_show_next = 1;
  338. }
  339. char* Ws2812GetColor(uint16_t led, char* scolor)
  340. {
  341. uint8_t sl_ledcolor[4];
  342. #if (USE_WS2812_CTYPE > NEO_3LED)
  343. RgbwColor lcolor = strip->GetPixelColor(led -1);
  344. sl_ledcolor[3] = lcolor.W;
  345. #else
  346. RgbColor lcolor = strip->GetPixelColor(led -1);
  347. #endif
  348. sl_ledcolor[0] = lcolor.R;
  349. sl_ledcolor[1] = lcolor.G;
  350. sl_ledcolor[2] = lcolor.B;
  351. scolor[0] = '\0';
  352. for (byte i = 0; i < light_subtype; i++) {
  353. if (Settings.flag.decimal_text) {
  354. snprintf_P(scolor, 25, PSTR("%s%s%d"), scolor, (i > 0) ? "," : "", sl_ledcolor[i]);
  355. } else {
  356. snprintf_P(scolor, 25, PSTR("%s%02X"), scolor, sl_ledcolor[i]);
  357. }
  358. }
  359. return scolor;
  360. }
  361. void Ws2812ShowScheme(uint8_t scheme)
  362. {
  363. switch (scheme) {
  364. case 0: // Clock
  365. if ((1 == state_250mS) || (ws_show_next)) {
  366. Ws2812Clock();
  367. ws_show_next = 0;
  368. }
  369. break;
  370. default:
  371. if (1 == Settings.light_fade) {
  372. Ws2812Gradient(scheme -1);
  373. } else {
  374. Ws2812Bars(scheme -1);
  375. }
  376. ws_show_next = 1;
  377. break;
  378. }
  379. }
  380. #endif // USE_WS2812