Micropython Basics: Analog Output
Table of Contents
Analog Output (PWM)
Unlike true analog signals, most microcontrollers — including ESP32, ESP8266, and Raspberry Pi Pico — do not have real analog output pins. Instead, they use a technique called PWM (Pulse Width Modulation) to simulate analog behavior by rapidly switching a digital pin on and off.
The ratio of the ON time to the total cycle time is known as the Duty Cycle. A higher duty cycle means the pin stays ON longer, effectively outputting a higher “average” voltage. This approach allows you to dim LEDs, control motor speeds, and adjust signal strength with precise digital control.
- LED brightness control (dimming)
- Servo motor angle adjustment
- Audio tone generation
- Fan or DC motor speed control
How PWM Works
PWM works by toggling the output pin between HIGH and LOW states at a fixed frequency. For example, if a pin outputs a PWM signal at 1 kHz (1,000 times per second), each cycle lasts 1 millisecond. Within that cycle, the pin may be HIGH for a portion of the time — determined by the duty cycle.
- A 50% duty cycle means the signal is HIGH for half the time (average voltage ≈ 1.65V on a 3.3V system).
- A 100% duty cycle means the signal is always HIGH (3.3V constant output).
- A 0% duty cycle means the signal is always LOW (0V constant output).
Creating PWM in MicroPython
MicroPython provides a built-in PWM
class to generate PWM signals from any
pin that supports it. The general syntax looks like this:
from machine import Pin, PWM
pwm = PWM(Pin(2)) # Initialize PWM on GPIO2 (for ESP32)
pwm.freq(1000) # Set frequency to 1 kHz
pwm.duty(512) # Set duty cycle (range depends on board)
The duty()
value represents how much of each PWM cycle the signal stays HIGH.
On most boards:
- ESP8266: Duty range is
0–1023
- ESP32: Duty range is
0–1023
(12-bit internally but typically scaled) - Raspberry Pi Pico: Duty range is
0–65535
(16-bit resolution)
Example: LED Brightness Control
PWM is perfect for dimming an LED smoothly. By gradually increasing or decreasing the duty cycle, you can fade an LED in and out, giving a visually pleasing effect.
from machine import Pin, PWM
from time import sleep
led = PWM(Pin(2)) # Use GPIO2 or built-in LED
led.freq(1000)
while True:
for duty in range(0, 1024, 10):
led.duty(duty)
sleep(0.01)
for duty in range(1023, -1, -10):
led.duty(duty)
sleep(0.01)
In this code, the LED gradually brightens and dims in an infinite loop.
The sleep(0.01)
delay controls how fast the fading happens.
PWM Frequency and Applications
The frequency determines how fast the PWM signal cycles. Lower frequencies (e.g., below 100 Hz) may cause visible flickering in LEDs, while higher frequencies (1 kHz or more) result in smoother light output.
Common frequency choices:
- LED Dimming → 500 Hz – 2 kHz
- Motors and Fans → 5 kHz – 20 kHz
- Servo Control → 50 Hz (with pulse width encoding)
- Audio Generation → Above 10 kHz
Stopping PWM Output
To stop PWM on a pin and return it to a normal digital state, use:
pwm.deinit()
This is especially important when you’re done with the pin or want to repurpose it for another task.
deinit()
when reconfiguring pins
or switching between analog and digital use.
PWM provides a versatile and efficient way to simulate analog signals with digital pins. Understanding how to configure frequency and duty cycles will allow you to create smooth dimming effects, motor controls, and even complex sound patterns using MicroPython.