Skip to content

Instantly share code, notes, and snippets.

@kriegsman
Created August 20, 2015 21:40
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save kriegsman/4a001f3dafc30e7d8341 to your computer and use it in GitHub Desktop.
Save kriegsman/4a001f3dafc30e7d8341 to your computer and use it in GitHub Desktop.
MarqueeOverlay - render marquee-like effects on segments of a display (for YUM cart, Burning Man 2015)
#include "FastLED.h"
// MarqueeOverlay
//
// Code to overlay a 'marquee' effect on top
// of other animations, e.g. to highlight individual
// letters of a sign one after another, while
// still allowing the underlying animations to show through.
//
// Initially designed for the "YUM cart" for Burning Man 2015.
// The YUM cart has six segments of 124 LEDs each.
// This example code assumes merely one sixty-LED strip,
// but includes definitions for 6 * 124 segments as well.
//
// Also initially designed to run on a Teensy 3.1,
// where SRAM is relatively plentiful. This code
// runs fine on an AVR Arduino, but much of the static
// data could be moved into PROGMEM to save SRAM there.
//
// Mark Kriegsman, August 2015.
#if FASTLED_VERSION < 3001000
#error "Requires FastLED 3.1 or later; check github for latest code."
#endif
#define DATA_PIN 13
//#define CLK_PIN 4
#define LED_TYPE WS2811
#define COLOR_ORDER GRB
#define NUM_LEDS 60
#define BRIGHTNESS 255
CRGB leds[NUM_LEDS];
void setup() {
delay(3000); // 3 second delay for recovery
// tell FastLED about the LED strip configuration
FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS)
.setCorrection(TypicalLEDStrip) // cpt-city palettes have different color balance
.setDither(BRIGHTNESS < 255);
// set master brightness control
FastLED.setBrightness(BRIGHTNESS);
}
void loop()
{
DrawRegularPattern(); // here's where you draw your regular animation
// Then you add this, and applies various 'marquee' effects, from time to time
// across the various segments of LED strip that you define (below).
ApplyMarqueeEffect();
FastLED.show();
FastLED.delay(10);
}
// This is just a stand-in for whatever your regular animations are.
void DrawRegularPattern()
{
static uint8_t start = 0;
start -= 1;
uint8_t hue = start;
for( uint16_t i = 0; i < NUM_LEDS; i++) {
leds[i] = CHSV( hue, 240, 192);
hue += 3;
}
}
/////////////////// Start of Marquee animation effects //////////////////
//
// Activate these by putting "ApplyMarqueeEffect();" just before
// your call to FastLED.show.
//
// How often to start a new marquee animation, in seconds:
const uint32_t gMarqueeCycleSeconds = 10;
// How long the marquee animation should run, in seconds:
const uint32_t gMarqueeEffectSeconds = 3;
// How long each 'step' of the marquee animation is, in milliseconds:
const uint16_t gMarqueeSequenceStepTimeMsMin = 50;
const uint16_t gMarqueeSequenceStepTimeMsMax = 450;
typedef struct TMarqueeSegment {
CRGB* mBase;
uint16_t mLength;
} TMarqueeSegment;
// Definitions for the "segments" of the LED strip(s) to
// be treated as separate areas. They can be in
// different CRGB arrays, or all in the same one, or any combination.
// Each segment is defined as a starting point in an array, and a length.
#ifndef NUM_LEDS_PER_STRIP
// Sixty-pixel demo, with six segments in it.
TMarqueeSegment gMarqueeSegments[] = {
{ leds + 0 * (NUM_LEDS/6), (NUM_LEDS/6)},
{ leds + 1 * (NUM_LEDS/6), (NUM_LEDS/6)},
{ leds + 2 * (NUM_LEDS/6), (NUM_LEDS/6)},
{ leds + 3 * (NUM_LEDS/6), (NUM_LEDS/6)},
{ leds + 4 * (NUM_LEDS/6), (NUM_LEDS/6)},
{ leds + 5 * (NUM_LEDS/6), (NUM_LEDS/6)}
};
#else
// If you just so happen to have six segments
// with NUM_LEDS_PER_STRIP pixels each, you could do this:
TMarqueeSegment gMarqueeSegments[] = {
{ leds + (0*NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP},
{ leds + (1*NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP},
{ leds + (2*NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP},
{ leds + (3*NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP},
{ leds + (4*NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP},
{ leds + (5*NUM_LEDS_PER_STRIP), NUM_LEDS_PER_STRIP}
};
#endif
// Animated sequence patterns for the marquees.
// Single digits refer to individual segments, '*' means all segments.
// So to affect segment 1, then segment 2, then segment 3, then all of them,
// you'd write the pattern "123*".
// If you have more physical segments than accounted for in the pattern,
// the pattern repeats across all segments. So for example, if you have
// six segments, but a pattern only uses the tokens 1, 2, and 3, the pattern
// is duplicated from segments 1..3 onto segments 4..6.
// The patterns listed here are executed in sequence, one after another.
const char* gMarqueeSequencePatterns[] = {
" 123",
"123456",
"112233",
"123 456 ",
"1 * 2 * 3 ",
"* ",
"1 2 3 ",
"14253625",
"123",
"* "
};
// There are multiple visual overlay effects used to create
// the marquee animation.
// If you don't like one of the effects, just comment out that line.
// You can also write your own overlay effects and just add them
// to this array.
// Visual effects are chosen at random.
typedef void (*TMarqueeSegmentEffect)( const struct TMarqueeSegment& segment);
const TMarqueeSegmentEffect gMarqueeSegmentEffects[] = {
// Effects that sprinkle 'glitter' on the background pattern
MarqueeEffectGlitterWhite,
MarqueeEffectGlitterBlack,
MarqueeEffectGlitterColor,
// Effects that create a crawling marquee on the background pattern
MarqueeEffectCrawlWhite,
MarqueeEffectCrawlBlack,
MarqueeEffectCrawlColor,
// Effects that display a steady, solid color
MarqueeEffectSolidWhite,
MarqueeEffectSolidBlack,
MarqueeEffectSolidColor,
// Effects that rapidly flicker a solid color
MarqueeEffectFlashWhite,
MarqueeEffectFlashBlack,
MarqueeEffectFlashColor,
// Effect that changes the hue of the background pattern
MarqueeEffectRotateColor
};
const uint8_t kMarqueeSequenceCount = sizeof( gMarqueeSequencePatterns ) / sizeof( const char*);
const uint8_t kMarqueeSegmentCount = sizeof(gMarqueeSegments) / sizeof( TMarqueeSegment);
const uint8_t kMarqueeSegmentEffectCount = sizeof( gMarqueeSegmentEffects) / sizeof( TMarqueeSegmentEffect);
uint8_t gMarqueeCurrentPatternNumber = 0;
uint8_t gMarqueeSegmentEffectNumber = 0;
void ApplyMarqueeEffect()
{
static bool running = true;
static uint32_t posStartTimeMs = 0;
static uint8_t seqpos = 0;
uint32_t curTimeMs = millis();
uint32_t cycletime = curTimeMs % (uint32_t)(gMarqueeCycleSeconds * 1000L);
bool shouldRun = cycletime < (uint32_t)(gMarqueeEffectSeconds * 1000L);
// Step 1: figure out if we're "off duty" right now, and if so,
// is it time to start up again?
if( !running ) {
if( shouldRun ) {
// start
running = true;
posStartTimeMs = curTimeMs;
seqpos = 0;
MarqueeChooseNewSegmentEffect();
gMarqueeCurrentPatternNumber = addmod8( gMarqueeCurrentPatternNumber, 1, kMarqueeSequenceCount);
} else {
return;
}
}
// Step 2: figure out which where we are in the current Marquee sequence
uint8_t len = MarqueeGetSequenceLength();
uint16_t gMarqueeSequenceStepTimeMs = beatsin88( 555, gMarqueeSequenceStepTimeMsMin, gMarqueeSequenceStepTimeMsMax);
if( (curTimeMs - posStartTimeMs) >= gMarqueeSequenceStepTimeMs) {
seqpos++;
posStartTimeMs = curTimeMs;
}
if( seqpos >= len) {
seqpos = 0;
if( !shouldRun) {
running = false;
return;
}
}
// Step 3: figure out which LED strip segment(s) to apply
// the effect to.
const char* cp = gMarqueeSequencePatterns[gMarqueeCurrentPatternNumber];
char c = cp[seqpos];
if( c == ' ') return;
uint8_t segno = 0;
if( c >= '0' && c <= '9') {
segno = c - '0';
} else if (c == '*') {
segno = 255; // meaning "all"
}
// junk characters = ignore;
if( segno == 0) return;
// apply to all segments
if( segno == 255) {
for( uint8_t s = 0; s < kMarqueeSegmentCount; s++) {
ApplyEffectToSegment( gMarqueeSegments[ s]);
}
} else {
// just one segment phase
segno--; // adjust for zero-index
uint8_t maxNamedSegment = MarqueeGetMax();
for( uint8_t s = segno; s < kMarqueeSegmentCount; s += maxNamedSegment) {
ApplyEffectToSegment( gMarqueeSegments[ s]);
}
}
}
uint8_t MarqueeGetSequenceLength()
{
return strlen( gMarqueeSequencePatterns[gMarqueeCurrentPatternNumber]);
}
uint8_t MarqueeGetMax()
{
uint8_t mx = 1;
const char* cp = gMarqueeSequencePatterns[gMarqueeCurrentPatternNumber];
char c;
while( (c = *cp)) {
if( c >= '0' && c <= '9') {
uint8_t n = c - '0';
if( n > mx ) {
mx = n;
}
}
cp++;
}
return mx;
}
void MarqueeChooseNewSegmentEffect()
{
gMarqueeSegmentEffectNumber = random8( kMarqueeSegmentEffectCount );
}
void ApplyEffectToSegment( const struct TMarqueeSegment& segment)
{
TMarqueeSegmentEffect effect = gMarqueeSegmentEffects[ gMarqueeSegmentEffectNumber ];
(effect)(segment);
}
void MarqueeEffectDim( const struct TMarqueeSegment& segment)
{
fadeToBlackBy( segment.mBase, segment.mLength, 192);
}
void MarqueeEffectFlashBlack( const struct TMarqueeSegment& segment)
{
MarqueeEffectFlashX( segment, CRGB::Black);
}
void MarqueeEffectFlashWhite( const struct TMarqueeSegment& segment)
{
MarqueeEffectFlashX( segment, CRGB(120,120,160)); // not FFFFFF for power reasons!
}
void MarqueeEffectFlashColor( const struct TMarqueeSegment& segment)
{
MarqueeEffectFlashX( segment, CHSV( millis() / 16, 200, 255));
}
void MarqueeEffectFlashX( const struct TMarqueeSegment& segment, const CRGB& color)
{
uint8_t m = millis() & 0x20;
if( m ) {
fill_solid( segment.mBase, segment.mLength, color);
}
}
void MarqueeEffectSolidBlack( const struct TMarqueeSegment& segment)
{
MarqueeEffectSolidX( segment, CRGB::Black);
}
void MarqueeEffectSolidWhite( const struct TMarqueeSegment& segment)
{
MarqueeEffectSolidX( segment, CRGB(120,120,160)); // not FFFFFF for power reasons!
}
void MarqueeEffectSolidColor( const struct TMarqueeSegment& segment)
{
MarqueeEffectSolidX( segment, CHSV( millis() / 16, 200, 255));
}
void MarqueeEffectSolidX( const struct TMarqueeSegment& segment, const CRGB& color)
{
fill_solid( segment.mBase, segment.mLength, color);
}
void MarqueeEffectGlitterBlack( const struct TMarqueeSegment& segment)
{
MarqueeEffectGlitterX( segment, CRGB::Black, 2);
}
void MarqueeEffectGlitterColor( const struct TMarqueeSegment& segment)
{
MarqueeEffectGlitterX( segment, CHSV( millis() / 16, 200, 255), 3);
}
void MarqueeEffectGlitterWhite( const struct TMarqueeSegment& segment)
{
MarqueeEffectGlitterX( segment, CRGB::White, 7);
}
void MarqueeEffectGlitterX( const struct TMarqueeSegment& segment, const CRGB& color, uint8_t glitFactor)
{
CRGB* segleds = segment.mBase;
uint16_t len = segment.mLength;
for( uint16_t i = random8(glitFactor); i < len; i += (random8(glitFactor) + 1)) {
segleds[i] = color;
}
}
void MarqueeEffectRotateColor( const struct TMarqueeSegment& segment)
{
CRGB* segleds = segment.mBase;
uint16_t len = segment.mLength;
for( uint16_t i = 0; i < len; i++) {
uint8_t t = segleds[i].r;
segleds[i].r = segleds[i].g;
segleds[i].g = segleds[i].b;
segleds[i].b = t;
}
}
void MarqueeEffectCrawlWhite( const struct TMarqueeSegment& segment)
{
MarqueeEffectCrawlX( segment, CRGB::White);
}
void MarqueeEffectCrawlBlack( const struct TMarqueeSegment& segment)
{
MarqueeEffectCrawlX( segment, CRGB::Black);
}
void MarqueeEffectCrawlColor( const struct TMarqueeSegment& segment)
{
MarqueeEffectCrawlX( segment, CHSV( millis() / 16, 200, 255));
}
void MarqueeEffectCrawlX( const struct TMarqueeSegment& segment, const CRGB& color)
{
CRGB* segleds = segment.mBase;
uint16_t len = segment.mLength;
// static uint8_t start = 0;
//start = addmod8( start, 1, 4);
uint8_t start = millis();
start = (start >> 5) & 0x03;
for( uint16_t i = start; i < len; i += 4) {
segleds[i] = color;
if( (i + 1) < len) {
segleds[i+1] = color;
}
}
}
/////////////////// End of Marquee animation effects //////////////////
@TechAlex
Copy link

Mark: Thank you very much for the MarqueeOverlay script. With these LEDs [ http://www.amazon.com/Diffused-Digital-String-string-Addressable/dp/B00IYSQV7Q/ref=pd_sim_201_2?ie=UTF8&dpID=61b6YYfj6-L&dpSrc=sims&preST=AC_UL160_SR160%2C160&refRID=1YGZ2NW71HW2HWMRM38Z ]
sold at Amazon, the effects are awesome. The "flickering" part is endless. All of these put together: addressable LEDs, some electronic skills, plus programmers like, Mark, you are able to create some Xmas lighting no found at your local Target or Walmart store. A really "awe" for the neighborhood!
Teensy 3.2 together with their OptoWS2812 adapter is the perfect setup. Happy Holidays!

@rogerguess
Copy link

Mark, love this! How could I set an brightness across all marque effects? Also, would love to see the effects detect if a pixel is full/nearly white and invert the pattern. Some of the patterns cannot be seen if the segment is all white.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment