I know, I know, but I can't seem to adapt any of the countless posts to do this.
I have a standard single color analog LED strip above my kitchen cabinets, PWM pin 44, and a second strip underneath, PWM 45.
All works. The reason is so the two can have separate brighness. I set this via HTTP calls from a VeraLite. All works.
The URL passes as blahblah/?U=XXX&O=YYY where U=the PWM to set on the under cabinets lights, and O=the PWM for the over cabinet strips. All works, up/down, either way. What happens is the URL is parsed and the U value goes into loop 1 and fades in or out, then loop 2 works on the O value. Obviously these occur separately.
What I want is the two fades to occur simultaneously. It's totally possible the values could independently be anywhere between 0 & 255 for either. One may go up while the other goes down or one might not change at all (sending the same val again is OK, too). They don't even need to fade at the same rate, just begin together. Everything I've tried looks like a test to see how many vars I can use to keep track of things - so hard to follow.
Thanks for reading.
Your goal is to adjust the output pwm value of each output by a small amount at regular intervals, until it reaches its target value.
The blink without delay example sketch shows you how to run code at regular intervals.
I suggest that for each output you should have a variable holding the current output value, and a variable holding the target output value. Since you want to have similar data for both outputs and will be doing the same thing with them, I suggest using arrays for these values.
At regular intervals you will do the following for each output:
compare the current value with the target value.
If the target value is different to the current value, adjust the current value towards the target value.
In the same sketch you will also be checking for commands to change the brightness. When you receive one, instead of altering the brightness directly you will simply alter the saved target value. The regular background processing will then slowly adjust the brightness towards the target value.
If you need any help other than the in post above you must post your code, BUT if that code contains a call to delay() then the only help to will be given is to point you at blink wiithout delay.
Mark
PeterH has the right idea. One nicety would be to have the two target values achieved at approximately the same time.
To do this, you could decide how long, in millis() you want the total dimming or brightening to take place, then divide that time (integer divide) by the number of steps it requires for each strip. You then set a duration variable for each strip, containing the length of time required for one change in value. Eqch time the duration for each strip is passed, you increment or decrement the value of the PWM, until it reaches the target value (or passes it),
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. 
Thanks, Sembazuru, I was able to see what you did there, shamelessly incorporate some of it into my code and make some progress. I need to work out the timing to my liking (read: my wife's liking), and it's pretty Frankencode-like ATM, but this helped a lot. My Saturday may be productive after all.