It's easy to use the Adafruit PDM microphone breakout with CircuitPython, using the built-in audiobusio module and PDMIn class. It allows you to record an input audio signal from the microphone using I2S.

This page will walk you through wiring up your PDM mic, and using it to print out sound levels to the serial console and show the values on the plotter in Mu.

CircuitPython Microcontroller Wiring

The following wiring diagrams show how to connect the PDM mic to a Feather M4 Express. If you're using another board, check out the Where's my PDMIn? section at the end for valid pin combinations for your board. Some boards, like the SAMD21 and SAMD51 have fixed pins that support PDM. Others like the nRF52840 can use any two pins.

The following is the header version of the PDM microphone breakout wired to a Feather M4 Express:

  • Mic 3V to Feather 3V
  • Mic GND to Feather Gnd
  • Mic CLK to Feather TX
  • Mic DAT to Feather D12

The following is the JST version of the PDM microphone breakout wired to a Feather M4 Express:

  • Mic 3V (red wire) to Feather 3V
  • Mic GND (black wire) to Feather Gnd
  • Mic DAT (blue wire) to Feather D12
  • Mic CLK (yellow wire) to Feather TX

If you're using Circuit Playground Express, there is a built in PDM microphone. There is a guide page dedicated to using Circuit Playground Express and the built-in microphone. If you're using a CPX, check that out instead!

CircuitPython Usage

As PDMIn is built into CircuitPython, no separate libraries are necessary for this example!

Save the following as code.py on your microcontroller board:

import time
import array
import math
import board
import audiobusio


# Remove DC bias before computing RMS.
def mean(values):
    return sum(values) / len(values)


def normalized_rms(values):
    minbuf = int(mean(values))
    samples_sum = sum(
        float(sample - minbuf) * (sample - minbuf)
        for sample in values
    )

    return math.sqrt(samples_sum / len(values))


# Main program
mic = audiobusio.PDMIn(board.TX, board.D12, sample_rate=16000, bit_depth=16)
samples = array.array('H', [0] * 160)


while True:
    mic.record(samples, len(samples))
    magnitude = normalized_rms(samples)
    print((magnitude,))
    time.sleep(0.1)

First you import time, array, math, board and audiobusio.

Then you have two helper functions. The first one uses math to return a mean, or average. It is used in the second helper. The second one uses math to return a normalised RMS average. You use these functions to take multiple sound samples really quickly and average them to get a more accurate reading.

Next you set up the microphone object and your samples variable.

Then you use the mic object to start taking sound samples. You use the normalised RMS to find the average of a given set of samples, and you call that the magnitude. Last, you print the magnitude to the serial console.

Note that the Mu plotter looks for tuple values to print. Tuples in Python come in parentheses () with comma separators. If you have two values, a tuple would look like (1.0, 3.14) Since you have only one value, you need to have it print out like (1.0,) note the parentheses around the number, and the comma after the number. Thus the extra parentheses and comma in print((magnitude,)).

Once you have everything setup and running, try speaking towards the microphone, and watch the plotter immediately react! Move further away from the microphone to cause smaller changes in the plotter line. Move closer to the board to see bigger spikes!

Note that the way that the code works with averaging a given number of readings over time means that short sounds like claps can sometimes get missed. If you feel like your mic is not responding, try a longer sound like a hum or speaking words.

It's a really easy way to test your microphone and see how it reads sound changes!

Where's my PDMIn?

Save the following as code.py on your board, and connect to the serial console to see a list of all the valid PDMIn pin combinations. Note: the code will run immediately and only runs once - if you connect to the serial console and don't see anything, press ctrl+D to reload and run the code again.

import board
import audiobusio
from microcontroller import Pin


def is_hardware_PDM(clock, data):
    try:
        p = audiobusio.PDMIn(clock, data)
        p.deinit()
        return True
    except ValueError:
        return False
    except RuntimeError:
        return True


def get_unique_pins():
    exclude = ['NEOPIXEL', 'APA102_MOSI', 'APA102_SCK']
    pins = [pin for pin in [
        getattr(board, p) for p in dir(board) if p not in exclude]
            if isinstance(pin, Pin)]
    unique = []
    for p in pins:
        if p not in unique:
            unique.append(p)
    return unique


for clock_pin in get_unique_pins():
    for data_pin in get_unique_pins():
        if clock_pin is data_pin:
            continue
        else:
            if is_hardware_PDM(clock_pin, data_pin):
                print("Clock pin:", clock_pin, "\t Data pin:", data_pin)
            else:
                pass

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

This page (CircuitPython) was last updated on Apr 15, 2021.

Text editor powered by tinymce.