PlatformIO package of the Teensy core framework compatible with GCC 10 & C++20
Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

384 lines
14KB

  1. #include "FastLED.h"
  2. #if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000)
  3. #warning "Requires FastLED 3.1 or later; check github for latest code."
  4. #endif
  5. #define NUM_LEDS 100
  6. #define LED_TYPE WS2811
  7. #define COLOR_ORDER GRB
  8. #define DATA_PIN 3
  9. //#define CLK_PIN 4
  10. #define VOLTS 12
  11. #define MAX_MA 4000
  12. // TwinkleFOX: Twinkling 'holiday' lights that fade in and out.
  13. // Colors are chosen from a palette; a few palettes are provided.
  14. //
  15. // This December 2015 implementation improves on the December 2014 version
  16. // in several ways:
  17. // - smoother fading, compatible with any colors and any palettes
  18. // - easier control of twinkle speed and twinkle density
  19. // - supports an optional 'background color'
  20. // - takes even less RAM: zero RAM overhead per pixel
  21. // - illustrates a couple of interesting techniques (uh oh...)
  22. //
  23. // The idea behind this (new) implementation is that there's one
  24. // basic, repeating pattern that each pixel follows like a waveform:
  25. // The brightness rises from 0..255 and then falls back down to 0.
  26. // The brightness at any given point in time can be determined as
  27. // as a function of time, for example:
  28. // brightness = sine( time ); // a sine wave of brightness over time
  29. //
  30. // So the way this implementation works is that every pixel follows
  31. // the exact same wave function over time. In this particular case,
  32. // I chose a sawtooth triangle wave (triwave8) rather than a sine wave,
  33. // but the idea is the same: brightness = triwave8( time ).
  34. //
  35. // Of course, if all the pixels used the exact same wave form, and
  36. // if they all used the exact same 'clock' for their 'time base', all
  37. // the pixels would brighten and dim at once -- which does not look
  38. // like twinkling at all.
  39. //
  40. // So to achieve random-looking twinkling, each pixel is given a
  41. // slightly different 'clock' signal. Some of the clocks run faster,
  42. // some run slower, and each 'clock' also has a random offset from zero.
  43. // The net result is that the 'clocks' for all the pixels are always out
  44. // of sync from each other, producing a nice random distribution
  45. // of twinkles.
  46. //
  47. // The 'clock speed adjustment' and 'time offset' for each pixel
  48. // are generated randomly. One (normal) approach to implementing that
  49. // would be to randomly generate the clock parameters for each pixel
  50. // at startup, and store them in some arrays. However, that consumes
  51. // a great deal of precious RAM, and it turns out to be totally
  52. // unnessary! If the random number generate is 'seeded' with the
  53. // same starting value every time, it will generate the same sequence
  54. // of values every time. So the clock adjustment parameters for each
  55. // pixel are 'stored' in a pseudo-random number generator! The PRNG
  56. // is reset, and then the first numbers out of it are the clock
  57. // adjustment parameters for the first pixel, the second numbers out
  58. // of it are the parameters for the second pixel, and so on.
  59. // In this way, we can 'store' a stable sequence of thousands of
  60. // random clock adjustment parameters in literally two bytes of RAM.
  61. //
  62. // There's a little bit of fixed-point math involved in applying the
  63. // clock speed adjustments, which are expressed in eighths. Each pixel's
  64. // clock speed ranges from 8/8ths of the system clock (i.e. 1x) to
  65. // 23/8ths of the system clock (i.e. nearly 3x).
  66. //
  67. // On a basic Arduino Uno or Leonardo, this code can twinkle 300+ pixels
  68. // smoothly at over 50 updates per seond.
  69. //
  70. // -Mark Kriegsman, December 2015
  71. CRGBArray<NUM_LEDS> leds;
  72. // Overall twinkle speed.
  73. // 0 (VERY slow) to 8 (VERY fast).
  74. // 4, 5, and 6 are recommended, default is 4.
  75. #define TWINKLE_SPEED 4
  76. // Overall twinkle density.
  77. // 0 (NONE lit) to 8 (ALL lit at once).
  78. // Default is 5.
  79. #define TWINKLE_DENSITY 5
  80. // How often to change color palettes.
  81. #define SECONDS_PER_PALETTE 30
  82. // Also: toward the bottom of the file is an array
  83. // called "ActivePaletteList" which controls which color
  84. // palettes are used; you can add or remove color palettes
  85. // from there freely.
  86. // Background color for 'unlit' pixels
  87. // Can be set to CRGB::Black if desired.
  88. CRGB gBackgroundColor = CRGB::Black;
  89. // Example of dim incandescent fairy light background color
  90. // CRGB gBackgroundColor = CRGB(CRGB::FairyLight).nscale8_video(16);
  91. // If AUTO_SELECT_BACKGROUND_COLOR is set to 1,
  92. // then for any palette where the first two entries
  93. // are the same, a dimmed version of that color will
  94. // automatically be used as the background color.
  95. #define AUTO_SELECT_BACKGROUND_COLOR 0
  96. // If COOL_LIKE_INCANDESCENT is set to 1, colors will
  97. // fade out slighted 'reddened', similar to how
  98. // incandescent bulbs change color as they get dim down.
  99. #define COOL_LIKE_INCANDESCENT 1
  100. CRGBPalette16 gCurrentPalette;
  101. CRGBPalette16 gTargetPalette;
  102. void setup() {
  103. delay( 3000 ); //safety startup delay
  104. FastLED.setMaxPowerInVoltsAndMilliamps( VOLTS, MAX_MA);
  105. FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS)
  106. .setCorrection(TypicalLEDStrip);
  107. chooseNextColorPalette(gTargetPalette);
  108. }
  109. void loop()
  110. {
  111. EVERY_N_SECONDS( SECONDS_PER_PALETTE ) {
  112. chooseNextColorPalette( gTargetPalette );
  113. }
  114. EVERY_N_MILLISECONDS( 10 ) {
  115. nblendPaletteTowardPalette( gCurrentPalette, gTargetPalette, 12);
  116. }
  117. drawTwinkles( leds);
  118. FastLED.show();
  119. }
  120. // This function loops over each pixel, calculates the
  121. // adjusted 'clock' that this pixel should use, and calls
  122. // "CalculateOneTwinkle" on each pixel. It then displays
  123. // either the twinkle color of the background color,
  124. // whichever is brighter.
  125. void drawTwinkles( CRGBSet& L)
  126. {
  127. // "PRNG16" is the pseudorandom number generator
  128. // It MUST be reset to the same starting value each time
  129. // this function is called, so that the sequence of 'random'
  130. // numbers that it generates is (paradoxically) stable.
  131. uint16_t PRNG16 = 11337;
  132. uint32_t clock32 = millis();
  133. // Set up the background color, "bg".
  134. // if AUTO_SELECT_BACKGROUND_COLOR == 1, and the first two colors of
  135. // the current palette are identical, then a deeply faded version of
  136. // that color is used for the background color
  137. CRGB bg;
  138. if( (AUTO_SELECT_BACKGROUND_COLOR == 1) &&
  139. (gCurrentPalette[0] == gCurrentPalette[1] )) {
  140. bg = gCurrentPalette[0];
  141. uint8_t bglight = bg.getAverageLight();
  142. if( bglight > 64) {
  143. bg.nscale8_video( 16); // very bright, so scale to 1/16th
  144. } else if( bglight > 16) {
  145. bg.nscale8_video( 64); // not that bright, so scale to 1/4th
  146. } else {
  147. bg.nscale8_video( 86); // dim, scale to 1/3rd.
  148. }
  149. } else {
  150. bg = gBackgroundColor; // just use the explicitly defined background color
  151. }
  152. uint8_t backgroundBrightness = bg.getAverageLight();
  153. for( CRGB& pixel: L) {
  154. PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
  155. uint16_t myclockoffset16= PRNG16; // use that number as clock offset
  156. PRNG16 = (uint16_t)(PRNG16 * 2053) + 1384; // next 'random' number
  157. // use that number as clock speed adjustment factor (in 8ths, from 8/8ths to 23/8ths)
  158. uint8_t myspeedmultiplierQ5_3 = ((((PRNG16 & 0xFF)>>4) + (PRNG16 & 0x0F)) & 0x0F) + 0x08;
  159. uint32_t myclock30 = (uint32_t)((clock32 * myspeedmultiplierQ5_3) >> 3) + myclockoffset16;
  160. uint8_t myunique8 = PRNG16 >> 8; // get 'salt' value for this pixel
  161. // We now have the adjusted 'clock' for this pixel, now we call
  162. // the function that computes what color the pixel should be based
  163. // on the "brightness = f( time )" idea.
  164. CRGB c = computeOneTwinkle( myclock30, myunique8);
  165. uint8_t cbright = c.getAverageLight();
  166. int16_t deltabright = cbright - backgroundBrightness;
  167. if( deltabright >= 32 || (!bg)) {
  168. // If the new pixel is significantly brighter than the background color,
  169. // use the new color.
  170. pixel = c;
  171. } else if( deltabright > 0 ) {
  172. // If the new pixel is just slightly brighter than the background color,
  173. // mix a blend of the new color and the background color
  174. pixel = blend( bg, c, deltabright * 8);
  175. } else {
  176. // if the new pixel is not at all brighter than the background color,
  177. // just use the background color.
  178. pixel = bg;
  179. }
  180. }
  181. }
  182. // This function takes a time in pseudo-milliseconds,
  183. // figures out brightness = f( time ), and also hue = f( time )
  184. // The 'low digits' of the millisecond time are used as
  185. // input to the brightness wave function.
  186. // The 'high digits' are used to select a color, so that the color
  187. // does not change over the course of the fade-in, fade-out
  188. // of one cycle of the brightness wave function.
  189. // The 'high digits' are also used to determine whether this pixel
  190. // should light at all during this cycle, based on the TWINKLE_DENSITY.
  191. CRGB computeOneTwinkle( uint32_t ms, uint8_t salt)
  192. {
  193. uint16_t ticks = ms >> (8-TWINKLE_SPEED);
  194. uint8_t fastcycle8 = ticks;
  195. uint16_t slowcycle16 = (ticks >> 8) + salt;
  196. slowcycle16 += sin8( slowcycle16);
  197. slowcycle16 = (slowcycle16 * 2053) + 1384;
  198. uint8_t slowcycle8 = (slowcycle16 & 0xFF) + (slowcycle16 >> 8);
  199. uint8_t bright = 0;
  200. if( ((slowcycle8 & 0x0E)/2) < TWINKLE_DENSITY) {
  201. bright = attackDecayWave8( fastcycle8);
  202. }
  203. uint8_t hue = slowcycle8 - salt;
  204. CRGB c;
  205. if( bright > 0) {
  206. c = ColorFromPalette( gCurrentPalette, hue, bright, NOBLEND);
  207. if( COOL_LIKE_INCANDESCENT == 1 ) {
  208. coolLikeIncandescent( c, fastcycle8);
  209. }
  210. } else {
  211. c = CRGB::Black;
  212. }
  213. return c;
  214. }
  215. // This function is like 'triwave8', which produces a
  216. // symmetrical up-and-down triangle sawtooth waveform, except that this
  217. // function produces a triangle wave with a faster attack and a slower decay:
  218. //
  219. // / \
  220. // / \
  221. // / \
  222. // / \
  223. //
  224. uint8_t attackDecayWave8( uint8_t i)
  225. {
  226. if( i < 86) {
  227. return i * 3;
  228. } else {
  229. i -= 86;
  230. return 255 - (i + (i/2));
  231. }
  232. }
  233. // This function takes a pixel, and if its in the 'fading down'
  234. // part of the cycle, it adjusts the color a little bit like the
  235. // way that incandescent bulbs fade toward 'red' as they dim.
  236. void coolLikeIncandescent( CRGB& c, uint8_t phase)
  237. {
  238. if( phase < 128) return;
  239. uint8_t cooling = (phase - 128) >> 4;
  240. c.g = qsub8( c.g, cooling);
  241. c.b = qsub8( c.b, cooling * 2);
  242. }
  243. // A mostly red palette with green accents and white trim.
  244. // "CRGB::Gray" is used as white to keep the brightness more uniform.
  245. const TProgmemRGBPalette16 RedGreenWhite_p FL_PROGMEM =
  246. { CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
  247. CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
  248. CRGB::Red, CRGB::Red, CRGB::Gray, CRGB::Gray,
  249. CRGB::Green, CRGB::Green, CRGB::Green, CRGB::Green };
  250. // A mostly (dark) green palette with red berries.
  251. #define Holly_Green 0x00580c
  252. #define Holly_Red 0xB00402
  253. const TProgmemRGBPalette16 Holly_p FL_PROGMEM =
  254. { Holly_Green, Holly_Green, Holly_Green, Holly_Green,
  255. Holly_Green, Holly_Green, Holly_Green, Holly_Green,
  256. Holly_Green, Holly_Green, Holly_Green, Holly_Green,
  257. Holly_Green, Holly_Green, Holly_Green, Holly_Red
  258. };
  259. // A red and white striped palette
  260. // "CRGB::Gray" is used as white to keep the brightness more uniform.
  261. const TProgmemRGBPalette16 RedWhite_p FL_PROGMEM =
  262. { CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
  263. CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray,
  264. CRGB::Red, CRGB::Red, CRGB::Red, CRGB::Red,
  265. CRGB::Gray, CRGB::Gray, CRGB::Gray, CRGB::Gray };
  266. // A mostly blue palette with white accents.
  267. // "CRGB::Gray" is used as white to keep the brightness more uniform.
  268. const TProgmemRGBPalette16 BlueWhite_p FL_PROGMEM =
  269. { CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
  270. CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
  271. CRGB::Blue, CRGB::Blue, CRGB::Blue, CRGB::Blue,
  272. CRGB::Blue, CRGB::Gray, CRGB::Gray, CRGB::Gray };
  273. // A pure "fairy light" palette with some brightness variations
  274. #define HALFFAIRY ((CRGB::FairyLight & 0xFEFEFE) / 2)
  275. #define QUARTERFAIRY ((CRGB::FairyLight & 0xFCFCFC) / 4)
  276. const TProgmemRGBPalette16 FairyLight_p FL_PROGMEM =
  277. { CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight,
  278. HALFFAIRY, HALFFAIRY, CRGB::FairyLight, CRGB::FairyLight,
  279. QUARTERFAIRY, QUARTERFAIRY, CRGB::FairyLight, CRGB::FairyLight,
  280. CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight, CRGB::FairyLight };
  281. // A palette of soft snowflakes with the occasional bright one
  282. const TProgmemRGBPalette16 Snow_p FL_PROGMEM =
  283. { 0x304048, 0x304048, 0x304048, 0x304048,
  284. 0x304048, 0x304048, 0x304048, 0x304048,
  285. 0x304048, 0x304048, 0x304048, 0x304048,
  286. 0x304048, 0x304048, 0x304048, 0xE0F0FF };
  287. // A palette reminiscent of large 'old-school' C9-size tree lights
  288. // in the five classic colors: red, orange, green, blue, and white.
  289. #define C9_Red 0xB80400
  290. #define C9_Orange 0x902C02
  291. #define C9_Green 0x046002
  292. #define C9_Blue 0x070758
  293. #define C9_White 0x606820
  294. const TProgmemRGBPalette16 RetroC9_p FL_PROGMEM =
  295. { C9_Red, C9_Orange, C9_Red, C9_Orange,
  296. C9_Orange, C9_Red, C9_Orange, C9_Red,
  297. C9_Green, C9_Green, C9_Green, C9_Green,
  298. C9_Blue, C9_Blue, C9_Blue,
  299. C9_White
  300. };
  301. // A cold, icy pale blue palette
  302. #define Ice_Blue1 0x0C1040
  303. #define Ice_Blue2 0x182080
  304. #define Ice_Blue3 0x5080C0
  305. const TProgmemRGBPalette16 Ice_p FL_PROGMEM =
  306. {
  307. Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
  308. Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
  309. Ice_Blue1, Ice_Blue1, Ice_Blue1, Ice_Blue1,
  310. Ice_Blue2, Ice_Blue2, Ice_Blue2, Ice_Blue3
  311. };
  312. // Add or remove palette names from this list to control which color
  313. // palettes are used, and in what order.
  314. const TProgmemRGBPalette16* ActivePaletteList[] = {
  315. &RetroC9_p,
  316. &BlueWhite_p,
  317. &RainbowColors_p,
  318. &FairyLight_p,
  319. &RedGreenWhite_p,
  320. &PartyColors_p,
  321. &RedWhite_p,
  322. &Snow_p,
  323. &Holly_p,
  324. &Ice_p
  325. };
  326. // Advance to the next color palette in the list (above).
  327. void chooseNextColorPalette( CRGBPalette16& pal)
  328. {
  329. const uint8_t numberOfPalettes = sizeof(ActivePaletteList) / sizeof(ActivePaletteList[0]);
  330. static uint8_t whichPalette = -1;
  331. whichPalette = addmod8( whichPalette, 1, numberOfPalettes);
  332. pal = *(ActivePaletteList[whichPalette]);
  333. }