007 Skill Lab Beginner

LED Dimming

A digital pin only knows on and off. PWM makes it look like it knows dim and bright.

Text

Mission: fade instead of blink

Blinking switches an LED fully on and fully off. Dimming uses PWM: the pin switches very quickly, and your eyes average the pulses into brightness. You will reuse the external LED circuit, then change only the code.
Media

PWM in one picture

PWM duty cycle diagram showing different on-time percentages
Duty cycle is the fraction of each cycle spent on. Image source: SunFounder Pico 2 W Starter Kit documentation, MicroPython Projects, © 2026 SunFounder.
Duty cycle is the important knob. A low duty cycle means short on-pulses and a dim LED. A high duty cycle means long on-pulses and a bright LED. Frequency is how often the pulse pattern repeats. For visible LEDs, 1000 Hz is fast enough that the LED appears steady instead of flickery.
Media

PWM-capable pins

Pico 2 W pin reference showing PWM-capable GPIO pins
Many Pico GPIO pins can use PWM, including GP15 in this lesson. Image source: SunFounder Pico 2 W Starter Kit documentation, MicroPython Projects, © 2026 SunFounder.
The Pico can create PWM on many GPIO pins. This lesson stays on GP15 so the external LED wiring remains familiar. Later, shared PWM slices can matter when two pins need different frequencies. For now, one LED on one PWM pin keeps the idea clean.
Wiring

Reuse the LED circuit

Breadboard wiring for Pico 2 W, resistor, and external LED on GP15
Same LED circuit, new control style. Image source: SunFounder Pico 2 W Starter Kit documentation, MicroPython Projects, © 2026 SunFounder.
Use the same GP15, resistor, LED, and GND path from the external LED lesson. Do not remove the resistor just because the LED is dimming. PWM changes timing, not the need for current limiting.
Code placeholder

Code: smooth fade loop

from machine import Pin, PWM
from time import sleep

led = PWM(Pin(15))
led.freq(1000)

try:
    while True:
        for duty in range(0, 65536, 1024):
            led.duty_u16(duty)
            sleep(0.01)

        for duty in range(65535, -1, -1024):
            led.duty_u16(duty)
            sleep(0.01)
finally:
    led.duty_u16(0)
    led.deinit()
Text

How the PWM code works

PWM(Pin(15)) turns GP15 into a PWM output object. led.freq(1000) sets the pulse frequency to 1000 cycles per second. MicroPython duty_u16 uses a 16-bit number. 0 means always off, 65535 means almost always on, and values between those extremes create different brightness levels. range(0, 65536, 1024) walks upward in chunks instead of jumping straight to full brightness. The second loop counts down, creating the fade-out. finally runs when you stop the program from Thonny. It turns the LED off and releases the PWM peripheral.
Debug

Debug checklist

If the LED only blinks or stays solid: - Confirm the code uses PWM(Pin(15)), not Pin(15, Pin.OUT). - Confirm duty_u16 is spelled with the underscore. - Check that the external LED circuit still works with the simple blink code. - Try a larger sleep value like 0.03 to make the fade easier to see. If the LED flickers visibly, try a higher frequency like 2000. If it is too bright, reduce the maximum duty value in the loop.
Remix

Remix: breathe pattern

Make the fade feel like a breathing status light: - Use a smaller step like 512 for smoother changes. - Add sleep(0.4) at full brightness. - Add sleep(0.8) after turning fully off. Small timing changes can make the same circuit feel calm, urgent, or playful.
Checkpoint

Checkpoint

Mark complete when: - Your external LED fades up and down. - You can explain duty cycle without using the word magic. - You changed either the step size, frequency, or sleep timing and observed the result. - You can explain why the resistor is still required.
Reflection

Private Dev Log

Write a short build note: 1. PWM makes brightness by... 2. My best fade setting was... 3. A project that needs dimming is...
Text

References