/* Octagon.ino 11/11/2023 This is for the colorful octagonal acrylic slab
with a groove containing 72 WS2812B neo-pixels.
It is intended to be used as a stand for a plant or a dish.
Since there are holes in it and the Adafruit 32u4 3v, 8 MHz ItsyBitsy microcontroller
is underneath, do not spill water on it.
The power connector is 5.5x2.1 coax , +ve center,7.5 - 20V, 10W
*/
#include <FastLED.h>
#include <EEPROM.h>
#define potPort A0
#define incPort 2
#define decPort 3
#define LED_PIN 5
#define NUM_LEDS 72
#define BRIGHTNESS 64
#define LED_TYPE WS2812B
#define COLOR_ORDER GRB
#define NUM_MODES 11 //Thanks to the ColorPalette example in FastLED
#define DOUBLECLICK 800
CRGB leds[NUM_LEDS];
uint8_t displayMode;
bool debugFlag = false;
long doubleClickDelay; //Prevents a second mode step on release.
int potVal;
CRGBPalette16 currentPalette;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
// *********************** Interrupt Handlers *************************
//The mode, which selects the current pallette, is changed by a 3-way momentary switch.
void incSwHardler(void) {
if ((millis() - doubleClickDelay) > DOUBLECLICK) {
displayMode++;
if (displayMode >= NUM_MODES) {
displayMode = 0;
}
debugFlag = true;
EEPROM.write(0, displayMode);
}
doubleClickDelay = millis();
}
void decSwHardler(void) {
if ((millis() - doubleClickDelay) > DOUBLECLICK) {
if (displayMode == 0) {
displayMode = NUM_MODES - 1;
}
else {
displayMode--;
}
debugFlag = true;
EEPROM.write(0, displayMode);
}
doubleClickDelay = millis();
}
// *********************** Palette Definitions *************************
void SetupBlackAndWhiteStripedPalette()
{
// 'black out' all 16 palette entries...
fill_solid( currentPalette, 16, CRGB::Black);
// and set every fourth one to white.
currentPalette[0] = CRGB::White;
currentPalette[4] = CRGB::White;
currentPalette[8] = CRGB::White;
currentPalette[12] = CRGB::White;
}
void SetupPurpleAndGreenPalette()
{
CRGB purple = CHSV( HUE_PURPLE, 255, 255);
CRGB green = CHSV( HUE_GREEN, 255, 255);
CRGB black = CRGB::Black;
currentPalette = CRGBPalette16(
green, green, black, black,
purple, purple, black, black,
green, green, black, black,
purple, purple, black, black );
}
const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
{
CRGB::Red,
CRGB::Gray, // 'white' is too bright compared to red and blue
CRGB::Blue,
CRGB::Black,
CRGB::Red,
CRGB::Gray,
CRGB::Blue,
CRGB::Black,
CRGB::Red,
CRGB::Red,
CRGB::Gray,
CRGB::Gray,
CRGB::Blue,
CRGB::Blue,
CRGB::Black,
CRGB::Black
};
// *********************** Palette Utilities *************************
void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
uint8_t brightness = 255;
for ( int i = 0; i < NUM_LEDS; ++i) {
leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
colorIndex += 3;
}
}
// This function fills the palette with totally random colors.
void SetupNightimePalette()
{
for ( int i = 0; i < 16; ++i) {
currentPalette[i] = CHSV(potVal, 255, (512-potVal));
}
}
int ChangePalettePeriodically()
{
int mult = 1000;
if ( displayMode == 0) {
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
mult = 400;
}
if ( displayMode == 1) {
currentPalette = RainbowStripeColors_p;
currentBlending = NOBLEND;
mult = 1800;
}
if ( displayMode == 2) {
currentPalette = RainbowStripeColors_p;
currentBlending = LINEARBLEND;
}
if ( displayMode == 3) {
SetupPurpleAndGreenPalette();
currentBlending = LINEARBLEND;
mult = 300;
}
if ( displayMode == 4) {
SetupNightimePalette();
currentBlending = LINEARBLEND;
}
if ( displayMode == 5) {
SetupBlackAndWhiteStripedPalette();
currentBlending = NOBLEND;
mult = 400;
}
if ( displayMode == 6) {
SetupBlackAndWhiteStripedPalette();
currentBlending = LINEARBLEND;
mult = 400;
}
if ( displayMode == 7) {
currentPalette = CloudColors_p;
currentBlending = LINEARBLEND;
mult = 250;
}
if ( displayMode == 8) {
currentPalette = PartyColors_p;
currentBlending = LINEARBLEND;
mult = 200;
}
if ( displayMode == 9) {
currentPalette = myRedWhiteBluePalette_p;
currentBlending = NOBLEND;
}
if ( displayMode == 10) {
currentPalette = myRedWhiteBluePalette_p;
currentBlending = LINEARBLEND;
}
return mult;
}
// *********************** Setup and Loop *************************
void setup() {
Serial.begin(9600);
pinMode(incPort, INPUT);
pinMode(decPort, INPUT);
pinMode(LED_PIN , OUTPUT);
delay(300);
displayMode = EEPROM.read(0);
attachInterrupt(digitalPinToInterrupt(incPort), incSwHardler, FALLING);
attachInterrupt(digitalPinToInterrupt(decPort), decSwHardler, FALLING);
FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness( BRIGHTNESS );
currentPalette = RainbowColors_p;
currentBlending = LINEARBLEND;
}
void loop() {
bool clockwise = false;
int updatesPerSecond = 12;
int multiplier = 1000;
// A potentiometer provides speed and direction of rotation control
potVal = analogRead(potPort);
if (potVal > 524) {
clockwise = true;
updatesPerSecond = potVal - 512;
}
if (potVal < 500) {
updatesPerSecond = 512 - potVal;
}
if (debugFlag) {
Serial.print("displayMode = ");
Serial.print(displayMode);
Serial.print(",pot = ");
Serial.println(potVal);
debugFlag = false;
}
multiplier = ChangePalettePeriodically(); // Selects current pallette indexed by mode
static uint8_t startIndex = 0; // Controls rotation
if (clockwise) {
startIndex = startIndex + 1; /* motion speed */
}
else {
startIndex = startIndex - 1;
}
FillLEDsFromPaletteColors( startIndex);
FastLED.show();
FastLED.delay(multiplier / updatesPerSecond);
}