Full Code of davepl/DavesGarageLEDSeries for AI

master e725a11c81bd cached
94 files
146.1 KB
42.8k tokens
103 symbols
1 requests
Download .txt
Repository: davepl/DavesGarageLEDSeries
Branch: master
Commit: e725a11c81bd
Files: 94
Total size: 146.1 KB

Directory structure:
gitextract_89p4x3xh/

├── .gitignore
├── Fans/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   ├── bounce.h
│   │   ├── comet.h
│   │   ├── fire.h
│   │   ├── ledgfx.h
│   │   ├── marquee.h
│   │   └── twinkle.h
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       └── main.cpp
├── LED Episode 02/
│   ├── .gitignore
│   ├── .travis.yml
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   ├── src/
│   │   └── main.cpp
│   └── test/
│       └── README
├── LED Episode 03/
│   ├── .gitignore
│   ├── .travis.yml
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   ├── src/
│   │   ├── main.cpp
│   │   └── v1.cpp
│   └── test/
│       └── README
├── LED Episode 06/
│   ├── .gitignore
│   ├── .travis.yml
│   ├── .vscode/
│   │   ├── extensions.json
│   │   ├── settings.json
│   │   └── tasks.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       ├── bounce.h
│       ├── comet.h
│       ├── main.cpp
│       ├── marquee.h
│       └── twinkle.h
├── LED Episode 07/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       ├── bounce.h
│       ├── comet.h
│       ├── main.cpp
│       ├── marquee.h
│       └── twinkle.h
├── LED Episode 08/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       ├── bounce.h
│       ├── comet.h
│       ├── main.cpp
│       ├── marquee.h
│       └── twinkle.h
├── LED Episode 09/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       ├── bounce.h
│       ├── comet.h
│       ├── main.cpp
│       ├── marquee.h
│       └── twinkle.h
├── LED Episode 10/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   ├── bounce.h
│   │   ├── comet.h
│   │   ├── fire.h
│   │   ├── ledgfx.h
│   │   ├── marquee.h
│   │   └── twinkle.h
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       └── main.cpp
└── LED Episode 11/
    ├── .gitignore
    ├── .vscode/
    │   └── extensions.json
    ├── include/
    │   ├── bounce.h
    │   ├── comet.h
    │   ├── fire.h
    │   ├── ledgfx.h
    │   ├── marquee.h
    │   └── twinkle.h
    ├── lib/
    │   └── README
    ├── platformio.ini
    └── src/
        └── main.cpp

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================



================================================
FILE: Fans/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: Fans/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: Fans/include/bounce.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        bounce.h     
//
// Description:
//
//      Bouncing Ball effect on an LED strip
//
// History:     Oct-04-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

using namespace std;
#include <vector>

#include "ledgfx.h"

static const CRGB ballColors [] =
{
    CRGB::Green,
    CRGB::Red,
    CRGB::Blue,
    CRGB::Orange,
    CRGB::Indigo
};

class BouncingBallEffect
{
  private:

    double InitialBallSpeed(double height) const
    {
        return sqrt(-2 * Gravity * height);         // Because MATH!
    }

    size_t  _cLength;           
    size_t  _cBalls;
    byte    _fadeRate;
    bool    _bMirrored;

    const double Gravity = -9.81;                   // Because PHYSICS!
    const double StartHeight = 1;                   // Drop balls from max height initially
    const double ImpactVelocity = InitialBallSpeed(StartHeight);
    const double SpeedKnob = 4.0;                   // Higher values will slow the effect

    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;
    vector<CRGB>   Colors;

  public:

    // BouncingBallEffect
    //
    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the
    // balls should be drawn mirrored from each side.

    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)
        : _cLength(cLength - 1),
          _cBalls(ballCount),
          _fadeRate(fade),
          _bMirrored(bMirrored),
          ClockTimeAtLastBounce(ballCount),
          Height(ballCount),
          BallSpeed(ballCount),
          Dampening(ballCount),
          Colors(ballCount)
    {
        for (size_t i = 0; i < ballCount; i++)
        {
            Height[i]                = StartHeight;                 // Current Ball Height
            ClockTimeAtLastBounce[i] = UnixTime();                  // When ball last hit ground state
            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball
            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch
            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];
        }
    }

    // Draw
    //
    // Draw each of the balls.  When any ball settles with too little energy, it it "kicked" to restart it

    virtual void Draw()
    {
        if (_fadeRate != 0)
        {
            for (size_t i = 0; i < _cLength; i++)
                FastLED.leds()[i].fadeToBlackBy(_fadeRate);
        }
        else
            FastLED.clear();
        
        // Draw each of the balls

        for (size_t i = 0; i < _cBalls; i++)
        {
            double TimeSinceLastBounce = (UnixTime() - ClockTimeAtLastBounce[i]) / SpeedKnob;

            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration
            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;

            // Ball hits ground - bounce!
            if (Height[i] < 0)
            {
                Height[i] = 0;
                BallSpeed[i] = Dampening[i] * BallSpeed[i];
                ClockTimeAtLastBounce[i] = Time();

                if (BallSpeed[i] < 0.01)
                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];
            }

            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);

            FastLED.leds()[position]   += Colors[i];
            FastLED.leds()[position+1] += Colors[i];

            if (_bMirrored)
            {
                FastLED.leds()[_cLength - 1 - position] += Colors[i];
                FastLED.leds()[_cLength - position]     += Colors[i];
            }
        }
        delay(20);
    }
};

================================================
FILE: Fans/include/comet.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:                  
//
// Description:
//
//   
//
// History:     Sep-28-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

void DrawComet()
{
    const byte fadeAmt = 128;
    const int cometSize = 5;
    const int deltaHue  = 4;

    static byte hue = HUE_RED;
    static int iDirection = 1;
    static int iPos = 0;

    hue += deltaHue;

    iPos += iDirection;
    if (iPos == (FastLED.count() - cometSize) || iPos == 0)
        iDirection *= -1;
    
    for (int i = 0; i < cometSize; i++)
        FastLED.leds()[iPos + i].setHue(hue);
    
    // Randomly fade the LEDs
    for (int j = 0; j < FastLED.count(); j++)
        if (random(10) > 5)
            FastLED.leds()[j] = FastLED.leds()[j].fadeToBlackBy(fadeAmt);  

    delay(30);
}

================================================
FILE: Fans/include/fire.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        fire.h                    
//
// Description: A realistic flame simulation for LED strips
//
// History:     Oct-23-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include "ledgfx.h"

class FireEffect
{
  protected:
    int     Size;               // How many pixels the flame is total
    int     Cooling;            // Rate at which the pixels cool off
    int     Sparks;             // How many sparks will be attempted each frame
    int     SparkHeight;        // If created, max height for a spark
    int     Sparking;           // Probability of a spark each attempt
    bool    bReversed;          // If reversed we draw from 0 outwards
    bool    bMirrored;          // If mirrored we split and duplicate the drawing

    byte  * heat;

    // When diffusing the fire upwards, these control how much to blend in from the cells below (ie: downward neighbors)
    // You can tune these coefficients to control how quickly and smoothly the fire spreads

    static const byte BlendSelf = 2;
    static const byte BlendNeighbor1 = 3;
    static const byte BlendNeighbor2 = 2;
    static const byte BlendNeighbor3 = 1;
    static const byte BlendTotal = (BlendSelf + BlendNeighbor1 + BlendNeighbor2 + BlendNeighbor3);

  public:

    FireEffect(int size, int cooling = 20, int sparking = 100, int sparks = 3, int sparkHeight = 4, bool breversed = true, bool bmirrored = true)
        : Size(size),
          Cooling(cooling),
          Sparks(sparks),
          SparkHeight(sparkHeight),
          Sparking(sparking),
          bReversed(breversed),
          bMirrored(bmirrored)
    {
        if (bMirrored)
            Size = Size / 2;

        heat = new byte[size] { 0 };
    }

    virtual ~FireEffect()
    {
        delete [] heat;
    }

    virtual void DrawFire(PixelOrder order = Sequential)
    {
        // First cool each cell by a litle bit
        for (int i = 0; i < Size; i++)
            heat[i] = max(0L, heat[i] - random(0, ((Cooling * 10) / Size) + 2));

        // Next drift heat up and diffuse it a little bit
        for (int i = 0; i < Size; i++)
            heat[i] = (heat[i] * BlendSelf +
                       heat[(i + 1) % Size] * BlendNeighbor1 +
                       heat[(i + 2) % Size] * BlendNeighbor2 +
                       heat[(i + 3) % Size] * BlendNeighbor3)
                      / BlendTotal;

        // Randomly ignite new sparks down in the flame kernel

        for (int i = 0 ; i < Sparks; i++)
        {
            if (random(255) < Sparking)
            {
                int y = Size - 1 - random(SparkHeight);
                heat[y] = heat[y] + random(160, 255);       // Can roll over which actually looks good!
            }
        }

        // Finally, convert heat to a color

        for (int i = 0; i < Size; i++)
        {
            CRGB color = HeatColor(heat[i]);
            int j = bReversed ? (Size - 1 - i) : i;
            DrawFanPixels(j, 1, color, order);
            if (bMirrored)
                DrawFanPixels(!bReversed ? (2 * Size - 1 - i) : Size + i, 1, color, order);
        }
    }
};

================================================
FILE: Fans/include/ledgfx.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        ledgfx.h
//
// Description:
//
//   LED Drawing Routines for Dave's Garage Tutorial series
//
// History:     OCt-18-2020     davepl      Created from main.cpp code
//---------------------------------------------------------------------------

#pragma once

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include <sys/time.h>                   // For time-of-day

// Utility Macros

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)

//inline double RandomDouble()
//{
//    return random(UINT32_MAX) / (double) UINT32_MAX;
//}

inline float RandomFloat()
{
    float r = random(1000000L) / 1000000.0f;
    return r;
}

inline double UnixTime()
{
    timeval tv = { 0 };
    gettimeofday(&tv, nullptr);
    return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);
}

// FractionalColor
//
// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we
// want to improve the color math or do color correction all in one location at a later date.

CRGB ColorFraction(CRGB colorIn, float fraction)
{
  fraction = min(1.0f, fraction);
  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));
}

void DrawPixels(float fPos, float count, CRGB color)
{
  // Calculate how much the first pixel will hold
  float availFirstPixel = 1.0f - (fPos - (long)(fPos));
  float amtFirstPixel = min(availFirstPixel, count);
  float remaining = min(count, FastLED.size()-fPos);
  int iPos = fPos;

  // Blend (add) in the color of the first partial pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);
    remaining -= amtFirstPixel;
  }

  // Now draw any full pixels in the middle

  while (remaining > 1.0f)
  {
    FastLED.leds()[iPos++] += color;
    remaining--;
  }

  // Draw tail pixel, up to a single full pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos] += ColorFraction(color, remaining);
  }
}

DEFINE_GRADIENT_PALETTE( vu_gpGreen ) 
{
      0,     0,   4,   0,   // near black green
     64,     0, 255,   0,   // green
    128,   255, 255,   0,   // yellow
    192,   255,   0,   0,   // red
    255,   255,   0,   0    // red
};

DEFINE_GRADIENT_PALETTE( vu_gpSeahawks ) 
{
    0,       0,     0,   4,      
    64,      3,    38,  58,      
   128,      0,    21,  50,      
   192,     78,   167,   1,      
   255,     54,    87, 140,      
};

static const int FanPixelsVertical[FAN_SIZE] =
{
  0, 1, 15, 2, 14, 3, 13, 4, 12, 5, 11, 6, 10, 7, 9, 8
};

static const int FanPixelsHorizontal[FAN_SIZE] =
{
  3, 4, 2, 5, 1, 6, 0, 7, 15, 8, 14, 9, 13, 10, 12, 11
};

enum PixelOrder
{
  Sequential = 0,
  Reverse    = 1,
  BottomUp   = 2,
  TopDown    = 4,
  LeftRight  = 8,
  RightLeft  = 16
};

int GetFanPixelOrder(int iPos, PixelOrder order = Sequential)
{
  while (iPos < 0)
    iPos += FAN_SIZE;

  int offset = (iPos + LED_FAN_OFFSET) % FAN_SIZE;  // Offset within this fan
  int roffset = (iPos + FAN_SIZE - LED_FAN_OFFSET) % FAN_SIZE;  // Offset within this fan

  int fanBase = iPos - (iPos % FAN_SIZE);           // Round down to previous multiple of FAN_SIZE
  
  switch (order)
  {
    case BottomUp:
      return fanBase + FAN_SIZE - 1 - (FanPixelsVertical[iPos % FAN_SIZE] + LED_FAN_OFFSET) % FAN_SIZE;
    
    case TopDown:
      return NUM_LEDS - 1 - (fanBase + (FanPixelsVertical[FAN_SIZE - 1 - (iPos % FAN_SIZE)] + LED_FAN_OFFSET) % FAN_SIZE);

    case LeftRight:
      return fanBase + (FanPixelsHorizontal[ iPos % FAN_SIZE ] + LED_FAN_OFFSET - 1) % FAN_SIZE;
    
    case RightLeft:
      return fanBase + (FanPixelsHorizontal[FAN_SIZE - 1 - (iPos % FAN_SIZE) ] + LED_FAN_OFFSET - 1) % FAN_SIZE;

   case Reverse:
      return fanBase + FAN_SIZE - 1 - roffset;

    case Sequential:
    default:
      return fanBase + offset;
  }
}

void DrawFanPixels(float fPos, float count, CRGB color, PixelOrder order = Sequential, int iFan = 0)
{
  fPos += iFan * FAN_SIZE;

  // Calculate how much the first pixel will hold
  float availFirstPixel = 1.0f - (fPos - (long)(fPos));
  float amtFirstPixel = min(availFirstPixel, count);
  float remaining = min(count, FastLED.size()-fPos);
  int iPos = fPos;

  // Blend (add) in the color of the first partial pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[GetFanPixelOrder(iPos++, order)] += ColorFraction(color, amtFirstPixel);
    remaining -= amtFirstPixel;
  }

  // Now draw any full pixels in the middle

  while (remaining > 1.0f)
  {
    FastLED.leds()[GetFanPixelOrder(iPos++, order)] += color;
    remaining--;
  }

  // Draw tail pixel, up to a single full pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[GetFanPixelOrder(iPos, order)] += ColorFraction(color, remaining);
  }
}


================================================
FILE: Fans/include/marquee.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        marque.h
//
// Description:
//
//   Draws a theatre-style marquee
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>


void DrawMarquee()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < FastLED.count(); i ++)
        FastLED.leds()[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < FastLED.count() - 1; i += 5)
    {
        FastLED.leds()[i] = CRGB::Black;
    }
    delay(50);
}

void DrawMarqueeMirrored()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < (FastLED.count() + 1) / 2; i ++)
    {
        FastLED.leds()[i] = c.setHue(k);
        FastLED.leds()[FastLED.count() - 1 - i] = c.setHue(k);
        k+= 8;
    }


    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < FastLED.count() / 2; i += 5)
    {
        FastLED.leds()[i] = CRGB::Black;
        FastLED.leds()[FastLED.count() - 1 - i] = CRGB::Black;
    }   

    delay(50);
}





================================================
FILE: Fans/include/twinkle.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        
//
// Description:
//
//   
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include "ledgfx.h"

static const CRGB TwinkleColors [] = 
{
    CRGB::Red,
    CRGB::Blue,
    CRGB::Purple,
    CRGB::Green,
    CRGB::Yellow
};

void DrawTwinkle()
{
    static int passCount = 0;
    if (passCount++ == FastLED.count()/4)
    {
        passCount = 0;
        FastLED.clear(false);
    }
    FastLED.leds()[random(FastLED.count())] = TwinkleColors[random(0, ARRAYSIZE(TwinkleColors))];
    delay(200);       
}

================================================
FILE: Fans/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: Fans/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
build_flags = -Wno-unused-variable
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
monitor_speed = 115200

lib_deps = U8g2
           FastLED
           
        
           





================================================
FILE: Fans/src/main.cpp
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        LED Episode 10
//
// Description:
//
//   Draws sample effects on a an addressable strip using FastLED
//
// History:     Sep-15-2020     davepl      Created
//              Oct-05-2020     davepl      Revised for Episode 07
//              Oct-11-2020     davepl      Revised for Episode 08
//              Oct-16-2020     davepl      Revised for Episode 09
//              Oct-23-2020     davepl      Revised for Episode 10
//---------------------------------------------------------------------------

#include <Arduino.h>            // Arduino Framework
#include <U8g2lib.h>            // For text on the little on-chip OLED
#define FASTLED_INTERNAL        // Suppress build banner
#include <FastLED.h>

#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16

#define FAN_SIZE       16       // How many pixels per fan
#define NUM_FANS       3        // Number of fans in the strans
#define LED_FAN_OFFSET 4        // How far from 12 o'clock first pixel is
#define NUM_LEDS       (FAN_SIZE*NUM_FANS)
#define LED_PIN        5

CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED

U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 255;         // 0-255 LED brightness scale
int g_PowerLimit = 3000;         // 900mW Power Limit

#include "ledgfx.h"
#include "comet.h"
#include "marquee.h"
#include "twinkle.h"
#include "fire.h"


void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");

  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total

  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(g_Brightness);
  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle
  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled
}

void loop() 
{
  bool bLED = 0;

  while (true)
  {
    FastLED.clear();
   
    /*
    // RGB Spinners
    float b = beat16(60) / 65535.0f * FAN_SIZE;
    DrawFanPixels(b, 1, CRGB::Red, Sequential, 0);
    DrawFanPixels(b, 1, CRGB::Green, Sequential, 1);
    DrawFanPixels(b, 1, CRGB::Blue, Sequential, 2);
    */

    /*
    // Left to Right Cyan Wipe
    float b = beatsin16(60) / 65535.0f * FAN_SIZE;
    for (int iFan = 0; iFan < NUM_FANS; iFan++)
        DrawFanPixels(0, b, CRGB::Cyan, LeftRight, iFan);
    */

    /*
    // Left to Right Cyan Wipe
    float b = beatsin16(60) / 65535.0f * FAN_SIZE;
    for (int iFan = 0; iFan < NUM_FANS; iFan++)
        DrawFanPixels(0, b, CRGB::Cyan, RightLeft, iFan);
    */

    /*
    // Bottom up Green Wipe
    float b = beatsin16(60) / 65535.0f * NUM_LEDS;
        DrawFanPixels(0, b, CRGB::Green, BottomUp);
    */
   
    /*
    // Bottom up Green Wipe
    float b = beatsin16(60) / 65535.0f * NUM_LEDS;
        DrawFanPixels(0, b, CRGB::Green, TopDown);
    */

    /*
    // Simple Color Cycle
    static byte hue = 0;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, CHSV(hue, 255, 255));
    hue += 4;
    */

    /*
    // Sequential Rainbows
    static byte basehue = 0;
    byte hue = basehue;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255));
    basehue += 4;
    */

    /*
    // Vertical Rainbow Wipe
    static byte basehue = 0;
    byte hue = basehue;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, CHSV(hue+=8, 255, 255), BottomUp);
    basehue += 4;
    */

    /*
    // Horizontal Rainbow Stripe
    static byte basehue = 0;
    byte hue = basehue;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255), LeftRight);
    basehue += 8;
    */

    /*
    // Rainbow Stripe Palette Effect
    static CRGBPalette256 pal(RainbowStripeColors_p);
    static byte baseColor = 0;
    byte hue = baseColor;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, ColorFromPalette(pal, hue += 4), BottomUp);
    baseColor += 1;
    */

    /*   
    // vu-Style Meter
    int b = beatsin16(30) * NUM_LEDS / 65535L;
    static const CRGBPalette256 vuPaletteGreen = vu_gpGreen;
    for (int i = 0; i < b; i++)
        DrawFanPixels(i, 1, ColorFromPalette(vuPaletteGreen, (int)(255 * i / NUM_LEDS)), BottomUp);
    */

    /*
    // Sequential Fire Fans
    static FireEffect fire(NUM_LEDS, 20, 100, 3, NUM_LEDS, true, false);
    fire.DrawFire();
    */

    /*
    // Bottom Up Fire Effect with extra sparking on first fan only
    static FireEffect fire(NUM_LEDS, 20, 140, 3, FAN_SIZE, true, false);
    fire.DrawFire(BottomUp);
    */

    /*
    // LeftRight (Wide) Fire Effect with extra sparking on first fan only
    static FireEffect fire(NUM_LEDS, 20, 140, 3, FAN_SIZE, true, false);
    fire.DrawFire(LeftRight);
    for (int i = 0; i < FAN_SIZE; i++)  // Copy end fan down onto others
    {
      g_LEDs[i] = g_LEDs[i + 2 * FAN_SIZE];             
      g_LEDs[i + FAN_SIZE] = g_LEDs[i + 2 * FAN_SIZE];
    }
    */

    int b = beatsin16(30) * NUM_LEDS / 65535L;
    static const CRGBPalette256 seawhawksPalette = vu_gpSeahawks;
    for (int i = 0; i < NUM_LEDS; i++)
        DrawFanPixels(i, 1, ColorFromPalette(seawhawksPalette, beat8(64) + (int)(255 * i / NUM_LEDS)), BottomUp);
    
    
    FastLED.show(g_Brightness);                          //  Show and delay

    EVERY_N_MILLISECONDS(250)
    {
      g_OLED.clearBuffer();
      g_OLED.setCursor(0, g_lineHeight);
      g_OLED.printf("FPS  : %u", FastLED.getFPS());
      g_OLED.setCursor(0, g_lineHeight * 2);
      g_OLED.printf("Power: %u mW", calculate_unscaled_power_mW(g_LEDs, 4));
      g_OLED.setCursor(0, g_lineHeight * 3);
      g_OLED.printf("Brite: %d", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));
      g_OLED.sendBuffer();
    }
    delay(33);
  }
}


================================================
FILE: LED Episode 02/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: LED Episode 02/.travis.yml
================================================
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < https://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
#   < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
#   < https://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
#   < https://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choose one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#


#
# Template #1: General project. Test it using existing `platformio.ini`.
#

# language: python
# python:
#     - "2.7"
#
# sudo: false
# cache:
#     directories:
#         - "~/.platformio"
#
# install:
#     - pip install -U platformio
#     - platformio update
#
# script:
#     - platformio run


#
# Template #2: The project is intended to be used as a library with examples.
#

# language: python
# python:
#     - "2.7"
#
# sudo: false
# cache:
#     directories:
#         - "~/.platformio"
#
# env:
#     - PLATFORMIO_CI_SRC=path/to/test/file.c
#     - PLATFORMIO_CI_SRC=examples/file.ino
#     - PLATFORMIO_CI_SRC=path/to/test/directory
#
# install:
#     - pip install -U platformio
#     - platformio update
#
# script:
#     - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N


================================================
FILE: LED Episode 02/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LED Episode 02/include/README
================================================

This directory is intended for project header files.

A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.

```src/main.c

#include "header.h"

int main (void)
{
 ...
}
```

Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.

In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.

Read more about using header files in official GCC documentation:

* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes

https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html


================================================
FILE: LED Episode 02/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: LED Episode 02/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino


================================================
FILE: LED Episode 02/src/main.cpp
================================================
#include <Arduino.h>

#define RED_PIN   16
#define GREEN_PIN 17
#define BLUE_PIN  18

// Courtesy http://www.instructables.com/id/How-to-Use-an-RGB-LED/?ALLSTEPS
// function to convert a color to its Red, Green, and Blue components.

uint32_t R, G, B;           // the Red Green and Blue color components

void hueToRGB(uint8_t hue, uint8_t brightness)
{
    uint16_t scaledHue = (hue * 6);
    uint8_t segment = scaledHue / 256; // segment 0 to 5 around the
                                            // color wheel
    uint16_t segmentOffset =
      scaledHue - (segment * 256); // position within the segment

    uint8_t complement = 0;
    uint16_t prev = (brightness * ( 255 -  segmentOffset)) / 256;
    uint16_t next = (brightness *  segmentOffset) / 256;

    switch(segment ) {
    case 0:      // red
        R = brightness;
        G = next;
        B = complement;
    break;
    case 1:     // yellow
        R = prev;
        G = brightness;
        B = complement;
    break;
    case 2:     // green
        R = complement;
        G = brightness;
        B = next;
    break;
    case 3:    // cyan
        R = complement;
        G = prev;
        B = brightness;
    break;
    case 4:    // blue
        R = next;
        G = complement;
        B = brightness;
    break;
   case 5:      // magenta
    default:
        R = brightness;
        G = complement;
        B = prev;
    break;
    }
}

void setup() 
{
  pinMode(RED_PIN,   OUTPUT);
  pinMode(GREEN_PIN, OUTPUT);
  pinMode(BLUE_PIN,  OUTPUT);

  digitalWrite(GREEN_PIN, HIGH);
  digitalWrite(BLUE_PIN,  HIGH);  

  ledcAttachPin(RED_PIN, 1);        // Assign PWM generator 1 to RED
  ledcAttachPin(GREEN_PIN, 2);      // Assign PWM generator 2 to GREEN
  ledcAttachPin(BLUE_PIN, 3);       // Assign PWM generator 3 to BLUE

  ledcSetup(1, 12000, 8);           // Set it to 12kHZ and 8-bit resolution
  ledcSetup(2, 12000, 8);           // Set it to 12kHZ and 8-bit resolution
  ledcSetup(3, 12000, 8);           // Set it to 12kHZ and 8-bit resolution
}

void loop() 
{
  for (int c = 0; c < 256; c++)
  {
    hueToRGB(c, 255);             // Convert color to max brightness

    ledcWrite(1, R);
    ledcWrite(2, G);
    ledcWrite(3, B);

    delay(100);
  }
}

================================================
FILE: LED Episode 02/test/README
================================================

This directory is intended for PIO Unit Testing and project tests.

Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.

More information about PIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html


================================================
FILE: LED Episode 03/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: LED Episode 03/.travis.yml
================================================
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < https://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
#   < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
#   < https://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
#   < https://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choose one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#


#
# Template #1: General project. Test it using existing `platformio.ini`.
#

# language: python
# python:
#     - "2.7"
#
# sudo: false
# cache:
#     directories:
#         - "~/.platformio"
#
# install:
#     - pip install -U platformio
#     - platformio update
#
# script:
#     - platformio run


#
# Template #2: The project is intended to be used as a library with examples.
#

# language: python
# python:
#     - "2.7"
#
# sudo: false
# cache:
#     directories:
#         - "~/.platformio"
#
# env:
#     - PLATFORMIO_CI_SRC=path/to/test/file.c
#     - PLATFORMIO_CI_SRC=examples/file.ino
#     - PLATFORMIO_CI_SRC=path/to/test/directory
#
# install:
#     - pip install -U platformio
#     - platformio update
#
# script:
#     - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N


================================================
FILE: LED Episode 03/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LED Episode 03/include/README
================================================

This directory is intended for project header files.

A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.

```src/main.c

#include "header.h"

int main (void)
{
 ...
}
```

Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.

In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.

Read more about using header files in official GCC documentation:

* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes

https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html


================================================
FILE: LED Episode 03/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: LED Episode 03/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
upload_port = /dev/cu.usbserial-4
lib_deps = U8g2 



================================================
FILE: LED Episode 03/src/main.cpp
================================================
#include <Arduino.h>
#include <U8g2lib.h>

U8G2_SSD1306_128X64_NONAME_F_SW_I2C g_TFT(U8G2_R2, 15, 4, 16);

void setup()
{
    pinMode(LED_BUILTIN, OUTPUT);           // Set the LED pin to output
    g_TFT.begin();                          // One-time startup
    g_TFT.clear();                          // Clear the screen
    g_TFT.setFont(u8g2_font_profont15_tf);  // Choose a suitable font
    g_TFT.setCursor(2, 12);                 // Cursor is at bottom of letter, so we need to move down
    //g_TFT.println("Hello World!");          // Print something

    // Draw a  border around the screen

    g_TFT.drawFrame(0, 0, 128, 64);

    // Draw a moire pattern like its 1984
    
    for (int x=0; x<128; x+= 4)
      g_TFT.drawLine(x, 0, 128-x, 64);

    // Draw some text on the left hand side

    g_TFT.setCursor(5, 32);
    g_TFT.print("Hello");
    g_TFT.setCursor(5, 42);
    g_TFT.print("World");

    // Draw a reticle on the right hand side

    const int reticleY = g_TFT.getHeight() / 2;           // Vertical center
    const int reticleR = g_TFT.getHeight() / 4 - 2;       // Slightly less than 1/4 screen height
    const int reticleX = g_TFT.getWidth() - reticleR - 8; // Right-justified with a small margin
    
    for (int r = reticleR; r > 0; r -= 3)
      g_TFT.drawCircle(reticleX, reticleY, r);
    g_TFT.drawHLine(reticleX - reticleR - 5, reticleY, 2 * reticleR + 10);
    g_TFT.drawVLine(reticleX, reticleY - reticleR - 5, 2 * reticleR + 10);

    g_TFT.sendBuffer();                     // Ship-It!

}

void loop() 
{
    digitalWrite(LED_BUILTIN, 0);
    delay(100);
    digitalWrite(LED_BUILTIN, 1);
    delay(100);
}

================================================
FILE: LED Episode 03/src/v1.cpp
================================================
#include <Arduino.h>
#include <U8g2lib.h>

#define DISPLAY_CLOCK_PIN   15
#define DISPLAY_DATA_PIN    4
#define DISPLAY_RESET_PIN   16

U8G2_SSD1306_128X64_NONAME_F_SW_I2C g_OLED(U8G2_R2, DISPLAY_CLOCK_PIN, DISPLAY_DATA_PIN, DISPLAY_RESET_PIN);

void setup() 
{
  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);  // Choose a suitable font
  g_OLED.setCursor(2, 12);                 // Cursor is at bottom of letter, so we need to move down
  g_OLED.print("Hello World");
  g_OLED.sendBuffer();
}

void loop() 
{
    digitalWrite(LED_BUILTIN, 0);
    delay(100);
    digitalWrite(LED_BUILTIN, 1);
    delay(100);
}




================================================
FILE: LED Episode 03/test/README
================================================

This directory is intended for PIO Unit Testing and project tests.

Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.

More information about PIO Unit Testing:
- https://docs.platformio.org/page/plus/unit-testing.html


================================================
FILE: LED Episode 06/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: LED Episode 06/.travis.yml
================================================
# Continuous Integration (CI) is the practice, in software
# engineering, of merging all developer working copies with a shared mainline
# several times a day < https://docs.platformio.org/page/ci/index.html >
#
# Documentation:
#
# * Travis CI Embedded Builds with PlatformIO
#   < https://docs.travis-ci.com/user/integration/platformio/ >
#
# * PlatformIO integration with Travis CI
#   < https://docs.platformio.org/page/ci/travis.html >
#
# * User Guide for `platformio ci` command
#   < https://docs.platformio.org/page/userguide/cmd_ci.html >
#
#
# Please choose one of the following templates (proposed below) and uncomment
# it (remove "# " before each line) or use own configuration according to the
# Travis CI documentation (see above).
#


#
# Template #1: General project. Test it using existing `platformio.ini`.
#

# language: python
# python:
#     - "2.7"
#
# sudo: false
# cache:
#     directories:
#         - "~/.platformio"
#
# install:
#     - pip install -U platformio
#     - platformio update
#
# script:
#     - platformio run


#
# Template #2: The project is intended to be used as a library with examples.
#

# language: python
# python:
#     - "2.7"
#
# sudo: false
# cache:
#     directories:
#         - "~/.platformio"
#
# env:
#     - PLATFORMIO_CI_SRC=path/to/test/file.c
#     - PLATFORMIO_CI_SRC=examples/file.ino
#     - PLATFORMIO_CI_SRC=path/to/test/directory
#
# install:
#     - pip install -U platformio
#     - platformio update
#
# script:
#     - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N


================================================
FILE: LED Episode 06/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LED Episode 06/.vscode/settings.json
================================================
{
    "files.associations": {
        "*.tcc": "cpp"
    }
}

================================================
FILE: LED Episode 06/.vscode/tasks.json
================================================
{
	"version": "2.0.0",
	"tasks": [
		{
			"type": "PlatformIO",
			"task": "Build",
			"problemMatcher": [
				"$platformio"
			],
			"group": "build",
			"label": "PlatformIO: Build"
		}
	]
}

================================================
FILE: LED Episode 06/include/README
================================================

This directory is intended for project header files.

A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.

```src/main.c

#include "header.h"

int main (void)
{
 ...
}
```

Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.

In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.

Read more about using header files in official GCC documentation:

* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes

https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html


================================================
FILE: LED Episode 06/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: LED Episode 06/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
build_flags = -Wno-unused-variable
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
monitor_speed = 115200

lib_deps = U8g2
           FastLED
           
        
           





================================================
FILE: LED Episode 06/src/bounce.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        bounce.h
//
// Description:
//
//   Draws bouncing balls on an LED strip
//
// History:     Ocf-01-2020     davepl      Created
//
//---------------------------------------------------------------------------

//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        
//
// Description:
//
//   
//
// History:     Oct-04-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <sys/time.h>                               // For time-of-day

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

using namespace std;                        
#include <vector>                               // Use the C++ resizable array

extern CRGB g_LEDs[];

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))   // Count elements in a static array

// BouncingBallEffect
//
// Draws a set of N bouncing balls using a simple little kinematics formula.  Clears the section first.

static const CRGB ballColors[] =
{
  CRGB::Green,
  CRGB::Red,
  CRGB::Blue,
  CRGB::Orange,
  CRGB::Indigo,
  CRGB::Cyan
};

class BouncingBallEffect 
{
private:

  double InitialBallSpeed(double height) const
  {
    return sqrt(-2 * Gravity * height);
  }

	size_t  _cLength;
	size_t  _cBalls;
  byte    _Fade;
	bool    _bMirrored;
  double  _SpeedKnob;

	const double Gravity = -9.81;                                       // Because PHYSICS!
	const double StartHeight = 1;                                       // Drop balls from max height to start
	const double ImpactVelocityStart = InitialBallSpeed(StartHeight);   // Speed for  a
	const double SpeedKnob = 4;                                         // High number will slow effect down

	vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening; // When the ball last bounced

  // Time - Return current time in floating form for easier calcs than ms 

  double Time() const
  {
    timeval tv = { 0 };
    gettimeofday(&tv, nullptr);
    return (double)(tv.tv_usec / (double)1000000 +(double)tv.tv_sec);
  }


public:

  // BouncingBallEffect
  //
  // Caller specs strip length, number of balls, persistence level (255 is least), and whether
  // the balls should be drawn mirrored from each side. 

	BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false, double SpeedKnob = 4.0)
		: _cLength(cLength-1),          // Reserve one LED for floating point fraction draw
		  _cBalls(ballCount),
      _Fade(fade),
		  _bMirrored(bMirrored),
      _SpeedKnob(SpeedKnob),
      ClockTimeAtLastBounce(ballCount),
      Height(ballCount),
      BallSpeed(ballCount),
      Dampening(ballCount)
	{

		for (size_t i = 0; i < ballCount; i++)
		{
			Height[i] 					        = StartHeight;                    // Current ball height
			ClockTimeAtLastBounce[i]    = Time();                         // When the last time it hit ground was              
			Dampening[i] 				        = 1.0 - i / pow(_cBalls, 2);     // Each ball bounces differently
			BallSpeed[i] 			          = InitialBallSpeed(Height[i]);    // Don't dampen initial launch to they go together
		}
	}

	// Draw
	//
	// Draw each of the balls.  When any ball gets too little energy it would just sit at the base so it is re-kicked with new energy.#pragma endregion

  virtual void Draw()
  {
    if (_Fade)
    {
      for (size_t i = 0; i < _cLength; i++)
      {
        g_LEDs[i].fadeToBlackBy(_Fade);     
      }
    }
    else
    {
      FastLED.clear();
    }

    // Draw each of the three balls
    for (size_t i = 0; i < _cBalls; i++)
    {     
      double TimeSinceLastBounce = (Time() - ClockTimeAtLastBounce[i]) / _SpeedKnob;
      Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;

      if (Height[i] < 0)
      {
        Height[i] = 0;
        BallSpeed[i] = Dampening[i] * BallSpeed[i];
        ClockTimeAtLastBounce[i] = Time();

        if (BallSpeed[i] < 1.0)
          BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];
      }

      static const CRGB ballColors[] = { CRGB::Red, CRGB::Blue, CRGB::Green, CRGB::Orange, CRGB::Violet };
      CRGB color = ballColors[i % ARRAYSIZE(ballColors)];

      double position = (Height[i] * (_cLength - 1.0) / StartHeight);
      DrawPixels(position, 1, color);
      if (_bMirrored) 
        DrawPixels(_cLength - 1 - position, 1, color);

    }
    delay(20);
  }
};


================================================
FILE: LED Episode 06/src/comet.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:                  
//
// Description:
//
//   
//
// History:     Sep-28-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

extern CRGB g_LEDs[];

void DrawComet()
{
    const byte fadeAmt = 128;
    const int cometSize = 5;
    const int deltaHue  = 4;

    static byte hue = HUE_RED;
    static int iDirection = 1;
    static int iPos = 0;

    hue += deltaHue;

    iPos += iDirection;
    if (iPos == (NUM_LEDS - cometSize) || iPos == 0)
        iDirection *= -1;
    
    for (int i = 0; i < cometSize; i++)
        g_LEDs[iPos + i].setHue(hue);
    
    // Randomly fade the LEDs
    for (int j = 0; j < NUM_LEDS; j++)
        if (random(10) > 5)
            g_LEDs[j] = g_LEDs[j].fadeToBlackBy(fadeAmt);  

    delay(50);
}

================================================
FILE: LED Episode 06/src/main.cpp
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        LED Episode 06
//
// Description:
//
//   Draws sample effects on a an addressable strip using FastLED
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>            // Arduino Framework
#include <U8g2lib.h>            // For text on the little on-chip OLED
#define FASTLED_INTERNAL        // Suppress build banner
#include <FastLED.h>

#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16

#define NUM_LEDS    45          // FastLED definitions
#define LED_PIN     5

CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED

U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 255;           // 0-255 LED brightness scale



// FramesPerSecond
//
// Tracks a weighted average to smooth out the values that it calcs as the simple reciprocal
// of the amount of time taken specified by the caller.  So 1/3 of a second is 3 fps, and it
// will take up to 10 frames or so to stabilize on that value.

double FramesPerSecond(double seconds)
{
  static double framesPerSecond; 
  framesPerSecond = (framesPerSecond * .9) + (1.0 / seconds * .1);
  return framesPerSecond;
}

void DrawPixels(double fPos, double count, CRGB color)
{
    double availFirstPixel = 1.0 - (fPos - (long)(fPos));
    double amtFirstPixel = min(availFirstPixel, count);
    count = min(count, FastLED.size()-fPos);
    if (fPos >= 0 && fPos < FastLED.size())
    {
        CRGB frontColor = color;
        frontColor.fadeToBlackBy(255 * (1.0 - amtFirstPixel));
        FastLED.leds()[(uint)fPos] += frontColor;
    }

    fPos += amtFirstPixel;
    count -= amtFirstPixel;

    while (count >= 1.0)
    {
        if (fPos >= 0 && fPos < FastLED.size())
        {
            FastLED.leds()[(uint)fPos] += color;
            count -= 1.0;
        }
        fPos += 1.0;
    }

    if (count > 0.0)
    {
        if (fPos >= 0 && fPos < FastLED.size())
        {
            CRGB backColor = color;
            backColor.fadeToBlackBy(255 * (1.0 - count));
            FastLED.leds()[(uint)fPos] += backColor;
        }
    }
}

void set_max_power_indicator_LED(uint8_t);

#include "twinkle.h"
#include "marquee.h"
#include "comet.h"
#include "bounce.h"

void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");

  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total

  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(g_Brightness);
  FastLED.setMaxPowerInMilliWatts(900);
  set_max_power_indicator_LED(LED_BUILTIN);
}

void loop() 
{
  double fps = 0;

  BouncingBallEffect balls(NUM_LEDS, 3, 0, false, 8.0);

  while (true)
  {
    double dStart = millis() / 1000.0;                 // Display a frame and calc how long it takes

    // Handle LEDs

    balls.Draw();

    // Handle OLED drawing

    uint32_t milliwatts = calculate_unscaled_power_mW(g_LEDs, NUM_LEDS);

    static unsigned long msLastUpdate = millis();
    if (millis() - msLastUpdate > 500)
    {
      g_OLED.clearBuffer();
      g_OLED.setCursor(0, g_lineHeight);
      g_OLED.printf("FPS: %.1lf", fps);
      g_OLED.setCursor(1, g_lineHeight * 2);
      g_OLED.printf("Power: %u mW", milliwatts);
      g_OLED.sendBuffer();
      msLastUpdate = millis();
    }

    FastLED.show(g_Brightness);

    double dEnd = millis() / 1000.0;
    fps = FramesPerSecond(dEnd - dStart);
  }
}


================================================
FILE: LED Episode 06/src/marquee.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        marque.h
//
// Description:
//
//   Draws a theatre-style marquee
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>


void DrawMarquee()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < NUM_LEDS; i ++)
        g_LEDs[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < NUM_LEDS - 1; i += 5)
    {
        g_LEDs[i] = CRGB::Black;
    }
    delay(50);
}

void DrawMarqueeMirrored()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < (NUM_LEDS + 1) / 2; i ++)
    {
        g_LEDs[i] = c.setHue(k);
        g_LEDs[NUM_LEDS - 1 - i] = c.setHue(k);
        k+= 8;
    }


    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < NUM_LEDS / 2; i += 5)
    {
        g_LEDs[i] = CRGB::Black;
        g_LEDs[NUM_LEDS - 1 - i] = CRGB::Black;
    }   

    delay(50);
}





================================================
FILE: LED Episode 06/src/twinkle.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        
//
// Description:
//
//   
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#define NUM_COLORS 5
static const CRGB TwinkleColors [NUM_COLORS] = 
{
    CRGB::Red,
    CRGB::Blue,
    CRGB::Purple,
    CRGB::Green,
    CRGB::Yellow
};

void DrawTwinkle()
{
    FastLED.clear(false);

    for (int i=0; i<NUM_LEDS/4; i++) 
    {
        g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];
        FastLED.show(g_Brightness);
        delay(200);
    }
}

void DrawTwinkle2()
{
    static int passCount = 0;
    if (passCount++ == NUM_LEDS/4)
    {
        passCount = 0;
        FastLED.clear(false);
    }
    g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];
    delay(200);       
}

================================================
FILE: LED Episode 07/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: LED Episode 07/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LED Episode 07/include/README
================================================

This directory is intended for project header files.

A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.

```src/main.c

#include "header.h"

int main (void)
{
 ...
}
```

Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.

In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.

Read more about using header files in official GCC documentation:

* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes

https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html


================================================
FILE: LED Episode 07/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: LED Episode 07/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
build_flags = -Wno-unused-variable
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
monitor_speed = 115200

lib_deps = U8g2
           FastLED
           
        
           





================================================
FILE: LED Episode 07/src/bounce.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        bounce.h     
//
// Description:
//
//      Bouncing Ball effect on an LED strip
//
// History:     Oct-04-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <sys/time.h>                   // For time-of-day

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

using namespace std;
#include <vector>

extern CRGB g_LEDs[];

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))   // Count elements in a static array

static const CRGB ballColors [] =
{
    CRGB::Green,
    CRGB::Red,
    CRGB::Blue,
    CRGB::Orange,
    CRGB::Indigo
};

class BouncingBallEffect
{
  private:

    double InitialBallSpeed(double height) const
    {
        return sqrt(-2 * Gravity * height);         // Because MATH!
    }

    size_t  _cLength;           
    size_t  _cBalls;
    byte    _fadeRate;
    bool    _bMirrored;

    const double Gravity = -9.81;                   // Because PHYSICS!
    const double StartHeight = 1;                   // Drop balls from max height initially
    const double ImpactVelocity = InitialBallSpeed(StartHeight);
    const double SpeedKnob = 4.0;                   // Higher values will slow the effect

    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;
    vector<CRGB>   Colors;

    static double Time()
    {
        timeval tv = { 0 };
        gettimeofday(&tv, nullptr);
        return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);
    }
    
  public:

    // BouncingBallEffect
    //
    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the
    // balls should be drawn mirrored from each side.

    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)
        : _cLength(cLength - 1),
          _cBalls(ballCount),
          _fadeRate(fade),
          _bMirrored(bMirrored),
          ClockTimeAtLastBounce(ballCount),
          Height(ballCount),
          BallSpeed(ballCount),
          Dampening(ballCount),
          Colors(ballCount)
    {
        for (size_t i = 0; i < ballCount; i++)
        {
            Height[i]                = StartHeight;         // Current Ball Height
            ClockTimeAtLastBounce[i] = Time();              // When ball last hit ground state
            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball
            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch
            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];
        }
    }

    // Draw
    //
    // Draw each of the balls.  When any ball settles with too little energy, it it "kicked" to restart it

    virtual void Draw()
    {
        if (_fadeRate != 0)
        {
            for (size_t i = 0; i < _cLength; i++)
                g_LEDs[i].fadeToBlackBy(_fadeRate);
        }
        else
            FastLED.clear();
        
        // Draw each of the balls

        for (size_t i = 0; i < _cBalls; i++)
        {
            double TimeSinceLastBounce = (Time() - ClockTimeAtLastBounce[i]) / SpeedKnob;

            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration
            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;

            // Ball hits ground - bounce!
            if (Height[i] < 0)
            {
                Height[i] = 0;
                BallSpeed[i] = Dampening[i] * BallSpeed[i];
                ClockTimeAtLastBounce[i] = Time();

                if (BallSpeed[i] < 0.01)
                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];
            }

            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);

            g_LEDs[position]   += Colors[i];
            g_LEDs[position+1] += Colors[i];

            if (_bMirrored)
            {
                g_LEDs[_cLength - 1 - position] += Colors[i];
                g_LEDs[_cLength - position]     += Colors[i];
            }
        }
        delay(20);
    }
};

================================================
FILE: LED Episode 07/src/comet.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:                  
//
// Description:
//
//   
//
// History:     Sep-28-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

extern CRGB g_LEDs[];

void DrawComet()
{
    const byte fadeAmt = 128;
    const int cometSize = 5;
    const int deltaHue  = 4;

    static byte hue = HUE_RED;
    static int iDirection = 1;
    static int iPos = 0;

    hue += deltaHue;

    iPos += iDirection;
    if (iPos == (NUM_LEDS - cometSize) || iPos == 0)
        iDirection *= -1;
    
    for (int i = 0; i < cometSize; i++)
        g_LEDs[iPos + i].setHue(hue);
    
    // Randomly fade the LEDs
    for (int j = 0; j < NUM_LEDS; j++)
        if (random(10) > 5)
            g_LEDs[j] = g_LEDs[j].fadeToBlackBy(fadeAmt);  

    delay(30);
}

================================================
FILE: LED Episode 07/src/main.cpp
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        LED Episode 08
//
// Description:
//
//   Draws sample effects on a an addressable strip using FastLED
//
// History:     Sep-15-2020     davepl      Created
//              Oct-05-2020     davepl      Revised for Episode 07
//              Oct-11-2020     davepl      Revised for Episode 08
//              Oct-16-2020     davepl      Revised for Episode 09
//---------------------------------------------------------------------------

#include <Arduino.h>            // Arduino Framework
#include <U8g2lib.h>            // For text on the little on-chip OLED
#define FASTLED_INTERNAL        // Suppress build banner
#include <FastLED.h>

#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16

#define NUM_LEDS    40          // FastLED definitions
#define LED_PIN     5

CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED

U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 255;         // 0-255 LED brightness scale
int g_PowerLimit = 900;         // 900mW Power Limit

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)

// FractionalColor
//
// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we
// want to improve the color math or do color correction all in one location at a later date.

CRGB ColorFraction(CRGB colorIn, float fraction)
{
  fraction = min(1.0f, fraction);
  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));
}

void DrawPixels(float fPos, float count, CRGB color)
{
  // Calculate how much the first pixel will hold
  float availFirstPixel = 1.0f - (fPos - (long)(fPos));
  float amtFirstPixel = min(availFirstPixel, count);
  float remaining = min(count, FastLED.size()-fPos);
  int iPos = fPos;

  // Blend (add) in the color of the first partial pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);
    remaining -= amtFirstPixel;
  }

  // Now draw any full pixels in the middle

  while (remaining > 1.0f)
  {
    FastLED.leds()[iPos++] += color;
    remaining--;
  }

  // Draw tail pixel, up to a single full pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos] += ColorFraction(color, remaining);
  }
}

void DrawMarqueeComparison()
{
  static float scroll = 0.0f;
  scroll += 0.1f;
  if (scroll > 5.0)
    scroll -= 5.0;

  for (float i = scroll; i < NUM_LEDS/2 -1; i+= 5)
  {
    DrawPixels(i, 3, CRGB::Green);
    DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);
  }
}


void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");

  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total

  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(g_Brightness);
  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle
  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled
}

void loop() 
{
  bool bLED = 0;

  while (true)
  {
    EVERY_N_MILLISECONDS(20)
    {
      /*
      fadeToBlackBy(g_LEDs, NUM_LEDS, 64);
      int cometSize = 5;
      int iPos = beatsin16(32, 0, NUM_LEDS-cometSize);
      byte hue = beatsin8(96);
      for (int i = iPos; i < iPos + cometSize; i++)
        g_LEDs[i] = CHSV(hue, 255, 255);
      */

      DrawMarqueeComparison();
    }

    EVERY_N_MILLISECONDS(250)
    {
      g_OLED.clearBuffer();
      g_OLED.setCursor(0, g_lineHeight);
      g_OLED.printf("FPS  : %u", FastLED.getFPS());
      g_OLED.setCursor(0, g_lineHeight * 2);
      g_OLED.printf("Power: %u mW", calculate_unscaled_power_mW(g_LEDs, NUM_LEDS));
      g_OLED.setCursor(0, g_lineHeight * 3);
      g_OLED.printf("Brite: %d", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));
      g_OLED.sendBuffer();
    }

    FastLED.setBrightness(g_Brightness);        //  Set the brightness scale
    FastLED.delay(10);                          //  Show and delay
  }
}


================================================
FILE: LED Episode 07/src/marquee.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        marque.h
//
// Description:
//
//   Draws a theatre-style marquee
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>


void DrawMarquee()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < NUM_LEDS; i ++)
        g_LEDs[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < NUM_LEDS - 1; i += 5)
    {
        g_LEDs[i] = CRGB::Black;
    }
    delay(50);
}

void DrawMarqueeMirrored()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < (NUM_LEDS + 1) / 2; i ++)
    {
        g_LEDs[i] = c.setHue(k);
        g_LEDs[NUM_LEDS - 1 - i] = c.setHue(k);
        k+= 8;
    }


    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < NUM_LEDS / 2; i += 5)
    {
        g_LEDs[i] = CRGB::Black;
        g_LEDs[NUM_LEDS - 1 - i] = CRGB::Black;
    }   

    delay(50);
}





================================================
FILE: LED Episode 07/src/twinkle.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        
//
// Description:
//
//   
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#define NUM_COLORS 5
static const CRGB TwinkleColors [NUM_COLORS] = 
{
    CRGB::Red,
    CRGB::Blue,
    CRGB::Purple,
    CRGB::Green,
    CRGB::Yellow
};

void DrawTwinkle()
{
    FastLED.clear(false);

    for (int i=0; i<NUM_LEDS/4; i++) 
    {
        g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];
        FastLED.show(g_Brightness);
        delay(200);
    }
}

void DrawTwinkle2()
{
    static int passCount = 0;
    if (passCount++ == NUM_LEDS/4)
    {
        passCount = 0;
        FastLED.clear(false);
    }
    g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];
    delay(200);       
}

================================================
FILE: LED Episode 08/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: LED Episode 08/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LED Episode 08/include/README
================================================

This directory is intended for project header files.

A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.

```src/main.c

#include "header.h"

int main (void)
{
 ...
}
```

Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.

In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.

Read more about using header files in official GCC documentation:

* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes

https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html


================================================
FILE: LED Episode 08/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: LED Episode 08/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
build_flags = -Wno-unused-variable
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
monitor_speed = 115200

lib_deps = U8g2
           FastLED
           
        
           





================================================
FILE: LED Episode 08/src/bounce.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        bounce.h     
//
// Description:
//
//      Bouncing Ball effect on an LED strip
//
// History:     Oct-04-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <sys/time.h>                   // For time-of-day

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

using namespace std;
#include <vector>

extern CRGB g_LEDs[];

static const CRGB ballColors [] =
{
    CRGB::Green,
    CRGB::Red,
    CRGB::Blue,
    CRGB::Orange,
    CRGB::Indigo
};

class BouncingBallEffect
{
  private:

    float InitialBallSpeed(float height) const
    {
        return sqrt(-2 * Gravity * height);         // Because MATH!
    }

    size_t  _cLength;           
    size_t  _cBalls;
    byte    _fadeRate;
    bool    _bMirrored;

    const float Gravity = -9.81;                   // Because PHYSICS!
    const float StartHeight = 1;                   // Drop balls from max height initially
    const float ImpactVelocity = InitialBallSpeed(StartHeight);
    const float SpeedKnob = 4.0;                   // Higher values will slow the effect

    vector<float> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;
    vector<CRGB>   Colors;

    static float Time()
    {
        timeval tv = { 0 };
        gettimeofday(&tv, nullptr);
        return (float)(tv.tv_usec / 1000000.0 + (float) tv.tv_sec);
    }
    
  public:

    // BouncingBallEffect
    //
    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the
    // balls should be drawn mirrored from each side.

    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)
        : _cLength(cLength - 1),
          _cBalls(ballCount),
          _fadeRate(fade),
          _bMirrored(bMirrored),
          ClockTimeAtLastBounce(ballCount),
          Height(ballCount),
          BallSpeed(ballCount),
          Dampening(ballCount),
          Colors(ballCount)
    {
        for (size_t i = 0; i < ballCount; i++)
        {
            Height[i]                = StartHeight;         // Current Ball Height
            ClockTimeAtLastBounce[i] = Time();              // When ball last hit ground state
            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball
            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch
            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];
        }
    }

    // Draw
    //
    // Draw each of the balls.  When any ball settles with too little energy, it it "kicked" to restart it

    virtual void Draw()
    {
        if (_fadeRate != 0)
        {
            for (size_t i = 0; i < _cLength; i++)
                g_LEDs[i].fadeToBlackBy(_fadeRate);
        }
        else
            FastLED.clear();
        
        // Draw each of the balls

        for (size_t i = 0; i < _cBalls; i++)
        {
            float TimeSinceLastBounce = (Time() - ClockTimeAtLastBounce[i]) / SpeedKnob;

            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration
            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;

            // Ball hits ground - bounce!
            if (Height[i] < 0)
            {
                Height[i] = 0;
                BallSpeed[i] = Dampening[i] * BallSpeed[i];
                ClockTimeAtLastBounce[i] = Time();

                if (BallSpeed[i] < 0.01)
                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];
            }

            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);

            g_LEDs[position]   += Colors[i];
            g_LEDs[position+1] += Colors[i];

            if (_bMirrored)
            {
                g_LEDs[_cLength - 1 - position] += Colors[i];
                g_LEDs[_cLength - position]     += Colors[i];
            }
        }
        delay(20);
    }
};

================================================
FILE: LED Episode 08/src/comet.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:                  
//
// Description:
//
//   
//
// History:     Sep-28-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

extern CRGB g_LEDs[];

void DrawComet()
{
    const byte fadeAmt = 128;
    const int cometSize = 5;
    const int deltaHue  = 4;

    static byte hue = HUE_RED;
    static int iDirection = 1;
    static int iPos = 0;

    hue += deltaHue;

    iPos += iDirection;
    if (iPos == (NUM_LEDS - cometSize) || iPos == 0)
        iDirection *= -1;
    
    for (int i = 0; i < cometSize; i++)
        g_LEDs[iPos + i].setHue(hue);
    
    // Randomly fade the LEDs
    for (int j = 0; j < NUM_LEDS; j++)
        if (random(10) > 5)
            g_LEDs[j] = g_LEDs[j].fadeToBlackBy(fadeAmt);  

    delay(30);
}

================================================
FILE: LED Episode 08/src/main.cpp
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        LED Episode 07
//
// Description:
//
//   Draws sample effects on a an addressable strip using FastLED
//
// History:     Sep-15-2020     davepl      Created
//              Oct-15-2020     davepl      Revised for Episode 07
//
//---------------------------------------------------------------------------

#include <Arduino.h>            // Arduino Framework
#include <U8g2lib.h>            // For text on the little on-chip OLED
#define FASTLED_INTERNAL        // Suppress build banner
#include <FastLED.h>

#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16

#define NUM_LEDS    40          // FastLED definitions
#define LED_PIN     5

CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED

U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 255;           // 0-255 LED brightness scale
int g_PowerLimit = 900;           // 900mW draw

#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))   

// FractionalColor
//
// Returns a fraction of a color; abstracts the fadeToBlack away so that we can later
// do better color correction as needed

CRGB ColorFraction(const CRGB colorIn, float fraction)
{
  fraction = min(1.0f, fraction);
  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));
}

// DrawPixels
//
// Draw a sub-pixel precise amount of pixels starting at a floating point offset; for example
// you can draw 2.75 pixels starting a 5.5, and it will end at 8.25

void DrawPixels(float fPos, float count, CRGB color)
{
    // Figure out how much the first pixel will hold
    float availFirstPixel = 1.0f - (fPos - (long)(fPos));  // If we are starting at 2.25, there would be 0.75 avail here
    float amtFirstPixel = min(availFirstPixel, count);     // But of course we never draw more than we need
    float remaining = min(count, FastLED.size()-fPos);     // How many pixels remain after we draw the front header pixel
    int iPos = fPos;

    // Blend (add) in the color value of this first partial pixel ...and decrement the remaining pixel count by that same amount

    if (remaining > 0.0f)
    {
      FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel); 
      remaining -= amtFirstPixel;                          
    }

    // Draw any full pixels and stop when we have a full pixel or less remainining

    while (remaining > 1.0f)                               // Final pixel can 'handle' up to 1.0 full pixels, so we draw anything more here
    {
      FastLED.leds()[iPos++] += color;                     // Draw them in one at aa time and update the remaining counts
      remaining--;
    }  

    // Draw tail pixel, up to a single full pixel

    if (remaining > 0.0f)  
    {                    
        FastLED.leds()[iPos] += ColorFraction(color, remaining);     
    }
}


#include "marquee.h"

void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");

  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total

  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(g_Brightness);
  set_max_power_indicator_LED(LED_BUILTIN);                               // FastLED will light LED if power limiting
  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);
}


void loop() 
{
  bool bLED = 0;
  float fps = 0;
  byte gHue = 0;

  while (true)
  {
    TIMES_PER_SECOND(50)
    {
      fadeToBlackBy(g_LEDs, NUM_LEDS, 64);
      float pos = beatsin16(32, 0, NUM_LEDS-10);
      byte hue = beatsin8(32, 0, 255);
      DrawPixels(pos, 10, CHSV(0, 255, 255));
      FastLED.show(g_Brightness);
    }

    /*
    TIMES_PER_SECOND(50)
    {
      DrawMarqueeComparison();
    }
    */
    EVERY_N_MILLISECONDS(250)
    {
      g_OLED.clearBuffer();
      g_OLED.setCursor(0, g_lineHeight);
      g_OLED.printf("FPS  : %u", FastLED.getFPS());
      g_OLED.setCursor(0, g_lineHeight * 2);
      g_OLED.printf("Power: %u mW", calculate_unscaled_power_mW(g_LEDs, NUM_LEDS));
      g_OLED.setCursor(0, g_lineHeight * 3);
      g_OLED.printf("Brite: %d", calculate_max_brightness_for_power_mW( g_Brightness, g_PowerLimit));
      g_OLED.sendBuffer();
    }

    FastLED.delay(10);
   }
}


================================================
FILE: LED Episode 08/src/marquee.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        marque.h
//
// Description:
//
//   Draws a theatre-style marquee
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

void DrawMarqueeComparison()
{
    static float scroll = 0.0f;
    scroll += 0.1f;
    if (scroll > 5.0f)
        scroll -= 5.0f;

    for (float i = scroll; i < NUM_LEDS/2 - 1; i += 5)
    {
        DrawPixels(i, 3, CRGB::Green);
        DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);
    }
}

void DrawMarquee()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < NUM_LEDS; i ++)
        g_LEDs[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < NUM_LEDS - 1; i += 5)
    {
        g_LEDs[i] = CRGB::Black;
    }
    delay(50);
}

void DrawMarqueeMirrored()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < (NUM_LEDS + 1) / 2; i ++)
    {
        g_LEDs[i] = c.setHue(k);
        g_LEDs[NUM_LEDS - 1 - i] = c.setHue(k);
        k+= 8;
    }


    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < NUM_LEDS / 2; i += 5)
    {
        g_LEDs[i] = CRGB::Black;
        g_LEDs[NUM_LEDS - 1 - i] = CRGB::Black;
    }   

    delay(50);
}





================================================
FILE: LED Episode 08/src/twinkle.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        
//
// Description:
//
//   
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#define NUM_COLORS 5
static const CRGB TwinkleColors [NUM_COLORS] = 
{
    CRGB::Red,
    CRGB::Blue,
    CRGB::Purple,
    CRGB::Green,
    CRGB::Yellow
};

void DrawTwinkle()
{
    FastLED.clear(false);

    for (int i=0; i<NUM_LEDS/4; i++) 
    {
        g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];
        FastLED.show(g_Brightness);
        delay(200);
    }
}

void DrawTwinkle2()
{
    static int passCount = 0;
    if (passCount++ == NUM_LEDS/4)
    {
        passCount = 0;
        FastLED.clear(false);
    }
    g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];
    delay(200);       
}

================================================
FILE: LED Episode 09/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: LED Episode 09/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LED Episode 09/include/README
================================================

This directory is intended for project header files.

A header file is a file containing C declarations and macro definitions
to be shared between several project source files. You request the use of a
header file in your project source file (C, C++, etc) located in `src` folder
by including it, with the C preprocessing directive `#include'.

```src/main.c

#include "header.h"

int main (void)
{
 ...
}
```

Including a header file produces the same results as copying the header file
into each source file that needs it. Such copying would be time-consuming
and error-prone. With a header file, the related declarations appear
in only one place. If they need to be changed, they can be changed in one
place, and programs that include the header file will automatically use the
new version when next recompiled. The header file eliminates the labor of
finding and changing all the copies as well as the risk that a failure to
find one copy will result in inconsistencies within a program.

In C, the usual convention is to give header files names that end with `.h'.
It is most portable to use only letters, digits, dashes, and underscores in
header file names, and at most one dot.

Read more about using header files in official GCC documentation:

* Include Syntax
* Include Operation
* Once-Only Headers
* Computed Includes

https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html


================================================
FILE: LED Episode 09/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: LED Episode 09/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
build_flags = -Wno-unused-variable
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
monitor_speed = 115200

lib_deps = U8g2
           FastLED
           
        
           





================================================
FILE: LED Episode 09/src/bounce.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        bounce.h     
//
// Description:
//
//      Bouncing Ball effect on an LED strip
//
// History:     Oct-04-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <sys/time.h>                   // For time-of-day

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

using namespace std;
#include <vector>

extern CRGB g_LEDs[];

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))   // Count elements in a static array

static const CRGB ballColors [] =
{
    CRGB::Green,
    CRGB::Red,
    CRGB::Blue,
    CRGB::Orange,
    CRGB::Indigo
};

class BouncingBallEffect
{
  private:

    double InitialBallSpeed(double height) const
    {
        return sqrt(-2 * Gravity * height);         // Because MATH!
    }

    size_t  _cLength;           
    size_t  _cBalls;
    byte    _fadeRate;
    bool    _bMirrored;

    const double Gravity = -9.81;                   // Because PHYSICS!
    const double StartHeight = 1;                   // Drop balls from max height initially
    const double ImpactVelocity = InitialBallSpeed(StartHeight);
    const double SpeedKnob = 4.0;                   // Higher values will slow the effect

    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;
    vector<CRGB>   Colors;

    static double Time()
    {
        timeval tv = { 0 };
        gettimeofday(&tv, nullptr);
        return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);
    }
    
  public:

    // BouncingBallEffect
    //
    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the
    // balls should be drawn mirrored from each side.

    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)
        : _cLength(cLength - 1),
          _cBalls(ballCount),
          _fadeRate(fade),
          _bMirrored(bMirrored),
          ClockTimeAtLastBounce(ballCount),
          Height(ballCount),
          BallSpeed(ballCount),
          Dampening(ballCount),
          Colors(ballCount)
    {
        for (size_t i = 0; i < ballCount; i++)
        {
            Height[i]                = StartHeight;         // Current Ball Height
            ClockTimeAtLastBounce[i] = Time();              // When ball last hit ground state
            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball
            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch
            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];
        }
    }

    // Draw
    //
    // Draw each of the balls.  When any ball settles with too little energy, it it "kicked" to restart it

    virtual void Draw()
    {
        if (_fadeRate != 0)
        {
            for (size_t i = 0; i < _cLength; i++)
                g_LEDs[i].fadeToBlackBy(_fadeRate);
        }
        else
            FastLED.clear();
        
        // Draw each of the balls

        for (size_t i = 0; i < _cBalls; i++)
        {
            double TimeSinceLastBounce = (Time() - ClockTimeAtLastBounce[i]) / SpeedKnob;

            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration
            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;

            // Ball hits ground - bounce!
            if (Height[i] < 0)
            {
                Height[i] = 0;
                BallSpeed[i] = Dampening[i] * BallSpeed[i];
                ClockTimeAtLastBounce[i] = Time();

                if (BallSpeed[i] < 0.01)
                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];
            }

            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);

            g_LEDs[position]   += Colors[i];
            g_LEDs[position+1] += Colors[i];

            if (_bMirrored)
            {
                g_LEDs[_cLength - 1 - position] += Colors[i];
                g_LEDs[_cLength - position]     += Colors[i];
            }
        }
        delay(20);
    }
};

================================================
FILE: LED Episode 09/src/comet.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:                  
//
// Description:
//
//   
//
// History:     Sep-28-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

extern CRGB g_LEDs[];

void DrawComet()
{
    const byte fadeAmt = 128;
    const int cometSize = 5;
    const int deltaHue  = 4;

    static byte hue = HUE_RED;
    static int iDirection = 1;
    static int iPos = 0;

    hue += deltaHue;

    iPos += iDirection;
    if (iPos == (NUM_LEDS - cometSize) || iPos == 0)
        iDirection *= -1;
    
    for (int i = 0; i < cometSize; i++)
        g_LEDs[iPos + i].setHue(hue);
    
    // Randomly fade the LEDs
    for (int j = 0; j < NUM_LEDS; j++)
        if (random(10) > 5)
            g_LEDs[j] = g_LEDs[j].fadeToBlackBy(fadeAmt);  

    delay(30);
}

================================================
FILE: LED Episode 09/src/main.cpp
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        LED Episode 08
//
// Description:
//
//   Draws sample effects on a an addressable strip using FastLED
//
// History:     Sep-15-2020     davepl      Created
//              Oct-05-2020     davepl      Revised for Episode 07
//              Oct-11-2020     davepl      Revised for Episode 08
//              Oct-16-2020     davepl      Revised for Episode 09
//---------------------------------------------------------------------------

#include <Arduino.h>            // Arduino Framework
#include <U8g2lib.h>            // For text on the little on-chip OLED
#define FASTLED_INTERNAL        // Suppress build banner
#include <FastLED.h>

#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16

#define NUM_LEDS    40          // FastLED definitions
#define LED_PIN     5

CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED

U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 255;         // 0-255 LED brightness scale
int g_PowerLimit = 900;         // 900mW Power Limit

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)

// FractionalColor
//
// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we
// want to improve the color math or do color correction all in one location at a later date.

CRGB ColorFraction(CRGB colorIn, float fraction)
{
  fraction = min(1.0f, fraction);
  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));
}

void DrawPixels(float fPos, float count, CRGB color)
{
  // Calculate how much the first pixel will hold
  float availFirstPixel = 1.0f - (fPos - (long)(fPos));
  float amtFirstPixel = min(availFirstPixel, count);
  float remaining = min(count, FastLED.size()-fPos);
  int iPos = fPos;

  // Blend (add) in the color of the first partial pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);
    remaining -= amtFirstPixel;
  }

  // Now draw any full pixels in the middle

  while (remaining > 1.0f)
  {
    FastLED.leds()[iPos++] += color;
    remaining--;
  }

  // Draw tail pixel, up to a single full pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos] += ColorFraction(color, remaining);
  }
}

void DrawMarqueeComparison()
{
  static float scroll = 0.0f;
  scroll += 0.1f;
  if (scroll > 5.0)
    scroll -= 5.0;

  for (float i = scroll; i < NUM_LEDS/2 -1; i+= 5)
  {
    DrawPixels(i, 3, CRGB::Green);
    DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);
  }
}


void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");

  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total

  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(g_Brightness);
  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle
  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled
}

void loop() 
{
  bool bLED = 0;

  while (true)
  {
    FastLED.clear();
    // Draw here 

    EVERY_N_MILLISECONDS(250)
    {
      g_OLED.clearBuffer();
      g_OLED.setCursor(0, g_lineHeight);
      g_OLED.printf("FPS  : %u", FastLED.getFPS());
      g_OLED.setCursor(0, g_lineHeight * 2);
      g_OLED.printf("Power: %u mW", calculate_unscaled_power_mW(g_LEDs, NUM_LEDS));
      g_OLED.setCursor(0, g_lineHeight * 3);
      g_OLED.printf("Brite: %d", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));
      g_OLED.sendBuffer();
    }

    FastLED.setBrightness(g_Brightness);        //  Set the brightness scale
    FastLED.delay(33);                          //  Show and delay
  }
}


================================================
FILE: LED Episode 09/src/marquee.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        marque.h
//
// Description:
//
//   Draws a theatre-style marquee
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>


void DrawMarquee()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < NUM_LEDS; i ++)
        g_LEDs[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < NUM_LEDS - 1; i += 5)
    {
        g_LEDs[i] = CRGB::Black;
    }
    delay(50);
}

void DrawMarqueeMirrored()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < (NUM_LEDS + 1) / 2; i ++)
    {
        g_LEDs[i] = c.setHue(k);
        g_LEDs[NUM_LEDS - 1 - i] = c.setHue(k);
        k+= 8;
    }


    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < NUM_LEDS / 2; i += 5)
    {
        g_LEDs[i] = CRGB::Black;
        g_LEDs[NUM_LEDS - 1 - i] = CRGB::Black;
    }   

    delay(50);
}





================================================
FILE: LED Episode 09/src/twinkle.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        
//
// Description:
//
//   
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#define NUM_COLORS 5
static const CRGB TwinkleColors [NUM_COLORS] = 
{
    CRGB::Red,
    CRGB::Blue,
    CRGB::Purple,
    CRGB::Green,
    CRGB::Yellow
};

void DrawTwinkle()
{
    FastLED.clear(false);

    for (int i=0; i<NUM_LEDS/4; i++) 
    {
        g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];
        FastLED.show(g_Brightness);
        delay(200);
    }
}

void DrawTwinkle2()
{
    static int passCount = 0;
    if (passCount++ == NUM_LEDS/4)
    {
        passCount = 0;
        FastLED.clear(false);
    }
    g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];
    delay(200);       
}

================================================
FILE: LED Episode 10/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: LED Episode 10/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LED Episode 10/include/bounce.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        bounce.h     
//
// Description:
//
//      Bouncing Ball effect on an LED strip
//
// History:     Oct-04-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

using namespace std;
#include <vector>

#include "ledgfx.h"

static const CRGB ballColors [] =
{
    CRGB::Green,
    CRGB::Red,
    CRGB::Blue,
    CRGB::Orange,
    CRGB::Indigo
};

class BouncingBallEffect
{
  private:

    double InitialBallSpeed(double height) const
    {
        return sqrt(-2 * Gravity * height);         // Because MATH!
    }

    size_t  _cLength;           
    size_t  _cBalls;
    byte    _fadeRate;
    bool    _bMirrored;

    const double Gravity = -9.81;                   // Because PHYSICS!
    const double StartHeight = 1;                   // Drop balls from max height initially
    const double ImpactVelocity = InitialBallSpeed(StartHeight);
    const double SpeedKnob = 4.0;                   // Higher values will slow the effect

    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;
    vector<CRGB>   Colors;

  public:

    // BouncingBallEffect
    //
    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the
    // balls should be drawn mirrored from each side.

    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)
        : _cLength(cLength - 1),
          _cBalls(ballCount),
          _fadeRate(fade),
          _bMirrored(bMirrored),
          ClockTimeAtLastBounce(ballCount),
          Height(ballCount),
          BallSpeed(ballCount),
          Dampening(ballCount),
          Colors(ballCount)
    {
        for (size_t i = 0; i < ballCount; i++)
        {
            Height[i]                = StartHeight;                 // Current Ball Height
            ClockTimeAtLastBounce[i] = UnixTime();                  // When ball last hit ground state
            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball
            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch
            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];
        }
    }

    // Draw
    //
    // Draw each of the balls.  When any ball settles with too little energy, it it "kicked" to restart it

    virtual void Draw()
    {
        if (_fadeRate != 0)
        {
            for (size_t i = 0; i < _cLength; i++)
                FastLED.leds()[i].fadeToBlackBy(_fadeRate);
        }
        else
            FastLED.clear();
        
        // Draw each of the balls

        for (size_t i = 0; i < _cBalls; i++)
        {
            double TimeSinceLastBounce = (UnixTime() - ClockTimeAtLastBounce[i]) / SpeedKnob;

            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration
            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;

            // Ball hits ground - bounce!
            if (Height[i] < 0)
            {
                Height[i] = 0;
                BallSpeed[i] = Dampening[i] * BallSpeed[i];
                ClockTimeAtLastBounce[i] = Time();

                if (BallSpeed[i] < 0.01)
                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];
            }

            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);

            FastLED.leds()[position]   += Colors[i];
            FastLED.leds()[position+1] += Colors[i];

            if (_bMirrored)
            {
                FastLED.leds()[_cLength - 1 - position] += Colors[i];
                FastLED.leds()[_cLength - position]     += Colors[i];
            }
        }
        delay(20);
    }
};

================================================
FILE: LED Episode 10/include/comet.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:                  
//
// Description:
//
//   
//
// History:     Sep-28-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

void DrawComet()
{
    const byte fadeAmt = 128;
    const int cometSize = 5;
    const int deltaHue  = 4;

    static byte hue = HUE_RED;
    static int iDirection = 1;
    static int iPos = 0;

    hue += deltaHue;

    iPos += iDirection;
    if (iPos == (FastLED.count() - cometSize) || iPos == 0)
        iDirection *= -1;
    
    for (int i = 0; i < cometSize; i++)
        FastLED.leds()[iPos + i].setHue(hue);
    
    // Randomly fade the LEDs
    for (int j = 0; j < FastLED.count(); j++)
        if (random(10) > 5)
            FastLED.leds()[j] = FastLED.leds()[j].fadeToBlackBy(fadeAmt);  

    delay(30);
}

================================================
FILE: LED Episode 10/include/fire.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        fire.h               
//
// Description:
//
//              LED Flame Effect
//
// History:     Sep-28-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include "ledgfx.h"

class FireEffectSmooth
{
  protected:
    float  * Temperatures;
    float    LastDraw;                  // Last time we drew the flame

    const float IGNITION_KNOB = 50.0f;  // Preference-based constant factor for ignition rate
    const float SPREADRATE_KNOB = 12.0f; // Preference-based constant for flame spread rate
    const float SPARKRATE_KNOB  = 8.0f; // Preference-based constant for spark ignition rate

  public:
    bool    Mirrored;                   // Should the flame be mirrored, drawn from both sides?
    bool    Reversed;                   // Only applicable when not reversed, should it be reversed?
    float   Cooling;                    // Pixel cooldown rate 
    int     Size;
    int     SparkHeight;                // Ignition zone for where new pixels start up
    float   SparkProbability;           // Probability of a spark in each ignition zone pixel
    float   SpreadRate;                 // Rate at which fire spreads pixel to pixel
    
    FireEffectSmooth(int size, bool mirrored = true, bool reversed = false, int sparkHeight = 0, float sparkProbability = 1.0, float cooling = 1.0, float spreadRate = 1.0)
    {
        Mirrored         = mirrored;
        if (mirrored)
            size /= 2;

        Reversed         = reversed;
        Cooling          = cooling;
        Size             = size;                                            // 
        SparkHeight      = sparkHeight;                                     // 
        SparkProbability = sparkProbability * SPARKRATE_KNOB / SparkHeight; // Chance that each LED cell will ignite when tested
        Temperatures     = new float[Size];                                 // Array of temperatures, one per LED
        SpreadRate       = spreadRate * SPREADRATE_KNOB;                    // How fast the flame spreads per second
        LastDraw         = UnixTime();                                      // Start of time
    }

    virtual ~FireEffectSmooth()               // Because we have a virtual function, destructor is virtual as well
    {
        delete [] Temperatures;
    }

    void DrawFire()
    {
        FastLED.clear();

        float elapsedSeconds = UnixTime() - LastDraw;
        float cooldown = 1.0f * RandomFloat() * Cooling * elapsedSeconds;
        LastDraw = UnixTime();

        for (int i = 0; i < Size; i++)
        {
            Temperatures[i] = max(0.0f, Temperatures[i] - cooldown); // Cool cell by cooldown amount, but don't go below zero
            
            int neighborIndex = (i == 0) ? Size - 1 : i - 1;        // Index of cell to our left, wrapping around to front
            float spreadAmount = min(0.25f, Temperatures[neighborIndex]) * SpreadRate * elapsedSeconds;
            spreadAmount = min(Temperatures[neighborIndex], spreadAmount);
            Temperatures[i]             += spreadAmount;            // Exchange 'spreadAmount' of heat between cells
            Temperatures[neighborIndex] -= spreadAmount;

            // Check to see if this cell ignites a new spark
            if (i <= SparkHeight && RandomFloat() < SparkProbability * elapsedSeconds)
            {
                //Temperatures[i] = Temperatures[i] + RandomFloat() * 30 * elapsedSeconds;
                Temperatures[i] = 2.0; // min(1.0f, (Temperatures[i] + RandomFloat() * 30 * elapsedSeconds));
                //printf("Spark at %d: %f", i, Temperatures[i]);
            }
        }
        for (int i = 0; i < Size; i++)
        {
            FastLED.leds()[i] = HeatColor(240 * min(1.0f, Temperatures[i]));
        }
    }
};

class ClassicFireEffect
{
protected:
    int     Size;
    int     Cooling;
    int     Sparks;
    int     SparkHeight;
    int     Sparking;
    bool    bReversed;
    bool    bMirrored;

    byte  * heat;

    // When diffusing the fire upwards, these control how much to blend in from the cells below (ie: downward neighbors)
    // You can tune these coefficients to control how quickly and smoothly the fire spreads.  

    static const byte BlendSelf = 2;
    static const byte BlendNeighbor1 = 3;
    static const byte BlendNeighbor2 = 2;
    static const byte BlendNeighbor3 = 1;
    static const byte BlendTotal = (BlendSelf + BlendNeighbor1 + BlendNeighbor2 + BlendNeighbor3);

public:
    
    // Lower sparking -> more flicker.  Higher sparking -> more consistent flame

    ClassicFireEffect(int size, int cooling = 80, int sparking = 50, int sparks = 3, int sparkHeight = 4, bool breversed = true, bool bmirrored = true) 
        : Size(size),
          Cooling(cooling),
          Sparks(sparks),
          SparkHeight(sparkHeight),
          Sparking(sparking),
          bReversed(breversed),
          bMirrored(bmirrored)
    {
        if (bMirrored)
            Size = Size / 2;

        heat = new byte[size] { 0 };
    }

    virtual ~ClassicFireEffect()
    {
        delete [] heat;
    }

    virtual void DrawFire()
    {
        // First cool each cell by a little bit
        for (int i = 0; i < Size; i++)
            heat[i] = max(0L, heat[i] - random(0, ((Cooling * 10) / Size) + 2));

        // Next drift heat up and diffuse it a little but
        for (int i = 0; i < Size; i++)
            heat[i] = (heat[i] * BlendSelf + 
                       heat[(i + 1) % Size] * BlendNeighbor1 + 
                       heat[(i + 2) % Size] * BlendNeighbor2 + 
                       heat[(i + 3) % Size] * BlendNeighbor3) 
                      / BlendTotal;

        // Randomly ignite new sparks down in the flame kernel
        for (int i = 0; i < Sparks; i++)
        {
            if (random(255) < Sparking)
            {
                int y = Size - 1 - random(SparkHeight);
                heat[y] = heat[y] + random(160, 255); // This randomly rolls over sometimes of course, and that's essential to the effect
            }
        }

        // Finally convert heat to a color
        for (int i = 0; i < Size; i++)
        {
            CRGB color = HeatColor(heat[i]);
            int j = bReversed ? (Size - 1 - i) : i;
            DrawPixels(j, 1, color);
            if (bMirrored)
            {
                int j2 = !bReversed ? (2 * Size - 1 - i) : Size + i;
                DrawPixels(j2, 1, color);
            }
        }
    }
};


================================================
FILE: LED Episode 10/include/ledgfx.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        ledgfx.h
//
// Description:
//
//   LED Drawing Routines for Dave's Garage Tutorial series
//
// History:     OCt-18-2020     davepl      Created from main.cpp code
//---------------------------------------------------------------------------

#pragma once

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include <sys/time.h>                   // For time-of-day

// Utility Macros

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)

//inline double RandomDouble()
//{
//    return random(UINT32_MAX) / (double) UINT32_MAX;
//}

inline float RandomFloat()
{
    float r = random(1000000L) / 1000000.0f;
    return r;
}

inline double UnixTime()
{
    timeval tv = { 0 };
    gettimeofday(&tv, nullptr);
    return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);
}

// FractionalColor
//
// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we
// want to improve the color math or do color correction all in one location at a later date.

CRGB ColorFraction(CRGB colorIn, float fraction)
{
  fraction = min(1.0f, fraction);
  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));
}

void DrawPixels(float fPos, float count, CRGB color)
{
  // Calculate how much the first pixel will hold
  float availFirstPixel = 1.0f - (fPos - (long)(fPos));
  float amtFirstPixel = min(availFirstPixel, count);
  float remaining = min(count, FastLED.size()-fPos);
  int iPos = fPos;

  // Blend (add) in the color of the first partial pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);
    remaining -= amtFirstPixel;
  }

  // Now draw any full pixels in the middle

  while (remaining > 1.0f)
  {
    FastLED.leds()[iPos++] += color;
    remaining--;
  }

  // Draw tail pixel, up to a single full pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos] += ColorFraction(color, remaining);
  }
}


================================================
FILE: LED Episode 10/include/marquee.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        marque.h
//
// Description:
//
//   Draws a theatre-style marquee
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>


void DrawMarquee()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < FastLED.count(); i ++)
        FastLED.leds()[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < FastLED.count() - 1; i += 5)
    {
        FastLED.leds()[i] = CRGB::Black;
    }
    delay(50);
}

void DrawMarqueeMirrored()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < (FastLED.count() + 1) / 2; i ++)
    {
        FastLED.leds()[i] = c.setHue(k);
        FastLED.leds()[FastLED.count() - 1 - i] = c.setHue(k);
        k+= 8;
    }


    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < FastLED.count() / 2; i += 5)
    {
        FastLED.leds()[i] = CRGB::Black;
        FastLED.leds()[FastLED.count() - 1 - i] = CRGB::Black;
    }   

    delay(50);
}





================================================
FILE: LED Episode 10/include/twinkle.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        
//
// Description:
//
//   
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include "ledgfx.h"

static const CRGB TwinkleColors [] = 
{
    CRGB::Red,
    CRGB::Blue,
    CRGB::Purple,
    CRGB::Green,
    CRGB::Yellow
};

void DrawTwinkle()
{
    static int passCount = 0;
    if (passCount++ == FastLED.count()/4)
    {
        passCount = 0;
        FastLED.clear(false);
    }
    FastLED.leds()[random(FastLED.count())] = TwinkleColors[random(0, ARRAYSIZE(TwinkleColors))];
    delay(200);       
}

================================================
FILE: LED Episode 10/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: LED Episode 10/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
build_flags = -Wno-unused-variable
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
monitor_speed = 115200

lib_deps = U8g2
           FastLED
           
        
           





================================================
FILE: LED Episode 10/src/main.cpp
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        LED Episode 08
//
// Description:
//
//   Draws sample effects on a an addressable strip using FastLED
//
// History:     Sep-15-2020     davepl      Created
//              Oct-05-2020     davepl      Revised for Episode 07
//              Oct-11-2020     davepl      Revised for Episode 08
//              Oct-16-2020     davepl      Revised for Episode 09
//---------------------------------------------------------------------------

#include <Arduino.h>            // Arduino Framework
#include <U8g2lib.h>            // For text on the little on-chip OLED
#define FASTLED_INTERNAL        // Suppress build banner
#include <FastLED.h>

#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16

#define NUM_LEDS    40          // FastLED definitions
#define LED_PIN     5

CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED

U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 255;         // 0-255 LED brightness scale
int g_PowerLimit = 3000;         // 900mW Power Limit

#include "ledgfx.h"
#include "comet.h"
#include "marquee.h"
#include "twinkle.h"
#include "fire.h"

void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");

  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total

  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(g_Brightness);
  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle
  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled
}

void DrawMarqueeComparison()
{
  static float scroll = 0.0f;
  scroll += 0.1f;
  if (scroll > 5.0)
    scroll -= 5.0;

  for (float i = scroll; i < NUM_LEDS/2 -1; i+= 5)
  {
    DrawPixels(i, 3, CRGB::Green);
    DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);
  }
}

void loop() 
{
  bool bLED = 0;

  //ClassicFireEffect fire(NUM_LEDS, 30, 100, 3, 2, false, true);   // Outwards from Middle
  //ClassicFireEffect fire(NUM_LEDS, 30, 100, 3, 2, true, true);    // Inwards toward Middle
  //ClassicFireEffect fire(NUM_LEDS, 20, 100, 3, 4, true, false);     // Outwards from Zero
  //ClassicFireEffect fire(NUM_LEDS, 20, 100, 3, 4, false, false);     // Inwards from End
  //ClassicFireEffect fire(NUM_LEDS, 50, 300, 30, 12, true, false);     // More Intense, Extra Sparking

  ClassicFireEffect fire(NUM_LEDS, 20, 100, 3, NUM_LEDS, true, false);     // Fan with correct rotation

  while (true)
  {
    FastLED.clear();
    fire.DrawFire();
    FastLED.show(g_Brightness);                          //  Show and delay

    EVERY_N_MILLISECONDS(250)
    {
      g_OLED.clearBuffer();
      g_OLED.setCursor(0, g_lineHeight);
      g_OLED.printf("FPS  : %u", FastLED.getFPS());
      g_OLED.setCursor(0, g_lineHeight * 2);
      g_OLED.printf("Power: %u mW", calculate_unscaled_power_mW(g_LEDs, 4));
      g_OLED.setCursor(0, g_lineHeight * 3);
      g_OLED.printf("Brite: %d", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));
      g_OLED.sendBuffer();
    }
    delay(33);
  }
}


================================================
FILE: LED Episode 11/.gitignore
================================================
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch


================================================
FILE: LED Episode 11/.vscode/extensions.json
================================================
{
    // See http://go.microsoft.com/fwlink/?LinkId=827846
    // for the documentation about the extensions.json format
    "recommendations": [
        "platformio.platformio-ide"
    ]
}


================================================
FILE: LED Episode 11/include/bounce.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        bounce.h     
//
// Description:
//
//      Bouncing Ball effect on an LED strip
//
// History:     Oct-04-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

using namespace std;
#include <vector>

#include "ledgfx.h"

static const CRGB ballColors [] =
{
    CRGB::Green,
    CRGB::Red,
    CRGB::Blue,
    CRGB::Orange,
    CRGB::Indigo
};

class BouncingBallEffect
{
  private:

    double InitialBallSpeed(double height) const
    {
        return sqrt(-2 * Gravity * height);         // Because MATH!
    }

    size_t  _cLength;           
    size_t  _cBalls;
    byte    _fadeRate;
    bool    _bMirrored;

    const double Gravity = -9.81;                   // Because PHYSICS!
    const double StartHeight = 1;                   // Drop balls from max height initially
    const double ImpactVelocity = InitialBallSpeed(StartHeight);
    const double SpeedKnob = 4.0;                   // Higher values will slow the effect

    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;
    vector<CRGB>   Colors;

  public:

    // BouncingBallEffect
    //
    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the
    // balls should be drawn mirrored from each side.

    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)
        : _cLength(cLength - 1),
          _cBalls(ballCount),
          _fadeRate(fade),
          _bMirrored(bMirrored),
          ClockTimeAtLastBounce(ballCount),
          Height(ballCount),
          BallSpeed(ballCount),
          Dampening(ballCount),
          Colors(ballCount)
    {
        for (size_t i = 0; i < ballCount; i++)
        {
            Height[i]                = StartHeight;                 // Current Ball Height
            ClockTimeAtLastBounce[i] = UnixTime();                  // When ball last hit ground state
            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball
            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch
            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];
        }
    }

    // Draw
    //
    // Draw each of the balls.  When any ball settles with too little energy, it it "kicked" to restart it

    virtual void Draw()
    {
        if (_fadeRate != 0)
        {
            for (size_t i = 0; i < _cLength; i++)
                FastLED.leds()[i].fadeToBlackBy(_fadeRate);
        }
        else
            FastLED.clear();
        
        // Draw each of the balls

        for (size_t i = 0; i < _cBalls; i++)
        {
            double TimeSinceLastBounce = (UnixTime() - ClockTimeAtLastBounce[i]) / SpeedKnob;

            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration
            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;

            // Ball hits ground - bounce!
            if (Height[i] < 0)
            {
                Height[i] = 0;
                BallSpeed[i] = Dampening[i] * BallSpeed[i];
                ClockTimeAtLastBounce[i] = Time();

                if (BallSpeed[i] < 0.01)
                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];
            }

            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);

            FastLED.leds()[position]   += Colors[i];
            FastLED.leds()[position+1] += Colors[i];

            if (_bMirrored)
            {
                FastLED.leds()[_cLength - 1 - position] += Colors[i];
                FastLED.leds()[_cLength - position]     += Colors[i];
            }
        }
        delay(20);
    }
};

================================================
FILE: LED Episode 11/include/comet.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:                  
//
// Description:
//
//   
//
// History:     Sep-28-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

void DrawComet()
{
    const byte fadeAmt = 128;
    const int cometSize = 5;
    const int deltaHue  = 4;

    static byte hue = HUE_RED;
    static int iDirection = 1;
    static int iPos = 0;

    hue += deltaHue;

    iPos += iDirection;
    if (iPos == (FastLED.count() - cometSize) || iPos == 0)
        iDirection *= -1;
    
    for (int i = 0; i < cometSize; i++)
        FastLED.leds()[iPos + i].setHue(hue);
    
    // Randomly fade the LEDs
    for (int j = 0; j < FastLED.count(); j++)
        if (random(10) > 5)
            FastLED.leds()[j] = FastLED.leds()[j].fadeToBlackBy(fadeAmt);  

    delay(30);
}

================================================
FILE: LED Episode 11/include/fire.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        fire.h                    
//
// Description: A realistic flame simulation for LED strips
//
// History:     Oct-23-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include "ledgfx.h"

class FireEffect
{
  protected:
    int     Size;               // How many pixels the flame is total
    int     Cooling;            // Rate at which the pixels cool off
    int     Sparks;             // How many sparks will be attempted each frame
    int     SparkHeight;        // If created, max height for a spark
    int     Sparking;           // Probability of a spark each attempt
    bool    bReversed;          // If reversed we draw from 0 outwards
    bool    bMirrored;          // If mirrored we split and duplicate the drawing

    byte  * heat;

    // When diffusing the fire upwards, these control how much to blend in from the cells below (ie: downward neighbors)
    // You can tune these coefficients to control how quickly and smoothly the fire spreads

    static const byte BlendSelf = 2;
    static const byte BlendNeighbor1 = 3;
    static const byte BlendNeighbor2 = 2;
    static const byte BlendNeighbor3 = 1;
    static const byte BlendTotal = (BlendSelf + BlendNeighbor1 + BlendNeighbor2 + BlendNeighbor3);

  public:

    FireEffect(int size, int cooling = 20, int sparking = 100, int sparks = 3, int sparkHeight = 4, bool breversed = true, bool bmirrored = true)
        : Size(size),
          Cooling(cooling),
          Sparks(sparks),
          SparkHeight(sparkHeight),
          Sparking(sparking),
          bReversed(breversed),
          bMirrored(bmirrored)
    {
        if (bMirrored)
            Size = Size / 2;

        heat = new byte[size] { 0 };
    }

    virtual ~FireEffect()
    {
        delete [] heat;
    }

    virtual void DrawFire(PixelOrder order = Sequential)
    {
        // First cool each cell by a litle bit
        for (int i = 0; i < Size; i++)
            heat[i] = max(0L, heat[i] - random(0, ((Cooling * 10) / Size) + 2));

        // Next drift heat up and diffuse it a little bit
        for (int i = 0; i < Size; i++)
            heat[i] = (heat[i] * BlendSelf +
                       heat[(i + 1) % Size] * BlendNeighbor1 +
                       heat[(i + 2) % Size] * BlendNeighbor2 +
                       heat[(i + 3) % Size] * BlendNeighbor3)
                      / BlendTotal;

        // Randomly ignite new sparks down in the flame kernel

        for (int i = 0 ; i < Sparks; i++)
        {
            if (random(255) < Sparking)
            {
                int y = Size - 1 - random(SparkHeight);
                heat[y] = heat[y] + random(160, 255);       // Can roll over which actually looks good!
            }
        }

        // Finally, convert heat to a color

        for (int i = 0; i < Size; i++)
        {
            CRGB color = HeatColor(heat[i]);
            int j = bReversed ? (Size - 1 - i) : i;
            DrawFanPixels(j, 1, color, order);
            if (bMirrored)
                DrawFanPixels(!bReversed ? (2 * Size - 1 - i) : Size + i, 1, color, order);
        }
    }
};

================================================
FILE: LED Episode 11/include/ledgfx.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        ledgfx.h
//
// Description:
//
//   LED Drawing Routines for Dave's Garage Tutorial series
//
// History:     OCt-18-2020     davepl      Created from main.cpp code
//---------------------------------------------------------------------------

#pragma once

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include <sys/time.h>                   // For time-of-day

// Utility Macros

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)

// Simple definitions of what direction we're talking about

enum PixelOrder
{
  Sequential  = 0,
  Reverse     = 1,
  BottomUp    = 2,
  TopDown     = 4,
  LeftRight   = 8,
  RightLeft   = 16
};

DEFINE_GRADIENT_PALETTE( vu_gpGreen ) 
{
      0,     0,   4,   0,   // near black green
     64,     0, 255,   0,   // green
    128,   255, 255,   0,   // yellow
    192,   255,   0,   0,   // red
    255,   255,   0,   0    // red
};

DEFINE_GRADIENT_PALETTE( gpSeahawks ) 
{
    0,       0,     0,   4,      
    64,      3,    38,  58,      
   128,      0,    21,  50,      
   192,     78,   167,   1,      
   255,     54,    87, 140,      
};

// These tables represent the physical order of LEDs when looking at
// the fan in a particular direction, like top to bottom or left to right

static const int FanPixelsVertical[FAN_SIZE] =
{
  0, 1, 15, 2, 14, 3, 13, 4, 12, 5, 11, 6, 10, 7, 9, 8
};

static const int FanPixelsHorizontal[FAN_SIZE] =
{
  3, 4, 2, 5, 1, 6, 0, 7, 15, 8, 14, 9, 13, 10, 12, 11
};

// GetFanPixelOrder
// 
// Returns the sequential strip postion of a an LED on the fans based
// on the index and direction specified, like 32nd most TopDown pixel.

int GetFanPixelOrder(int iPos, PixelOrder order = Sequential)
{
  while (iPos < 0)
    iPos += FAN_SIZE;

  int offset = (iPos + LED_FAN_OFFSET) % FAN_SIZE;
  int roffset = (iPos + FAN_SIZE - LED_FAN_OFFSET) % FAN_SIZE;
  int fanBase = iPos - (iPos % FAN_SIZE);

  switch (order)
  {
    case BottomUp:
      return fanBase + FAN_SIZE - 1 - (FanPixelsVertical[iPos % FAN_SIZE] + LED_FAN_OFFSET) % FAN_SIZE;
    
    case TopDown:
      return NUM_LEDS - 1 - (fanBase + (FanPixelsVertical[FAN_SIZE - 1 - (iPos % FAN_SIZE)] + LED_FAN_OFFSET) % FAN_SIZE);
    
    case LeftRight:
      return fanBase + (FanPixelsHorizontal[ iPos % FAN_SIZE ] + LED_FAN_OFFSET - 1) % FAN_SIZE;
    
    case RightLeft:
      return fanBase + (FanPixelsHorizontal[FAN_SIZE - 1 - (iPos % FAN_SIZE)] + LED_FAN_OFFSET - 1) % FAN_SIZE;

    case Reverse:
      return fanBase + FAN_SIZE - 1 - roffset;

    case Sequential:
    default:
      return fanBase + offset;
  }
}


inline float RandomFloat()
{
    float r = random(1000000L) / 1000000.0f;
    return r;
}

inline double UnixTime()
{
    timeval tv = { 0 };
    gettimeofday(&tv, nullptr);
    return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);
}

// FractionalColor
//
// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we
// want to improve the color math or do color correction all in one location at a later date.

CRGB ColorFraction(CRGB colorIn, float fraction)
{
  fraction = min(1.0f, fraction);
  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));
}

// DrawFanPixels
//
// Just like DrawPixels but draws logically into a fan bank in a direction such as top down rather than
// just straight sequential strip order

void DrawFanPixels(float fPos, float count, CRGB color, PixelOrder order = Sequential, int iFan = 0)
{
  fPos += iFan * FAN_SIZE;

  // Calculate how much the first pixel will hold

  float availFirstPixel = 1.0f - (fPos - (long)(fPos));
  float amtFirstPixel = min(availFirstPixel, count);
  float remaining = min(count, FastLED.size()-fPos);
  int iPos = fPos;

  // Blend (add) in the color of the first partial pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[GetFanPixelOrder(iPos++, order)] += ColorFraction(color, amtFirstPixel);
    remaining -= amtFirstPixel;
  }

  // Now draw any full pixels in the middle

  while (remaining > 1.0f)
  {
    FastLED.leds()[GetFanPixelOrder(iPos++, order)] += color;
    remaining--;
  }

  // Draw tail pixel, up to a single full pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[GetFanPixelOrder(iPos, order)] += ColorFraction(color, remaining);
  }
}

// DrawPixels
// 
// Uses floating point math to draw a floating point number of pixels starting at a 
// floating point offset into the strip

void DrawPixels(float fPos, float count, CRGB color)
{
  // Calculate how much the first pixel will hold
  float availFirstPixel = 1.0f - (fPos - (long)(fPos));
  float amtFirstPixel = min(availFirstPixel, count);
  float remaining = min(count, FastLED.size()-fPos);
  int iPos = fPos;

  // Blend (add) in the color of the first partial pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);
    remaining -= amtFirstPixel;
  }

  // Now draw any full pixels in the middle

  while (remaining > 1.0f)
  {
    FastLED.leds()[iPos++] += color;
    remaining--;
  }

  // Draw tail pixel, up to a single full pixel

  if (remaining > 0.0f)
  {
    FastLED.leds()[iPos] += ColorFraction(color, remaining);
  }
}


================================================
FILE: LED Episode 11/include/marquee.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        marque.h
//
// Description:
//
//   Draws a theatre-style marquee
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#include <U8g2lib.h>
#define FASTLED_INTERNAL
#include <FastLED.h>


void DrawMarquee()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < FastLED.count(); i ++)
        FastLED.leds()[i] = c.setHue(k+=8);

    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < FastLED.count() - 1; i += 5)
    {
        FastLED.leds()[i] = CRGB::Black;
    }
    delay(50);
}

void DrawMarqueeMirrored()
{
    static byte j = 0;
    j+=4;
    byte k = j;

    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);

    CRGB c;
    for (int i = 0; i < (FastLED.count() + 1) / 2; i ++)
    {
        FastLED.leds()[i] = c.setHue(k);
        FastLED.leds()[FastLED.count() - 1 - i] = c.setHue(k);
        k+= 8;
    }


    static int scroll = 0;
    scroll++;

    for (int i = scroll % 5; i < FastLED.count() / 2; i += 5)
    {
        FastLED.leds()[i] = CRGB::Black;
        FastLED.leds()[FastLED.count() - 1 - i] = CRGB::Black;
    }   

    delay(50);
}





================================================
FILE: LED Episode 11/include/twinkle.h
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.
//
// File:        
//
// Description:
//
//   
//
// History:     Sep-15-2020     davepl      Created
//
//---------------------------------------------------------------------------

#include <Arduino.h>
#define FASTLED_INTERNAL
#include <FastLED.h>

#include "ledgfx.h"

static const CRGB TwinkleColors [] = 
{
    CRGB::Red,
    CRGB::Blue,
    CRGB::Purple,
    CRGB::Green,
    CRGB::Yellow
};

void DrawTwinkle()
{
    static int passCount = 0;
    if (passCount++ == FastLED.count()/4)
    {
        passCount = 0;
        FastLED.clear(false);
    }
    FastLED.leds()[random(FastLED.count())] = TwinkleColors[random(0, ARRAYSIZE(TwinkleColors))];
    delay(200);       
}

================================================
FILE: LED Episode 11/lib/README
================================================

This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.

The source code of each library should be placed in a an own separate directory
("lib/your_library_name/[here are source files]").

For example, see a structure of the following two libraries `Foo` and `Bar`:

|--lib
|  |
|  |--Bar
|  |  |--docs
|  |  |--examples
|  |  |--src
|  |     |- Bar.c
|  |     |- Bar.h
|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
|  |
|  |--Foo
|  |  |- Foo.c
|  |  |- Foo.h
|  |
|  |- README --> THIS FILE
|
|- platformio.ini
|--src
   |- main.c

and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>

int main (void)
{
  ...
}

```

PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.

More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html


================================================
FILE: LED Episode 11/platformio.ini
================================================
; PlatformIO Project Configuration File
;
;   Build options: build flags, source filter
;   Upload options: custom upload port, speed and extra flags
;   Library options: dependencies, extra library storages
;   Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html

[env:heltec_wifi_kit_32]
platform = espressif32
board = heltec_wifi_kit_32
framework = arduino
build_flags = -Wno-unused-variable
upload_port = /dev/cu.SLAB_USBtoUART
monitor_port = /dev/cu.SLAB_USBtoUART
monitor_speed = 115200

lib_deps = U8g2
           FastLED
           
        
           





================================================
FILE: LED Episode 11/src/main.cpp
================================================
//+--------------------------------------------------------------------------
//
// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.
//
// File:        LED Episode 11
//
// Description:
//
//   Draws sample effects on a an addressable strip using FastLED
//
// History:     Sep-15-2020     davepl      Created
//              Oct-05-2020     davepl      Revised for Episode 07
//              Oct-11-2020     davepl      Revised for Episode 08
//              Oct-16-2020     davepl      Revised for Episode 09
//              Oct-23-2020     davepl      Revised for Episode 10
//---------------------------------------------------------------------------

#include <Arduino.h>            // Arduino Framework
#include <U8g2lib.h>            // For text on the little on-chip OLED
#define FASTLED_INTERNAL        // Suppress build banner
#include <FastLED.h>

#define OLED_CLOCK  15          // Pins for the OLED display
#define OLED_DATA   4
#define OLED_RESET  16

#define FAN_SIZE      16        // Number of LEDs in each fan
#define NUM_FANS       3        // Number of Fans
#define LED_FAN_OFFSET 4        // How far from bottom first pixel is
#define NUM_LEDS      48        // FastLED definitions
#define LED_PIN        5

CRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED

U8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);
int g_lineHeight = 0;
int g_Brightness = 255;         // 0-255 LED brightness scale
int g_PowerLimit = 3000;        // 900mW Power Limit

#include "ledgfx.h"
#include "comet.h"
#include "marquee.h"
#include "twinkle.h"
#include "fire.h"

void setup() 
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial) { }
  Serial.println("ESP32 Startup");

  g_OLED.begin();
  g_OLED.clear();
  g_OLED.setFont(u8g2_font_profont15_tf);
  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total

  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library
  FastLED.setBrightness(g_Brightness);
  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle
  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled
}

void DrawMarqueeComparison()
{
  static float scroll = 0.0f;
  scroll += 0.1f;
  if (scroll > 5.0)
    scroll -= 5.0;

  for (float i = scroll; i < NUM_LEDS/2 -1; i+= 5)
  {
    DrawPixels(i, 3, CRGB::Green);
    DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);
  }
}

void loop() 
{
  bool bLED = 0;

  while (true)
  {
    FastLED.clear();

    /*
    // RGB Spinners
    float b = beat16(60) / 65535.0f * FAN_SIZE;
    DrawFanPixels(b, 1, CRGB::Red,   Sequential,  0);
    DrawFanPixels(b, 1, CRGB::Green, Sequential,  1);
    DrawFanPixels(b, 1, CRGB::Blue,  Sequential,  2);
    */

    /*
    // Left to Right Cyan Wipe
    float b = beatsin16(60) / 65535.0f * FAN_SIZE;
    for (int iFan = 0; iFan < NUM_FANS; iFan++)
      DrawFanPixels(0, b, CRGB::Cyan, LeftRight, iFan);
    */

    /* Right to Left Cyan Wipe
    float b = beatsin16(60) / 65535.0f * FAN_SIZE;
    for (int iFan = 0; iFan < NUM_FANS; iFan++)
      DrawFanPixels(0, b, CRGB::Cyan, RightLeft, iFan);
    */

    /*
    // Bottom Up Green Wipe
    float b = beatsin16(60) / 65535.0f * NUM_LEDS;
      DrawFanPixels(0, b, CRGB::Green, BottomUp);
    */

    /*
    // Top Down Green Wipe
    float b = beatsin16(60) / 65535.0f * NUM_LEDS;
        DrawFanPixels(0, b, CRGB::Green, TopDown);    
    */

    /*
    // Simple Color Cycle
    static byte hue = 0;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, CHSV(hue, 255, 255));
    hue += 4;
    */

    /*
    // Sequential Color Rainbows
    static byte basehue = 0;
    byte hue = basehue;
    basehue += 4;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255));
    basehue += 4;
    */

    /*
    // Vertical Rainbow Wipe
    static byte basehue = 0;
    byte hue = basehue;
    basehue += 8;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255), LeftRight);
    */

    /*
    static byte basehue = 0;
    byte hue = basehue;
    basehue += 8;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255), BottomUp);
    */

    /*
    // Rainbow Strip Palette Effect
    static CRGBPalette256 pal(RainbowStripeColors_p);
    static byte baseColor = 0;
    byte hue = baseColor;
    for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, ColorFromPalette(pal, hue += 4), BottomUp);
    baseColor += 1;
    */

    /*
    // vu-style Meter bar
    int b = beatsin16(30) * NUM_LEDS / 65535L;
    static const CRGBPalette256 vuPaletteGreen = vu_gpGreen;
    for (int i = 0; i < b; i++)
      DrawFanPixels(i, 1, ColorFromPalette(vuPaletteGreen, (int)(255 * i / NUM_LEDS)), BottomUp);
    */

    /*
    // Sequential Fire Fans
    static FireEffect fire(NUM_LEDS, 20, 100, 3, NUM_LEDS, true, false);
    fire.DrawFire();
    */

    /*
    // Bottom Up Fire Effect with extra sparking on first fan only
    static FireEffect fire(NUM_LEDS, 20, 140, 3, FAN_SIZE, true, false);
    fire.DrawFire(BottomUp);
    */

    // LeftRight (Wide Style) Fire Effect with extra sparking on first fan only

    /*
    static FireEffect fire(NUM_LEDS, 20, 140, 3, FAN_SIZE, true, false);
    fire.DrawFire(LeftRight);
    for (int i = 0; i < FAN_SIZE; i++)
    {
      g_LEDs[i] = g_LEDs[i + 2 * FAN_SIZE];
      g_LEDs[i + FAN_SIZE] = g_LEDs[i + 2 * FAN_SIZE];
    }
    */

   int b = beatsin16(30) * NUM_LEDS / 65535L;
   static const CRGBPalette256 seahawksPalette = gpSeahawks;
   for (int i = 0; i < NUM_LEDS; i++)
      DrawFanPixels(i, 1, ColorFromPalette(seahawksPalette, beat8(64) + (int)(255 * i / NUM_LEDS)), BottomUp);
  

    FastLED.show(g_Brightness);                          //  Show and delay

    EVERY_N_MILLISECONDS(250)
    {
      g_OLED.clearBuffer();
      g_OLED.setCursor(0, g_lineHeight);
      g_OLED.printf("FPS  : %u", FastLED.getFPS());
      g_OLED.setCursor(0, g_lineHeight * 2);
      g_OLED.printf("Power: %u mW", calculate_unscaled_power_mW(g_LEDs, 4));
      g_OLED.setCursor(0, g_lineHeight * 3);
      g_OLED.printf("Brite: %d", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));
      g_OLED.sendBuffer();
    }
    delay(33);
  }
}
Download .txt
gitextract_89p4x3xh/

├── .gitignore
├── Fans/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   ├── bounce.h
│   │   ├── comet.h
│   │   ├── fire.h
│   │   ├── ledgfx.h
│   │   ├── marquee.h
│   │   └── twinkle.h
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       └── main.cpp
├── LED Episode 02/
│   ├── .gitignore
│   ├── .travis.yml
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   ├── src/
│   │   └── main.cpp
│   └── test/
│       └── README
├── LED Episode 03/
│   ├── .gitignore
│   ├── .travis.yml
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   ├── src/
│   │   ├── main.cpp
│   │   └── v1.cpp
│   └── test/
│       └── README
├── LED Episode 06/
│   ├── .gitignore
│   ├── .travis.yml
│   ├── .vscode/
│   │   ├── extensions.json
│   │   ├── settings.json
│   │   └── tasks.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       ├── bounce.h
│       ├── comet.h
│       ├── main.cpp
│       ├── marquee.h
│       └── twinkle.h
├── LED Episode 07/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       ├── bounce.h
│       ├── comet.h
│       ├── main.cpp
│       ├── marquee.h
│       └── twinkle.h
├── LED Episode 08/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       ├── bounce.h
│       ├── comet.h
│       ├── main.cpp
│       ├── marquee.h
│       └── twinkle.h
├── LED Episode 09/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   └── README
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       ├── bounce.h
│       ├── comet.h
│       ├── main.cpp
│       ├── marquee.h
│       └── twinkle.h
├── LED Episode 10/
│   ├── .gitignore
│   ├── .vscode/
│   │   └── extensions.json
│   ├── include/
│   │   ├── bounce.h
│   │   ├── comet.h
│   │   ├── fire.h
│   │   ├── ledgfx.h
│   │   ├── marquee.h
│   │   └── twinkle.h
│   ├── lib/
│   │   └── README
│   ├── platformio.ini
│   └── src/
│       └── main.cpp
└── LED Episode 11/
    ├── .gitignore
    ├── .vscode/
    │   └── extensions.json
    ├── include/
    │   ├── bounce.h
    │   ├── comet.h
    │   ├── fire.h
    │   ├── ledgfx.h
    │   ├── marquee.h
    │   └── twinkle.h
    ├── lib/
    │   └── README
    ├── platformio.ini
    └── src/
        └── main.cpp
Download .txt
SYMBOL INDEX (103 symbols across 44 files)

FILE: Fans/include/bounce.h
  function class (line 33) | class BouncingBallEffect
  function virtual (line 87) | virtual void Draw()

FILE: Fans/include/comet.h
  function DrawComet (line 19) | void DrawComet()

FILE: Fans/include/fire.h
  function class (line 19) | class FireEffect
  function virtual (line 58) | virtual ~FireEffect()
  function virtual (line 63) | virtual void DrawFire(PixelOrder order = Sequential)

FILE: Fans/include/ledgfx.h
  function RandomFloat (line 32) | inline float RandomFloat()
  function UnixTime (line 38) | inline double UnixTime()
  function CRGB (line 50) | CRGB ColorFraction(CRGB colorIn, float fraction)
  function DrawPixels (line 56) | void DrawPixels(float fPos, float count, CRGB color)
  function DEFINE_GRADIENT_PALETTE (line 88) | DEFINE_GRADIENT_PALETTE( vu_gpGreen )

FILE: Fans/include/marquee.h
  function DrawMarquee (line 21) | void DrawMarquee()
  function DrawMarqueeMirrored (line 43) | void DrawMarqueeMirrored()

FILE: Fans/include/twinkle.h
  function DrawTwinkle (line 30) | void DrawTwinkle()

FILE: Fans/src/main.cpp
  function setup (line 47) | void setup()
  function loop (line 67) | void loop()

FILE: LED Episode 02/src/main.cpp
  function hueToRGB (line 12) | void hueToRGB(uint8_t hue, uint8_t brightness)
  function setup (line 59) | void setup()
  function loop (line 77) | void loop()

FILE: LED Episode 03/src/main.cpp
  function setup (line 6) | void setup()
  function loop (line 46) | void loop()

FILE: LED Episode 03/src/v1.cpp
  function setup (line 10) | void setup()
  function loop (line 20) | void loop()

FILE: LED Episode 06/src/bounce.h
  function class (line 56) | class BouncingBallEffect
  function virtual (line 120) | virtual void Draw()

FILE: LED Episode 06/src/comet.h
  function DrawComet (line 21) | void DrawComet()

FILE: LED Episode 06/src/main.cpp
  function FramesPerSecond (line 41) | double FramesPerSecond(double seconds)
  function DrawPixels (line 48) | void DrawPixels(double fPos, double count, CRGB color)
  function setup (line 91) | void setup()
  function loop (line 111) | void loop()

FILE: LED Episode 06/src/marquee.h
  function DrawMarquee (line 21) | void DrawMarquee()
  function DrawMarqueeMirrored (line 43) | void DrawMarqueeMirrored()

FILE: LED Episode 06/src/twinkle.h
  function DrawTwinkle (line 29) | void DrawTwinkle()
  function DrawTwinkle2 (line 41) | void DrawTwinkle2()

FILE: LED Episode 07/src/bounce.h
  function class (line 37) | class BouncingBallEffect
  function virtual (line 98) | virtual void Draw()

FILE: LED Episode 07/src/comet.h
  function DrawComet (line 21) | void DrawComet()

FILE: LED Episode 07/src/main.cpp
  function CRGB (line 44) | CRGB ColorFraction(CRGB colorIn, float fraction)
  function DrawPixels (line 50) | void DrawPixels(float fPos, float count, CRGB color)
  function DrawMarqueeComparison (line 82) | void DrawMarqueeComparison()
  function setup (line 97) | void setup()
  function loop (line 117) | void loop()

FILE: LED Episode 07/src/marquee.h
  function DrawMarquee (line 21) | void DrawMarquee()
  function DrawMarqueeMirrored (line 43) | void DrawMarqueeMirrored()

FILE: LED Episode 07/src/twinkle.h
  function DrawTwinkle (line 29) | void DrawTwinkle()
  function DrawTwinkle2 (line 41) | void DrawTwinkle2()

FILE: LED Episode 08/src/bounce.h
  function class (line 35) | class BouncingBallEffect
  function virtual (line 96) | virtual void Draw()

FILE: LED Episode 08/src/comet.h
  function DrawComet (line 21) | void DrawComet()

FILE: LED Episode 08/src/main.cpp
  function CRGB (line 43) | CRGB ColorFraction(const CRGB colorIn, float fraction)
  function DrawPixels (line 54) | void DrawPixels(float fPos, float count, CRGB color)
  function setup (line 89) | void setup()
  function loop (line 110) | void loop()

FILE: LED Episode 08/src/marquee.h
  function DrawMarqueeComparison (line 20) | void DrawMarqueeComparison()
  function DrawMarquee (line 34) | void DrawMarquee()
  function DrawMarqueeMirrored (line 56) | void DrawMarqueeMirrored()

FILE: LED Episode 08/src/twinkle.h
  function DrawTwinkle (line 29) | void DrawTwinkle()
  function DrawTwinkle2 (line 41) | void DrawTwinkle2()

FILE: LED Episode 09/src/bounce.h
  function class (line 37) | class BouncingBallEffect
  function virtual (line 98) | virtual void Draw()

FILE: LED Episode 09/src/comet.h
  function DrawComet (line 21) | void DrawComet()

FILE: LED Episode 09/src/main.cpp
  function CRGB (line 44) | CRGB ColorFraction(CRGB colorIn, float fraction)
  function DrawPixels (line 50) | void DrawPixels(float fPos, float count, CRGB color)
  function DrawMarqueeComparison (line 82) | void DrawMarqueeComparison()
  function setup (line 97) | void setup()
  function loop (line 117) | void loop()

FILE: LED Episode 09/src/marquee.h
  function DrawMarquee (line 21) | void DrawMarquee()
  function DrawMarqueeMirrored (line 43) | void DrawMarqueeMirrored()

FILE: LED Episode 09/src/twinkle.h
  function DrawTwinkle (line 29) | void DrawTwinkle()
  function DrawTwinkle2 (line 41) | void DrawTwinkle2()

FILE: LED Episode 10/include/bounce.h
  function class (line 33) | class BouncingBallEffect
  function virtual (line 87) | virtual void Draw()

FILE: LED Episode 10/include/comet.h
  function DrawComet (line 19) | void DrawComet()

FILE: LED Episode 10/include/fire.h
  function class (line 21) | class FireEffectSmooth
  function virtual (line 56) | virtual ~FireEffectSmooth()               // Because we have a virtual f...
  function DrawFire (line 61) | void DrawFire()
  function class (line 94) | class ClassicFireEffect
  function virtual (line 135) | virtual ~ClassicFireEffect()
  function virtual (line 140) | virtual void DrawFire()

FILE: LED Episode 10/include/ledgfx.h
  function RandomFloat (line 32) | inline float RandomFloat()
  function UnixTime (line 38) | inline double UnixTime()
  function CRGB (line 50) | CRGB ColorFraction(CRGB colorIn, float fraction)
  function DrawPixels (line 56) | void DrawPixels(float fPos, float count, CRGB color)

FILE: LED Episode 10/include/marquee.h
  function DrawMarquee (line 21) | void DrawMarquee()
  function DrawMarqueeMirrored (line 43) | void DrawMarqueeMirrored()

FILE: LED Episode 10/include/twinkle.h
  function DrawTwinkle (line 30) | void DrawTwinkle()

FILE: LED Episode 10/src/main.cpp
  function setup (line 42) | void setup()
  function DrawMarqueeComparison (line 62) | void DrawMarqueeComparison()
  function loop (line 76) | void loop()

FILE: LED Episode 11/include/bounce.h
  function class (line 33) | class BouncingBallEffect
  function virtual (line 87) | virtual void Draw()

FILE: LED Episode 11/include/comet.h
  function DrawComet (line 19) | void DrawComet()

FILE: LED Episode 11/include/fire.h
  function class (line 19) | class FireEffect
  function virtual (line 58) | virtual ~FireEffect()
  function virtual (line 63) | virtual void DrawFire(PixelOrder order = Sequential)

FILE: LED Episode 11/include/ledgfx.h
  type PixelOrder (line 29) | enum PixelOrder
  function DEFINE_GRADIENT_PALETTE (line 39) | DEFINE_GRADIENT_PALETTE( vu_gpGreen )

FILE: LED Episode 11/include/marquee.h
  function DrawMarquee (line 21) | void DrawMarquee()
  function DrawMarqueeMirrored (line 43) | void DrawMarqueeMirrored()

FILE: LED Episode 11/include/twinkle.h
  function DrawTwinkle (line 30) | void DrawTwinkle()

FILE: LED Episode 11/src/main.cpp
  function setup (line 46) | void setup()
  function DrawMarqueeComparison (line 66) | void DrawMarqueeComparison()
  function loop (line 80) | void loop()
Condensed preview — 94 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (161K chars).
[
  {
    "path": ".gitignore",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "Fans/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "Fans/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "Fans/include/bounce.h",
    "chars": 4013,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "Fans/include/comet.h",
    "chars": 1027,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "Fans/include/fire.h",
    "chars": 3376,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "Fans/include/ledgfx.h",
    "chars": 4898,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "Fans/include/marquee.h",
    "chars": 1466,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "Fans/include/twinkle.h",
    "chars": 824,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "Fans/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "Fans/platformio.ini",
    "chars": 665,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "Fans/src/main.cpp",
    "chars": 6358,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "LED Episode 02/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "LED Episode 02/.travis.yml",
    "chars": 1557,
    "preview": "# Continuous Integration (CI) is the practice, in software\n# engineering, of merging all developer working copies with a"
  },
  {
    "path": "LED Episode 02/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "LED Episode 02/include/README",
    "chars": 1386,
    "preview": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro defin"
  },
  {
    "path": "LED Episode 02/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "LED Episode 02/platformio.ini",
    "chars": 440,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "LED Episode 02/src/main.cpp",
    "chars": 2245,
    "preview": "#include <Arduino.h>\n\n#define RED_PIN   16\n#define GREEN_PIN 17\n#define BLUE_PIN  18\n\n// Courtesy http://www.instructabl"
  },
  {
    "path": "LED Episode 02/test/README",
    "chars": 490,
    "preview": "\nThis directory is intended for PIO Unit Testing and project tests.\n\nUnit Testing is a software testing method by which "
  },
  {
    "path": "LED Episode 03/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "LED Episode 03/.travis.yml",
    "chars": 1557,
    "preview": "# Continuous Integration (CI) is the practice, in software\n# engineering, of merging all developer working copies with a"
  },
  {
    "path": "LED Episode 03/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "LED Episode 03/include/README",
    "chars": 1386,
    "preview": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro defin"
  },
  {
    "path": "LED Episode 03/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "LED Episode 03/platformio.ini",
    "chars": 512,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "LED Episode 03/src/main.cpp",
    "chars": 1651,
    "preview": "#include <Arduino.h>\n#include <U8g2lib.h>\n\nU8G2_SSD1306_128X64_NONAME_F_SW_I2C g_TFT(U8G2_R2, 15, 4, 16);\n\nvoid setup()\n"
  },
  {
    "path": "LED Episode 03/src/v1.cpp",
    "chars": 644,
    "preview": "#include <Arduino.h>\n#include <U8g2lib.h>\n\n#define DISPLAY_CLOCK_PIN   15\n#define DISPLAY_DATA_PIN    4\n#define DISPLAY_"
  },
  {
    "path": "LED Episode 03/test/README",
    "chars": 490,
    "preview": "\nThis directory is intended for PIO Unit Testing and project tests.\n\nUnit Testing is a software testing method by which "
  },
  {
    "path": "LED Episode 06/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "LED Episode 06/.travis.yml",
    "chars": 1557,
    "preview": "# Continuous Integration (CI) is the practice, in software\n# engineering, of merging all developer working copies with a"
  },
  {
    "path": "LED Episode 06/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "LED Episode 06/.vscode/settings.json",
    "chars": 60,
    "preview": "{\n    \"files.associations\": {\n        \"*.tcc\": \"cpp\"\n    }\n}"
  },
  {
    "path": "LED Episode 06/.vscode/tasks.json",
    "chars": 192,
    "preview": "{\n\t\"version\": \"2.0.0\",\n\t\"tasks\": [\n\t\t{\n\t\t\t\"type\": \"PlatformIO\",\n\t\t\t\"task\": \"Build\",\n\t\t\t\"problemMatcher\": [\n\t\t\t\t\"$platfor"
  },
  {
    "path": "LED Episode 06/include/README",
    "chars": 1386,
    "preview": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro defin"
  },
  {
    "path": "LED Episode 06/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "LED Episode 06/platformio.ini",
    "chars": 665,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "LED Episode 06/src/bounce.h",
    "chars": 4655,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 06/src/comet.h",
    "chars": 1012,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 06/src/main.cpp",
    "chars": 3973,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "LED Episode 06/src/marquee.h",
    "chars": 1376,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 06/src/twinkle.h",
    "chars": 1029,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 07/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "LED Episode 07/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "LED Episode 07/include/README",
    "chars": 1386,
    "preview": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro defin"
  },
  {
    "path": "LED Episode 07/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "LED Episode 07/platformio.ini",
    "chars": 665,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "LED Episode 07/src/bounce.h",
    "chars": 4276,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 07/src/comet.h",
    "chars": 1012,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 07/src/main.cpp",
    "chars": 4558,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "LED Episode 07/src/marquee.h",
    "chars": 1376,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 07/src/twinkle.h",
    "chars": 1029,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 08/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "LED Episode 08/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "LED Episode 08/include/README",
    "chars": 1386,
    "preview": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro defin"
  },
  {
    "path": "LED Episode 08/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "LED Episode 08/platformio.ini",
    "chars": 665,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "LED Episode 08/src/bounce.h",
    "chars": 4180,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 08/src/comet.h",
    "chars": 1012,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 08/src/main.cpp",
    "chars": 4713,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "LED Episode 08/src/marquee.h",
    "chars": 1668,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 08/src/twinkle.h",
    "chars": 1029,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 09/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "LED Episode 09/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "LED Episode 09/include/README",
    "chars": 1386,
    "preview": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro defin"
  },
  {
    "path": "LED Episode 09/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "LED Episode 09/platformio.ini",
    "chars": 665,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "LED Episode 09/src/bounce.h",
    "chars": 4276,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 09/src/comet.h",
    "chars": 1012,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 09/src/main.cpp",
    "chars": 4259,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "LED Episode 09/src/marquee.h",
    "chars": 1376,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 09/src/twinkle.h",
    "chars": 1029,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 10/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "LED Episode 10/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "LED Episode 10/include/bounce.h",
    "chars": 4013,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 10/include/comet.h",
    "chars": 1027,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 10/include/fire.h",
    "chars": 6718,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 10/include/ledgfx.h",
    "chars": 2125,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "LED Episode 10/include/marquee.h",
    "chars": 1466,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 10/include/twinkle.h",
    "chars": 824,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 10/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "LED Episode 10/platformio.ini",
    "chars": 665,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "LED Episode 10/src/main.cpp",
    "chars": 3658,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "LED Episode 11/.gitignore",
    "chars": 94,
    "preview": ".pio\n.vscode/.browse.c_cpp.db*\n.vscode/c_cpp_properties.json\n.vscode/launch.json\n.vscode/ipch\n"
  },
  {
    "path": "LED Episode 11/.vscode/extensions.json",
    "chars": 197,
    "preview": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json form"
  },
  {
    "path": "LED Episode 11/include/bounce.h",
    "chars": 4013,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 11/include/comet.h",
    "chars": 1027,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 11/include/fire.h",
    "chars": 3376,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 11/include/ledgfx.h",
    "chars": 5378,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  },
  {
    "path": "LED Episode 11/include/marquee.h",
    "chars": 1466,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 11/include/twinkle.h",
    "chars": 824,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer."
  },
  {
    "path": "LED Episode 11/lib/README",
    "chars": 1037,
    "preview": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries a"
  },
  {
    "path": "LED Episode 11/platformio.ini",
    "chars": 665,
    "preview": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom uploa"
  },
  {
    "path": "LED Episode 11/src/main.cpp",
    "chars": 6542,
    "preview": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer."
  }
]

About this extraction

This page contains the full source code of the davepl/DavesGarageLEDSeries GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 94 files (146.1 KB), approximately 42.8k tokens, and a symbol index with 103 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!