Learn electronics, coding, and projects — step by step.

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.

✅ Example Use Cases:
  • 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).

💡 Tip: Because the PWM signal switches very quickly, many analog devices like LEDs or motors naturally smooth the signal, appearing to receive a “true” analog voltage.

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)
⚙️ Note: The duty range varies by board, so always check your device’s MicroPython documentation. Using the wrong range won’t damage the board but may produce unexpected brightness or speed levels.

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.

✅ Pro Tip: You can control multiple PWM channels simultaneously — for example, to fade RGB LEDs by assigning a separate PWM pin to each color channel.

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
⚠️ Warning: When controlling inductive loads like motors or solenoids with PWM, always use a transistor or MOSFET driver and a flyback diode to prevent voltage spikes that can damage your board.

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.

💡 Tip: Always clean up your PWM objects with 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.

×



Related Articles: (by Series)