Skip to Content

Audio mit XIAO-ESP32-S3-Sense aufnehmen

Audio mit XIAO-ESP32-S3-Sense aufnehmen

In diesem Tutorial lernst du, wie man Audiosignale mit dem Seeed Studio XIAO-ESP32-S3-Sense Board aufnimmt. Falls du das XIAO-ESP32-S3 Sense noch nicht benutzt hast, schau dir zuerst das Getting started with XIAO-ESP32-S3-Sense Tutorial an.

Benötigte Teile

Natürlich benötigst du ein XIAO ESP32 S3 Sense Board von Seeed Studio, um die Codebeispiele auszuprobieren. Beachte, dass das Board heiß werden kann und du eventuell einen kleine Heatsink (siehe unten aufgeführtes Teil) anbringen möchtest.

Außerdem brauchst du eine SD-Karte, um die aufgenommenen Audiodaten zu speichern. Ich habe eine 32 GB Karte aufgeführt, aber auch eine kleinere (8GB) reicht zum Ausprobieren völlig aus. Und falls dein Computer keinen eingebauten SD-Kartenleser hat, benötigst du auch einen solchen.

Seeed Studio XIAO ESP32 S3 Sense

USB C Kabel

Kleiner Kühlkörper 9×9 mm

SD-Karte 32GB

SD-Kartenleser

Makerguides is a participant in affiliate advertising programs designed to provide a means for sites to earn advertising fees by linking to Amazon, AliExpress, Elecrow, and other sites. As an Affiliate we may earn from qualifying purchases.

Mikrofon des XIAO-ESP32-S3-Sense

Das XIAO-ESP32-S3-Sense verfügt über ein eingebautes digitales MEMS Microphone vom Typ MSM261D3526H1CPM, das sich auf dem Sense Hat befindet. Siehe das Bild unten:

Mikrofon auf dem Sense Hat

Du kannst mit dem Mikrofon über zwei Signalleitungen (PDM_CLK, PDM_DATA) für das I2S protocol kommunizieren, die mit IO42 und IO41 verbunden sind, wie im Schaltplan unten gezeigt:

Schematics for Microphone on Sense Hat
Schaltplan für Mikrofon auf dem Sense Hat

Beachte, dass sich auf der Rückseite der PCB des Sense Hats zwei „Jumper“-Pads mit der Beschriftung J1 und J2 (rote Pfeile) befinden. Siehe das Bild unten:

Jumper-Pads auf dem Sense Hat (source)

Wenn du den dünnen Draht zwischen diesen Pads (entlang der weißen Linien) durchtrennst, deaktivierst du das Mikrofon, aber die GPIOs D11 und D12 auf dem Sense Hat werden verfügbar, ansonsten werden sie vom Mikrofon verwendet. Für mehr Details siehe das Pin Multiplexing Information.

Mikrofonsignal vom XIAO-ESP32-S3-Sense auslesen

Als erstes Codebeispiel zeigen wir das vom Mikrofon erkannte Audiosignal im Serial Monitor und Serial Plotter an:

#include "ESP_I2S.h"

const int8_t I2S_CLK = 42;
const int8_t I2S_DIN = 41;
const uint32_t SAMPLERATE = 16000;

I2SClass I2S;

void setup() {
  Serial.begin(115200);

  I2S.setPinsPdmRx(I2S_CLK, I2S_DIN);
  if (!I2S.begin(I2S_MODE_PDM_RX, SAMPLERATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
    Serial.println("Can't find microphone!");
    while (1)
      ;
  }
}

void loop() {
  int sample = I2S.read();
  if (sample > 1) {
    Serial.println(sample);
  }
}

Konstanten und Objekte

Der Code beginnt mit dem Einbinden der ESP_I2S Bibliothek. Dann definieren wir Konstanten für die Pins, an die die I2S-Schnittstelle des Mikrofons angeschlossen ist, sowie die Abtastrate:

#include "ESP_I2S.h"

const int8_t I2S_CLK = 42;
const int8_t I2S_DIN = 41;
const uint32_t SAMPLERATE = 16000;

Anschließend erstellen wir die I2S-Objekte, die uns erlauben, Daten vom und zum Mikrofon über die I2S protocol zu übertragen:

I2SClass I2S;

Setup-Funktion

In der Setup-Funktion initialisieren wir die serielle Schnittstelle, setzen die Pins für die I2C-Schnittstelle und starten die Kommunikation für I2S:

void setup() {
  Serial.begin(115200);

  I2S.setPinsPdmRx(I2S_CLK, I2S_DIN);
  if (!I2S.begin(I2S_MODE_PDM_RX, SAMPLERATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
    Serial.println("Can't find microphone!");
    while (1)
      ;
  }
}

Der I2S_MODE_PDM_RX Parameter konfiguriert das I2S-Peripheriegerät so, dass es im PDM (Pulse-Density Modulation) Empfangsmodus arbeitet. PDM-Mikrofone geben einen hochfrequenten Bitstrom aus, bei dem die Dichte der ‚1‘-Bits der Signalstärke entspricht. In diesem Modus übernimmt die ESP32-Hardware die Dekodierung dieses dichten Bitstroms in nutzbare PCM-Audiosamples.

Der SAMPLERATE Parameter definiert die Anzahl der pro Sekunde erfassten Audiosamples, gemessen in Hertz (Hz). Im Code ist dieser Wert auf 16000 gesetzt, was bedeutet, dass das Mikrofon 16.000 Mal pro Sekunde abgetastet wird. Diese Abtastrate ist ein guter Kompromiss zwischen ausreichender Detailgenauigkeit für menschliche Sprache und geringem Verarbeitungs- und Speicheraufwand. Niedrigere Abtastraten reduzieren Speicher- und CPU-Belastung, was für batteriebetriebene Systeme vorteilhaft ist, aber die Auflösung verringert.

Der I2S_DATA_BIT_WIDTH_16BIT Parameter legt die Bit-Tiefe jedes PCM-Audiosamples auf 16 Bit fest. Die Bit-Tiefe gibt an, wie genau jedes Audiosample die Amplitude der ursprünglichen Schallwelle repräsentiert. Eine 16-Bit-Tiefe bietet 65.536 mögliche Amplitudenstufen pro Sample, was einen guten Dynamikumfang für die Erfassung feiner Lautstärke- und Tonvariationen ermöglicht.

Der I2S_SLOT_MODE_MONO Parameter konfiguriert die I2S-Schnittstelle so, dass sie im Mono-Kanal-Modus arbeitet, also nur ein einzelner Audiokanal für die Datenerfassung verwendet wird. Das ist passend, wenn man mit einem einzelnen PDM-Mikrofon arbeitet, da Stereo (Zweikanalbetrieb) unnötig und verschwenderisch wäre.

Loop-Funktion

In der Loop-Funktion lesen wir ein einzelnes Audiosample über I2S.read() vom Mikrofon. Wenn das Sample größer als 1 ist (um Stille/Rauschuntergrund herauszufiltern), geben wir es im Serial Monitor aus:

void loop() {
  int sample = I2S.read();
  if (sample > 1) {
    Serial.println(sample);
  }
}

Wenn du den Code ausführst, den Serial Plotter öffnest und mit konstanter Frequenz pfeifst, solltest du eine schöne Sinuswelle im Serial Plotter sehen:

Audio Signal in Serial Monitor measured with Microphone
Audiosignal im Serial Monitor gemessen mit Mikrofon

Wenn du die Frequenz variierst, indem du etwas tiefer oder höher pfeifst, wirst du sehen, dass sich die Frequenz der angezeigten Sinuswelle entsprechend ändert.

Audio mit XIAO-ESP32-S3-Sense aufnehmen

In diesem Beispiel zeige ich, wie man 5 Sekunden Audio aufnimmt und die Daten als Audiodatei im WAV-Format auf der SD-Karte speichert.

Wir wollen die Aufnahme durch Drücken eines Knopfes starten und dann für die nächsten 5 Sekunden aufnehmen. Die folgende Verdrahtung zeigt, wie man einen Knopf an Pin D7 des Boards anschließt:

Button on pin D7 of the XIAO-ESP32-S3-Sense
Knopf an Pin D7 des XIAO-ESP32-S3-Sense

Unten ist der komplette Code für das Projekt. Schau ihn dir zuerst kurz an, dann gehen wir auf die Details ein:

#include "ESP_I2S.h"
#include "FS.h"
#include "SD.h"

const uint32_t SAMPLERATE = 16000;
const int LEN = 5;  // seconds
const byte btnPin = D7;
const byte ledPin = BUILTIN_LED;

I2SClass i2s;

void recordAudio() {
  static int cnt = 0;
  static char filename[64];
  uint8_t *wav_buffer;
  size_t wav_size;

  Serial.print("RECORDING ... ");
  wav_buffer = i2s.recordWAV(LEN, &wav_size);

  sprintf(filename, "/audio%d.wav", cnt++);
  File file = SD.open(filename, FILE_WRITE);
  file.write(wav_buffer, wav_size);
  file.close();
  free(wav_buffer);
  Serial.printf("COMPLETE => %s\n", filename);
}

void setup() {
  Serial.begin(115200);

  pinMode(btnPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

  i2s.setPinsPdmRx(42, 41);
  if (!i2s.begin(I2S_MODE_PDM_RX, SAMPLERATE,
                 I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
    Serial.println("Can't find microphone!");
  }

  if (!SD.begin(21)) {
    Serial.println("Failed to mount SD Card!");
  }
}

void loop() {
  if (!digitalRead(btnPin)) {
    delay(500);
    digitalWrite(ledPin, LOW);
    recordAudio();
    digitalWrite(ledPin, HIGH);
  }
}

Bibliotheken

Der Code beginnt mit dem Einbinden zweier wichtiger Bibliotheken:

#include "ESP_I2S.h"
#include "FS.h"
#include "SD.h"

Die ESP_I2S.h Bibliothek bietet eine Abstraktion für die Handhabung von Audioeingang über die I2S-Schnittstelle, die das PDM-Mikrofon nutzt. Die FS.h und SD.h Bibliotheken ermöglichen den Zugriff auf das Dateisystem der SD-Karte, um Audiodateien zu speichern.

Konstanten

Als nächstes werden mehrere Konstanten definiert:

const uint32_t SAMPLERATE = 16000;
const int LEN = 5;  // seconds
const byte btnPin = D7;
const byte ledPin = BUILTIN_LED;

Die SAMPLERATE ist auf 16.000 Samples pro Sekunde gesetzt, eine Standardrate für verständliche Sprachaufnahmen. LEN gibt die Aufnahmedauer in Sekunden an. btnPin ist dem Knopf-Eingangspin (D7) zugewiesen, und ledPin ist die Onboard-LED, die während der Aufnahme als visuelles Feedback dient.

Objekte

Als nächstes erstellen wir das I2SClass Objekt, das zur Konfiguration und Steuerung der I2S-Audioschnittstelle verwendet wird.

I2SClass i2s;

recordAudio-Funktion

In der recordAudio() Funktion wird die Audioaufnahme und das Speichern der Datei behandelt:

void recordAudio() {
  static int cnt = 0;
  static char filename[64];
  uint8_t *wav_buffer;
  size_t wav_size;

Ein statischer Zähler cnt wird verwendet, um für jede Aufnahme eindeutige Dateinamen zu erzeugen. Ein Buffer-Pointer wav_buffer und eine Variable wav_size werden deklariert, um die aufgenommenen Audiodaten und deren Größe zu halten.

  Serial.print("RECORDING ... ");
  wav_buffer = i2s.recordWAV(LEN, &wav_size);

Die Funktion beginnt mit einer Ausgabe im Serial Monitor. Die recordWAV() Funktion wird dann auf dem i2s Objekt aufgerufen, die Audio für die Dauer von LEN Sekunden aufnimmt und einen Pointer auf die WAV-Daten sowie deren Größe zurückgibt.

  sprintf(filename, "/audio%d.wav", cnt++);
  File file = SD.open(filename, FILE_WRITE);
  file.write(wav_buffer, wav_size);
  file.close();
  free(wav_buffer);

Ein Dateiname wird mit dem Zähler generiert, z.B. /audio0.wav, /audio1.wav usw. Die SD-Karte wird mit SD.open angesprochen, und die WAV-Daten werden in die Datei geschrieben. Der von wav_buffer verwendete Speicher wird anschließend freigegeben, um Speicherlecks zu vermeiden.

  Serial.printf("COMPLETE => %s\n", filename);
}

Nach dem Speichern wird eine Abschlussmeldung mit dem Dateinamen der gespeicherten Aufnahme im Serial Monitor ausgegeben.

Setup-Funktion

In der setup() Funktion initialisieren wir zuerst die serielle Kommunikation mit 115200 Baud, um Debugging-Meldungen zu ermöglichen.

void setup() {
  Serial.begin(115200);

Dann konfigurieren wir den Knopf als Eingang mit internem Pull-up-Widerstand, das heißt, er liest standardmäßig HIGH und LOW, wenn gedrückt. Der LED-Pin wird als Ausgang gesetzt.

  pinMode(btnPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

Anschließend setzen wir GPIO 42 und 41 für den PDM-Mikrofoneingang, passend zur Standardverdrahtung des Onboard-Mikrofons des XIAO ESP32-S3 Sense.

  i2s.setPinsPdmRx(42, 41);

Die I2S-Schnittstelle wird im PDM-Empfangsmodus mit der angegebenen Abtastrate, 16-Bit-Breite und Mono-Kanal initialisiert. Falls die Initialisierung fehlschlägt, wird eine Fehlermeldung angezeigt.

  if (!i2s.begin(I2S_MODE_PDM_RX, SAMPLERATE,
                 I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_MONO)) {
    Serial.println("Can't find microphone!");
  }

Zum Schluss initialisieren wir die SD-Karte mit GPIO 21 als CS (Chip Select) Pin. Falls das Mounten fehlschlägt, geben wir eine Fehlermeldung aus.

  if (!SD.begin(21)) {
    Serial.println("Failed to mount SD Card!");
  }
}

Loop-Funktion

Schließlich prüft die loop() Funktion auf Knopfdruck und startet die Aufnahme:

void loop() {
  if (!digitalRead(btnPin)) {
    delay(500);
    digitalWrite(ledPin, LOW);
    recordAudio();
    digitalWrite(ledPin, HIGH);
  }
}

Der Knopf wird ausgelesen; ist er LOW (gedrückt), wartet das Programm 500 Millisekunden zum Entprellen, schaltet dann die LED an, ruft recordAudio() auf und schaltet die LED nach der Aufnahme wieder aus. So gibt es eine visuelle Rückmeldung, dass die Aufnahme läuft.

Fazit

In diesem Tutorial hast du gelernt, wie man mit dem XIAO-ESP32-S3 Sense Audio aufnimmt. Jetzt bist du bereit, zum Beispiel sprachgesteuerte Anwendungen zu bauen. Siehe unser Voice control with XIAO-ESP32-S3-Sense and Edge Impulse Tutorial.

Wenn du mehr Informationen zum XIAO-ESP32-S3 Sense brauchst, schau dir das Getting started with XIAO-ESP32-S3-Sense Tutorial an. Und für Video-Streaming siehe unser Stream Video with with XIAO-ESP32-S3-Sense Tutorial.

Vergiss nicht, auch das Getting Started Wiki by Seeed Studio für das XIAO-ESP32-S3-Sense zu checken, das viele Informationen zum Board und zahlreiche Codebeispiele bietet.

Wenn du Fragen hast, hinterlasse sie gerne im Kommentarbereich.

Viel Spaß beim Tüfteln ; )