To control the piezo from CircuitPython we'll use its built in PWM, or pulse-width modulation, signal generation capabilities.  Be sure to read the CircuitPython analog I/O guide for more details on PWM signals!

Before continuing make sure your hardware is wired up as shown on the previous page.

Next make sure you are running the latest version of Adafruit CircuitPython for your board, then connect to the board's serial REPL so you are at the CircuitPython >>> prompt.

Setup

First you need to import a few modules to access the PWM output capabilities of CircuitPython:

import board
import pulseio

Now you can create a PWM signal output that will drive the buzzer to make sound:

buzzer = pulseio.PWMOut(board.D5, variable_frequency=True)

There are a couple important things happening with the line above.  This is an initializer which is creating an instance of the PWMOut class and part of that initialization process is specifying these two values:

  • The pin that will be the PWM output.  In this case it's pin D5 on the development board.  If you're using a different output be sure to specify the right pin name here (and make sure the pin supports PWM output as mentioned on the previous page!).
  • The variable_frequency boolean is True.  This is an optional value that we're specifying as a keyword argument to tell the PWM output that we want to be able to change its frequency, or how often the signal changes.  By default PWM outputs in CircuitPython have a fixed frequency, but if this special keyword is specified you can instead change the frequency of the output.

Making Tones with PWM

Now we can generate tones using the PWM output.  Remember from the CircuitPython analog I/O guide PWM page a PWM signal is just a fast on/off signal, i.e. a square wave.  When a square wave modulates the movement of something, like a buzzer, it generates a pressure wave that the human ear interprets as sound.  By changing the frequency, or how often the square wave changes from high to low, you can change the frequency of the tone that you'll hear.

Changing the frequency of the PWM output is easy, just modify the frequency attribute to set a new value in hertz.  For example a 440 hz wave is the same pitch as an A4 note in music:

buzzer.frequency = 440

You might notice after setting the frequency above nothing actually happened--no sound is being made by the buzzer.  Is the buzzer broken?  Nope!  There's one more thing you need to do to create the square wave signal, you need to set the duty cycle of the PWM output.

Like the CircuitPython analog I/O PWM page mentions the duty cycle of a signal is the percent of time that it's high vs. low.  We want to generate a square wave which is high and low for exactly the same amount of time (i.e. 50% duty cycle).  This will generate a square wave of the previously specified frequency on the PWM output, and as a result induce a pressure wave from the buzzer that you'll hear as sound.

To simplify turning on and off the buzzer let's make a couple variables to represent the 0% and 50% duty cycle values that turn the buzzer off and on respectively:

OFF = 0
ON = 2**15

The ON value is 2^15, or 32768, which is about half of the maximum duty cycle value (65535).

Now set the buzzer duty cycle to ON to start playing the tone at the previously set frequency:

buzzer.duty_cycle = ON

You should hear the buzzer start to play a tone at 440 hz!

To stop the tone playback change the duty cycle to OFF or 0%:

buzzer.duty_cycle = OFF

Now the buzzer stops making sound!

You can change the frequency at any time too, whether the buzzer is playing sound or not.  Here are some interesting frequency values to try (they're the frequencies for musical notes):

  • 262 hz, C4
  • 294 hz, D4
  • 330 hz, E4
  • 349 hz, F4
  • 392 hz, G4
  • 440 hz, A4
  • 494 hz B4

Try turning the buzzer on and changing the frequency between a few note values:

buzzer.duty_cycle = ON
buzzer.frequency = 262 # C4
buzzer.frequency = 294 # D4
buzzer.frequency = 330 # E4
buzzer.duty_cycle = OFF

That's all there is to simple tone playback with a piezo buzzer and CircuitPython's built-in PWM output!

Here's a complete example that will play a scale of tones up and down repeatedly.  Save this as main.py on your board and be ready after saving it as the music playback will immediately start:

import time

import board
import pulseio


# Define a list of tones/music notes to play.
TONE_FREQ = [ 262,  # C4
              294,  # D4
              330,  # E4
              349,  # F4
              392,  # G4
              440,  # A4
              494 ] # B4

# Create piezo buzzer PWM output.
buzzer = pulseio.PWMOut(board.D5, variable_frequency=True)

# Start at the first note and start making sound.
buzzer.frequency = TONE_FREQ[0]
buzzer.duty_cycle = 2**15  # 32768 value is 50% duty cycle, a square wave.

# Main loop will go through each tone in order up and down.
while True:
    # Play tones going from start to end of list.
    for i in range(len(TONE_FREQ)):
        buzzer.frequency = TONE_FREQ[i]
        time.sleep(0.5)  # Half second delay.
    # Then play tones going from end to start of list.
    for i in range(len(TONE_FREQ)-1, -1, -1):
        buzzer.frequency = TONE_FREQ[i]
        time.sleep(0.5)

Making Tones With SimpleIO

Another option to generate a tone is with the SimpleIO module for CircuitPython.  This module simplifies tone generation by doing all the PWM duty cycle and frequency logic for you automatically.  With a simple tone function call you can play a tone for a period of time--no setup or duty cycle calculation necessary!  You might find this approach easier to start with for basic tone playback compared to the PWM approach shown above.

To follow this approach you'll need to install the Adafruit CircuitPython SimpleIO library on your CircuitPython board.  

First make sure you are running the latest version of Adafruit CircuitPython for your board.

Next you'll need to install the necessary libraries to use the hardware--carefully follow the steps to find and install these libraries from Adafruit's CircuitPython library bundle.  Our introduction guide has a great page on how to install the library bundle for both express and non-express boards.

Remember for non-express boards like the, you'll need to manually install the necessary libraries from the bundle:

  • simpleio.mpy

You can also download the simpleio.mpy from its releases page on Github.

Before continuing make sure your board's lib folder or root filesystem has the simpleio.mpy file copied over.

Next connect to the board's serial REPL so you are at the CircuitPython >>> prompt.  Then import the board and simpleio modules:

import board
import simpleio

Now you'e ready to use the tone function in SimpleIO to play a tone on a pin connected to a piezo buzzer.  Try the following to play a 440 hz tone for 1 second:

    simpleio.tone(board.D5, 440, duration=1.0)
  

You should hear a 440 hz tone, or an A4 note, played for one second.  The parameters you pass to the tone function control the frequency and duration of the tone:

  • The first parameter is the pin connected to the buzzer or speaker, in this case digital pin 5 as shown in the wiring for this guide.
  • The second parameter is the frequency in Hz of the tone to play back. This can be a floating point but note that at higher frequencies (> 10KHz) the precision may not match perfectly
  • The last parameter is a keyword duration which specifies how long to play the tone in seconds.  By default if you don't provide a value here the tone will be played for one second, however you can specify shorter or longer values.

Another example is playing a 400 hz tone for 3 seconds:

simpleio.tone(board.D5, 400, duration=3.0)

Notice how the frequency and duration parameters changed to specify these different values!

That's all there is to basic tone playback on a piezo with the SimpleIO module!  Here's another complete example that plays a scale of notes using SimpleIO's tone function.  Again save this as a main.py on your board to have it run:

import board

import simpleio


# Define pin connected to piezo buzzer.
PIEZO_PIN = board.D5

# Define a list of tones/music notes to play.
TONE_FREQ = [ 262,  # C4
              294,  # D4
              330,  # E4
              349,  # F4
              392,  # G4
              440,  # A4
              494 ] # B4


# Main loop will go through each tone in order up and down.
while True:
    # Play tones going from start to end of list.
    for i in range(len(TONE_FREQ)):
        simpleio.tone(PIEZO_PIN, TONE_FREQ[i], duration=0.5)
    # Then play tones going from end to start of list.
    for i in range(len(TONE_FREQ)-1, -1, -1):
        simpleio.tone(PIEZO_PIN, TONE_FREQ[i], duration=0.5)

This guide was first published on Jan 05, 2018. It was last updated on Jan 05, 2018.

This page (CircuitPython) was last updated on Dec 14, 2017.

Text editor powered by tinymce.