🛠 Jouer une mélodie avec un buzzer passif et le PWM
Objectives
Build a small synthesizer: a passive buzzer is nothing more than a membrane that we vibrate with a square signal. The RP2040 provides the ideal clock: 16 PWM channels (8 slices × 2 channels) driven by the 125 MHz clock (RP2040 datasheet). You will calculate the period and duty cycle to play accurate notes, then a complete melody.
Hardware
- 1 × generic RP2040 board
- 1 × passive buzzer (5–12 V; "active" buzzers sound on simple voltage and cannot play melodies)
- 1 × cable: buzzer pin to GP8, the other to GND (some buzzers have polarity: + wire to the GPIO)
- MicroPython rp2 firmware
Complete code
# melodie.py — GP8, PWM 50 % de rapport cyclique, gamme ~31 Hz..62 kHz
from machine import Pin, PWM
import time
buz = PWM(Pin(8))
buz.duty_u16(32768) # 50 % = son le plus fort (16 bits)
NOTE = { # fréquences tempérées (LA4 = 440 Hz)
'C4': 262, 'D4': 294, 'E4': 330, 'F4': 349, 'G4': 392,
'A4': 440, 'B4': 494, 'C5': 523, ' ': 0,
}
def note(nom, duree_ms):
f = NOTE.get(nom, 0)
if f:
buz.freq(f)
buz.duty_u16(32768)
else: # silence
buz.duty_u16(0)
time.sleep_ms(duree_ms)
buz.duty_u16(0)
time.sleep_ms(20) # petite articulation entre notes
# « Au clair de la lune » (début)
partition = [('C4',400),('C4',400),('C4',400),('D4',400),
('E4',800),('D4',800),('C4',1200),(' ',400)]
while True:
for n, d in partition:
note(n, d)
time.sleep_ms(800)
Expected verification
The melody sounds in tune (compare A4 to the tuning fork on your phone). By changing buz.freq(440) live in the REPL, the sound pitch goes up/down continuously: you control the pitch. A lower duty_u16 (e.g., 8000) lowers the volume without changing the note — PWM also serves as amplitude.
Common errors
- Active buzzer: it contains its own oscillator, only a voltage is needed and PWM does not change the pitch — buy a passive one.
- Forgetting
duty_u16(32768): by default, the duty cycle may be 0, so no sound comes out even with the correct frequency. - Frequency outside the useful range: a passive buzzer sounds correctly between ~100 Hz and 10 kHz; below that it crackles, above that nothing is heard.
- Only one PWM slice per frequency: if another peripheral (servo, LED) uses the same slice, changing the frequency detunes it — the 8 slices are there for this, one per use.
- Buzzer whistling continuously at boot: the pin retains the state from the previous program; add
buz.duty_u16(0)at startup.
Other tutorials for this board
- Piloter la LED RGB embarquée WS2812 d'une carte RP2040 générique
- Avoir une console série sur une carte sans pont USB (UART0)
- Afficher sur un OLED I2C SSD1306 et déboguer le bus
- Logger sur carte microSD en SPI (mode carte)
- Interruptions GPIO et alarmes matérielles avec le Pico SDK
- Transformer la carte en clavier USB HID avec TinyUSB
Code examples for this board
clignotante.pyPythonbouton-interruption.pyPythonpwm-servo.pyPythonlecture-adc.pyPythonneopixel.pyPythonoled-i2c.pyPython