├── examples ├── ActiveGroup │ └── ActiveGroup.ino ├── Custom │ └── Custom.ino ├── SimpleTest │ └── SimpleTest.ino ├── Sequence │ └── Sequence.ino └── XmasTown │ └── XmasTown.ino ├── LedString.h ├── README.md └── LedString.cpp /examples/ActiveGroup/ActiveGroup.ino: -------------------------------------------------------------------------------- 1 | // LedString ActiveGroup Test 2 | // Uses an ActiveGroup to simulate habitation activity in buildings 3 | 4 | #include 5 | LedString lights; 6 | 7 | #define DATA_PIN 2 8 | #define NUM_LEDS 20 9 | 10 | String pattern = "AAAAAAAAAAAAAAAAAAAA"; 11 | 12 | // allocate space for leds 13 | CRGB leds[NUM_LEDS]; 14 | 15 | void setup() { 16 | FastLED.addLeds(leds, NUM_LEDS); 17 | lights.setup(leds, NUM_LEDS); 18 | lights.begin(pattern); 19 | } 20 | 21 | void loop() { 22 | lights.loop(); 23 | } 24 | -------------------------------------------------------------------------------- /examples/Custom/Custom.ino: -------------------------------------------------------------------------------- 1 | // Custom ActiveGroup 2 | // This sketch demonstrates how to customize the ActiveGroup behavior. 3 | // To do this you simply create an instance of ActiveGroup with non-default parameters. 4 | 5 | #include 6 | LedString lights; 7 | 8 | #define DATA_PIN 2 9 | #define NUM_LEDS 10 10 | CRGB leds[NUM_LEDS]; 11 | 12 | void setup() { 13 | FastLED.addLeds(leds, NUM_LEDS); 14 | lights.setup(leds, NUM_LEDS); 15 | 16 | // Turn leds on and off every quarter second, keeping about 20% of the leds on at once 17 | ActiveGroup *group = new ActiveGroup('X', 250, 250, CRGB::Green, 20); 18 | lights.addHandler(group); 19 | lights.begin("XXXXXXXXXX"); 20 | } 21 | 22 | void loop() { 23 | lights.loop(); 24 | } 25 | -------------------------------------------------------------------------------- /examples/SimpleTest/SimpleTest.ino: -------------------------------------------------------------------------------- 1 | // LedString Simple Test - run a string of 8 leds 2 | 3 | #include 4 | LedString lights; 5 | 6 | #define DATA_PIN 2 7 | #define NUM_LEDS 8 8 | 9 | // Red, Green, Blue and Fire 10 | String pattern = "RRGGBBFF"; 11 | 12 | // allocate space for 8 leds 13 | CRGB leds[NUM_LEDS]; 14 | 15 | void setup() { 16 | Serial.begin(115200); 17 | while (!Serial) { ; } 18 | Serial.println("\n**************"); 19 | // addLeds must be called here rather than being done by the library 20 | // because FastLED requires the pin number to be a compile-time constant. 21 | FastLED.addLeds(leds, NUM_LEDS); 22 | 23 | lights.setup(leds, NUM_LEDS); 24 | lights.begin(pattern); 25 | } 26 | 27 | void loop() { 28 | lights.loop(); 29 | } 30 | -------------------------------------------------------------------------------- /examples/Sequence/Sequence.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #define DATA_PIN 2 3 | #define NUM_LEDS 8 4 | 5 | LedString myLedString; 6 | CRGB leds[NUM_LEDS]; 7 | 8 | class ColorSequence : public LedHandler { 9 | private: 10 | CRGB* colors; 11 | int colorCount; 12 | int nextColor; 13 | public: 14 | ColorSequence (char label, uint32_t interval, CRGB colors[], int count) : LedHandler(label, interval) { 15 | this->colors = colors; 16 | this->colorCount = count; 17 | this->nextColor = count; 18 | } 19 | void start(LedString ls) { 20 | if (this->enabled) { 21 | this->nextColor++; 22 | if (this->nextColor >= this->colorCount) { 23 | this->nextColor = 0; 24 | } 25 | } 26 | } 27 | void loop(LedString ls, int ledNumber) { 28 | ls.leds[ledNumber] = this->colors[this->nextColor]; 29 | } 30 | }; 31 | 32 | CRGB colors1[] = { CRGB::Red, CRGB::Green, CRGB::Blue, CRGB::Black }; 33 | CRGB colors2[] = { CRGB::Blue, CRGB::White }; 34 | 35 | void setup() { 36 | Serial.begin(115200); while (!Serial) { ; } 37 | Serial.println("\n*****"); 38 | 39 | FastLED.clear(); 40 | FastLED.addLeds(leds, NUM_LEDS); 41 | myLedString.setup(leds, NUM_LEDS); 42 | 43 | Serial.println("Adding custom handlers"); 44 | ColorSequence *cs1 = new ColorSequence('1', 500, colors1, 4); 45 | myLedString.addHandler(cs1); 46 | ColorSequence *cs2 = new ColorSequence('2', 500, colors2, 2); 47 | myLedString.addHandler(cs2); 48 | 49 | Serial.println("Calling begin"); 50 | myLedString.begin("11112222"); 51 | } 52 | 53 | void loop() { 54 | myLedString.loop(); 55 | } 56 | 57 | -------------------------------------------------------------------------------- /LedString.h: -------------------------------------------------------------------------------- 1 | #ifndef LedString_h 2 | #define LedString_h 3 | 4 | #include 5 | 6 | #define MAX_LEDS 100 // max number of behaviors, i.e. leds; need to make this dynamic and depend on the size of the pattern set by the app 7 | #define MAX_LED_STRING_HANDLERS 20 8 | 9 | class LedString; // needed because handlers and LedString mutually reference each other. 10 | 11 | class LedHandler { // implements led behaviors 12 | public: 13 | char label; // 1-char label to use in pattern 14 | uint32_t interval = 0; // milliseconds between events; if 0 do once only at startup 15 | uint32_t whenLast = 0; // time of last event 16 | bool enabled = false; // true if the handler should execute during this cycle 17 | LedHandler(char label, uint32_t interval); 18 | virtual void setup(LedString ls); // executed when a pattern is created or changed 19 | virtual void start(LedString ls); // executed before cycling through the leds 20 | virtual void loop(LedString ls, int i); // executed on each led with this label 21 | }; 22 | 23 | class SimpleHandler : public LedHandler { 24 | public: 25 | CRGB color; 26 | SimpleHandler(char label, uint32_t interval, CRGB color); 27 | virtual void loop(LedString ls, int i); 28 | }; 29 | 30 | class FlameHandler : public LedHandler { 31 | public: 32 | FlameHandler(char label, uint32_t interval); 33 | virtual void loop(LedString ls, int i); 34 | }; 35 | 36 | class ActiveGroup : public LedHandler { 37 | public: 38 | int groupCount; 39 | int groupCountOn; 40 | int countdown; 41 | int percentOn; 42 | bool isTurningOn; 43 | CRGB color; 44 | uint32_t minInterval; 45 | uint32_t maxInterval; 46 | 47 | // public: 48 | ActiveGroup(char label, uint32_t minInterval, uint32_t maxInterval, CRGB color, int percentOn); 49 | virtual void setup(LedString ls); 50 | virtual void start(LedString ls); 51 | virtual void loop(LedString ls, int i); 52 | }; 53 | 54 | class LedString 55 | { 56 | public: 57 | CRGB* leds = 0; 58 | String pattern; 59 | 60 | static const int FLICKER_RATE = 80; // ms between brightness changes 61 | static const int FLICKER_EXTRA = 2; // when brightness is this close to FIRE_MIN or MAX, FLICKER_MIN or MAX is used 62 | static const int FIRE_MIN = 150; 63 | static const int FIRE_MAX = 190; 64 | static const int FLICKER_MIN = 130; 65 | static const int FLICKER_MAX = 225; ////// WS28xx 66 | // int FIRE_MIN = 80; int FIRE_MAX = 160; int FLICKER_MIN = 10; int FLICKER_MAX = 230; ////// NEOPIXEL 67 | 68 | const int HABITATION_MIN_INTERVAL = 2000; // default interval range for built-in ActiveGroup 69 | const int HABITATION_MAX_INTERVAL = 10000; 70 | const int HABITATION_DEFAULT_PERCENT = 75; // default percentage of leds that should be on at any time 71 | 72 | int ledCount; 73 | 74 | void setup(CRGB *ledArray, int ledCount); 75 | void begin(String pattern); 76 | void loop(); 77 | uint32_t currentTime(); 78 | uint32_t previousTime(); 79 | void addHandler(LedHandler* h); 80 | void addSimpleHandler(char label, uint32_t interval, CRGB color); 81 | void setPattern(String st); 82 | 83 | bool isOn(int led); 84 | void turnOn(int led); 85 | void turnOff(int led); 86 | void turnAllOn(); 87 | void turnAllOff(); 88 | void setLed(int i, CRGB color); 89 | 90 | private: 91 | void addBuiltInHandlers(); 92 | void addBehavior(char label, int ledNumber); 93 | void populateBehaviors(); 94 | bool isEventTime(uint32_t interval, uint32_t &previousTime); 95 | void setupHandlers(); 96 | void enableHandlers(); 97 | void doBehaviors(); 98 | void doCycle(); 99 | }; 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # LedString 2 | FastLED wrapper to simplify using LED lighting for model towns, castles, villages. 3 | 4 | This wrapper class uses FastLED to set up lighting for buildings in a model town, medieval village, etc. 5 | LEDs are animated individually by assigning a predefined behavior to each led. The code can be used with WS2811, WS2812, WS2812B, WS2813, or NEOPIXEL rings and matrices. 6 | 7 | Behavior for individual LEDs is defined using a character string, one character per LED. Blanks can be inserted anywhere for readability, and are ignored. 8 | 9 | #### Built-in Behaviors 10 | * W: White, always lit 11 | * R: Red, always lit 12 | * G: Green, always lit 13 | * B: Blue, always lit 14 | * Y: Yellow, always lit 15 | * O: Off (same as setting the color to Black) 16 | * S: Switched on and off semi-randomly to give an appearance of habitation. At a random interval between SWITCH_MIN and SWITCH_MAX milliseconds, one randomly chosen led marked "S" is toggled on or off. 17 | * F: Fire (flickering simulation of a fireplace, torch, etc). Fire LEDs flicker independently of each other. 18 | 19 | An app may assign any other single character to a custom behavior, or override any of the standard ones (see example code Custom.ino). 20 | 21 | Example: "WWOFW SSFWO OOWSS FSSSW" 22 | 23 | The constant NUM_LEDS must be defined as per the FastLED docs. If the pattern string (excluding blanks) is longer or shorter than this, it is truncated or padded with "O" respectively. 24 | 25 | Note: As per FastLED docs the value for DATA_PIN used to call addLeds must be a constant (or multiple constants for multiple led strings). 26 | This code works on Arduino and ESP8266. 27 | 28 | Since only one "S" node is toggled on or off per interval, the more "S" leds you use, the less often an individual one will be switched. So if you want the appearance of more activity, edit LedString.h and try assigning a lower value for SWITCH_MAX. 29 | 30 | The default hardware is WS2811 or WS2812. To select different hardware you must make two edits: 31 | - in LedString.h uncomment the appropriate line to set FIRE_MIN, etc. for your type of leds. 32 | - in LedString.cpp uncomment the appropriate call to FastLED.addLeds for your type of leds. 33 | 34 | These edits are necessary because of FastLED reqirements, which LedString is based on. Due to compile-time optimizations in FastLED, certain values cannot be passed in as parameters. 35 | 36 | ### Usage Example 37 | 38 | ``` 39 | #include "LedString.h" 40 | 41 | LedString lights; 42 | 43 | void setup() { 44 | lights.doSetup("LLOFL SSFLO OOLSS FSSSL"); // define LED behaviors 45 | lights.doStart(); // set Lit and Off leds that don't change; 46 | // turn Switched leds on 47 | } 48 | 49 | void loop() { 50 | lights.doLoop(); // change the values of Fires, Swiched leds and Customs whenever it is time to do so 51 | } 52 | ``` 53 | 54 | ## Custom Behavior 55 | Custom behavior is a limited way to add more lighting options (see examples/CustomBehavior). Basically you designate custom LEDs with the letter "C" in doSetup(behaviorString), and write a function such as ```void myCustom(int ledNumber)``` that implements the behaviors. Then in setup() call **lights.setCustom**(myCustom). Your function will be called for every led whose behavior is specified with a "C". The LedString package has utility functions you can use such as turnOn(led) and turnOff(led), and you can reference LEDs as lights.leds[n] to call CRGB and CHSV functions. If you need to do something at the start of cycling through the leds, such as resetting variables used in custom behaviors, put that code in a function such as ```void myCycleSetup()``` and in setup() call **lights.setCycleSetup**(myCycleSetup). Your function will be called every time the main loop starts cycling through the leds. 56 | 57 | ## Notes 58 | 1. To control a 50-led string, the wiring is straightforward: connect the LED string's control line to pin 3 (or whatever you you designate as DATA_PIN, connect an adequate 5V power supply to the LEDs, and tie the power supply's DC ground to the Arduino ground. 59 | 60 | 2. Small sections of LED strings can be run off Arduino power alone. I don't know the limit, but I have run a string of 8 leds cut from a string of 50. 61 | 62 | 3. All fire LEDs are updated at the same regular interval, but this pattern is not apparent because each fire's brightness changes by a random amount. 63 | 64 | 4. The fire flicker interval acts as the master timing interval for the LED refresh cycle, even if no LEDs are designated as fires. This is simply because firelight was the first thing I implemented. The Arduino doLoop() checks whether it's time to flicker. If so, it makes a pass through the behavior string and updates fire leds and any other leds that need updating at that time. This means all effects occur at some multiple of the flicker rate. I figured this limitation was acceptable in a package designed to handle slow-paced lighting for towns, villages, Christmas trees and such. A tree full of flickering fire leds would be interesting - I have not tried that, but in my tests 50 fires look fine with an 80ms flicker rate. 65 | 66 | -------------------------------------------------------------------------------- /examples/XmasTown/XmasTown.ino: -------------------------------------------------------------------------------- 1 | //// LED settings 2 | #include 3 | #define NUM_STRIPS 5 4 | #define NUM_LEDS 50 5 | #define DATA_PIN_0 2 // top shelf 6 | #define DATA_PIN_1 3 7 | #define DATA_PIN_2 4 8 | #define DATA_PIN_3 5 9 | #define DATA_PIN_4 6 // bottom shelf 10 | 11 | CRGB leds[NUM_STRIPS][NUM_LEDS]; 12 | LedString strips[NUM_STRIPS]; 13 | 14 | String configFilename = "/config.txt"; 15 | struct Config { 16 | char pattern[NUM_STRIPS][NUM_LEDS]; 17 | }; 18 | Config config; 19 | 20 | void setupLeds() { 21 | FastLED.clear(); 22 | FastLED.addLeds(leds[0], NUM_LEDS); 23 | FastLED.addLeds(leds[1], NUM_LEDS); 24 | FastLED.addLeds(leds[2], NUM_LEDS); 25 | FastLED.addLeds(leds[3], NUM_LEDS); 26 | FastLED.addLeds(leds[4], NUM_LEDS); 27 | delay(100); 28 | for (int i=0; i 35 | #include 36 | 37 | void getConfig() { 38 | Serial.println("Getting stored settings"); 39 | String msg = ""; 40 | if (!SPIFFS.begin()) { 41 | msg = "Error starting SPIFFS."; 42 | } else { 43 | File file = SPIFFS.open(configFilename, "r"); 44 | if (!file) { 45 | msg = "Error opening settings file."; 46 | } else { 47 | StaticJsonDocument<512> doc; 48 | DeserializationError error = deserializeJson(doc, file); 49 | if (error) { 50 | msg = "Error reading settings."; 51 | } else { 52 | for (int i=0; i doc; 71 | for (int i=0; i customInterval) 92 | { 93 | lastCustomTime = msNow; 94 | // if the led is red make it blue, else make it red 95 | if (led.red == 255) { 96 | led = CRGB::Blue; 97 | } else { 98 | led = CRGB::Red; 99 | } 100 | } 101 | FastLED.show(); // physically update the leds 102 | } 103 | 104 | //// Web server 105 | #include 106 | #include 107 | #include 108 | #include 109 | #include "netAccess.h" // local credentials 110 | 111 | const char* ssid = NET_SSID; 112 | const char* password = NET_PASSWORD; 113 | ESP8266WebServer server(80); 114 | 115 | const String formHtml = "\ 116 | \ 117 | \ 118 | Xmas Town Lighting Control\ 119 | \ 125 | \ 126 | \ 127 |

Xmas Town Lighting Control

\ 128 | W: White, Y: Yellow, R: Red, G: Green, B: Blue, S: Switched, F: Fire\ 129 |
\ 130 | Level 5
\ 131 | Level 4
\ 132 | Level 3
\ 133 | Level 2
\ 134 | Level 1
\ 135 | \ 136 |
\ 137 | \ 138 | "; 139 | 140 | void handleRoot() { 141 | String st = formHtml; 142 | st.replace("$0$", strips[0].pattern); 143 | st.replace("$1$", strips[1].pattern); 144 | st.replace("$2$", strips[2].pattern); 145 | st.replace("$3$", strips[3].pattern); 146 | st.replace("$4$", strips[4].pattern); 147 | server.send(200, "text/html", st); 148 | } 149 | 150 | void handlePlain() { 151 | handleRoot(); 152 | } 153 | 154 | void handleForm() { 155 | if (server.method() != HTTP_POST) { 156 | server.send(405, "text/plain", "Method Not Allowed"); 157 | } else { 158 | Serial.println("**** UPDATING"); 159 | for (int i=0; ilabel = label; 22 | this->interval = interval; 23 | } 24 | 25 | void LedHandler::setup(LedString ls) 26 | { 27 | // do when a pattern is created or changed 28 | } 29 | 30 | void LedHandler::start(LedString ls) 31 | { 32 | // if handler is enabled, do before cycling through leds 33 | } 34 | 35 | void LedHandler::loop(LedString ls, int i) 36 | { 37 | // do for each led with this label 38 | } 39 | 40 | /////////////// SimpleHandler 41 | 42 | SimpleHandler::SimpleHandler(char label, uint32_t interval, CRGB color) 43 | : LedHandler(label, interval) 44 | { 45 | this->color = color; 46 | } 47 | 48 | void SimpleHandler::loop(LedString ls, int i) 49 | { 50 | ls.leds[i] = color; 51 | } 52 | 53 | /////////////// Flame 54 | 55 | FlameHandler::FlameHandler(char label, uint32_t interval) 56 | : LedHandler(label, interval) 57 | { } 58 | 59 | void FlameHandler::loop(LedString ls, int i) 60 | { 61 | int value = random(LedString::FIRE_MIN, LedString::FIRE_MAX); 62 | // occasional intense flicker 63 | if (value <= LedString::FIRE_MIN + LedString::FLICKER_EXTRA) 64 | { 65 | value = LedString::FIRE_MIN - ((LedString::FIRE_MIN - LedString::FLICKER_MIN) / (value - LedString::FIRE_MIN + 1)); 66 | } 67 | else if (value >= LedString::FIRE_MAX - LedString::FLICKER_EXTRA) 68 | { 69 | value = LedString::FIRE_MAX + ((LedString::FLICKER_MAX - LedString::FIRE_MAX) / (LedString::FIRE_MAX - value)); 70 | } 71 | ls.leds[i] = CHSV(25, 187, value); 72 | }; 73 | 74 | /////////////// ActiveGroup 75 | 76 | ActiveGroup::ActiveGroup(char label, uint32_t minInterval, uint32_t maxInterval, CRGB color, int percentOn) 77 | : LedHandler(label, 0) 78 | { 79 | this->minInterval = minInterval; 80 | this->maxInterval = max(maxInterval, minInterval+1); // in case minInterval = maxInterval 81 | this->color = color; 82 | this->percentOn = percentOn; 83 | } 84 | 85 | void ActiveGroup::setup(LedString ls) { 86 | // count the leds in this group and turn each one on/off randomly according to percentOn 87 | groupCount = 0; 88 | groupCountOn = 0; 89 | for (int i = 0; i < ls.ledCount; i++) 90 | { 91 | if (ls.pattern.charAt(i) == this->label) { 92 | groupCount++; 93 | if (random(0, 100) < percentOn) { 94 | ls.leds[i] = color; 95 | groupCountOn++; 96 | } else { 97 | ls.leds[i] = CRGB::Black; 98 | } 99 | } 100 | } 101 | isTurningOn = (percentOn >= 50); // will be reversed when start() first executes 102 | } 103 | 104 | void ActiveGroup::start(LedString ls) { 105 | isTurningOn = (groupCountOn < (ls.ledCount * percentOn / 100)); 106 | 107 | if (isTurningOn) { 108 | countdown = random(0, groupCount - groupCountOn); 109 | } else { 110 | countdown = random(0, groupCountOn); 111 | } 112 | } 113 | 114 | void ActiveGroup::loop(LedString ls, int ledNumber) { 115 | bool isOn = ls.isOn(ledNumber); 116 | if (isTurningOn && !isOn) { 117 | if (countdown > 0) { 118 | countdown--; 119 | } else { 120 | ls.leds[ledNumber] = color; 121 | groupCountOn++; 122 | enabled = false; 123 | interval = random(minInterval, maxInterval); 124 | } 125 | } else if (!isTurningOn && isOn) { 126 | if (countdown > 0) 127 | { 128 | countdown--; 129 | } 130 | else 131 | { 132 | ls.leds[ledNumber] = CRGB::Black; 133 | groupCountOn--; 134 | enabled = false; 135 | interval = random(minInterval, maxInterval); 136 | } 137 | } else { 138 | } 139 | } 140 | 141 | /////////////// LedString 142 | 143 | uint32_t _time = 0; 144 | uint32_t _previousTime = 0; 145 | 146 | uint32_t LedString::currentTime() { 147 | return _time; 148 | } 149 | 150 | uint32_t LedString::previousTime() { 151 | return _previousTime; 152 | } 153 | 154 | // array of defined handlers the system knows about 155 | LedHandler* handlers[MAX_LED_STRING_HANDLERS]; 156 | int handler_count = 0; 157 | 158 | // behaviors is an array of handlers, one for each led; 159 | LedHandler* behaviors[MAX_LEDS]; 160 | 161 | void LedString::addHandler(LedHandler* h) { 162 | // if exists replace 163 | for (int i=0; ilabel == h->label) { 165 | handlers[i] = h; 166 | return; 167 | } 168 | } 169 | // not found so append 170 | handlers[handler_count] = h; 171 | handler_count++; 172 | } 173 | 174 | void LedString::addSimpleHandler(char label, uint32_t interval, CRGB color) 175 | { 176 | SimpleHandler* h = new SimpleHandler(label, interval, color); 177 | addHandler(h); 178 | } 179 | 180 | void LedString::setLed(int i, CRGB color) { 181 | leds[i] = color; 182 | } 183 | 184 | bool LedString::isOn(int led) { 185 | return ( 186 | (leds[led].red > 0) || 187 | (leds[led].green > 0) || 188 | (leds[led].blue > 0)); 189 | } 190 | 191 | void LedString::turnOn(int i) { 192 | leds[i] = CRGB::White; 193 | } 194 | 195 | void LedString::turnOff(int i) { 196 | leds[i] = CRGB::Black; 197 | } 198 | 199 | void LedString::turnAllOn() { 200 | for (int i = 0; i < ledCount; i++) { 201 | leds[i] = CRGB::White; 202 | } 203 | } 204 | 205 | void LedString::turnAllOff() { 206 | for (int i = 0; i < ledCount; i++) { 207 | leds[i] = CRGB::Black; 208 | } 209 | } 210 | 211 | bool LedString::isEventTime(uint32_t interval, uint32_t &previousTime) { 212 | if (_time - previousTime >= interval) { 213 | previousTime = _time; 214 | return true; 215 | } 216 | else { 217 | // this includes when millis() rolls over to 0, because gap will be < 0 218 | return false; 219 | } 220 | } 221 | 222 | void LedString::addBuiltInHandlers() { 223 | addSimpleHandler('O', 0, CRGB::Black); // also used as the dummy handler for unknown pattern chars 224 | addSimpleHandler('R', 0, CRGB::Red); 225 | addSimpleHandler('G', 0, CRGB::Green); 226 | addSimpleHandler('B', 0, CRGB::Blue); 227 | addSimpleHandler('Y', 0, CRGB::Yellow); 228 | addSimpleHandler('W', 0, CRGB::White); 229 | FlameHandler *fh = new FlameHandler('F', FLICKER_RATE); 230 | addHandler(fh); 231 | ActiveGroup *ag = new ActiveGroup('A', HABITATION_MIN_INTERVAL, HABITATION_MAX_INTERVAL, CRGB::White, HABITATION_DEFAULT_PERCENT); 232 | addHandler(ag); 233 | } 234 | 235 | void LedString::addBehavior(char label, int ledNumber) { 236 | // find the handler for the label and append it to behaviors 237 | for (int i=0; ilabel == label) { 239 | behaviors[ledNumber] = handlers[i]; 240 | return; 241 | } 242 | } 243 | behaviors[ledNumber] = handlers[0]; // use dummy handler 244 | } 245 | 246 | void LedString::populateBehaviors() { 247 | // for each character in the pattern add the corresponding handler to the behaviors list 248 | for (int i = 0; i < ledCount; i++) { 249 | char label = pattern.charAt(i); 250 | addBehavior(label, i); 251 | } 252 | } 253 | 254 | void LedString::setupHandlers() { 255 | for (int i = 0; i < handler_count; i++) 256 | { 257 | handlers[i]->setup(*this); 258 | } 259 | } 260 | 261 | void LedString::setPattern(String newPattern) { 262 | // if new string is longer than original it is truncated; 263 | // if shorter it is padded with Os to turn off the unused leds. 264 | pattern = newPattern; 265 | pattern.replace(" ", ""); 266 | pattern.remove(ledCount); 267 | int shortness = ledCount - pattern.length(); 268 | for (int i=0; ienabled = (isEventTime(h->interval, h->whenLast)); 281 | if (h->enabled) { 282 | h->whenLast = _time; 283 | h->start(*this); 284 | } 285 | } 286 | } 287 | 288 | void LedString::doBehaviors() { 289 | 290 | for (int i = 0; i < ledCount; i++) { 291 | LedHandler* h = behaviors[i]; 292 | if (h->enabled) { 293 | h->loop(*this, i); 294 | } 295 | } 296 | } 297 | 298 | //void LedString::setup(String ledPattern, int ledCount) { 299 | // leds = (CRGB*)malloc(ledCount * sizeof(CRGB)); 300 | // FastLED.addLeds(leds, ledCount); 301 | // //FastLED.addLeds(leds, ledCount); 302 | // setup(ledPattern, leds); 303 | //} 304 | 305 | void LedString::setup(CRGB *ledArray, int numLeds) { 306 | leds = ledArray; 307 | ledCount = numLeds; 308 | turnAllOff(); 309 | addBuiltInHandlers(); 310 | } 311 | 312 | void LedString::begin(String newPattern) { 313 | setPattern(newPattern); 314 | } 315 | 316 | void LedString::loop() { 317 | _time = millis(); 318 | enableHandlers(); 319 | doBehaviors(); 320 | FastLED.show(); 321 | } --------------------------------------------------------------------------------