├── patterns ├── BeaconUtil.h ├── RandomColors.h ├── Gradient.h ├── Usa.h ├── Rainbow.h ├── LarsonScanner.h ├── Generic.h ├── ColorBars.h ├── Sos.h ├── BeaconScanner.h ├── AlternatingColors.h ├── Pattern.h ├── Lightning.h ├── BeaconPulse2.h ├── BeaconPulse.h └── Fire2012.h ├── animations ├── Animation.h └── Fade.h ├── AnimationController.cpp ├── Leds.h ├── AnimationController.h ├── PatternController.h ├── README.md └── PatternController.cpp /patterns/BeaconUtil.h: -------------------------------------------------------------------------------- 1 | #ifndef BEACONUTIL_H 2 | #define BEACONUTIL_H 3 | 4 | #include 5 | 6 | static constexpr uint8_t LEDS_PER_STICK = 8; 7 | static constexpr uint8_t STICKS_PER_BEACON = 3; 8 | static constexpr uint8_t LEDS_PER_BEACON = (STICKS_PER_BEACON * LEDS_PER_STICK); 9 | 10 | uint8_t get_stick_index(uint16_t index){ 11 | uint8_t beacon_index = index % LEDS_PER_BEACON; 12 | uint8_t stick_index = beacon_index % LEDS_PER_STICK; 13 | if(beacon_index >= LEDS_PER_STICK && beacon_index < (LEDS_PER_STICK * 2)) { 14 | stick_index = LEDS_PER_STICK - stick_index - 1; 15 | } 16 | return stick_index; 17 | } 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /patterns/RandomColors.h: -------------------------------------------------------------------------------- 1 | #ifndef RANDOMCOLORS_H 2 | #define RANDOMCOLORS_H 3 | 4 | #include "Pattern.h" 5 | 6 | class RandomColors: public Pattern{ 7 | 8 | public: 9 | 10 | RandomColors(uint8_t spacing) : spacing(spacing){} 11 | 12 | void call(LedArray leds, uint16_t frame){ 13 | uint8_t i = 0; 14 | while(!leds.get(i) && i < spacing){ 15 | i++; 16 | } 17 | 18 | //all of the _leds up to spacing were black 19 | if(i == spacing){ 20 | leds.push(random_color()); 21 | } else { 22 | leds.push(CRGB::Black); 23 | } 24 | } 25 | 26 | protected: 27 | uint8_t spacing; 28 | 29 | }; 30 | 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /patterns/Gradient.h: -------------------------------------------------------------------------------- 1 | #ifndef GRADIENT_H 2 | #define GRADIENT_H 3 | 4 | #include "Pattern.h" 5 | 6 | class Gradient: public Pattern{ 7 | 8 | public: 9 | 10 | Gradient(){ 11 | randomize(); 12 | } 13 | 14 | Gradient(const CRGB mod_color) : mod(mod_color){}; 15 | 16 | void call(LedArray leds, uint16_t frame){ 17 | for (uint16_t i = 0; i < leds.length; i++) 18 | { 19 | uint8_t x = 4 * (frame - 2 * i); 20 | leds.head[i] = CRGB(mod.r - x, mod.g - x, mod.b - x).nscale8_video(127); 21 | } 22 | } 23 | 24 | void randomize(){ 25 | mod = random_color(); 26 | } 27 | 28 | protected: 29 | CRGB mod = random_color(); 30 | }; 31 | 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /patterns/Usa.h: -------------------------------------------------------------------------------- 1 | #ifndef USA_H 2 | #define USA_H 3 | 4 | #include "Pattern.h" 5 | #include "Generic.h" 6 | 7 | 8 | 9 | class Usa: public Pattern{ 10 | 11 | static const CRGB usa_pattern[3]; 12 | 13 | public: 14 | 15 | Usa(){ 16 | usa_generic = Generic(usa_pattern, 3); 17 | }; 18 | 19 | Usa(uint8_t repeat_length) { 20 | usa_generic = Generic(usa_pattern, 3, repeat_length); 21 | } 22 | 23 | void call(LedArray leds, uint16_t frame){ 24 | usa_generic.call(leds, frame); 25 | } 26 | 27 | 28 | protected: 29 | Generic usa_generic; 30 | }; 31 | 32 | const CRGB Usa::usa_pattern[3] = {CRGB::Red, CRGB::White, CRGB::Blue}; 33 | 34 | #endif 35 | -------------------------------------------------------------------------------- /patterns/Rainbow.h: -------------------------------------------------------------------------------- 1 | #ifndef RAINBOW_H 2 | #define RAINBOW_H 3 | 4 | #include "Pattern.h" 5 | 6 | class Rainbow: public Pattern{ 7 | 8 | public: 9 | 10 | Rainbow(int8_t rainbow_density) : rainbow_density(rainbow_density){} 11 | 12 | void call(LedArray leds, uint16_t frame){ 13 | leds.push(CRGB::Black); 14 | if(rainbow_density >-2 && rainbow_density <2){ 15 | fill_rainbow(leds.head, 1, frame); 16 | } else if(rainbow_density>1){ 17 | fill_rainbow(leds.head, 1, frame * rainbow_density); 18 | } else { 19 | fill_rainbow(leds.head, 1, (frame / -rainbow_density)); 20 | } 21 | 22 | } 23 | 24 | protected: 25 | int8_t rainbow_density; 26 | 27 | }; 28 | 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /patterns/LarsonScanner.h: -------------------------------------------------------------------------------- 1 | #ifndef LARSONSCANNER_H 2 | #define LARSONSCANNER_H 3 | 4 | #include "Pattern.h" 5 | 6 | class LarsonScanner: public Pattern{ 7 | 8 | public: 9 | 10 | LarsonScanner(){ 11 | randomize(); 12 | }; 13 | 14 | LarsonScanner(CRGB color1, CRGB color2) : color1(color1), color2(color2){} 15 | 16 | void call(LedArray leds, uint16_t frame){ 17 | // 0 == forwards, 1 = backwards 18 | int direction = (frame / leds.length) % 2; 19 | if(direction == 0){ 20 | leds.push(color1); 21 | } else { 22 | leds.push_back(color2); 23 | } 24 | } 25 | 26 | void randomize(){ 27 | fillRandomContrastingColors(color1, color2); 28 | } 29 | 30 | protected: 31 | CRGB color1; 32 | CRGB color2; 33 | 34 | }; 35 | 36 | 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /patterns/Generic.h: -------------------------------------------------------------------------------- 1 | #ifndef GENERIC_H 2 | #define GENERIC_H 3 | 4 | #include "Pattern.h" 5 | 6 | class Generic: public Pattern{ 7 | 8 | public: 9 | 10 | Generic(){} 11 | 12 | Generic(const CRGB * pattern, const int8_t pattern_length) : 13 | pattern(pattern), pattern_length(pattern_length){} 14 | 15 | Generic(const CRGB * pattern, const int8_t pattern_length, const int8_t repeat_length) : 16 | pattern(pattern), pattern_length(pattern_length), repeat_length(repeat_length){} 17 | 18 | void call(LedArray leds, uint16_t frame){ 19 | int pattern_index = (frame/repeat_length) % (pattern_length); 20 | leds.push(pattern[pattern_index]); 21 | } 22 | 23 | protected: 24 | 25 | const CRGB * pattern; 26 | uint8_t pattern_length; 27 | uint8_t repeat_length = 1; 28 | 29 | }; 30 | 31 | 32 | #endif 33 | -------------------------------------------------------------------------------- /patterns/ColorBars.h: -------------------------------------------------------------------------------- 1 | #ifndef COLORBARS_H 2 | #define COLORBARS_H 3 | 4 | #include "Pattern.h" 5 | 6 | class ColorBars: public Pattern{ 7 | 8 | public: 9 | 10 | ColorBars(){ 11 | length = random(4,10); 12 | randomize(); 13 | } 14 | 15 | ColorBars(const uint8_t length): length(length){ 16 | randomize(); 17 | } 18 | 19 | ColorBars(CRGB color1, CRGB color2, const uint8_t length): color1(color1), color2(color2), length(length){} 20 | 21 | void call(LedArray leds, uint16_t frame){ 22 | if(frame % (length*2) < length){ 23 | leds.push(color1); 24 | } else { 25 | leds.push(color2); 26 | } 27 | } 28 | 29 | void randomize(){ 30 | fillRandomContrastingColors(color1, color2); 31 | 32 | } 33 | 34 | protected: 35 | 36 | CRGB color1; 37 | CRGB color2; 38 | uint8_t length; 39 | 40 | }; 41 | 42 | #endif 43 | -------------------------------------------------------------------------------- /animations/Animation.h: -------------------------------------------------------------------------------- 1 | #ifndef ANIMATION_H 2 | #define ANIMATION_H 3 | 4 | #include 5 | #include "patterns/Pattern.h" 6 | 7 | class Animation{ 8 | public: 9 | Animation(CRGB* leds, int num_leds, int num_frames) 10 | : num_frames_(num_frames), frame_(0) { 11 | leds_ = LedArray(leds, num_leds); 12 | }; 13 | 14 | bool nextFrame() { 15 | generateNextFrame(); 16 | frame_++; 17 | return (frame_ < num_frames_); 18 | }; 19 | 20 | void reset(int num_frames){ 21 | num_frames_ = num_frames_; 22 | restart(); 23 | } 24 | 25 | void restart(){ 26 | frame_ = 0; 27 | } 28 | 29 | virtual void generateNextFrame() = 0; 30 | 31 | protected: 32 | LedArray leds_; 33 | int num_frames_, frame_; 34 | }; 35 | 36 | #endif // ANIMATION_H -------------------------------------------------------------------------------- /patterns/Sos.h: -------------------------------------------------------------------------------- 1 | #ifndef SOS_H 2 | #define SOS_H 3 | 4 | #include "Pattern.h" 5 | 6 | class Sos: public Pattern{ 7 | 8 | public: 9 | 10 | Sos() {} 11 | 12 | void call(LedArray leds, uint16_t frame){ 13 | constexpr uint8_t num_intervals = 34; 14 | constexpr bool SOS[num_intervals] = {1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0}; 15 | int interval_time = 300; //in milliseconds 16 | long mod_time = interval_time * num_intervals; 17 | byte current_time_unit = (millis() % mod_time) / interval_time; 18 | //SOS in binary - S -s_s_s space-___ O-l_l_l space-___ S -s_s_s 19 | if (SOS[current_time_unit]) { 20 | fill_solid(leds.head, leds.length, CRGB::Red); 21 | } else { 22 | fill_solid(leds.head, leds.length, CRGB::Black); 23 | } 24 | } 25 | 26 | }; 27 | 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /animations/Fade.h: -------------------------------------------------------------------------------- 1 | #ifndef FADE_H 2 | #define FADE_H 3 | 4 | #include "Animation.h" 5 | #include "pixeltypes.h" 6 | 7 | class Fade: public Animation{ 8 | 9 | public: 10 | 11 | Fade(CRGB* leds, int num_leds, int num_frames) 12 | : Animation(leds, num_leds, num_frames) { 13 | randomize(); 14 | } 15 | 16 | Fade(CRGB* leds, int num_leds, int num_frames, CRGB new_color) 17 | : Animation(leds, num_leds, num_frames) { 18 | color_ = new_color; 19 | } 20 | 21 | void generateNextFrame(){ 22 | CRGB faded = color_; 23 | uint8_t dim_value = 255 - (frame_ + 1)*255/num_frames_; 24 | faded.nscale8(dim_value); 25 | fill_solid(leds_.head, leds_.length, faded); 26 | } 27 | 28 | void randomize(){ 29 | color_ = Pattern::random_color(); 30 | } 31 | 32 | protected: 33 | CRGB color_; 34 | 35 | }; 36 | 37 | #endif -------------------------------------------------------------------------------- /patterns/BeaconScanner.h: -------------------------------------------------------------------------------- 1 | #ifndef BEACONSCANNER_H 2 | #define BEACONSCANNER_H 3 | 4 | #include "Pattern.h" 5 | #include "BeaconUtil.h" 6 | 7 | class BeaconScanner: public Pattern{ 8 | 9 | public: 10 | 11 | BeaconScanner(){ 12 | randomize(); 13 | }; 14 | 15 | BeaconScanner(const CRGB color) : color(color){} 16 | 17 | void call(LedArray leds, uint16_t frame){ 18 | int16_t periodic_index = PERIOD/2 - ((frame + PERIOD/2) % (PERIOD)); 19 | uint8_t abs_periodic_index = abs(periodic_index); 20 | for(uint16_t i = 0; i < leds.length; i++){ 21 | uint8_t stick_index = get_stick_index(i); 22 | if(stick_index == abs_periodic_index){ 23 | leds.leds[i] = color; 24 | } else { 25 | leds.leds[i] = CRGB::Black; 26 | } 27 | } 28 | } 29 | 30 | void randomize(){ 31 | color = random_color(); 32 | } 33 | 34 | protected: 35 | CRGB color; 36 | static constexpr uint8_t PERIOD = (LEDS_PER_STICK - 1) * 2; 37 | }; 38 | 39 | 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /patterns/AlternatingColors.h: -------------------------------------------------------------------------------- 1 | #ifndef ALTERNATINGCOLORS_H 2 | #define ALTERNATINGCOLORS_H 3 | 4 | #include "Pattern.h" 5 | 6 | class AlternatingColors: public Pattern{ 7 | 8 | public: 9 | 10 | AlternatingColors(){ 11 | length = random(4,10); 12 | randomize(); 13 | } 14 | 15 | AlternatingColors(const uint8_t length): length(length){ 16 | randomize(); 17 | } 18 | 19 | AlternatingColors(CRGB color1, CRGB color2, const uint8_t length): color1(color1), color2(color2), length(length){} 20 | 21 | void call(LedArray leds, uint16_t frame){ 22 | if(frame % 25 == 0){ 23 | leds.shift_leds(length); 24 | if(frame % 2){ 25 | fill_solid(leds.leds, length, color1); 26 | } else { 27 | fill_solid(leds.leds, length, color2); 28 | } 29 | } 30 | } 31 | 32 | void randomize(){ 33 | fillRandomContrastingColors(color1, color2); 34 | } 35 | 36 | protected: 37 | 38 | CRGB color1; 39 | CRGB color2; 40 | uint8_t length; 41 | 42 | }; 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /patterns/Pattern.h: -------------------------------------------------------------------------------- 1 | #ifndef PATTERN_H 2 | #define PATTERN_H 3 | 4 | #include "../Leds.h" 5 | 6 | class Pattern{ 7 | 8 | public: 9 | 10 | uint16_t frame_interval = 0; 11 | 12 | virtual void call(LedArray _leds, uint16_t frame) = 0; 13 | 14 | virtual void randomize(){}; 15 | 16 | //FPS = 1000/frame_interval 17 | void set_fps(uint8_t fps){ 18 | if(fps == 0){ 19 | frame_interval = 0; // 0 means use the PatternController frame_interval 20 | } else{ 21 | frame_interval = 1000/fps; 22 | } 23 | } 24 | 25 | #define SATURATION 255 26 | #define VALUE 255 27 | 28 | static CRGB random_color(){ 29 | return CHSV(random(255), SATURATION, VALUE); 30 | } 31 | 32 | static CRGB complementary_color(CRGB color){ 33 | CHSV hsv_color = rgb2hsv_approximate(color); 34 | hsv_color.hue = hsv_color.hue - 128; 35 | CRGB rgb_complement; 36 | hsv2rgb_rainbow( hsv_color, rgb_complement); 37 | return rgb_complement; 38 | } 39 | 40 | static void fillRandomContrastingColors(CRGB &c1, CRGB &c2) { 41 | int h1 = random(255); 42 | int offset = random(180) - 90; 43 | int h2 = (h1 + 128 + offset) % 255; 44 | 45 | //LOG("\tRandomContrastingColors generated\th1: %d\th2: %d", h1, h2); 46 | c1 = CHSV(h1, SATURATION, VALUE); 47 | c2 = CHSV(h2, SATURATION, VALUE); 48 | } 49 | 50 | 51 | }; 52 | 53 | 54 | #endif 55 | -------------------------------------------------------------------------------- /AnimationController.cpp: -------------------------------------------------------------------------------- 1 | #include "AnimationController.h" 2 | 3 | // Create an instance of the AnimationController library 4 | AnimationController::AnimationController(){}; 5 | 6 | //FPS = 1000/frame_interval 7 | void AnimationController::set_fps(uint8_t fps){ 8 | if(fps == 0){ 9 | frame_interval = DEFAULT_FRAME_INTERVAL; // avoiding divide by zero 10 | } else{ 11 | frame_interval = 1000/fps; 12 | } 13 | } 14 | 15 | void AnimationController::set(Animation * animation, uint8_t slot_index){ 16 | if(slot_index < ANIMATION_SLOTS){ 17 | animations[slot_index] = animation; 18 | //Serial.println("Pattern Added"); 19 | } 20 | //Debug message if out of bounds? 21 | } 22 | 23 | void AnimationController::clear(){ 24 | for(int i = 0; i < ANIMATION_SLOTS; i++){ 25 | animations[i] = NULL; 26 | } 27 | } 28 | 29 | void AnimationController::show(){ 30 | unsigned long now = millis(); 31 | 32 | for(int i = 0; i < ANIMATION_SLOTS; i++){ 33 | if(active[i]){ 34 | if(now - last_frame_start_times[i] > frame_interval){ 35 | last_frame_start_times[i] = now; 36 | active[i] = animations[i]->nextFrame(); 37 | } 38 | } 39 | } 40 | } 41 | 42 | void AnimationController::fire_animation(uint8_t slot_index){ 43 | last_frame_start_times[slot_index] = millis(); 44 | active[slot_index] = true; 45 | } 46 | -------------------------------------------------------------------------------- /Leds.h: -------------------------------------------------------------------------------- 1 | #ifndef _LEDS_H 2 | #define _LEDS_H 3 | 4 | #include 5 | 6 | // the definition of the leds class. 7 | struct LedArray 8 | { 9 | uint16_t length; 10 | uint8_t shift_direction; 11 | CRGB * leds; 12 | CRGB * head; 13 | CRGB * tail; 14 | bool reverse; 15 | 16 | LedArray (){} 17 | 18 | LedArray (CRGB * new_leds, uint16_t new_length) { 19 | leds = new_leds; 20 | length = new_length; 21 | set_reverse(false); 22 | } 23 | 24 | CRGB get(uint16_t index){ 25 | if(!reverse){ 26 | return leds[index]; 27 | } else { 28 | return leds[length - index - 1]; 29 | } 30 | } 31 | 32 | void push (CRGB color) { 33 | shift_leds(shift_direction); 34 | *head = color; 35 | } 36 | 37 | void push_back(CRGB color){ 38 | shift_leds(shift_direction * -1); 39 | *tail = color; 40 | } 41 | 42 | void shift_leds(int16_t amount){ 43 | CRGB temp[length]; 44 | for (uint16_t x = 0; x < (length - amount); x++) { 45 | temp[x + amount] = leds[x]; 46 | } 47 | memcpy(leds, temp, sizeof(CRGB) * length); 48 | } 49 | 50 | void set_reverse(bool is_reverse){ 51 | reverse = is_reverse; 52 | if(!is_reverse){ 53 | head = &leds[0]; 54 | tail = &leds[length - 1]; 55 | shift_direction = 1; 56 | } else { 57 | head = &leds[length -1]; 58 | tail = &leds[0]; 59 | shift_direction = -1; 60 | } 61 | } 62 | 63 | }; 64 | #endif // _LEDS_H 65 | -------------------------------------------------------------------------------- /AnimationController.h: -------------------------------------------------------------------------------- 1 | #ifndef AnimationController_h 2 | #define AnimationController_h 3 | 4 | #include "Leds.h" 5 | #include "animations/Animation.h" 6 | #include "Arduino.h" 7 | #include 8 | 9 | #ifndef ANIMATION_SLOTS 10 | #define ANIMATION_SLOTS 8 11 | #endif 12 | 13 | #define DEFAULT_FRAME_INTERVAL 100 14 | 15 | class AnimationController 16 | { 17 | //need to keep track of time at a class level 18 | 19 | Animation* animations[ANIMATION_SLOTS]; 20 | unsigned long last_frame_start_times[ANIMATION_SLOTS]; 21 | bool active[ANIMATION_SLOTS] = {false}; 22 | 23 | public: 24 | uint16_t frame_interval = DEFAULT_FRAME_INTERVAL; //ms 25 | 26 | // Create an instance of the AnimationController library 27 | AnimationController(); 28 | 29 | //FPS = 1000/frame_interval 30 | void set_fps(uint8_t fps); 31 | 32 | // run a specific pattern right now 33 | //not sure yet how this plays with duration 34 | //void run(Pattern pattern, list of args); 35 | 36 | //set animation in slot 37 | void set(Animation * animation, uint8_t slot_index); 38 | 39 | //clear all animation slots 40 | void clear(); 41 | 42 | //Select pattern by index. Wraps around if pattern index > number of patterns 43 | void fire_animation(uint8_t slot_index); 44 | 45 | //Analagous to FastLED show 46 | void show(); 47 | 48 | protected: 49 | unsigned long previous_millis; 50 | 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /patterns/Lightning.h: -------------------------------------------------------------------------------- 1 | #ifndef LIGHTNING_H 2 | #define LIGHTNING_H 3 | 4 | #include "Pattern.h" 5 | 6 | class Lightning: public Pattern{ 7 | 8 | public: 9 | 10 | Lightning(CRGB color, const uint8_t length): color(color), length(length){} 11 | 12 | void call(LedArray leds, uint16_t frame){ 13 | uint8_t segments = leds.length/length; 14 | 15 | time_since_last_strike = millis() - time_of_last_strike; 16 | if(time_since_last_strike > min_time_between_strikes){ 17 | for(int i = 0; i < segments; i++){ 18 | apply_lightning(&leds.leds[i*length], length); 19 | } 20 | } else{ 21 | fadeToBlackBy(leds.leds, leds.length, 8); 22 | } 23 | //add some stuff for final non-uniform segment, if any 24 | } 25 | 26 | void apply_lightning(CRGB * leds, uint8_t segment_length){ 27 | uint8_t random_strike = random8(); 28 | if(random_strike < strike_frequency){ 29 | if(time_since_last_strike != 0){ 30 | time_since_last_strike = 0; 31 | time_of_last_strike = millis(); 32 | min_time_between_strikes = random16(500, 3000); 33 | } 34 | fill_solid(leds, segment_length, color); 35 | } else { 36 | fadeToBlackBy(leds, segment_length, 8); 37 | } 38 | } 39 | 40 | void randomize(){ 41 | uint8_t rand_int = random(8); 42 | if(rand_int == 0){ 43 | color = CRGB::White; 44 | } else { 45 | color = random_color(); 46 | } 47 | } 48 | 49 | protected: 50 | 51 | CRGB color; 52 | uint8_t length; 53 | uint16_t time_since_last_strike = 1000; 54 | uint16_t time_of_last_strike = 0; 55 | uint16_t min_time_between_strikes = 800; 56 | const uint8_t strike_frequency = 85; 57 | 58 | }; 59 | 60 | #endif 61 | -------------------------------------------------------------------------------- /patterns/BeaconPulse2.h: -------------------------------------------------------------------------------- 1 | #ifndef BEACONPULSE2_H 2 | #define BEACONPULSE2_H 3 | 4 | #include "Pattern.h" 5 | #include "BeaconUtil.h" 6 | 7 | class BeaconPulse2: public Pattern{ 8 | 9 | enum Direction {INCREASING, DECREASING}; 10 | 11 | public: 12 | 13 | BeaconPulse2(){ 14 | randomize(); 15 | }; 16 | 17 | BeaconPulse2(const CRGB color) : color(color){} 18 | 19 | void call(LedArray leds, uint16_t frame){ 20 | uint8_t cur_brightness = sin8(frame * speed); 21 | //uint8_t wavelength = max_levels / speed; 22 | uint8_t num_beacons = leds.length / LEDS_PER_BEACON; 23 | if(cur_brightness >= prev_brightness){ 24 | if(direction == DECREASING){ 25 | beacon_number = (beacon_number + 1) % num_beacons; 26 | } 27 | direction = INCREASING; 28 | } else { 29 | direction = DECREASING; 30 | } 31 | prev_brightness = cur_brightness; 32 | //uint8_t cur_beacon_number = (frame / wavelength) % num_beacons; 33 | // Serial.print("cur_brightness: "); 34 | // Serial.println(cur_brightness); 35 | for(uint16_t i = 0; i < leds.length; i++){ 36 | uint8_t cur_beacon = i / LEDS_PER_BEACON; 37 | if((cur_beacon + beacon_number) % 2){ 38 | leds.leds[i] = color; 39 | leds.leds[i].subtractFromRGB(255 - cur_brightness); 40 | } else{ 41 | leds.leds[i] = CRGB::Black; 42 | } 43 | } 44 | } 45 | 46 | void randomize(){ 47 | color = random_color(); 48 | //speed = random(1,8); 49 | } 50 | 51 | protected: 52 | CRGB color; 53 | Direction direction = INCREASING; 54 | uint8_t speed = 2; 55 | uint8_t prev_brightness = 0; 56 | uint8_t beacon_number = 0; 57 | static constexpr uint16_t max_levels = 256; 58 | 59 | 60 | }; 61 | 62 | #endif 63 | -------------------------------------------------------------------------------- /PatternController.h: -------------------------------------------------------------------------------- 1 | #ifndef PatternController_h 2 | #define PatternController_h 3 | 4 | #include "Leds.h" 5 | #include "patterns/Pattern.h" 6 | #include "Arduino.h" 7 | #include 8 | 9 | #ifndef PATTERN_ARRAY_MAX_SIZE 10 | #define PATTERN_ARRAY_MAX_SIZE 12 11 | #endif 12 | 13 | #define DEFAULT_FRAME_INTERVAL 100 14 | 15 | class PatternController 16 | { 17 | //need to keep track of time at a class level 18 | 19 | LedArray _leds; 20 | Pattern* patterns[PATTERN_ARRAY_MAX_SIZE]; 21 | uint8_t current_pattern = 0; 22 | uint8_t pattern_array_size = 0; 23 | uint32_t now = 0; 24 | uint32_t pattern_start_ts = 0; 25 | uint32_t frame_ts = 0; 26 | 27 | 28 | public: 29 | bool cycle = true; 30 | bool randomize = true; 31 | uint32_t pattern_duration = 15000;//ms 32 | uint16_t frame_interval = DEFAULT_FRAME_INTERVAL; //ms 33 | uint16_t frame = 0; 34 | 35 | 36 | // Create an instance of the PatternController library 37 | PatternController(CRGB * leds, uint16_t length); 38 | 39 | //FPS = 1000/frame_interval 40 | void set_fps(uint8_t fps); 41 | 42 | // run a specific pattern right now 43 | //not sure yet how this plays with duration 44 | //void run(Pattern pattern, list of args); 45 | 46 | //add pattern to list of patterns 47 | void add(Pattern * pattern); 48 | 49 | //remove all patterns from list of patterns 50 | void clear(); 51 | 52 | //Select pattern by index. Wraps around if pattern index > number of patterns 53 | void select_pattern(uint8_t pattern_index); 54 | 55 | //skip the current pattern 56 | void next_pattern(); 57 | 58 | //Analagous to FastLED show 59 | void show(); 60 | 61 | protected: 62 | unsigned long previous_millis; 63 | 64 | }; 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Led Pattern Library 2 | Led strip patterns for use with the popular [FastLED](https://github.com/FastLED/FastLED) Library. 3 | 4 | ## Getting Started 5 | 6 | Clone the repo into your Arduino library path. 7 | 8 | If you're using PlatformIO, make sure the library path is specified in your platformio.ini file, e.g. 9 | 10 | ``` 11 | [platformio] 12 | lib_extra_dirs = ${env.HOME}/Arduino 13 | ``` 14 | ## Usage 15 | 16 | Include the FastLED library, PatternController.h, and header files for any of the patterns you want to use. 17 | 18 | ``` 19 | #include "FastLED.h" 20 | 21 | #include "PatternController.h" 22 | 23 | #include "patterns/ColorBars.h" 24 | #include "patterns/Gradient.h" 25 | ``` 26 | 27 | Create a PatternController object using the constructor. Pass in the CRGB array used with FastLED and the number of LEDs for that array. 28 | 29 | ``` 30 | CRGB leds[NUM_LEDS]; 31 | PatternController pattern_master = PatternController(leds, NUM_LEDS); 32 | ``` 33 | 34 | Create pattern objects and register them with the PatternController object. 35 | 36 | ``` 37 | ColorBars cb = ColorBars(CRGB::Blue, CRGB::Black, 24); 38 | Gradient grad = Gradient(); 39 | 40 | void setup(){ 41 | //Other setup, such as FastLED 42 | ... 43 | FastLED.addLeds(leds, NUM_LEDS).setCorrection(TypicalSMD5050); 44 | ... 45 | //PatternController setup 46 | pattern_master.add(&cb); 47 | pattern_master.add(&grad); 48 | } 49 | ``` 50 | and in the main loop, call the .show() method of the PatternController before FastLED.show() 51 | 52 | ``` 53 | void loop(){ 54 | pattern_master.show(); 55 | FastLED.show(); 56 | } 57 | ``` 58 | 59 | 60 | For a usage example, see [multi-beacon.cpp](https://github.com/johnmyrda/multi-beacon/blob/master/src/multi-beacon.cpp) from my [LED Beacon](https://github.com/johnmyrda/multi-beacon) project. 61 | -------------------------------------------------------------------------------- /patterns/BeaconPulse.h: -------------------------------------------------------------------------------- 1 | #ifndef BEACONPULSE_H 2 | #define BEACONPULSE_H 3 | 4 | #include "Pattern.h" 5 | #include "BeaconUtil.h" 6 | 7 | class BeaconPulse: public Pattern{ 8 | 9 | public: 10 | 11 | BeaconPulse(){ 12 | randomize(); 13 | }; 14 | 15 | BeaconPulse(const CRGB color) : color(color){} 16 | 17 | void call(LedArray leds, uint16_t frame){ 18 | uint8_t modifier = max_levels / brightness_levels; 19 | uint8_t cur_brightness = (frame % brightness_levels) * modifier + modifier - 1; 20 | 21 | uint8_t num_beacons = leds.length / LEDS_PER_BEACON; 22 | uint8_t cur_beacon_number = (frame / brightness_levels) % num_beacons; 23 | uint8_t prev_beacon_number = (cur_beacon_number + num_beacons - 1) % num_beacons; 24 | for(uint16_t i = 0; i < leds.length; i++){ 25 | uint8_t beacon_number = i / LEDS_PER_BEACON; 26 | if(beacon_number % spacing == cur_beacon_number % spacing){ 27 | leds.leds[i] = color; 28 | leds.leds[i].subtractFromRGB(255 - cur_brightness); 29 | } else if(beacon_number % spacing == prev_beacon_number % spacing){ 30 | leds.leds[i] = color; 31 | leds.leds[i].subtractFromRGB(cur_brightness); 32 | } else{ 33 | leds.leds[i] = CRGB::Black; 34 | } 35 | } 36 | } 37 | 38 | // uint16_t calculate_brightness_levels(uint8_t speed){ 39 | // double log_2_speed = log(double(speed + 1))/log(2.0); 40 | // if(log_2_speed < 2.0){ 41 | // log_2_speed = 2.0; 42 | // } 43 | // double temp_brightness_levels = 256.0/log_2_speed; 44 | // return uint16_t(temp_brightness_levels); 45 | // } 46 | 47 | void randomize(){ 48 | color = random_color(); 49 | brightness_levels = random(30,150); 50 | } 51 | 52 | protected: 53 | CRGB color; 54 | uint16_t brightness_levels = 64; 55 | static constexpr uint16_t max_levels = 256; 56 | static constexpr uint8_t spacing = 3; 57 | }; 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /PatternController.cpp: -------------------------------------------------------------------------------- 1 | #include "PatternController.h" 2 | 3 | PatternController::PatternController(CRGB * leds, uint16_t length){ 4 | _leds = LedArray(leds, length); 5 | } 6 | 7 | //FPS = 1000/frame_interval 8 | void PatternController::set_fps(uint8_t fps){ 9 | if(fps == 0){ 10 | frame_interval = DEFAULT_FRAME_INTERVAL; // avoiding divide by zero 11 | } else{ 12 | frame_interval = 1000/fps; 13 | } 14 | } 15 | 16 | void PatternController::add(Pattern * pattern){ 17 | if(pattern_array_size < PATTERN_ARRAY_MAX_SIZE){ 18 | patterns[pattern_array_size] = pattern; 19 | pattern_array_size++; 20 | //Serial.println("Pattern Added"); 21 | } 22 | //Debug message if full? 23 | } 24 | 25 | void PatternController::clear(){ 26 | for(int i = 0; i < PATTERN_ARRAY_MAX_SIZE; i++){ 27 | patterns[i] = NULL; 28 | } 29 | current_pattern = 0; 30 | pattern_array_size = 0; 31 | } 32 | 33 | void PatternController::show(){ 34 | now = millis(); 35 | 36 | if(cycle && (now - pattern_start_ts) > pattern_duration){ 37 | //Serial.println(F("next_pattern")); 38 | next_pattern(); 39 | } 40 | 41 | uint16_t cur_frame_interval; 42 | if(patterns[current_pattern]->frame_interval != 0){ 43 | cur_frame_interval = patterns[current_pattern]->frame_interval; 44 | } else { 45 | cur_frame_interval = frame_interval; 46 | } 47 | 48 | uint32_t elapsed = now - frame_ts; 49 | if(elapsed > cur_frame_interval){ //&& elapsed < frame_interval*2 50 | frame = frame+1; 51 | frame_ts = frame_ts + cur_frame_interval;//*num_frames. num_frames = elapsed/frame_interval 52 | patterns[current_pattern]->call(_leds, frame); 53 | } 54 | } 55 | 56 | void PatternController::select_pattern(uint8_t pattern_index){ 57 | current_pattern = pattern_index % pattern_array_size; // ensures all possible inputs are valid 58 | pattern_start_ts = now; // should this be last pattern start + pattern duration instead? 59 | frame_ts = pattern_start_ts; 60 | frame = 0; 61 | if(randomize){ 62 | //Serial.print("Randomizing pattern #"); 63 | //Serial.println(current_pattern); 64 | patterns[current_pattern]->randomize(); 65 | } 66 | } 67 | 68 | void PatternController::next_pattern(){ 69 | select_pattern(current_pattern + 1); 70 | } 71 | -------------------------------------------------------------------------------- /patterns/Fire2012.h: -------------------------------------------------------------------------------- 1 | // The MIT License (MIT) 2 | // 3 | // Copyright (c) 2013 FastLED 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | // this software and associated documentation files (the "Software"), to deal in 7 | // the Software without restriction, including without limitation the rights to 8 | // use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | // the Software, and to permit persons to whom the Software is furnished to do so, 10 | // subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in all 13 | // copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | // FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | // IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | // 22 | // https://github.com/FastLED/FastLED/tree/master/examples/Fire2012 23 | // Fire2012 by Mark Kriegsman, July 2012 24 | // as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY 25 | //// 26 | // This basic one-dimensional 'fire' simulation works roughly as follows: 27 | // There's a underlying array of 'heat' cells, that model the temperature 28 | // at each point along the line. Every cycle through the simulation, 29 | // four steps are performed: 30 | // 1) All cells cool down a little bit, losing heat to the air 31 | // 2) The heat from each cell drifts 'up' and diffuses a little 32 | // 3) Sometimes randomly new 'sparks' of heat are added at the bottom 33 | // 4) The heat from each cell is rendered as a color into the leds array 34 | // The heat-to-color mapping uses a black-body radiation approximation. 35 | // 36 | // Temperature is in arbitrary units from 0 (cold black) to 255 (white hot). 37 | // 38 | // This simulation scales it self a bit depending on leds.length; it should look 39 | // "OK" on anywhere from 20 to 100 LEDs without too much tweaking. 40 | // 41 | // I recommend running this simulation at anywhere from 30-100 frames per second, 42 | // meaning an interframe delay of about 10-35 milliseconds. 43 | // 44 | // Looks best on a high-density LED setup (60+ pixels/meter). 45 | // 46 | // 47 | // There are two main parameters you can play with to control the look and 48 | // feel of your fire: COOLING (used in step 1 above), and SPARKING (used 49 | // in step 3 above). 50 | // 51 | // COOLING: How much does the air cool as it rises? 52 | // Less cooling = taller flames. More cooling = shorter flames. 53 | // Default 50, suggested range 20-100 54 | //#define COOLING 55 55 | 56 | // SPARKING: What chance (out of 255) is there that a new spark will be lit? 57 | // Higher chance = more roaring fire. Lower chance = more flickery fire. 58 | // Default 120, suggested range 50-200. 59 | //#define SPARKING 120 60 | 61 | 62 | #ifndef FIRE2012_H 63 | #define FIRE2012_H 64 | 65 | #include "Pattern.h" 66 | 67 | class Fire2012: public Pattern{ 68 | 69 | public: 70 | 71 | Fire2012(byte * heat, uint16_t length): heat(heat), length(length){} 72 | 73 | Fire2012(byte * heat, uint16_t length, uint8_t cooling, uint8_t sparking) : heat(heat), length(length), cooling(cooling), sparking(sparking){} 74 | 75 | void call(LedArray leds, uint16_t frame){ 76 | // Array of temperature readings at each simulation cell 77 | 78 | // Step 1. Cool down every cell a little 79 | for( uint16_t i = 0; i < length; i++) { 80 | heat[i] = qsub8( heat[i], random8(0, ((cooling * 10) / length) + 2)); 81 | } 82 | 83 | // Step 2. Heat from each cell drifts 'up' and diffuses a little 84 | for( int k= length - 1; k >= 2; k--) { 85 | heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3; 86 | } 87 | 88 | // Step 3. Randomly ignite new 'sparks' of heat near the bottom 89 | if( random8() < sparking ) { 90 | int y = random8(7); 91 | heat[y] = qadd8( heat[y], random8(160,255) ); 92 | } 93 | 94 | // Step 4. Map from heat cells to LED colors 95 | for( uint16_t j = 0; j < length; j++) { 96 | CRGB color = HeatColor( heat[j]); 97 | int pixelnumber; 98 | if( reverse_direction ) { 99 | pixelnumber = (leds.length-1) - j; 100 | } else { 101 | pixelnumber = j; 102 | } 103 | leds.leds[pixelnumber] = color; 104 | } 105 | } 106 | 107 | void randomize(){ 108 | // COOLING: How much does the air cool as it rises? 109 | // Less cooling = taller flames. More cooling = shorter flames. 110 | // Default 50, suggested range 20-100 111 | cooling = random8(20,100); 112 | 113 | // SPARKING: What chance (out of 255) is there that a new spark will be lit? 114 | // Higher chance = more roaring fire. Lower chance = more flickery fire. 115 | // Default 120, suggested range 50-200. 116 | sparking = random8(50, 200); 117 | } 118 | 119 | protected: 120 | byte * heat; 121 | uint16_t length; 122 | 123 | public: 124 | uint8_t cooling = 50; 125 | uint8_t sparking = 120; 126 | bool reverse_direction = false; 127 | 128 | }; 129 | 130 | 131 | #endif 132 | --------------------------------------------------------------------------------