Help with per LED brightness as seen on graph

So I hooked up 5 LED diodes to digital outputs as seen in code with current limiting resistors, everything works in that regard. I'm trying to gradually fade up and down the brightness to represent the graph in the picture. It should simulate Christmas lights on a certain diode in different time intervals this effect lasts. I tried it before with the delay() function, but I found out I cannot do "multithreading" with it so I used millis() function for parallel execution. My code won't work as intended so I asked it here how would someone implement parallel execution of having this effect happening differently on every LED diode.

int i;
unsigned long timePassed = 0;
unsigned long ledInterval = 40;

unsigned long previousTimeLED1 = 0;
unsigned long previousTimeLED2 = 0;
unsigned long previousTimeLED3 = 0;
unsigned long previousTimeLED4 = 0;
unsigned long previousTimeLED5 = 0;

uint8_t bright=0;

void triangleRise(int pin) {
    bright += 2;
    analogWrite(pin, bright);
}
void triangleFall(int pin) {
    bright -= 2;
    analogWrite(pin, bright);
}
void setup() {
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  timePassed = millis();
  if (timePassed - previousTimeLED1>= ledInterval)
  {
    triangleRise(3);
    triangleFall(3);
    previousTimeLED1=timePassed;
  }
  if (timePassed - previousTimeLED2 >= ledInterval)
  {
    triangleRise(5); 
    triangleFall(5);
    previousTimeLED2=timePassed;
  }
  if (timePassed - previousTimeLED3 >= ledInterval)
  {
    triangleRise(6);
    triangleFall(6);
    previousTimeLED3=timePassed;
  }
  if (timePassed - previousTimeLED4 >= ledInterval)
  {
    triangleRise(9);
    triangleFall(9);
    previousTimeLED4=timePassed;
  }
  if (timePassed - previousTimeLED5 >= ledInterval)
  {
    triangleRise(10);
    triangleFall(10);
    previousTimeLED5=timePassed;
  }
}

If you ‘call’ triangleRise(3); then you ‘call’ triangleFall(3); what do you think will happen ?

Since you only have one ‘bright’ variable what happens ?

Variable bright cancels out to 0 so I need to figure out how to independently change the value of different brightness variables. So I should add different brightness variables to each LED and try it out?

Kuddos for using code tags on your first post. The key to making them all different is to embed the constants within a separate function for each led.

void setup() {
  pinMode(3, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  Serial.begin(9600);
}

void blink1(unsigned long now)
{
  const int pin = 3;
  const unsigned long interval = 40;
  static int bright;
  static int step = 2;
  static unsigned long previousTime;

  if (now - previousTime >= interval) {
    previousTime = now;
    bright += step;
    if ( bright > 255 ) {
      bright = 255;
      step = -step;
    }
    if ( bright < 0 ) {
      bright = 0;
      step = -step;
    }
    analogWrite(pin, bright);
  }
}

void blink2(unsigned long now)
{
  const int pin = 5;
  const unsigned long interval = 60;
  static int bright;
  static int step = 4;
  static unsigned long previousTime;

  if (now - previousTime >= interval) {
    previousTime = now;
    bright += step;
    if ( bright > 255 ) {
      bright = 255;
      step = -step;
    }
    if ( bright < 0 ) {
      bright = 0;
      step = -step;
    }
    analogWrite(pin, bright);
  }
}

void blink3(unsigned long now)
{
  const int pin = 6;
  const unsigned long interval = 85;
  static int bright;
  static int step = 5;
  static unsigned long previousTime;

  if (now - previousTime >= interval) {
    previousTime = now;
    bright += step;
    if ( bright > 255 ) {
      bright = 255;
      step = -step;
    }
    if ( bright < 0 ) {
      bright = 0;
      step = -step;
    }
    analogWrite(pin, bright);
  }
}

void blink4(unsigned long now)
{
  const int pin = 9;
  const unsigned long interval = 92;
  static int bright;
  static int step = 7;
  static unsigned long previousTime;

  if (now - previousTime >= interval) {
    previousTime = now;
    bright += step;
    if ( bright > 255 ) {
      bright = 255;
      step = -step;
    }
    if ( bright < 0 ) {
      bright = 0;
      step = -step;
    }
    analogWrite(pin, bright);
  }
}

void blink5(unsigned long now)
{
  const int pin = 10;
  const unsigned long interval = 83;
  static int bright;
  static int step = 3;
  static unsigned long previousTime;

  if (now - previousTime >= interval) {
    previousTime = now;
    bright += step;
    if ( bright > 255 ) {
      bright = 255;
      step = -step;
    }
    if ( bright < 0 ) {
      bright = 0;
      step = -step;
    }
    analogWrite(pin, bright);
  }
}

void loop() {
  unsigned long timePassed = millis();
  blink1(timePassed);
  blink2(timePassed);
  blink3(timePassed);
  blink4(timePassed);
  blink5(timePassed);
}

Writing code is all about trying things to see if changes work. :wink:

Get one LED sequence to work, then add the other LEDs.

As “larryd” has pointed out, you increment the brightness and right after you decrement it. You should make something like this:

int step = 2;
int brightness = 0;

void loop()
{
  brightness += step;
  if (brightness >= 255)
  {
    brightness = 255;
    step = -step; //Invert direction
  }
  else if (brightness <= 0)
  {
    brightness = 0;
    step = -step;
  }
  analogWrite(led, brighness);
  delay(50);
}

a separate function for each led.

That sounds like an ugly solution

How about an array of structs each with data about a single LED and a single function ?

Thank you everybody for your solutions, I'm going to rewrite the blh64's code in an array of structs for better readability. A little problem I have is that transition is not smooth as I used the delay() function before like I can see every step, not like a smooth rising/falling linear curve.

UKHeliBob:
That sounds like an ugly solution

How about an array of structs each with data about a single LED and a single function ?

True, it would be more elegant, but probably not best for a beginner to understand. A brute force method is just fine for 5 leds. Probably not for 50 leds.

hrterminator:
Thank you everybody for your solutions, I'm going to rewrite the blh64's code in an array of structs for better readability. A little problem I have is that transition is not smooth as I used the delay() function before like I can see every step, not like a smooth rising/falling linear curve.

It was configured that way. Just change

  static int step = 2;

to

  static int step = 1;

Also if you want a really natural fade you have to have gamma correction.

blh64:
True, it would be more elegant, but probably not best for a beginner to understand. A brute force method is just fine for 5 leds. Probably not for 50 leds.

The brute force method is certainly easy to write and understand, but however many LEDs are involved such a crude method offends me, hence my suggestion. I like my programs not only to work but to be elegant, but each to their own

I see that the OP has embraced the idea of using an array of structs

Here you can this.

#include <multiMap.h>
#include <timeObj.h>
#include <idlers.h>


#define  PROFILE_MS  400   // Total time that the profile spans.
#define  CHANGE_MS   2      // How much time between recalculating new brightness values.



// Create a class that takes a  time,value profile and plays it as a PWM.
// And, runs in the background.


class mappedAnalog   : public idler {

   public:
               mappedAnalog(multiMap* inMap,int inPin,float inChange,float totalMs);
   virtual     ~mappedAnalog(void);

               void  start(float delayMs=0);
   virtual     void  idle(void);

               multiMap*   ourProfile;
               int         pin;
               int         frameMs;
               float       changeMs;
               float       profileMs;
               timeObj     changeTimer;
               bool        delaying;
};


mappedAnalog::mappedAnalog(multiMap* inMap,int inPin,float inChange,float totalMs) {

   ourProfile  = inMap;
   pin         = inPin;
   frameMs     = 0;
   changeMs    = inChange;
   profileMs   = totalMs;
   changeTimer.setTime(changeMs,false);
   delaying    = false;
}


mappedAnalog::~mappedAnalog(void) {  }


void mappedAnalog::start(float delayMs) {

   if (delayMs>0) {
      changeTimer.setTime(delayMs);
      delaying = true;
   }
   hookup();
   pinMode(pin,OUTPUT);
   frameMs = 0;
   changeTimer.start();
}


void mappedAnalog::idle(void) {

   float newBright;
   
   if (delaying) {
      if(changeTimer.ding()) {
         changeTimer.setTime(changeMs);
         changeTimer.start();
         delaying = false;
      }
   } else {
      if (changeTimer.ding()) {
         if (frameMs>=profileMs) {
            frameMs = 0;
         } else {
            frameMs = frameMs + changeMs;
         }
         newBright = ourProfile->map(frameMs);
         analogWrite(pin, round(newBright));
         changeTimer.start();
      }
   }
}




// ********************* Program starts here *********************



multiMap       profile;    // Your time / brightness profile.
mappedAnalog*  LED1;       // An LED
mappedAnalog*  LED2;       // ANother..
mappedAnalog*  LED3;       // Notice these are actually just global pointers to LED objects. Make as many as you need.


void setup(void) {

   profile.addPoint(0,0);                 // Build the pofile by adding points (time in ms, brighness value)
   profile.addPoint(PROFILE_MS/2,255);    // Second point.
   profile.addPoint(PROFILE_MS,0);        // Last point. ( You can actually add as many points as you like. Go crazy! )
   
   LED1 = new mappedAnalog(&profile,3,CHANGE_MS,PROFILE_MS);   // Create first LED object. Address of profile, pin num, frame rate, total time for profile.
   LED1->start();                                              // Fire uip first LED.
   LED2 = new mappedAnalog(&profile,9,CHANGE_MS,PROFILE_MS);   // Create second LED.
   LED2->start(200);                                           // Fire it up with a prese delay before starting.
   LED3 = new mappedAnalog(&profile,10,CHANGE_MS,PROFILE_MS);  // Third LED
   LED3->start(250);                                           // And again a delay from time 0 for actual start.
}


void loop(void) {
   
   idle();  // Runs all the background things.
}

If you would like to try this you’ll need to grab LC_baseTools from the library manager.

Good luck!

-jim lee