Initial commit: D1 Mini Blinkin LED Driver Emulator for FTC
This commit is contained in:
commit
dca4c4cb26
12 changed files with 2155 additions and 0 deletions
898
src/main.cpp
Normal file
898
src/main.cpp
Normal file
|
|
@ -0,0 +1,898 @@
|
|||
/**
|
||||
* D1 Mini Blinkin LED Controller - Corrected to match REV Blinkin Java Driver
|
||||
*
|
||||
* This version correctly matches the pattern ordering from the Java RevBlinkinLedDriver class
|
||||
*
|
||||
* Hardware connections:
|
||||
* - D2 (GPIO4): PWM input from REV Control Hub servo port
|
||||
* - D4 (GPIO2): LED data output (WS2812B)
|
||||
* - GND: Common ground with Control Hub
|
||||
* - 5V: Power for D1 Mini (can be from USB or external)
|
||||
*
|
||||
* IMPORTANT: The pattern enum order MUST match the Java driver exactly!
|
||||
*/
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <FastLED.h>
|
||||
|
||||
// Configuration
|
||||
#define NUM_LEDS 120 // Number of LEDs in your strip
|
||||
#define BRIGHTNESS 128 // LED brightness (0-255)
|
||||
#define DEBUG_MODE true // Enable serial debugging
|
||||
#define PWM_INPUT_PIN D2 // Pin for PWM input (GPIO4)
|
||||
#define LED_DATA_PIN D4 // Pin for LED data (GPIO2)
|
||||
#define ONBOARD_LED LED_BUILTIN // Onboard LED for status
|
||||
|
||||
// PWM constants
|
||||
#define PWM_MIN 900 // Minimum valid PWM
|
||||
#define PWM_MAX 2100 // Maximum valid PWM
|
||||
#define PWM_TIMEOUT 100000 // Timeout in microseconds (100ms)
|
||||
#define PWM_DEADBAND 5 // Deadband for PWM changes
|
||||
#define PWM_FILTER_SAMPLES 3 // Number of samples for filtering
|
||||
|
||||
// LED strip
|
||||
CRGB leds[NUM_LEDS];
|
||||
|
||||
// Pattern enumeration - MUST MATCH Java RevBlinkinLedDriver enum order EXACTLY!
|
||||
enum Pattern {
|
||||
// Fixed Palette Patterns (0-31)
|
||||
RAINBOW_RAINBOW_PALETTE, // 0
|
||||
RAINBOW_PARTY_PALETTE, // 1
|
||||
RAINBOW_OCEAN_PALETTE, // 2
|
||||
RAINBOW_LAVA_PALETTE, // 3
|
||||
RAINBOW_FOREST_PALETTE, // 4
|
||||
RAINBOW_WITH_GLITTER, // 5
|
||||
CONFETTI, // 6
|
||||
SHOT_RED, // 7
|
||||
SHOT_BLUE, // 8
|
||||
SHOT_WHITE, // 9
|
||||
SINELON_RAINBOW_PALETTE, // 10
|
||||
SINELON_PARTY_PALETTE, // 11
|
||||
SINELON_OCEAN_PALETTE, // 12
|
||||
SINELON_LAVA_PALETTE, // 13
|
||||
SINELON_FOREST_PALETTE, // 14
|
||||
BEATS_PER_MINUTE_RAINBOW_PALETTE, // 15
|
||||
BEATS_PER_MINUTE_PARTY_PALETTE, // 16
|
||||
BEATS_PER_MINUTE_OCEAN_PALETTE, // 17
|
||||
BEATS_PER_MINUTE_LAVA_PALETTE, // 18
|
||||
BEATS_PER_MINUTE_FOREST_PALETTE, // 19
|
||||
FIRE_MEDIUM, // 20
|
||||
FIRE_LARGE, // 21
|
||||
TWINKLES_RAINBOW_PALETTE, // 22
|
||||
TWINKLES_PARTY_PALETTE, // 23
|
||||
TWINKLES_OCEAN_PALETTE, // 24
|
||||
TWINKLES_LAVA_PALETTE, // 25
|
||||
TWINKLES_FOREST_PALETTE, // 26
|
||||
COLOR_WAVES_RAINBOW_PALETTE, // 27
|
||||
COLOR_WAVES_PARTY_PALETTE, // 28
|
||||
COLOR_WAVES_OCEAN_PALETTE, // 29
|
||||
COLOR_WAVES_LAVA_PALETTE, // 30
|
||||
COLOR_WAVES_FOREST_PALETTE, // 31
|
||||
LARSON_SCANNER_RED, // 32
|
||||
LARSON_SCANNER_GRAY, // 33
|
||||
LIGHT_CHASE_RED, // 34
|
||||
LIGHT_CHASE_BLUE, // 35
|
||||
LIGHT_CHASE_GRAY, // 36
|
||||
HEARTBEAT_RED, // 37
|
||||
HEARTBEAT_BLUE, // 38
|
||||
HEARTBEAT_WHITE, // 39
|
||||
HEARTBEAT_GRAY, // 40
|
||||
BREATH_RED, // 41
|
||||
BREATH_BLUE, // 42
|
||||
BREATH_GRAY, // 43
|
||||
STROBE_RED, // 44
|
||||
STROBE_BLUE, // 45
|
||||
STROBE_GOLD, // 46
|
||||
STROBE_WHITE, // 47
|
||||
|
||||
// CP1: Color 1 Patterns (48-57)
|
||||
CP1_END_TO_END_BLEND_TO_BLACK, // 48
|
||||
CP1_LARSON_SCANNER, // 49
|
||||
CP1_LIGHT_CHASE, // 50
|
||||
CP1_HEARTBEAT_SLOW, // 51
|
||||
CP1_HEARTBEAT_MEDIUM, // 52
|
||||
CP1_HEARTBEAT_FAST, // 53
|
||||
CP1_BREATH_SLOW, // 54
|
||||
CP1_BREATH_FAST, // 55
|
||||
CP1_SHOT, // 56
|
||||
CP1_STROBE, // 57
|
||||
|
||||
// CP2: Color 2 Patterns (58-67)
|
||||
CP2_END_TO_END_BLEND_TO_BLACK, // 58
|
||||
CP2_LARSON_SCANNER, // 59
|
||||
CP2_LIGHT_CHASE, // 60
|
||||
CP2_HEARTBEAT_SLOW, // 61
|
||||
CP2_HEARTBEAT_MEDIUM, // 62
|
||||
CP2_HEARTBEAT_FAST, // 63
|
||||
CP2_BREATH_SLOW, // 64
|
||||
CP2_BREATH_FAST, // 65
|
||||
CP2_SHOT, // 66
|
||||
CP2_STROBE, // 67
|
||||
|
||||
// CP1_2: Color 1 and 2 Patterns (68-77)
|
||||
CP1_2_SPARKLE_1_ON_2, // 68
|
||||
CP1_2_SPARKLE_2_ON_1, // 69
|
||||
CP1_2_COLOR_GRADIENT, // 70
|
||||
CP1_2_BEATS_PER_MINUTE, // 71
|
||||
CP1_2_END_TO_END_BLEND_1_TO_2, // 72
|
||||
CP1_2_END_TO_END_BLEND, // 73
|
||||
CP1_2_NO_BLENDING, // 74
|
||||
CP1_2_TWINKLES, // 75
|
||||
CP1_2_COLOR_WAVES, // 76
|
||||
CP1_2_SINELON, // 77
|
||||
|
||||
// Solid Colors (78-99)
|
||||
HOT_PINK, // 78
|
||||
DARK_RED, // 79
|
||||
RED, // 80
|
||||
RED_ORANGE, // 81
|
||||
ORANGE, // 82
|
||||
GOLD, // 83
|
||||
YELLOW, // 84
|
||||
LAWN_GREEN, // 85
|
||||
LIME, // 86
|
||||
DARK_GREEN, // 87
|
||||
GREEN, // 88
|
||||
BLUE_GREEN, // 89
|
||||
AQUA, // 90
|
||||
SKY_BLUE, // 91
|
||||
DARK_BLUE, // 92
|
||||
BLUE, // 93
|
||||
BLUE_VIOLET, // 94
|
||||
VIOLET, // 95
|
||||
WHITE, // 96
|
||||
GRAY, // 97
|
||||
DARK_GRAY, // 98
|
||||
BLACK // 99
|
||||
};
|
||||
|
||||
// Custom color palette support
|
||||
CRGB customColor1 = CRGB::Red;
|
||||
CRGB customColor2 = CRGB::Blue;
|
||||
|
||||
// Global variables
|
||||
Pattern currentPattern = BLACK;
|
||||
Pattern lastPattern = BLACK;
|
||||
unsigned long lastPWM = 0;
|
||||
unsigned long lastPatternChange = 0;
|
||||
unsigned long lastPWMReceived = 0;
|
||||
bool isActive = false;
|
||||
uint8_t gHue = 0; // For rainbow effects
|
||||
|
||||
// PWM filtering buffer
|
||||
unsigned long pwmBuffer[PWM_FILTER_SAMPLES];
|
||||
int pwmBufferIndex = 0;
|
||||
|
||||
void setup() {
|
||||
// Initialize serial for debugging
|
||||
if (DEBUG_MODE) {
|
||||
Serial.begin(115200);
|
||||
Serial.println("\n\nD1 Mini Blinkin LED Controller - Corrected Version");
|
||||
Serial.println("Pattern order matches Java RevBlinkinLedDriver");
|
||||
Serial.println("Using D2 for PWM input (GPIO4)");
|
||||
Serial.println("Using D4 for LED output (GPIO2)");
|
||||
Serial.println("Waiting for PWM signal...");
|
||||
}
|
||||
|
||||
// Disable WiFi to save power
|
||||
WiFi.mode(WIFI_OFF);
|
||||
WiFi.forceSleepBegin();
|
||||
|
||||
// Initialize pins
|
||||
pinMode(PWM_INPUT_PIN, INPUT);
|
||||
pinMode(ONBOARD_LED, OUTPUT);
|
||||
digitalWrite(ONBOARD_LED, HIGH); // LED off (inverted on D1 Mini)
|
||||
|
||||
// Initialize LED strip
|
||||
FastLED.addLeds<WS2812B, LED_DATA_PIN, GRB>(leds, NUM_LEDS);
|
||||
FastLED.setBrightness(BRIGHTNESS);
|
||||
FastLED.clear();
|
||||
FastLED.show();
|
||||
|
||||
// Initialize PWM buffer
|
||||
for (int i = 0; i < PWM_FILTER_SAMPLES; i++) {
|
||||
pwmBuffer[i] = 0;
|
||||
}
|
||||
|
||||
// Show startup animation
|
||||
startupAnimation();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Read PWM with filtering
|
||||
unsigned long pwm = readPWMFiltered();
|
||||
|
||||
// Check if we have a valid PWM signal
|
||||
if (pwm > 0) {
|
||||
lastPWMReceived = millis();
|
||||
isActive = true;
|
||||
digitalWrite(ONBOARD_LED, LOW); // LED on
|
||||
|
||||
// Convert PWM to pattern
|
||||
int newPattern = pwmToPattern(pwm);
|
||||
|
||||
// Check if pattern changed (with deadband)
|
||||
if (newPattern != currentPattern && abs((int)(pwm - lastPWM)) > PWM_DEADBAND) {
|
||||
lastPattern = currentPattern;
|
||||
currentPattern = (Pattern)newPattern;
|
||||
lastPatternChange = millis();
|
||||
lastPWM = pwm;
|
||||
|
||||
if (DEBUG_MODE) {
|
||||
Serial.print("Pattern: ");
|
||||
Serial.print(getPatternName(currentPattern));
|
||||
Serial.print(" (#");
|
||||
Serial.print(newPattern);
|
||||
Serial.print(", PWM: ");
|
||||
Serial.print(pwm);
|
||||
Serial.println(" us)");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No PWM signal - check for timeout
|
||||
if (millis() - lastPWMReceived > 500) { // 500ms timeout
|
||||
isActive = false;
|
||||
digitalWrite(ONBOARD_LED, HIGH); // LED off
|
||||
currentPattern = BLACK;
|
||||
}
|
||||
}
|
||||
|
||||
// Update LED pattern
|
||||
updatePattern(currentPattern);
|
||||
|
||||
// Periodic status update
|
||||
static unsigned long lastStatus = 0;
|
||||
if (DEBUG_MODE && millis() - lastStatus > 1000) {
|
||||
lastStatus = millis();
|
||||
Serial.print("Status: ");
|
||||
Serial.print(isActive ? "Active" : "Inactive");
|
||||
Serial.print(" | PWM: ");
|
||||
Serial.print(lastPWM);
|
||||
Serial.print(" | Pattern: ");
|
||||
Serial.println(getPatternName(currentPattern));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long readPWMFiltered() {
|
||||
// Read PWM pulse width with filtering
|
||||
unsigned long pulse = pulseIn(PWM_INPUT_PIN, HIGH, PWM_TIMEOUT);
|
||||
|
||||
// Filter out invalid readings
|
||||
if (pulse < PWM_MIN || pulse > PWM_MAX) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Add to filter buffer
|
||||
pwmBuffer[pwmBufferIndex] = pulse;
|
||||
pwmBufferIndex = (pwmBufferIndex + 1) % PWM_FILTER_SAMPLES;
|
||||
|
||||
// Calculate average
|
||||
unsigned long sum = 0;
|
||||
int validSamples = 0;
|
||||
for (int i = 0; i < PWM_FILTER_SAMPLES; i++) {
|
||||
if (pwmBuffer[i] > 0) {
|
||||
sum += pwmBuffer[i];
|
||||
validSamples++;
|
||||
}
|
||||
}
|
||||
|
||||
if (validSamples > 0) {
|
||||
return sum / validSamples;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pwmToPattern(unsigned long pwm) {
|
||||
// Based on Java driver calculation:
|
||||
// BASE_SERVO_POSITION = 0.2525 (505 * 0.0005)
|
||||
// PATTERN_OFFSET = 10
|
||||
// PWM = 1005 + (pattern_number * 10) microseconds
|
||||
|
||||
// Valid PWM range check
|
||||
if (pwm < 1000 || pwm > 2000) {
|
||||
return BLACK; // Default to BLACK for invalid PWM
|
||||
}
|
||||
|
||||
// Calculate pattern number from PWM
|
||||
// Pattern 0 should be at 1005μs, Pattern 1 at 1015μs, etc.
|
||||
int patternNumber = (pwm - 1005) / 10;
|
||||
|
||||
// Add rounding for values in between
|
||||
if ((pwm - 1005) % 10 >= 5) {
|
||||
patternNumber++;
|
||||
}
|
||||
|
||||
// Clamp to valid pattern range (0-99)
|
||||
if (patternNumber < 0) patternNumber = 0;
|
||||
if (patternNumber > 99) patternNumber = 99;
|
||||
|
||||
if (DEBUG_MODE && pwm != lastPWM) {
|
||||
Serial.print("PWM decode: ");
|
||||
Serial.print(pwm);
|
||||
Serial.print("us -> Pattern #");
|
||||
Serial.println(patternNumber);
|
||||
}
|
||||
|
||||
return patternNumber;
|
||||
}
|
||||
|
||||
void updatePattern(Pattern pattern) {
|
||||
static unsigned long lastUpdate = 0;
|
||||
unsigned long currentMillis = millis();
|
||||
|
||||
// Update animations at 60 FPS
|
||||
if (currentMillis - lastUpdate < 16) return;
|
||||
lastUpdate = currentMillis;
|
||||
|
||||
switch(pattern) {
|
||||
// Fixed Palette Patterns
|
||||
case RAINBOW_RAINBOW_PALETTE: rainbowCycle(); break;
|
||||
case RAINBOW_PARTY_PALETTE: partyMode(); break;
|
||||
case RAINBOW_OCEAN_PALETTE: oceanWave(); break;
|
||||
case RAINBOW_LAVA_PALETTE: lavaFlow(); break;
|
||||
case RAINBOW_FOREST_PALETTE: forestBreeze(); break;
|
||||
case RAINBOW_WITH_GLITTER: rainbowWithGlitter(); break;
|
||||
case CONFETTI: confetti(); break;
|
||||
case SHOT_RED: shot(CRGB::Red); break;
|
||||
case SHOT_BLUE: shot(CRGB::Blue); break;
|
||||
case SHOT_WHITE: shot(CRGB::White); break;
|
||||
case SINELON_RAINBOW_PALETTE: sinelon(RainbowColors_p); break;
|
||||
case SINELON_PARTY_PALETTE: sinelon(PartyColors_p); break;
|
||||
case SINELON_OCEAN_PALETTE: sinelon(OceanColors_p); break;
|
||||
case SINELON_LAVA_PALETTE: sinelon(LavaColors_p); break;
|
||||
case SINELON_FOREST_PALETTE: sinelon(ForestColors_p); break;
|
||||
case BEATS_PER_MINUTE_RAINBOW_PALETTE: bpm(RainbowColors_p); break;
|
||||
case BEATS_PER_MINUTE_PARTY_PALETTE: bpm(PartyColors_p); break;
|
||||
case BEATS_PER_MINUTE_OCEAN_PALETTE: bpm(OceanColors_p); break;
|
||||
case BEATS_PER_MINUTE_LAVA_PALETTE: bpm(LavaColors_p); break;
|
||||
case BEATS_PER_MINUTE_FOREST_PALETTE: bpm(ForestColors_p); break;
|
||||
case FIRE_MEDIUM: fire(55); break;
|
||||
case FIRE_LARGE: fire(120); break;
|
||||
case TWINKLES_RAINBOW_PALETTE: twinkles(RainbowColors_p); break;
|
||||
case TWINKLES_PARTY_PALETTE: twinkles(PartyColors_p); break;
|
||||
case TWINKLES_OCEAN_PALETTE: twinkles(OceanColors_p); break;
|
||||
case TWINKLES_LAVA_PALETTE: twinkles(LavaColors_p); break;
|
||||
case TWINKLES_FOREST_PALETTE: twinkles(ForestColors_p); break;
|
||||
case COLOR_WAVES_RAINBOW_PALETTE: colorWaves(RainbowColors_p); break;
|
||||
case COLOR_WAVES_PARTY_PALETTE: colorWaves(PartyColors_p); break;
|
||||
case COLOR_WAVES_OCEAN_PALETTE: colorWaves(OceanColors_p); break;
|
||||
case COLOR_WAVES_LAVA_PALETTE: colorWaves(LavaColors_p); break;
|
||||
case COLOR_WAVES_FOREST_PALETTE: colorWaves(ForestColors_p); break;
|
||||
case LARSON_SCANNER_RED: larsonScanner(CRGB::Red); break;
|
||||
case LARSON_SCANNER_GRAY: larsonScanner(CRGB::Gray); break;
|
||||
case LIGHT_CHASE_RED: lightChase(CRGB::Red); break;
|
||||
case LIGHT_CHASE_BLUE: lightChase(CRGB::Blue); break;
|
||||
case LIGHT_CHASE_GRAY: lightChase(CRGB::Gray); break;
|
||||
case HEARTBEAT_RED: heartbeat(CRGB::Red); break;
|
||||
case HEARTBEAT_BLUE: heartbeat(CRGB::Blue); break;
|
||||
case HEARTBEAT_WHITE: heartbeat(CRGB::White); break;
|
||||
case HEARTBEAT_GRAY: heartbeat(CRGB::Gray); break;
|
||||
case BREATH_RED: breathe(CRGB::Red); break;
|
||||
case BREATH_BLUE: breathe(CRGB::Blue); break;
|
||||
case BREATH_GRAY: breathe(CRGB::Gray); break;
|
||||
case STROBE_RED: strobe(CRGB::Red); break;
|
||||
case STROBE_BLUE: strobe(CRGB::Blue); break;
|
||||
case STROBE_GOLD: strobe(CRGB::Gold); break;
|
||||
case STROBE_WHITE: strobe(CRGB::White); break;
|
||||
|
||||
// CP1 Patterns (use customColor1)
|
||||
case CP1_END_TO_END_BLEND_TO_BLACK: endToEndBlendToBlack(customColor1); break;
|
||||
case CP1_LARSON_SCANNER: larsonScanner(customColor1); break;
|
||||
case CP1_LIGHT_CHASE: lightChase(customColor1); break;
|
||||
case CP1_HEARTBEAT_SLOW: heartbeatSlow(customColor1); break;
|
||||
case CP1_HEARTBEAT_MEDIUM: heartbeat(customColor1); break;
|
||||
case CP1_HEARTBEAT_FAST: heartbeatFast(customColor1); break;
|
||||
case CP1_BREATH_SLOW: breatheSlow(customColor1); break;
|
||||
case CP1_BREATH_FAST: breatheFast(customColor1); break;
|
||||
case CP1_SHOT: shot(customColor1); break;
|
||||
case CP1_STROBE: strobe(customColor1); break;
|
||||
|
||||
// CP2 Patterns (use customColor2)
|
||||
case CP2_END_TO_END_BLEND_TO_BLACK: endToEndBlendToBlack(customColor2); break;
|
||||
case CP2_LARSON_SCANNER: larsonScanner(customColor2); break;
|
||||
case CP2_LIGHT_CHASE: lightChase(customColor2); break;
|
||||
case CP2_HEARTBEAT_SLOW: heartbeatSlow(customColor2); break;
|
||||
case CP2_HEARTBEAT_MEDIUM: heartbeat(customColor2); break;
|
||||
case CP2_HEARTBEAT_FAST: heartbeatFast(customColor2); break;
|
||||
case CP2_BREATH_SLOW: breatheSlow(customColor2); break;
|
||||
case CP2_BREATH_FAST: breatheFast(customColor2); break;
|
||||
case CP2_SHOT: shot(customColor2); break;
|
||||
case CP2_STROBE: strobe(customColor2); break;
|
||||
|
||||
// CP1_2 Patterns (use both colors)
|
||||
case CP1_2_SPARKLE_1_ON_2: sparkle(customColor1, customColor2); break;
|
||||
case CP1_2_SPARKLE_2_ON_1: sparkle(customColor2, customColor1); break;
|
||||
case CP1_2_COLOR_GRADIENT: colorGradient(customColor1, customColor2); break;
|
||||
case CP1_2_BEATS_PER_MINUTE: bpmTwoColor(customColor1, customColor2); break;
|
||||
case CP1_2_END_TO_END_BLEND_1_TO_2: endToEndBlend(customColor1, customColor2); break;
|
||||
case CP1_2_END_TO_END_BLEND: endToEndBlend(customColor1, customColor2); break;
|
||||
case CP1_2_NO_BLENDING: noBlending(customColor1, customColor2); break;
|
||||
case CP1_2_TWINKLES: twinklesTwoColor(customColor1, customColor2); break;
|
||||
case CP1_2_COLOR_WAVES: colorWavesTwoColor(customColor1, customColor2); break;
|
||||
case CP1_2_SINELON: sinelonTwoColor(customColor1, customColor2); break;
|
||||
|
||||
// Solid Colors
|
||||
case HOT_PINK: setAll(CRGB::HotPink); break;
|
||||
case DARK_RED: setAll(CRGB::DarkRed); break;
|
||||
case RED: setAll(CRGB::Red); break;
|
||||
case RED_ORANGE: setAll(CRGB::OrangeRed); break;
|
||||
case ORANGE: setAll(CRGB::Orange); break;
|
||||
case GOLD: setAll(CRGB::Gold); break;
|
||||
case YELLOW: setAll(CRGB::Yellow); break;
|
||||
case LAWN_GREEN: setAll(CRGB::LawnGreen); break;
|
||||
case LIME: setAll(CRGB::Lime); break;
|
||||
case DARK_GREEN: setAll(CRGB::DarkGreen); break;
|
||||
case GREEN: setAll(CRGB::Green); break;
|
||||
case BLUE_GREEN: setAll(CRGB(0, 128, 128)); break;
|
||||
case AQUA: setAll(CRGB::Aqua); break;
|
||||
case SKY_BLUE: setAll(CRGB::SkyBlue); break;
|
||||
case DARK_BLUE: setAll(CRGB::DarkBlue); break;
|
||||
case BLUE: setAll(CRGB::Blue); break;
|
||||
case BLUE_VIOLET: setAll(CRGB::BlueViolet); break;
|
||||
case VIOLET: setAll(CRGB::Violet); break;
|
||||
case WHITE: setAll(CRGB::White); break;
|
||||
case GRAY: setAll(CRGB::Gray); break;
|
||||
case DARK_GRAY: setAll(CRGB::DarkGray); break;
|
||||
case BLACK: setAll(CRGB::Black); break;
|
||||
|
||||
default: setAll(CRGB::Black); break;
|
||||
}
|
||||
|
||||
FastLED.show();
|
||||
}
|
||||
|
||||
// Pattern implementation functions
|
||||
void setAll(CRGB color) {
|
||||
fill_solid(leds, NUM_LEDS, color);
|
||||
}
|
||||
|
||||
void rainbowCycle() {
|
||||
fill_rainbow(leds, NUM_LEDS, gHue, 7);
|
||||
gHue++;
|
||||
}
|
||||
|
||||
void rainbowWithGlitter() {
|
||||
rainbowCycle();
|
||||
if(random8() < 80) {
|
||||
leds[random16(NUM_LEDS)] += CRGB::White;
|
||||
}
|
||||
}
|
||||
|
||||
void partyMode() {
|
||||
fadeToBlackBy(leds, NUM_LEDS, 10);
|
||||
int pos = random16(NUM_LEDS);
|
||||
leds[pos] += CHSV(gHue + random8(64), 200, 255);
|
||||
gHue++;
|
||||
}
|
||||
|
||||
void oceanWave() {
|
||||
fill_palette(leds, NUM_LEDS, gHue, 9, OceanColors_p, 255, LINEARBLEND);
|
||||
gHue++;
|
||||
}
|
||||
|
||||
void lavaFlow() {
|
||||
fill_palette(leds, NUM_LEDS, gHue, 9, LavaColors_p, 255, LINEARBLEND);
|
||||
gHue++;
|
||||
}
|
||||
|
||||
void forestBreeze() {
|
||||
fill_palette(leds, NUM_LEDS, gHue, 9, ForestColors_p, 255, LINEARBLEND);
|
||||
gHue++;
|
||||
}
|
||||
|
||||
void confetti() {
|
||||
fadeToBlackBy(leds, NUM_LEDS, 10);
|
||||
int pos = random16(NUM_LEDS);
|
||||
leds[pos] += CHSV(gHue + random8(64), 200, 255);
|
||||
}
|
||||
|
||||
void shot(CRGB color) {
|
||||
fadeToBlackBy(leds, NUM_LEDS, 10);
|
||||
static int pos = 0;
|
||||
leds[pos] = color;
|
||||
pos = (pos + 1) % NUM_LEDS;
|
||||
}
|
||||
|
||||
void sinelon(CRGBPalette16 palette) {
|
||||
fadeToBlackBy(leds, NUM_LEDS, 20);
|
||||
int pos = beatsin16(13, 0, NUM_LEDS - 1);
|
||||
leds[pos] += ColorFromPalette(palette, gHue, 192);
|
||||
gHue++;
|
||||
}
|
||||
|
||||
void bpm(CRGBPalette16 palette) {
|
||||
uint8_t BeatsPerMinute = 62;
|
||||
uint8_t beat = beatsin8(BeatsPerMinute, 64, 255);
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10));
|
||||
}
|
||||
gHue++;
|
||||
}
|
||||
|
||||
void fire(int cooling) {
|
||||
static byte heat[NUM_LEDS];
|
||||
|
||||
// Cool down
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
heat[i] = qsub8(heat[i], random8(0, ((cooling * 10) / NUM_LEDS) + 2));
|
||||
}
|
||||
|
||||
// Heat drift
|
||||
for(int k = NUM_LEDS - 1; k >= 2; k--) {
|
||||
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3;
|
||||
}
|
||||
|
||||
// Ignite
|
||||
if(random8() < 120) {
|
||||
int y = random8(7);
|
||||
heat[y] = qadd8(heat[y], random8(160, 255));
|
||||
}
|
||||
|
||||
// Convert to LED colors
|
||||
for(int j = 0; j < NUM_LEDS; j++) {
|
||||
CRGB color = HeatColor(heat[j]);
|
||||
leds[j] = color;
|
||||
}
|
||||
}
|
||||
|
||||
void twinkles(CRGBPalette16 palette) {
|
||||
fadeToBlackBy(leds, NUM_LEDS, 80);
|
||||
if(random8() < 80) {
|
||||
leds[random16(NUM_LEDS)] = ColorFromPalette(palette, random8(), 255);
|
||||
}
|
||||
}
|
||||
|
||||
void colorWaves(CRGBPalette16 palette) {
|
||||
static uint16_t sPseudotime = 0;
|
||||
static uint16_t sLastMillis = 0;
|
||||
static uint16_t sHue16 = 0;
|
||||
|
||||
uint8_t brightdepth = beatsin88(341, 96, 224);
|
||||
uint16_t brightnessthetainc16 = beatsin88(203, (25 * 256), (40 * 256));
|
||||
uint8_t msmultiplier = beatsin88(147, 23, 60);
|
||||
|
||||
uint16_t hue16 = sHue16;
|
||||
uint16_t hueinc16 = beatsin88(113, 1, 3000);
|
||||
|
||||
uint16_t ms = millis();
|
||||
uint16_t deltams = ms - sLastMillis;
|
||||
sLastMillis = ms;
|
||||
sPseudotime += deltams * msmultiplier;
|
||||
sHue16 += deltams * beatsin88(400, 5, 9);
|
||||
uint16_t brightnesstheta16 = sPseudotime;
|
||||
|
||||
for(uint16_t i = 0; i < NUM_LEDS; i++) {
|
||||
hue16 += hueinc16;
|
||||
uint8_t hue8 = hue16 / 256;
|
||||
|
||||
brightnesstheta16 += brightnessthetainc16;
|
||||
uint16_t b16 = sin16(brightnesstheta16) + 32768;
|
||||
|
||||
uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536;
|
||||
uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536;
|
||||
bri8 += (255 - brightdepth);
|
||||
|
||||
leds[i] = ColorFromPalette(palette, hue8, bri8);
|
||||
}
|
||||
}
|
||||
|
||||
void larsonScanner(CRGB color) {
|
||||
static int pos = 0;
|
||||
static int dir = 1;
|
||||
|
||||
fadeToBlackBy(leds, NUM_LEDS, 50);
|
||||
leds[pos] = color;
|
||||
|
||||
pos += dir;
|
||||
if(pos <= 0 || pos >= NUM_LEDS - 1) dir = -dir;
|
||||
}
|
||||
|
||||
void lightChase(CRGB color) {
|
||||
static int pos = 0;
|
||||
|
||||
fadeToBlackBy(leds, NUM_LEDS, 90);
|
||||
for(int i = 0; i < NUM_LEDS; i += 3) {
|
||||
leds[(i + pos) % NUM_LEDS] = color;
|
||||
}
|
||||
pos = (pos + 1) % 3;
|
||||
}
|
||||
|
||||
void heartbeat(CRGB color) {
|
||||
static uint8_t BeatsPerMinute = 62;
|
||||
uint8_t beat = beatsin8(BeatsPerMinute, 0, 255);
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
leds[i] = color;
|
||||
leds[i].fadeLightBy(255 - beat);
|
||||
}
|
||||
}
|
||||
|
||||
void heartbeatSlow(CRGB color) {
|
||||
static uint8_t BeatsPerMinute = 40;
|
||||
uint8_t beat = beatsin8(BeatsPerMinute, 0, 255);
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
leds[i] = color;
|
||||
leds[i].fadeLightBy(255 - beat);
|
||||
}
|
||||
}
|
||||
|
||||
void heartbeatFast(CRGB color) {
|
||||
static uint8_t BeatsPerMinute = 100;
|
||||
uint8_t beat = beatsin8(BeatsPerMinute, 0, 255);
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
leds[i] = color;
|
||||
leds[i].fadeLightBy(255 - beat);
|
||||
}
|
||||
}
|
||||
|
||||
void breathe(CRGB color) {
|
||||
uint8_t brightness = beatsin8(12, 50, 255);
|
||||
fill_solid(leds, NUM_LEDS, color);
|
||||
FastLED.setBrightness(scale8(BRIGHTNESS, brightness));
|
||||
}
|
||||
|
||||
void breatheSlow(CRGB color) {
|
||||
uint8_t brightness = beatsin8(8, 50, 255);
|
||||
fill_solid(leds, NUM_LEDS, color);
|
||||
FastLED.setBrightness(scale8(BRIGHTNESS, brightness));
|
||||
}
|
||||
|
||||
void breatheFast(CRGB color) {
|
||||
uint8_t brightness = beatsin8(20, 50, 255);
|
||||
fill_solid(leds, NUM_LEDS, color);
|
||||
FastLED.setBrightness(scale8(BRIGHTNESS, brightness));
|
||||
}
|
||||
|
||||
void strobe(CRGB color) {
|
||||
static unsigned long lastStrobe = 0;
|
||||
static bool on = false;
|
||||
|
||||
if(millis() - lastStrobe > 50) {
|
||||
on = !on;
|
||||
lastStrobe = millis();
|
||||
fill_solid(leds, NUM_LEDS, on ? color : CRGB::Black);
|
||||
}
|
||||
}
|
||||
|
||||
void endToEndBlendToBlack(CRGB color) {
|
||||
static uint8_t pos = 0;
|
||||
|
||||
fadeToBlackBy(leds, NUM_LEDS, 10);
|
||||
|
||||
// Create gradient from color to black
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
uint8_t brightness = 255 - (abs(i - pos) * 8);
|
||||
if(brightness > 0) {
|
||||
leds[i] = color;
|
||||
leds[i].fadeLightBy(255 - brightness);
|
||||
}
|
||||
}
|
||||
|
||||
pos = (pos + 1) % NUM_LEDS;
|
||||
}
|
||||
|
||||
void sparkle(CRGB sparkleColor, CRGB baseColor) {
|
||||
fill_solid(leds, NUM_LEDS, baseColor);
|
||||
|
||||
if(random8() < 80) {
|
||||
leds[random16(NUM_LEDS)] = sparkleColor;
|
||||
}
|
||||
}
|
||||
|
||||
void colorGradient(CRGB color1, CRGB color2) {
|
||||
fill_gradient_RGB(leds, 0, color1, NUM_LEDS-1, color2);
|
||||
}
|
||||
|
||||
void bpmTwoColor(CRGB color1, CRGB color2) {
|
||||
uint8_t BeatsPerMinute = 62;
|
||||
uint8_t beat = beatsin8(BeatsPerMinute, 0, 255);
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
if(i % 2 == 0) {
|
||||
leds[i] = color1;
|
||||
leds[i].fadeLightBy(255 - beat);
|
||||
} else {
|
||||
leds[i] = color2;
|
||||
leds[i].fadeLightBy(beat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void endToEndBlend(CRGB color1, CRGB color2) {
|
||||
static uint8_t pos = 0;
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
uint8_t blend = (i + pos) * 255 / NUM_LEDS;
|
||||
leds[i] = blend_CRGB(color1, color2, blend);
|
||||
}
|
||||
|
||||
pos = (pos + 1) % NUM_LEDS;
|
||||
}
|
||||
|
||||
void noBlending(CRGB color1, CRGB color2) {
|
||||
// Split the strip in half
|
||||
for(int i = 0; i < NUM_LEDS/2; i++) {
|
||||
leds[i] = color1;
|
||||
}
|
||||
for(int i = NUM_LEDS/2; i < NUM_LEDS; i++) {
|
||||
leds[i] = color2;
|
||||
}
|
||||
}
|
||||
|
||||
void twinklesTwoColor(CRGB color1, CRGB color2) {
|
||||
fadeToBlackBy(leds, NUM_LEDS, 80);
|
||||
|
||||
if(random8() < 80) {
|
||||
int pos = random16(NUM_LEDS);
|
||||
leds[pos] = (random8() < 128) ? color1 : color2;
|
||||
}
|
||||
}
|
||||
|
||||
void colorWavesTwoColor(CRGB color1, CRGB color2) {
|
||||
static uint8_t hue = 0;
|
||||
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
uint8_t brightness = sin8(hue + (i * 10));
|
||||
uint8_t blend = sin8(hue + (i * 5));
|
||||
CRGB color = blend_CRGB(color1, color2, blend);
|
||||
leds[i] = color;
|
||||
leds[i].fadeLightBy(255 - brightness);
|
||||
}
|
||||
|
||||
hue++;
|
||||
}
|
||||
|
||||
void sinelonTwoColor(CRGB color1, CRGB color2) {
|
||||
fadeToBlackBy(leds, NUM_LEDS, 20);
|
||||
|
||||
int pos1 = beatsin16(13, 0, NUM_LEDS - 1);
|
||||
int pos2 = beatsin16(11, 0, NUM_LEDS - 1);
|
||||
|
||||
leds[pos1] += color1;
|
||||
leds[pos2] += color2;
|
||||
}
|
||||
|
||||
// Helper function for blending colors
|
||||
CRGB blend_CRGB(CRGB color1, CRGB color2, uint8_t blend) {
|
||||
return CRGB(
|
||||
((color1.r * (255 - blend)) + (color2.r * blend)) / 255,
|
||||
((color1.g * (255 - blend)) + (color2.g * blend)) / 255,
|
||||
((color1.b * (255 - blend)) + (color2.b * blend)) / 255
|
||||
);
|
||||
}
|
||||
|
||||
void startupAnimation() {
|
||||
// Quick rainbow sweep
|
||||
for(int i = 0; i < NUM_LEDS; i++) {
|
||||
leds[i] = CHSV(i * 255 / NUM_LEDS, 255, 255);
|
||||
FastLED.show();
|
||||
delay(10);
|
||||
}
|
||||
|
||||
// Fade to black
|
||||
for(int i = 0; i < 50; i++) {
|
||||
fadeToBlackBy(leds, NUM_LEDS, 10);
|
||||
FastLED.show();
|
||||
delay(20);
|
||||
}
|
||||
}
|
||||
|
||||
String getPatternName(Pattern pattern) {
|
||||
switch(pattern) {
|
||||
// Fixed Palette Patterns
|
||||
case RAINBOW_RAINBOW_PALETTE: return "RAINBOW_RAINBOW_PALETTE";
|
||||
case RAINBOW_PARTY_PALETTE: return "RAINBOW_PARTY_PALETTE";
|
||||
case RAINBOW_OCEAN_PALETTE: return "RAINBOW_OCEAN_PALETTE";
|
||||
case RAINBOW_LAVA_PALETTE: return "RAINBOW_LAVA_PALETTE";
|
||||
case RAINBOW_FOREST_PALETTE: return "RAINBOW_FOREST_PALETTE";
|
||||
case RAINBOW_WITH_GLITTER: return "RAINBOW_WITH_GLITTER";
|
||||
case CONFETTI: return "CONFETTI";
|
||||
case SHOT_RED: return "SHOT_RED";
|
||||
case SHOT_BLUE: return "SHOT_BLUE";
|
||||
case SHOT_WHITE: return "SHOT_WHITE";
|
||||
case SINELON_RAINBOW_PALETTE: return "SINELON_RAINBOW_PALETTE";
|
||||
case SINELON_PARTY_PALETTE: return "SINELON_PARTY_PALETTE";
|
||||
case SINELON_OCEAN_PALETTE: return "SINELON_OCEAN_PALETTE";
|
||||
case SINELON_LAVA_PALETTE: return "SINELON_LAVA_PALETTE";
|
||||
case SINELON_FOREST_PALETTE: return "SINELON_FOREST_PALETTE";
|
||||
case BEATS_PER_MINUTE_RAINBOW_PALETTE: return "BPM_RAINBOW_PALETTE";
|
||||
case BEATS_PER_MINUTE_PARTY_PALETTE: return "BPM_PARTY_PALETTE";
|
||||
case BEATS_PER_MINUTE_OCEAN_PALETTE: return "BPM_OCEAN_PALETTE";
|
||||
case BEATS_PER_MINUTE_LAVA_PALETTE: return "BPM_LAVA_PALETTE";
|
||||
case BEATS_PER_MINUTE_FOREST_PALETTE: return "BPM_FOREST_PALETTE";
|
||||
case FIRE_MEDIUM: return "FIRE_MEDIUM";
|
||||
case FIRE_LARGE: return "FIRE_LARGE";
|
||||
case TWINKLES_RAINBOW_PALETTE: return "TWINKLES_RAINBOW_PALETTE";
|
||||
case TWINKLES_PARTY_PALETTE: return "TWINKLES_PARTY_PALETTE";
|
||||
case TWINKLES_OCEAN_PALETTE: return "TWINKLES_OCEAN_PALETTE";
|
||||
case TWINKLES_LAVA_PALETTE: return "TWINKLES_LAVA_PALETTE";
|
||||
case TWINKLES_FOREST_PALETTE: return "TWINKLES_FOREST_PALETTE";
|
||||
case COLOR_WAVES_RAINBOW_PALETTE: return "COLOR_WAVES_RAINBOW_PALETTE";
|
||||
case COLOR_WAVES_PARTY_PALETTE: return "COLOR_WAVES_PARTY_PALETTE";
|
||||
case COLOR_WAVES_OCEAN_PALETTE: return "COLOR_WAVES_OCEAN_PALETTE";
|
||||
case COLOR_WAVES_LAVA_PALETTE: return "COLOR_WAVES_LAVA_PALETTE";
|
||||
case COLOR_WAVES_FOREST_PALETTE: return "COLOR_WAVES_FOREST_PALETTE";
|
||||
case LARSON_SCANNER_RED: return "LARSON_SCANNER_RED";
|
||||
case LARSON_SCANNER_GRAY: return "LARSON_SCANNER_GRAY";
|
||||
case LIGHT_CHASE_RED: return "LIGHT_CHASE_RED";
|
||||
case LIGHT_CHASE_BLUE: return "LIGHT_CHASE_BLUE";
|
||||
case LIGHT_CHASE_GRAY: return "LIGHT_CHASE_GRAY";
|
||||
case HEARTBEAT_RED: return "HEARTBEAT_RED";
|
||||
case HEARTBEAT_BLUE: return "HEARTBEAT_BLUE";
|
||||
case HEARTBEAT_WHITE: return "HEARTBEAT_WHITE";
|
||||
case HEARTBEAT_GRAY: return "HEARTBEAT_GRAY";
|
||||
case BREATH_RED: return "BREATH_RED";
|
||||
case BREATH_BLUE: return "BREATH_BLUE";
|
||||
case BREATH_GRAY: return "BREATH_GRAY";
|
||||
case STROBE_RED: return "STROBE_RED";
|
||||
case STROBE_BLUE: return "STROBE_BLUE";
|
||||
case STROBE_GOLD: return "STROBE_GOLD";
|
||||
case STROBE_WHITE: return "STROBE_WHITE";
|
||||
|
||||
// CP1 Patterns
|
||||
case CP1_END_TO_END_BLEND_TO_BLACK: return "CP1_END_TO_END_BLEND_TO_BLACK";
|
||||
case CP1_LARSON_SCANNER: return "CP1_LARSON_SCANNER";
|
||||
case CP1_LIGHT_CHASE: return "CP1_LIGHT_CHASE";
|
||||
case CP1_HEARTBEAT_SLOW: return "CP1_HEARTBEAT_SLOW";
|
||||
case CP1_HEARTBEAT_MEDIUM: return "CP1_HEARTBEAT_MEDIUM";
|
||||
case CP1_HEARTBEAT_FAST: return "CP1_HEARTBEAT_FAST";
|
||||
case CP1_BREATH_SLOW: return "CP1_BREATH_SLOW";
|
||||
case CP1_BREATH_FAST: return "CP1_BREATH_FAST";
|
||||
case CP1_SHOT: return "CP1_SHOT";
|
||||
case CP1_STROBE: return "CP1_STROBE";
|
||||
|
||||
// CP2 Patterns
|
||||
case CP2_END_TO_END_BLEND_TO_BLACK: return "CP2_END_TO_END_BLEND_TO_BLACK";
|
||||
case CP2_LARSON_SCANNER: return "CP2_LARSON_SCANNER";
|
||||
case CP2_LIGHT_CHASE: return "CP2_LIGHT_CHASE";
|
||||
case CP2_HEARTBEAT_SLOW: return "CP2_HEARTBEAT_SLOW";
|
||||
case CP2_HEARTBEAT_MEDIUM: return "CP2_HEARTBEAT_MEDIUM";
|
||||
case CP2_HEARTBEAT_FAST: return "CP2_HEARTBEAT_FAST";
|
||||
case CP2_BREATH_SLOW: return "CP2_BREATH_SLOW";
|
||||
case CP2_BREATH_FAST: return "CP2_BREATH_FAST";
|
||||
case CP2_SHOT: return "CP2_SHOT";
|
||||
case CP2_STROBE: return "CP2_STROBE";
|
||||
|
||||
// CP1_2 Patterns
|
||||
case CP1_2_SPARKLE_1_ON_2: return "CP1_2_SPARKLE_1_ON_2";
|
||||
case CP1_2_SPARKLE_2_ON_1: return "CP1_2_SPARKLE_2_ON_1";
|
||||
case CP1_2_COLOR_GRADIENT: return "CP1_2_COLOR_GRADIENT";
|
||||
case CP1_2_BEATS_PER_MINUTE: return "CP1_2_BEATS_PER_MINUTE";
|
||||
case CP1_2_END_TO_END_BLEND_1_TO_2: return "CP1_2_END_TO_END_BLEND_1_TO_2";
|
||||
case CP1_2_END_TO_END_BLEND: return "CP1_2_END_TO_END_BLEND";
|
||||
case CP1_2_NO_BLENDING: return "CP1_2_NO_BLENDING";
|
||||
case CP1_2_TWINKLES: return "CP1_2_TWINKLES";
|
||||
case CP1_2_COLOR_WAVES: return "CP1_2_COLOR_WAVES";
|
||||
case CP1_2_SINELON: return "CP1_2_SINELON";
|
||||
|
||||
// Solid Colors
|
||||
case HOT_PINK: return "HOT_PINK";
|
||||
case DARK_RED: return "DARK_RED";
|
||||
case RED: return "RED";
|
||||
case RED_ORANGE: return "RED_ORANGE";
|
||||
case ORANGE: return "ORANGE";
|
||||
case GOLD: return "GOLD";
|
||||
case YELLOW: return "YELLOW";
|
||||
case LAWN_GREEN: return "LAWN_GREEN";
|
||||
case LIME: return "LIME";
|
||||
case DARK_GREEN: return "DARK_GREEN";
|
||||
case GREEN: return "GREEN";
|
||||
case BLUE_GREEN: return "BLUE_GREEN";
|
||||
case AQUA: return "AQUA";
|
||||
case SKY_BLUE: return "SKY_BLUE";
|
||||
case DARK_BLUE: return "DARK_BLUE";
|
||||
case BLUE: return "BLUE";
|
||||
case BLUE_VIOLET: return "BLUE_VIOLET";
|
||||
case VIOLET: return "VIOLET";
|
||||
case WHITE: return "WHITE";
|
||||
case GRAY: return "GRAY";
|
||||
case DARK_GRAY: return "DARK_GRAY";
|
||||
case BLACK: return "BLACK";
|
||||
|
||||
default: return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue