Yet another PWM multiple pins together, BUT...

I have a sketch where I use the BWoD methodology to fade a single LED. There is a lot of extra code, but it might help to see how I have all the different bits interleaved together. (I even have the fade on rate different than the fade off rate, look carefully to see how I did that.)

/*
 * Entry Sensor Light: with Sharp Distance Sensor and PWM Output
 * - Use a distance sensor (Sharp# GP2Y0A710K0F) to fade a PWM output up when
 *    the sensed distance changes. Then after a time out, fade the PWM output
 *    down at a (potentially) different rate.
 *
 * By Chris "Sembazuru" Elliott, SembazuruCDE (at) GMail.com
 * 2013/10/28
 *
 * To the extent possible under law, Chris "Sembazuru" Elliott has waived all
 *  copyright and related or neighboring rights to SharpDistanceSensorPWMout.ino
 *  See http://creativecommons.org/publicdomain/zero/1.0/ for details.
 *  (Attribution would be nice, but in no way compulsory.)
 *
 * Hardware configuration on Arduino UNO:
 * - Analog output of sensor to A0
 * - LED13 used to show trigger status
 * - PWM on pin 3 used for the fading output (pin 4 used as ground reference for
 *    testing with 0.1" 5V LED)
 * - A5 pin used to disable serial diagnostics (A4 pin used as ground reference
 *    for testing with 0.1" shorting pins)
 */

// See http://playground.arduino.cc/Main/runningMedian for details and downloads.
#include <RunningMedian.h>

// Pair of pins to enable serial diagnostics. Short these two pins (or just the diagnosticsPin to ground) to silence serial diagnostics.
const byte diagnosticsPin = A5;
const byte diagnosticsGnd = A4;
boolean startDiagnostics = true;

// Constants and variables for data collection.
const byte sensorPin = A0;
unsigned int currentADC;
float currentAverage;
const byte medianArraySize = 19; // number of data points to collect to calculate the average (class range is 5-19)
const byte medianAverageSize = 9; // number of data points to average in the middle of the sorted median array to get an average reading while ignoring outliers
const unsigned long readingInterval = 17; // Sensor readings take 16.5ms +/- 3.7ms.
unsigned long readingStart = millis();
RunningMedian myMedian = RunningMedian(medianArraySize);

// Constants and variables for triggering.
const unsigned int PAcnt = 5; // highest index number for the array holding previous values. Number of previous values will be this plus 2 because array indexes start counting at 0, not 1 and comparison is based on the overflow value.
float previousAverages[PAcnt + 1]; // holding array for previous values to be used as a small pipe
float lastSignificantAverage = 0; // will recieve the previousAverages pipe overflow and be used in the trigger comparison
const unsigned int triggerThreshold = 5; // +/- value for average deltas to indicate if a trigger should happen
const byte triggerLED = 13; // Indicator LED for trigger
const unsigned long triggerDelay = 10000; // How long the trigger will last (minimum) in milliseconds
unsigned long triggerStart; // To allow capturing the time at the start of a trigger
boolean triggered = false; // flag for trigger state

// Constants and variables for fading the output up and down.
boolean fading = false; // true == need to fade, false == done fading.
boolean fadeDir = false; // true == up, false == down.
const byte fadePin = 3; // PWM pin to fade up and down.
const byte fadeGnd = 4; // Adjacent ground provided for use of 0.1" spacing 5V LED.
const int fadeMin = 0;
const int fadeMax = 255;
const byte fadeStep = 4; // How much to change the fadeValue by each iteration.
const byte fadeUDRatio = 2; // Amount to divide the fadeStep by when fading down to change the fade up to fade down rate ratio.
const unsigned long fadeTime = 3000; // How many milliseconds (base) to take for the fade up.
const unsigned long fadeStepInterval = fadeTime / ((fadeMax - fadeMin) / fadeStep); // Calculate how long between fade steps. Do this calculation once here.
unsigned long fadeStepStart = millis();
int fadeValue = fadeMin;

void setup()
{
  Serial.begin(115200);
  while (!Serial); // Wait for serial port to connect. Needed for Leonardo only.
  delay(1000); // Simply to allow time for the ERW versions of the IDE time to automagically open the Serial Monitor. 1 second chosen arbitrarily.

  // Setup pins for enabling/disabling serial diagnostics.
  pinMode(diagnosticsGnd, OUTPUT);
  digitalWrite(diagnosticsGnd, LOW);
  pinMode(diagnosticsPin, INPUT_PULLUP);

  // Setup pins for using the onboard LED to indicate triggering.
  pinMode(triggerLED, OUTPUT);
  digitalWrite(triggerLED, LOW);

  // Setup pins for the fading (fading) PWM pin.
  pinMode(fadeGnd, OUTPUT);
  digitalWrite(fadeGnd, LOW);
  analogWrite(fadePin, fadeValue);

  // Initialize previousAvereages array here because it broke above.
  for (int i = 0; i < PAcnt; i++)
  {
    previousAverages[i] = 0; // Zero it out. Don't want undefined values mucking about.
  }
}

void loop()
{
  if ((millis() - readingStart) > readingInterval)
  {
    getData();
    triggerCheck();
    diagnostics(readingStart);
  }
  if (fading)
  {
    if ((millis() - fadeStepStart) > fadeStepInterval)
    {
      fadeValue = fadeOutput(fadeValue, fadeStep, fadeDir);
      diagnostics(fadeStepStart);
    }
  }
}

void getData()
{
  do
  {
    readingStart = millis();
    currentADC = analogRead(sensorPin);
    myMedian.add(currentADC);
    lastSignificantAverage = previousAverages[PAcnt]; // grab the last value as overflow
    for (int i = PAcnt; i > 0; i--)
    {
      previousAverages[i] = previousAverages[i - 1]; // shift all the values up one spot in the array
    }
    previousAverages[0] = currentAverage; // put the previous average in the beginning of the array
    currentAverage = myMedian.getAverage(medianAverageSize); // get the new average
  }
  while (myMedian.getCount() < myMedian.getSize()); // If the array isn't full loop back and take another reading.
}

void triggerCheck()
{
  // Trigger if the median drops by more than the threshold or raises by more than the threshold.
  if ((currentAverage < (lastSignificantAverage - triggerThreshold)) || (currentAverage > (lastSignificantAverage + triggerThreshold)))
  {
    triggerStart = millis();
    triggered = true;
    fading = true; // Start (or continue) fading...
    fadeDir = true; //  ...up
    digitalWrite(triggerLED, HIGH);
  }
  // If currently triggered, check to see if enough time has passed since the last trigger event to turn off the trigger.
  if (triggered)
  {
    if ((millis() - triggerStart) > triggerDelay)
    {
      triggered = false;
      fading =  true; // Start (or continue) fading...
      fadeDir = false; //  ...down
      digitalWrite(triggerLED, LOW);
    }
  }
}

int fadeOutput(int _value, int _step, boolean _dir)
{
  fadeStepStart = millis();
  if (!_dir) // if down...
  {
    _step = -_step / fadeUDRatio; // ...set step direction negative and scale the rate
  }
  _value += _step;
  if (_value < fadeMin)
  {
    _value = fadeMin;
    fading = false;
  }
  if (_value > fadeMax)
  {
    _value = fadeMax;
    fading = false;
  }
  analogWrite(fadePin, _value);
  return _value;
}

void diagnostics(unsigned long _time)
{
  char _comma[] = ",";

  if (!digitalRead(diagnosticsPin)) // Check the diagnostic pin, only send out diagnostics if the pin is high.
  {
    startDiagnostics = true; // re-enable the header line for output
    return;
  }

  if (startDiagnostics)
  {
    // Use the F() macro throughout to not waste valueable SRAM on diagnostic messages.
    startDiagnostics = false;
    Serial.print(F("\"Time\""));
    Serial.print(_comma);
    Serial.print(F("\"myMedian.getCount()\""));
    Serial.print(_comma);
    Serial.print(F("\"currentADC\""));
    Serial.print(_comma);
    Serial.print(F("\"currentAverage\""));
    Serial.print(_comma);
    Serial.print(F("\"lastSignificantAverage\""));
    Serial.print(_comma);
    Serial.print(F("\"deltaAverage\""));
    Serial.print(_comma);
    Serial.print(F("\"triggered\""));
    Serial.print(_comma);
    Serial.print(F("\"fading\""));
    Serial.print(_comma);
    Serial.print(F("\"fadeDir\""));
    Serial.print(_comma);
    Serial.print(F("\"fadeValue\""));
    Serial.println();
  }
  Serial.print(_time);
  Serial.print(_comma);
  Serial.print(myMedian.getCount());
  Serial.print(_comma);
  Serial.print(currentADC);
  Serial.print(_comma);
  Serial.print(currentAverage);
  Serial.print(_comma);
  Serial.print(lastSignificantAverage);
  Serial.print(_comma);
  Serial.print(currentAverage - lastSignificantAverage);
  Serial.print(_comma);
  Serial.print(triggered);
  Serial.print(_comma);
  Serial.print(fading);
  Serial.print(_comma);
  Serial.print(fadeDir);
  Serial.print(_comma);
  Serial.print(fadeValue);
  Serial.println();
}

Hope this helps more than confuses. :wink: