O ESP32 é um microcontrolador que fornece uma API para Bluetooth A2DP que pode ser usada para receber dados de som, por exemplo, do seu telefone celular e os disponibiliza através de um método de retorno de chamada. A saída é um fluxo de dados PCM, decodificado do formato SBC. A documentação pode ser encontrada aqui.
I2S é um padrão de interface de barramento serial elétrico usado para conectar dispositivos de áudio digital. É usado para comunicar dados de áudio PCM entre circuitos integrados em um dispositivo eletrônico.
Portanto, podemos simplesmente alimentar a entrada do Bluetooth para a saída I2S: Um exemplo disso no Espressif pode ser encontrado no Github.
Infelizmente este exemplo não me deixou feliz, então decidi convertê-lo em uma biblioteca Arduino simples que é muito fácil de usar a partir de um IDE de software Arduino.
Como o nome desta biblioteca indica, ela suporta o protocolo Bluetooth A2DP, que fornece apenas streaming de áudio!
Ele também suporta Perfil de Controle Remoto de Áudio/Vídeo (AVRCP) junto com A2DP.
O perfil mãos-livres (HFP), perfil de fone de ouvido (HSP) e AVRCP independente sem A2DP não são suportados!
Espressif está aposentando a API I2S legada: Portanto, com o Arduino v3.0.0 (IDF v5), minha antiga integração I2S não estará mais disponível. A sintaxe herdada ainda funciona, desde que você não atualize.
Para oferecer suporte a uma API de saída exclusiva e independente de versão, é recomendável instalar e usar a biblioteca AudioTools. Portanto a documentação e todos os exemplos foram atualizados para utilizar esta nova abordagem.
No entanto, você também pode enviar para qualquer outra classe herdada do Arduino Print: por exemplo, o Arduino ESP32 I2SClass ou você pode usar o retorno de chamada de dados descrito abaixo.
Isto pode ser usado, por exemplo, para construir seu próprio alto-falante Bluetooth.
Aqui está o exemplo mais simples que usa apenas as configurações padrão adequadas:
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
I2SStream i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
Isso cria um novo dispositivo Bluetooth com o nome “MyMusic” e a saída será enviada para os seguintes pinos I2S padrão que precisam ser conectados a um DAC externo:
Observe que esses pins padrão mudaram em comparação com a API legada!
Você pode definir seus próprios pins facilmente antes do 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 () {
}
Você também pode usar a API Arduino ESP32 I2S: Você não precisa instalar nenhuma biblioteca adicional para isso.
# 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 () {}
Observe que esta API também depende da versão instalada: O exemplo acima é para ESP32 >= 3.0.0!
Você também pode enviar a saída diretamente para o DAC interno do ESP32 usando o AnalogAudioStream do AudioTools:
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink (out);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
A saída agora vai para os pinos GPIO25 (Canal 1) e GPIO26 (Canal 2) do DAC.
Você pode ser notificado quando um pacote for recebido. A API usa dados PCM normalmente formatados como taxa de amostragem de 44,1 kHz, dados de amostra de 16 bits de dois canais.
// 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 " );
}
Ou você pode acessar o pacote:
// 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
}
No método a2dp_sink.set_stream_reader()
você pode fornecer um parâmetro opcional que define se deseja que a saída para I2S seja ativa ou desativada - Portanto, você pode usar este método para, por exemplo, desligar o I2S apenas chamando a2dp_sink.set_stream_reader(read_data_stream, false)
Você pode registrar um método que será chamado quando o sistema receber qualquer metadado AVRC ( esp_avrc_md_attr_mask_t
). Aqui está um exemplo
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 " );
Por padrão você deve obter as informações mais importantes, mas você pode ajustar isso chamando o método set_avrc_metadata_attribute_mask
, por exemplo, se você precisar apenas do título e do tempo de jogo, você pode chamar:
set_avrc_metadata_attribute_mask (ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_PLAYING_TIME);
antes de iniciar o coletor A2DP. Observe que data2 é na verdade uma string char*, portanto, mesmo que ESP_AVRC_MD_ATTR_PLAYING_TIME
esteja documentado como milissegundos de duração da mídia, você precisará analisá-lo antes de fazer contas. Veja o exemplo de metadados para mais informações.
Da mesma forma que avrc_metadata_callback
, ESP IDF v4+ suporta retornos de chamada esp_avrc_rn_param_t
selecionados como set_avrc_rn_playstatus_callback
, set_avrc_rn_play_pos_callback
e set_avrc_rn_track_change_callback
que podem ser usados para obter esp_avrc_playback_stat_t playback
, uint32_t play_pos
posição de reprodução e uint8_t elm_id
sinalizador de mudança de faixa respectivamente. Veja o exemplo Playing_status_callbacks para mais detalhes.
Eu adicionei os seguintes comandos AVRC, que você pode usar para 'controlar' sua fonte A2DP:
Isto pode ser usado para alimentar, por exemplo, seu alto-falante Bluetooth com seus dados de áudio.
Também podemos gerar som e enviá-lo, por exemplo, para um alto-falante Bluetooth.
O codec de áudio suportado no ESP32 A2DP é SBC: A API usa dados PCM normalmente formatados como taxa de amostragem de 44,1 kHz, dados de amostra de 16 bits de dois canais.
Ao iniciar o BluetoothA2DPSource, você precisa passar o nome do Bluetooth ao qual deseja se conectar e uma 'função de retorno de chamada' que gera os dados de som:
# 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 () {
}
Nos exemplos você encontra uma implementação que gera som com a ajuda da função sin(). Você também pode indicar vários nomes Bluetooth alternativos. O sistema apenas se conecta ao primeiro que estiver disponível:
void setup () {
static std::vector< char *> bt_names = { " MyMusic " , " RadioPlayer " , " MusicPlayer " };
a2dp_source. start (bt_names, get_sound_data);
}
Você também pode fornecer os dados diretamente como um array simples 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 () {
}
A matriz pode ser preparada, por exemplo, da seguinte maneira:
Você pode querer compilar com o Esquema de Partição: Aplicativo Enorme!
No exemplo acima, fornecemos os dados com um canal. Isto tem a vantagem de ocupar muito menos espaço que uma gravação de 2 canais, que você poderia usar da seguinte maneira:
SoundData *data = new TwoChannelSoundData((Frame*)StarWars10_raw,StarWars10_raw_len/4);
No construtor você pode passar parâmetros adicionais:
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 usa o logger ESP32 que você pode ativar no Arduino em - Ferramentas - Core Debug Log.
O código atual depende puramente do ESP-IDF (que também é fornecido pelo núcleo do Arduino ESP32). Não há outras dependências e isso inclui a API do Arduino!
Por isso apoiamos:
Esta restrição limita contudo os exemplos fornecidos.
Antes de clonar o projeto, leia as seguintes informações que podem ser encontradas no Wiki.
Você pode usar esta biblioteca isoladamente, mas ela faz parte do meu projeto de ferramentas de áudio. Assim, você pode facilmente aprimorar essa funcionalidade com efeitos sonoros, usar filtros, usar coletores de áudio ou fontes de áudio alternativas, fazer FFT, etc. Aqui está um exemplo simples de como você pode analisar os dados de áudio com FFT.
Passei muito tempo para fornecer uma documentação abrangente e completa. Portanto, leia a documentação primeiro e verifique os problemas e discussões antes de postar novos no Github.
Abra problemas apenas para bugs e se não for um bug, use uma discussão: Forneça informações suficientes sobre
para permitir que outras pessoas entendam e reproduzam seu problema.
Finalmente, acima de tudo, não me envie e-mails nem poste perguntas no meu site pessoal!
Inspire-se em projetos que usaram esta biblioteca e compartilhe seus projetos com a comunidade.
Para Arduino você pode baixar a biblioteca como zip e chamar include Biblioteca -> biblioteca zip. Ou você pode clonar este projeto na pasta de bibliotecas do Arduino, por exemplo, com
cd ~ /Documents/Arduino/libraries
git clone https://github.com/pschatzmann/ESP32-A2DP.git
git clone https://github.com/pschatzmann/arduino-audio-tools.git
Para os exemplos fornecidos, você também precisará instalar a biblioteca de ferramentas de áudio.
Para outras estruturas, consulte o Wiki
O histórico de alterações pode ser encontrado no Wiki
Este software é totalmente gratuito, mas você pode me deixar feliz me recompensando com uma guloseima