Skip to content

Latest commit

 

History

History
339 lines (280 loc) · 8.43 KB

File metadata and controls

339 lines (280 loc) · 8.43 KB

LightString.ino

An Arduino sketch for controlling colors and animations on a WS2812 LED string using the FelixTheCatLED and Felix8A libraries.

Sketch & Hardware Setup using FelixTheCatLED::Button

#include <EEPROM.h>
#include <FelixTheCatLED.h>
#include <Felix8A.h>
#include <Adafruit_NeoPixel.h>

/***** FelixTheCatLED::Button Setup *****/
#define BUTTON_PIN 2
FelixTheCatLED::Button button(BUTTON_PIN);

/***** NeoPixel Setup *****/
#define LED_PIN A0
#define NUM_LEDS 100
Adafruit_NeoPixel* lightString = nullptr;

Color Array Setup using Felix8A::Palette

/***** Classic Christmas Tree Light Multi-color Palette *****/
constexpr uint32_t color0 = Felix8A::Color::RED;
constexpr uint32_t color1 = Felix8A::Color::ORANGE;
constexpr uint32_t color2 = Felix8A::Color::GREEN;
constexpr uint32_t color3 = Felix8A::Color::BLUE;
constexpr uint32_t color4 = Felix8A::Color::GRAY;
constexpr uint32_t colorArray[] = { color0, color1, color2, color3, color4 };
constexpr Felix8A::Palette ColorPalette(colorArray);
/***** Preset Classic Christmas Tree Light Multi-color Palette *****/
// const Felix8A::Palette ColorPalette = Felix8A::ChristmasTree;

Initial Variables for Solid Color Palette

/***** Initial Variables for Solid Color Palette *****/
const int numColors = ColorPalette.size();
const int numModes = 4;
int currentMode = 0;
int currentColor = 0;
bool isAnimated = false;
bool stateUpdated = true;

EEPROM Setup using Felix8A::Math::wrap

/***** EEPROM  Setup *****/
#define EEPROM_MODE_ADDR 0
#define EEPROM_COLOR_ADDR 1

void loadSettings() {
  EEPROM.get(EEPROM_MODE_ADDR, currentMode);
  EEPROM.get(EEPROM_COLOR_ADDR, currentColor);

  if (currentMode < 0 || currentMode >= numModes) currentMode = 0;
  if (currentColor < 0 || currentColor >= numColors) currentColor = 0;
}

void saveSettings() {
  currentMode = Felix8A::Math::wrap(currentMode, 0, numModes);
  currentColor = Felix8A::Math::wrap(currentColor, 0, numColors);

  EEPROM.update(EEPROM_MODE_ADDR, currentMode);
  EEPROM.update(EEPROM_COLOR_ADDR, currentColor);
}

Color Setting Functions

Solid Color Setting & Animated Solid Color Firefly Functions

void firefly(uint32_t baseColor) {
  static uint8_t brightness[NUM_LEDS] = {0};
  static int8_t direction[NUM_LEDS] = {0}; // 1 = up, -1 = down, 0 = idle
  static unsigned long lastFireflyUpdate = 0;

  if (!Felix8A::Time::every(30, lastFireflyUpdate)) return;

  for (int i = 0; i < lightString->numPixels(); i++) {
    if (direction[i] == 0) {
      if (random(100) < 3) {
        direction[i] = 1;
        brightness[i] = 10;
      }
    }

    if (direction[i] != 0) {
      brightness[i] += direction[i] * 10;

      if (brightness[i] >= 200) {
        brightness[i] = 200;
        direction[i] = -1;
      }

      if (brightness[i] <= 0) {
        brightness[i] = 0;
        direction[i] = 0;
      }
    }

    uint32_t scaled = Felix8A::Color::scale(baseColor, brightness[i]);
    lightString->setPixelColor(i, scaled);
  }

  lightString->show();
}

void solidColor() {
  if (isAnimated) {
    firefly(ColorPalette[currentColor]);
  } else if (stateUpdated) {
    lightString->fill(ColorPalette[currentColor]); lightString->show();
  }
}

Solid Color to White Gradient Setting Functions using Felix8A::Time

void setColorWhiteGradient(uint32_t color, int step) {
  uint32_t white = Felix8A::Color::rgb(150, 150, 150);
  // uint32_t blend1 = Felix8A::Color::blend(color, white, 64);
  // uint32_t blend2 = Felix8A::Color::blend(color, white, 128);
  // uint32_t blend3 = Felix8A::Color::blend(color, white, 192);
  uint32_t blend1 = Felix8A::Palette::blend(color, white, 64);
  uint32_t blend2 = Felix8A::Palette::blend(color, white, 128);
  uint32_t blend3 = Felix8A::Palette::blend(color, white, 192);

  for (int i = 0; i < lightString->numPixels(); i++) {
    uint8_t phase = (i + step) % 5;

    // if (phase == 0) {
    //   lightString->setPixelColor(i, color);
    // } else if (phase == 1) {
    //   lightString->setPixelColor(i, blend1);
    // } else if (phase == 2) {
    //   lightString->setPixelColor(i, blend2);
    // } else if (phase == 3) {
    //   lightString->setPixelColor(i, blend3);
    // } else {
    //   lightString->setPixelColor(i, white);
    // }

    switch (phase) {
      case 0: lightString->setPixelColor(i, color); break;
      case 1: lightString->setPixelColor(i, blend1); break;
      case 2: lightString->setPixelColor(i, blend2); break;
      case 3: lightString->setPixelColor(i, blend3); break;
      default: lightString->setPixelColor(i, white); break;
    }
  }

  lightString->show();
}

void solidColorGradient() {
  static unsigned long lastUpdate = 0;
  static int animStep = 0;

  if (isAnimated) {
    if (stateUpdated) animStep = 0;
    int numGradientPhases = 5;

    if (Felix8A::Time::every(150, lastUpdate)) {
      setColorWhiteGradient(ColorPalette[currentColor], animStep);
      animStep = (animStep + 1) % numGradientPhases;
    }
  } else if (stateUpdated) {
    setColorWhiteGradient(ColorPalette[currentColor], 0);
  }
}

Multi-color Functions

Multi-color Setting Functions using Felix8A::Time

void setMultiColor(int step) {
  for (int i = 0; i < lightString->numPixels(); i++) {
    lightString->setPixelColor(i, ColorPalette.reversed(i + step));
  }

  lightString->show();
}

void multiColor() {
  static unsigned long lastAnimUpdate = 0;
  static int colorStep = 0;

  if (isAnimated) {
    if (stateUpdated) colorStep = 0;

    if (Felix8A::Time::every(150, lastAnimUpdate)) {
      setMultiColor(colorStep);
      colorStep = (colorStep + 1) % numColors;
    }
  } else if (stateUpdated) {
    setMultiColor(0);
  }
}

Multi-color Twinkle Animation Function

void multicolorTwinkle() {
  static unsigned long lastTwinkle = 0;

  if (Felix8A::Time::every(100, lastTwinkle)) {
    int count = lightString->numPixels();

    for (int i = 0; i < count; i++) {
      uint32_t color = lightString->getPixelColor(i);
      lightString->setPixelColor(i, Felix8A::Color::scale(color, 220));
    }

    int newPixels = random(1, 4);
    for (int i = 0; i < newPixels; i++) {
      int pixel = random(count);
      int randColor = random(ColorPalette.count());
      lightString->setPixelColor(pixel, ColorPalette[randColor]);
    }

    lightString->show();
  }
}

Mode Switch Code

Lights Off Function

void lightsOff() {
  if (stateUpdated) {
    lightString->clear(); lightString->show();
  }
}

Set & Update Function Switch

void updateMode() {
  switch (currentMode) {
    case 0: solidColor(); break;
    case 1: solidColorGradient(); break;
    case 2: multiColor(); break;
    case 3: multicolorTwinkle(); break;
    default: lightsOff(); break;
  }
}

Main Arduino Code

Setup Code

void setup() {
  loadSettings();

  button.begin();
  button.setHoldTime(750);
  button.setMultiClickTime(250);

  if (button.isDown()) {
    lightString = new Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_RGB + NEO_KHZ800);
  } else {
    lightString = new Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
  }
  lightString->begin();
  lightString->setBrightness(51);
  lightString->show();
}

Loop Code

void loop() {
  button.update();

  // if (button.wasClicked()) {
  //   currentMode++;
  //   stateUpdated = true;
  //   saveSettings();
  // }

  // if (button.wasDoubleClicked()) {
  //   currentMode--;
  //   stateUpdated = true;
  //   saveSettings();
  // }

  // if (button.wasTripleClicked()) {
  //   isAnimated = !isAnimated;
  //   stateUpdated = true;
  // }

  // if (button.wasHeld()) {
  //   currentColor++;
  //   stateUpdated = true;
  //   saveSettings();
  // }

  FelixTheCatLED::Button::Event e;

  while ((e = button.poll()) != FelixTheCatLED::Button::Event::None) {
    switch (e) {
      case FelixTheCatLED::Button::Event::Click:
        currentMode++;
        stateUpdated = true;
        saveSettings();
        break;

      case FelixTheCatLED::Button::Event::DoubleClick:
        currentMode--;
        stateUpdated = true;
        saveSettings();
        break;

      case FelixTheCatLED::Button::Event::TripleClick:
        isAnimated = !isAnimated;
        stateUpdated = true;
        break;

      case FelixTheCatLED::Button::Event::Hold:
        currentColor++;
        stateUpdated = true;
        saveSettings();
        break;

      default:
        break;
    }
  }

  updateMode();
  stateUpdated = false;
}
/********1*********2*********3*********4*********5*********6*********7*********/