#include #ifdef __AVR_ATtiny85__ #include #endif // Begin config section #define PIN 0 #define BRIGHTNESS 64 #define MODE_CHANGE_TIME 10000 #define COLOR_MIN 0 #define COLOR_MAX 255 #define STEPS_MIN 5 #define STEPS_MAX 10 #define SLOW_INTERVAL_MIN 25 #define SLOW_INTERVAL_MAX 55 #define RAINBOW_CYCLE_MIN_INTERVAL 1 #define RAINBOW_CYCLE_MAX_INTERVAL 10 #define BLINK_MIN_INTERVAL 250 #define BLINK_MAX_INTERVAL 500 // End config section enum pattern { RAINBOW_CYCLE, COLOR_WIPE, DOUBLE_COLOR_WIPE, SCANNER, DOUBLE_SCANNER, RANDOM, DOUBLE_RANDOM, BLINK, DOT }; class NeoPatterns : public Adafruit_NeoPixel { public: pattern ActivePattern; uint16_t Interval; unsigned long lastUpdate; uint32_t PixelColor; uint16_t TotalSteps; uint16_t Index; void (*OnComplete)(); NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)()) : Adafruit_NeoPixel(pixels, pin, type) { OnComplete = callback; } void Update() { if ((millis() - lastUpdate) > Interval) { lastUpdate = millis(); switch (ActivePattern) { case RAINBOW_CYCLE: RainbowCycleUpdate(); break; case COLOR_WIPE: ColorWipeUpdate(); break; case DOUBLE_COLOR_WIPE: DoubleColorWipeUpdate(); break; case SCANNER: ScannerUpdate(); break; case DOUBLE_SCANNER: DoubleScannerUpdate(); break; case RANDOM: RandomUpdate(); break; case DOUBLE_RANDOM: DoubleRandomUpdate(); break; case BLINK: BlinkUpdate(); break; case DOT: DotUpdate(); break; default: break; } } } void Increment() { show(); Index++; if (Index >= TotalSteps) { Index = 0; OnComplete(); } } void RainbowCycle(uint16_t interval) { ActivePattern = RAINBOW_CYCLE; Interval = interval; TotalSteps = 255; Index = 0; } void RainbowCycleUpdate() { for (int i = 0; i < numPixels(); i++) { setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255)); } Increment(); } void ColorWipe(uint32_t color, uint16_t interval) { ActivePattern = COLOR_WIPE; Interval = interval; TotalSteps = numPixels(); PixelColor = color; Index = 0; } void ColorWipeUpdate() { setPixelColor(Index, PixelColor); Increment(); } void DoubleColorWipe(uint32_t color, uint16_t interval) { ActivePattern = DOUBLE_COLOR_WIPE; Interval = interval; TotalSteps = numPixels() / 2; PixelColor = color; Index = 0; } void DoubleColorWipeUpdate() { DoubleSet(Index, PixelColor); Increment(); } void Scanner(uint32_t color, uint16_t interval) { ActivePattern = SCANNER; Interval = interval; TotalSteps = (numPixels() - 1) * 2; PixelColor = color; Index = 0; } void ScannerUpdate() { for (int i = 0; i < numPixels(); i++) { uint32_t c; if (i == Index || i == TotalSteps - Index) { c = PixelColor; } else { c = DimColor(getPixelColor(i)); } setPixelColor(i, c); } Increment(); } void DoubleScanner(uint32_t color, uint16_t interval) { ActivePattern = DOUBLE_SCANNER; Interval = interval; TotalSteps = (numPixels() / 2); PixelColor = color; Index = 0; } void DoubleScannerUpdate() { for (int i = 0; i < numPixels() / 2; i++) { uint32_t c; if (i == Index) { c = PixelColor; } else { c = DimColor(getPixelColor(i)); } DoubleSet(i, c); } Increment(); } void Random(uint32_t color, uint16_t steps, uint16_t interval) { ActivePattern = RANDOM; Interval = interval; TotalSteps = steps; PixelColor = color; Index = 0; } void RandomUpdate() { setPixelColor(random(numPixels()), PixelColor); Increment(); } void DoubleRandom(uint32_t color, uint16_t steps, uint16_t interval) { ActivePattern = DOUBLE_RANDOM; Interval = interval; TotalSteps = steps; PixelColor = color; Index = 0; } void DoubleRandomUpdate() { int i = random(numPixels() / 2); // this saves 4 bytes instead of calling random in the function call of DoubleSet DoubleSet(i, PixelColor); Increment(); } void Blink(uint32_t color, uint16_t steps, uint16_t interval) { ActivePattern = BLINK; Interval = interval; if (steps % 2 != 0) { steps++; } TotalSteps = steps; PixelColor = color; Index = 0; } void BlinkUpdate() { uint32_t c; if (Index % 2 == 0) { c = PixelColor; } else { c = Color(0, 0, 0); } ColorSet(c); Increment(); } void Dot(uint32_t color, uint16_t steps, uint16_t interval) { ActivePattern = DOT; Interval = interval; TotalSteps = steps; PixelColor = color; Index = 0; } void DotUpdate() { ColorSet(Color(0, 0, 0)); show(); setPixelColor(random(numPixels()), PixelColor); Increment(); } uint32_t DimColor(uint32_t color) { return Color(((color >> 16) & 0xFF) >> 1, ((color >> 8) & 0xFF) >> 1, (color & 0xFF) >> 1); } uint32_t Wheel(byte WheelPos) { WheelPos = 255 - WheelPos; if (WheelPos < 85) { return Color(255 - WheelPos * 3, 0, WheelPos * 3); } else if (WheelPos < 170) { WheelPos -= 85; return Color(0, WheelPos * 3, 255 - WheelPos * 3); } else { WheelPos -= 170; return Color(WheelPos * 3, 255 - WheelPos * 3, 0); } } void ColorSet(uint32_t color) { for (int i = 0; i < numPixels(); i++) { setPixelColor(i, color); } } void DoubleSet(uint16_t i, uint32_t color) { setPixelColor(i, color); setPixelColor(numPixels() - 1 - i, color); } }; void RingsComplete(); NeoPatterns Rings(32, PIN, NEO_GRB + NEO_KHZ800, &RingsComplete); uint8_t mode = 0; uint32_t prev_time; void setup() { #ifdef __AVR_ATtiny85__ if (F_CPU == 16000000) clock_prescale_set(clock_div_1); #endif Rings.begin(); Rings.setBrightness(BRIGHTNESS); Rings.RainbowCycle(random(RAINBOW_CYCLE_MIN_INTERVAL, RAINBOW_CYCLE_MAX_INTERVAL + 1)); prev_time = millis(); } void loop() { Rings.Update(); /* We could store the return of the first millis call in a variable. But this routine is fast, so we only loose some milliseconds (if even) and we are not that time critical, that the MODE changes extacly after MODE_CHANGE_TIME. Calling millis twice, saves us 8 bytes, which is critical for the small storage space we have. */ if ((millis() - prev_time) >= MODE_CHANGE_TIME) { mode = random(0, 9); prev_time = millis(); } } void RingsComplete() { uint16_t slow_interval = random(SLOW_INTERVAL_MIN, SLOW_INTERVAL_MAX + 1); uint16_t steps = random(STEPS_MIN, STEPS_MAX + 1); uint32_t color = Rings.Wheel(random(COLOR_MIN, COLOR_MAX + 1)); switch (mode) { case 0: Rings.RainbowCycle(random(RAINBOW_CYCLE_MIN_INTERVAL, RAINBOW_CYCLE_MAX_INTERVAL + 1)); break; case 1: Rings.ColorWipe(color, slow_interval); break; case 2: Rings.DoubleColorWipe(color, slow_interval); break; case 3: Rings.Scanner(color, slow_interval); break; case 4: Rings.DoubleScanner(color, slow_interval); break; case 5: Rings.Random(color, steps, slow_interval); break; case 6: Rings.DoubleRandom(color, steps, slow_interval); break; case 7: Rings.Blink(color, steps, random(BLINK_MIN_INTERVAL, BLINK_MAX_INTERVAL + 1)); break; case 8: Rings.Dot(color, steps, slow_interval); break; default: break; } }