diff --git a/examples/esp8266_webinterface/esp8266_webinterface.ino b/examples/esp8266_webinterface/esp8266_webinterface.ino index 0110394..b461f36 100644 --- a/examples/esp8266_webinterface/esp8266_webinterface.ino +++ b/examples/esp8266_webinterface/esp8266_webinterface.ino @@ -39,8 +39,18 @@ 2018-01-06 added custom effects list option and auto-cycle feature */ -#include -#include +#ifdef ARDUINO_ARCH_ESP32 + #include + #include + #define WEB_SERVER WebServer + #define ESP_RESET ESP.restart() +#else + #include + #include + #define WEB_SERVER ESP8266WebServer + #define ESP_RESET ESP.reset() +#endif + #include extern const char index_html[]; @@ -78,7 +88,7 @@ uint8_t myModes[] = {}; // *** optionally create a custom list of effect/mode nu bool auto_cycle = false; WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); -ESP8266WebServer server(HTTP_PORT); +WEB_SERVER server(HTTP_PORT); void setup(){ Serial.begin(115200); @@ -171,7 +181,7 @@ void wifi_setup() { Serial.print("Tried "); Serial.print(WIFI_TIMEOUT); Serial.print("ms. Resetting ESP now."); - ESP.reset(); + ESP_RESET; } } diff --git a/examples/serial_control/serial_control.ino b/examples/serial_control/serial_control.ino index 48f1722..56037bd 100644 --- a/examples/serial_control/serial_control.ino +++ b/examples/serial_control/serial_control.ino @@ -49,8 +49,8 @@ // NEO_RGBW Pixels are wired for RGBW bitstream (NeoPixel RGBW products) WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); -char cmd[MAX_NUM_CHARS]; // char[] to store incoming serial commands -bool cmd_complete = false; // whether the command string is complete +char scmd[MAX_NUM_CHARS]; // char[] to store incoming serial commands +bool scmd_complete = false; // whether the command string is complete void setup() { Serial.begin(115200); @@ -70,7 +70,7 @@ void loop() { recvChar(); // read serial comm - if(cmd_complete) { + if(scmd_complete) { process_command(); } } @@ -79,46 +79,46 @@ void loop() { * Checks received command and calls corresponding functions. */ void process_command() { - if (strcmp(cmd,"b+") == 0) { + if (strcmp(scmd,"b+") == 0) { ws2812fx.increaseBrightness(25); Serial.print(F("Increased brightness by 25 to: ")); Serial.println(ws2812fx.getBrightness()); } - if (strcmp(cmd,"b-") == 0) { + if (strcmp(scmd,"b-") == 0) { ws2812fx.decreaseBrightness(25); Serial.print(F("Decreased brightness by 25 to: ")); Serial.println(ws2812fx.getBrightness()); } - if (strncmp(cmd,"b ",2) == 0) { - uint8_t b = (uint8_t)atoi(cmd + 2); + if (strncmp(scmd,"b ",2) == 0) { + uint8_t b = (uint8_t)atoi(scmd + 2); ws2812fx.setBrightness(b); Serial.print(F("Set brightness to: ")); Serial.println(ws2812fx.getBrightness()); } - if (strcmp(cmd,"s+") == 0) { + if (strcmp(scmd,"s+") == 0) { ws2812fx.setSpeed(ws2812fx.getSpeed() * 1.2); Serial.print(F("Increased speed by 20% to: ")); Serial.println(ws2812fx.getSpeed()); } - if (strcmp(cmd,"s-") == 0) { + if (strcmp(scmd,"s-") == 0) { ws2812fx.setSpeed(ws2812fx.getSpeed() * 0.8); Serial.print(F("Decreased speed by 20% to: ")); Serial.println(ws2812fx.getSpeed()); } - if (strncmp(cmd,"s ",2) == 0) { - uint16_t s = (uint16_t)atoi(cmd + 2); + if (strncmp(scmd,"s ",2) == 0) { + uint16_t s = (uint16_t)atoi(scmd + 2); ws2812fx.setSpeed(s); Serial.print(F("Set speed to: ")); Serial.println(ws2812fx.getSpeed()); } - if (strncmp(cmd,"m ",2) == 0) { - uint8_t m = (uint8_t)atoi(cmd + 2); + if (strncmp(scmd,"m ",2) == 0) { + uint8_t m = (uint8_t)atoi(scmd + 2); ws2812fx.setMode(m); Serial.print(F("Set mode to: ")); Serial.print(ws2812fx.getMode()); @@ -126,15 +126,15 @@ void process_command() { Serial.println(ws2812fx.getModeName(ws2812fx.getMode())); } - if (strncmp(cmd,"c ",2) == 0) { - uint32_t c = (uint32_t)strtoul(cmd + 2, NULL, 16); + if (strncmp(scmd,"c ",2) == 0) { + uint32_t c = (uint32_t)strtoul(scmd + 2, NULL, 16); ws2812fx.setColor(c); Serial.print(F("Set color to: 0x")); Serial.println(ws2812fx.getColor(), HEX); } - cmd[0] = '\0'; // reset the commandstring - cmd_complete = false; // reset command complete + scmd[0] = '\0'; // reset the commandstring + scmd_complete = false; // reset command complete } /* @@ -179,19 +179,19 @@ void printModes() { /* - * Reads new input from serial to cmd string. Command is completed on \n + * Reads new input from serial to scmd string. Command is completed on \n */ void recvChar(void) { static byte index = 0; - while (Serial.available() > 0 && cmd_complete == false) { + while (Serial.available() > 0 && scmd_complete == false) { char rc = Serial.read(); if (rc != '\n') { - if(index < MAX_NUM_CHARS) cmd[index++] = rc; + if(index < MAX_NUM_CHARS) scmd[index++] = rc; } else { - cmd[index] = '\0'; // terminate the string + scmd[index] = '\0'; // terminate the string index = 0; - cmd_complete = true; - Serial.print("received '"); Serial.print(cmd); Serial.println("'"); + scmd_complete = true; + Serial.print("received '"); Serial.print(scmd); Serial.println("'"); } } } diff --git a/examples/ws2812fx_teensy/ws2812fx_teensy.ino b/examples/ws2812fx_teensy/ws2812fx_teensy.ino new file mode 100644 index 0000000..0a8cdc0 --- /dev/null +++ b/examples/ws2812fx_teensy/ws2812fx_teensy.ino @@ -0,0 +1,87 @@ +/* + This sketch is a modification of the ws2812fx_dma sketch, which uses + Paul Stoffregen's WS2812Serial library (instead of Michael Miller's + NeoPixelBus library) to enable the Teensy microcontroller's DMA to + drive the LEDs. + + Note the WS2812Serial lib isn't installable from the Teensyduino IDE's + Manage Libraries menu, but instead must be installed using the + Sketch -> Include Library -> Add .ZIP Library after downloading + WS2812Serial from Paul's GitHub page: + https://github.com/PaulStoffregen/WS2812Serial + + Keith Lord - 2021 + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2021 Keith Lord + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sub-license, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + CHANGELOG + 2021-08-20 initial version +*/ + +#include +#include + +// Usable pins: +// Teensy LC: 1, 4, 5, 24 +// Teensy 3.2: 1, 5, 8, 10, 31 (overclock to 120 MHz for pin 8) +// Teensy 3.5: 1, 5, 8, 10, 26, 32, 33, 48 +// Teensy 3.6: 1, 5, 8, 10, 26, 32, 33 +// Teensy 4.0: 1, 8, 14, 17, 20, 24, 29, 39 +// Teensy 4.1: 1, 8, 14, 17, 20, 24, 29, 35, 47, 53 +#define LED_PIN 1 // digital pin used to drive the LED strip +#define LED_COUNT 144 // number of LEDs on the strip + +// allocate a display buffer for WS2812Serial +DMAMEM byte displayMemory[LED_COUNT * 12]; // 12 bytes per RGB LED or 16 bytes per RGBW LED + +WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); + +WS2812Serial *ws2812serialRef; + +void setup() { + Serial.begin(115200); + + ws2812fx.init(); + ws2812fx.setBrightness(32); + + const uint32_t colors[] = {GREEN, BLACK, BLACK}; + ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_LARSON_SCANNER, colors, 5000, NO_OPTIONS); + + ws2812fx.setCustomShow(myCustomShow); // set the custom show function + ws2812fx.start(); + + // Instantiate and init the WS2812Serial instance last, so the LED_PIN pin is configured properly. + // For the WS2812Serial instance always use WS2812_BGR, since it's sense of color byte order seems backwards. + ws2812serialRef = new WS2812Serial(LED_COUNT, displayMemory, ws2812fx.getPixels(), LED_PIN, WS2812_BGR); + ws2812serialRef->begin(); +} + +void loop() { + ws2812fx.service(); +} + +void myCustomShow(void) { + ws2812serialRef->show(); // run the WS2812Serial show() function +} diff --git a/examples/ws2812fx_transitions/ws2812fx_transitions.ino b/examples/ws2812fx_transitions/ws2812fx_transitions.ino new file mode 100644 index 0000000..f1896c8 --- /dev/null +++ b/examples/ws2812fx_transitions/ws2812fx_transitions.ino @@ -0,0 +1,84 @@ +/* + A demo of effect transitions. + A new class WS2812FXT (note the 'T' at the end) provides the ability to + create effect transitions. Note, WS2812FXT essentially creates THREE + WS2812FX instances to do it's work, so uses considerably more memory + than the normal WS2812FX class. + + WS2812FXT creates three WS2812FX objects, two virtual LED strips (v1 and v2) + and one physical strip (p1). You setup the two virtual strips using the normal + WS2812FX API, where each virtual strip can have its own brightness, effect, + speed, segments, etc. The physical strip is used to drive the LLEDs. Behind + the scenes WS2812FXT takes care of blending the two virtual strips together + to create the transition from v1 to v2 on the physical strip. + + To transition from the v1 effect to the v2 effect, call the startTransition() + function passing in the duration of the transition (in ms), and optionally + the transition direction (i.e direction == true produces a transition from + v1 to v2, while direction == false produces a transition from v2 to v1). + + LICENSE + + The MIT License (MIT) + + Copyright (c) 2021 Keith Lord + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sub-license, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + + CHANGELOG + 2021-08-20 initial version +*/ + +#include + +#define LED_PIN 4 +#define LED_COUNT 144 + +WS2812FXT ws2812fxt = WS2812FXT(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800); + +void setup() { + + ws2812fxt.init(); + + // setup the effects on the two virtual strips + // note v1 and v2 are pointers, so you must use pointer syntax + // (i.e. use ws2812fxt.v1->setMode(...), not ws2812fxt.v1.setMode(...)) + ws2812fxt.v1->setBrightness(32); + ws2812fxt.v1->setSegment(0, 0, LED_COUNT-1, FX_MODE_RAINBOW_CYCLE, BLACK, 5000); + + ws2812fxt.v2->setBrightness(128); + ws2812fxt.v2->setSegment(0, 0, LED_COUNT-1, FX_MODE_LARSON_SCANNER, BLUE, 3000); + + ws2812fxt.start(); +} + +void loop() { + ws2812fxt.service(); + + // every ten seconds transition from v1 to v2 then back to v1 + static bool direction = true; + static unsigned long timer = 10000; + unsigned long now = millis(); + + if(now > timer) { + ws2812fxt.startTransition(5000, direction); // transition duration = 5 seconds + direction = !direction; // change the transition direction + timer = now + 10000; // transition every ten seconds + } +} diff --git a/extras/WS2812FX API.md b/extras/WS2812FX API.md new file mode 100644 index 0000000..1370141 --- /dev/null +++ b/extras/WS2812FX API.md @@ -0,0 +1,198 @@ +# WS2812FX API + +The WS2812FX Users Guide documents the major library functions used for creating +a lighting sketch, but the library also contains many "helper" functions +that may be of use to more advanced developers. +This document will list and elaborate on the libraries API. + +*** + +## Disclaimer + +Although the voltages and components used in WS2812FX projects are generally +safe, working with electronics does carry some risk. So… + +Descriptions of circuits, software and other related information in this +document are provided only to illustrate the use of WS2812FX. You are fully +responsible for the incorporation of these circuits, software, and information +in the design of your project. WS2812FX contributors and maintainers assume no +responsibility for any losses incurred by you or third parties arising from the +use of these circuits, software, or information. + +The authors have used reasonable care in preparing the information included in +this document, but we do not warrant that such information is error free. We +assume no liability whatsoever for any damages resulting from errors in or +omissions from the information included herein. + +--- +## Getting Feedback About How an Animation is Progressing + +The isFrame() and isCycle() can be used to provide feedback about the state +of an effect to the user's sketch. + +*isFrame()* returns true when the LEDs in a segment have changed (think of it +as marking one "frame" of animation). + +*isCycle()* returns true when a repetitive effect ends a cycle. For instance, +the Larson Scanner effect will cause isCycle() to return true when the lights +have completed a cycle (i.e. lights traveling from the start of the segment +to the end and back to the start). + +These functions can be called in the loop() function, after the call to the +WS2812FX service(), to count animation frames or effect cycles, and synchronize +the lights to other events. +```c++ +void loop() { + ws2812fx.service(); + + if(ws2812fx.isFrame()) { + Serial.println("Frame complete"); + } + if(ws2812fx.isCycle()) { + Serial.println("Cycle complete"); + } +} +``` +The ws2812fx_soundfx example sketch makes good use of this feature. + +--- +## Overriding the Default Number of Segments +By default WS2812FX creates data structures to support up to ten segments per +LED strip. Unfortunately, the data structures consume a small amount of SRAM +memory wether you use all ten segments or not. If you're programming a +memory constrained microprocessor, like an Arduino, you might want to reduce +the segment data structures to free up more memory for your sketch. Conversely, +if you want to partition your strip into more than ten segments, you can +increase the allowed number of segments per strip. You change the maximum number +of segments per strip by using an enhanced constructor that has two additional +parameters: +>WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800, NUM_SEGMENTS, NUM_ACTIVE_SEGMENTS); + +The *NUM_SEGMENTS* parameter sets the maximum number of segments allowed per +strip and *NUM_ACTIVE_SEGMENTS* sets how many segments can be active at +the same time. +```c++ +// Setup a strip that can have only one segment (conserves memory) +WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800, 1, 1); + +// Setup a strip that can have up to 20 segments and all 20 can be active +WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800, 20, 20); + +// Setup a strip that can have up to 20 segments, but only 5 can be active at +// the same time. This scenario might be used if you're creating 20 idle +// (inactive) segments, and dynamically activating a subset segments (5 or +// fewer) based on external events. See the "Segment management" section of +// the WS2812FX users guide. +WS2812FX ws2812fx = WS2812FX(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800, 20, 5); +``` + +--- +## Active and Idle Segments +When you want to create dynamic lighting, that is, lighting that changes over +time, typically you would simply run setSegment statements to update the LEDs +based on some external event. Like so: +```c++ +unsigned long timer = 0; // a timer +void loop() { + unsigned long now = millis(); + ws2812fx.service(); + + if((now - timer) > 10000) { // every 10 seconds + uint32_t newColor = ws2812fx.color_wheel(ws2812fx.random8()); // a random color + ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_STATIC, newColor, 1000); + timer = now; // reset the timer + } +} +``` + +Alternatively, you can setup active and idle segments at the beginning of the +sketch and switch which segments are active based on some external event. +```c++ +void setup() { +... + // create one active segment and one idle segment + // note both segments span the entire LED strip + ws2812fx.setSegment (0, 0, LED_COUNT-1, FX_MODE_BLINK, YELLOW, 2000); // seg[0] + ws2812fx.setIdleSegment(1, 0, LED_COUNT-1, FX_MODE_BLINK, GREEN, 2000); // seg[1] +... +} + +unsigned long timer = 0; // a timer +void loop() { + unsigned long now = millis(); + ws2812fx.service(); + + if((now - timer) > 10000) { // every 10 seconds + if(ws2812fx.isActiveSegment(0)) { // if seg[0] is active, switch to seg[1] + ws2812fx.swapActiveSegment(0, 1); + } else { // else, switch to seg[0] + ws2812fx.swapActiveSegment(1, 0); + } + timer = now; // reset the timer + } +} +``` +The library provides these functions to manage active/idle segments: + - addActiveSegment(seg) - makes a segment active + - removeActiveSegment(seg) - makes a segment idle + - swapActiveSegment(oldSeg, newSeg) - makes segment *oldSeg* idle, and segment *newSeg* active + - isActiveSegment(seg) - returns true if a segment is active + - getActiveSegments() - returns an array of _NUM_ACTIVE_SEGMENTS_ bytes representing the active/idle state of each segment. A byte value of 255 indicates the segment is idle , and any other value indicates the segment is active. + +The *ws2812fx_segment_sequence* example sketch demonstrates this technique. + +--- +## Miscellaneous Helper Functions + **Fast random number generation** + - random8() - return an 8-bit random number + - random8(lim) - return an 8-bit random number between 0 and (lim - 1) + - random16() - return a 16-bit random number + - random16(lim) - return a 16-bit random number between 0 and (lim - 1) + - setRandomSeed(seed) - set the 16-bit random number generator seed + - get_random_wheel_index(num) - returns an 8-bit random number that is at least 42 more or less than _num_. Used mostly when generating a new random color that is significantly different from the current color: + ```c++ + // generate a new, significantly different, random color + uint8_t newColorIndex = ws2812fx.get_random_wheel_index(oldColorIndex); + uint32_t newColor = ws2812fx.color_wheel(newColorIndex); + ``` + + **Color functions** + - getColor() - returns the 32-bit color for segment 0. + - getColor(seg) - returns the 32-bit color for segment _seg_. + - getColors(seg) - returns an array of three 32-bit colors for segment _seg_. + - color_wheel(index) - return a 32-bit RGB color value. The 8-bit index parameter selects one color from a virtual table of 256 colors. The virtual table contains a rainbow of colors in ROYGBIV order: + red - orange - yellow - green - blue -indigo - violet - red + ```c++ + // generate a random color + uint32_t color = ws2812fx.color_wheel(ws2812fx.random8()); + ``` + - color_blend(color1, color2, blendAmount) - returns the resulting 32-bit color created by blending together _color1_ and _color2_ by _blendAmount_. If _blendAmount_ = 0, color1 is returned. If _blendAmount_ = 255, color2 is returned. For intermediate values of _blendAmount_, a proportionally blended color is returned. + + - intensitySum() - returns the 32-bit sum of all LED intensities. Used for making strip power estimates. + - intensitySums() - returns an array of three (for RGB LEDs) or four (for RGBW LEDs) 32-bit values. Each value is the sum of all individual RGB color intensities. Used for making strip power estimates. Slower than _intensitySum()_, but gives more fine grained detail for each color's power estimate. + +--- +**LED strip functions** +- setPixels(numPixels, ptr) - frees the underlaying Adafruit_Neopixel pixels array, and swaps it out for a user created pixels array. Used carefully, it can allow greater access to the raw pixel data. The _ws2812fx_virtual_strip_ example sketch uses this technique to drive two physical strips with one virtual strip. +```c++ +uint8_t myPixels[LED_COUNT * 3]; // 3 bytes per LED for RGB LEDs +setPixels(LED_COUNT, myPixels); +``` +- getNumBytes() - returns the number of bytes allocated to the pixels array. +- getNumBytesPerPixel() - returns 3 if the strip is RGB LEDs, or 4 if the strip is RGBW LEDs +- blend() - blends pixel data from two strips. +```c++ +// blend the pixel data from strips ws2812fx1 and ws2812fx2 +// to create the pixel data for strip ws2812fx3 +uint8_t *src1_p = ws2812fx1.getPixels(); // get pointers to the strips' pixel data +uint8_t *src2_p = ws2812fx2.getPixels(); +uint8_t *dest_p = ws2812fx3.getPixels(); +uint16_t numBytes = ws2812fx3.getNumBytes(); // number of bytes of pixel data +uint8_t blendAmt = 128; // blend amount 0-255: 0 = all ws2812fx1, 255 = all ws2812fx2 +ws2812fx3.blend(dest_p, src1_p, src2_p, numBytes, blendAmt); +``` + + + +--- +To be continued... diff --git a/extras/WS2812FX Users Guide.md b/extras/WS2812FX Users Guide.md index d272385..23b8e6b 100644 --- a/extras/WS2812FX Users Guide.md +++ b/extras/WS2812FX Users Guide.md @@ -223,13 +223,28 @@ hardware setup. After you have the sketch working, you can play with the settings to get a feel for how each parameter affects the strip. Vary the speed and brightness. The -color can be changed by specifying one of the built-in color keywords, like -“RED”, “GREEN”, “BLUE” or “WHITE” or use a hexadecimal number, like 0x40c020, to -create a unique color by mixing different intensities of red, green and blue. +color can be changed by specifying one of the built-in color macros: +```c++ +#define RED (uint32_t)0xFF0000 +#define GREEN (uint32_t)0x00FF00 +#define BLUE (uint32_t)0x0000FF +#define WHITE (uint32_t)0xFFFFFF +#define BLACK (uint32_t)0x000000 +#define YELLOW (uint32_t)0xFFFF00 +#define CYAN (uint32_t)0x00FFFF +#define MAGENTA (uint32_t)0xFF00FF +#define PURPLE (uint32_t)0x400080 +#define ORANGE (uint32_t)0xFF3000 +#define PINK (uint32_t)0xFF1493 +#define GRAY (uint32_t)0x101010 +#define ULTRAWHITE (uint32_t)0xFFFFFFFF /* for RGBW LEDs */ +``` +Or use a hexadecimal number, like 0x40c020, to create a unique color by +mixing different intensities of red, green and blue. The [w3schools.com colors](https://www.w3schools.com/colors/) web page has a nice explanation about expressing colors as hexadecimal numbers. Note, the web -page shows hex numbers as “\#aabbcc”, but in the Arduino IDE they are written -“0xaabbcc”. +page shows hex numbers as “\#RRGGBB”, but in the Arduino IDE they are written +“0xRRGGBB”. The best part of WS2812FX is the many different animation effects that it can create. More than 50! The only way to get a feel for each one is to try them. In @@ -276,7 +291,7 @@ program a segment that spans the entire length of the strip. Think about it; it makes sense. The next three parameters are effect/mode, color and speed, which you’ve seen -before. The last parameter is new and specifies whether to run the animation in +before. The last parameter is optional and specifies whether to run the animation in the reverse direction. It’s a boolean parameter, so it can be true or false, and applies only to effects that have a direction, like FX_MODE_COLOR_WIPE or FX_MODE_THEATER_CHASE. Specifying reverse=false will run the animation from @@ -296,15 +311,15 @@ Segments provide a great deal of flexibility to subdivide the strip. You can hard code the first and last LED indexes like so: ```c++ // divide a strip of 30 LEDs in half -ws2812fx.setSegment(0, 0, 14, FX_MODE_FADE, RED, 2000, false); -ws2812fx.setSegment(1, 15, 29, FX_MODE_SCAN, BLUE, 2000, false); +ws2812fx.setSegment(0, 0, 14, FX_MODE_FADE, RED, 2000); +ws2812fx.setSegment(1, 15, 29, FX_MODE_SCAN, BLUE, 2000); ``` or you can use LED_COUNT and some math to specify the first and last LED indexes: ```c++ // divide a strip of LEDs into thirds int size = LED_COUNT/3; // calc the size of each segment -ws2812fx.setSegment(0, 0, size-1, FX_MODE_FADE, RED, 2000, false); +ws2812fx.setSegment(0, 0, size-1, FX_MODE_FADE, RED, 2000); ws2812fx.setSegment(1, size, size*2-1, FX_MODE_SCAN, BLUE, 2000, true); ws2812fx.setSegment(2, size*2, LED_COUNT-1, FX_MODE_COMET, GREEN, 2000, true); ``` @@ -314,11 +329,16 @@ changing the LED_COUNT value; setSegment() also allows you to specify up to three colors using this syntax: ```c++ uint32_t colors[] = {RED, GREEN, BLUE}; // create an array of colors -ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_BLINK, colors, 2000, false); +ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_BLINK, colors, 2000); +``` +or there's a COLORS(...) macro that can be used to express colors more succinctly: +```c++ +ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_BLINK, COLORS(RED, GREEN, BLUE), 2000); ``` -or more succinctly: +There's also the DIM and DARK macros which can modify a color's intensity. +DIM reduces a color's intensity by 74% and DARK reduces intensity by 94%. ```c++ -ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_BLINK, COLORS(RED, GREEN, BLUE), 2000, false); +uint32_t colors[] = {RED, DIM(GREEN), DARK(BLUE)}; ``` Not all modes support multiple colors. FX_MODE_COLOR_WIPE, for instance, will alternate between the first and second color. FX_MODE_TRICOLOR_CHASE creates a @@ -466,7 +486,7 @@ custom effect function. ``` 3. Create a segment using the FX_MODE_CUSTOM mode. ```c++ -ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_CUSTOM, RED, 300, NO_OPTIONS); +ws2812fx.setSegment(0, 0, LED_COUNT-1, FX_MODE_CUSTOM, RED, 300); ``` *** diff --git a/extras/WS2812FX change log.txt b/extras/WS2812FX change log.txt index cb42654..1d4041c 100644 --- a/extras/WS2812FX change log.txt +++ b/extras/WS2812FX change log.txt @@ -1,6 +1,45 @@ WS2182FX Change Log +v1.3.4 changes 8/21/2021 +------------------------ + +1) Updated the web interface example sketches to be compatible + with ESP32 as suggested by kadu in PR #280. Although beware + of the ESP32 "flickering" issue when using more than a few + LEDs (issue #223). Use the RMT technique in the ws2812fx_esp32 + example sketch as a workaround. + +2) Added setRandomSeed() function to allow greater control of + random number generation as suggested by schumar in issue #288. + +3) Added the ws2812fx_teensy example sketch to demo how to use + Paul Stoffregen's non-blocking WS2812Serial library for the + Teensy. Inspired by issue #277. + +4) If you aren't using any options in a setSegment() statement, + you can just omit the option parameter at the end. + Eg. ws2812fx.setSegment(0, 0, 31, FX_MODE_BLINK, RED, 1000); + Also setSegment() with no arguments will initialize segment 0 + with reasonable default values. + Eg. ws2812fx.setSegment() is equivalent to + ws2812fx.setSegment(0, 0, getLength()-1, DEFAULT_MODE, DEFAULT_COLOR, DEFAULT_SPEED) + +5) Created a generic blend() function, which will eventually be + used to merge pixel data to create effect transitions. + +6) Refactored the color_blend() function to use the new generic + blend() function. Better performance. + +7) Added a new WS2812FXT class to help create effect transitions + and the ws2812fx_transitions example sketch to demo its use. + +8) Started working on some API documentation. + +9) Fixed an odd bug in the serial_control example sketch that was + causing a compile error on Raspberry Pi Pico. + + v1.3.3 changes 4/15/2021 ------------------------ diff --git a/keywords.txt b/keywords.txt index 41db7e8..b272916 100644 --- a/keywords.txt +++ b/keywords.txt @@ -31,6 +31,7 @@ SIZE_LARGE LITERAL1 SIZE_XLARGE LITERAL1 WS2812FX KEYWORD1 +WS2812FXT KEYWORD1 init KEYWORD2 service KEYWORD2 @@ -73,6 +74,7 @@ isRunning KEYWORD2 isTriggered KEYWORD2 isFrame KEYWORD2 isCycle KEYWORD2 +setRandomSeed KEYWORD2 random8 KEYWORD2 random16 KEYWORD2 getLength KEYWORD2 diff --git a/library.json b/library.json index 6194297..6955b76 100644 --- a/library.json +++ b/library.json @@ -6,7 +6,7 @@ "name": "Harm Aldick", "url": "https://github.com/kitesurfer1404/WS2812FX" }, - "version": "1.3.3", + "version": "1.3.4", "downloadUrl": "https://github.com/kitesurfer1404/WS2812FX/archive/master.zip", "export": { "include": "WS2812FX-master" diff --git a/library.properties b/library.properties index e9ccd4e..ad1665d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=WS2812FX -version=1.3.3 +version=1.3.4 author=Harm Aldick maintainer=Harm Aldick sentence=WS2812 FX Library for Arduino and ESP microprocessors. diff --git a/src/WS2812FX.cpp b/src/WS2812FX.cpp index 45fb06e..606f558 100644 --- a/src/WS2812FX.cpp +++ b/src/WS2812FX.cpp @@ -65,10 +65,10 @@ void WS2812FX::init() { // } // } -void WS2812FX::service() { +bool WS2812FX::service() { + bool doShow = false; if(_running || _triggered) { unsigned long now = millis(); // Be aware, millis() rolls over every 49 days - bool doShow = false; for(uint8_t i=0; i < _active_segments_len; i++) { if(_active_segments[i] != INACTIVE_SEGMENT) { _seg = &_segments[_active_segments[i]]; @@ -90,6 +90,7 @@ void WS2812FX::service() { } _triggered = false; } + return doShow; } // overload setPixelColor() functions so we can use gamma correction @@ -378,23 +379,36 @@ const __FlashStringHelper* WS2812FX::getModeName(uint8_t m) { } } -void WS2812FX::setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options) { - uint32_t colors[] = {color, 0, 0}; - setIdleSegment(n, start, stop, mode, colors, speed, options); +void WS2812FX::setSegment() { + setSegment(0, 0, getLength()-1, DEFAULT_MODE, DEFAULT_COLOR, DEFAULT_SPEED, NO_OPTIONS); } -void WS2812FX::setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options) { - setSegment(n, start, stop, mode, colors, speed, options); - if(n < _active_segments_len) removeActiveSegment(n);; +void WS2812FX::setSegment(uint8_t n) { + setSegment(n, 0, getLength()-1, DEFAULT_MODE, DEFAULT_COLOR, DEFAULT_SPEED, NO_OPTIONS); } -void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse) { - uint32_t colors[] = {color, 0, 0}; - setSegment(n, start, stop, mode, colors, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS)); +void WS2812FX::setSegment(uint8_t n, uint16_t start) { + setSegment(n, start, getLength()-1, DEFAULT_MODE, DEFAULT_COLOR, DEFAULT_SPEED, NO_OPTIONS); } -void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse) { - setSegment(n, start, stop, mode, colors, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS)); +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop) { + setSegment(n, start, stop, DEFAULT_MODE, DEFAULT_COLOR, DEFAULT_SPEED, NO_OPTIONS); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode) { + setSegment(n, start, stop, mode, DEFAULT_COLOR, DEFAULT_SPEED, NO_OPTIONS); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color) { + setSegment(n, start, stop, mode, color, DEFAULT_SPEED, NO_OPTIONS); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed) { + setSegment(n, start, stop, mode, color, speed, NO_OPTIONS); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse) { + setSegment(n, start, stop, mode, color, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS)); } void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options) { @@ -402,6 +416,18 @@ void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode setSegment(n, start, stop, mode, colors, speed, options); } +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[]) { + setSegment(n, start, stop, mode, colors, DEFAULT_SPEED, NO_OPTIONS); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed) { + setSegment(n, start, stop, mode, colors, speed, NO_OPTIONS); +} + +void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse) { + setSegment(n, start, stop, mode, colors, speed, (uint8_t)(reverse ? REVERSE : NO_OPTIONS)); +} + void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options) { if(n < _segments_len) { if(n + 1 > _num_segments) _num_segments = n + 1; @@ -417,6 +443,20 @@ void WS2812FX::setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode } } +void WS2812FX::setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed) { + setIdleSegment(n, start, stop, mode, color, speed, NO_OPTIONS); +} + +void WS2812FX::setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options) { + uint32_t colors[] = {color, 0, 0}; + setIdleSegment(n, start, stop, mode, colors, speed, options); +} + +void WS2812FX::setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options) { + setSegment(n, start, stop, mode, colors, speed, options); + if(n < _active_segments_len) removeActiveSegment(n);; +} + void WS2812FX::addActiveSegment(uint8_t seg) { uint8_t* ptr = (uint8_t*)memchr(_active_segments, seg, _active_segments_len); if(ptr != NULL) return; // segment already active @@ -525,28 +565,32 @@ uint8_t WS2812FX::get_random_wheel_index(uint8_t pos) { return r; } +void WS2812FX::setRandomSeed(uint16_t seed) { + _rand16seed = seed; +} + // fast 8-bit random number generator shamelessly borrowed from FastLED uint8_t WS2812FX::random8() { - _rand16seed = (_rand16seed * 2053) + 13849; - return (uint8_t)((_rand16seed + (_rand16seed >> 8)) & 0xFF); + _rand16seed = (_rand16seed * 2053) + 13849; + return (uint8_t)((_rand16seed + (_rand16seed >> 8)) & 0xFF); } // note random8(lim) generates numbers in the range 0 to (lim -1) uint8_t WS2812FX::random8(uint8_t lim) { - uint8_t r = random8(); - r = ((uint16_t)r * lim) >> 8; - return r; + uint8_t r = random8(); + r = ((uint16_t)r * lim) >> 8; + return r; } uint16_t WS2812FX::random16() { - return (uint16_t)random8() * 256 + random8(); + return (uint16_t)random8() * 256 + random8(); } // note random16(lim) generates numbers in the range 0 to (lim - 1) uint16_t WS2812FX::random16(uint16_t lim) { - uint16_t r = random16(); - r = ((uint32_t)r * lim) >> 16; - return r; + uint16_t r = random16(); + r = ((uint32_t)r * lim) >> 16; + return r; } // Return the sum of all LED intensities (can be used for @@ -1063,28 +1107,25 @@ void WS2812FX::fade_out(uint32_t targetColor) { /* * color blend function */ -uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blend) { - if(blend == 0) return color1; - if(blend == 255) return color2; - - uint8_t w1 = (color1 >> 24) & 0xff; - uint8_t r1 = (color1 >> 16) & 0xff; - uint8_t g1 = (color1 >> 8) & 0xff; - uint8_t b1 = color1 & 0xff; - - uint8_t w2 = (color2 >> 24) & 0xff; - uint8_t r2 = (color2 >> 16) & 0xff; - uint8_t g2 = (color2 >> 8) & 0xff; - uint8_t b2 = color2 & 0xff; - - uint32_t w3 = ((w2 * blend) + (w1 * (255U - blend))) / 256U; - uint32_t r3 = ((r2 * blend) + (r1 * (255U - blend))) / 256U; - uint32_t g3 = ((g2 * blend) + (g1 * (255U - blend))) / 256U; - uint32_t b3 = ((b2 * blend) + (b1 * (255U - blend))) / 256U; - - return ((w3 << 24) | (r3 << 16) | (g3 << 8) | (b3)); +uint32_t WS2812FX::color_blend(uint32_t color1, uint32_t color2, uint8_t blendAmt) { + uint32_t blendedColor; + blend((uint8_t*)&blendedColor, (uint8_t*)&color1, (uint8_t*)&color2, sizeof(uint32_t), blendAmt); + return blendedColor; } +uint8_t* WS2812FX::blend(uint8_t *dest, uint8_t *src1, uint8_t *src2, uint16_t cnt, uint8_t blendAmt) { + if(blendAmt == 0) { + memmove(dest, src1, cnt); + } else if(blendAmt == 255) { + memmove(dest, src2, cnt); + } else { + for(uint16_t i=0; i @@ -370,12 +371,11 @@ class WS2812FX : public Adafruit_NeoPixel { resetSegments(); setSegment(0, 0, num_leds - 1, DEFAULT_MODE, DEFAULT_COLOR, DEFAULT_SPEED, NO_OPTIONS); - } + }; void // timer(void), init(void), - service(void), start(void), stop(void), pause(void), @@ -406,15 +406,29 @@ class WS2812FX : public Adafruit_NeoPixel { trigger(void), setCycle(void), setNumSegments(uint8_t n), - setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse), - setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options), + + setSegment(), + setSegment(uint8_t n), + setSegment(uint8_t n, uint16_t start), + setSegment(uint8_t n, uint16_t start, uint16_t stop), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, bool reverse), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options), + + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[]), + setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, bool reverse), setSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options), + + setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed), setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, uint32_t color, uint16_t speed, uint8_t options), setIdleSegment(uint8_t n, uint16_t start, uint16_t stop, uint8_t mode, const uint32_t colors[], uint16_t speed, uint8_t options), addActiveSegment(uint8_t seg), removeActiveSegment(uint8_t seg), swapActiveSegment(uint8_t oldSeg, uint8_t newSeg), + resetSegments(void), resetSegmentRuntimes(void), resetSegmentRuntime(uint8_t), @@ -423,9 +437,11 @@ class WS2812FX : public Adafruit_NeoPixel { setPixelColor(uint16_t n, uint8_t r, uint8_t g, uint8_t b, uint8_t w), copyPixels(uint16_t d, uint16_t s, uint16_t c), setPixels(uint16_t, uint8_t*), + setRandomSeed(uint16_t), show(void); bool + service(void), isRunning(void), isTriggered(void), isFrame(void), @@ -464,6 +480,7 @@ class WS2812FX : public Adafruit_NeoPixel { uint32_t* getColors(uint8_t); uint32_t* intensitySums(void); uint8_t* getActiveSegments(void); + uint8_t* blend(uint8_t*, uint8_t*, uint8_t*, uint16_t, uint8_t); const __FlashStringHelper* getModeName(uint8_t m); @@ -596,6 +613,71 @@ class WS2812FX : public Adafruit_NeoPixel { uint16_t _seg_len; // num LEDs in the currently active segment }; +class WS2812FXT { + public: + WS2812FXT(uint16_t num_leds, uint8_t pin, neoPixelType type, + uint8_t max_num_segments=MAX_NUM_SEGMENTS, + uint8_t max_num_active_segments=MAX_NUM_ACTIVE_SEGMENTS) { + v1 = new WS2812FX(num_leds, pin, type, max_num_segments, max_num_active_segments); + v2 = new WS2812FX(num_leds, pin, type, max_num_segments, max_num_active_segments); + dest = new WS2812FX(num_leds, pin, type, max_num_segments, max_num_active_segments); + }; + + void init(void) { + v1->init(); + v2->init(); + v1->setCustomShow([]{ return; }); + v2->setCustomShow([]{ return; }); + } + + void start(void) { + v1->start(); + v2->start(); + } + + void service(void) { + bool doShow = v1->service() || v2->service(); + if(doShow) { + _show(); + } + } + + void startTransition(uint16_t duration, bool direction = true) { + transitionStartTime = millis(); + transitionDuration = duration; + transitionDirection = direction; + } + + private: + void _show(void) { + unsigned long now = millis(); + + uint8_t *dest_p = dest->getPixels(); + uint8_t *vstart_p = transitionDirection ? v1->getPixels() : v2->getPixels(); + uint8_t *vstop_p = transitionDirection ? v2->getPixels() : v1->getPixels(); + uint16_t numBytes = dest->getNumBytes(); + + if(now < transitionStartTime) { + memmove(dest_p, vstart_p, numBytes); + } else if(now > transitionStartTime + transitionDuration) { + memmove(dest_p, vstop_p, numBytes); + } else { + uint8_t blendAmt = map(now, transitionStartTime, transitionStartTime + transitionDuration, 0, 255); + dest->blend(dest_p, vstart_p, vstop_p, numBytes, blendAmt); + } + + dest->Adafruit_NeoPixel::show(); + } + + public: + WS2812FX* v1 = NULL; + WS2812FX* v2 = NULL; + WS2812FX* dest = NULL; + unsigned long transitionStartTime = MAX_MILLIS; + uint16_t transitionDuration = 5000; + bool transitionDirection = true; +}; + // define static array of member function pointers. // function pointers MUST be in the same order as the corresponding name in the _name array. static WS2812FX::mode_ptr _modes[MODE_COUNT] = {