Fading LED: analogWrite millis() Example

Add a fading LED without delay()

Fading LED millis() Example

It’s a well-known fact of engineering: LEDs make everything look better. And that means a Fading LED is even better. Using Arduino’s analogWrite(), fading a LED is just a matter of a loop. If you use delay(), you can’t easily add other actions. What can you do? Well, Fading a LED with millis() is pretty simple. Here’s the code to do it and a quick explanation.

// Example Fading LED with analogWrite and millis()
// See baldengineer.com/fading-led-analogwrite-millis-example.html for more information
// Created by James Lewis

const byte pwmLED = 5;

// define directions for LED fade
#define UP 0
#define DOWN 1

// constants for min and max PWM
const int minPWM = 0;
const int maxPWM = 255;

// State Variable for Fade Direction
byte fadeDirection = UP;

// Global Fade Value
// but be bigger than byte and signed, for rollover
int fadeValue = 0;

// How smooth to fade?
byte fadeIncrement = 5;

// millis() timing Variable, just for fading
unsigned long previousFadeMillis;

// How fast to increment?
int fadeInterval = 50;

void setup() {
  // put pwmLED into known state (off)
  analogWrite(pwmLED, fadeValue); 

void doTheFade(unsigned long thisMillis) {
  // is it time to update yet?
  // if not, nothing happens
  if (thisMillis - previousFadeMillis >= fadeInterval) {
    // yup, it's time!
    if (fadeDirection == UP) {
      fadeValue = fadeValue + fadeIncrement;  
      if (fadeValue >= maxPWM) {
        // At max, limit and change direction
        fadeValue = maxPWM;
        fadeDirection = DOWN;
    } else {
      //if we aren't going up, we're going down
      fadeValue = fadeValue - fadeIncrement;
      if (fadeValue <= minPWM) {
        // At min, limit and change direction
        fadeValue = minPWM;
        fadeDirection = UP;
    // Only need to update when it changes
    analogWrite(pwmLED, fadeValue);  

    // reset millis for the next iteration (fade timer only)
    previousFadeMillis = thisMillis;

void loop() {
  // get the current time, for this time around loop
  // all millis() timer checks will use this time stamp
  unsigned long currentMillis = millis();


Global Variables for Fading LED


The constant “pwmLED” is the pin that gets faded. (Make that pin supports analogWrite().) The two #define statements, UP and DOWN, are simple states for the LED. They make the code easier to read. The “maxPWM” value is set to 255 for 8-bit AVR boards like the Uno. If you’re using the ESP8266, set this value to 1023. (You can also use the constant “PWMRANGE.”)

One side note. While this fading LED code is linear, the LED’s brightness is not. For uniform intensity, the fade value needs a correction factor. This is more evident on bright blue LEDs than on dim red ones. But that’s a subject for another post.


Next, let’s look at how to keep track of which way the LED is fading.

The variable “fadeDirection” keeps track of the fading state. It is a simple flag variable. The code starts with the fade going UP, or turning on. I created it as a byte instead of bool. (This type allows you to add more states in the future.) The variable “fadeValue” keeps track of the LED’s intensity. You can modify the smoothness of the fade using “fadeIncrement.” While changing “fadeInterval” controls the speed. Keep in mind that “fadeInterval” is the number of milliseconds between states.


As with all millis() code, you need to track the time since the timer event occurred. In my millis() examples, I use the variable “previousMillis.” But each timer event needs a unique copy of the previous millis(). In this code, I’ve given the previousMillis variable a unique name, “previousFadeMillis.” When combining this fading LED example with your code, that unique name will prevent a collision.

Also, you might want to create multiple interval variables.


The analogWrite() called in setup() is optional. Since you don’t need to call pinMode() with analogWrite(), I put an analogWrite() in setup(). Here’s why.

Calling pinMode() implies the pin’s function is a digital input or output. Since we want the PWM function, that is counter intuitive. At the same time, I like being able to glance at pin assignments by looking just at setup(). So when I see an analogWrite(), I know I’m using the pin “pwmLED” as a PWM pin.


The actual PWM fading happens with the code in this function. By isolating it to a function, it’s much easier to add the fade to existing code. The function’s argument “thisMillis” is the millis() timestamp from loop(). The first if-statement is the standard reset millis() check. If it’s time to update the LED fading, it happens. Otherwise, the function just exits.

In conclusion, this example can be used to do a simple fade without delay(). Normally you’ll call doTheFade() from loop(). Anytime you have lots of processing, give doTheFade() a quick call. If it isn’t time to fade yet, it’ll just return because there is nothing to do yet. This function will give the appearance of your Arduino program multitasking!

Checkout other millis() Examples

Long comments, URLs, and code tend to get flagged for spam moderation. No need to resubmit.

ALL comments submitted with fake or throw-away services are deleted, regardless of content.

Don't be a dweeb.

Leave a comment

21 thoughts on “Fading LED: analogWrite millis() Example

  1. Thank you for making this! I am trying to build an alarm clock with a fading light in it (a wake up light). for the interface with this alarm clock I am using a touch screen. For the fading I want to be able to switch off the alarm during the fading, that is why Millis seem to be ideal.
    I am currently testing the code and it works fine as long as I call upon it in the loop directly. However when I try to use a button on the touch screen (the check button) to start the function the LED goes on, but it does not fade.
    Any idea what the problem could be?
    I pasted the code here: https://pastebin.com/JjLrF6Zv
    Thanks in advance!

  2. Thank you so much for posting this article with a great explanation. I was having trouble getting LEDs, a servo motor, and a couple of other items functioning currently using delay(). This should really help out.