I2S, or Inter-IC Sound, is a standard for transmitting digital audio data. It requires at least three connections. The first connection is a clock, called bit clock (BCLK, or sometimes written as serial clock or SCK). The second connection, which determines the channel (left or right) being sent, is called word select (WS). When stereo data is sent, WS is toggled so that the left and right channels are sent alternately, one data word at a time. The third connection, which transmits the data, is called serial data (SD).

Typically, there is a transmitter device which generates the bit clock, word select signal, and the data, and sends them to a receiver device. In this case, your microcontroller acts as the transmitter, and an I2S breakout acts as the receiver. The MAX98357A is an example of an I2S class D amplifier that allows you to connect directly to a speaker such as this one.

I2S and CircuitPython

CircuitPython supports sending I2S audio signals using the audiobusio module, making it simple to use the I2S interface with your microcontroller.

In this section, you'll learn how to use CircuitPython to play different types of audio using I2S, including tones and WAV files.

Necessary Hardware

You'll need the following additional hardware to complete the examples on this page.

Angled shot of a Adafruit I2S 3W Class D Amplifier Breakout.
Listen to this good news - we now have an all in one digital audio amp breakout board that works incredibly well with the 
$5.95
In Stock
Enclosed Speaker with wires
Listen up! This single  2.8" x 1.2" speaker is the perfect addition to any audio project where you need 4 ohm impedance and 3W or less of power. We...
$3.95
In Stock
Premium Male/Male Jumper Wires - 20 x 6 (150mm) folded over
These Male/Male Jumper Wires are handy for making wire harnesses or jumpering between headers on PCB's. These premium jumper wires are 6" (150mm) long and come in a...
$1.95
In Stock

Wiring the MAX98357A

Connect the MAX98357A breakout to your microcontroller as follows.

Make sure you have a very solid connection between the breakout ground pin and the ground pin on your board! An insufficient connection here can result in raspy-sounding tones with intermittent volume increases. If you experience this result, try reseating your ground connection.
  • Board 3V to breakout VIN
  • Board GND to breakout GND
  • Board A0 to breakout BCLK
  • Board A1 to breakout LRC
  • Board A2 to breakout DIN
  • Speaker + to screw terminal +
  • Speaker - to screw terminal -

I2S Tone Playback

The first example generates one period of a sine wave and then loops it to generate a tone. You can change the volume and the frequency (in Hz) of the tone by changing the associated variables. Inside the loop, you play the tone for one second and stop it for one second.

Update your code.py to the following.

Click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, open the folder that matches your CircuitPython version, and copy the code.py file to your CIRCUITPY drive.

Your CIRCUITPY drive should now look similar to the following image:

CIRCUITPY
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
CircuitPython I2S Tone playback example.
Plays a tone for one second on, one
second off, in a loop.
"""
import time
import array
import math
import audiocore
import board
import audiobusio

audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)

tone_volume = 0.1  # Increase this to increase the volume of the tone.
frequency = 440  # Set this to the Hz of the tone you want to generate.
length = 8000 // frequency
sine_wave = array.array("h", [0] * length)
for i in range(length):
    sine_wave[i] = int((math.sin(math.pi * 2 * i / length)) * tone_volume * (2 ** 15 - 1))
sine_wave_sample = audiocore.RawSample(sine_wave)

while True:
    audio.play(sine_wave_sample, loop=True)
    time.sleep(1)
    audio.stop()
    time.sleep(1)

Now you'll hear one second of a 440Hz tone, and one second of silence.

You can try changing the 440 Hz of the tone to produce a tone of a different pitch. Try changing the number of seconds in time.sleep() to produce longer or shorter tones.

I2S WAV File Playback

The second example plays a WAV file. You open the file in a readable format. Then, you play the file and, once finished, print Done playing! to the serial console. You can use any supported wave file.

Update your code.py to the following.

Click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, open the folder that matches your CircuitPython version, and copy the StreetChicken.wav file and the code.py file to your CIRCUITPY drive.

Your CIRCUITPY drive should now look similar to the following image:

CIRCUITPY
# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
CircuitPython I2S WAV file playback.
Plays a WAV file once.
"""
import audiocore
import board
import audiobusio

audio = audiobusio.I2SOut(board.A0, board.A1, board.A2)

with open("StreetChicken.wav", "rb") as wave_file:
    wav = audiocore.WaveFile(wave_file)

    print("Playing wav file!")
    audio.play(wav)
    while audio.playing:
        pass

print("Done!")

Now you'll hear the wave file play, and on completion, print Done Playing! to the serial console.

You can play a different WAV file by updating "StreetChicken.wav" to be the name of your CircuitPython-compatible WAV file.

You can do other things while the WAV file plays! There is a pass in this example where you can include other code, such as code to blink an LED.

CircuitPython I2S-Compatible Pin Combinations

I2S audio is supported on specific pins. The good news is, there's a simple way to find out which pins support audio playback.

In the example below, click the Download Project Bundle button below to download the necessary libraries and the code.py file in a zip file. Extract the contents of the zip file, open the directory CircuitPython_Templates/i2s_find_pins/ and then click on the directory that matches the version of CircuitPython you're using and copy the contents of that directory to your CIRCUITPY drive.

Your CIRCUITPY drive should now look similar to the following image:

CIRCUITPY

Then, connect to the serial console to see a list of pins printed out. This file runs only once, so if you do not see anything in the output, press CTRL+D to reload and run the code again.

# SPDX-FileCopyrightText: 2021 Kattni Rembor for Adafruit Industries
# SPDX-License-Identifier: MIT
"""
CircuitPython I2S Pin Combination Identification Script
"""
import board
import audiobusio
from microcontroller import Pin


def is_hardware_i2s(bit_clock, word_select, data):
    try:
        p = audiobusio.I2SOut(bit_clock, word_select, data)
        p.deinit()
        return True
    except ValueError:
        return False


def get_unique_pins():
    exclude = [
        getattr(board, p)
        for p in [
            # This is not an exhaustive list of unexposed pins. Your results
            # may include other pins that you cannot easily connect to.
            "NEOPIXEL",
            "DOTSTAR_CLOCK",
            "DOTSTAR_DATA",
            "APA102_SCK",
            "APA102_MOSI",
            "LED",
            "SWITCH",
            "BUTTON",
        ]
        if p in dir(board)
    ]
    pins = [
        pin
        for pin in [getattr(board, p) for p in dir(board)]
        if isinstance(pin, Pin) and pin not in exclude
    ]
    unique = []
    for p in pins:
        if p not in unique:
            unique.append(p)
    return unique


for bit_clock_pin in get_unique_pins():
    for word_select_pin in get_unique_pins():
        for data_pin in get_unique_pins():
            if bit_clock_pin is word_select_pin or bit_clock_pin is data_pin or word_select_pin \
                    is data_pin:
                continue
            if is_hardware_i2s(bit_clock_pin, word_select_pin, data_pin):
                print("Bit clock pin:", bit_clock_pin, "\t Word select pin:", word_select_pin,
                      "\t Data pin:", data_pin)
            else:
                pass

For details about the I2S API, check out the CircuitPython docs.

This guide was first published on Aug 16, 2022. It was last updated on Dec 08, 2023.

This page (I2S) was last updated on Dec 08, 2023.

Text editor powered by tinymce.