Record and play back
Two blocks from the kit on one ESP32-S3: press BOOT, speak for three seconds into the TK101 microphone, and hear it back from the TK100. The sketch records first and plays after, because a speaker next to a live microphone howls, and it holds CTRL LOW between plays so the amplifier stays silent.
What you need
- An ESP32-S3 dev board with a BOOT button, on a USB cable.
- The TK100 with the kit's speaker in its socket, and the TK101 microphone.
- Ten jumper wires. Both blocks share the ESP32-S3's 3V3 and GND.
Wire it
| Block | Pin | ESP32-S3 |
|---|---|---|
| TK100 | GND, 3V3 | GND, 3V3 |
| TK100 | BCLK | GPIO 15 |
| TK100 | LRCLK | GPIO 16 |
| TK100 | DIN | GPIO 17 |
| TK100 | CTRL | GPIO 18 |
| TK101 | GND, 3V3 | GND, 3V3 |
| TK101 | CLK | GPIO 5 |
| TK101 | DATA | GPIO 6 |
This is the book's pin map, and the same one the TK101 and TK103 handbooks use, so any two of the three blocks can be wired at once. Keep the speaker pointing away from the microphone.
One step at a time
The ESP32-S3 has two I2S ports, and only I2S0 can receive PDM. The microphone gets that one and the amplifier the other; the ESP_I2S library picks them by itself. Both run at 16 kHz.
record() reads three seconds, 48000 samples, into a buffer of 96 kB taken
with malloc() at startup. Then it cleans the clip up: it subtracts the
average, because a microphone's samples sit on a small offset that would
otherwise play as a constant push on the cone, and it multiplies by GAIN,
because speech at arm's length reaches the microphone as small numbers.
constrain() keeps every result inside the 16-bit range.
play() is where CTRL does its work: HIGH before the first sample, LOW 100 ms
after the last, so between plays the amplifier is shut down and silent.
Why not live
A speaker a few centimetres from a microphone picks up its own output, plays it again louder, and in a moment the loop is a howl. Recording first and playing after breaks the loop. To listen live, the TK101 handbook does it with headphones on the TK103.
What it prints
Press BOOT to record
Recording...
Playing...
Press BOOT to recordThe code
Ten wires: six to the TK100, four to the TK101. Press BOOT, speak for three seconds, and the clip plays straight back. Press again for another.
// TK100 + TK101: press BOOT, speak for three seconds, hear it back.
//
// Wiring, TK100 amplifier to ESP32-S3:
// GND -> GND, 3V3 -> 3V3
// BCLK -> GPIO 15, LRCLK -> GPIO 16, DIN -> GPIO 17, CTRL -> GPIO 18
// Speaker in the white socket.
// Wiring, TK101 microphone to ESP32-S3:
// GND -> GND, 3V3 -> 3V3, CLK -> GPIO 5, DATA -> GPIO 6
// The BOOT button is GPIO 0 on the board itself.
//
// 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_BCLK = 15, PIN_LRCLK = 16, PIN_DIN = 17, PIN_AMP_ON = 18;
const int PIN_CLK = 5, PIN_DATA = 6;
const int PIN_BUTTON = 0; // BOOT, LOW while pressed
const int RATE = 16000;
const int SECONDS = 3;
const int GAIN = 8; // speech is quiet; raise if faint
I2SClass mic; // PDM in
I2SClass amp; // standard I2S out
int16_t *clip; // 3 s x 16000 = 48000 samples
const size_t CLIP_LEN = RATE * SECONDS;
void setup() {
Serial.begin(115200);
pinMode(PIN_BUTTON, INPUT_PULLUP);
pinMode(PIN_AMP_ON, OUTPUT);
digitalWrite(PIN_AMP_ON, LOW); // amplifier off until playback
clip = (int16_t *)malloc(CLIP_LEN * sizeof(int16_t)); // 96 kB
mic.setPinsPdmRx(PIN_CLK, PIN_DATA);
amp.setPins(PIN_BCLK, PIN_LRCLK, PIN_DIN);
bool ok = clip &&
mic.begin(I2S_MODE_PDM_RX, RATE, I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_MONO) &&
amp.begin(I2S_MODE_STD, RATE, I2S_DATA_BIT_WIDTH_16BIT,
I2S_SLOT_MODE_STEREO);
if (!ok) {
Serial.println("Start failed: check memory and pins");
while (true) delay(1000);
}
Serial.println("Press BOOT to record");
}
void record() {
Serial.println("Recording...");
mic.readBytes((char *)clip, 1024); // drop the stale start
mic.readBytes((char *)clip, CLIP_LEN * sizeof(int16_t));
long sum = 0; // remove the offset
for (size_t i = 0; i < CLIP_LEN; i++) sum += clip[i];
int offset = sum / (long)CLIP_LEN;
for (size_t i = 0; i < CLIP_LEN; i++) {
long s = (long)(clip[i] - offset) * GAIN;
clip[i] = constrain(s, -32768, 32767);
}
}
void play() {
Serial.println("Playing...");
digitalWrite(PIN_AMP_ON, HIGH);
int16_t frame[2];
for (size_t i = 0; i < CLIP_LEN; i++) {
frame[0] = frame[1] = clip[i]; // same sample, both channels
amp.write((uint8_t *)frame, sizeof(frame));
}
delay(100); // let the last buffer drain
digitalWrite(PIN_AMP_ON, LOW);
Serial.println("Press BOOT to record");
}
void loop() {
if (digitalRead(PIN_BUTTON) == LOW) {
record();
play();
}
}Compiled, not run, for an ESP32S3 Dev Module on esp32 core 3.3.10. Both blocks were tested by their maker with his own code: the microphone on GPIO 5 and 6, as here, and the amplifier on other pins; this book moves the amplifier to 15 to 18 so the two never share a pin.
When it does not work
Check CTRL first: play() drives GPIO 18 HIGH, so CTRL must be wired there. If it is, try tk100_tone.ino on the same wiring; if the scale plays, the amplifier is fine and the microphone side is the problem.
The clip is too quiet: a voice at arm's length reaches the microphone as a small signal. Speak closer to the small metal can on the TK101, or raise GAIN from 8 to 16. Check its CLK is on GPIO 5 and DATA on GPIO 6.
Either the 96 kB for the clip could not be found, or one of the two I2S ports refused its pins. Check nothing else in the sketch uses GPIO 5, 6 or 15 to 18, and that the board setting is ESP32S3 Dev Module.
GAIN is too high for your voice at that distance: samples over the 16-bit range are clipped flat by constrain(). Lower GAIN to 4, or speak from further away.
A speaker a few centimetres from the microphone feeds its own sound back into it, round and round, and builds into a howl. Record then play avoids it. For live listening, use headphones on the TK103.
Symptom first: where to look when the speaker makes no sound.
When it stays silent →Edit this page — content/books/i2s-amplifier/record-and-play-back.mdx
Questions about this product
See what other owners have asked, and read their solutions.
I2S Amplifier
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.