1. Start by importing Pin and PWM from the machine library and sleep from the utime library.
from machine import Pin, PWM
from utime import sleep
  1. Initialize PWM (aka pulse width modulation) on Pin 15 and assign it to the variable buzzer.
buzzer = PWM(Pin(22))
  1. Assign a freq property to buzzer. You must choose a number that ranges between 10 and 12,000. The higher the number, the higher pitched the sound. Let’s try 500.
buzzer.freq(500)
  1. Set the duty_u16 property of the buzzer object to 1000. This makes the buzzer as loud as it can be. A lower value is quieter and 0 is no sound at all. Considering how quiet these buzzers are, maximum volume isn’t very loud at all.
buzzer.duty_u16(1000)
  1. Set a 1 second delay and then set duty to 0 so that the sound stops. If you don’t do this, the buzz will continue, even after the program is finished executing.
sleep(1)
buzzer.duty_u16(0)

Your final code for this simple test should look like this.

from machine import Pin, PWM
from utime import sleep
 
buzzer = PWM(Pin(15))
buzzer.freq(500)
buzzer.duty_u16(1000)
sleep(1)
buzzer.duty_u16(0)

Because you can use different frequencies, you can create a full array of musical notes. There are a few lists of musical note frequencies online and many of them trace back to Brett Hagman’s Arduino tone library on Github. We’ll use these values to create a list we can use to play any song early in our code.

  1. Import the necessary libraries and initialize PWM on pin 15.
from machine import Pin, PWM
from utime import sleep
 
buzzer = PWM(Pin(22))
  1. Create a dictionary called notes with the following values.
tones = {
"B0": 31,
"C1": 33,
"CS1": 35,
"D1": 37,
"DS1": 39,
"E1": 41,
"F1": 44,
"FS1": 46,
"G1": 49,
"GS1": 52,
"A1": 55,
"AS1": 58,
"B1": 62,
"C2": 65,
"CS2": 69,
"D2": 73,
"DS2": 78,
"E2": 82,
"F2": 87,
"FS2": 93,
"G2": 98,
"GS2": 104,
"A2": 110,
"AS2": 117,
"B2": 123,
"C3": 131,
"CS3": 139,
"D3": 147,
"DS3": 156,
"E3": 165,
"F3": 175,
"FS3": 185,
"G3": 196,
"GS3": 208,
"A3": 220,
"AS3": 233,
"B3": 247,
"C4": 262,
"CS4": 277,
"D4": 294,
"DS4": 311,
"E4": 330,
"F4": 349,
"FS4": 370,
"G4": 392,
"GS4": 415,
"A4": 440,
"AS4": 466,
"B4": 494,
"C5": 523,
"CS5": 554,
"D5": 587,
"DS5": 622,
"E5": 659,
"F5": 698,
"FS5": 740,
"G5": 784,
"GS5": 831,
"A5": 880,
"AS5": 932,
"B5": 988,
"C6": 1047,
"CS6": 1109,
"D6": 1175,
"DS6": 1245,
"E6": 1319,
"F6": 1397,
"FS6": 1480,
"G6": 1568,
"GS6": 1661,
"A6": 1760,
"AS6": 1865,
"B6": 1976,
"C7": 2093,
"CS7": 2217,
"D7": 2349,
"DS7": 2489,
"E7": 2637,
"F7": 2794,
"FS7": 2960,
"G7": 3136,
"GS7": 3322,
"A7": 3520,
"AS7": 3729,
"B7": 3951,
"C8": 4186,
"CS8": 4435,
"D8": 4699,
"DS8": 4978
}
  1. Create a list (aka array) of notes for your song. Use the letter P to represent pauses in the music. Each note should be in quotation marks.
song = ["E5","G5","A5","P","E5","G5","B5","A5","P","E5","G5","A5","P","G5","E5"]

Hat tip to the folks on this thread for sharing the notes for this famous riff.

  1. Create a function called playtone that will take any frequency and play it at full volume.
def playtone(frequency):
    buzzer.duty_u16(1000)
    buzzer.freq(frequency)
  1. Create a function called bequiet that will silence the buzzer by change duty_u16 to 0.
def bequiet():
    buzzer.duty_u16(0)
  1. Create a function called playsong that you will use to iterate through the array of notes and play each or pause when it sees P.
def playsong(mysong):
    for i in range(len(mysong)):
        if (mysong[i] == "P"):
            bequiet()
        else:
            playtone(tones[mysong[i]])
        sleep(0.3)
    bequiet()

Here’s what’s going on here. First, we create a for loop that iterates through all the values in the mysong array. If the value is equal to P, it triggers be quiet and, if not, it triggers playtone. Note that playtone requires a numeric frequency value so we have to get the number for each note from the tones list. If we just playtone(mysong[i]), it will fail because it will try to play the string “E5,” instead of frequency 659, which is the integer it needs.

For each tone or pause, the system maintains state for 0.3 seconds of sleep. If you want a faster tempo, you can lower that time. If you want a slower tempo, increase it.

  1. Trigger the playsong function with the song parameter.
playsong(song)

When you run this code, you will hear your buzzer play a familiar melody. Here’s what the complete code should look like:

from machine import Pin, PWM
from utime import sleep
buzzer = PWM(Pin(15))
 
tones = {
"B0": 31,
"C1": 33,
"CS1": 35,
"D1": 37,
"DS1": 39,
"E1": 41,
"F1": 44,
"FS1": 46,
"G1": 49,
"GS1": 52,
"A1": 55,
"AS1": 58,
"B1": 62,
"C2": 65,
"CS2": 69,
"D2": 73,
"DS2": 78,
"E2": 82,
"F2": 87,
"FS2": 93,
"G2": 98,
"GS2": 104,
"A2": 110,
"AS2": 117,
"B2": 123,
"C3": 131,
"CS3": 139,
"D3": 147,
"DS3": 156,
"E3": 165,
"F3": 175,
"FS3": 185,
"G3": 196,
"GS3": 208,
"A3": 220,
"AS3": 233,
"B3": 247,
"C4": 262,
"CS4": 277,
"D4": 294,
"DS4": 311,
"E4": 330,
"F4": 349,
"FS4": 370,
"G4": 392,
"GS4": 415,
"A4": 440,
"AS4": 466,
"B4": 494,
"C5": 523,
"CS5": 554,
"D5": 587,
"DS5": 622,
"E5": 659,
"F5": 698,
"FS5": 740,
"G5": 784,
"GS5": 831,
"A5": 880,
"AS5": 932,
"B5": 988,
"C6": 1047,
"CS6": 1109,
"D6": 1175,
"DS6": 1245,
"E6": 1319,
"F6": 1397,
"FS6": 1480,
"G6": 1568,
"GS6": 1661,
"A6": 1760,
"AS6": 1865,
"B6": 1976,
"C7": 2093,
"CS7": 2217,
"D7": 2349,
"DS7": 2489,
"E7": 2637,
"F7": 2794,
"FS7": 2960,
"G7": 3136,
"GS7": 3322,
"A7": 3520,
"AS7": 3729,
"B7": 3951,
"C8": 4186,
"CS8": 4435,
"D8": 4699,
"DS8": 4978
}
 
song = ["E5","G5","A5","P","E5","G5","B5","A5","P","E5","G5","A5","P","G5","E5"]
 
def playtone(frequency):
    buzzer.duty_u16(1000)
    buzzer.freq(frequency)
 
def bequiet():
    buzzer.duty_u16(0)
 
def playsong(mysong):
    for i in range(len(mysong)):
        if (mysong[i] == "P"):
            bequiet()
        else:
            playtone(tones[mysong[i]])
        sleep(0.3)
    bequiet()
playsong(song)