El ESP32 es un microcontrolador que proporciona una API para Bluetooth A2DP que puede usarse para recibir datos de sonido, por ejemplo, desde su teléfono móvil y los pone a disposición mediante un método de devolución de llamada. La salida es un flujo de datos PCM, decodificado del formato SBC. La documentación se puede encontrar aquí.
I2S es un estándar de interfaz de bus serie eléctrico que se utiliza para conectar dispositivos de audio digitales entre sí. Se utiliza para comunicar datos de audio PCM entre circuitos integrados en un dispositivo electrónico.
Entonces podemos simplemente enviar la entrada de Bluetooth a la salida de I2S: se puede encontrar un ejemplo de esto de Espressif en Github.
Desafortunadamente, este ejemplo no me hizo feliz, así que decidí convertirlo en una biblioteca Arduino simple que es muy fácil de usar desde un IDE de software Arduino.
Como lo indica el nombre de esta biblioteca, es compatible con el protocolo Bluetooth A2DP que solo proporciona transmisión de audio.
También admite perfil de control remoto de audio/vídeo (AVRCP) junto con A2DP.
¡No se admiten el perfil de manos libres (HFP), el perfil de auriculares (HSP) y el AVRCP independiente sin A2DP!
Espressif está retirando la API I2S heredada: por lo tanto, con Arduino v3.0.0 (IDF v5), mi antigua integración I2S ya no estará disponible. La sintaxis heredada seguirá funcionando mientras no actualice.
Para admitir una API de salida única que sea independiente de la versión, se recomienda instalar y utilizar la biblioteca AudioTools. Por eso, la documentación y todos los ejemplos se han actualizado para utilizar este nuevo enfoque.
Sin embargo, también puede enviar a cualquier otra clase que herede de Arduino Print: por ejemplo, Arduino ESP32 I2SClass o puede usar la devolución de llamada de datos que se describe a continuación.
Esto se puede utilizar, por ejemplo, para construir su propio altavoz Bluetooth.
Aquí está el ejemplo más simple que solo usa la configuración predeterminada adecuada:
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
I2SStream i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
Esto crea un nuevo dispositivo Bluetooth con el nombre "MyMusic" y la salida se enviará a los siguientes pines I2S predeterminados que deben conectarse a un DAC externo:
¡Tenga en cuenta que estos pines predeterminados han cambiado en comparación con la API heredada!
Puedes definir tus propios pines fácilmente antes de start
.
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
I2SStream i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
auto cfg = i2s. defaultConfig ();
cfg. pin_bck = 14 ;
cfg. pin_ws = 15 ;
cfg. pin_data = 22 ;
i2s. begin (cfg);
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
También puede utilizar la API Arduino ESP32 I2S: no necesita instalar ninguna biblioteca adicional para esto.
# include " ESP_I2S.h "
# include " BluetoothA2DPSink.h "
const uint8_t I2S_SCK = 5 ; /* Audio data bit clock */
const uint8_t I2S_WS = 25 ; /* Audio data left and right clock */
const uint8_t I2S_SDOUT = 26 ; /* ESP32 audio data output (to speakers) */
I2SClass i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
i2s. setPins (I2S_SCK, I2S_WS, I2S_SDOUT);
if (!i2s. begin (I2S_MODE_STD, 44100 , I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO, I2S_STD_SLOT_BOTH)) {
Serial. println ( " Failed to initialize I2S! " );
while ( 1 ); // do nothing
}
a2dp_sink. start ( " MyMusic " );
}
void loop () {}
Tenga en cuenta que esta API también depende de la versión instalada: ¡El ejemplo anterior es para ESP32 >= 3.0.0!
También puede enviar la salida directamente al DAC interno del ESP32 utilizando AnalogAudioStream desde AudioTools:
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink (out);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
La salida ahora va a los pines del DAC GPIO25 (Canal 1) y GPIO26 (Canal 2).
Puede recibir una notificación cuando se reciba un paquete. La API utiliza datos PCM normalmente formateados con una frecuencia de muestreo de 44,1 kHz y datos de muestra de 16 bits y dos canales.
// In the setup function:
a2dp_sink.set_on_data_received(data_received_callback);
// Then somewhere in your sketch:
void data_received_callback () {
Serial. println ( " Data packet received " );
}
O puedes acceder al paquete:
// In the setup function:
a2dp_sink.set_stream_reader(read_data_stream);
// Then somewhere in your sketch:
void read_data_stream ( const uint8_t *data, uint32_t length)
{
int16_t *samples = ( int16_t *) data;
uint32_t sample_count = length/ 2 ;
// Do something with the data packet
}
En el método a2dp_sink.set_stream_reader()
puede proporcionar un parámetro opcional que define si desea que la salida a I2S esté activa o desactivada. Por lo tanto, puede usar este método, por ejemplo, para apagar I2S simplemente llamando a2dp_sink.set_stream_reader(read_data_stream, false)
Puede registrar un método que se llamará cuando el sistema reciba cualquier metadato AVRC ( esp_avrc_md_attr_mask_t
). Aquí hay un ejemplo
void avrc_metadata_callback ( uint8_t data1, const uint8_t *data2) {
Serial. printf ( " AVRC metadata rsp: attribute id 0x%x, %s n " , data1, data2);
}
a2dp_sink.set_avrc_metadata_callback(avrc_metadata_callback);
a2dp_sink.start( " BT " );
De forma predeterminada, deberías obtener la información más importante; sin embargo, puedes ajustarla llamando al método set_avrc_metadata_attribute_mask
, por ejemplo, si solo necesitas el título y el tiempo de reproducción, puedes llamar:
set_avrc_metadata_attribute_mask (ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_PLAYING_TIME);
antes de iniciar el sumidero A2DP. Tenga en cuenta que data2 es en realidad una cadena char*, por lo que, aunque ESP_AVRC_MD_ATTR_PLAYING_TIME
está documentado como milisegundos de duración del medio, deberá analizarlo antes de hacer cálculos. Consulte el ejemplo de metadatos para obtener más información.
De manera similar a avrc_metadata_callback
, ESP IDF v4+ admite devoluciones de llamada esp_avrc_rn_param_t
seleccionadas como set_avrc_rn_playstatus_callback
, set_avrc_rn_play_pos_callback
y set_avrc_rn_track_change_callback
que se pueden usar para obtener el estado de reproducción esp_avrc_playback_stat_t playback
. Posición de reproducción uint32_t play_pos
y bandera de cambio de pista uint8_t elm_id
respectivamente. Consulte el ejemplo de playing_status_callbacks para obtener más detalles.
He agregado los siguientes comandos AVRC, que puedes usar para "controlar" tu fuente A2DP:
Esto se puede utilizar para alimentar, por ejemplo, su altavoz Bluetooth con sus datos de audio.
También podemos generar sonido y enviarlo, por ejemplo, a un altavoz Bluetooth.
El códec de audio admitido en ESP32 A2DP es SBC: la API utiliza datos PCM normalmente formateados con una frecuencia de muestreo de 44,1 kHz y datos de muestra de 16 bits de dos canales.
Cuando inicia BluetoothA2DPSource, debe pasar el nombre de Bluetooth al que desea conectarse y una 'función de devolución de llamada' que genera los datos de sonido:
# include " BluetoothA2DPSource.h "
BluetoothA2DPSource a2dp_source;
// callback
int32_t get_sound_data (Frame *data, int32_t frameCount) {
// generate your sound data
// return the effective length (in frames) of the generated sound (which usually is identical with the requested len)
// 1 frame is 2 channels * 2 bytes = 4 bytes
return frameCount;
}
void setup () {
a2dp_source. start ( " MyMusic " , get_sound_data);
}
void loop () {
}
En los ejemplos puede encontrar una implementación que genera sonido con la ayuda de la función sin(). También puede indicar varios nombres de Bluetooth alternativos. El sistema simplemente se conecta al primero que esté disponible:
void setup () {
static std::vector< char *> bt_names = { " MyMusic " , " RadioPlayer " , " MusicPlayer " };
a2dp_source. start (bt_names, get_sound_data);
}
También puedes proporcionar los datos directamente como una matriz simple de uint8_t:
# include " BluetoothA2DPSource.h "
extern const uint8_t StarWars10_raw[];
extern const unsigned int StarWars10_raw_len;
BluetoothA2DPSource a2dp_source;
SoundData *music = new OneChannelSoundData(( int16_t *)StarWars30_raw, StarWars30_raw_len/ 2 );
void setup () {
a2dp_source. start ( " RadioPlayer " );
a2dp_source. write_data (music);
}
void loop () {
}
La matriz se puede preparar, por ejemplo, de la siguiente manera:
Es posible que desees compilar con el esquema de partición: ¡aplicación enorme!
En el ejemplo anterior proporcionamos los datos con un canal. Esto tiene la ventaja de que utiliza mucho menos espacio que una grabación de 2 canales, que puedes utilizar de la siguiente manera:
SoundData *data = new TwoChannelSoundData((Frame*)StarWars10_raw,StarWars10_raw_len/4);
En el constructor puedes pasar parámetros adicionales:
TwoChannelSoundData (Frame *data, int32_t frameCount, bool loop= false );
OneChannelSoundData ( int16_t *data, int32_t frameCount, bool loop= false , ChannelInfo channelInfo=Both);
OneChannel8BitSoundData ( int8_t *data, int32_t frameCount, bool loop= false , ChannelInfo channelInfo=Both);
Esta biblioteca utiliza el registrador ESP32 que puede activar en Arduino en - Herramientas - Registro de depuración central.
El código actual depende exclusivamente del ESP-IDF (que también es proporcionado por el núcleo Arduino ESP32). ¡No hay otras dependencias y esto incluye la API de Arduino!
Por eso apoyamos:
Sin embargo, esta restricción limita los ejemplos proporcionados.
Antes de clonar el proyecto, lea la siguiente información que se puede encontrar en la Wiki.
Puede utilizar esta biblioteca de forma independiente, pero es parte de mi proyecto de herramientas de audio. Por lo tanto, puede mejorar fácilmente esta funcionalidad con efectos de sonido, usar filtros, usar receptores de audio o fuentes de audio alternativos, hacer FFT, etc. Aquí hay un ejemplo simple de cómo puede analizar los datos de audio con FFT.
Dediqué mucho tiempo a proporcionar una documentación amplia y completa. Así que lea primero la documentación y verifique los problemas y discusiones antes de publicar nuevos en Github.
Abra los problemas solo para errores y, si no es un error, utilice una discusión: proporcione suficiente información sobre
para permitir que otros comprendan y reproduzcan su problema.
Finalmente, sobre todo, ¡no me envíes ningún correo electrónico ni publiques preguntas en mi sitio web personal!
Inspírate en proyectos que utilizaban esta biblioteca y comparte tus proyectos con la comunidad.
Para Arduino, puede descargar la biblioteca como zip y llamar a incluir Biblioteca -> biblioteca zip. O puede clonar este proyecto en la carpeta de bibliotecas de Arduino, por ejemplo, con
cd ~ /Documents/Arduino/libraries
git clone https://github.com/pschatzmann/ESP32-A2DP.git
git clone https://github.com/pschatzmann/arduino-audio-tools.git
Para los ejemplos proporcionados, también deberá instalar la biblioteca de herramientas de audio.
Para otros marcos, consulte la Wiki.
El historial de cambios se puede encontrar en la Wiki.
Este software es totalmente gratuito, pero puedes hacerme feliz recompensándome con un regalo.