Arduino programs within programs

Associate
Joined
27 Nov 2003
Posts
2,486
Location
Loughborough
Hiya,

I'm working on a project I started for a local church to control WS2812B LED's using an arduino.

I've learnt enough to change colour using a 12 way rotary switch but I hope to change programs using the switch instead. I've figured out it has something to do with calling the separate .h files but I'm not a programmer... can anyone point me in the right direction? :-)

Here is the code I have for 7 colours but I want the remaining 5 to be something like a chase, sound to light or fire effect, I have all the programs saved but I've not mastered calling them if something could offer me some advice at all? (willing to make a beer money donation for advice!)

Code:
// Include the FastLED library
#include <FastLED.h>

// Define the number of LEDs and the data pin
#define NUM_LEDS 32
#define DATA_PIN 6

// Define the brightness level
#define BRIGHTNESS 32

// Define the LED array
CRGB leds[NUM_LEDS];

// Define the program selection pins
#define PROGRAM_1 40
#define PROGRAM_2 41
#define PROGRAM_3 42
#define PROGRAM_4 43
#define PROGRAM_5 44
#define PROGRAM_6 45
#define PROGRAM_7 46
#define PROGRAM_8 47
#define PROGRAM_9 48
#define PROGRAM_10 49
#define PROGRAM_11 50
#define PROGRAM_12 51

// Define the program variables
int program = 0;
int lastProgram = 0;

// Setup function
void setup() {
  // Set the LED data pin as output
  pinMode(DATA_PIN, OUTPUT);

  // Set the program selection pins as input with pullup
  pinMode(PROGRAM_1, INPUT_PULLUP);
  pinMode(PROGRAM_2, INPUT_PULLUP);
  pinMode(PROGRAM_3, INPUT_PULLUP);
  pinMode(PROGRAM_4, INPUT_PULLUP);
  pinMode(PROGRAM_5, INPUT_PULLUP);
  pinMode(PROGRAM_6, INPUT_PULLUP);
  pinMode(PROGRAM_7, INPUT_PULLUP);
  pinMode(PROGRAM_8, INPUT_PULLUP);
  pinMode(PROGRAM_9, INPUT_PULLUP);
  pinMode(PROGRAM_10, INPUT_PULLUP);
  pinMode(PROGRAM_11, INPUT_PULLUP);
  pinMode(PROGRAM_12, INPUT_PULLUP);

  // Initialize the LED array
  FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(BRIGHTNESS);

  // Set the initial program
  program = 1;
}

// Loop function
void loop() {
  // Check for program selection
  if (digitalRead(PROGRAM_1) == LOW) {
    program = 1;
  } else if (digitalRead(PROGRAM_2) == LOW) {
    program = 2;
  } else if (digitalRead(PROGRAM_3) == LOW) {
    program = 3;
  } else if (digitalRead(PROGRAM_4) == LOW) {
    program = 4;
  } else if (digitalRead(PROGRAM_5) == LOW) {
    program = 5;
  } else if (digitalRead(PROGRAM_6) == LOW) {
    program = 6;
  } else if (digitalRead(PROGRAM_7) == LOW) {
    program = 7;
  } else if (digitalRead(PROGRAM_8) == LOW) {
    program = 8;
  } else if (digitalRead(PROGRAM_9) == LOW) {
    program = 9;
  } else if (digitalRead(PROGRAM_10) == LOW) {
    program = 10;
  } else if (digitalRead(PROGRAM_11) == LOW) {
    program = 11;
  } else if (digitalRead(PROGRAM_12) == LOW) {
    program = 12;
  }

  // Check if the program has changed
  if (program != lastProgram) {
    // Clear the LED array
    FastLED.clear();

    // Set the new program
    switch (program) {
      case 1:
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::White;
  }
  FastLED.show();
        break;
      case 2:
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::Red;
  }
  FastLED.show();
        break;
      case 3:
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::Blue;
  }
  FastLED.show();
        break;
      case 4:
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::Green;
  }
  FastLED.show();
        break;
      case 5:
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::Yellow;
  }
  FastLED.show();
        break;
      case 6:
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::Cyan;
  }
  FastLED.show();
        break;
      case 7:
  for (int i = 0; i < NUM_LEDS; i++) {
    leds[i] = CRGB::Purple;
  }
  FastLED.show();
        break;
      case 8:
        // Program 8 code here
        break;
      case 9:
        // Program 9 code here
        break;
      case 10:
        // Program 10 code here
        break;
      case 11:
        // Program 11 code here
        break;
      case 12:
        // Program 12 code here
        break;
    }

    // Show the new LED array
    FastLED.show();

    // Update the last program variable
    lastProgram = program;
  }
}

Thanks for any help you can offer.
 
I'm.not gona lie, I know.zero.about what you asked, but instead of letting your thread go unanswered I thought this might help. You may not have thought to ask AI.. It said:

It sounds like you're on the right track with using a rotary switch to control the LEDs, and you're looking to expand your project to include additional effects like chase, sound-to-light, or fire.

To get this to work, you need to create separate effect functions (like chase(), fire(), etc.) in separate .h files and call these functions in your main code. Here's how you can organize it:


Step 1: Create separate effect files

For each effect (e.g., chase, fire), create a .h file that contains the function for that effect. For example:

chase.h:
Code:
#ifndef CHASE_H
#define CHASE_H

#include <FastLED.h>

extern CRGB leds[];

void chase() {

    static int position = 0;

    int tail = NUM_LEDS - 1;

    // Turn off all LEDs

    FastLED.clear();



    // Set the current "chase" LED to red

    leds[position] = CRGB::Red;


    // Move the position

    position = (position + 1) % NUM_LEDS;


    // Show the chase effect

    FastLED.show();


    // Add a delay for the chase effect

    delay(50);

}

#endif

fire.h:

Code:
#ifndef FIRE_H

#define FIRE_H

#include <FastLED.h>

extern CRGB leds[];

void fire() {

    // Simple fire effect simulation (can be made more advanced)

    for (int i = 0; i < NUM_LEDS; i++) {

        leds = CRGB::Orange;

    }

    FastLED.show();

    delay(100);
}

#endif

Step 2: Include the header files in your main code

In your main .ino file, include these effect files at the top:
Code:
#include "chase.h"

#include "fire.h"

Step 3: Update your switch statement

In the switch case where you set the LED programs, call the appropriate function based on the program number. For example:

Code:
void loop() {

    // Check for program selection (this part remains the same)

    // Check if the program has changed

    if (program != lastProgram) {

        // Clear the LED array

        FastLED.clear();

        // Set the new program

        switch (program) {

            case 1:

                // Static color White

                for (int i = 0; i < NUM_LEDS; i++) {

                    leds = CRGB::White;

                }

                FastLED.show();

                break;

            case 2:

                // Static color Red

                for (int i = 0; i < NUM_LEDS; i++) {

                    leds = CRGB::Red;

                }

                FastLED.show();

                break;

            // Other static colors here...

            case 8:

                chase(); // Call the chase effect

                break;

            case 9:

                fire(); // Call the fire effect

                break;

            case 10:

                // Program 10 code here

                break;

            case 11:

                // Program 11 code here

                break;

            case 12:

                // Program 12 code here

                break;

        }


        // Update the last program variable

        lastProgram = program;

    }

}

Step 4: Organize and structure the code

Make sure all your effects have a clear structure, like having each effect in its own .h file, and if necessary, create a .cpp file for each effect to keep your code organized.

For example, if you have more complex logic in your effect functions, you can use .cpp files to separate out the logic from the header files.

Additional Tips

Sound-to-light effect: For a sound-to-light effect, you'll need to use a microphone sensor (e.g., an analog microphone) to read the sound and then convert the input to something the LEDs can respond to (such as flashing the LEDs based on the sound level).

Chase effect: If you want a more advanced chase effect, you can change colors and speed based on a timer or change the direction of the chase.

Conclusion

This method will allow you to cleanly separate the LED effects and easily call them based on the program number selected from the rotary switch. Each effect is modular, and you can keep expanding by adding more effect functions in separate header files
 
Last edited:
I've figured out it has something to do with calling the separate .h files but I'm not a programmer... can anyone point me in the right direction?

You don't call .h files - h files are definitions included to give meaning to things called or variables referenced in the main code section(s).

I don't know much about FastLED but assuming you are just working from the examples then you'd need to copy the main function from the example into your code and then in the case statement for the relevant number call the function i.e. fire() or whatever, but you may also need some additional definitions and setup code from the example to make it work.

What you are asking is kind of complex because each effect program will have its own dependencies, setup and loop code which need integrating, some of which might be conflicting so need modifications of the code to resolve.

EDIT: As an aside, using multiple digital inputs for something like this while beginner friendly is a horrible way of doing it, a resistor ladder as input to a single pin and using the analogue input voltage level to distinguish which position is being selected is better, there may even be better approaches - I'm only passingly familiar with Arduino coding.
 
Last edited:
Thanks both for your answers, I'll need to read and digest it. :-)

I'd read using separate .h files was the only way to use different void loops which is why I went that route but I'll read in greater detail and take it from there with the results.

I'm also 3d printing the case and framework to hold it all so it is quite entertaining to do.

Edit - some of the programs use the Fastled library and some use the Neopixel library, hence the separation and need for individual void loops. The audio component is handled using Amplie-tie and is really quite a lovely way of doing it, code attached below if you want a try at it. I don't claim to have done much of the original coding at all as it is still work in progress. :-)

Code:
/*
LED VU meter for Arduino and Adafruit NeoPixel LEDs.
 
 Hardware requirements:
 - Most Arduino or Arduino-compatible boards (ATmega 328P or better).
 - Adafruit Electret Microphone Amplifier (ID: 1063)
 - Adafruit Flora RGB Smart Pixels (ID: 1260)
 OR
 - Adafruit NeoPixel Digital LED strip (ID: 1138)
 - Optional: battery for portable use (else power through USB or adapter)
 Software requirements:
 - Adafruit NeoPixel library
 
 Connections:
 - 3.3V to mic amp +
 - GND to mic amp -
 - Analog pin to microphone output (configurable below)
 - Digital pin to LED data input (configurable below)
 See notes in setup() regarding 5V vs. 3.3V boards - there may be an
 extra connection to make and one line of code to enable or disable.
 
 Written by Adafruit Industries.  Distributed under the BSD license.
 This paragraph must be included in any redistribution.
 
 fscale function:
 Floating Point Autoscale Function V0.1
 Written by Paul Badger 2007
 Modified from code by Greg Shakar
 
 */

#include <Adafruit_NeoPixel.h>
#include <math.h>

#define LED_PIN    6
#define NUM_LEDS  32
#define MIC_PIN   A0

#define SAMPLE_WINDOW   30  // Sample window for average level
#define PEAK_HANG 24 //Time of pause before peak dot falls
#define PEAK_FALL 4  //Rate of falling peak dot
#define INPUT_FLOOR 10 //Lower range of analogRead input
#define INPUT_CEILING 100 //Max range of analogRead input, the lower the value the more sensitive (1023 = max)



byte peak = 16;      // Peak level of column; used for falling dots
unsigned int sample;

byte dotCount = 0;  //Frame counter for peak dot
byte dotHangCount = 0; //Frame counter for holding peak dot

Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

void setup()
{
  // This is only needed on 5V Arduinos (Uno, Leonardo, etc.).
  // Connect 3.3V to mic AND TO AREF ON ARDUINO and enable this
  // line.  Audio samples are 'cleaner' at 3.3V.
  // COMMENT OUT THIS LINE FOR 3.3V ARDUINOS (FLORA, ETC.):
  //  analogReference(EXTERNAL);

  // Serial.begin(9600);
  strip.begin();
  strip.show(); // Initialize all pixels to 'off'

}

void loop()
{
  unsigned long startMillis= millis();  // Start of sample window
  float peakToPeak = 0;   // peak-to-peak level

  unsigned int signalMax = 0;
  unsigned int signalMin = 1023;
  unsigned int c, y;


  // collect data for length of sample window (in mS)
  while (millis() - startMillis < SAMPLE_WINDOW)
  {
    sample = analogRead(MIC_PIN);
    if (sample < 1024)  // toss out spurious readings
    {
      if (sample > signalMax)
      {
        signalMax = sample;  // save just the max levels
      }
      else if (sample < signalMin)
      {
        signalMin = sample;  // save just the min levels
      }
    }
  }
  peakToPeak = signalMax - signalMin;  // max - min = peak-peak amplitude
 
  // Serial.println(peakToPeak);


  //Fill the strip with rainbow gradient
  for (int i=0;i<=strip.numPixels()-1;i++){
    strip.setPixelColor(i,Wheel(map(i,0,strip.numPixels()-1,30,150)));
  }


  //Scale the input logarithmically instead of linearly
  c = fscale(INPUT_FLOOR, INPUT_CEILING, strip.numPixels(), 0, peakToPeak, 2);

 


  if(c < peak) {
    peak = c;        // Keep dot on top
    dotHangCount = 0;    // make the dot hang before falling
  }
  if (c <= strip.numPixels()) { // Fill partial column with off pixels
    drawLine(strip.numPixels(), strip.numPixels()-c, strip.Color(0, 0, 0));
  }

  // Set the peak dot to match the rainbow gradient
  y = strip.numPixels() - peak;
 
  strip.setPixelColor(y-1,Wheel(map(y,0,strip.numPixels()-1,30,150)));

  strip.show();

  // Frame based peak dot animation
  if(dotHangCount > PEAK_HANG) { //Peak pause length
    if(++dotCount >= PEAK_FALL) { //Fall rate
      peak++;
      dotCount = 0;
    }
  }
  else {
    dotHangCount++;
  }
}

//Used to draw a line between two points of a given color
void drawLine(uint8_t from, uint8_t to, uint32_t c) {
  uint8_t fromTemp;
  if (from > to) {
    fromTemp = from;
    from = to;
    to = fromTemp;
  }
  for(int i=from; i<=to; i++){
    strip.setPixelColor(i, c);
  }
}


float fscale( float originalMin, float originalMax, float newBegin, float
newEnd, float inputValue, float curve){

  float OriginalRange = 0;
  float NewRange = 0;
  float zeroRefCurVal = 0;
  float normalizedCurVal = 0;
  float rangedValue = 0;
  boolean invFlag = 0;


  // condition curve parameter
  // limit range

  if (curve > 10) curve = 10;
  if (curve < -10) curve = -10;

  curve = (curve * -.1) ; // - invert and scale - this seems more intuitive - postive numbers give more weight to high end on output
  curve = pow(10, curve); // convert linear scale into lograthimic exponent for other pow function

  /*
   Serial.println(curve * 100, DEC);   // multply by 100 to preserve resolution
   Serial.println();
   */

  // Check for out of range inputValues
  if (inputValue < originalMin) {
    inputValue = originalMin;
  }
  if (inputValue > originalMax) {
    inputValue = originalMax;
  }

  // Zero Refference the values
  OriginalRange = originalMax - originalMin;

  if (newEnd > newBegin){
    NewRange = newEnd - newBegin;
  }
  else
  {
    NewRange = newBegin - newEnd;
    invFlag = 1;
  }

  zeroRefCurVal = inputValue - originalMin;
  normalizedCurVal  =  zeroRefCurVal / OriginalRange;   // normalize to 0 - 1 float

  // Check for originalMin > originalMax  - the math for all other cases i.e. negative numbers seems to work out fine
  if (originalMin > originalMax ) {
    return 0;
  }

  if (invFlag == 0){
    rangedValue =  (pow(normalizedCurVal, curve) * NewRange) + newBegin;

  }
  else     // invert the ranges
  { 
    rangedValue =  newBegin - (pow(normalizedCurVal, curve) * NewRange);
  }

  return rangedValue;
}


// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  if(WheelPos < 85) {
    return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  }
  else if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  else {
    WheelPos -= 170;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

Also, one of the Fastled programs, this is why I couldnt try and do it all in one program (I don't think anyway!)

Code:
#include <FastLED.h>

#define LED_PIN     6
#define NUM_LEDS    32
#define BRIGHTNESS  32

#define COLOR_ORDER GRB
#define CHIPSET     WS2812B
#define FRAMES_PER_SECOND 60

bool gReverseDirection = false;

CRGB leds[NUM_LEDS];

void setup() {
  delay(3000); // sanity delay
  FastLED.addLeds<CHIPSET, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip );
  FastLED.setBrightness(BRIGHTNESS);
}

void loop()
{
  // Add entropy to random number generator; we use a lot of it.
  // random16_add_entropy( random());

  Fire2012(); // run simulation frame
 
  FastLED.show(); // display this frame
  FastLED.delay(1000 / FRAMES_PER_SECOND);
}


// Fire2012 by Mark Kriegsman, July 2012
// as part of "Five Elements" shown here: http://youtu.be/knWiGsmgycY
////
// This basic one-dimensional 'fire' simulation works roughly as follows:
// There's a underlying array of 'heat' cells, that model the temperature
// at each point along the line.  Every cycle through the simulation,
// four steps are performed:
//  1) All cells cool down a little bit, losing heat to the air
//  2) The heat from each cell drifts 'up' and diffuses a little
//  3) Sometimes randomly new 'sparks' of heat are added at the bottom
//  4) The heat from each cell is rendered as a color into the leds array
//     The heat-to-color mapping uses a black-body radiation approximation.
//
// Temperature is in arbitrary units from 0 (cold black) to 255 (white hot).
//
// This simulation scales it self a bit depending on NUM_LEDS; it should look
// "OK" on anywhere from 20 to 100 LEDs without too much tweaking.
//
// I recommend running this simulation at anywhere from 30-100 frames per second,
// meaning an interframe delay of about 10-35 milliseconds.
//
// Looks best on a high-density LED setup (60+ pixels/meter).
//
//
// There are two main parameters you can play with to control the look and
// feel of your fire: COOLING (used in step 1 above), and SPARKING (used
// in step 3 above).
//
// COOLING: How much does the air cool as it rises?
// Less cooling = taller flames.  More cooling = shorter flames.
// Default 50, suggested range 20-100
#define COOLING  55

// SPARKING: 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.
#define SPARKING 120


void Fire2012()
{
// Array of temperature readings at each simulation cell
  static uint8_t 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) + 2));
    }
 
    // 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(160,255) );
    }

    // Step 4.  Map from heat cells to LED colors
    for( int j = 0; j < NUM_LEDS; j++) {
      CRGB color = HeatColor( heat[j]);
      int pixelnumber;
      if( gReverseDirection ) {
        pixelnumber = (NUM_LEDS-1) - j;
      } else {
        pixelnumber = j;
      }
      leds[pixelnumber] = color;
    }
}
 
Last edited:
Back
Top Bottom