>_ DevTrendsnl

Taal

Home

Talen

Secties

Frontend Backend Mobiel DevOps AI / ML GameDev Blockchain Embedded Beveiliging
C-plus-plus

Hoe je een microcontroller van $5 omtovert tot een Bluetooth-speaker of draadloze speler

Als je ooit de officiële Espressif-voorbeelden voor Bluetooth-audio hebt geopend, herinner je je waarschijnlijk dat gevoel van lichte horror. Dozens regels low-level magie, een heleboel Bluedroid callbacks, FreeRTOS-wachtrijconfiguratie, en een constant gevoel dat een pointer zoekraakt.

Zwitserse ontwikkelaar Phil Schatzmann liep tegen precies deze frustratie aan en schreef de ESP32-A2DP-bibliotheek. Alle complexe boilerplate wordt onder de motorkap verborgen en biedt een strakke C++-interface voor audiostreaming.

esp32

Wat er onder de motorkap zit en hoe het werkt

De bibliotheek verwerkt twee hoofd taken:

  • A2DP Sink (ontvanger): ESP32 doet alsof het een draadloze speaker of koptelefoon is, ontvangt de stream van je telefoon, en stuurt deze naar een externe DAC, de interne DAC van het bord, of een raw memory buffer.
  • A2DP Source (zender): de microcontroller zelf zendt audio uit (algoritmisch gegenereerd, gelezen van een SD-kaart, of van een microfoon) naar externe Bluetooth-speakers.

Het project is gebaseerd op het standaard A2DP-profiel en ondersteunt het verzenden van besturingssignalen via AVRCP. Dit betekent dat de microcontroller nummers op je telefoon kan wisselen, afspelen kan pauzeren, of metadata kan lezen (nummertitel, artiestnaam, duur).

Voor audio-uitvoer werkt de bibliotheek via de I2S-bus. In ESP-IDF v5 en Arduino ESP32 v3 is de I2S API aanzienlijk herschreven. Om compatibiliteit te behouden bij het updaten van het framework, raadt de auteur aan de bibliotheek direct te koppelen aan je eigen project arduino-audio-tools, hoewel je kunt volstaan met de standaard klasse I2SClass of simpelweg raw PCM-samples kunt pakken.

Hoe je een draadloze ontvanger in vijf regels bouwt

Hier is een minimaal voorbeeld om de audio-ontvanger aan de praat te krijgen:

#include "AudioTools.h"
#include "BluetoothA2DPSink.h"

I2SStream i2s;
BluetoothA2DPSink a2dp_sink(i2s);

void setup() {
    Serial.begin(115200);
    a2dp_sink.start("MyMusic");
}

void loop() {
}

Na het flashen verschijnt het bord in de Bluetooth-apparaatlijst als "MyMusic". Standaard gaat de audiostream (16-bit, 44,1 kHz stereo) naar I2S-pinnen GPIO14 (BCK), GPIO15 (WS) en GPIO22 (Data Out). Als je bedrading anders is, worden de pinnen ingesteld in twee regels via de config i2s.defaultConfig().

Als je geen externe DAC-chip (zoals de populaire PCM5102A of MAX98357A) bij de hand hebt, kun je de ingebouwde 8-bit DAC op GPIO25 en GPIO26 gebruiken:

#include "AudioTools.h"
#include "BluetoothA2DPSink.h"

AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink(out);

void setup() {
    Serial.begin(115200);
    a2dp_sink.start("MyMusic");
}

void loop() {}

Geluid via de ingebouwde DAC zal een beetje ruisachtig zijn voor audiofielen, maar het is perfect prima voor spraakmeldingen of radio in de keuken.

Metadata lezen en nummers besturen

Een nuttig detail: de bibliotheek laat je nummerinfo ophalen via AVRCP. Als je een klein display aansluit, kun je gemakkelijk een infoscherm maken voor een mediacentrum:

void avrc_metadata_callback(uint8_t data1, const uint8_t *data2) {
    Serial.printf("Метаданные: id 0x%x, текст: %s\n", data1, data2);
}

void setup() {
    Serial.begin(115200);
    a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
    a2dp_sink.start("ESP32_Receiver");
}

void loop() {}

Om de speler op je telefoon te besturen met fysieke knoppen, zijn de methoden play(), pause(), next(), previous(), fast_forward() en rewind() beschikbaar.

Zendermodus: wanneer ESP32 het geluid genereert

Als je audio naar Bluetooth-koptelefoons of een soundbar moet sturen, schakel je over naar BluetoothA2DPSource. De stream wordt via een callback aan de bibliotheek gevoed:

#include "BluetoothA2DPSource.h"

BluetoothA2DPSource a2dp_source;

int32_t get_sound_data(uint8_t *data, int32_t byteCount) {
    // Здесь заполняем буфер PCM данными
    return byteCount;
}

void setup() {
    a2dp_source.set_data_callback(get_sound_data);
    a2dp_source.start("Target_Speaker");  
}

void loop() {}

Overigens kun je een lijst met apparaatnamen aan de start()-methode doorgeven (via std::vector<const char*>). Het bord maakt automatisch verbinding met het eerste dat het in de lijst vindt.

Toegang tot de raw audiostream en codecs

Als je I2S niet nodig hebt en audio algoritmisch direct op de microcontroller moet verwerken (bijvoorbeeld een lichtshow maken via FFT of een fragment naar geheugen opnemen), kun je PCM-pakketten onderscheppen via set_stream_reader():

void read_data_stream(const uint8_t *data, uint32_t length) {
    int16_t *samples = (int16_t*) data;
    uint32_t sample_count = length / 2;
    // Обработка семплов
}

void setup() {
    a2dp_sink.set_stream_reader(read_data_stream, false); // false отключает вывод на I2S
    a2dp_sink.start("DSP_Node");
}

De standaard codec is standaard SBC. In recente versies heeft de auteur experimentele ondersteuning toegevoegd voor het aansluiten van externe decoders via add_decoder(), waaronder AAC (hoewel voor volledige functionaliteit aan de IDF-kant ESP-IDF versie 6.1 of hoger vereist zal zijn).

Beperkingen waar je van op de hoogte moet zijn

  • HFP- en HSP-profielen (handsfree bellen, gesprekken met microfoon) worden niet ondersteund. Het project gaat strikt genomen om het verzenden van mediastreams.
  • De bibliotheek is veeleisend voor Bluetooth-stack-bronnen. Het combineren van zwaar Wi-Fi-verkeer en stabiele A2DP-streaming op een basis-ESP32 kan lastig zijn, en audiohaperingen zijn mogelijk.
  • De documentatie in de repository is gedetailleerd, maar de auteur is streng over de opmaak van bugrapporten: voordat je een issue opent op GitHub, moet je de versies van de bibliotheken en ESP32-core controleren.

Wie hier profijt van heeft

Het project komt van pas als je een oude cassetterecorder nieuw leven wilt inblazen, een draadloze ontvanger in een vintage versterker wilt integreren, of een aangepaste synthesizer wilt bouwen die audio rechtstreeks naar draadloze koptelefoons streamt.

De bibliotheek is compatibel met Arduino IDE, PlatformIO en pure Espressif IDF als component. Om te beginnen heb je alleen een ESP32 WROOM-bord en een op PCM5102A gebaseerde DAC-printplaat voor een paar dollar nodig.

Gerelateerde projecten