XL LED/Brightness/06. Brightness is a duty cycle
Brightness · 06 of 9

Brightness is a duty cycle

A digital pin is only ever on or off. analogWrite switches it on and off hundreds of times a second and varies how long it stays on in each cycle. The LED follows too quickly for the eye, so you see the average.

On and off, very quickly

A digital pin has two states: HIGH, the board's logic voltage, and LOW, 0 V. There is no setting in between. So analogWrite(pin, 128) does not put 2.5 V on the pin. It switches the pin fully on and fully off, over and over, and makes the on part of each cycle half as long as the whole.

That fraction is the duty cycle. It runs from 0, always off, to 255, always on, and the name for the technique is PWM, pulse-width modulation.

Brightness is a duty cycle
analogWrite value64 of 255
Your board
Duty
25 %
Average current
3.3 mA
Looks
53 % bright
The pin is still only ever 5 V or 0 V. It is HIGH for 25 % of every 2041 µs period and the LED flashes that fast, far quicker than an eye can follow, so what you see is the average. Note the last number: 25 % of the current looks like about 53 % of the brightness.

How fast

On an Uno's pin 9 the cycle repeats 490 times a second, so each one lasts about 2 ms. The ESP32, ESP32-S3 and Pico cores use 1000 times a second by default. Either is far too fast for an eye to follow. The LED genuinely flashes, but what you see is the average.

The LED keeps up easily. Nothing on this block slows it down: no capacitor, no driver, just the LED and its resistor. Each time the pin goes HIGH, the full 13 mA flows at once (on an Uno), and each time it goes LOW, it stops.

The average is what counts

At a duty of 64, a quarter of 255, the pin is HIGH a quarter of the time, so the average current is a quarter of 13 mA, about 3.3 mA. That is also what a battery powering the project would see.

The ceiling is the same as for digitalWrite. PWM can make the LED dimmer than full, never brighter. On a 3.3 V board the full is about 2 mA, so the whole range is squeezed into what the previous article worked out.

Which pins

On an Uno only six pins can do this: 3, 5, 6, 9, 10 and 11, each marked with a tilde (~) on the board. On the ESP32 boards and the Pico, nearly every output pin can. The sketch uses D9 on an Uno for that reason, and the wiring from the first blink already works.

The code

Ramps analogWrite from 0 to 255 and back, one step every 8 ms. Change LED_PIN to the PWM pin you wired SIGNAL to.

xl_led_fade.ino
/*
  XL LED - fade with analogWrite                          TK01 / /p/tk01

  Wiring. Count from the square pad on the TinkerBlock board, LED side
  up, header at the bottom:

    GND    -> GND
    NC     -> nothing   (both NC pins are unconnected on the board)
    SIGNAL -> a PWM pin: D9 on an Uno, GPIO 4 on an ESP32 or ESP32-S3,
              GP15 on a Raspberry Pi Pico

  Arduino IDE
    Tools > Board                 your board, e.g. ESP32S3 Dev Module
    Tools > Port                  the one that appears when you plug in
    Tools > USB CDC On Boot       Enabled   (ESP32-S3 only)
    No library needed. ESP32 boards need board package 2.0 or later.
*/

// A PWM pin. Uno: 9. ESP32, ESP32-S3: 4. Pico: 15.
const int LED_PIN = 4;

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

void loop() {
  // 0 is always LOW, 255 is always HIGH; in between, the pin spends
  // that fraction of every PWM period HIGH.
  for (int duty = 0; duty <= 255; duty++) {
    analogWrite(LED_PIN, duty);
    delay(8);
  }
  for (int duty = 255; duty >= 0; duty--) {
    analogWrite(LED_PIN, duty);
    delay(8);
  }
}

The fade takes about two seconds each way, and it will look wrong: quick to brighten, then almost no change over the top half. That is not the sketch. It is how the eye works, and a fade that looks even is in the next article.

When it does not work

analogWrite does nothing on my pin.

The pin may not be PWM-capable. On an Uno only pins 3, 5, 6, 9, 10 and 11 are, marked with a tilde. On an ESP32, ESP32-S3 and Pico nearly every output pin is. Wire SIGNAL to D9 on an Uno and the sketch works unchanged.

analogWrite is not declared, on my ESP32.

Update the ESP32 board package. analogWrite arrived in the ESP32 core's 2.0 releases and is standard in 3.x. On an old core, use the ledc functions instead; the ESP32 PWM article linked below shows both.

The LED flickers on camera but looks steady to me.

That is the PWM itself. A phone camera samples the light far more often than an eye does and catches the off parts of each cycle. It does not mean anything is wrong, and it is not visible in the room.

Half of 255 does not look half as bright.

Correct, and it is not a fault. Brightness as the eye sees it is not proportional to the duty cycle, so 128 looks much more than half bright. The next article corrects it with a small table.

Where this goes next

Why the ramp above looks lopsided, and the ten-line table that fixes it.

A fade that looks even

Edit this page — content/books/xl-led/brightness-is-a-duty-cycle.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

XL LED

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum