torstengeppert
Published © GPL3+

Uke on fire

Ukulele illuminated by LED elements w/ illumination depending on result of FFT analysis of ukulele's sound.

IntermediateFull instructions provided2,654
Uke on fire

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Capacitor 470 µF
Capacitor 470 µF
×1
Adafruit Microphone Breakout MAX4466
×1
Rotary Potentiometer, 10 kohm
Rotary Potentiometer, 10 kohm
×1
Resistor 330 ohm
Resistor 330 ohm
×3
Resistor 10k ohm
Resistor 10k ohm
×1
Tactile Switch, Top Actuated
Tactile Switch, Top Actuated
×1
WS2812B single LEDS
×30
LED Ring (generic, 16 LEDs)
×1
Ukulele DIY construction kit
×1

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Drill / Driver, Cordless
Drill / Driver, Cordless
Saw (tiny, generic)
Wood glue
Rubber, foam
Craft glue

Story

Read more

Schematics

Schematics for UkeOnFire

Code

Arduino code for "UkeOnFire" project

Arduino
Sketch used for illuminating a modified ukulele w/ different patterns based on FFT analysis of audio signal.
Here code for "smaller" ukulele w/ 20 + 10 LEDs. Can be easily adjusted to more LEDs, see corresponding sections in code.
/*  
 *   
 * Control of several LED strips mounted to a Ukulele (tuning: G - C - E - A)
 *   - NR1: D3: LED ring @ sound hole of Ukulele (16 LEDs)
 *   - NR2: D5: LED strip @ lower part of fretboad (Frets 6 to 10, lower left LED @ fret 10 (i.e., on A-string) = #1 for adressing. 4 "rows", 5 LEDs per row => 20 LEDs in total)
 *          - lowest row ("A-string") displays intensity of lowest ("bass") frequencies, ..., highest row ("G-string") displays intensity of highest frequencies
 *   - NR3: D6: LED strip @ upper part of fretboad (Frets 1 to 5, lower left LED @ fret 5 (i.e., on G-string) = #1 for adressing. 2 "rows", 5 LEDs per row => 10 LEDs in total)
 *          - lowest row ("E-string") displays intensity of lowest ("bass") frequencies, ..., highest row ("C-string") displays intensity of highest frequencies
 *   
 *   Inputs:
 *   - A0: Audio signal (recorded from a microphone pointing inside the Ukulele) (--> ADMUX = 0x40)
 *   - A1: Brightness of (all) LEDs (selectable via a potentiometer) (--> ADMUX = 0x41)
 *   - D2: Mode selection (determined by counting how often a switch button is pressed)
 *   
 *  Inputs/ideas and (modified) software parts w/ respect to FFT and LED control with Arduino based
 *   - on project by Antoine Rochebois. Thanks ! https://create.arduino.cc/projecthub/AntoineKia/interactive-led-table-for-50-650b83?ref=platform&ref_id=424_trending___&offset=84
 *   - LED fire effect inspired by various examples in the www, e.g., Mark Kriegsman's code under https://pastebin.com/xYEpxqgq
 * Written by T. Geppert, April/May 2020
 */

/*
 * Constants and alike
 */

// LED stuff =============================================================================
// LED Ring / Stripe ---------------------------------------------------------------------
#include <FastLED.h>
#define NUM_LEDS_NR1 16                       // Number of LEDs in ring (aka "NR1")
#define LED_DATA_PIN_NR1 3                    // Data Pin for control of in NR1
#define NUM_LEDS_NR2 20                       // Number of LEDs in strip 1 ("lower" LED strip="closer to body") (aka "NR2")
#define LED_DATA_PIN_NR2 5                    // Data Pin for control of LEDs in NR2
#define NUM_LEDS_NR3 10                       // Number of LEDs in strip 2 ("upper" LED strip="closer to headstock")(aka "NR3")
#define LED_DATA_PIN_NR3 6                    // Data Pin for control of LEDs in NR3

#define NUM_LED_BINS_NR1 1                     // number of LED stripes=LED bins in LED strip Nr1
#define NUM_LED_BINS_NR2 4                     // number of LED stripes=LED bins in LED strip Nr2
#define NUM_LED_BINS_NR3 2                     // number of LED stripes=LED bins in LED strip Nr3
#define NUM_LEDS_PER_LED_BIN_NR1 16            // number of LEDs per FFT bin ("height" of LED_bin)
#define NUM_LEDS_PER_LED_BIN_NR2 5             // number of LEDs per FFT bin ("height" of LED_bin)
#define NUM_LEDS_PER_LED_BIN_NR3 5             // number of LEDs per FFT bin ("height" of LED_bin)

CRGB leds_NR1[NUM_LEDS_NR1];                    // create array for LED ring (NR1)
CRGB leds_NR2[NUM_LEDS_NR2];                    // create array for strip NR2
CRGB leds_NR3[NUM_LEDS_NR3];                    // create array for strip NR3

#define LED_HUE_STD 13                      // STandarD HUE = "color" of LED; 0~red, 32~orange, 64~yellow
#define LED_SAT_STD 255                     // STandarD SATuration = saturation of color of LED; 0~neutral grey, 255~pure color
#define LED_VAL_STD 75 

#define LED_BRIGHT_PIN A1                   // controls brightness of LEDs;  in code below A0 is addressed via "ADMUX = 0x41" (since operating ADC in special mode)
int LED_HUE_NR1 = LED_HUE_STD;              // HUE used for strip Nr1
int LED_HUE_NR2 = LED_HUE_STD;              // HUE used for strip Nr2
int LED_HUE_NR3 = LED_HUE_STD;              // HUE used for strip Nr3
int LED_SAT = LED_SAT_STD;                  // SAT used for FastLED depending on input conditions
int LED_VAL = LED_VAL_STD;                  // VAL used for FastLED depending on input conditions

int switchOnDelay = 50;                     // time  (ms) between switchin on subsequent leds

// "LED fire" effect related
#define COOLING  100                         // How much does the air cool as it rises? Less cooling = taller flames.  More cooling = shorter flames. Default 55, suggested range 20-100
#define SPARKING 90                        // What chance (out of 255) is there that a new spark will be lit? Higher chance = more roaring fire.  Lower chance = more flickery fire. Default 120, suggested range 50-200.

// Audio stuff =============================================================================
#define AUDIO_IN_PIN A0                   // Audio signal input; in code below A0 is addressed via "ADMUX = 0x40" (since operating ADC in special mode)

// FFT stuff =============================================================================
#define LOG_OUT 0         //set output of FFT library to linear not logarithmical
#define LIN_OUT 1         //set output of FFT library to linear not logarithmical
#define FFT_N 64          //set to 64 point FFT => 32 frequency bins (assuming input frequencies from ~0...22 000 Hz => ~ 2 x 343 Hz/bin

#include <FFT.h>          //include the FFT library; doc found e.g., https://github.com/imagest108/arduino/blob/master/libraries/ArduinoFFT/FFT/fft_read_me.txt
#include <math.h>         //include library for mathematic functions

byte mapFFTBinToLEDBin_NR1[NUM_LED_BINS_NR1+1] = {4, 16};           // mapping of the FFT bins to number of LED stripes; 64 point FFT results in 64/4 bins. here: bundle bins  5-16 (lowest bins lead to flickering of lowest LEDs... so simply remove from being used as basis for evaluation. Reason NOT understood)
//byte mapFFTBinToLEDBin_NR2[NUM_LED_BINS_NR2+1] = {1, 3, 5, 7, 16};  // mapping of the FFT bins to number of LED stripes; 64 point FFT results in 64/4 bins. here: bundle bins  2-3, 4-5, 6-7, 8-16 (lowest bins lead to flickering of lowest LEDs... so simply remove from being used as basis for evaluation. Reason NOT understood)
byte mapFFTBinToLEDBin_NR2[NUM_LED_BINS_NR2+1] = {1, 4, 8, 12, 16};  // mapping of the FFT bins to number of LED stripes; 64 point FFT results in 64/4 bins. here: bundle bins  2-3, 4-5, 6-7, 8-16 (lowest bins lead to flickering of lowest LEDs... so simply remove from being used as basis for evaluation. Reason NOT understood)

byte mapFFTBinToLEDBin_NR3[NUM_LED_BINS_NR3+1] = {1, 3, 16};        // mapping of the FFT bins to number of LED stripes; 64 point FFT results in 64/4 bins. here: bundle bins  2-3, 4-16 (lowest bins lead to flickering of lowest LEDs... so simply remove from being used as basis for evaluation. Reason NOT understood)
byte currentBinHeight_NR1[NUM_LED_BINS_NR1] = {0};                  // heigth (=numbers of LEDs ON in LED bin in LED strip 1)
byte currentBinHeight_NR2[NUM_LED_BINS_NR2] = {0, 0, 0, 0};         // heigth (=numbers of LEDs ON in LED bin in LED strip 2)
byte currentBinHeight_NR3[NUM_LED_BINS_NR3] = {0, 0};               // heigth (=numbers of LEDs ON in LED bin in LED strip 3)
float faktoren_NR1[NUM_LED_BINS_NR1] = {1};                       //factors to increase the height of each column in strip NR1
float faktoren_NR2[NUM_LED_BINS_NR2] = {0.7, 0.9, 1.1, 1.3};            //factors to increase the height of each column in strip NR2
float faktoren_NR3[NUM_LED_BINS_NR3] = {0.8, 1.1};                    //factors to increase the height of each column in strip NR3

// Mode selection stuff =============================================================================
#define modeSelectorPin 2                                           // use digital pin 2 for mode selection
int modeSelectorPinSwitchState = LOW ;                                 // HIGH if external mode selector button is pressed
int operationMode = 0;                                              // operation mode (e.g., "0" = "all off", "1" = only NR2 controlled by FFT, ...)
int operationModeMax = 7;                                           // maximum number of operation modes (counting starts w/ "0")
int operationModeSelectionDelay = 250;                              // time between subsequent readings of external mode selection button

/*
 * Subroutines and alike ===================================================================================
 */
 
void blinkTest () {                            // Light up all LEDs in each ring/strip subsequently (kind of a function check...)
  //=============
  for (int i=0; i<NUM_LEDS_NR1; i++) {
    LED_HUE_NR1 = ( 2 +  10 * i) % 255;
    // Turn ON leds_NR1
    leds_NR1[i]=CHSV(LED_HUE_NR1, LED_SAT, LED_VAL);
    FastLED.show();
    delay(switchOnDelay);

    // Turn OFF leds_NR1
    leds_NR1[i]=CHSV(LED_HUE_NR1, LED_SAT, 0);
    FastLED.show();
    delay(switchOnDelay);
  }

  for (int i=0; i<NUM_LEDS_NR2; i++) {
    LED_HUE_NR2 = ( 2 +  10 * i) % 255;
    // Turn ON leds_NR2
    leds_NR2[i]=CHSV(LED_HUE_NR2, LED_SAT, LED_VAL);
    FastLED.show();
    delay(switchOnDelay);

    // Turn OFF leds_NR1
    leds_NR2[i]=CHSV(LED_HUE_NR2, LED_SAT, 0);
    FastLED.show();
    delay(switchOnDelay);
  }

  for (int i=0; i<NUM_LEDS_NR3; i++) {
    LED_HUE_NR3 = ( 2 +  10 * i) % 255;
    // Turn ON leds_NR3
    leds_NR3[i]=CHSV(LED_HUE_NR3, LED_SAT, LED_VAL);
    FastLED.show();
    delay(switchOnDelay);

    // Turn OFF leds_NR3
    leds_NR3[i]=CHSV(LED_HUE_NR3, LED_SAT, 0);
    FastLED.show();
    delay(switchOnDelay);
  }
}

void allOFF () {                               // actively turn off all LEDs (set their color to black)
  for ( int a = 0; a < NUM_LEDS_NR1; a++) {
    leds_NR1[a] = CRGB::Black;
  }
  for ( int a = 0; a < NUM_LEDS_NR2; a++) {
    leds_NR2[a] = CRGB::Black;
  }
  for ( int a = 0; a < NUM_LEDS_NR3; a++) {
    leds_NR3[a] = CRGB::Black;
  }
  FastLED.show();
}

void setLEDStrip_NR1 (byte LED_column, byte LED_columnHeight, byte brightness) {
  byte h = map(LED_columnHeight, 0, 255, 0, NUM_LEDS_PER_LED_BIN_NR1);
  h = (unsigned char)(h * faktoren_NR1[LED_column]);
  if (h < currentBinHeight_NR1[LED_column]) {
    currentBinHeight_NR1[LED_column]--;
  }
  else if (h > currentBinHeight_NR1[LED_column]) {
    currentBinHeight_NR1[LED_column] = h;
  }
  if (LED_columnHeight > 150) {
    LED_HUE_NR1 += 20;                                            //CHANGE THIS VALUE IF YOU WANT THE DIFFERENCE BETWEEN THE COLORS TO BE BIGGER
    if (LED_HUE_NR1 > 250) LED_HUE_NR1 = 0;
  }

  for (byte y = 0; y < NUM_LEDS_PER_LED_BIN_NR1; y++) {            //set colors of pixels according to column and hue
    if (currentBinHeight_NR1[LED_column] > y) {
      if (LED_column % 2 == 0) {
        leds_NR1[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR1)] = CHSV((LED_HUE_NR1 * NUM_LEDS_PER_LED_BIN_NR1) + (LED_column * NUM_LEDS_PER_LED_BIN_NR1), 255, brightness);
      } else {
        leds_NR1[NUM_LEDS_PER_LED_BIN_NR1 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR1)] = CHSV((LED_HUE_NR1 * NUM_LEDS_PER_LED_BIN_NR1) + (LED_column * NUM_LEDS_PER_LED_BIN_NR1), 255, brightness);
      }
    } else {
      if (LED_column % 2 == 0) {
        leds_NR1[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR1)] = CRGB::Black;
      } else {
        leds_NR1[NUM_LEDS_PER_LED_BIN_NR1 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR1)] = CRGB::Black;
      }
    }
  }
}

void setLEDStrip_NR2 (byte LED_column, byte LED_columnHeight, byte brightness) {
  byte h = map(LED_columnHeight, 0, 255, 0, NUM_LEDS_PER_LED_BIN_NR2);
  h = (unsigned char)(h * faktoren_NR2[LED_column]);
  if (h < currentBinHeight_NR2[LED_column]) {
    currentBinHeight_NR2[LED_column]--;
  }
  else if (h > currentBinHeight_NR2[LED_column]) {
    currentBinHeight_NR2[LED_column] = h;
  }
  if (LED_columnHeight > 150) {
    LED_HUE_NR2 += 20;                                            //CHANGE THIS VALUE IF YOU WANT THE DIFFERENCE BETWEEN THE COLORS TO BE BIGGER
    if (LED_HUE_NR2 > 250) LED_HUE_NR2 = 0;
  }

  for (byte y = 0; y < NUM_LEDS_PER_LED_BIN_NR2; y++) {            //set colors of pixels according to column and hue
    if (currentBinHeight_NR2[LED_column] > y) {
      if (LED_column % 2 == 0) {
        leds_NR2[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR2)] = CHSV((LED_HUE_NR2 * NUM_LEDS_PER_LED_BIN_NR2) + (LED_column * NUM_LEDS_PER_LED_BIN_NR2), 255, brightness);
      } else {
        leds_NR2[NUM_LEDS_PER_LED_BIN_NR2 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR2)] = CHSV((LED_HUE_NR2 * NUM_LEDS_PER_LED_BIN_NR2) + (LED_column * NUM_LEDS_PER_LED_BIN_NR2), 255, brightness);
      }
    } else {
      if (LED_column % 2 == 0) {
        leds_NR2[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR2)] = CRGB::Black;
      } else {
        leds_NR2[NUM_LEDS_PER_LED_BIN_NR2 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR2)] = CRGB::Black;
      }
    }
  }
}

void setLEDStrip_NR3 (byte LED_column, byte LED_columnHeight, byte brightness) {
  byte h = map(LED_columnHeight, 0, 255, 0, NUM_LEDS_PER_LED_BIN_NR3);
  h = (unsigned char)(h * faktoren_NR3[LED_column]);
  if (h < currentBinHeight_NR3[LED_column]) {
    currentBinHeight_NR3[LED_column]--;
  }
  else if (h > currentBinHeight_NR3[LED_column]) {
    currentBinHeight_NR3[LED_column] = h;
  }
  if (LED_columnHeight > 150) {
    LED_HUE_NR3 += 20;                                            //CHANGE THIS VALUE IF YOU WANT THE DIFFERENCE BETWEEN THE COLORS TO BE BIGGER
    if (LED_HUE_NR3 > 250) LED_HUE_NR3 = 0;
  }

  for (byte y = 0; y < NUM_LEDS_PER_LED_BIN_NR3; y++) {            //set colors of pixels according to column and hue
    if (currentBinHeight_NR3[LED_column] > y) {
      if (LED_column % 2 == 0) {
        leds_NR3[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR3)] = CHSV((LED_HUE_NR3 * NUM_LEDS_PER_LED_BIN_NR3) + (LED_column * NUM_LEDS_PER_LED_BIN_NR3), 255, brightness);
      } else {
        leds_NR3[NUM_LEDS_PER_LED_BIN_NR3 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR3)] = CHSV((LED_HUE_NR3 * NUM_LEDS_PER_LED_BIN_NR3) + (LED_column * NUM_LEDS_PER_LED_BIN_NR3), 255, brightness);
      }
    } else {
      if (LED_column % 2 == 0) {
        leds_NR3[y + (LED_column * NUM_LEDS_PER_LED_BIN_NR3)] = CRGB::Black;
      } else {
        leds_NR3[NUM_LEDS_PER_LED_BIN_NR3 - y + (LED_column * NUM_LEDS_PER_LED_BIN_NR3)] = CRGB::Black;
      }
    }
  }
}

void NR1_CtrlViaFFT() {
  // LED object NR1 (ring) controlled by FFT result
  // for each of the LED stripes=LED bins, check highest intensity w/i the respective bundle of FFT bins. Based on that result set numbers of LEDs w/i the LED bin to be lit
    for (byte i =0; i < NUM_LED_BINS_NR1; i++) {
    byte maxIntensity = 0;
    for (byte x = mapFFTBinToLEDBin_NR1[i]; x < mapFFTBinToLEDBin_NR1[i+1]; x++) {
      if ( (unsigned char) fft_lin_out[x] > maxIntensity ) {
        maxIntensity = (unsigned char) fft_lin_out[x];
      }
    }
    // set height for each LED strip based on determined maximum intensity w/i each bundle
    setLEDStrip_NR1 (i, maxIntensity, LED_VAL);
  }
}

void NR2_CtrlViaFFT() {
  // LED object NR2 controlled by FFT result  
  // for each of the LED stripes=LED bins, check highest intensity w/i the respective bundle of FFT bins. Based on that result set numbers of LEDs w/i the LED bin to be lit
  for (byte i =0; i < NUM_LED_BINS_NR2; i++) {
    byte maxIntensity = 0;
    for (byte x = mapFFTBinToLEDBin_NR2[i]; x < mapFFTBinToLEDBin_NR2[i+1]; x++) {
      if ( (unsigned char) fft_lin_out[x] > maxIntensity ) {
        maxIntensity = (unsigned char) fft_lin_out[x];
      }
    }
    // set height for each LED strip based on determined maximum intensity w/i each bundle
    setLEDStrip_NR2 (i, maxIntensity, LED_VAL);
  }
}

void NR3_CtrlViaFFT() {
  // LED object NR2 controlled by FFT result  
  // for each of the LED stripes=LED bins, check highest intensity w/i the respective bundle of FFT bins. Based on that result set numbers of LEDs w/i the LED bin to be lit
  for (byte i =0; i < NUM_LED_BINS_NR3; i++) {
    byte maxIntensity = 0;
    for (byte x = mapFFTBinToLEDBin_NR3[i]; x < mapFFTBinToLEDBin_NR3[i+1]; x++) {
      if ( (unsigned char) fft_lin_out[x] > maxIntensity ) {
        maxIntensity = (unsigned char) fft_lin_out[x];
      }
    }
    // set height for each LED strip based on determined maximum intensity w/i each bundle
    setLEDStrip_NR3 (i, maxIntensity, LED_VAL);
  }   
}

void NR2_LEDFire() {

  static byte heat_NR2 [NUM_LEDS_NR2];        // array for heat values
  
  // Step 1: Cool down all cells by a somewhat random amount, except the sparking ones (each LED in each LED Bin; no differentiation between different LED bins)
  for (int a = 0; a < NUM_LED_BINS_NR2; a++) {                  // each LED Bin
    for ( int b = 0; b < NUM_LEDS_PER_LED_BIN_NR2; b++) {       // each LED
      heat_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b] = qsub8 ( heat_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b], random8 (0, (COOLING / NUM_LEDS_PER_LED_BIN_NR2 * 80 ))); // "*4" seems to be a reasonable factor. Play around to get the cooling rate you need to get a nice effect
    }
  }

  // Step 2: spread the heat (each LED in each LED Bin); consider only 1 neighbouring LED due to very low number of LEDs (<<10) in each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR2; a++) {                  // each LED Bin
    for ( int b = NUM_LEDS_PER_LED_BIN_NR2-1; b > 0; b--) {    // each LED
      if (a %2 ==0) {
        heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + b ] = ( heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + b ] + heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + b - 1 ] ) / 1.75;
      }
      else {
        heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * (a+1) - b - 1 ] = ( heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * (a+1) - b - 1 ] + heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * (a+1) - b ] ) / 1.75;    
      }
    }
  }
  
  // Step 3: Randomly ignite new flame sources of heat at the very bottom of each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR2; a++) {                  // each LED Bin
    if( random8() < SPARKING ) {           
      if( a %2 == 0) {                                          // for even LED bins
        heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + 0 ] = qadd8( heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + 0 ], random8(50, 75)) ;      //  Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
      } else {                                                  // for odd LED bins
        heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + NUM_LEDS_PER_LED_BIN_NR2 - 1 ] = qadd8( heat_NR2[ NUM_LEDS_PER_LED_BIN_NR2 * a + NUM_LEDS_PER_LED_BIN_NR2 - 1 ], random8(50, 75)) ;      //  Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
      } 
    }
  }    

  // Step 4: Map heat array to LED array. Decrease brightness for higher LEDs in each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR2; a++) {                  // each LED Bin
    for( int b = 0; b < NUM_LEDS_PER_LED_BIN_NR2; b++) {
        leds_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b] = HeatColor( heat_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b]);
        leds_NR2[NUM_LEDS_PER_LED_BIN_NR2 * a + b].nscale8_video(LED_VAL);
    }
  }
}

void NR3_LEDFire() {

  static byte heat_NR3 [NUM_LEDS_NR3];        // array for heat values
  
  // Step 1: Cool down all cells by a somewhat random amount, except the sparking ones (each LED in each LED Bin; no differentiation between different LED bins)
  for (int a = 0; a < NUM_LED_BINS_NR3; a++) {                  // each LED Bin
    for ( int b = 0; b < NUM_LEDS_PER_LED_BIN_NR3; b++) {       // each LED
      heat_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b] = qsub8 ( heat_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b], random8 (0, (COOLING / NUM_LEDS_PER_LED_BIN_NR3 * 80 ))); // "*4" seems to be a reasonable factor. Play around to get the cooling rate you need to get a nice effect
    }
  }

  // Step 2: spread the heat (each LED in each LED Bin); consider only 1 neighbouring LED due to very low number of LEDs (<<10) in each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR3; a++) {                  // each LED Bin
    for ( int b = NUM_LEDS_PER_LED_BIN_NR3-1; b > 0; b--) {    // each LED
      if (a %2 ==0) {
        heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + b ] = ( heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + b ] + heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + b - 1 ] ) / 1.75;
      }
      else {
        heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * (a+1) - b - 1 ] = ( heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * (a+1) - b - 1 ] + heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * (a+1) - b ] ) / 1.75;    
      }
    }
  }
  
  // Step 3: Randomly ignite new flame sources of heat at the very bottom of each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR3; a++) {                  // each LED Bin
    if( random8() < SPARKING ) {           
      if( a %2 == 0) {                                          // for even LED bins
        heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + 0 ] = qadd8( heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + 0 ], random8(50, 75)) ;      //  Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
      } else {                                                  // for odd LED bins
        heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + NUM_LEDS_PER_LED_BIN_NR3 - 1 ] = qadd8( heat_NR3[ NUM_LEDS_PER_LED_BIN_NR3 * a + NUM_LEDS_PER_LED_BIN_NR3 - 1 ], random8(50, 75)) ;      //  Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
      } 
    }
  }    

  // Step 4: Map heat array to LED array. Decrease brightness for higher LEDs in each LED bin
  for (int a = 0; a < NUM_LED_BINS_NR3; a++) {                  // each LED Bin
    for( int b = 0; b < NUM_LEDS_PER_LED_BIN_NR3; b++) {
        leds_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b] = HeatColor( heat_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b]);
        leds_NR3[NUM_LEDS_PER_LED_BIN_NR3 * a + b].nscale8_video(LED_VAL);
    }
  }
}

/*
 * End of Subroutines and alike ===================================================================================
 */

/*
 * Setup ===================================================================================
 */
void setup() {
  FastLED.addLeds<NEOPIXEL, LED_DATA_PIN_NR1>(leds_NR1, NUM_LEDS_NR1);    // setup LED strip NR1
  FastLED.addLeds<NEOPIXEL, LED_DATA_PIN_NR2>(leds_NR2, NUM_LEDS_NR2);    // setup LED strip NR2
  FastLED.addLeds<NEOPIXEL, LED_DATA_PIN_NR3>(leds_NR3, NUM_LEDS_NR3);    // setup LED strip NR3

  pinMode(LED_DATA_PIN_NR1, OUTPUT);
  pinMode(LED_DATA_PIN_NR2, OUTPUT);
  pinMode(LED_DATA_PIN_NR3, OUTPUT);

  pinMode(LED_BRIGHT_PIN, INPUT);
  pinMode(AUDIO_IN_PIN, INPUT);

  pinMode(modeSelectorPin, INPUT);

  // setup Arduino Nano registers
  ADCSRA = 0xe5;                                                    //set the ADC to free running mode
  ADMUX = 0x40;                                                     //use pin A0
  DIDR0 = 0x03;                                                     //turn off the digital input for Pins A0 (audio signal input) and A1 (LED brightness)
  analogReference(DEFAULT);                                         //set aref to DEFAULT (was EXTERNAL in example, however, due to lack of schematics, I guess analogReference was connected to +5V, however not sure. In order to avoid potential damage to the ATmega - since I am not quite sure whats's going on inside and since I'm a novice to this Arduino topics, I chose to go for DEFAULT - although wasting some bits

  //check if all LEDs are controllable (only in case switch button is pressed during startup)
  modeSelectorPinSwitchState = digitalRead(modeSelectorPin);
  if ( modeSelectorPinSwitchState == HIGH) {
    blinkTest();
  }
  
  LED_HUE_NR1 = 0;                    // reset LED HUE value
  LED_HUE_NR2 = 0;                    // reset LED HUE value  
  LED_HUE_NR3 = 0;                    // reset LED HUE value
  
  allOFF();                           // actively turn off all LEDs (set their color to black)

  delay(500);  

}

/*
 * End of Setup ===================================================================================
 */



/*
 * Main Loop ===================================================================================
 */
 
void loop() {
  // Data acquisition and FFT ----------------------------------------------------------------------------------------------
  // -----------------------------------------------------------------------------------------------------------------------
  // Collect FFT data using input signal @ Pin A0
  for (int i = 0 ; i < 2 * FFT_N ; i += 2) {                      //save FFT_N samples
    while (!(ADCSRA & 0x10));                                     //wait for adc to be ready (i.e., current sampling finished)
    ADMUX = 0x40;                                                 //use pin A0
    ADCSRA = 0xf5;                                                //restart adc
    byte m = ADCL;                                                //fetch adc data
    byte j = ADCH;
   int k = (j << 8) | m;                                         //form into an int
   k -= 0x0200;                                                  //form into a signed int
   k <<= 6;                                                      //form into a 16b signed int
   fft_input[i] = k;                                             //put real data into even bins
   fft_input[i+1] = 0;                                           //set odd bins to 0   
  }  
  
  // Do FFT processing
  fft_window();                                                   // window the data for better frequency response
  fft_reorder();                                                  // reorder the data before doing the fft
  fft_run();                                                      // process the data in the fft
  fft_mag_lin();                                                  // take the output of the fft
  
  fft_lin_out[0] = 0;
  fft_lin_out[1] = 0;

  // set LED brightness ----------------------------------------------------------------------------------------------
  // -----------------------------------------------------------------------------------------------------------------
  // since ADC is in free running mode, switch input pin via ADMUX. Also add slight delay (otherwise - for whatever reason - data will not be passed to variable)
  while (!(ADCSRA & 0x10));                                     //wait for adc to be ready (i.e., current sampling finished)  
  ADMUX = 0x41;                                                 //use pin A1
  delay(1);
  byte m = ADCL;                                                //fetch adc data
  byte j = ADCH;
  int k = (j << 8) | m;                                         //form into an int
  LED_VAL = map (k, 0, 1023, 255, 0);                           // map potentiometer to allowed brightness for FastLED
  sei();                                                        // turn on interrupts

  // select behaviour based on operating mode ------------------------------------------------------------------------
  // -----------------------------------------------------------------------------------------------------------------
  modeSelectorPinSwitchState = digitalRead(modeSelectorPin);    // HIGH = button for selection of operating mode has been pressed
  
  if ( modeSelectorPinSwitchState == HIGH ) {
    operationMode = operationMode+1;                            // switch to next operation mode
    if (operationMode > operationModeMax) {
      operationMode = 0;
    }       
    delay (operationModeSelectionDelay);                        // prevent increasing operationMode in case of external mode selection button is pressed too long

    // Turn OFF all LEDs (to ensure only LEDs according to selected operation mode are on
    // actively turn off all LEDs (set their color to black)
    allOFF();
  }
  
  switch (operationMode) {
    /*
     * # button pressed |         |         |         |          |          |          |
     * (= case below)   | FFT NR1 | FFT NR2 | FFT NR3 | FIRE NR1 | FIRE NR2 | FIRE NR3 |
     * --------------------------------------------------------------------------------
     *        0         |    -    |    -    |    -    |    -     |    -     |    -
     *        1         |    -    |    X    |    -    |    -     |    -     |    -
     *        2         |    -    |    X    |    X    |    -     |    -     |    -
     *        3         |    X    |    X    |    X    |    -     |    -     |    -
     *        4         |    X    |    -    |    -    |    -     |    -     |    -
     *        5         |    -    |    -    |    -    |    -     |    X     |    -     
     *        6         |    -    |    -    |    -    |    -     |    -     |    X     
     *        7         |    -    |    -    |    -    |    -     |    X     |    X     
     */
    case 0:
      break;
    case 1:
      NR2_CtrlViaFFT();
      break;
    case 2:
      NR2_CtrlViaFFT();
      NR3_CtrlViaFFT();
      break;
    case 3:
      NR1_CtrlViaFFT();
      NR2_CtrlViaFFT();
      NR3_CtrlViaFFT();
      break;  
    case 4:
      NR1_CtrlViaFFT();
      break;
    case 5:
      NR2_LEDFire();
      delay(50);
      break;
    case 6:
      NR3_LEDFire();
      delay(50);
      break;
    case 7:
      NR2_LEDFire();
      delay(20);
      NR3_LEDFire();
      delay(30);
      break;      
  }
   
  FastLED.show();

delay(10); 

}
/*
 * End of Main Loop  ===================================================================================
 */

Credits

torstengeppert
0 projects • 4 followers

Comments