[
  {
    "path": ".gitignore",
    "content": "\n"
  },
  {
    "path": "Fans/.gitignore",
    "content": ".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",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "Fans/include/bounce.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        bounce.h     \n//\n// Description:\n//\n//      Bouncing Ball effect on an LED strip\n//\n// History:     Oct-04-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nusing namespace std;\n#include <vector>\n\n#include \"ledgfx.h\"\n\nstatic const CRGB ballColors [] =\n{\n    CRGB::Green,\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Orange,\n    CRGB::Indigo\n};\n\nclass BouncingBallEffect\n{\n  private:\n\n    double InitialBallSpeed(double height) const\n    {\n        return sqrt(-2 * Gravity * height);         // Because MATH!\n    }\n\n    size_t  _cLength;           \n    size_t  _cBalls;\n    byte    _fadeRate;\n    bool    _bMirrored;\n\n    const double Gravity = -9.81;                   // Because PHYSICS!\n    const double StartHeight = 1;                   // Drop balls from max height initially\n    const double ImpactVelocity = InitialBallSpeed(StartHeight);\n    const double SpeedKnob = 4.0;                   // Higher values will slow the effect\n\n    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;\n    vector<CRGB>   Colors;\n\n  public:\n\n    // BouncingBallEffect\n    //\n    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the\n    // balls should be drawn mirrored from each side.\n\n    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)\n        : _cLength(cLength - 1),\n          _cBalls(ballCount),\n          _fadeRate(fade),\n          _bMirrored(bMirrored),\n          ClockTimeAtLastBounce(ballCount),\n          Height(ballCount),\n          BallSpeed(ballCount),\n          Dampening(ballCount),\n          Colors(ballCount)\n    {\n        for (size_t i = 0; i < ballCount; i++)\n        {\n            Height[i]                = StartHeight;                 // Current Ball Height\n            ClockTimeAtLastBounce[i] = UnixTime();                  // When ball last hit ground state\n            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball\n            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch\n            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];\n        }\n    }\n\n    // Draw\n    //\n    // Draw each of the balls.  When any ball settles with too little energy, it it \"kicked\" to restart it\n\n    virtual void Draw()\n    {\n        if (_fadeRate != 0)\n        {\n            for (size_t i = 0; i < _cLength; i++)\n                FastLED.leds()[i].fadeToBlackBy(_fadeRate);\n        }\n        else\n            FastLED.clear();\n        \n        // Draw each of the balls\n\n        for (size_t i = 0; i < _cBalls; i++)\n        {\n            double TimeSinceLastBounce = (UnixTime() - ClockTimeAtLastBounce[i]) / SpeedKnob;\n\n            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration\n            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;\n\n            // Ball hits ground - bounce!\n            if (Height[i] < 0)\n            {\n                Height[i] = 0;\n                BallSpeed[i] = Dampening[i] * BallSpeed[i];\n                ClockTimeAtLastBounce[i] = Time();\n\n                if (BallSpeed[i] < 0.01)\n                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];\n            }\n\n            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);\n\n            FastLED.leds()[position]   += Colors[i];\n            FastLED.leds()[position+1] += Colors[i];\n\n            if (_bMirrored)\n            {\n                FastLED.leds()[_cLength - 1 - position] += Colors[i];\n                FastLED.leds()[_cLength - position]     += Colors[i];\n            }\n        }\n        delay(20);\n    }\n};"
  },
  {
    "path": "Fans/include/comet.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:                  \n//\n// Description:\n//\n//   \n//\n// History:     Sep-28-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nvoid DrawComet()\n{\n    const byte fadeAmt = 128;\n    const int cometSize = 5;\n    const int deltaHue  = 4;\n\n    static byte hue = HUE_RED;\n    static int iDirection = 1;\n    static int iPos = 0;\n\n    hue += deltaHue;\n\n    iPos += iDirection;\n    if (iPos == (FastLED.count() - cometSize) || iPos == 0)\n        iDirection *= -1;\n    \n    for (int i = 0; i < cometSize; i++)\n        FastLED.leds()[iPos + i].setHue(hue);\n    \n    // Randomly fade the LEDs\n    for (int j = 0; j < FastLED.count(); j++)\n        if (random(10) > 5)\n            FastLED.leds()[j] = FastLED.leds()[j].fadeToBlackBy(fadeAmt);  \n\n    delay(30);\n}"
  },
  {
    "path": "Fans/include/fire.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        fire.h                    \n//\n// Description: A realistic flame simulation for LED strips\n//\n// History:     Oct-23-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include \"ledgfx.h\"\n\nclass FireEffect\n{\n  protected:\n    int     Size;               // How many pixels the flame is total\n    int     Cooling;            // Rate at which the pixels cool off\n    int     Sparks;             // How many sparks will be attempted each frame\n    int     SparkHeight;        // If created, max height for a spark\n    int     Sparking;           // Probability of a spark each attempt\n    bool    bReversed;          // If reversed we draw from 0 outwards\n    bool    bMirrored;          // If mirrored we split and duplicate the drawing\n\n    byte  * heat;\n\n    // When diffusing the fire upwards, these control how much to blend in from the cells below (ie: downward neighbors)\n    // You can tune these coefficients to control how quickly and smoothly the fire spreads\n\n    static const byte BlendSelf = 2;\n    static const byte BlendNeighbor1 = 3;\n    static const byte BlendNeighbor2 = 2;\n    static const byte BlendNeighbor3 = 1;\n    static const byte BlendTotal = (BlendSelf + BlendNeighbor1 + BlendNeighbor2 + BlendNeighbor3);\n\n  public:\n\n    FireEffect(int size, int cooling = 20, int sparking = 100, int sparks = 3, int sparkHeight = 4, bool breversed = true, bool bmirrored = true)\n        : Size(size),\n          Cooling(cooling),\n          Sparks(sparks),\n          SparkHeight(sparkHeight),\n          Sparking(sparking),\n          bReversed(breversed),\n          bMirrored(bmirrored)\n    {\n        if (bMirrored)\n            Size = Size / 2;\n\n        heat = new byte[size] { 0 };\n    }\n\n    virtual ~FireEffect()\n    {\n        delete [] heat;\n    }\n\n    virtual void DrawFire(PixelOrder order = Sequential)\n    {\n        // First cool each cell by a litle bit\n        for (int i = 0; i < Size; i++)\n            heat[i] = max(0L, heat[i] - random(0, ((Cooling * 10) / Size) + 2));\n\n        // Next drift heat up and diffuse it a little bit\n        for (int i = 0; i < Size; i++)\n            heat[i] = (heat[i] * BlendSelf +\n                       heat[(i + 1) % Size] * BlendNeighbor1 +\n                       heat[(i + 2) % Size] * BlendNeighbor2 +\n                       heat[(i + 3) % Size] * BlendNeighbor3)\n                      / BlendTotal;\n\n        // Randomly ignite new sparks down in the flame kernel\n\n        for (int i = 0 ; i < Sparks; i++)\n        {\n            if (random(255) < Sparking)\n            {\n                int y = Size - 1 - random(SparkHeight);\n                heat[y] = heat[y] + random(160, 255);       // Can roll over which actually looks good!\n            }\n        }\n\n        // Finally, convert heat to a color\n\n        for (int i = 0; i < Size; i++)\n        {\n            CRGB color = HeatColor(heat[i]);\n            int j = bReversed ? (Size - 1 - i) : i;\n            DrawFanPixels(j, 1, color, order);\n            if (bMirrored)\n                DrawFanPixels(!bReversed ? (2 * Size - 1 - i) : Size + i, 1, color, order);\n        }\n    }\n};"
  },
  {
    "path": "Fans/include/ledgfx.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        ledgfx.h\n//\n// Description:\n//\n//   LED Drawing Routines for Dave's Garage Tutorial series\n//\n// History:     OCt-18-2020     davepl      Created from main.cpp code\n//---------------------------------------------------------------------------\n\n#pragma once\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include <sys/time.h>                   // For time-of-day\n\n// Utility Macros\n\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))\n#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)\n\n//inline double RandomDouble()\n//{\n//    return random(UINT32_MAX) / (double) UINT32_MAX;\n//}\n\ninline float RandomFloat()\n{\n    float r = random(1000000L) / 1000000.0f;\n    return r;\n}\n\ninline double UnixTime()\n{\n    timeval tv = { 0 };\n    gettimeofday(&tv, nullptr);\n    return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);\n}\n\n// FractionalColor\n//\n// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we\n// want to improve the color math or do color correction all in one location at a later date.\n\nCRGB ColorFraction(CRGB colorIn, float fraction)\n{\n  fraction = min(1.0f, fraction);\n  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));\n}\n\nvoid DrawPixels(float fPos, float count, CRGB color)\n{\n  // Calculate how much the first pixel will hold\n  float availFirstPixel = 1.0f - (fPos - (long)(fPos));\n  float amtFirstPixel = min(availFirstPixel, count);\n  float remaining = min(count, FastLED.size()-fPos);\n  int iPos = fPos;\n\n  // Blend (add) in the color of the first partial pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);\n    remaining -= amtFirstPixel;\n  }\n\n  // Now draw any full pixels in the middle\n\n  while (remaining > 1.0f)\n  {\n    FastLED.leds()[iPos++] += color;\n    remaining--;\n  }\n\n  // Draw tail pixel, up to a single full pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos] += ColorFraction(color, remaining);\n  }\n}\n\nDEFINE_GRADIENT_PALETTE( vu_gpGreen ) \n{\n      0,     0,   4,   0,   // near black green\n     64,     0, 255,   0,   // green\n    128,   255, 255,   0,   // yellow\n    192,   255,   0,   0,   // red\n    255,   255,   0,   0    // red\n};\n\nDEFINE_GRADIENT_PALETTE( vu_gpSeahawks ) \n{\n    0,       0,     0,   4,      \n    64,      3,    38,  58,      \n   128,      0,    21,  50,      \n   192,     78,   167,   1,      \n   255,     54,    87, 140,      \n};\n\nstatic const int FanPixelsVertical[FAN_SIZE] =\n{\n  0, 1, 15, 2, 14, 3, 13, 4, 12, 5, 11, 6, 10, 7, 9, 8\n};\n\nstatic const int FanPixelsHorizontal[FAN_SIZE] =\n{\n  3, 4, 2, 5, 1, 6, 0, 7, 15, 8, 14, 9, 13, 10, 12, 11\n};\n\nenum PixelOrder\n{\n  Sequential = 0,\n  Reverse    = 1,\n  BottomUp   = 2,\n  TopDown    = 4,\n  LeftRight  = 8,\n  RightLeft  = 16\n};\n\nint GetFanPixelOrder(int iPos, PixelOrder order = Sequential)\n{\n  while (iPos < 0)\n    iPos += FAN_SIZE;\n\n  int offset = (iPos + LED_FAN_OFFSET) % FAN_SIZE;  // Offset within this fan\n  int roffset = (iPos + FAN_SIZE - LED_FAN_OFFSET) % FAN_SIZE;  // Offset within this fan\n\n  int fanBase = iPos - (iPos % FAN_SIZE);           // Round down to previous multiple of FAN_SIZE\n  \n  switch (order)\n  {\n    case BottomUp:\n      return fanBase + FAN_SIZE - 1 - (FanPixelsVertical[iPos % FAN_SIZE] + LED_FAN_OFFSET) % FAN_SIZE;\n    \n    case TopDown:\n      return NUM_LEDS - 1 - (fanBase + (FanPixelsVertical[FAN_SIZE - 1 - (iPos % FAN_SIZE)] + LED_FAN_OFFSET) % FAN_SIZE);\n\n    case LeftRight:\n      return fanBase + (FanPixelsHorizontal[ iPos % FAN_SIZE ] + LED_FAN_OFFSET - 1) % FAN_SIZE;\n    \n    case RightLeft:\n      return fanBase + (FanPixelsHorizontal[FAN_SIZE - 1 - (iPos % FAN_SIZE) ] + LED_FAN_OFFSET - 1) % FAN_SIZE;\n\n   case Reverse:\n      return fanBase + FAN_SIZE - 1 - roffset;\n\n    case Sequential:\n    default:\n      return fanBase + offset;\n  }\n}\n\nvoid DrawFanPixels(float fPos, float count, CRGB color, PixelOrder order = Sequential, int iFan = 0)\n{\n  fPos += iFan * FAN_SIZE;\n\n  // Calculate how much the first pixel will hold\n  float availFirstPixel = 1.0f - (fPos - (long)(fPos));\n  float amtFirstPixel = min(availFirstPixel, count);\n  float remaining = min(count, FastLED.size()-fPos);\n  int iPos = fPos;\n\n  // Blend (add) in the color of the first partial pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[GetFanPixelOrder(iPos++, order)] += ColorFraction(color, amtFirstPixel);\n    remaining -= amtFirstPixel;\n  }\n\n  // Now draw any full pixels in the middle\n\n  while (remaining > 1.0f)\n  {\n    FastLED.leds()[GetFanPixelOrder(iPos++, order)] += color;\n    remaining--;\n  }\n\n  // Draw tail pixel, up to a single full pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[GetFanPixelOrder(iPos, order)] += ColorFraction(color, remaining);\n  }\n}\n"
  },
  {
    "path": "Fans/include/marquee.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        marque.h\n//\n// Description:\n//\n//   Draws a theatre-style marquee\n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#include <U8g2lib.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n\nvoid DrawMarquee()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < FastLED.count(); i ++)\n        FastLED.leds()[i] = c.setHue(k+=8);\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < FastLED.count() - 1; i += 5)\n    {\n        FastLED.leds()[i] = CRGB::Black;\n    }\n    delay(50);\n}\n\nvoid DrawMarqueeMirrored()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < (FastLED.count() + 1) / 2; i ++)\n    {\n        FastLED.leds()[i] = c.setHue(k);\n        FastLED.leds()[FastLED.count() - 1 - i] = c.setHue(k);\n        k+= 8;\n    }\n\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < FastLED.count() / 2; i += 5)\n    {\n        FastLED.leds()[i] = CRGB::Black;\n        FastLED.leds()[FastLED.count() - 1 - i] = CRGB::Black;\n    }   \n\n    delay(50);\n}\n\n\n\n"
  },
  {
    "path": "Fans/include/twinkle.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        \n//\n// Description:\n//\n//   \n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include \"ledgfx.h\"\n\nstatic const CRGB TwinkleColors [] = \n{\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Purple,\n    CRGB::Green,\n    CRGB::Yellow\n};\n\nvoid DrawTwinkle()\n{\n    static int passCount = 0;\n    if (passCount++ == FastLED.count()/4)\n    {\n        passCount = 0;\n        FastLED.clear(false);\n    }\n    FastLED.leds()[random(FastLED.count())] = TwinkleColors[random(0, ARRAYSIZE(TwinkleColors))];\n    delay(200);       \n}"
  },
  {
    "path": "Fans/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "Fans/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:heltec_wifi_kit_32]\nplatform = espressif32\nboard = heltec_wifi_kit_32\nframework = arduino\nbuild_flags = -Wno-unused-variable\nupload_port = /dev/cu.SLAB_USBtoUART\nmonitor_port = /dev/cu.SLAB_USBtoUART\nmonitor_speed = 115200\n\nlib_deps = U8g2\n           FastLED\n           \n        \n           \n\n\n\n"
  },
  {
    "path": "Fans/src/main.cpp",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        LED Episode 10\n//\n// Description:\n//\n//   Draws sample effects on a an addressable strip using FastLED\n//\n// History:     Sep-15-2020     davepl      Created\n//              Oct-05-2020     davepl      Revised for Episode 07\n//              Oct-11-2020     davepl      Revised for Episode 08\n//              Oct-16-2020     davepl      Revised for Episode 09\n//              Oct-23-2020     davepl      Revised for Episode 10\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>            // Arduino Framework\n#include <U8g2lib.h>            // For text on the little on-chip OLED\n#define FASTLED_INTERNAL        // Suppress build banner\n#include <FastLED.h>\n\n#define OLED_CLOCK  15          // Pins for the OLED display\n#define OLED_DATA   4\n#define OLED_RESET  16\n\n#define FAN_SIZE       16       // How many pixels per fan\n#define NUM_FANS       3        // Number of fans in the strans\n#define LED_FAN_OFFSET 4        // How far from 12 o'clock first pixel is\n#define NUM_LEDS       (FAN_SIZE*NUM_FANS)\n#define LED_PIN        5\n\nCRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED\n\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);\nint g_lineHeight = 0;\nint g_Brightness = 255;         // 0-255 LED brightness scale\nint g_PowerLimit = 3000;         // 900mW Power Limit\n\n#include \"ledgfx.h\"\n#include \"comet.h\"\n#include \"marquee.h\"\n#include \"twinkle.h\"\n#include \"fire.h\"\n\n\nvoid setup() \n{\n  pinMode(LED_BUILTIN, OUTPUT);\n  pinMode(LED_PIN, OUTPUT);\n\n  Serial.begin(115200);\n  while (!Serial) { }\n  Serial.println(\"ESP32 Startup\");\n\n  g_OLED.begin();\n  g_OLED.clear();\n  g_OLED.setFont(u8g2_font_profont15_tf);\n  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total\n\n  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library\n  FastLED.setBrightness(g_Brightness);\n  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle\n  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled\n}\n\nvoid loop() \n{\n  bool bLED = 0;\n\n  while (true)\n  {\n    FastLED.clear();\n   \n    /*\n    // RGB Spinners\n    float b = beat16(60) / 65535.0f * FAN_SIZE;\n    DrawFanPixels(b, 1, CRGB::Red, Sequential, 0);\n    DrawFanPixels(b, 1, CRGB::Green, Sequential, 1);\n    DrawFanPixels(b, 1, CRGB::Blue, Sequential, 2);\n    */\n\n    /*\n    // Left to Right Cyan Wipe\n    float b = beatsin16(60) / 65535.0f * FAN_SIZE;\n    for (int iFan = 0; iFan < NUM_FANS; iFan++)\n        DrawFanPixels(0, b, CRGB::Cyan, LeftRight, iFan);\n    */\n\n    /*\n    // Left to Right Cyan Wipe\n    float b = beatsin16(60) / 65535.0f * FAN_SIZE;\n    for (int iFan = 0; iFan < NUM_FANS; iFan++)\n        DrawFanPixels(0, b, CRGB::Cyan, RightLeft, iFan);\n    */\n\n    /*\n    // Bottom up Green Wipe\n    float b = beatsin16(60) / 65535.0f * NUM_LEDS;\n        DrawFanPixels(0, b, CRGB::Green, BottomUp);\n    */\n   \n    /*\n    // Bottom up Green Wipe\n    float b = beatsin16(60) / 65535.0f * NUM_LEDS;\n        DrawFanPixels(0, b, CRGB::Green, TopDown);\n    */\n\n    /*\n    // Simple Color Cycle\n    static byte hue = 0;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, CHSV(hue, 255, 255));\n    hue += 4;\n    */\n\n    /*\n    // Sequential Rainbows\n    static byte basehue = 0;\n    byte hue = basehue;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255));\n    basehue += 4;\n    */\n\n    /*\n    // Vertical Rainbow Wipe\n    static byte basehue = 0;\n    byte hue = basehue;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, CHSV(hue+=8, 255, 255), BottomUp);\n    basehue += 4;\n    */\n\n    /*\n    // Horizontal Rainbow Stripe\n    static byte basehue = 0;\n    byte hue = basehue;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255), LeftRight);\n    basehue += 8;\n    */\n\n    /*\n    // Rainbow Stripe Palette Effect\n    static CRGBPalette256 pal(RainbowStripeColors_p);\n    static byte baseColor = 0;\n    byte hue = baseColor;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, ColorFromPalette(pal, hue += 4), BottomUp);\n    baseColor += 1;\n    */\n\n    /*   \n    // vu-Style Meter\n    int b = beatsin16(30) * NUM_LEDS / 65535L;\n    static const CRGBPalette256 vuPaletteGreen = vu_gpGreen;\n    for (int i = 0; i < b; i++)\n        DrawFanPixels(i, 1, ColorFromPalette(vuPaletteGreen, (int)(255 * i / NUM_LEDS)), BottomUp);\n    */\n\n    /*\n    // Sequential Fire Fans\n    static FireEffect fire(NUM_LEDS, 20, 100, 3, NUM_LEDS, true, false);\n    fire.DrawFire();\n    */\n\n    /*\n    // Bottom Up Fire Effect with extra sparking on first fan only\n    static FireEffect fire(NUM_LEDS, 20, 140, 3, FAN_SIZE, true, false);\n    fire.DrawFire(BottomUp);\n    */\n\n    /*\n    // LeftRight (Wide) Fire Effect with extra sparking on first fan only\n    static FireEffect fire(NUM_LEDS, 20, 140, 3, FAN_SIZE, true, false);\n    fire.DrawFire(LeftRight);\n    for (int i = 0; i < FAN_SIZE; i++)  // Copy end fan down onto others\n    {\n      g_LEDs[i] = g_LEDs[i + 2 * FAN_SIZE];             \n      g_LEDs[i + FAN_SIZE] = g_LEDs[i + 2 * FAN_SIZE];\n    }\n    */\n\n    int b = beatsin16(30) * NUM_LEDS / 65535L;\n    static const CRGBPalette256 seawhawksPalette = vu_gpSeahawks;\n    for (int i = 0; i < NUM_LEDS; i++)\n        DrawFanPixels(i, 1, ColorFromPalette(seawhawksPalette, beat8(64) + (int)(255 * i / NUM_LEDS)), BottomUp);\n    \n    \n    FastLED.show(g_Brightness);                          //  Show and delay\n\n    EVERY_N_MILLISECONDS(250)\n    {\n      g_OLED.clearBuffer();\n      g_OLED.setCursor(0, g_lineHeight);\n      g_OLED.printf(\"FPS  : %u\", FastLED.getFPS());\n      g_OLED.setCursor(0, g_lineHeight * 2);\n      g_OLED.printf(\"Power: %u mW\", calculate_unscaled_power_mW(g_LEDs, 4));\n      g_OLED.setCursor(0, g_lineHeight * 3);\n      g_OLED.printf(\"Brite: %d\", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));\n      g_OLED.sendBuffer();\n    }\n    delay(33);\n  }\n}\n"
  },
  {
    "path": "LED Episode 02/.gitignore",
    "content": ".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",
    "content": "# Continuous Integration (CI) is the practice, in software\n# engineering, of merging all developer working copies with a shared mainline\n# several times a day < https://docs.platformio.org/page/ci/index.html >\n#\n# Documentation:\n#\n# * Travis CI Embedded Builds with PlatformIO\n#   < https://docs.travis-ci.com/user/integration/platformio/ >\n#\n# * PlatformIO integration with Travis CI\n#   < https://docs.platformio.org/page/ci/travis.html >\n#\n# * User Guide for `platformio ci` command\n#   < https://docs.platformio.org/page/userguide/cmd_ci.html >\n#\n#\n# Please choose one of the following templates (proposed below) and uncomment\n# it (remove \"# \" before each line) or use own configuration according to the\n# Travis CI documentation (see above).\n#\n\n\n#\n# Template #1: General project. Test it using existing `platformio.ini`.\n#\n\n# language: python\n# python:\n#     - \"2.7\"\n#\n# sudo: false\n# cache:\n#     directories:\n#         - \"~/.platformio\"\n#\n# install:\n#     - pip install -U platformio\n#     - platformio update\n#\n# script:\n#     - platformio run\n\n\n#\n# Template #2: The project is intended to be used as a library with examples.\n#\n\n# language: python\n# python:\n#     - \"2.7\"\n#\n# sudo: false\n# cache:\n#     directories:\n#         - \"~/.platformio\"\n#\n# env:\n#     - PLATFORMIO_CI_SRC=path/to/test/file.c\n#     - PLATFORMIO_CI_SRC=examples/file.ino\n#     - PLATFORMIO_CI_SRC=path/to/test/directory\n#\n# install:\n#     - pip install -U platformio\n#     - platformio update\n#\n# script:\n#     - platformio ci --lib=\".\" --board=ID_1 --board=ID_2 --board=ID_N\n"
  },
  {
    "path": "LED Episode 02/.vscode/extensions.json",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "LED Episode 02/include/README",
    "content": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro definitions\nto be shared between several project source files. You request the use of a\nheader file in your project source file (C, C++, etc) located in `src` folder\nby including it, with the C preprocessing directive `#include'.\n\n```src/main.c\n\n#include \"header.h\"\n\nint main (void)\n{\n ...\n}\n```\n\nIncluding a header file produces the same results as copying the header file\ninto each source file that needs it. Such copying would be time-consuming\nand error-prone. With a header file, the related declarations appear\nin only one place. If they need to be changed, they can be changed in one\nplace, and programs that include the header file will automatically use the\nnew version when next recompiled. The header file eliminates the labor of\nfinding and changing all the copies as well as the risk that a failure to\nfind one copy will result in inconsistencies within a program.\n\nIn C, the usual convention is to give header files names that end with `.h'.\nIt is most portable to use only letters, digits, dashes, and underscores in\nheader file names, and at most one dot.\n\nRead more about using header files in official GCC documentation:\n\n* Include Syntax\n* Include Operation\n* Once-Only Headers\n* Computed Includes\n\nhttps://gcc.gnu.org/onlinedocs/cpp/Header-Files.html\n"
  },
  {
    "path": "LED Episode 02/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "LED Episode 02/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:esp32dev]\nplatform = espressif32\nboard = esp32dev\nframework = arduino\n"
  },
  {
    "path": "LED Episode 02/src/main.cpp",
    "content": "#include <Arduino.h>\n\n#define RED_PIN   16\n#define GREEN_PIN 17\n#define BLUE_PIN  18\n\n// Courtesy http://www.instructables.com/id/How-to-Use-an-RGB-LED/?ALLSTEPS\n// function to convert a color to its Red, Green, and Blue components.\n\nuint32_t R, G, B;           // the Red Green and Blue color components\n\nvoid hueToRGB(uint8_t hue, uint8_t brightness)\n{\n    uint16_t scaledHue = (hue * 6);\n    uint8_t segment = scaledHue / 256; // segment 0 to 5 around the\n                                            // color wheel\n    uint16_t segmentOffset =\n      scaledHue - (segment * 256); // position within the segment\n\n    uint8_t complement = 0;\n    uint16_t prev = (brightness * ( 255 -  segmentOffset)) / 256;\n    uint16_t next = (brightness *  segmentOffset) / 256;\n\n    switch(segment ) {\n    case 0:      // red\n        R = brightness;\n        G = next;\n        B = complement;\n    break;\n    case 1:     // yellow\n        R = prev;\n        G = brightness;\n        B = complement;\n    break;\n    case 2:     // green\n        R = complement;\n        G = brightness;\n        B = next;\n    break;\n    case 3:    // cyan\n        R = complement;\n        G = prev;\n        B = brightness;\n    break;\n    case 4:    // blue\n        R = next;\n        G = complement;\n        B = brightness;\n    break;\n   case 5:      // magenta\n    default:\n        R = brightness;\n        G = complement;\n        B = prev;\n    break;\n    }\n}\n\nvoid setup() \n{\n  pinMode(RED_PIN,   OUTPUT);\n  pinMode(GREEN_PIN, OUTPUT);\n  pinMode(BLUE_PIN,  OUTPUT);\n\n  digitalWrite(GREEN_PIN, HIGH);\n  digitalWrite(BLUE_PIN,  HIGH);  \n\n  ledcAttachPin(RED_PIN, 1);        // Assign PWM generator 1 to RED\n  ledcAttachPin(GREEN_PIN, 2);      // Assign PWM generator 2 to GREEN\n  ledcAttachPin(BLUE_PIN, 3);       // Assign PWM generator 3 to BLUE\n\n  ledcSetup(1, 12000, 8);           // Set it to 12kHZ and 8-bit resolution\n  ledcSetup(2, 12000, 8);           // Set it to 12kHZ and 8-bit resolution\n  ledcSetup(3, 12000, 8);           // Set it to 12kHZ and 8-bit resolution\n}\n\nvoid loop() \n{\n  for (int c = 0; c < 256; c++)\n  {\n    hueToRGB(c, 255);             // Convert color to max brightness\n\n    ledcWrite(1, R);\n    ledcWrite(2, G);\n    ledcWrite(3, B);\n\n    delay(100);\n  }\n}"
  },
  {
    "path": "LED Episode 02/test/README",
    "content": "\nThis directory is intended for PIO Unit Testing and project tests.\n\nUnit Testing is a software testing method by which individual units of\nsource code, sets of one or more MCU program modules together with associated\ncontrol data, usage procedures, and operating procedures, are tested to\ndetermine whether they are fit for use. Unit testing finds problems early\nin the development cycle.\n\nMore information about PIO Unit Testing:\n- https://docs.platformio.org/page/plus/unit-testing.html\n"
  },
  {
    "path": "LED Episode 03/.gitignore",
    "content": ".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",
    "content": "# Continuous Integration (CI) is the practice, in software\n# engineering, of merging all developer working copies with a shared mainline\n# several times a day < https://docs.platformio.org/page/ci/index.html >\n#\n# Documentation:\n#\n# * Travis CI Embedded Builds with PlatformIO\n#   < https://docs.travis-ci.com/user/integration/platformio/ >\n#\n# * PlatformIO integration with Travis CI\n#   < https://docs.platformio.org/page/ci/travis.html >\n#\n# * User Guide for `platformio ci` command\n#   < https://docs.platformio.org/page/userguide/cmd_ci.html >\n#\n#\n# Please choose one of the following templates (proposed below) and uncomment\n# it (remove \"# \" before each line) or use own configuration according to the\n# Travis CI documentation (see above).\n#\n\n\n#\n# Template #1: General project. Test it using existing `platformio.ini`.\n#\n\n# language: python\n# python:\n#     - \"2.7\"\n#\n# sudo: false\n# cache:\n#     directories:\n#         - \"~/.platformio\"\n#\n# install:\n#     - pip install -U platformio\n#     - platformio update\n#\n# script:\n#     - platformio run\n\n\n#\n# Template #2: The project is intended to be used as a library with examples.\n#\n\n# language: python\n# python:\n#     - \"2.7\"\n#\n# sudo: false\n# cache:\n#     directories:\n#         - \"~/.platformio\"\n#\n# env:\n#     - PLATFORMIO_CI_SRC=path/to/test/file.c\n#     - PLATFORMIO_CI_SRC=examples/file.ino\n#     - PLATFORMIO_CI_SRC=path/to/test/directory\n#\n# install:\n#     - pip install -U platformio\n#     - platformio update\n#\n# script:\n#     - platformio ci --lib=\".\" --board=ID_1 --board=ID_2 --board=ID_N\n"
  },
  {
    "path": "LED Episode 03/.vscode/extensions.json",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "LED Episode 03/include/README",
    "content": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro definitions\nto be shared between several project source files. You request the use of a\nheader file in your project source file (C, C++, etc) located in `src` folder\nby including it, with the C preprocessing directive `#include'.\n\n```src/main.c\n\n#include \"header.h\"\n\nint main (void)\n{\n ...\n}\n```\n\nIncluding a header file produces the same results as copying the header file\ninto each source file that needs it. Such copying would be time-consuming\nand error-prone. With a header file, the related declarations appear\nin only one place. If they need to be changed, they can be changed in one\nplace, and programs that include the header file will automatically use the\nnew version when next recompiled. The header file eliminates the labor of\nfinding and changing all the copies as well as the risk that a failure to\nfind one copy will result in inconsistencies within a program.\n\nIn C, the usual convention is to give header files names that end with `.h'.\nIt is most portable to use only letters, digits, dashes, and underscores in\nheader file names, and at most one dot.\n\nRead more about using header files in official GCC documentation:\n\n* Include Syntax\n* Include Operation\n* Once-Only Headers\n* Computed Includes\n\nhttps://gcc.gnu.org/onlinedocs/cpp/Header-Files.html\n"
  },
  {
    "path": "LED Episode 03/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "LED Episode 03/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:heltec_wifi_kit_32]\nplatform = espressif32\nboard = heltec_wifi_kit_32\nframework = arduino\nupload_port = /dev/cu.usbserial-4\nlib_deps = U8g2 \n\n"
  },
  {
    "path": "LED Episode 03/src/main.cpp",
    "content": "#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{\n    pinMode(LED_BUILTIN, OUTPUT);           // Set the LED pin to output\n    g_TFT.begin();                          // One-time startup\n    g_TFT.clear();                          // Clear the screen\n    g_TFT.setFont(u8g2_font_profont15_tf);  // Choose a suitable font\n    g_TFT.setCursor(2, 12);                 // Cursor is at bottom of letter, so we need to move down\n    //g_TFT.println(\"Hello World!\");          // Print something\n\n    // Draw a  border around the screen\n\n    g_TFT.drawFrame(0, 0, 128, 64);\n\n    // Draw a moire pattern like its 1984\n    \n    for (int x=0; x<128; x+= 4)\n      g_TFT.drawLine(x, 0, 128-x, 64);\n\n    // Draw some text on the left hand side\n\n    g_TFT.setCursor(5, 32);\n    g_TFT.print(\"Hello\");\n    g_TFT.setCursor(5, 42);\n    g_TFT.print(\"World\");\n\n    // Draw a reticle on the right hand side\n\n    const int reticleY = g_TFT.getHeight() / 2;           // Vertical center\n    const int reticleR = g_TFT.getHeight() / 4 - 2;       // Slightly less than 1/4 screen height\n    const int reticleX = g_TFT.getWidth() - reticleR - 8; // Right-justified with a small margin\n    \n    for (int r = reticleR; r > 0; r -= 3)\n      g_TFT.drawCircle(reticleX, reticleY, r);\n    g_TFT.drawHLine(reticleX - reticleR - 5, reticleY, 2 * reticleR + 10);\n    g_TFT.drawVLine(reticleX, reticleY - reticleR - 5, 2 * reticleR + 10);\n\n    g_TFT.sendBuffer();                     // Ship-It!\n\n}\n\nvoid loop() \n{\n    digitalWrite(LED_BUILTIN, 0);\n    delay(100);\n    digitalWrite(LED_BUILTIN, 1);\n    delay(100);\n}"
  },
  {
    "path": "LED Episode 03/src/v1.cpp",
    "content": "#include <Arduino.h>\n#include <U8g2lib.h>\n\n#define DISPLAY_CLOCK_PIN   15\n#define DISPLAY_DATA_PIN    4\n#define DISPLAY_RESET_PIN   16\n\nU8G2_SSD1306_128X64_NONAME_F_SW_I2C g_OLED(U8G2_R2, DISPLAY_CLOCK_PIN, DISPLAY_DATA_PIN, DISPLAY_RESET_PIN);\n\nvoid setup() \n{\n  g_OLED.begin();\n  g_OLED.clear();\n  g_OLED.setFont(u8g2_font_profont15_tf);  // Choose a suitable font\n  g_OLED.setCursor(2, 12);                 // Cursor is at bottom of letter, so we need to move down\n  g_OLED.print(\"Hello World\");\n  g_OLED.sendBuffer();\n}\n\nvoid loop() \n{\n    digitalWrite(LED_BUILTIN, 0);\n    delay(100);\n    digitalWrite(LED_BUILTIN, 1);\n    delay(100);\n}\n\n\n"
  },
  {
    "path": "LED Episode 03/test/README",
    "content": "\nThis directory is intended for PIO Unit Testing and project tests.\n\nUnit Testing is a software testing method by which individual units of\nsource code, sets of one or more MCU program modules together with associated\ncontrol data, usage procedures, and operating procedures, are tested to\ndetermine whether they are fit for use. Unit testing finds problems early\nin the development cycle.\n\nMore information about PIO Unit Testing:\n- https://docs.platformio.org/page/plus/unit-testing.html\n"
  },
  {
    "path": "LED Episode 06/.gitignore",
    "content": ".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",
    "content": "# Continuous Integration (CI) is the practice, in software\n# engineering, of merging all developer working copies with a shared mainline\n# several times a day < https://docs.platformio.org/page/ci/index.html >\n#\n# Documentation:\n#\n# * Travis CI Embedded Builds with PlatformIO\n#   < https://docs.travis-ci.com/user/integration/platformio/ >\n#\n# * PlatformIO integration with Travis CI\n#   < https://docs.platformio.org/page/ci/travis.html >\n#\n# * User Guide for `platformio ci` command\n#   < https://docs.platformio.org/page/userguide/cmd_ci.html >\n#\n#\n# Please choose one of the following templates (proposed below) and uncomment\n# it (remove \"# \" before each line) or use own configuration according to the\n# Travis CI documentation (see above).\n#\n\n\n#\n# Template #1: General project. Test it using existing `platformio.ini`.\n#\n\n# language: python\n# python:\n#     - \"2.7\"\n#\n# sudo: false\n# cache:\n#     directories:\n#         - \"~/.platformio\"\n#\n# install:\n#     - pip install -U platformio\n#     - platformio update\n#\n# script:\n#     - platformio run\n\n\n#\n# Template #2: The project is intended to be used as a library with examples.\n#\n\n# language: python\n# python:\n#     - \"2.7\"\n#\n# sudo: false\n# cache:\n#     directories:\n#         - \"~/.platformio\"\n#\n# env:\n#     - PLATFORMIO_CI_SRC=path/to/test/file.c\n#     - PLATFORMIO_CI_SRC=examples/file.ino\n#     - PLATFORMIO_CI_SRC=path/to/test/directory\n#\n# install:\n#     - pip install -U platformio\n#     - platformio update\n#\n# script:\n#     - platformio ci --lib=\".\" --board=ID_1 --board=ID_2 --board=ID_N\n"
  },
  {
    "path": "LED Episode 06/.vscode/extensions.json",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "LED Episode 06/.vscode/settings.json",
    "content": "{\n    \"files.associations\": {\n        \"*.tcc\": \"cpp\"\n    }\n}"
  },
  {
    "path": "LED Episode 06/.vscode/tasks.json",
    "content": "{\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\"$platformio\"\n\t\t\t],\n\t\t\t\"group\": \"build\",\n\t\t\t\"label\": \"PlatformIO: Build\"\n\t\t}\n\t]\n}"
  },
  {
    "path": "LED Episode 06/include/README",
    "content": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro definitions\nto be shared between several project source files. You request the use of a\nheader file in your project source file (C, C++, etc) located in `src` folder\nby including it, with the C preprocessing directive `#include'.\n\n```src/main.c\n\n#include \"header.h\"\n\nint main (void)\n{\n ...\n}\n```\n\nIncluding a header file produces the same results as copying the header file\ninto each source file that needs it. Such copying would be time-consuming\nand error-prone. With a header file, the related declarations appear\nin only one place. If they need to be changed, they can be changed in one\nplace, and programs that include the header file will automatically use the\nnew version when next recompiled. The header file eliminates the labor of\nfinding and changing all the copies as well as the risk that a failure to\nfind one copy will result in inconsistencies within a program.\n\nIn C, the usual convention is to give header files names that end with `.h'.\nIt is most portable to use only letters, digits, dashes, and underscores in\nheader file names, and at most one dot.\n\nRead more about using header files in official GCC documentation:\n\n* Include Syntax\n* Include Operation\n* Once-Only Headers\n* Computed Includes\n\nhttps://gcc.gnu.org/onlinedocs/cpp/Header-Files.html\n"
  },
  {
    "path": "LED Episode 06/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "LED Episode 06/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:heltec_wifi_kit_32]\nplatform = espressif32\nboard = heltec_wifi_kit_32\nframework = arduino\nbuild_flags = -Wno-unused-variable\nupload_port = /dev/cu.SLAB_USBtoUART\nmonitor_port = /dev/cu.SLAB_USBtoUART\nmonitor_speed = 115200\n\nlib_deps = U8g2\n           FastLED\n           \n        \n           \n\n\n\n"
  },
  {
    "path": "LED Episode 06/src/bounce.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        bounce.h\n//\n// Description:\n//\n//   Draws bouncing balls on an LED strip\n//\n// History:     Ocf-01-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        \n//\n// Description:\n//\n//   \n//\n// History:     Oct-04-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <sys/time.h>                               // For time-of-day\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nusing namespace std;                        \n#include <vector>                               // Use the C++ resizable array\n\nextern CRGB g_LEDs[];\n\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))   // Count elements in a static array\n\n// BouncingBallEffect\n//\n// Draws a set of N bouncing balls using a simple little kinematics formula.  Clears the section first.\n\nstatic const CRGB ballColors[] =\n{\n  CRGB::Green,\n  CRGB::Red,\n  CRGB::Blue,\n  CRGB::Orange,\n  CRGB::Indigo,\n  CRGB::Cyan\n};\n\nclass BouncingBallEffect \n{\nprivate:\n\n  double InitialBallSpeed(double height) const\n  {\n    return sqrt(-2 * Gravity * height);\n  }\n\n\tsize_t  _cLength;\n\tsize_t  _cBalls;\n  byte    _Fade;\n\tbool    _bMirrored;\n  double  _SpeedKnob;\n\n\tconst double Gravity = -9.81;                                       // Because PHYSICS!\n\tconst double StartHeight = 1;                                       // Drop balls from max height to start\n\tconst double ImpactVelocityStart = InitialBallSpeed(StartHeight);   // Speed for  a\n\tconst double SpeedKnob = 4;                                         // High number will slow effect down\n\n\tvector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening; // When the ball last bounced\n\n  // Time - Return current time in floating form for easier calcs than ms \n\n  double Time() const\n  {\n    timeval tv = { 0 };\n    gettimeofday(&tv, nullptr);\n    return (double)(tv.tv_usec / (double)1000000 +(double)tv.tv_sec);\n  }\n\n\npublic:\n\n  // BouncingBallEffect\n  //\n  // Caller specs strip length, number of balls, persistence level (255 is least), and whether\n  // the balls should be drawn mirrored from each side. \n\n\tBouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false, double SpeedKnob = 4.0)\n\t\t: _cLength(cLength-1),          // Reserve one LED for floating point fraction draw\n\t\t  _cBalls(ballCount),\n      _Fade(fade),\n\t\t  _bMirrored(bMirrored),\n      _SpeedKnob(SpeedKnob),\n      ClockTimeAtLastBounce(ballCount),\n      Height(ballCount),\n      BallSpeed(ballCount),\n      Dampening(ballCount)\n\t{\n\n\t\tfor (size_t i = 0; i < ballCount; i++)\n\t\t{\n\t\t\tHeight[i] \t\t\t\t\t        = StartHeight;                    // Current ball height\n\t\t\tClockTimeAtLastBounce[i]    = Time();                         // When the last time it hit ground was              \n\t\t\tDampening[i] \t\t\t\t        = 1.0 - i / pow(_cBalls, 2);     // Each ball bounces differently\n\t\t\tBallSpeed[i] \t\t\t          = InitialBallSpeed(Height[i]);    // Don't dampen initial launch to they go together\n\t\t}\n\t}\n\n\t// Draw\n\t//\n\t// 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\n\n  virtual void Draw()\n  {\n    if (_Fade)\n    {\n      for (size_t i = 0; i < _cLength; i++)\n      {\n        g_LEDs[i].fadeToBlackBy(_Fade);     \n      }\n    }\n    else\n    {\n      FastLED.clear();\n    }\n\n    // Draw each of the three balls\n    for (size_t i = 0; i < _cBalls; i++)\n    {     \n      double TimeSinceLastBounce = (Time() - ClockTimeAtLastBounce[i]) / _SpeedKnob;\n      Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;\n\n      if (Height[i] < 0)\n      {\n        Height[i] = 0;\n        BallSpeed[i] = Dampening[i] * BallSpeed[i];\n        ClockTimeAtLastBounce[i] = Time();\n\n        if (BallSpeed[i] < 1.0)\n          BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];\n      }\n\n      static const CRGB ballColors[] = { CRGB::Red, CRGB::Blue, CRGB::Green, CRGB::Orange, CRGB::Violet };\n      CRGB color = ballColors[i % ARRAYSIZE(ballColors)];\n\n      double position = (Height[i] * (_cLength - 1.0) / StartHeight);\n      DrawPixels(position, 1, color);\n      if (_bMirrored) \n        DrawPixels(_cLength - 1 - position, 1, color);\n\n    }\n    delay(20);\n  }\n};\n"
  },
  {
    "path": "LED Episode 06/src/comet.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:                  \n//\n// Description:\n//\n//   \n//\n// History:     Sep-28-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nextern CRGB g_LEDs[];\n\nvoid DrawComet()\n{\n    const byte fadeAmt = 128;\n    const int cometSize = 5;\n    const int deltaHue  = 4;\n\n    static byte hue = HUE_RED;\n    static int iDirection = 1;\n    static int iPos = 0;\n\n    hue += deltaHue;\n\n    iPos += iDirection;\n    if (iPos == (NUM_LEDS - cometSize) || iPos == 0)\n        iDirection *= -1;\n    \n    for (int i = 0; i < cometSize; i++)\n        g_LEDs[iPos + i].setHue(hue);\n    \n    // Randomly fade the LEDs\n    for (int j = 0; j < NUM_LEDS; j++)\n        if (random(10) > 5)\n            g_LEDs[j] = g_LEDs[j].fadeToBlackBy(fadeAmt);  \n\n    delay(50);\n}"
  },
  {
    "path": "LED Episode 06/src/main.cpp",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        LED Episode 06\n//\n// Description:\n//\n//   Draws sample effects on a an addressable strip using FastLED\n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>            // Arduino Framework\n#include <U8g2lib.h>            // For text on the little on-chip OLED\n#define FASTLED_INTERNAL        // Suppress build banner\n#include <FastLED.h>\n\n#define OLED_CLOCK  15          // Pins for the OLED display\n#define OLED_DATA   4\n#define OLED_RESET  16\n\n#define NUM_LEDS    45          // FastLED definitions\n#define LED_PIN     5\n\nCRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED\n\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);\nint g_lineHeight = 0;\nint g_Brightness = 255;           // 0-255 LED brightness scale\n\n\n\n// FramesPerSecond\n//\n// Tracks a weighted average to smooth out the values that it calcs as the simple reciprocal\n// of the amount of time taken specified by the caller.  So 1/3 of a second is 3 fps, and it\n// will take up to 10 frames or so to stabilize on that value.\n\ndouble FramesPerSecond(double seconds)\n{\n  static double framesPerSecond; \n  framesPerSecond = (framesPerSecond * .9) + (1.0 / seconds * .1);\n  return framesPerSecond;\n}\n\nvoid DrawPixels(double fPos, double count, CRGB color)\n{\n    double availFirstPixel = 1.0 - (fPos - (long)(fPos));\n    double amtFirstPixel = min(availFirstPixel, count);\n    count = min(count, FastLED.size()-fPos);\n    if (fPos >= 0 && fPos < FastLED.size())\n    {\n        CRGB frontColor = color;\n        frontColor.fadeToBlackBy(255 * (1.0 - amtFirstPixel));\n        FastLED.leds()[(uint)fPos] += frontColor;\n    }\n\n    fPos += amtFirstPixel;\n    count -= amtFirstPixel;\n\n    while (count >= 1.0)\n    {\n        if (fPos >= 0 && fPos < FastLED.size())\n        {\n            FastLED.leds()[(uint)fPos] += color;\n            count -= 1.0;\n        }\n        fPos += 1.0;\n    }\n\n    if (count > 0.0)\n    {\n        if (fPos >= 0 && fPos < FastLED.size())\n        {\n            CRGB backColor = color;\n            backColor.fadeToBlackBy(255 * (1.0 - count));\n            FastLED.leds()[(uint)fPos] += backColor;\n        }\n    }\n}\n\nvoid set_max_power_indicator_LED(uint8_t);\n\n#include \"twinkle.h\"\n#include \"marquee.h\"\n#include \"comet.h\"\n#include \"bounce.h\"\n\nvoid setup() \n{\n  pinMode(LED_BUILTIN, OUTPUT);\n  pinMode(LED_PIN, OUTPUT);\n\n  Serial.begin(115200);\n  while (!Serial) { }\n  Serial.println(\"ESP32 Startup\");\n\n  g_OLED.begin();\n  g_OLED.clear();\n  g_OLED.setFont(u8g2_font_profont15_tf);\n  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total\n\n  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library\n  FastLED.setBrightness(g_Brightness);\n  FastLED.setMaxPowerInMilliWatts(900);\n  set_max_power_indicator_LED(LED_BUILTIN);\n}\n\nvoid loop() \n{\n  double fps = 0;\n\n  BouncingBallEffect balls(NUM_LEDS, 3, 0, false, 8.0);\n\n  while (true)\n  {\n    double dStart = millis() / 1000.0;                 // Display a frame and calc how long it takes\n\n    // Handle LEDs\n\n    balls.Draw();\n\n    // Handle OLED drawing\n\n    uint32_t milliwatts = calculate_unscaled_power_mW(g_LEDs, NUM_LEDS);\n\n    static unsigned long msLastUpdate = millis();\n    if (millis() - msLastUpdate > 500)\n    {\n      g_OLED.clearBuffer();\n      g_OLED.setCursor(0, g_lineHeight);\n      g_OLED.printf(\"FPS: %.1lf\", fps);\n      g_OLED.setCursor(1, g_lineHeight * 2);\n      g_OLED.printf(\"Power: %u mW\", milliwatts);\n      g_OLED.sendBuffer();\n      msLastUpdate = millis();\n    }\n\n    FastLED.show(g_Brightness);\n\n    double dEnd = millis() / 1000.0;\n    fps = FramesPerSecond(dEnd - dStart);\n  }\n}\n"
  },
  {
    "path": "LED Episode 06/src/marquee.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        marque.h\n//\n// Description:\n//\n//   Draws a theatre-style marquee\n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#include <U8g2lib.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n\nvoid DrawMarquee()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < NUM_LEDS; i ++)\n        g_LEDs[i] = c.setHue(k+=8);\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < NUM_LEDS - 1; i += 5)\n    {\n        g_LEDs[i] = CRGB::Black;\n    }\n    delay(50);\n}\n\nvoid DrawMarqueeMirrored()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < (NUM_LEDS + 1) / 2; i ++)\n    {\n        g_LEDs[i] = c.setHue(k);\n        g_LEDs[NUM_LEDS - 1 - i] = c.setHue(k);\n        k+= 8;\n    }\n\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < NUM_LEDS / 2; i += 5)\n    {\n        g_LEDs[i] = CRGB::Black;\n        g_LEDs[NUM_LEDS - 1 - i] = CRGB::Black;\n    }   \n\n    delay(50);\n}\n\n\n\n"
  },
  {
    "path": "LED Episode 06/src/twinkle.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        \n//\n// Description:\n//\n//   \n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#define NUM_COLORS 5\nstatic const CRGB TwinkleColors [NUM_COLORS] = \n{\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Purple,\n    CRGB::Green,\n    CRGB::Yellow\n};\n\nvoid DrawTwinkle()\n{\n    FastLED.clear(false);\n\n    for (int i=0; i<NUM_LEDS/4; i++) \n    {\n        g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];\n        FastLED.show(g_Brightness);\n        delay(200);\n    }\n}\n\nvoid DrawTwinkle2()\n{\n    static int passCount = 0;\n    if (passCount++ == NUM_LEDS/4)\n    {\n        passCount = 0;\n        FastLED.clear(false);\n    }\n    g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];\n    delay(200);       \n}"
  },
  {
    "path": "LED Episode 07/.gitignore",
    "content": ".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",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "LED Episode 07/include/README",
    "content": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro definitions\nto be shared between several project source files. You request the use of a\nheader file in your project source file (C, C++, etc) located in `src` folder\nby including it, with the C preprocessing directive `#include'.\n\n```src/main.c\n\n#include \"header.h\"\n\nint main (void)\n{\n ...\n}\n```\n\nIncluding a header file produces the same results as copying the header file\ninto each source file that needs it. Such copying would be time-consuming\nand error-prone. With a header file, the related declarations appear\nin only one place. If they need to be changed, they can be changed in one\nplace, and programs that include the header file will automatically use the\nnew version when next recompiled. The header file eliminates the labor of\nfinding and changing all the copies as well as the risk that a failure to\nfind one copy will result in inconsistencies within a program.\n\nIn C, the usual convention is to give header files names that end with `.h'.\nIt is most portable to use only letters, digits, dashes, and underscores in\nheader file names, and at most one dot.\n\nRead more about using header files in official GCC documentation:\n\n* Include Syntax\n* Include Operation\n* Once-Only Headers\n* Computed Includes\n\nhttps://gcc.gnu.org/onlinedocs/cpp/Header-Files.html\n"
  },
  {
    "path": "LED Episode 07/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "LED Episode 07/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:heltec_wifi_kit_32]\nplatform = espressif32\nboard = heltec_wifi_kit_32\nframework = arduino\nbuild_flags = -Wno-unused-variable\nupload_port = /dev/cu.SLAB_USBtoUART\nmonitor_port = /dev/cu.SLAB_USBtoUART\nmonitor_speed = 115200\n\nlib_deps = U8g2\n           FastLED\n           \n        \n           \n\n\n\n"
  },
  {
    "path": "LED Episode 07/src/bounce.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        bounce.h     \n//\n// Description:\n//\n//      Bouncing Ball effect on an LED strip\n//\n// History:     Oct-04-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <sys/time.h>                   // For time-of-day\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nusing namespace std;\n#include <vector>\n\nextern CRGB g_LEDs[];\n\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))   // Count elements in a static array\n\nstatic const CRGB ballColors [] =\n{\n    CRGB::Green,\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Orange,\n    CRGB::Indigo\n};\n\nclass BouncingBallEffect\n{\n  private:\n\n    double InitialBallSpeed(double height) const\n    {\n        return sqrt(-2 * Gravity * height);         // Because MATH!\n    }\n\n    size_t  _cLength;           \n    size_t  _cBalls;\n    byte    _fadeRate;\n    bool    _bMirrored;\n\n    const double Gravity = -9.81;                   // Because PHYSICS!\n    const double StartHeight = 1;                   // Drop balls from max height initially\n    const double ImpactVelocity = InitialBallSpeed(StartHeight);\n    const double SpeedKnob = 4.0;                   // Higher values will slow the effect\n\n    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;\n    vector<CRGB>   Colors;\n\n    static double Time()\n    {\n        timeval tv = { 0 };\n        gettimeofday(&tv, nullptr);\n        return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);\n    }\n    \n  public:\n\n    // BouncingBallEffect\n    //\n    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the\n    // balls should be drawn mirrored from each side.\n\n    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)\n        : _cLength(cLength - 1),\n          _cBalls(ballCount),\n          _fadeRate(fade),\n          _bMirrored(bMirrored),\n          ClockTimeAtLastBounce(ballCount),\n          Height(ballCount),\n          BallSpeed(ballCount),\n          Dampening(ballCount),\n          Colors(ballCount)\n    {\n        for (size_t i = 0; i < ballCount; i++)\n        {\n            Height[i]                = StartHeight;         // Current Ball Height\n            ClockTimeAtLastBounce[i] = Time();              // When ball last hit ground state\n            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball\n            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch\n            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];\n        }\n    }\n\n    // Draw\n    //\n    // Draw each of the balls.  When any ball settles with too little energy, it it \"kicked\" to restart it\n\n    virtual void Draw()\n    {\n        if (_fadeRate != 0)\n        {\n            for (size_t i = 0; i < _cLength; i++)\n                g_LEDs[i].fadeToBlackBy(_fadeRate);\n        }\n        else\n            FastLED.clear();\n        \n        // Draw each of the balls\n\n        for (size_t i = 0; i < _cBalls; i++)\n        {\n            double TimeSinceLastBounce = (Time() - ClockTimeAtLastBounce[i]) / SpeedKnob;\n\n            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration\n            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;\n\n            // Ball hits ground - bounce!\n            if (Height[i] < 0)\n            {\n                Height[i] = 0;\n                BallSpeed[i] = Dampening[i] * BallSpeed[i];\n                ClockTimeAtLastBounce[i] = Time();\n\n                if (BallSpeed[i] < 0.01)\n                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];\n            }\n\n            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);\n\n            g_LEDs[position]   += Colors[i];\n            g_LEDs[position+1] += Colors[i];\n\n            if (_bMirrored)\n            {\n                g_LEDs[_cLength - 1 - position] += Colors[i];\n                g_LEDs[_cLength - position]     += Colors[i];\n            }\n        }\n        delay(20);\n    }\n};"
  },
  {
    "path": "LED Episode 07/src/comet.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:                  \n//\n// Description:\n//\n//   \n//\n// History:     Sep-28-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nextern CRGB g_LEDs[];\n\nvoid DrawComet()\n{\n    const byte fadeAmt = 128;\n    const int cometSize = 5;\n    const int deltaHue  = 4;\n\n    static byte hue = HUE_RED;\n    static int iDirection = 1;\n    static int iPos = 0;\n\n    hue += deltaHue;\n\n    iPos += iDirection;\n    if (iPos == (NUM_LEDS - cometSize) || iPos == 0)\n        iDirection *= -1;\n    \n    for (int i = 0; i < cometSize; i++)\n        g_LEDs[iPos + i].setHue(hue);\n    \n    // Randomly fade the LEDs\n    for (int j = 0; j < NUM_LEDS; j++)\n        if (random(10) > 5)\n            g_LEDs[j] = g_LEDs[j].fadeToBlackBy(fadeAmt);  \n\n    delay(30);\n}"
  },
  {
    "path": "LED Episode 07/src/main.cpp",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        LED Episode 08\n//\n// Description:\n//\n//   Draws sample effects on a an addressable strip using FastLED\n//\n// History:     Sep-15-2020     davepl      Created\n//              Oct-05-2020     davepl      Revised for Episode 07\n//              Oct-11-2020     davepl      Revised for Episode 08\n//              Oct-16-2020     davepl      Revised for Episode 09\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>            // Arduino Framework\n#include <U8g2lib.h>            // For text on the little on-chip OLED\n#define FASTLED_INTERNAL        // Suppress build banner\n#include <FastLED.h>\n\n#define OLED_CLOCK  15          // Pins for the OLED display\n#define OLED_DATA   4\n#define OLED_RESET  16\n\n#define NUM_LEDS    40          // FastLED definitions\n#define LED_PIN     5\n\nCRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED\n\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);\nint g_lineHeight = 0;\nint g_Brightness = 255;         // 0-255 LED brightness scale\nint g_PowerLimit = 900;         // 900mW Power Limit\n\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))\n#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)\n\n// FractionalColor\n//\n// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we\n// want to improve the color math or do color correction all in one location at a later date.\n\nCRGB ColorFraction(CRGB colorIn, float fraction)\n{\n  fraction = min(1.0f, fraction);\n  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));\n}\n\nvoid DrawPixels(float fPos, float count, CRGB color)\n{\n  // Calculate how much the first pixel will hold\n  float availFirstPixel = 1.0f - (fPos - (long)(fPos));\n  float amtFirstPixel = min(availFirstPixel, count);\n  float remaining = min(count, FastLED.size()-fPos);\n  int iPos = fPos;\n\n  // Blend (add) in the color of the first partial pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);\n    remaining -= amtFirstPixel;\n  }\n\n  // Now draw any full pixels in the middle\n\n  while (remaining > 1.0f)\n  {\n    FastLED.leds()[iPos++] += color;\n    remaining--;\n  }\n\n  // Draw tail pixel, up to a single full pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos] += ColorFraction(color, remaining);\n  }\n}\n\nvoid DrawMarqueeComparison()\n{\n  static float scroll = 0.0f;\n  scroll += 0.1f;\n  if (scroll > 5.0)\n    scroll -= 5.0;\n\n  for (float i = scroll; i < NUM_LEDS/2 -1; i+= 5)\n  {\n    DrawPixels(i, 3, CRGB::Green);\n    DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);\n  }\n}\n\n\nvoid setup() \n{\n  pinMode(LED_BUILTIN, OUTPUT);\n  pinMode(LED_PIN, OUTPUT);\n\n  Serial.begin(115200);\n  while (!Serial) { }\n  Serial.println(\"ESP32 Startup\");\n\n  g_OLED.begin();\n  g_OLED.clear();\n  g_OLED.setFont(u8g2_font_profont15_tf);\n  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total\n\n  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library\n  FastLED.setBrightness(g_Brightness);\n  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle\n  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled\n}\n\nvoid loop() \n{\n  bool bLED = 0;\n\n  while (true)\n  {\n    EVERY_N_MILLISECONDS(20)\n    {\n      /*\n      fadeToBlackBy(g_LEDs, NUM_LEDS, 64);\n      int cometSize = 5;\n      int iPos = beatsin16(32, 0, NUM_LEDS-cometSize);\n      byte hue = beatsin8(96);\n      for (int i = iPos; i < iPos + cometSize; i++)\n        g_LEDs[i] = CHSV(hue, 255, 255);\n      */\n\n      DrawMarqueeComparison();\n    }\n\n    EVERY_N_MILLISECONDS(250)\n    {\n      g_OLED.clearBuffer();\n      g_OLED.setCursor(0, g_lineHeight);\n      g_OLED.printf(\"FPS  : %u\", FastLED.getFPS());\n      g_OLED.setCursor(0, g_lineHeight * 2);\n      g_OLED.printf(\"Power: %u mW\", calculate_unscaled_power_mW(g_LEDs, NUM_LEDS));\n      g_OLED.setCursor(0, g_lineHeight * 3);\n      g_OLED.printf(\"Brite: %d\", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));\n      g_OLED.sendBuffer();\n    }\n\n    FastLED.setBrightness(g_Brightness);        //  Set the brightness scale\n    FastLED.delay(10);                          //  Show and delay\n  }\n}\n"
  },
  {
    "path": "LED Episode 07/src/marquee.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        marque.h\n//\n// Description:\n//\n//   Draws a theatre-style marquee\n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#include <U8g2lib.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n\nvoid DrawMarquee()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < NUM_LEDS; i ++)\n        g_LEDs[i] = c.setHue(k+=8);\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < NUM_LEDS - 1; i += 5)\n    {\n        g_LEDs[i] = CRGB::Black;\n    }\n    delay(50);\n}\n\nvoid DrawMarqueeMirrored()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < (NUM_LEDS + 1) / 2; i ++)\n    {\n        g_LEDs[i] = c.setHue(k);\n        g_LEDs[NUM_LEDS - 1 - i] = c.setHue(k);\n        k+= 8;\n    }\n\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < NUM_LEDS / 2; i += 5)\n    {\n        g_LEDs[i] = CRGB::Black;\n        g_LEDs[NUM_LEDS - 1 - i] = CRGB::Black;\n    }   \n\n    delay(50);\n}\n\n\n\n"
  },
  {
    "path": "LED Episode 07/src/twinkle.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        \n//\n// Description:\n//\n//   \n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#define NUM_COLORS 5\nstatic const CRGB TwinkleColors [NUM_COLORS] = \n{\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Purple,\n    CRGB::Green,\n    CRGB::Yellow\n};\n\nvoid DrawTwinkle()\n{\n    FastLED.clear(false);\n\n    for (int i=0; i<NUM_LEDS/4; i++) \n    {\n        g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];\n        FastLED.show(g_Brightness);\n        delay(200);\n    }\n}\n\nvoid DrawTwinkle2()\n{\n    static int passCount = 0;\n    if (passCount++ == NUM_LEDS/4)\n    {\n        passCount = 0;\n        FastLED.clear(false);\n    }\n    g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];\n    delay(200);       \n}"
  },
  {
    "path": "LED Episode 08/.gitignore",
    "content": ".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",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "LED Episode 08/include/README",
    "content": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro definitions\nto be shared between several project source files. You request the use of a\nheader file in your project source file (C, C++, etc) located in `src` folder\nby including it, with the C preprocessing directive `#include'.\n\n```src/main.c\n\n#include \"header.h\"\n\nint main (void)\n{\n ...\n}\n```\n\nIncluding a header file produces the same results as copying the header file\ninto each source file that needs it. Such copying would be time-consuming\nand error-prone. With a header file, the related declarations appear\nin only one place. If they need to be changed, they can be changed in one\nplace, and programs that include the header file will automatically use the\nnew version when next recompiled. The header file eliminates the labor of\nfinding and changing all the copies as well as the risk that a failure to\nfind one copy will result in inconsistencies within a program.\n\nIn C, the usual convention is to give header files names that end with `.h'.\nIt is most portable to use only letters, digits, dashes, and underscores in\nheader file names, and at most one dot.\n\nRead more about using header files in official GCC documentation:\n\n* Include Syntax\n* Include Operation\n* Once-Only Headers\n* Computed Includes\n\nhttps://gcc.gnu.org/onlinedocs/cpp/Header-Files.html\n"
  },
  {
    "path": "LED Episode 08/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "LED Episode 08/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:heltec_wifi_kit_32]\nplatform = espressif32\nboard = heltec_wifi_kit_32\nframework = arduino\nbuild_flags = -Wno-unused-variable\nupload_port = /dev/cu.SLAB_USBtoUART\nmonitor_port = /dev/cu.SLAB_USBtoUART\nmonitor_speed = 115200\n\nlib_deps = U8g2\n           FastLED\n           \n        \n           \n\n\n\n"
  },
  {
    "path": "LED Episode 08/src/bounce.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        bounce.h     \n//\n// Description:\n//\n//      Bouncing Ball effect on an LED strip\n//\n// History:     Oct-04-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <sys/time.h>                   // For time-of-day\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nusing namespace std;\n#include <vector>\n\nextern CRGB g_LEDs[];\n\nstatic const CRGB ballColors [] =\n{\n    CRGB::Green,\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Orange,\n    CRGB::Indigo\n};\n\nclass BouncingBallEffect\n{\n  private:\n\n    float InitialBallSpeed(float height) const\n    {\n        return sqrt(-2 * Gravity * height);         // Because MATH!\n    }\n\n    size_t  _cLength;           \n    size_t  _cBalls;\n    byte    _fadeRate;\n    bool    _bMirrored;\n\n    const float Gravity = -9.81;                   // Because PHYSICS!\n    const float StartHeight = 1;                   // Drop balls from max height initially\n    const float ImpactVelocity = InitialBallSpeed(StartHeight);\n    const float SpeedKnob = 4.0;                   // Higher values will slow the effect\n\n    vector<float> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;\n    vector<CRGB>   Colors;\n\n    static float Time()\n    {\n        timeval tv = { 0 };\n        gettimeofday(&tv, nullptr);\n        return (float)(tv.tv_usec / 1000000.0 + (float) tv.tv_sec);\n    }\n    \n  public:\n\n    // BouncingBallEffect\n    //\n    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the\n    // balls should be drawn mirrored from each side.\n\n    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)\n        : _cLength(cLength - 1),\n          _cBalls(ballCount),\n          _fadeRate(fade),\n          _bMirrored(bMirrored),\n          ClockTimeAtLastBounce(ballCount),\n          Height(ballCount),\n          BallSpeed(ballCount),\n          Dampening(ballCount),\n          Colors(ballCount)\n    {\n        for (size_t i = 0; i < ballCount; i++)\n        {\n            Height[i]                = StartHeight;         // Current Ball Height\n            ClockTimeAtLastBounce[i] = Time();              // When ball last hit ground state\n            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball\n            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch\n            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];\n        }\n    }\n\n    // Draw\n    //\n    // Draw each of the balls.  When any ball settles with too little energy, it it \"kicked\" to restart it\n\n    virtual void Draw()\n    {\n        if (_fadeRate != 0)\n        {\n            for (size_t i = 0; i < _cLength; i++)\n                g_LEDs[i].fadeToBlackBy(_fadeRate);\n        }\n        else\n            FastLED.clear();\n        \n        // Draw each of the balls\n\n        for (size_t i = 0; i < _cBalls; i++)\n        {\n            float TimeSinceLastBounce = (Time() - ClockTimeAtLastBounce[i]) / SpeedKnob;\n\n            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration\n            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;\n\n            // Ball hits ground - bounce!\n            if (Height[i] < 0)\n            {\n                Height[i] = 0;\n                BallSpeed[i] = Dampening[i] * BallSpeed[i];\n                ClockTimeAtLastBounce[i] = Time();\n\n                if (BallSpeed[i] < 0.01)\n                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];\n            }\n\n            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);\n\n            g_LEDs[position]   += Colors[i];\n            g_LEDs[position+1] += Colors[i];\n\n            if (_bMirrored)\n            {\n                g_LEDs[_cLength - 1 - position] += Colors[i];\n                g_LEDs[_cLength - position]     += Colors[i];\n            }\n        }\n        delay(20);\n    }\n};"
  },
  {
    "path": "LED Episode 08/src/comet.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:                  \n//\n// Description:\n//\n//   \n//\n// History:     Sep-28-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nextern CRGB g_LEDs[];\n\nvoid DrawComet()\n{\n    const byte fadeAmt = 128;\n    const int cometSize = 5;\n    const int deltaHue  = 4;\n\n    static byte hue = HUE_RED;\n    static int iDirection = 1;\n    static int iPos = 0;\n\n    hue += deltaHue;\n\n    iPos += iDirection;\n    if (iPos == (NUM_LEDS - cometSize) || iPos == 0)\n        iDirection *= -1;\n    \n    for (int i = 0; i < cometSize; i++)\n        g_LEDs[iPos + i].setHue(hue);\n    \n    // Randomly fade the LEDs\n    for (int j = 0; j < NUM_LEDS; j++)\n        if (random(10) > 5)\n            g_LEDs[j] = g_LEDs[j].fadeToBlackBy(fadeAmt);  \n\n    delay(30);\n}"
  },
  {
    "path": "LED Episode 08/src/main.cpp",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        LED Episode 07\n//\n// Description:\n//\n//   Draws sample effects on a an addressable strip using FastLED\n//\n// History:     Sep-15-2020     davepl      Created\n//              Oct-15-2020     davepl      Revised for Episode 07\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>            // Arduino Framework\n#include <U8g2lib.h>            // For text on the little on-chip OLED\n#define FASTLED_INTERNAL        // Suppress build banner\n#include <FastLED.h>\n\n#define OLED_CLOCK  15          // Pins for the OLED display\n#define OLED_DATA   4\n#define OLED_RESET  16\n\n#define NUM_LEDS    40          // FastLED definitions\n#define LED_PIN     5\n\nCRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED\n\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);\nint g_lineHeight = 0;\nint g_Brightness = 255;           // 0-255 LED brightness scale\nint g_PowerLimit = 900;           // 900mW draw\n\n#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))   \n\n// FractionalColor\n//\n// Returns a fraction of a color; abstracts the fadeToBlack away so that we can later\n// do better color correction as needed\n\nCRGB ColorFraction(const CRGB colorIn, float fraction)\n{\n  fraction = min(1.0f, fraction);\n  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));\n}\n\n// DrawPixels\n//\n// Draw a sub-pixel precise amount of pixels starting at a floating point offset; for example\n// you can draw 2.75 pixels starting a 5.5, and it will end at 8.25\n\nvoid DrawPixels(float fPos, float count, CRGB color)\n{\n    // Figure out how much the first pixel will hold\n    float availFirstPixel = 1.0f - (fPos - (long)(fPos));  // If we are starting at 2.25, there would be 0.75 avail here\n    float amtFirstPixel = min(availFirstPixel, count);     // But of course we never draw more than we need\n    float remaining = min(count, FastLED.size()-fPos);     // How many pixels remain after we draw the front header pixel\n    int iPos = fPos;\n\n    // Blend (add) in the color value of this first partial pixel ...and decrement the remaining pixel count by that same amount\n\n    if (remaining > 0.0f)\n    {\n      FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel); \n      remaining -= amtFirstPixel;                          \n    }\n\n    // Draw any full pixels and stop when we have a full pixel or less remainining\n\n    while (remaining > 1.0f)                               // Final pixel can 'handle' up to 1.0 full pixels, so we draw anything more here\n    {\n      FastLED.leds()[iPos++] += color;                     // Draw them in one at aa time and update the remaining counts\n      remaining--;\n    }  \n\n    // Draw tail pixel, up to a single full pixel\n\n    if (remaining > 0.0f)  \n    {                    \n        FastLED.leds()[iPos] += ColorFraction(color, remaining);     \n    }\n}\n\n\n#include \"marquee.h\"\n\nvoid setup() \n{\n  pinMode(LED_BUILTIN, OUTPUT);\n  pinMode(LED_PIN, OUTPUT);\n\n  Serial.begin(115200);\n  while (!Serial) { }\n  Serial.println(\"ESP32 Startup\");\n\n  g_OLED.begin();\n  g_OLED.clear();\n  g_OLED.setFont(u8g2_font_profont15_tf);\n  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total\n\n  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library\n  FastLED.setBrightness(g_Brightness);\n  set_max_power_indicator_LED(LED_BUILTIN);                               // FastLED will light LED if power limiting\n  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);\n}\n\n\nvoid loop() \n{\n  bool bLED = 0;\n  float fps = 0;\n  byte gHue = 0;\n\n  while (true)\n  {\n    TIMES_PER_SECOND(50)\n    {\n      fadeToBlackBy(g_LEDs, NUM_LEDS, 64);\n      float pos = beatsin16(32, 0, NUM_LEDS-10);\n      byte hue = beatsin8(32, 0, 255);\n      DrawPixels(pos, 10, CHSV(0, 255, 255));\n      FastLED.show(g_Brightness);\n    }\n\n    /*\n    TIMES_PER_SECOND(50)\n    {\n      DrawMarqueeComparison();\n    }\n    */\n    EVERY_N_MILLISECONDS(250)\n    {\n      g_OLED.clearBuffer();\n      g_OLED.setCursor(0, g_lineHeight);\n      g_OLED.printf(\"FPS  : %u\", FastLED.getFPS());\n      g_OLED.setCursor(0, g_lineHeight * 2);\n      g_OLED.printf(\"Power: %u mW\", calculate_unscaled_power_mW(g_LEDs, NUM_LEDS));\n      g_OLED.setCursor(0, g_lineHeight * 3);\n      g_OLED.printf(\"Brite: %d\", calculate_max_brightness_for_power_mW( g_Brightness, g_PowerLimit));\n      g_OLED.sendBuffer();\n    }\n\n    FastLED.delay(10);\n   }\n}\n"
  },
  {
    "path": "LED Episode 08/src/marquee.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        marque.h\n//\n// Description:\n//\n//   Draws a theatre-style marquee\n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#include <U8g2lib.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nvoid DrawMarqueeComparison()\n{\n    static float scroll = 0.0f;\n    scroll += 0.1f;\n    if (scroll > 5.0f)\n        scroll -= 5.0f;\n\n    for (float i = scroll; i < NUM_LEDS/2 - 1; i += 5)\n    {\n        DrawPixels(i, 3, CRGB::Green);\n        DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);\n    }\n}\n\nvoid DrawMarquee()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < NUM_LEDS; i ++)\n        g_LEDs[i] = c.setHue(k+=8);\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < NUM_LEDS - 1; i += 5)\n    {\n        g_LEDs[i] = CRGB::Black;\n    }\n    delay(50);\n}\n\nvoid DrawMarqueeMirrored()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < (NUM_LEDS + 1) / 2; i ++)\n    {\n        g_LEDs[i] = c.setHue(k);\n        g_LEDs[NUM_LEDS - 1 - i] = c.setHue(k);\n        k+= 8;\n    }\n\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < NUM_LEDS / 2; i += 5)\n    {\n        g_LEDs[i] = CRGB::Black;\n        g_LEDs[NUM_LEDS - 1 - i] = CRGB::Black;\n    }   \n\n    delay(50);\n}\n\n\n\n"
  },
  {
    "path": "LED Episode 08/src/twinkle.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        \n//\n// Description:\n//\n//   \n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#define NUM_COLORS 5\nstatic const CRGB TwinkleColors [NUM_COLORS] = \n{\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Purple,\n    CRGB::Green,\n    CRGB::Yellow\n};\n\nvoid DrawTwinkle()\n{\n    FastLED.clear(false);\n\n    for (int i=0; i<NUM_LEDS/4; i++) \n    {\n        g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];\n        FastLED.show(g_Brightness);\n        delay(200);\n    }\n}\n\nvoid DrawTwinkle2()\n{\n    static int passCount = 0;\n    if (passCount++ == NUM_LEDS/4)\n    {\n        passCount = 0;\n        FastLED.clear(false);\n    }\n    g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];\n    delay(200);       \n}"
  },
  {
    "path": "LED Episode 09/.gitignore",
    "content": ".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",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "LED Episode 09/include/README",
    "content": "\nThis directory is intended for project header files.\n\nA header file is a file containing C declarations and macro definitions\nto be shared between several project source files. You request the use of a\nheader file in your project source file (C, C++, etc) located in `src` folder\nby including it, with the C preprocessing directive `#include'.\n\n```src/main.c\n\n#include \"header.h\"\n\nint main (void)\n{\n ...\n}\n```\n\nIncluding a header file produces the same results as copying the header file\ninto each source file that needs it. Such copying would be time-consuming\nand error-prone. With a header file, the related declarations appear\nin only one place. If they need to be changed, they can be changed in one\nplace, and programs that include the header file will automatically use the\nnew version when next recompiled. The header file eliminates the labor of\nfinding and changing all the copies as well as the risk that a failure to\nfind one copy will result in inconsistencies within a program.\n\nIn C, the usual convention is to give header files names that end with `.h'.\nIt is most portable to use only letters, digits, dashes, and underscores in\nheader file names, and at most one dot.\n\nRead more about using header files in official GCC documentation:\n\n* Include Syntax\n* Include Operation\n* Once-Only Headers\n* Computed Includes\n\nhttps://gcc.gnu.org/onlinedocs/cpp/Header-Files.html\n"
  },
  {
    "path": "LED Episode 09/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "LED Episode 09/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:heltec_wifi_kit_32]\nplatform = espressif32\nboard = heltec_wifi_kit_32\nframework = arduino\nbuild_flags = -Wno-unused-variable\nupload_port = /dev/cu.SLAB_USBtoUART\nmonitor_port = /dev/cu.SLAB_USBtoUART\nmonitor_speed = 115200\n\nlib_deps = U8g2\n           FastLED\n           \n        \n           \n\n\n\n"
  },
  {
    "path": "LED Episode 09/src/bounce.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        bounce.h     \n//\n// Description:\n//\n//      Bouncing Ball effect on an LED strip\n//\n// History:     Oct-04-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <sys/time.h>                   // For time-of-day\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nusing namespace std;\n#include <vector>\n\nextern CRGB g_LEDs[];\n\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))   // Count elements in a static array\n\nstatic const CRGB ballColors [] =\n{\n    CRGB::Green,\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Orange,\n    CRGB::Indigo\n};\n\nclass BouncingBallEffect\n{\n  private:\n\n    double InitialBallSpeed(double height) const\n    {\n        return sqrt(-2 * Gravity * height);         // Because MATH!\n    }\n\n    size_t  _cLength;           \n    size_t  _cBalls;\n    byte    _fadeRate;\n    bool    _bMirrored;\n\n    const double Gravity = -9.81;                   // Because PHYSICS!\n    const double StartHeight = 1;                   // Drop balls from max height initially\n    const double ImpactVelocity = InitialBallSpeed(StartHeight);\n    const double SpeedKnob = 4.0;                   // Higher values will slow the effect\n\n    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;\n    vector<CRGB>   Colors;\n\n    static double Time()\n    {\n        timeval tv = { 0 };\n        gettimeofday(&tv, nullptr);\n        return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);\n    }\n    \n  public:\n\n    // BouncingBallEffect\n    //\n    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the\n    // balls should be drawn mirrored from each side.\n\n    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)\n        : _cLength(cLength - 1),\n          _cBalls(ballCount),\n          _fadeRate(fade),\n          _bMirrored(bMirrored),\n          ClockTimeAtLastBounce(ballCount),\n          Height(ballCount),\n          BallSpeed(ballCount),\n          Dampening(ballCount),\n          Colors(ballCount)\n    {\n        for (size_t i = 0; i < ballCount; i++)\n        {\n            Height[i]                = StartHeight;         // Current Ball Height\n            ClockTimeAtLastBounce[i] = Time();              // When ball last hit ground state\n            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball\n            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch\n            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];\n        }\n    }\n\n    // Draw\n    //\n    // Draw each of the balls.  When any ball settles with too little energy, it it \"kicked\" to restart it\n\n    virtual void Draw()\n    {\n        if (_fadeRate != 0)\n        {\n            for (size_t i = 0; i < _cLength; i++)\n                g_LEDs[i].fadeToBlackBy(_fadeRate);\n        }\n        else\n            FastLED.clear();\n        \n        // Draw each of the balls\n\n        for (size_t i = 0; i < _cBalls; i++)\n        {\n            double TimeSinceLastBounce = (Time() - ClockTimeAtLastBounce[i]) / SpeedKnob;\n\n            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration\n            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;\n\n            // Ball hits ground - bounce!\n            if (Height[i] < 0)\n            {\n                Height[i] = 0;\n                BallSpeed[i] = Dampening[i] * BallSpeed[i];\n                ClockTimeAtLastBounce[i] = Time();\n\n                if (BallSpeed[i] < 0.01)\n                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];\n            }\n\n            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);\n\n            g_LEDs[position]   += Colors[i];\n            g_LEDs[position+1] += Colors[i];\n\n            if (_bMirrored)\n            {\n                g_LEDs[_cLength - 1 - position] += Colors[i];\n                g_LEDs[_cLength - position]     += Colors[i];\n            }\n        }\n        delay(20);\n    }\n};"
  },
  {
    "path": "LED Episode 09/src/comet.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:                  \n//\n// Description:\n//\n//   \n//\n// History:     Sep-28-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nextern CRGB g_LEDs[];\n\nvoid DrawComet()\n{\n    const byte fadeAmt = 128;\n    const int cometSize = 5;\n    const int deltaHue  = 4;\n\n    static byte hue = HUE_RED;\n    static int iDirection = 1;\n    static int iPos = 0;\n\n    hue += deltaHue;\n\n    iPos += iDirection;\n    if (iPos == (NUM_LEDS - cometSize) || iPos == 0)\n        iDirection *= -1;\n    \n    for (int i = 0; i < cometSize; i++)\n        g_LEDs[iPos + i].setHue(hue);\n    \n    // Randomly fade the LEDs\n    for (int j = 0; j < NUM_LEDS; j++)\n        if (random(10) > 5)\n            g_LEDs[j] = g_LEDs[j].fadeToBlackBy(fadeAmt);  \n\n    delay(30);\n}"
  },
  {
    "path": "LED Episode 09/src/main.cpp",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        LED Episode 08\n//\n// Description:\n//\n//   Draws sample effects on a an addressable strip using FastLED\n//\n// History:     Sep-15-2020     davepl      Created\n//              Oct-05-2020     davepl      Revised for Episode 07\n//              Oct-11-2020     davepl      Revised for Episode 08\n//              Oct-16-2020     davepl      Revised for Episode 09\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>            // Arduino Framework\n#include <U8g2lib.h>            // For text on the little on-chip OLED\n#define FASTLED_INTERNAL        // Suppress build banner\n#include <FastLED.h>\n\n#define OLED_CLOCK  15          // Pins for the OLED display\n#define OLED_DATA   4\n#define OLED_RESET  16\n\n#define NUM_LEDS    40          // FastLED definitions\n#define LED_PIN     5\n\nCRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED\n\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);\nint g_lineHeight = 0;\nint g_Brightness = 255;         // 0-255 LED brightness scale\nint g_PowerLimit = 900;         // 900mW Power Limit\n\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))\n#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)\n\n// FractionalColor\n//\n// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we\n// want to improve the color math or do color correction all in one location at a later date.\n\nCRGB ColorFraction(CRGB colorIn, float fraction)\n{\n  fraction = min(1.0f, fraction);\n  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));\n}\n\nvoid DrawPixels(float fPos, float count, CRGB color)\n{\n  // Calculate how much the first pixel will hold\n  float availFirstPixel = 1.0f - (fPos - (long)(fPos));\n  float amtFirstPixel = min(availFirstPixel, count);\n  float remaining = min(count, FastLED.size()-fPos);\n  int iPos = fPos;\n\n  // Blend (add) in the color of the first partial pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);\n    remaining -= amtFirstPixel;\n  }\n\n  // Now draw any full pixels in the middle\n\n  while (remaining > 1.0f)\n  {\n    FastLED.leds()[iPos++] += color;\n    remaining--;\n  }\n\n  // Draw tail pixel, up to a single full pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos] += ColorFraction(color, remaining);\n  }\n}\n\nvoid DrawMarqueeComparison()\n{\n  static float scroll = 0.0f;\n  scroll += 0.1f;\n  if (scroll > 5.0)\n    scroll -= 5.0;\n\n  for (float i = scroll; i < NUM_LEDS/2 -1; i+= 5)\n  {\n    DrawPixels(i, 3, CRGB::Green);\n    DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);\n  }\n}\n\n\nvoid setup() \n{\n  pinMode(LED_BUILTIN, OUTPUT);\n  pinMode(LED_PIN, OUTPUT);\n\n  Serial.begin(115200);\n  while (!Serial) { }\n  Serial.println(\"ESP32 Startup\");\n\n  g_OLED.begin();\n  g_OLED.clear();\n  g_OLED.setFont(u8g2_font_profont15_tf);\n  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total\n\n  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library\n  FastLED.setBrightness(g_Brightness);\n  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle\n  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled\n}\n\nvoid loop() \n{\n  bool bLED = 0;\n\n  while (true)\n  {\n    FastLED.clear();\n    // Draw here \n\n    EVERY_N_MILLISECONDS(250)\n    {\n      g_OLED.clearBuffer();\n      g_OLED.setCursor(0, g_lineHeight);\n      g_OLED.printf(\"FPS  : %u\", FastLED.getFPS());\n      g_OLED.setCursor(0, g_lineHeight * 2);\n      g_OLED.printf(\"Power: %u mW\", calculate_unscaled_power_mW(g_LEDs, NUM_LEDS));\n      g_OLED.setCursor(0, g_lineHeight * 3);\n      g_OLED.printf(\"Brite: %d\", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));\n      g_OLED.sendBuffer();\n    }\n\n    FastLED.setBrightness(g_Brightness);        //  Set the brightness scale\n    FastLED.delay(33);                          //  Show and delay\n  }\n}\n"
  },
  {
    "path": "LED Episode 09/src/marquee.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        marque.h\n//\n// Description:\n//\n//   Draws a theatre-style marquee\n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#include <U8g2lib.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n\nvoid DrawMarquee()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < NUM_LEDS; i ++)\n        g_LEDs[i] = c.setHue(k+=8);\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < NUM_LEDS - 1; i += 5)\n    {\n        g_LEDs[i] = CRGB::Black;\n    }\n    delay(50);\n}\n\nvoid DrawMarqueeMirrored()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < (NUM_LEDS + 1) / 2; i ++)\n    {\n        g_LEDs[i] = c.setHue(k);\n        g_LEDs[NUM_LEDS - 1 - i] = c.setHue(k);\n        k+= 8;\n    }\n\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < NUM_LEDS / 2; i += 5)\n    {\n        g_LEDs[i] = CRGB::Black;\n        g_LEDs[NUM_LEDS - 1 - i] = CRGB::Black;\n    }   \n\n    delay(50);\n}\n\n\n\n"
  },
  {
    "path": "LED Episode 09/src/twinkle.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        \n//\n// Description:\n//\n//   \n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#define NUM_COLORS 5\nstatic const CRGB TwinkleColors [NUM_COLORS] = \n{\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Purple,\n    CRGB::Green,\n    CRGB::Yellow\n};\n\nvoid DrawTwinkle()\n{\n    FastLED.clear(false);\n\n    for (int i=0; i<NUM_LEDS/4; i++) \n    {\n        g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];\n        FastLED.show(g_Brightness);\n        delay(200);\n    }\n}\n\nvoid DrawTwinkle2()\n{\n    static int passCount = 0;\n    if (passCount++ == NUM_LEDS/4)\n    {\n        passCount = 0;\n        FastLED.clear(false);\n    }\n    g_LEDs[random(NUM_LEDS)] = TwinkleColors[random(0, NUM_COLORS)];\n    delay(200);       \n}"
  },
  {
    "path": "LED Episode 10/.gitignore",
    "content": ".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",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "LED Episode 10/include/bounce.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        bounce.h     \n//\n// Description:\n//\n//      Bouncing Ball effect on an LED strip\n//\n// History:     Oct-04-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nusing namespace std;\n#include <vector>\n\n#include \"ledgfx.h\"\n\nstatic const CRGB ballColors [] =\n{\n    CRGB::Green,\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Orange,\n    CRGB::Indigo\n};\n\nclass BouncingBallEffect\n{\n  private:\n\n    double InitialBallSpeed(double height) const\n    {\n        return sqrt(-2 * Gravity * height);         // Because MATH!\n    }\n\n    size_t  _cLength;           \n    size_t  _cBalls;\n    byte    _fadeRate;\n    bool    _bMirrored;\n\n    const double Gravity = -9.81;                   // Because PHYSICS!\n    const double StartHeight = 1;                   // Drop balls from max height initially\n    const double ImpactVelocity = InitialBallSpeed(StartHeight);\n    const double SpeedKnob = 4.0;                   // Higher values will slow the effect\n\n    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;\n    vector<CRGB>   Colors;\n\n  public:\n\n    // BouncingBallEffect\n    //\n    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the\n    // balls should be drawn mirrored from each side.\n\n    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)\n        : _cLength(cLength - 1),\n          _cBalls(ballCount),\n          _fadeRate(fade),\n          _bMirrored(bMirrored),\n          ClockTimeAtLastBounce(ballCount),\n          Height(ballCount),\n          BallSpeed(ballCount),\n          Dampening(ballCount),\n          Colors(ballCount)\n    {\n        for (size_t i = 0; i < ballCount; i++)\n        {\n            Height[i]                = StartHeight;                 // Current Ball Height\n            ClockTimeAtLastBounce[i] = UnixTime();                  // When ball last hit ground state\n            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball\n            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch\n            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];\n        }\n    }\n\n    // Draw\n    //\n    // Draw each of the balls.  When any ball settles with too little energy, it it \"kicked\" to restart it\n\n    virtual void Draw()\n    {\n        if (_fadeRate != 0)\n        {\n            for (size_t i = 0; i < _cLength; i++)\n                FastLED.leds()[i].fadeToBlackBy(_fadeRate);\n        }\n        else\n            FastLED.clear();\n        \n        // Draw each of the balls\n\n        for (size_t i = 0; i < _cBalls; i++)\n        {\n            double TimeSinceLastBounce = (UnixTime() - ClockTimeAtLastBounce[i]) / SpeedKnob;\n\n            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration\n            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;\n\n            // Ball hits ground - bounce!\n            if (Height[i] < 0)\n            {\n                Height[i] = 0;\n                BallSpeed[i] = Dampening[i] * BallSpeed[i];\n                ClockTimeAtLastBounce[i] = Time();\n\n                if (BallSpeed[i] < 0.01)\n                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];\n            }\n\n            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);\n\n            FastLED.leds()[position]   += Colors[i];\n            FastLED.leds()[position+1] += Colors[i];\n\n            if (_bMirrored)\n            {\n                FastLED.leds()[_cLength - 1 - position] += Colors[i];\n                FastLED.leds()[_cLength - position]     += Colors[i];\n            }\n        }\n        delay(20);\n    }\n};"
  },
  {
    "path": "LED Episode 10/include/comet.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:                  \n//\n// Description:\n//\n//   \n//\n// History:     Sep-28-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nvoid DrawComet()\n{\n    const byte fadeAmt = 128;\n    const int cometSize = 5;\n    const int deltaHue  = 4;\n\n    static byte hue = HUE_RED;\n    static int iDirection = 1;\n    static int iPos = 0;\n\n    hue += deltaHue;\n\n    iPos += iDirection;\n    if (iPos == (FastLED.count() - cometSize) || iPos == 0)\n        iDirection *= -1;\n    \n    for (int i = 0; i < cometSize; i++)\n        FastLED.leds()[iPos + i].setHue(hue);\n    \n    // Randomly fade the LEDs\n    for (int j = 0; j < FastLED.count(); j++)\n        if (random(10) > 5)\n            FastLED.leds()[j] = FastLED.leds()[j].fadeToBlackBy(fadeAmt);  \n\n    delay(30);\n}"
  },
  {
    "path": "LED Episode 10/include/fire.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        fire.h               \n//\n// Description:\n//\n//              LED Flame Effect\n//\n// History:     Sep-28-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include \"ledgfx.h\"\n\nclass FireEffectSmooth\n{\n  protected:\n    float  * Temperatures;\n    float    LastDraw;                  // Last time we drew the flame\n\n    const float IGNITION_KNOB = 50.0f;  // Preference-based constant factor for ignition rate\n    const float SPREADRATE_KNOB = 12.0f; // Preference-based constant for flame spread rate\n    const float SPARKRATE_KNOB  = 8.0f; // Preference-based constant for spark ignition rate\n\n  public:\n    bool    Mirrored;                   // Should the flame be mirrored, drawn from both sides?\n    bool    Reversed;                   // Only applicable when not reversed, should it be reversed?\n    float   Cooling;                    // Pixel cooldown rate \n    int     Size;\n    int     SparkHeight;                // Ignition zone for where new pixels start up\n    float   SparkProbability;           // Probability of a spark in each ignition zone pixel\n    float   SpreadRate;                 // Rate at which fire spreads pixel to pixel\n    \n    FireEffectSmooth(int size, bool mirrored = true, bool reversed = false, int sparkHeight = 0, float sparkProbability = 1.0, float cooling = 1.0, float spreadRate = 1.0)\n    {\n        Mirrored         = mirrored;\n        if (mirrored)\n            size /= 2;\n\n        Reversed         = reversed;\n        Cooling          = cooling;\n        Size             = size;                                            // \n        SparkHeight      = sparkHeight;                                     // \n        SparkProbability = sparkProbability * SPARKRATE_KNOB / SparkHeight; // Chance that each LED cell will ignite when tested\n        Temperatures     = new float[Size];                                 // Array of temperatures, one per LED\n        SpreadRate       = spreadRate * SPREADRATE_KNOB;                    // How fast the flame spreads per second\n        LastDraw         = UnixTime();                                      // Start of time\n    }\n\n    virtual ~FireEffectSmooth()               // Because we have a virtual function, destructor is virtual as well\n    {\n        delete [] Temperatures;\n    }\n\n    void DrawFire()\n    {\n        FastLED.clear();\n\n        float elapsedSeconds = UnixTime() - LastDraw;\n        float cooldown = 1.0f * RandomFloat() * Cooling * elapsedSeconds;\n        LastDraw = UnixTime();\n\n        for (int i = 0; i < Size; i++)\n        {\n            Temperatures[i] = max(0.0f, Temperatures[i] - cooldown); // Cool cell by cooldown amount, but don't go below zero\n            \n            int neighborIndex = (i == 0) ? Size - 1 : i - 1;        // Index of cell to our left, wrapping around to front\n            float spreadAmount = min(0.25f, Temperatures[neighborIndex]) * SpreadRate * elapsedSeconds;\n            spreadAmount = min(Temperatures[neighborIndex], spreadAmount);\n            Temperatures[i]             += spreadAmount;            // Exchange 'spreadAmount' of heat between cells\n            Temperatures[neighborIndex] -= spreadAmount;\n\n            // Check to see if this cell ignites a new spark\n            if (i <= SparkHeight && RandomFloat() < SparkProbability * elapsedSeconds)\n            {\n                //Temperatures[i] = Temperatures[i] + RandomFloat() * 30 * elapsedSeconds;\n                Temperatures[i] = 2.0; // min(1.0f, (Temperatures[i] + RandomFloat() * 30 * elapsedSeconds));\n                //printf(\"Spark at %d: %f\", i, Temperatures[i]);\n            }\n        }\n        for (int i = 0; i < Size; i++)\n        {\n            FastLED.leds()[i] = HeatColor(240 * min(1.0f, Temperatures[i]));\n        }\n    }\n};\n\nclass ClassicFireEffect\n{\nprotected:\n    int     Size;\n    int     Cooling;\n    int     Sparks;\n    int     SparkHeight;\n    int     Sparking;\n    bool    bReversed;\n    bool    bMirrored;\n\n    byte  * heat;\n\n    // When diffusing the fire upwards, these control how much to blend in from the cells below (ie: downward neighbors)\n    // You can tune these coefficients to control how quickly and smoothly the fire spreads.  \n\n    static const byte BlendSelf = 2;\n    static const byte BlendNeighbor1 = 3;\n    static const byte BlendNeighbor2 = 2;\n    static const byte BlendNeighbor3 = 1;\n    static const byte BlendTotal = (BlendSelf + BlendNeighbor1 + BlendNeighbor2 + BlendNeighbor3);\n\npublic:\n    \n    // Lower sparking -> more flicker.  Higher sparking -> more consistent flame\n\n    ClassicFireEffect(int size, int cooling = 80, int sparking = 50, int sparks = 3, int sparkHeight = 4, bool breversed = true, bool bmirrored = true) \n        : Size(size),\n          Cooling(cooling),\n          Sparks(sparks),\n          SparkHeight(sparkHeight),\n          Sparking(sparking),\n          bReversed(breversed),\n          bMirrored(bmirrored)\n    {\n        if (bMirrored)\n            Size = Size / 2;\n\n        heat = new byte[size] { 0 };\n    }\n\n    virtual ~ClassicFireEffect()\n    {\n        delete [] heat;\n    }\n\n    virtual void DrawFire()\n    {\n        // First cool each cell by a little bit\n        for (int i = 0; i < Size; i++)\n            heat[i] = max(0L, heat[i] - random(0, ((Cooling * 10) / Size) + 2));\n\n        // Next drift heat up and diffuse it a little but\n        for (int i = 0; i < Size; i++)\n            heat[i] = (heat[i] * BlendSelf + \n                       heat[(i + 1) % Size] * BlendNeighbor1 + \n                       heat[(i + 2) % Size] * BlendNeighbor2 + \n                       heat[(i + 3) % Size] * BlendNeighbor3) \n                      / BlendTotal;\n\n        // Randomly ignite new sparks down in the flame kernel\n        for (int i = 0; i < Sparks; i++)\n        {\n            if (random(255) < Sparking)\n            {\n                int y = Size - 1 - random(SparkHeight);\n                heat[y] = heat[y] + random(160, 255); // This randomly rolls over sometimes of course, and that's essential to the effect\n            }\n        }\n\n        // Finally convert heat to a color\n        for (int i = 0; i < Size; i++)\n        {\n            CRGB color = HeatColor(heat[i]);\n            int j = bReversed ? (Size - 1 - i) : i;\n            DrawPixels(j, 1, color);\n            if (bMirrored)\n            {\n                int j2 = !bReversed ? (2 * Size - 1 - i) : Size + i;\n                DrawPixels(j2, 1, color);\n            }\n        }\n    }\n};\n"
  },
  {
    "path": "LED Episode 10/include/ledgfx.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        ledgfx.h\n//\n// Description:\n//\n//   LED Drawing Routines for Dave's Garage Tutorial series\n//\n// History:     OCt-18-2020     davepl      Created from main.cpp code\n//---------------------------------------------------------------------------\n\n#pragma once\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include <sys/time.h>                   // For time-of-day\n\n// Utility Macros\n\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))\n#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)\n\n//inline double RandomDouble()\n//{\n//    return random(UINT32_MAX) / (double) UINT32_MAX;\n//}\n\ninline float RandomFloat()\n{\n    float r = random(1000000L) / 1000000.0f;\n    return r;\n}\n\ninline double UnixTime()\n{\n    timeval tv = { 0 };\n    gettimeofday(&tv, nullptr);\n    return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);\n}\n\n// FractionalColor\n//\n// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we\n// want to improve the color math or do color correction all in one location at a later date.\n\nCRGB ColorFraction(CRGB colorIn, float fraction)\n{\n  fraction = min(1.0f, fraction);\n  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));\n}\n\nvoid DrawPixels(float fPos, float count, CRGB color)\n{\n  // Calculate how much the first pixel will hold\n  float availFirstPixel = 1.0f - (fPos - (long)(fPos));\n  float amtFirstPixel = min(availFirstPixel, count);\n  float remaining = min(count, FastLED.size()-fPos);\n  int iPos = fPos;\n\n  // Blend (add) in the color of the first partial pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);\n    remaining -= amtFirstPixel;\n  }\n\n  // Now draw any full pixels in the middle\n\n  while (remaining > 1.0f)\n  {\n    FastLED.leds()[iPos++] += color;\n    remaining--;\n  }\n\n  // Draw tail pixel, up to a single full pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos] += ColorFraction(color, remaining);\n  }\n}\n"
  },
  {
    "path": "LED Episode 10/include/marquee.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        marque.h\n//\n// Description:\n//\n//   Draws a theatre-style marquee\n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#include <U8g2lib.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n\nvoid DrawMarquee()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < FastLED.count(); i ++)\n        FastLED.leds()[i] = c.setHue(k+=8);\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < FastLED.count() - 1; i += 5)\n    {\n        FastLED.leds()[i] = CRGB::Black;\n    }\n    delay(50);\n}\n\nvoid DrawMarqueeMirrored()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < (FastLED.count() + 1) / 2; i ++)\n    {\n        FastLED.leds()[i] = c.setHue(k);\n        FastLED.leds()[FastLED.count() - 1 - i] = c.setHue(k);\n        k+= 8;\n    }\n\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < FastLED.count() / 2; i += 5)\n    {\n        FastLED.leds()[i] = CRGB::Black;\n        FastLED.leds()[FastLED.count() - 1 - i] = CRGB::Black;\n    }   \n\n    delay(50);\n}\n\n\n\n"
  },
  {
    "path": "LED Episode 10/include/twinkle.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        \n//\n// Description:\n//\n//   \n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include \"ledgfx.h\"\n\nstatic const CRGB TwinkleColors [] = \n{\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Purple,\n    CRGB::Green,\n    CRGB::Yellow\n};\n\nvoid DrawTwinkle()\n{\n    static int passCount = 0;\n    if (passCount++ == FastLED.count()/4)\n    {\n        passCount = 0;\n        FastLED.clear(false);\n    }\n    FastLED.leds()[random(FastLED.count())] = TwinkleColors[random(0, ARRAYSIZE(TwinkleColors))];\n    delay(200);       \n}"
  },
  {
    "path": "LED Episode 10/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "LED Episode 10/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:heltec_wifi_kit_32]\nplatform = espressif32\nboard = heltec_wifi_kit_32\nframework = arduino\nbuild_flags = -Wno-unused-variable\nupload_port = /dev/cu.SLAB_USBtoUART\nmonitor_port = /dev/cu.SLAB_USBtoUART\nmonitor_speed = 115200\n\nlib_deps = U8g2\n           FastLED\n           \n        \n           \n\n\n\n"
  },
  {
    "path": "LED Episode 10/src/main.cpp",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        LED Episode 08\n//\n// Description:\n//\n//   Draws sample effects on a an addressable strip using FastLED\n//\n// History:     Sep-15-2020     davepl      Created\n//              Oct-05-2020     davepl      Revised for Episode 07\n//              Oct-11-2020     davepl      Revised for Episode 08\n//              Oct-16-2020     davepl      Revised for Episode 09\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>            // Arduino Framework\n#include <U8g2lib.h>            // For text on the little on-chip OLED\n#define FASTLED_INTERNAL        // Suppress build banner\n#include <FastLED.h>\n\n#define OLED_CLOCK  15          // Pins for the OLED display\n#define OLED_DATA   4\n#define OLED_RESET  16\n\n#define NUM_LEDS    40          // FastLED definitions\n#define LED_PIN     5\n\nCRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED\n\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);\nint g_lineHeight = 0;\nint g_Brightness = 255;         // 0-255 LED brightness scale\nint g_PowerLimit = 3000;         // 900mW Power Limit\n\n#include \"ledgfx.h\"\n#include \"comet.h\"\n#include \"marquee.h\"\n#include \"twinkle.h\"\n#include \"fire.h\"\n\nvoid setup() \n{\n  pinMode(LED_BUILTIN, OUTPUT);\n  pinMode(LED_PIN, OUTPUT);\n\n  Serial.begin(115200);\n  while (!Serial) { }\n  Serial.println(\"ESP32 Startup\");\n\n  g_OLED.begin();\n  g_OLED.clear();\n  g_OLED.setFont(u8g2_font_profont15_tf);\n  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total\n\n  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library\n  FastLED.setBrightness(g_Brightness);\n  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle\n  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled\n}\n\nvoid DrawMarqueeComparison()\n{\n  static float scroll = 0.0f;\n  scroll += 0.1f;\n  if (scroll > 5.0)\n    scroll -= 5.0;\n\n  for (float i = scroll; i < NUM_LEDS/2 -1; i+= 5)\n  {\n    DrawPixels(i, 3, CRGB::Green);\n    DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);\n  }\n}\n\nvoid loop() \n{\n  bool bLED = 0;\n\n  //ClassicFireEffect fire(NUM_LEDS, 30, 100, 3, 2, false, true);   // Outwards from Middle\n  //ClassicFireEffect fire(NUM_LEDS, 30, 100, 3, 2, true, true);    // Inwards toward Middle\n  //ClassicFireEffect fire(NUM_LEDS, 20, 100, 3, 4, true, false);     // Outwards from Zero\n  //ClassicFireEffect fire(NUM_LEDS, 20, 100, 3, 4, false, false);     // Inwards from End\n  //ClassicFireEffect fire(NUM_LEDS, 50, 300, 30, 12, true, false);     // More Intense, Extra Sparking\n\n  ClassicFireEffect fire(NUM_LEDS, 20, 100, 3, NUM_LEDS, true, false);     // Fan with correct rotation\n\n  while (true)\n  {\n    FastLED.clear();\n    fire.DrawFire();\n    FastLED.show(g_Brightness);                          //  Show and delay\n\n    EVERY_N_MILLISECONDS(250)\n    {\n      g_OLED.clearBuffer();\n      g_OLED.setCursor(0, g_lineHeight);\n      g_OLED.printf(\"FPS  : %u\", FastLED.getFPS());\n      g_OLED.setCursor(0, g_lineHeight * 2);\n      g_OLED.printf(\"Power: %u mW\", calculate_unscaled_power_mW(g_LEDs, 4));\n      g_OLED.setCursor(0, g_lineHeight * 3);\n      g_OLED.printf(\"Brite: %d\", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));\n      g_OLED.sendBuffer();\n    }\n    delay(33);\n  }\n}\n"
  },
  {
    "path": "LED Episode 11/.gitignore",
    "content": ".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",
    "content": "{\r\n    // See http://go.microsoft.com/fwlink/?LinkId=827846\r\n    // for the documentation about the extensions.json format\r\n    \"recommendations\": [\r\n        \"platformio.platformio-ide\"\r\n    ]\r\n}\r\n"
  },
  {
    "path": "LED Episode 11/include/bounce.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        bounce.h     \n//\n// Description:\n//\n//      Bouncing Ball effect on an LED strip\n//\n// History:     Oct-04-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nusing namespace std;\n#include <vector>\n\n#include \"ledgfx.h\"\n\nstatic const CRGB ballColors [] =\n{\n    CRGB::Green,\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Orange,\n    CRGB::Indigo\n};\n\nclass BouncingBallEffect\n{\n  private:\n\n    double InitialBallSpeed(double height) const\n    {\n        return sqrt(-2 * Gravity * height);         // Because MATH!\n    }\n\n    size_t  _cLength;           \n    size_t  _cBalls;\n    byte    _fadeRate;\n    bool    _bMirrored;\n\n    const double Gravity = -9.81;                   // Because PHYSICS!\n    const double StartHeight = 1;                   // Drop balls from max height initially\n    const double ImpactVelocity = InitialBallSpeed(StartHeight);\n    const double SpeedKnob = 4.0;                   // Higher values will slow the effect\n\n    vector<double> ClockTimeAtLastBounce, Height, BallSpeed, Dampening;\n    vector<CRGB>   Colors;\n\n  public:\n\n    // BouncingBallEffect\n    //\n    // Caller specs strip length, number of balls, persistence level (255 is least), and whether the\n    // balls should be drawn mirrored from each side.\n\n    BouncingBallEffect(size_t cLength, size_t ballCount = 3, byte fade = 0, bool bMirrored = false)\n        : _cLength(cLength - 1),\n          _cBalls(ballCount),\n          _fadeRate(fade),\n          _bMirrored(bMirrored),\n          ClockTimeAtLastBounce(ballCount),\n          Height(ballCount),\n          BallSpeed(ballCount),\n          Dampening(ballCount),\n          Colors(ballCount)\n    {\n        for (size_t i = 0; i < ballCount; i++)\n        {\n            Height[i]                = StartHeight;                 // Current Ball Height\n            ClockTimeAtLastBounce[i] = UnixTime();                  // When ball last hit ground state\n            Dampening[i]             = 0.90 - i / pow(_cBalls, 2);  // Bounciness of this ball\n            BallSpeed[i]             = InitialBallSpeed(Height[i]); // Don't dampen initial launch\n            Colors[i]                = ballColors[i % ARRAYSIZE(ballColors) ];\n        }\n    }\n\n    // Draw\n    //\n    // Draw each of the balls.  When any ball settles with too little energy, it it \"kicked\" to restart it\n\n    virtual void Draw()\n    {\n        if (_fadeRate != 0)\n        {\n            for (size_t i = 0; i < _cLength; i++)\n                FastLED.leds()[i].fadeToBlackBy(_fadeRate);\n        }\n        else\n            FastLED.clear();\n        \n        // Draw each of the balls\n\n        for (size_t i = 0; i < _cBalls; i++)\n        {\n            double TimeSinceLastBounce = (UnixTime() - ClockTimeAtLastBounce[i]) / SpeedKnob;\n\n            // Use standard constant acceleration function - https://en.wikipedia.org/wiki/Acceleration\n            Height[i] = 0.5 * Gravity * pow(TimeSinceLastBounce, 2.0) + BallSpeed[i] * TimeSinceLastBounce;\n\n            // Ball hits ground - bounce!\n            if (Height[i] < 0)\n            {\n                Height[i] = 0;\n                BallSpeed[i] = Dampening[i] * BallSpeed[i];\n                ClockTimeAtLastBounce[i] = Time();\n\n                if (BallSpeed[i] < 0.01)\n                    BallSpeed[i] = InitialBallSpeed(StartHeight) * Dampening[i];\n            }\n\n            size_t position = (size_t)(Height[i] * (_cLength - 1) / StartHeight);\n\n            FastLED.leds()[position]   += Colors[i];\n            FastLED.leds()[position+1] += Colors[i];\n\n            if (_bMirrored)\n            {\n                FastLED.leds()[_cLength - 1 - position] += Colors[i];\n                FastLED.leds()[_cLength - position]     += Colors[i];\n            }\n        }\n        delay(20);\n    }\n};"
  },
  {
    "path": "LED Episode 11/include/comet.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:                  \n//\n// Description:\n//\n//   \n//\n// History:     Sep-28-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\nvoid DrawComet()\n{\n    const byte fadeAmt = 128;\n    const int cometSize = 5;\n    const int deltaHue  = 4;\n\n    static byte hue = HUE_RED;\n    static int iDirection = 1;\n    static int iPos = 0;\n\n    hue += deltaHue;\n\n    iPos += iDirection;\n    if (iPos == (FastLED.count() - cometSize) || iPos == 0)\n        iDirection *= -1;\n    \n    for (int i = 0; i < cometSize; i++)\n        FastLED.leds()[iPos + i].setHue(hue);\n    \n    // Randomly fade the LEDs\n    for (int j = 0; j < FastLED.count(); j++)\n        if (random(10) > 5)\n            FastLED.leds()[j] = FastLED.leds()[j].fadeToBlackBy(fadeAmt);  \n\n    delay(30);\n}"
  },
  {
    "path": "LED Episode 11/include/fire.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        fire.h                    \n//\n// Description: A realistic flame simulation for LED strips\n//\n// History:     Oct-23-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include \"ledgfx.h\"\n\nclass FireEffect\n{\n  protected:\n    int     Size;               // How many pixels the flame is total\n    int     Cooling;            // Rate at which the pixels cool off\n    int     Sparks;             // How many sparks will be attempted each frame\n    int     SparkHeight;        // If created, max height for a spark\n    int     Sparking;           // Probability of a spark each attempt\n    bool    bReversed;          // If reversed we draw from 0 outwards\n    bool    bMirrored;          // If mirrored we split and duplicate the drawing\n\n    byte  * heat;\n\n    // When diffusing the fire upwards, these control how much to blend in from the cells below (ie: downward neighbors)\n    // You can tune these coefficients to control how quickly and smoothly the fire spreads\n\n    static const byte BlendSelf = 2;\n    static const byte BlendNeighbor1 = 3;\n    static const byte BlendNeighbor2 = 2;\n    static const byte BlendNeighbor3 = 1;\n    static const byte BlendTotal = (BlendSelf + BlendNeighbor1 + BlendNeighbor2 + BlendNeighbor3);\n\n  public:\n\n    FireEffect(int size, int cooling = 20, int sparking = 100, int sparks = 3, int sparkHeight = 4, bool breversed = true, bool bmirrored = true)\n        : Size(size),\n          Cooling(cooling),\n          Sparks(sparks),\n          SparkHeight(sparkHeight),\n          Sparking(sparking),\n          bReversed(breversed),\n          bMirrored(bmirrored)\n    {\n        if (bMirrored)\n            Size = Size / 2;\n\n        heat = new byte[size] { 0 };\n    }\n\n    virtual ~FireEffect()\n    {\n        delete [] heat;\n    }\n\n    virtual void DrawFire(PixelOrder order = Sequential)\n    {\n        // First cool each cell by a litle bit\n        for (int i = 0; i < Size; i++)\n            heat[i] = max(0L, heat[i] - random(0, ((Cooling * 10) / Size) + 2));\n\n        // Next drift heat up and diffuse it a little bit\n        for (int i = 0; i < Size; i++)\n            heat[i] = (heat[i] * BlendSelf +\n                       heat[(i + 1) % Size] * BlendNeighbor1 +\n                       heat[(i + 2) % Size] * BlendNeighbor2 +\n                       heat[(i + 3) % Size] * BlendNeighbor3)\n                      / BlendTotal;\n\n        // Randomly ignite new sparks down in the flame kernel\n\n        for (int i = 0 ; i < Sparks; i++)\n        {\n            if (random(255) < Sparking)\n            {\n                int y = Size - 1 - random(SparkHeight);\n                heat[y] = heat[y] + random(160, 255);       // Can roll over which actually looks good!\n            }\n        }\n\n        // Finally, convert heat to a color\n\n        for (int i = 0; i < Size; i++)\n        {\n            CRGB color = HeatColor(heat[i]);\n            int j = bReversed ? (Size - 1 - i) : i;\n            DrawFanPixels(j, 1, color, order);\n            if (bMirrored)\n                DrawFanPixels(!bReversed ? (2 * Size - 1 - i) : Size + i, 1, color, order);\n        }\n    }\n};"
  },
  {
    "path": "LED Episode 11/include/ledgfx.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        ledgfx.h\n//\n// Description:\n//\n//   LED Drawing Routines for Dave's Garage Tutorial series\n//\n// History:     OCt-18-2020     davepl      Created from main.cpp code\n//---------------------------------------------------------------------------\n\n#pragma once\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include <sys/time.h>                   // For time-of-day\n\n// Utility Macros\n\n#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))\n#define TIMES_PER_SECOND(x) EVERY_N_MILLISECONDS(1000/x)\n\n// Simple definitions of what direction we're talking about\n\nenum PixelOrder\n{\n  Sequential  = 0,\n  Reverse     = 1,\n  BottomUp    = 2,\n  TopDown     = 4,\n  LeftRight   = 8,\n  RightLeft   = 16\n};\n\nDEFINE_GRADIENT_PALETTE( vu_gpGreen ) \n{\n      0,     0,   4,   0,   // near black green\n     64,     0, 255,   0,   // green\n    128,   255, 255,   0,   // yellow\n    192,   255,   0,   0,   // red\n    255,   255,   0,   0    // red\n};\n\nDEFINE_GRADIENT_PALETTE( gpSeahawks ) \n{\n    0,       0,     0,   4,      \n    64,      3,    38,  58,      \n   128,      0,    21,  50,      \n   192,     78,   167,   1,      \n   255,     54,    87, 140,      \n};\n\n// These tables represent the physical order of LEDs when looking at\n// the fan in a particular direction, like top to bottom or left to right\n\nstatic const int FanPixelsVertical[FAN_SIZE] =\n{\n  0, 1, 15, 2, 14, 3, 13, 4, 12, 5, 11, 6, 10, 7, 9, 8\n};\n\nstatic const int FanPixelsHorizontal[FAN_SIZE] =\n{\n  3, 4, 2, 5, 1, 6, 0, 7, 15, 8, 14, 9, 13, 10, 12, 11\n};\n\n// GetFanPixelOrder\n// \n// Returns the sequential strip postion of a an LED on the fans based\n// on the index and direction specified, like 32nd most TopDown pixel.\n\nint GetFanPixelOrder(int iPos, PixelOrder order = Sequential)\n{\n  while (iPos < 0)\n    iPos += FAN_SIZE;\n\n  int offset = (iPos + LED_FAN_OFFSET) % FAN_SIZE;\n  int roffset = (iPos + FAN_SIZE - LED_FAN_OFFSET) % FAN_SIZE;\n  int fanBase = iPos - (iPos % FAN_SIZE);\n\n  switch (order)\n  {\n    case BottomUp:\n      return fanBase + FAN_SIZE - 1 - (FanPixelsVertical[iPos % FAN_SIZE] + LED_FAN_OFFSET) % FAN_SIZE;\n    \n    case TopDown:\n      return NUM_LEDS - 1 - (fanBase + (FanPixelsVertical[FAN_SIZE - 1 - (iPos % FAN_SIZE)] + LED_FAN_OFFSET) % FAN_SIZE);\n    \n    case LeftRight:\n      return fanBase + (FanPixelsHorizontal[ iPos % FAN_SIZE ] + LED_FAN_OFFSET - 1) % FAN_SIZE;\n    \n    case RightLeft:\n      return fanBase + (FanPixelsHorizontal[FAN_SIZE - 1 - (iPos % FAN_SIZE)] + LED_FAN_OFFSET - 1) % FAN_SIZE;\n\n    case Reverse:\n      return fanBase + FAN_SIZE - 1 - roffset;\n\n    case Sequential:\n    default:\n      return fanBase + offset;\n  }\n}\n\n\ninline float RandomFloat()\n{\n    float r = random(1000000L) / 1000000.0f;\n    return r;\n}\n\ninline double UnixTime()\n{\n    timeval tv = { 0 };\n    gettimeofday(&tv, nullptr);\n    return (double)(tv.tv_usec / 1000000.0 + (double) tv.tv_sec);\n}\n\n// FractionalColor\n//\n// Returns a fraction of a color; abstracts the fadeToBlack out to this function in case we\n// want to improve the color math or do color correction all in one location at a later date.\n\nCRGB ColorFraction(CRGB colorIn, float fraction)\n{\n  fraction = min(1.0f, fraction);\n  return CRGB(colorIn).fadeToBlackBy(255 * (1.0f - fraction));\n}\n\n// DrawFanPixels\n//\n// Just like DrawPixels but draws logically into a fan bank in a direction such as top down rather than\n// just straight sequential strip order\n\nvoid DrawFanPixels(float fPos, float count, CRGB color, PixelOrder order = Sequential, int iFan = 0)\n{\n  fPos += iFan * FAN_SIZE;\n\n  // Calculate how much the first pixel will hold\n\n  float availFirstPixel = 1.0f - (fPos - (long)(fPos));\n  float amtFirstPixel = min(availFirstPixel, count);\n  float remaining = min(count, FastLED.size()-fPos);\n  int iPos = fPos;\n\n  // Blend (add) in the color of the first partial pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[GetFanPixelOrder(iPos++, order)] += ColorFraction(color, amtFirstPixel);\n    remaining -= amtFirstPixel;\n  }\n\n  // Now draw any full pixels in the middle\n\n  while (remaining > 1.0f)\n  {\n    FastLED.leds()[GetFanPixelOrder(iPos++, order)] += color;\n    remaining--;\n  }\n\n  // Draw tail pixel, up to a single full pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[GetFanPixelOrder(iPos, order)] += ColorFraction(color, remaining);\n  }\n}\n\n// DrawPixels\n// \n// Uses floating point math to draw a floating point number of pixels starting at a \n// floating point offset into the strip\n\nvoid DrawPixels(float fPos, float count, CRGB color)\n{\n  // Calculate how much the first pixel will hold\n  float availFirstPixel = 1.0f - (fPos - (long)(fPos));\n  float amtFirstPixel = min(availFirstPixel, count);\n  float remaining = min(count, FastLED.size()-fPos);\n  int iPos = fPos;\n\n  // Blend (add) in the color of the first partial pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos++] += ColorFraction(color, amtFirstPixel);\n    remaining -= amtFirstPixel;\n  }\n\n  // Now draw any full pixels in the middle\n\n  while (remaining > 1.0f)\n  {\n    FastLED.leds()[iPos++] += color;\n    remaining--;\n  }\n\n  // Draw tail pixel, up to a single full pixel\n\n  if (remaining > 0.0f)\n  {\n    FastLED.leds()[iPos] += ColorFraction(color, remaining);\n  }\n}\n"
  },
  {
    "path": "LED Episode 11/include/marquee.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        marque.h\n//\n// Description:\n//\n//   Draws a theatre-style marquee\n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#include <U8g2lib.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n\nvoid DrawMarquee()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < FastLED.count(); i ++)\n        FastLED.leds()[i] = c.setHue(k+=8);\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < FastLED.count() - 1; i += 5)\n    {\n        FastLED.leds()[i] = CRGB::Black;\n    }\n    delay(50);\n}\n\nvoid DrawMarqueeMirrored()\n{\n    static byte j = 0;\n    j+=4;\n    byte k = j;\n\n    // Roughly equivalent to fill_rainbow(g_LEDs, NUM_LEDS, j, 8);\n\n    CRGB c;\n    for (int i = 0; i < (FastLED.count() + 1) / 2; i ++)\n    {\n        FastLED.leds()[i] = c.setHue(k);\n        FastLED.leds()[FastLED.count() - 1 - i] = c.setHue(k);\n        k+= 8;\n    }\n\n\n    static int scroll = 0;\n    scroll++;\n\n    for (int i = scroll % 5; i < FastLED.count() / 2; i += 5)\n    {\n        FastLED.leds()[i] = CRGB::Black;\n        FastLED.leds()[FastLED.count() - 1 - i] = CRGB::Black;\n    }   \n\n    delay(50);\n}\n\n\n\n"
  },
  {
    "path": "LED Episode 11/include/twinkle.h",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2020 Dave Plummer.  All Rights Reserved.\n//\n// File:        \n//\n// Description:\n//\n//   \n//\n// History:     Sep-15-2020     davepl      Created\n//\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>\n#define FASTLED_INTERNAL\n#include <FastLED.h>\n\n#include \"ledgfx.h\"\n\nstatic const CRGB TwinkleColors [] = \n{\n    CRGB::Red,\n    CRGB::Blue,\n    CRGB::Purple,\n    CRGB::Green,\n    CRGB::Yellow\n};\n\nvoid DrawTwinkle()\n{\n    static int passCount = 0;\n    if (passCount++ == FastLED.count()/4)\n    {\n        passCount = 0;\n        FastLED.clear(false);\n    }\n    FastLED.leds()[random(FastLED.count())] = TwinkleColors[random(0, ARRAYSIZE(TwinkleColors))];\n    delay(200);       \n}"
  },
  {
    "path": "LED Episode 11/lib/README",
    "content": "\nThis directory is intended for project specific (private) libraries.\nPlatformIO will compile them to static libraries and link into executable file.\n\nThe source code of each library should be placed in a an own separate directory\n(\"lib/your_library_name/[here are source files]\").\n\nFor example, see a structure of the following two libraries `Foo` and `Bar`:\n\n|--lib\n|  |\n|  |--Bar\n|  |  |--docs\n|  |  |--examples\n|  |  |--src\n|  |     |- Bar.c\n|  |     |- Bar.h\n|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html\n|  |\n|  |--Foo\n|  |  |- Foo.c\n|  |  |- Foo.h\n|  |\n|  |- README --> THIS FILE\n|\n|- platformio.ini\n|--src\n   |- main.c\n\nand a contents of `src/main.c`:\n```\n#include <Foo.h>\n#include <Bar.h>\n\nint main (void)\n{\n  ...\n}\n\n```\n\nPlatformIO Library Dependency Finder will find automatically dependent\nlibraries scanning project source files.\n\nMore information about PlatformIO Library Dependency Finder\n- https://docs.platformio.org/page/librarymanager/ldf.html\n"
  },
  {
    "path": "LED Episode 11/platformio.ini",
    "content": "; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https://docs.platformio.org/page/projectconf.html\n\n[env:heltec_wifi_kit_32]\nplatform = espressif32\nboard = heltec_wifi_kit_32\nframework = arduino\nbuild_flags = -Wno-unused-variable\nupload_port = /dev/cu.SLAB_USBtoUART\nmonitor_port = /dev/cu.SLAB_USBtoUART\nmonitor_speed = 115200\n\nlib_deps = U8g2\n           FastLED\n           \n        \n           \n\n\n\n"
  },
  {
    "path": "LED Episode 11/src/main.cpp",
    "content": "//+--------------------------------------------------------------------------\n//\n// NightDriver - (c) 2018 Dave Plummer.  All Rights Reserved.\n//\n// File:        LED Episode 11\n//\n// Description:\n//\n//   Draws sample effects on a an addressable strip using FastLED\n//\n// History:     Sep-15-2020     davepl      Created\n//              Oct-05-2020     davepl      Revised for Episode 07\n//              Oct-11-2020     davepl      Revised for Episode 08\n//              Oct-16-2020     davepl      Revised for Episode 09\n//              Oct-23-2020     davepl      Revised for Episode 10\n//---------------------------------------------------------------------------\n\n#include <Arduino.h>            // Arduino Framework\n#include <U8g2lib.h>            // For text on the little on-chip OLED\n#define FASTLED_INTERNAL        // Suppress build banner\n#include <FastLED.h>\n\n#define OLED_CLOCK  15          // Pins for the OLED display\n#define OLED_DATA   4\n#define OLED_RESET  16\n\n#define FAN_SIZE      16        // Number of LEDs in each fan\n#define NUM_FANS       3        // Number of Fans\n#define LED_FAN_OFFSET 4        // How far from bottom first pixel is\n#define NUM_LEDS      48        // FastLED definitions\n#define LED_PIN        5\n\nCRGB g_LEDs[NUM_LEDS] = {0};    // Frame buffer for FastLED\n\nU8G2_SSD1306_128X64_NONAME_F_HW_I2C g_OLED(U8G2_R2, OLED_RESET, OLED_CLOCK, OLED_DATA);\nint g_lineHeight = 0;\nint g_Brightness = 255;         // 0-255 LED brightness scale\nint g_PowerLimit = 3000;        // 900mW Power Limit\n\n#include \"ledgfx.h\"\n#include \"comet.h\"\n#include \"marquee.h\"\n#include \"twinkle.h\"\n#include \"fire.h\"\n\nvoid setup() \n{\n  pinMode(LED_BUILTIN, OUTPUT);\n  pinMode(LED_PIN, OUTPUT);\n\n  Serial.begin(115200);\n  while (!Serial) { }\n  Serial.println(\"ESP32 Startup\");\n\n  g_OLED.begin();\n  g_OLED.clear();\n  g_OLED.setFont(u8g2_font_profont15_tf);\n  g_lineHeight = g_OLED.getFontAscent() - g_OLED.getFontDescent();        // Descent is a negative number so we add it to the total\n\n  FastLED.addLeds<WS2812B, LED_PIN, GRB>(g_LEDs, NUM_LEDS);               // Add our LED strip to the FastLED library\n  FastLED.setBrightness(g_Brightness);\n  set_max_power_indicator_LED(LED_BUILTIN);                               // Light the builtin LED if we power throttle\n  FastLED.setMaxPowerInMilliWatts(g_PowerLimit);                          // Set the power limit, above which brightness will be throttled\n}\n\nvoid DrawMarqueeComparison()\n{\n  static float scroll = 0.0f;\n  scroll += 0.1f;\n  if (scroll > 5.0)\n    scroll -= 5.0;\n\n  for (float i = scroll; i < NUM_LEDS/2 -1; i+= 5)\n  {\n    DrawPixels(i, 3, CRGB::Green);\n    DrawPixels(NUM_LEDS-1-(int)i, 3, CRGB::Red);\n  }\n}\n\nvoid loop() \n{\n  bool bLED = 0;\n\n  while (true)\n  {\n    FastLED.clear();\n\n    /*\n    // RGB Spinners\n    float b = beat16(60) / 65535.0f * FAN_SIZE;\n    DrawFanPixels(b, 1, CRGB::Red,   Sequential,  0);\n    DrawFanPixels(b, 1, CRGB::Green, Sequential,  1);\n    DrawFanPixels(b, 1, CRGB::Blue,  Sequential,  2);\n    */\n\n    /*\n    // Left to Right Cyan Wipe\n    float b = beatsin16(60) / 65535.0f * FAN_SIZE;\n    for (int iFan = 0; iFan < NUM_FANS; iFan++)\n      DrawFanPixels(0, b, CRGB::Cyan, LeftRight, iFan);\n    */\n\n    /* Right to Left Cyan Wipe\n    float b = beatsin16(60) / 65535.0f * FAN_SIZE;\n    for (int iFan = 0; iFan < NUM_FANS; iFan++)\n      DrawFanPixels(0, b, CRGB::Cyan, RightLeft, iFan);\n    */\n\n    /*\n    // Bottom Up Green Wipe\n    float b = beatsin16(60) / 65535.0f * NUM_LEDS;\n      DrawFanPixels(0, b, CRGB::Green, BottomUp);\n    */\n\n    /*\n    // Top Down Green Wipe\n    float b = beatsin16(60) / 65535.0f * NUM_LEDS;\n        DrawFanPixels(0, b, CRGB::Green, TopDown);    \n    */\n\n    /*\n    // Simple Color Cycle\n    static byte hue = 0;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, CHSV(hue, 255, 255));\n    hue += 4;\n    */\n\n    /*\n    // Sequential Color Rainbows\n    static byte basehue = 0;\n    byte hue = basehue;\n    basehue += 4;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255));\n    basehue += 4;\n    */\n\n    /*\n    // Vertical Rainbow Wipe\n    static byte basehue = 0;\n    byte hue = basehue;\n    basehue += 8;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255), LeftRight);\n    */\n\n    /*\n    static byte basehue = 0;\n    byte hue = basehue;\n    basehue += 8;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, CHSV(hue+=16, 255, 255), BottomUp);\n    */\n\n    /*\n    // Rainbow Strip Palette Effect\n    static CRGBPalette256 pal(RainbowStripeColors_p);\n    static byte baseColor = 0;\n    byte hue = baseColor;\n    for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, ColorFromPalette(pal, hue += 4), BottomUp);\n    baseColor += 1;\n    */\n\n    /*\n    // vu-style Meter bar\n    int b = beatsin16(30) * NUM_LEDS / 65535L;\n    static const CRGBPalette256 vuPaletteGreen = vu_gpGreen;\n    for (int i = 0; i < b; i++)\n      DrawFanPixels(i, 1, ColorFromPalette(vuPaletteGreen, (int)(255 * i / NUM_LEDS)), BottomUp);\n    */\n\n    /*\n    // Sequential Fire Fans\n    static FireEffect fire(NUM_LEDS, 20, 100, 3, NUM_LEDS, true, false);\n    fire.DrawFire();\n    */\n\n    /*\n    // Bottom Up Fire Effect with extra sparking on first fan only\n    static FireEffect fire(NUM_LEDS, 20, 140, 3, FAN_SIZE, true, false);\n    fire.DrawFire(BottomUp);\n    */\n\n    // LeftRight (Wide Style) Fire Effect with extra sparking on first fan only\n\n    /*\n    static FireEffect fire(NUM_LEDS, 20, 140, 3, FAN_SIZE, true, false);\n    fire.DrawFire(LeftRight);\n    for (int i = 0; i < FAN_SIZE; i++)\n    {\n      g_LEDs[i] = g_LEDs[i + 2 * FAN_SIZE];\n      g_LEDs[i + FAN_SIZE] = g_LEDs[i + 2 * FAN_SIZE];\n    }\n    */\n\n   int b = beatsin16(30) * NUM_LEDS / 65535L;\n   static const CRGBPalette256 seahawksPalette = gpSeahawks;\n   for (int i = 0; i < NUM_LEDS; i++)\n      DrawFanPixels(i, 1, ColorFromPalette(seahawksPalette, beat8(64) + (int)(255 * i / NUM_LEDS)), BottomUp);\n  \n\n    FastLED.show(g_Brightness);                          //  Show and delay\n\n    EVERY_N_MILLISECONDS(250)\n    {\n      g_OLED.clearBuffer();\n      g_OLED.setCursor(0, g_lineHeight);\n      g_OLED.printf(\"FPS  : %u\", FastLED.getFPS());\n      g_OLED.setCursor(0, g_lineHeight * 2);\n      g_OLED.printf(\"Power: %u mW\", calculate_unscaled_power_mW(g_LEDs, 4));\n      g_OLED.setCursor(0, g_lineHeight * 3);\n      g_OLED.printf(\"Brite: %d\", calculate_max_brightness_for_power_mW(g_Brightness, g_PowerLimit));\n      g_OLED.sendBuffer();\n    }\n    delay(33);\n  }\n}\n"
  }
]