How to efficiently reuse a function for multiple pins?

In order to control mulitple pins, I created an array with 14 positions for all possible pins on my Uno. I want to make it a bit more memory efficient. The only way I know, is to make the array shorter and create another array with pins I actually want to control (they’re not 0, 1, 2, but 9, 10 and 11). That would require some extra setup effort, which isn’t so nice when I want to reuse the code in a library.

As far as I know, arrays can’t be defined with a non constant variable.

The code in question is this sketch with a pretty nice breathing and fading effect for LEDs. Would there be a better way to manage multiple instances of the setBreath() function?

#include <SoftwareSerial.h>
#include <math.h>

#define PIN_GREEN 9
#define PIN_YELLOW 10
#define PIN_RED 11

const int NumberOfFadingLEDs = 14; // I would like to make this more dynamic, as it takes 99 bytes of memory more than if I use 3 instead.

void setup()
{
  // Serial
  Serial.begin(9600); 

  // LED pins
  pinMode(PIN_GREEN, OUTPUT);       
  pinMode(PIN_YELLOW, OUTPUT);       
  pinMode(PIN_RED, OUTPUT);

}
// Calculate next brightness value during breath cycle:
float breath( float fadeSpeed = 1, long offset = 0, int sign = HIGH){
  // fadeSpeed: 1.0 is a good default, 0.75 a good sleep mode, 3 is pretty fast
  // offset: if lED has to fade in/out, we need to do a phase shift to allign the function with millis()
  // sign: HIGH = fade in, LOW = fade out.
  long offsetMillis = millis() - offset;
  float phaseShift = (-1+2*sign)*6;  // Aligned with fade in speed. For phase shift aligned with 0: (-1+2*sign)*3*PI/2
  return 108.0*(exp(sin( (fadeSpeed*offsetMillis/2000.0*PI) + phaseShift) - 0.36787944)); 
}
// Calculate next brightness value during fade:
float fade( float brightness, float fadeSpeed, int toggle){
  // toggle: HIGH = fade in, LOW = fade out.
  static long previousFadeStep;
  if( millis() > previousFadeStep + 25UL){
    previousFadeStep = millis(); 
    brightness = brightness + (-1+2*toggle)*1.5*fadeSpeed;
  }
  return brightness;
}
// Start or end the breathing cycle with a fade:
void setBreath( int pin, float fadeSpeed = 1.0, int toggle = HIGH){
  static bool init[NumberOfFadingLEDs];
  static long offset[NumberOfFadingLEDs];
  static float brightness[NumberOfFadingLEDs];

  if (toggle == HIGH){// fade in and start breath
    if (init[pin] == 0){
      if ( brightness[pin] < 56){
        brightness[pin] = fade(brightness[pin], fadeSpeed, toggle);
      }
      else{
        offset[pin] = millis() ;
        init[pin] = 1;
      }
    }
    else{
      brightness[pin] = breath( fadeSpeed, offset[pin] );
    }
  }
  else{ // fade out and stop breath 
    if (brightness[pin] > 5){
      brightness[pin] = fade(brightness[pin], fadeSpeed, toggle);
    }
    if ( brightness[pin] < 5 ){
      brightness[pin] = 0;
    }
  }
  analogWrite(pin, brightness[pin]);      
}

void loop(){
  long currentMillis = millis();

  if (currentMillis < 3000){
    setBreath(PIN_YELLOW, 4, HIGH);
  } 
  if (currentMillis > 8000)  {
    setBreath(PIN_YELLOW, 0.5, LOW);
  }
  if ((currentMillis > 1000) && ( currentMillis < 14000)){
    setBreath(PIN_RED, 1, HIGH);
  }
  if ((currentMillis > 16000) && ( currentMillis < 99000)){
    setBreath(PIN_RED, 1, LOW);
  }
  setBreath(PIN_GREEN, 1.2, HIGH);   
}

As far as I know, arrays can't be defined with a non constant variable.

A variable is by definition not a constant (hence the name).

What you may want to look at is using a structure to define the data that needs to be used to control a LED. A structure is like a database record - it groups related data of different types together and allows you to manipulate it as a logical unit. In you case this could be the pin number, true/false state, fading time, etc. You can then pass the data to the function with one parameter (or just pass a pointer to the structure). Search for references to this related to C and C++.

Structures are related to classes in C++ - basically a class is a structure with related code. If you want to take it one step further you could define a class for the LED you want to animate and have both the data and a local instance of the code (ie, an object) defined together.

Structures can be initialised using static data and class instances are initialise through their constructor parameters.

Alright I just got to learn more C++. Thanks for the pointers!