L'ESP32 est un microcontrôleur qui fournit une API pour Bluetooth A2DP qui peut être utilisée pour recevoir des données sonores, par exemple depuis votre téléphone mobile, et les rend disponibles via une méthode de rappel. La sortie est un flux de données PCM, décodé à partir du format SBC. La documentation peut être trouvée ici.
I2S est une norme d'interface de bus série électrique utilisée pour connecter des appareils audio numériques entre eux. Il est utilisé pour communiquer des données audio PCM entre des circuits intégrés dans un appareil électronique.
Nous pouvons donc simplement transmettre l'entrée de Bluetooth à la sortie I2S : un exemple de cela d'Espressif peut être trouvé sur Github.
Malheureusement, cet exemple ne m'a pas fait plaisir, j'ai donc décidé de le convertir en une simple bibliothèque Arduino très facile à utiliser à partir d'un IDE logiciel Arduino.
Comme le nom de cette bibliothèque l'indique, elle prend en charge le protocole Bluetooth A2DP qui permet uniquement le streaming audio !
Il prend également en charge le profil de télécommande audio/vidéo (AVRCP) avec A2DP.
Le profil mains libres (HFP), le profil casque (HSP) et l'AVRCP autonome sans A2DP ne sont pas pris en charge !
Espressif retire l'ancienne API I2S : Ainsi, avec Arduino v3.0.0 (IDF v5), mon ancienne intégration I2S ne sera plus disponible. La syntaxe héritée fonctionne toujours tant que vous n'effectuez pas de mise à niveau.
Afin de prendre en charge une API de sortie unique indépendante de la version, il est recommandé d'installer et d'utiliser la bibliothèque AudioTools. La documentation et tous les exemples ont donc été mis à jour pour utiliser cette nouvelle approche.
Cependant, vous pouvez également effectuer une sortie vers n'importe quelle autre classe héritant d'Arduino Print : par exemple l'Arduino ESP32 I2SClass ou vous pouvez utiliser le rappel de données décrit ci-dessous.
Cela peut être utilisé par exemple pour construire votre propre haut-parleur Bluetooth.
Voici l'exemple le plus simple qui utilise simplement les paramètres par défaut appropriés :
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
I2SStream i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
Cela crée un nouveau périphérique Bluetooth nommé « MyMusic » et la sortie sera envoyée aux broches I2S par défaut suivantes qui doivent être connectées à un DAC externe :
Veuillez noter que ces broches par défaut ont changé par rapport à l'ancienne API !
Vous pouvez définir facilement vos propres broches avant le 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 () {
}
Vous pouvez également utiliser l'API Arduino ESP32 I2S : vous n'avez pas besoin d'installer de bibliothèque supplémentaire pour cela.
# 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 () {}
Veuillez noter que cette API dépend également de la version installée : L'exemple ci-dessus concerne ESP32 >= 3.0.0 !
Vous pouvez également envoyer la sortie directement au DAC interne de l'ESP32 en utilisant AnalogAudioStream depuis AudioTools :
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink (out);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
La sortie va maintenant aux broches DAC GPIO25 (canal 1) et GPIO26 (canal 2).
Vous pouvez être averti lorsqu'un paquet est reçu. L'API utilise des données PCM normalement formatées avec un taux d'échantillonnage de 44,1 kHz, des données d'échantillonnage à deux canaux de 16 bits.
// 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 vous pouvez accéder au paquet :
// 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
}
Dans la méthode a2dp_sink.set_stream_reader()
, vous pouvez fournir un paramètre facultatif qui définit si vous souhaitez que la sortie vers I2S soit active ou désactivée. Vous pouvez donc utiliser cette méthode pour, par exemple, désactiver I2S en appelant simplement a2dp_sink.set_stream_reader(read_data_stream, false)
Vous pouvez enregistrer une méthode qui sera appelée lorsque le système recevra des métadonnées AVRC ( esp_avrc_md_attr_mask_t
). Voici un exemple
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 " );
Par défaut, vous devriez obtenir les informations les plus importantes, mais vous pouvez ajuster cela en appelant la méthode set_avrc_metadata_attribute_mask
, par exemple si vous avez juste besoin du titre et de la durée de lecture, vous pouvez appeler :
set_avrc_metadata_attribute_mask (ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_PLAYING_TIME);
avant de démarrer le récepteur A2DP. Notez que data2 est en fait une chaîne char*, donc même si ESP_AVRC_MD_ATTR_PLAYING_TIME
est documenté comme les millisecondes de durée du média, vous devrez l'analyser avant de faire des calculs dessus. Voir l'exemple de métadonnées pour en savoir plus.
De la même manière que avrc_metadata_callback
, ESP IDF v4+ prend en charge les rappels esp_avrc_rn_param_t
sélectionnés comme set_avrc_rn_playstatus_callback
, set_avrc_rn_play_pos_callback
et set_avrc_rn_track_change_callback
qui peuvent être utilisés pour obtenir l'état de lecture esp_avrc_playback_stat_t playback
, la position de lecture uint32_t play_pos
et uint8_t elm_id
suivent respectivement l'indicateur de changement. Voir l'exemple Playing_status_callbacks pour plus de détails.
J'ai ajouté les commandes AVRC suivantes, que vous pouvez utiliser pour « contrôler » votre source A2DP :
Cela peut être utilisé pour alimenter, par exemple, votre haut-parleur Bluetooth avec vos données audio.
Nous pouvons également générer du son et l'envoyer par exemple à un haut-parleur Bluetooth.
Le codec audio pris en charge dans ESP32 A2DP est SBC : l'API utilise des données PCM normalement formatées avec un taux d'échantillonnage de 44,1 kHz, des données d'échantillonnage à deux canaux de 16 bits.
Lorsque vous démarrez BluetoothA2DPSource, vous devez transmettre le nom Bluetooth auquel vous souhaitez vous connecter et une « fonction de rappel » qui génère les données sonores :
# 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 () {
}
Dans les exemples, vous pouvez trouver une implémentation qui génère du son à l'aide de la fonction sin(). Vous pouvez également indiquer plusieurs noms Bluetooth alternatifs. Le système se connecte simplement au premier disponible :
void setup () {
static std::vector< char *> bt_names = { " MyMusic " , " RadioPlayer " , " MusicPlayer " };
a2dp_source. start (bt_names, get_sound_data);
}
Vous pouvez également fournir les données directement sous forme d'un simple tableau 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 () {
}
Le tableau peut être préparé par exemple de la manière suivante :
Vous souhaiterez peut-être compiler avec le système de partition : Huge App !
Dans l'exemple ci-dessus, nous fournissons les données avec un seul canal. Cela présente l'avantage d'utiliser beaucoup moins d'espace qu'un enregistrement à 2 canaux, que vous pouvez utiliser de la manière suivante :
SoundData *data = new TwoChannelSoundData((Frame*)StarWars10_raw,StarWars10_raw_len/4);
Dans le constructeur, vous pouvez passer des paramètres supplémentaires :
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);
Cette bibliothèque utilise l'enregistreur ESP32 que vous pouvez activer dans Arduino dans - Outils - Core Debug Log.
Le code actuel dépend uniquement de l'ESP-IDF (qui est également fourni par le noyau Arduino ESP32). Il n'y a pas d'autres dépendances et cela inclut l'API Arduino !
C'est pourquoi nous soutenons :
Cette restriction limite cependant les exemples fournis.
Avant de cloner le projet, veuillez lire les informations suivantes qui se trouvent dans le Wiki.
Vous pouvez utiliser cette bibliothèque de manière autonome, mais elle fait partie de mon projet d'outils audio. Vous pouvez donc facilement améliorer cette fonctionnalité avec des effets sonores, utiliser des filtres, utiliser des récepteurs audio ou des sources audio alternatives, effectuer une FFT, etc. Voici un exemple simple de la façon dont vous pouvez analyser les données audio avec FFT.
J'ai passé beaucoup de temps à fournir une documentation complète et complète. Veuillez donc d'abord lire la documentation et vérifier les problèmes et les discussions avant d'en publier de nouveaux sur Github.
Ouvrez des tickets uniquement pour les bugs et s'il ne s'agit pas d'un bug, utilisez une discussion : fournissez suffisamment d'informations sur
pour permettre aux autres de comprendre et de reproduire votre problème.
Enfin et surtout ne m'envoyez pas d'e-mails et ne posez pas de questions sur mon site personnel !
Inspirez-vous des projets qui utilisaient cette bibliothèque et partagez vos projets avec la communauté.
Pour Arduino, vous pouvez télécharger la bibliothèque au format zip et appeler include Library -> zip library. Ou vous pouvez cloner ce projet dans le dossier des bibliothèques Arduino, par exemple avec
cd ~ /Documents/Arduino/libraries
git clone https://github.com/pschatzmann/ESP32-A2DP.git
git clone https://github.com/pschatzmann/arduino-audio-tools.git
Pour les exemples fournis, vous devrez également installer la bibliothèque d'outils audio.
Pour d'autres frameworks, voir le Wiki
L'historique des modifications peut être trouvé dans le Wiki
Ce logiciel est totalement gratuit, mais vous pouvez me faire plaisir en me récompensant avec une friandise