Skip to content

Instantly share code, notes, and snippets.

@joebataz
Created September 15, 2025 17:33
Show Gist options
  • Save joebataz/e385603ed3967fa36c5195edf8974a77 to your computer and use it in GitHub Desktop.
Save joebataz/e385603ed3967fa36c5195edf8974a77 to your computer and use it in GitHub Desktop.
FastLED/DMX no stepper
#include <Conceptinetics.h>
#include <FastLED.h> // FastLED library. Preferably the latest copy of FastLED 2.1.
#define DMX_SLAVE_CHANNELS 16
#define DATA_PIN 5
#define CLOCK_PIN 3
#define NUM_LEDS 64
#define BRIGHTNESS 64
#define LED_TYPE APA102 //RGB,RBG,BRG,BGR
#define COLOR_ORDER BGR
#define UPDATES_PER_SECOND 1000
int p1 = 0;
int p2 = 0;
int p3 = 0;
int p4 = 0;
int p5 = 0;
int p6 = 0;
int p7 = 0;
int p8 = 0;
int p9 = 0;
int p10 = 0;
int p11 = 0;
int p12 = 0;
int p13 = 0;
int p14 = 0;
int p15 = 0;
int p16 = 0;
int CurPatt = 1; //main function selector
int CurGrad = 0; //gradient selector
int bright = 0;
int palIdx = 0;
int SPARKING = 20;
int COOLING = 80;
int bottomColor = 200;
int speedFactor = 8;
int patterns = 19;
// color macros
#define qsubd(x, b) ((x>b)?wavebright:0) // Use qsuba for smooth pixel colouring and qsubd for non-smooth pixel colouring
#define qsuba(x, b) ((x>b)?x-b:0)
//constants
const int NUM_BALLS = 10; // Number of bouncing balls you want (recommend < 7, but 20 is fun in its own way)
//
//bouncer vars
float h0 = 1; // Starting height, in meters, of the ball (strip length)
float GRAVITY = -2.5; // Downward (negative) acceleration of gravity in m/s^2
float h[NUM_BALLS] ; // An array of heights
float vImpact0 = sqrt( -2 * GRAVITY * h0 ); // Impact velocity of the ball when it hits the ground if "dropped" from the top of the strip
float vImpact[NUM_BALLS] ; // As time goes on the impact velocity will change, so make an array to store those values
float tCycle[NUM_BALLS] ; // The time since the last time the ball struck the ground
int pos[NUM_BALLS] ; // The integer position of the dot on the strip (LED index)
long tLast[NUM_BALLS] ; // The clock time of the last ground strike
float COR[NUM_BALLS] ; // Coefficient of Restitution (bounce damping)
//generic variables
uint8_t max_bright = 128; // for patts that need to be dimmer
uint8_t CurSpeed = 4; // main speed parmeter
uint8_t CurBright = 4; //main brightness parameter
//ripple definitions and variables
#define maxsteps 18 // Case statement wouldn't allow a variable.
uint8_t colour; // Ripple colour is randomized.
int center = 0; // Center of the current ripple.
int step = -1; // -1 is the initializing step.
uint8_t myfade = 128; // Starting brightness.
uint8_t bgcol = 8; // Background colour rotates.
int thisdelay = 50; // Standard delay value.
//move defs
uint8_t wavebright = 128; //You can change the brightness of the waves/bars rolling across the screen. Best to make them not as bright as the sparkles.
uint8_t thishue = 0; //starting hue value for the this wave.
uint8_t thathue = 140; //starting hue value for the that wave.
uint8_t thisrot = 0; //how quickly the hue rotates for this wave.
uint8_t thatrot = 0; //how quickly the hue rotates for that wave.
uint8_t allsat = 255; //saturated with colour.
uint8_t allfreq = 32; //frequency/width of bars.
uint8_t thisphase = 0; //this phase change
uint8_t thatphase = 0; //thatchange
uint8_t thiscutoff = 192; //cutoff value to display this wave. Lower value = longer wave.
uint8_t thatcutoff = 192; //cutoff value to display that wave.
uint8_t twinkrun = 0; //Enable/disable twinkles.
uint8_t thisdir = 0; //direction
int8_t thisspeed = 4; //this speed.
int8_t thatspeed = -4; //that speed.
typedef struct { //structure for the twinkles
int twinkled; // Supports a long strand of LED's.
int twinkbright; // Defined as 'int', so that we can trigger change on a negative brightness value.
} twinks;
#define numtwinks 12 // You can change the number of twinkles.
twinks mytwinks[numtwinks];
int tSpeed = 0;
DMX_Slave dmx_slave ( DMX_SLAVE_CHANNELS );
//
CRGB leds[NUM_LEDS];
CRGBPalette16 currentPalette;
CRGBPalette16 currentGradient;
CRGBPalette16 gPal;
TBlendType currentBlending;
extern CRGBPalette16 myRedWhiteBluePalette;
extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM;
const TProgmemRGBPalette16 BLACKOUT_p PROGMEM =
{0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,
0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000,0x000000};
const TProgmemRGBPalette16 SALS_p PROGMEM =
{0xFF6600,0xFF0000,0xFF6600,0x993300,0xFF6600,0x880000,0xFF6600,0x880000,
0xFF6600,0xFF0000,0xFF6600,0x993300,0xFF6600,0x880000,0xFF6600,0x880000};
const TProgmemPalette16 RWB_p PROGMEM =
{CRGB::Red,CRGB::Gray,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};
void setup()
{
delay(1000);
dmx_slave.enable ();
dmx_slave.setStartAddress (130);//289 = one stripe - right side - 305 = two stripe - left side
FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
FastLED.setBrightness( BRIGHTNESS );
currentPalette = CloudColors_p;
currentBlending = LINEARBLEND;
//bouncer
for (int i = 0 ; i < NUM_BALLS ; i++) { // Initialize variables
tLast[i] = millis();
h[i] = h0;
pos[i] = 0; // Balls start on the ground
vImpact[i] = vImpact0; // And "pop" up at vImpact0
tCycle[i] = 0;
COR[i] = 0.90 - float(i)/pow(NUM_BALLS,2);
}
}
//// the loop routine runs over and over again forever:
void loop()
{
if (map(dmx_slave.getChannelValue(1),0,255,0,19) != CurPatt){
//CurPatt = 10;
LoadPalette();
}
GetParams();
}
//
void LoadPalette()
{
CurPatt = map(dmx_slave.getChannelValue(1),0,255,0,19);
//CurPatt = 6;
currentBlending = LINEARBLEND;
if (CurPatt == 13)
{
random16_add_entropy(random());
Fire2012WithPalette();
currentBlending = LINEARBLEND;
FastLED.show();
FastLED.delay(20);
}
else if (CurPatt == 14)
{
bouncer();
}
else if (CurPatt == 15)
{
ripple();
show_at_max_brightness_for_power();
delay_at_max_brightness_for_power(thisdelay*1.5);
}
else if (CurPatt == 16)
{
ripple();
show_at_max_brightness_for_power();
delay_at_max_brightness_for_power(thisdelay*1.5);
}
else if (CurPatt == 17)
{
ChangeMe();
two_sin();
twinkleover();
show_at_max_brightness_for_power();
delay_at_max_brightness_for_power(thisdelay*1.5);
}
else if (CurPatt == 18)
{
pickGradient();
palettetest( leds, NUM_LEDS, currentGradient);
FastLED.show();
FastLED.delay(20);
}
else
{
RunManual();
static uint8_t startIndex = 0;
startIndex = startIndex + CurSpeed; /* motion speed */
FillLEDsFromPaletteColors( startIndex);
FastLED.show();
FastLED.delay(20);
}
}
//
void GetParams()
{
p1 = dmx_slave.getChannelValue(2);
p2 = dmx_slave.getChannelValue(3);
p3 = dmx_slave.getChannelValue(4);
p4 = dmx_slave.getChannelValue(5);
p5 = dmx_slave.getChannelValue(6);
p6 = dmx_slave.getChannelValue(7);
p7 = dmx_slave.getChannelValue(8);
p8 = dmx_slave.getChannelValue(9);
p9 = dmx_slave.getChannelValue(10);
p10 = dmx_slave.getChannelValue(11);
p11 = dmx_slave.getChannelValue(12);
p12 = dmx_slave.getChannelValue(13);
p13 = dmx_slave.getChannelValue(14);
p14 = dmx_slave.getChannelValue(15);
p15 = dmx_slave.getChannelValue(16);
CurPatt = map(p1,0,255,0,patterns);
CurBright = map(p14,0,255,1,255);
CurSpeed = map(p15,0,255,-16,16);
}
//
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void RunManual()
{
currentBlending = LINEARBLEND;
switch (CurPatt)
{
case 0:currentPalette = BLACKOUT_p;break;
case 1:currentPalette = RainbowColors_p;break;
case 2:currentPalette = RainbowStripeColors_p;currentBlending = NOBLEND;break;
case 3:currentPalette = PartyColors_p;break;
case 4:currentBlending = LINEARBLEND;currentPalette = OceanColors_p;break;
case 5:currentPalette = HeatColors_p;break;
case 6:currentPalette = ForestColors_p;break;
case 7:currentBlending = LINEARBLEND;currentPalette = CloudColors_p;break;
case 8:currentPalette = LavaColors_p;break;
case 9:currentPalette = RWB_p;currentBlending = NOBLEND;break;
case 10:SetupPurpleAndGreenPalette();break;
case 11:SetupBlackAndWhiteStripedPalette();break;
case 12:currentPalette = SALS_p;break;
}
}// end manual
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void ChangeMe() {
wavebright = map(p14,0,255,0,128);
allfreq = map(p1,0,255,1,128);
thisspeed = map(p2,0,255,-50,50);
thatspeed = map(p3,0,255,-50,50);
thisrot = map(p4,0,255,0,255);
thatrot = map(p5,0,255,0,255);
thishue = map(p6,0,255,0,255);
thathue = map(p7,0,255,0,255);
thiscutoff = map(p8,0,255,1,240);
thatcutoff = map(p9,0,255,1,240);
} // ChangeMe()
void SetupTotallyRandomPalette()
{
for( int i = 0; i < 16; i++)
{currentPalette[i] = CHSV( random8(), 255, random8());}
}
//**********************************************************************************************************************************
//**********************************************************************************************************************************
const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM =
{CRGB::Red,CRGB::Gray,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};
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void palettetest( CRGB* ledarray, uint16_t numleds, const CRGBPalette16& gCurrentPalette)
{
CurGrad = map(p1,0,255,0,15);
palIdx = map(p15,255,0,-16,16);//speed
bright = p14;
static uint8_t startindex = 0;
startindex = startindex - palIdx;
fill_palette( ledarray, numleds, startindex, (256 / NUM_LEDS) + 1, gCurrentPalette, bright, LINEARBLEND);
}
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void FillLEDsFromPaletteColors( uint8_t colorIndex)
{
uint8_t brightness = CurBright;//p15;
currentBlending = NOBLEND;
for( int i = 0; i < NUM_LEDS; i++) {
leds[i] = ColorFromPalette( currentPalette, colorIndex, brightness, currentBlending);
colorIndex += 3;
}
}
//**********************************************************************************************************************************
//**********************************************************************************************************************************
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 );
}
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void SetupBlackAndWhiteStripedPalette()
{fill_solid( currentPalette, 16, CRGB::Black);
currentPalette[0] = CRGB::White;currentPalette[4] = CRGB::White;
currentPalette[8] = CRGB::White;currentPalette[12] = CRGB::White;}
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void Fire2012WithPalette(){
speedFactor = map(p15,255,0,1,1.5);
COOLING = map(p1,0,255,20,200);
SPARKING = map(p2,0,255,50,300);
bottomColor = map(p3,0,255,125,245);
currentPalette = HeatColors_p;
// Array of temperature readings at each simulation cell
static byte heat[NUM_LEDS];
// Step 1. Cool down every cell a little
for( int i = 0; i < NUM_LEDS; i++) {
heat[i] = qsub8( heat[i], random8(0, ((COOLING * 10) / NUM_LEDS) + speedFactor));
}
// Step 2. Heat from each cell drifts 'up' and diffuses a little
for( int k= NUM_LEDS - 1; k >= 2; k--) {
heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2] ) / 3;
}
// Step 3. Randomly ignite new 'sparks' of heat near the bottom
if( random8() < SPARKING ) {
int y = random8(7);
heat[y] = qadd8( heat[y], random8(bottomColor,255) );
}
// Step 4. Map from heat cells to LED colors
for( int j = 0; j < NUM_LEDS; j++) {
// Scale the heat value from 0-255 down to 0-240
// for best results with color palettes.
byte colorindex = scale8( heat[j], 230);
leds[j] = ColorFromPalette( currentPalette, colorindex);
}
}
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void bouncer() {
// GRAVITY = map(p1,0,1023,-2.5,-3.5);
// h0 = 1;
for (int i = 0 ; i < NUM_BALLS ; i++) {
tCycle[i] = millis() - tLast[i] ; // Calculate the time since the last time the ball was on the ground
// A little kinematics equation calculates positon as a function of time, acceleration (gravity) and intial velocity
h[i] = 0.5 * GRAVITY * pow( tCycle[i]/1000 , 2.0 ) + vImpact[i] * tCycle[i]/1000;
if ( h[i] < 0 ) {
h[i] = 0; // If the ball crossed the threshold of the "ground," put it back on the ground
vImpact[i] = COR[i] * vImpact[i] ; // and recalculate its new upward velocity as it's old velocity * COR
tLast[i] = millis();
if ( vImpact[i] < 0.01 ) vImpact[i] = vImpact0; // If the ball is barely moving, "pop" it back up at vImpact0
}
pos[i] = round( h[i] * (NUM_LEDS - 1) / h0); // Map "h" to a "pos" integer index position on the LED strip
}
//Choose color of LEDs, then the "pos" LED on
for (int i = 0 ; i < NUM_BALLS ; i++) leds[pos[i]] = CHSV( uint8_t (i * 40) , 255, 255);
FastLED.show();
//Then off for the next loop around
for (int i = 0 ; i < NUM_BALLS ; i++) {
leds[pos[i]] = CRGB::Black;
}
}
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void ripple() {
thisdelay = map(p1,0,255,1,64);
colour = map(p2,0,255,0,256);
myfade = map(p3,0,255,16,256);
for (int i = 0; i < NUM_LEDS; i++) leds[i] = CHSV(bgcol++, 255, CurBright/2); // Rotate background colour.
switch (step) {
case -1: // Initialize ripple variables.
center = random(NUM_LEDS);
step = 0;
break;
case 0:
leds[center] = CHSV(colour, 255, 255); // Display the first pixel of the ripple.
step ++;
break;
case maxsteps: // At the end of the ripples.
step = -1;
break;
default:
// Middle of the ripples.
leds[wrap(center + step)] += CHSV(colour, 255, myfade/step*3); // Display the next pixels in the range for one side.
leds[wrap(center - step)] += CHSV(colour, 255, myfade/step*3); // Display the next pixels in the range for the other side.
step ++; // Next step.
break;
} // switch step
} // ripple()
//**********************************************************************************************************************************
//**********************************************************************************************************************************
int wrap(int step) {
if(step < 0) return NUM_LEDS + step;
if(step > NUM_LEDS - 1) return step - NUM_LEDS;
return step;
} // wrap()
void two_sin() { // This is the heart of this program. Sure is short.
if (thisdir == 0) {thisphase += thisspeed; thatphase += thatspeed;}
else {thisphase -= thisspeed; thatphase -= thatspeed;}
thishue = thishue + thisrot; // Hue rotation is fun for thiswave.
thathue = thathue + thatrot; // It's also fun for thatwave.
for (int k=0; k<NUM_LEDS-1; k++) {
int thisbright = qsubd(cubicwave8((k*allfreq)+thisphase), thiscutoff); // qsub sets a minimum value called thiscutoff. If < thiscutoff, then bright = 0. Otherwise, bright = 128 (as defined in qsub)..
int thatbright = qsubd(cubicwave8((k*allfreq)+128+thatphase), thatcutoff); // This wave is 180 degrees out of phase (with the value of 128).
leds[k] = CHSV(thishue, allsat, thisbright); // Assigning hues and brightness to the led array.
leds[k] += CHSV(thathue, allsat, thatbright);
}
} // two_sin()
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void twinkleover() {
tSpeed = map(p15,0,255,1,10);
for (int i = 0; i < numtwinks; i++) {
if (mytwinks[i].twinkbright <0) {
mytwinks[i].twinkled = random8(0, NUM_LEDS-1);
mytwinks[i].twinkbright = random8(230, 250);
}
leds[mytwinks[i].twinkled] = CHSV(80, 120, mytwinks[i].twinkbright); // Trying to make a soft white twinkle
mytwinks[i].twinkbright -= tSpeed;
}
} // twinkle()
//**********************************************************************************************************************************
//**********************************************************************************************************************************
// Gradient Color Palette definitions from
// http://soliton.vm.bytemark.co.uk/pub/cpt-city
DEFINE_GRADIENT_PALETTE( ib_jul01_gp ) {
0,194,1,1,94,1,29,18,132,57,131,28,255,113,1,1};
DEFINE_GRADIENT_PALETTE( es_rivendell_15_gp ) {
0,1,14,5,101,16,36,14,165,56,68,30,242,150,156,99,255,150,156,99};
DEFINE_GRADIENT_PALETTE( rgi_15_gp ) {
0,4,1,31,31,55,1,16,63,197,3,7,95,59,2,17,127,6,2,34,159,39,6,33,191,112,13,32,223,56,9,35,255,22,6,38};
DEFINE_GRADIENT_PALETTE( retro2_16_gp ) {
0,188,135,1,255,46,7,1};
DEFINE_GRADIENT_PALETTE( es_pinksplash_08_gp ) {
0,126,11,255,127,197,1,22,175,210,157,172,221,157,3,112,255,157,3,112};
DEFINE_GRADIENT_PALETTE( es_ocean_breeze_068_gp ) {
0,100,156,153,51,1,99,137,101,1,68,84,104,35,142,168,178,0,63,117,255,1,10,10};
DEFINE_GRADIENT_PALETTE( departure_gp ) {
0,8,3,0,42,23,7,0,63,75,38,6,84,169,99,38,106,213,169,119,116,255,255,255,138,135,255,138,148,22,255,24,170,0,255,0,191,0,136,0,212,0,55,0,255,0,55,0};
DEFINE_GRADIENT_PALETTE( es_landscape_64_gp ) {
0,0,0,0,37,2,25,1,76,15,115,5,127,79,213,1,128,126,211,47,130,188,209,247,153,144,182,205,204,59,117,250,255,1,37,192};
DEFINE_GRADIENT_PALETTE( rainbowsherbet_gp ) {
0,255,33,4,43,255,68,25,86,255,7,25,127,255,82,103,170,255,255,242,209,42,255,22,255,87,255,65};
DEFINE_GRADIENT_PALETTE( GMT_drywet_gp ) {
0,47,30,2,42,213,147,24,84,103,219,52,127,3,219,207,170,1,48,214,212,1,1,111,255,1,7,33};
DEFINE_GRADIENT_PALETTE( ib15_gp ) {
0,113,91,147,72,157,88,78,89,208,85,33,107,255,29,11,141,137,31,39,255,59,33,89};
DEFINE_GRADIENT_PALETTE( Fuschia_7_gp ) {
0,43,3,153,63,100,4,103,127,188,5,66,191,161,11,115,255,135,20,182};
DEFINE_GRADIENT_PALETTE( es_emerald_dragon_08_gp ) {
0,97,255,1,101,47,133,1,178,13,43,1,255,2,10,1};
DEFINE_GRADIENT_PALETTE( Colorfull_gp ) {
0,10,85,5,25,29,109,18,60,59,138,42,93,83,99,52,106,110,66,64,109,123,49,65,113,139,35,66,116,192,117,98,124,255,255,137,168,100,180,155,255,22,121,174};
DEFINE_GRADIENT_PALETTE( Sunset_Real_gp ) {
0,120,0,0,22,179,22,0,51,255,104,0,85,167,22,18,135,100,0,103,198,16,0,130,255,0,0,160};
DEFINE_GRADIENT_PALETTE( Spectral_10_gp ) {
0,73,1,8,25,73,1,8,25,159,11,13,51,159,
11,13,51,227,39,9,76,227,39,9,76,249,109,
22,102,249,109,22,102,252,191,55,127,252,
191,55,127,194,233,69,153,194,233,69,153,
90,186,84,178,90,186,84,178,23,139,85,204,
23,139,85,204,3,63,120,229,3,63,120,229,
19,19,82,255,19,19,82};
//**********************************************************************************************************************************
//**********************************************************************************************************************************
//**********************************************************************************************************************************
//**********************************************************************************************************************************
void pickGradient()
{
currentBlending = LINEARBLEND;
switch (CurGrad)
{
case 0:currentGradient = Sunset_Real_gp;break;
case 1:currentGradient = es_rivendell_15_gp;break;
case 2:currentGradient = rgi_15_gp;break;
case 3:currentGradient = es_pinksplash_08_gp;break;
case 4:currentGradient = departure_gp;break;
case 5:currentGradient = es_emerald_dragon_08_gp;break;
case 6:currentGradient = rainbowsherbet_gp;break;
case 7:currentGradient = GMT_drywet_gp;break;
case 8:currentGradient = Fuschia_7_gp;break;
case 9:currentGradient = es_ocean_breeze_068_gp;break;
case 10:currentGradient = retro2_16_gp;break;
case 11:currentGradient = ib_jul01_gp;break;
case 12:currentGradient = ib15_gp;break;
case 13:currentGradient = Colorfull_gp;break;
case 14:currentGradient = Spectral_10_gp;break;
case 15:currentGradient = es_landscape_64_gp;break;
}
}// end pickGradient
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment