Skip to content

Instantly share code, notes, and snippets.

@margyle
Forked from kriegsman/DiscoStrobe.ino
Last active August 29, 2015 14:24
Show Gist options
  • Save margyle/f7375ac6809e94c0cb72 to your computer and use it in GitHub Desktop.
Save margyle/f7375ac6809e94c0cb72 to your computer and use it in GitHub Desktop.

Revisions

  1. @kriegsman kriegsman created this gist Jul 8, 2015.
    225 changes: 225 additions & 0 deletions DiscoStrobe.ino
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,225 @@
    #include "FastLED.h"

    // DiscoStrobe
    // *Flashing* rainbow lights that zoom back and forth to a beat.
    // See your doctor before using this code if you have certain neurological conditions.
    //
    // Mark Kriegsman, July 2015


    #if FASTLED_VERSION < 3001000
    #error "Requires FastLED 3.1 or later; check github for latest code."
    #endif

    #define DATA_PIN 3
    //#define CLK_PIN 4
    #define LED_TYPE WS2811
    #define COLOR_ORDER GRB
    #define NUM_LEDS 200
    CRGB leds[NUM_LEDS];


    #define BRIGHTNESS 255
    #define FRAMES_PER_SECOND 100

    #define ZOOMING_BEATS_PER_MINUTE 122

    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).setDither(BRIGHTNESS < 255);
    //FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip).setDither(BRIGHTNESS < 255);

    // set master brightness control
    FastLED.setBrightness(BRIGHTNESS);
    }


    void loop()
    {
    // draw the light pattern into the 'leds' array
    discostrobe();

    // send the 'leds' array out to the actual LED strip
    FastLED.show();

    // delay just enough to keep a steady frame rate, e.g 100 FPS
    delayToSyncFrameRate( FRAMES_PER_SECOND);
    }


    void discostrobe()
    {
    // First, we black out all the LEDs
    fill_solid( leds, NUM_LEDS, CRGB::Black);

    // To achive the strobe effect, we actually only draw lit pixels
    // every Nth frame (e.g. every 4th frame).
    // sStrobePhase is a counter that runs from zero to kStrobeCycleLength-1,
    // and then resets to zero.
    const uint8_t kStrobeCycleLength = 4; // light every Nth frame
    static uint8_t sStrobePhase = 0;
    sStrobePhase = sStrobePhase + 1;
    if( sStrobePhase >= kStrobeCycleLength ) {
    sStrobePhase = 0;
    }

    // We only draw lit pixels when we're in strobe phase zero;
    // in all the other phases we leave the LEDs all black.
    if( sStrobePhase == 0 ) {

    // The dash spacing cycles from 4 to 9 and back, 8x/min (about every 7.5 sec)
    uint8_t dashperiod= beatsin8( 8/*cycles per minute*/, 4,10);
    // The width of the dashes is a fraction of the dashperiod, with a minimum of one pixel
    uint8_t dashwidth = (dashperiod / 4) + 1;

    // The distance that the dashes move each cycles varies
    // between 1 pixel/cycle and half-the-dashperiod/cycle.
    // At the maximum speed, it's impossible to visually distinguish
    // whether the dashes are moving left or right, and the code takes
    // advantage of that moment to reverse the direction of the dashes.
    // So it looks like they're speeding up faster and faster to the
    // right, and then they start slowing down, but as they do it becomes
    // visible that they're no longer moving right; they've been
    // moving left. Easier to see than t o explain.
    //
    // The dashes zoom back and forth at a speed that 'goes well' with
    // most dance music, a little faster than 120 Beats Per Minute. You
    // can adjust this for faster or slower 'zooming' back and forth.
    uint8_t zoomBPM = ZOOMING_BEATS_PER_MINUTE;
    int8_t dashmotionspeed = beatsin8( (zoomBPM /2), 1,dashperiod);
    // This is where we reverse the direction under cover of high speed
    // visual aliasing.
    if( dashmotionspeed >= (dashperiod/2)) {
    dashmotionspeed = 0 - (dashperiod - dashmotionspeed );
    }


    // The hueShift controls how much the hue of each dash varies from
    // the adjacent dash. If hueShift is zero, all the dashes are the
    // same color. If hueShift is 128, alterating dashes will be two
    // different colors. And if hueShift is range of 10..40, the
    // dashes will make rainbows.
    // Initially, I just had hueShift cycle from 0..130 using beatsin8.
    // It looked great with very low values, and with high values, but
    // a bit 'busy' in the middle, which I didnt like.
    // uint8_t hueShift = beatsin8(2,0,130);
    //
    // So instead I layered in a bunch of 'cubic easings'
    // (see http://easings.net/#easeInOutCubic )
    // so that the resultant wave cycle spends a great deal of time
    // "at the bottom" (solid color dashes), and at the top ("two
    // color stripes"), and makes quick transitions between them.
    uint8_t cycle = beat8(2); // two cycles per minute
    uint8_t easedcycle = ease8InOutCubic( ease8InOutCubic( cycle));
    uint8_t wavecycle = cubicwave8( easedcycle);
    uint8_t hueShift = scale8( wavecycle,130);


    // Each frame of the animation can be repeated multiple times.
    // This slows down the apparent motion, and gives a more static
    // strobe effect. After experimentation, I set the default to 1.
    uint8_t strobesPerPosition = 1; // try 1..4


    // Now that all the parameters for this frame are calculated,
    // we call the 'worker' function that does the next part of the work.
    discoWorker( dashperiod, dashwidth, dashmotionspeed, strobesPerPosition, hueShift);
    }
    }


    // discoWorker updates the positions of the dashes, and calls the draw function
    //
    void discoWorker(
    uint8_t dashperiod, uint8_t dashwidth, int8_t dashmotionspeed,
    uint8_t stroberepeats,
    uint8_t huedelta)
    {
    static uint8_t sRepeatCounter = 0;
    static int8_t sStartPosition = 0;
    static uint8_t sStartHue = 0;

    // Always keep the hue shifting a little
    sStartHue += 1;

    // Increment the strobe repeat counter, and
    // move the dash starting position when needed.
    sRepeatCounter = sRepeatCounter + 1;
    if( sRepeatCounter>= stroberepeats) {
    sRepeatCounter = 0;

    sStartPosition = sStartPosition + dashmotionspeed;

    // These adjustments take care of making sure that the
    // starting hue is adjusted to keep the apparent color of
    // each dash the same, even when the state position wraps around.
    if( sStartPosition >= dashperiod ) {
    while( sStartPosition >= dashperiod) { sStartPosition -= dashperiod; }
    sStartHue -= huedelta;
    } else if( sStartPosition < 0) {
    while( sStartPosition < 0) { sStartPosition += dashperiod; }
    sStartHue += huedelta;
    }
    }

    // draw dashes with full brightness (value), and somewhat
    // desaturated (whitened) so that the LEDs actually throw more light.
    const uint8_t kSaturation = 208;
    const uint8_t kValue = 255;

    // call the function that actually just draws the dashes now
    drawRainbowDashes( sStartPosition, NUM_LEDS-1,
    dashperiod, dashwidth,
    sStartHue, huedelta,
    kSaturation, kValue);
    }


    // drawRainbowDashes - draw rainbow-colored 'dashes' of light along the led strip:
    // starting from 'startpos', up to and including 'lastpos'
    // with a given 'period' and 'width'
    // starting from a given hue, which changes for each successive dash by a 'huedelta'
    // at a given saturation and value.
    //
    // period = 5, width = 2 would be _ _ _ X X _ _ _ Y Y _ _ _ Z Z _ _ _ A A _ _ _
    // \-------/ \-/
    // period 5 width 2
    //
    static void drawRainbowDashes(
    uint8_t startpos, uint16_t lastpos, uint8_t period, uint8_t width,
    uint8_t huestart, uint8_t huedelta, uint8_t saturation, uint8_t value)
    {
    uint8_t hue = huestart;
    for( uint16_t i = startpos; i <= lastpos; i += period) {
    CRGB color = CHSV( hue, saturation, value);

    // draw one dash
    uint16_t pos = i;
    for( uint8_t w = 0; w < width; w++) {
    leds[ pos ] = color;
    pos++;
    if( pos >= NUM_LEDS) {
    break;
    }
    }

    hue += huedelta;
    }
    }


    // delayToSyncFrameRate - delay how many milliseconds are needed
    // to maintain a stable frame rate.
    static void delayToSyncFrameRate( uint8_t framesPerSecond)
    {
    static uint32_t msprev = 0;
    uint32_t mscur = millis();
    uint16_t msdelta = mscur - msprev;
    uint16_t mstargetdelta = 1000 / framesPerSecond;
    if( msdelta < mstargetdelta) {
    delay( mstargetdelta - msdelta);
    }
    msprev = mscur;
    }