PWM - Fade a pin

I am a noob with C/C++, But i have done some Perl in my day and can understand a lot of the programing languages out there.

This is my first code I ever made for an Arduino Leonardo that verified with no error's, but will it work the way I think it should?
It should be another way of doing http://bildr.org/2012/03/rfp30n06le-arduino/
Using PWM to fade a pin from 0 to 255 and 255 to 0.
I don't want to upload it and it brake my new toy.

//////////////////////////////////////////////////////////////////
//By: S_Flex
//Date: 11/09/2012
//Fade PWM output
//////////////////////////////////////////////////////////////////

#define fadePin3  3 
#define DelayTime 15 

int ThisLoop = 0; // current loop style 0 for ++ and 255 for --
int NextLoop = 255;

void setup(){
  pinMode(fadePin3, OUTPUT);
}

void loop(){

  // Fade pin loop
  for(int i = ThisLoop; i<NextLoop;){
    if (ThisLoop == 0) i++; // Will this work? Or should I do if & else?
    if (ThisLoop == 255) i--; 
    analogWrite(fadePin3, i);
    delay(DelayTime);
  }

  // Set next loop
  if(ThisLoop == 0) {
    ThisLoop = 255;
    NextLoop = 0;
  }
  else if (ThisLoop == 255) {
    ThisLoop = 0;
    NextLoop = 255; 
  }
}

You're not going to break anything by uploading code, unless you've miswired your LED, but you might want to go and look at how "for" loops are structured.

PWM pins don't need to have their pinMode set.

go and look at how "for" loops are structured

You can easily omit the increment part of a for loop.
In this case it's just right, although I had to look twice to get how it is intended. ( but that's straightforward and super clear for a Perl programmer ) :wink:

AWOL:
You're not going to break anything by uploading code, unless you've miswired your LED, but you might want to go and look at how "for" loops are structured.

PWM pins don't need to have their pinMode set.

So I dont need anything in "void setup"?

My for loop ahhhhh...... yep I see it now, thanks.

Better?

//////////////////////////////////////////////////////////////////
//By: S_Flex
//Date: 11/09/2012
//Fade PWM output
//////////////////////////////////////////////////////////////////

#define fadePin3  3 
#define DelayTime 15 

int ThisLoop = 0; // current loop style 0 for ++ and 255 for --
int NextLoop = 255;

int temploop = 0;

void setup(){
  //pinMode(fadePin3, OUTPUT);
}

void loop(){

  // Fade pin loop
  for(int i = ThisLoop; i<NextLoop;){
    analogWrite(fadePin3, i);
    delay(DelayTime);
    if (ThisLoop == 0) i++;
    if (ThisLoop == 255) i--; 
  }
  // Set next loop
  temploop = ThisLoop;
  ThisLoop = NextLoop;
  NextLoop = temploop;    
}

A for loop without its i++ at the end looks strange to me :slight_smile:

I'd use a direction variable with values +1 or -1 and would add that to the current pwm value.

int i;
int pwm = 0;
int direction = +1;    // either +1 or -1

analogWrite(pin, pwm);

for (i = 0; i < 255; i++) {
    pwm += direction;
    analogWrite(pin, pwm);
    delay(d);    // see "blink without delay" example for how to get rid of this!
}
direction = -direction;

If direction is greater that 1 then 255 needs to be decreased accordingly.

HTH

tuxduino:
A for loop without its i++ at the end looks strange to me :slight_smile:

I'd use a direction variable with values +1 or -1 and would add that to the current pwm value.

int i;

int pwm = 0;
int direction = +1;    // either +1 or -1

analogWrite(pin, pwm);

for (i = 0; i < 255; i++) {
    pwm += direction;
    analogWrite(pin, pwm);
    delay(d);    // see "blink without delay" example for how to get rid of this!
}
direction = -direction;




If direction is greater that 1 then 255 needs to be decreased accordingly.

HTH

That looks a lot better then mine.. thanks

S_Flex:
I am a noob with C/C++, But i have done some Perl in my day and can understand a lot of the programing languages out there.

Writing perl with a C++ compiler probably isn't the best way to approach things.

S_Flex:
I don't want to upload it and it brake my new toy.

You won't break it by what you upload, only by what you connect to the pins.

After going through the code to check that the numbers didn't go out of rang and didn't have any repeating numbers.
I used this code for a fan on a mosfet and it works!!!!!!!!
I made it so the settings can be changed at the constants. I'm starting the PWM at 175 because I have a fan that will make noise on low RPM's but it was just a small test for something bigger that will most likely use this code.
Thanks to all that helped.

const int fadePin3  = 3;
const int DelayTime = 50;
const int PWMstart  = 175; // This can be 0 to 254

int i             = PWMstart;
int pwm           = PWMstart;
int PWMdirection  = +1;    // either +1 or -1

void setup(){
  // Not needed
}

void loop(){
  // Fade pin loop
  for (i = PWMstart; i < 255; i++) {    
    analogWrite(fadePin3, pwm);
    delay(DelayTime);    // see "blink without delay" example for how to get rid of this!
    pwm += PWMdirection;
  }
  // Switch fade direction
  if (pwm == 255) PWMdirection = -1;
  if (pwm == PWMstart) PWMdirection = +1; 
}
  if (pwm == 255) PWMdirection = -1;
  if (pwm == PWMstart) PWMdirection = +1;

Exact value tests are rarely a good idea. if(pwm >= 255) and if(pwm <= PWMstart) would involve typing exactly the same number of characters, and handle any issues that might (however unlikely) happen.

Depending on the project requirements, it could be a good idea to also substitute 255 with a named constant, like PWMend, to be able to easily limit the max fan speed.

Also, if the code is to be included in a bigger sketch, then getting rid of delay() is a must. As it is now, the sketch can't do nothing else while the for() loop is running.

I'm putting together an example...

Ok, I'm away from the bench right now, and I just have an arduino+lcd shield to test things on...

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);    // nuelectronics lcd-shield v1.1

int numCols = 16;
int numRows = 2;

const int PWM_pin  = 3;
const unsigned long Fade_delayMs = 500;    // with just 50ms I could not read the numbers on the lcd ;-)
const int Fade_start  = 175; // This can be 0 to 254
const int Fade_end    = 255; // this can be PWMstart+1..255

int Fade_value;
int Fade_direction = +1;
int Fade_step = 8;        // this example lets you increase or decrease the pwm value by more than 1 count at a time

unsigned long Fade_prevMillis;


/* to be used with the real circuit */
void pwmOut(int value) {
    analogWrite(PWM_pin, value);
}

/* to run a simulation */
void lcdOut(int value) {
    lcd.clear();
    lcd.print(value, DEC);
}

void Fade_outFunc(int value) {
    // run one or both output functions...
    lcdOut(value);
    pwmOut(value);
}


void setup() {
    lcd.begin(numCols, numRows);
    lcd.clear();

    Fade_value = Fade_start;
    Fade_outFunc(Fade_value);
}


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

    if(currentMillis - Fade_prevMillis > Fade_delayMs) {
        Fade_prevMillis = currentMillis;

        Fade_value += Fade_direction * Fade_step;
        if (Fade_value >= Fade_end) {
            Fade_value = Fade_end;
            Fade_direction = -Fade_direction;
        }
        else if (Fade_value <= Fade_start) {
            Fade_value = Fade_start;
            Fade_direction = -Fade_direction;
        }

        Fade_outFunc(Fade_value);
    }
}

All the variables and functions that I use for the fading algorithm are prefixed with Fade_, to ease integration in a bigger sketch.

The fading algorithm is decoupled from the hardware-related code. This makes it easy to change the sketch behaviour from writing to and lcd to changing a fan speed by just enabling one or more "out" functions inside Fade_outFunc().

This all cries to be turned into a Fade() class, but that's another topic on its own... :wink:

To make it behave like your previous sketch, change Fade_delayMs to 50 and Fade_step to 1.

tuxduino:
Ok, I'm away from the bench right now, and I just have an arduino+lcd shield to test things on...

#include <LiquidCrystal.h>

LiquidCrystal lcd(8, 9, 4, 5, 6, 7);    // nuelectronics lcd-shield v1.1

int numCols = 16;
int numRows = 2;

const int PWM_pin  = 3;
const unsigned long Fade_delayMs = 500;    // with just 50ms I could not read the numbers on the lcd :wink:
const int Fade_start  = 175; // This can be 0 to 254
const int Fade_end    = 255; // this can be PWMstart+1..255

int Fade_value;
int Fade_direction = +1;
int Fade_step = 8;        // this example lets you increase or decrease the pwm value by more than 1 count at a time

unsigned long Fade_prevMillis;

/* to be used with the real circuit */
void pwmOut(int value) {
   analogWrite(PWM_pin, value);
}

/* to run a simulation */
void lcdOut(int value) {
   lcd.clear();
   lcd.print(value, DEC);
}

void Fade_outFunc(int value) {
   // run one or both output functions...
   lcdOut(value);
   pwmOut(value);
}

void setup() {
   lcd.begin(numCols, numRows);
   lcd.clear();

Fade_value = Fade_start;
   Fade_outFunc(Fade_value);
}

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

if(currentMillis - Fade_prevMillis > Fade_delayMs) {
       Fade_prevMillis = currentMillis;

Fade_value += Fade_direction * Fade_step;
       if (Fade_value >= Fade_end) {
           Fade_value = Fade_end;
           Fade_direction = -Fade_direction;
       }
       else if (Fade_value <= Fade_start) {
           Fade_value = Fade_start;
           Fade_direction = -Fade_direction;
       }

Fade_outFunc(Fade_value);
   }
}




All the variables and functions that I use for the fading algorithm are prefixed with Fade_, to ease integration in a bigger sketch.

The fading algorithm is decoupled from the hardware-related code. This makes it easy to change the sketch behaviour from writing to and lcd to changing a fan speed by just enabling one or more "out" functions inside Fade_outFunc().

This all cries to be turned into a Fade() class, but that's another topic on its own... ;-)

To make it behave like your previous sketch, change Fade_delayMs to 50 and Fade_step to 1.

Thanks!!! that saved me a week and a lot of questions.

I have one question. Would my Arduino Leonardo be maxed out if I added 2 fans, 2 peltiers, 4 TMP36 temp sensor and a 7-segment Serial Display? Can I fit more maybe 1 to 4 more push buttons?

I also had an idea to use one analog pin for all my push buttons and put the push buttons on different resistance to use parts of one analog rang for all my switching. Do you know what i mean?

I also had an idea to use one analog pin for all my push buttons and put the push buttons on different resistance to use parts of one analog rang for all my switching. Do you know what i mean?

The lcd shield I used in my test has exactly that: 5 pushbuttons on a single analog pin. Read the analog value and you know if a button was pressed, and which one. Just beware that you won't see exact analog reads. You'll have to map analog read ranges to pushbuttons. Also, provide a NO_BUTTON state, in which you are when the analog value is > 1000.

Using that last code you posted I'm starting to work on a program that will control more than one PWM pin. What I have now hasn't been tested and uses a lot of arrays, but is readable.
If I can get it into an array of arrays it would be less readable, but may cut down on the clutter.
Also doing more than one at a time I think the program may have some timing issues where pins are left on or off longer than set by Fade_delaysMs because of the delays before and after each other.
I may just make each delay the same but its kinda nice to have that control.

One thing about the code is I'm not sure if sizeof will return the array number or whats the way to do that?

const int PWM_pins[]                = {3,   4,   8,   3,   6};   // Pin numbers used
const unsigned long Fade_delaysMs[] = {500, 500, 500, 500, 500}; // with just 50ms I could not read the numbers on the lcd ;-)
const int Fade_starts[]             = {175, 175, 175, 175, 175}; // This can be 0 to 254
const int Fade_ends[]               = {255, 255, 255, 255, 255}; // this can be PWMstart+1..255
const int Fade_steps[]              = {8,   8,   8,   8,   8};   // this example lets you increase or decrease the pwm value by more than 1 count at a time
int Fade_directions[]               = {+1,  +1,  +1,  +1,  +1};  // Fade direction for each pin

int Fade_values[] = {0,0,0,0,0}; 
unsigned long Fade_prevMillis[] = {0,0,0,0,0};

void Fade_outFunc(int array_number) {
    unsigned long currentMillis = millis();

    if(currentMillis - Fade_prevMillis[array_number] > Fade_delaysMs[array_number]) {
        Fade_prevMillis[array_number] = currentMillis;

        Fade_values[array_number] += Fade_directions[array_number] * Fade_steps[array_number];
        if (Fade_values[array_number] >= Fade_ends[array_number]) {
            Fade_values[array_number] = Fade_ends[array_number];
            Fade_directions[array_number] = -Fade_directions[array_number];
        }
        else if (Fade_values[array_number] <= Fade_starts[array_number]) {
            Fade_values[array_number] = Fade_starts[array_number];
            Fade_directions[array_number] = -Fade_directions[array_number];
        }

        analogWrite(PWM_pins[array_number], Fade_values[array_number]);
    }    
}


void setup() {
 for (int i = 0; i < sizeof(PWM_pins) - 1; i++){
    Fade_values[i] = Fade_starts[i];
    analogWrite(PWM_pins[i], Fade_values[i]);
 }
}

int a = 0;
void loop() {
 for (a = 0; a < sizeof(PWM_pins) - 1; a++){
    Fade_outFunc(a);
 }
}

The following macro gives you the number of elements of an array:

#define ARY_LEN(a) (sizeof(a)/sizeof(a[0]))

Looks good, but you misunderstood what outFunc() was for. The name stands for "take the computed value and drive the hardware with it" (in some way).
Your outFunc looks more like a "Fade_run".

Here's my version:

#define ARY_LEN(a) (sizeof(a)/sizeof(a[0]))

#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);    // nuelectronics lcd-shield v1.1

int numCols = 16;
int numRows = 2;

const int PWM_pin[]                = { 3,   4,   8,   3,   6   };   // Pin numbers used
const unsigned long Fade_delayMs[] = { 100, 300, 300, 400, 500 };
const int Fade_start[]             = { 175, 175, 175, 175, 175 }; // This can be 0 to 254
const int Fade_end[]               = { 255, 255, 255, 255, 255 }; // this can be PWMstart+1..255
const int Fade_step[]              = { 1,   3,   5,   5,   8   };
int Fade_direction[]               = { +1,  +1,  +1,  +1,  +1  };  // Fade direction for each pin
int Fade_value[]                   = {  0,   0,   0,   0,   0  }; 
unsigned long Fade_prevMillis[]    = {  0,   0,   0,   0,   0  };


/* to be used with the real circuit */
void pwmOut(int fadeNum, int value) {
    analogWrite(PWM_pin[fadeNum], value);
}


/* to run a simulation */
void lcdOut(int fadeNum, int value) {
    int col, row;
    if (fadeNum <= 3) {
        row = 0;
        col = fadeNum * 4;
    }
    else {
        row = 1;
        col = (fadeNum - 4) * 4;
    }
    lcd.setCursor(col, row);
    lcd.print("    ");
    lcd.setCursor(col, row);
    lcd.print(value, DEC);
}


void Fade_outFunc(int fadeNum, int value) {
    // run one or both output functions...
    lcdOut(fadeNum, value);
    //pwmOut(fadeNum, value);
}


void setup() {
    lcd.begin(numCols, numRows);
    lcd.clear();

    for (unsigned int i = 0; i < ARY_LEN(PWM_pin); i++){
        Fade_value[i] = Fade_start[i];
        Fade_outFunc(i, Fade_value[i]);
    }
}


void Fade_run(unsigned long currentMillis, int fadeNum) {
    if(currentMillis - Fade_prevMillis[fadeNum] > Fade_delayMs[fadeNum]) {
        Fade_prevMillis[fadeNum] = currentMillis;

        Fade_value[fadeNum] += Fade_direction[fadeNum] * Fade_step[fadeNum];
        if (Fade_value[fadeNum] >= Fade_end[fadeNum]) {
            Fade_value[fadeNum] = Fade_end[fadeNum];
            Fade_direction[fadeNum] = -Fade_direction[fadeNum];
        }
        else if (Fade_value[fadeNum] <= Fade_start[fadeNum]) {
            Fade_value[fadeNum] = Fade_start[fadeNum];
            Fade_direction[fadeNum] = -Fade_direction[fadeNum];
        }

        Fade_outFunc(fadeNum, Fade_value[fadeNum]);
    }
}


void loop() {
    for (unsigned int i = 0; i < ARY_LEN(PWM_pin); i++) {
        Fade_run(millis(), i);
    }
}

(I modified the delay and step values to have a visual confirmation that the 5 "fades" were running independently).

As an exercise, I tried to rewrite the multiple-fade code in OO style. Probably wastes some ram bytes and some cpu cycles, but the main sketch looks indeed more elegant, or at least more concise :stuck_out_tongue_winking_eye:

This is the sketch:

#define ARY_LEN(a) (sizeof(a)/sizeof(a[0]))

#include <LiquidCrystal.h>
#include "Fade.h"
#include "LcdFade.h"
#include "PwmFade.h"


LiquidCrystal lcd(8, 9, 4, 5, 6, 7);    // nuelectronics lcd-shield v1.1

int numCols = 16;
int numRows = 2;


Fade* faders[] = {
    new LcdFade(  0, 255,   4, 250, true, lcd, 0, 0),
    new LcdFade(100, 200,  10, 300, true, lcd, 0, 4),
    new LcdFade(175, 255,   1, 500, true, lcd, 0, 8),
    new PwmFade(175, 255,   5,  50, true, 3)
};


void setup() {
    lcd.begin(numCols, numRows);
    lcd.clear();
}


void loop() {
    for (unsigned int i = 0; i < ARY_LEN(faders); i++) {
        faders[i]->run();
    }
}

To avoid creating a two-pages long post I've attached the other files.

The main point was encapsulating the fading algorithm in a general Fade class, and leaving the definition of what to do with the fading value to the derived classes. Therefore the Fade class has a pure virtual outFunc(), which makes it an abstract class.

In my previous example I had two "actuation" functions, one that actually drove a pwm pin, and another that printed the value on an lcd. Thus I created two child classes, one for each of those output functions.

Each child class has some additional data members required for the specific "action" it has to perform with the value.

The array of "faders" is an array of pointers to the base class, so inside loop() I can just call "run()" and let the polymorphism figure out which actual function to call.

This code looses the ability to perform two actions with the same fading value. This can be easily changed in different ways. For example the two child classes could be merged into one.

LcdFade.h (600 Bytes)

PwmFade.h (401 Bytes)

Fade.h (529 Bytes)

Fade.cpp (1001 Bytes)

Ok, I probably have too much free time :stuck_out_tongue:

Just noticed the Fade class is missing a few bits (which is now to late to write...):

  • a reset method;
  • the ability to modify the fade parameters after object instantiation.

Doesn't the for need <= to get to the last number?

I'm sure I will have more question later, but for now you gave me something to play with.

Thanks for the help.

Doesn't the for need <= to get to the last number?

What exact piece of code are you referring to ?