PDM microphone/Using it/08. Hear it live
Using it · 08 of 9

Hear it live

The TK101 and the TK103 on one ESP32-S3: the microphone in on I2S0, the DAC out on I2S1, and what you say in your headphones a moment later. The sketch takes out a slow average, multiplies by 4, and sends every sample to both ears in a 32-bit slot, because at 16 kHz the DAC needs 64 bit-clock pulses per sample.

What you need

  • An ESP32-S3 dev board and its USB cable.
  • The TK101 and the TK103 DAC from the kit.
  • A pair of wired headphones or earbuds with a 3.5 mm plug.
  • Ten jumper wires.

Headphones, not a speaker. A speaker a few centimetres from the microphone feeds back into a howl, and the TK103 is a line output that cannot drive a bare speaker anyway.

Wire it

BlockPinESP32-S3
TK101GND, 3V3GND, 3V3
TK101CLKGPIO 5
TK101DATAGPIO 6
TK103GND, 3V3GND, 3V3
TK103BCKGPIO 42
TK103DINGPIO 41
TK103LRCKGPIO 40
TK103XSMTGPIO 39

Both blocks share the ESP32-S3's 3V3 and GND. The microphone goes on I2S0, the only port that takes PDM in; the ESP_I2S library puts the DAC on I2S1 by itself.

One sample's trip

One sample, microphone to headphones
GAIN 4
The sound
GAIN
Headphones get
0.4 % of full scale
DAC bit clock
1.0 MHz
Bits per sample
64, as the DAC needs 64
Quiet speech, brought up. × 4 makes it 132, still far from the top, so a louder voice has room. Start at 4 in headphones and raise it if it is faint.

Three things happen to every sample between the two blocks:

  • A slow average comes out. offset moves a thousandth of the way towards each new sample, so it settles on the microphone's offset over a few thousand samples and averages the sound itself away.
  • It is multiplied by GAIN, 4 as the sketch ships, and held inside 16 bits by constrain().
  • It moves into a 32-bit slot, times 65536, and goes to both ears. At 16 kHz the TK103's chip locks only to a bit clock of 64 pulses per sample; 16-bit stereo would give it 32. Two 32-bit slots give it 64.

The DAC is held muted on XSMT while its clocks start, then un-muted, so it starts without a click.

What it prints

The Serial Monitor shows Listening, then a line like peak and a number twice a second: the loudest sample after gain. Talk, and you should hear yourself in the headphones a moment later, with the peak rising as you speak.

The code

tk101_monitor.ino

Reads 256 samples at a time from the microphone, subtracts a slowly moving average, multiplies by GAIN, and writes each one twice, left and right, into 32-bit slots for the TK103. Prints the loudest sample twice a second.

// TK101 + TK103: hear the microphone live in headphones.
//
// Wiring, TK101 microphone to ESP32-S3:
//   GND -> GND, 3V3 -> 3V3, CLK -> GPIO 5, DATA -> GPIO 6
// Wiring, TK103 DAC to ESP32-S3:
//   GND -> GND, 3V3 -> 3V3
//   BCK -> GPIO 42, DIN -> GPIO 41, LRCK -> GPIO 40, XSMT -> GPIO 39
//   Headphones in the jack. Headphones, not a speaker: a speaker near
//   the microphone feeds back into a howl.
//
// Arduino IDE: Tools > Board > "ESP32S3 Dev Module" (esp32 core 3.x),
// USB CDC On Boot: Enabled. Serial Monitor at 115200.

#include <ESP_I2S.h>

const int PIN_CLK = 5, PIN_DATA = 6;
const int PIN_BCK = 42, PIN_DIN = 41, PIN_LRCK = 40, PIN_XSMT = 39;
const int RATE = 16000;
const int GAIN = 4;                     // start low in headphones

I2SClass mic;                           // PDM in, on I2S0
I2SClass dac;                           // standard out, on I2S1

int16_t in[256];                        // 16 ms of sound
int32_t out[512];                       // the same, as left-right pairs
float offset = 0;

void setup() {
  Serial.begin(115200);
  pinMode(PIN_XSMT, OUTPUT);
  digitalWrite(PIN_XSMT, LOW);          // muted while the clocks start

  mic.setPinsPdmRx(PIN_CLK, PIN_DATA);
  dac.setPins(PIN_BCK, PIN_LRCK, PIN_DIN);

  // The DAC gets 32-bit slots: at 16 kHz the PCM5102 locks to a bit
  // clock of 64 per sample, and 16-bit stereo would give it only 32.
  bool ok =
    mic.begin(I2S_MODE_PDM_RX, RATE, I2S_DATA_BIT_WIDTH_16BIT,
              I2S_SLOT_MODE_MONO) &&
    dac.begin(I2S_MODE_STD, RATE, I2S_DATA_BIT_WIDTH_32BIT,
              I2S_SLOT_MODE_STEREO);
  if (!ok) {
    Serial.println("I2S did not start");
    while (true) delay(1000);
  }
  delay(50);
  digitalWrite(PIN_XSMT, HIGH);         // un-mute: the DAC ramps up
  Serial.println("Listening");
}

void loop() {
  size_t n = mic.readBytes((char *)in, sizeof(in)) / 2;
  int peak = 0;
  for (size_t i = 0; i < n; i++) {
    offset += (in[i] - offset) * 0.001f;          // slow average
    long s = (long)(in[i] - offset) * GAIN;
    s = constrain(s, -32768, 32767);
    peak = max(peak, (int)abs(s));
    out[2 * i] = out[2 * i + 1] = s * 65536;      // top 16 of 32 bits
  }
  dac.write((uint8_t *)out, n * 2 * sizeof(int32_t));

  static uint32_t last = 0;
  if (millis() - last > 500) {
    last = millis();
    Serial.printf("peak %d\n", peak);
  }
}

Compiled with esp32 core 3.3.10 for the ESP32S3 Dev Module, not run. The owner ran this arrangement on his bench, microphone on 5 and 6, DAC on 42, 41, 40 and 39, at 16 kHz, with his own code on the older I2S driver. Headphones only: a speaker near the microphone howls.

When it does not work

It howls or whistles as soon as it starts.

The sound is getting back into the microphone. Put the headphones on rather than leaving them on the desk, keep any speaker away, and lower GAIN to 2 or 1. Never plug a speaker into this build.

Silence in the headphones, but the peak numbers move.

The microphone works; the DAC side does not. Check BCK to GPIO 42, DIN to 41, LRCK to 40 and XSMT to 39, and that the headphones are pushed all the way into the TK103's jack. XSMT low mutes the DAC, so a wire from it to GND silences everything.

The peak stays at 0 and the headphones are silent.

Start with the microphone alone: run the level sketch from How loud is the room. If that reads 0 too, check CLK on GPIO 5 and DATA on GPIO 6, not swapped, and 3V3 on the TK101.

It sounds harsh and crackly when I talk close.

GAIN is too high for that level: loud peaks reach 32767 and constrain() flattens them. Lower GAIN, or talk from further away.

The peak lines slow down or stop.

readBytes() waits for samples, and with none coming it returns after a timeout, about a second by default, rather than waiting for ever. Lines arriving about once a second mean the PDM input is not delivering: check the microphone's wiring and that the sketch printed Listening.

Where this goes next

Symptom first: the LED, the start-up message, and a peak stuck at 0.

When it reads nothing

Edit this page — content/books/pdm-microphone/hear-it-live.mdx

Community

Questions about this product

See what other owners have asked, and read their solutions.

Ask a question ↗

PDM Microphone

Loading discussions…

Discuss this article

Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.

Browse Modules and blocks on the forum