ESP32 — это микроконтроллер, который предоставляет API для Bluetooth A2DP, который можно использовать для получения звуковых данных, например, с вашего мобильного телефона, и делает их доступными с помощью метода обратного вызова. Выходные данные представляют собой поток данных PCM, декодированный из формата SBC. Документацию можно найти здесь.
I2S — это стандарт интерфейса электрической последовательной шины, используемый для соединения цифровых аудиоустройств. Он используется для передачи аудиоданных PCM между интегральными схемами электронного устройства.
Таким образом, мы можем просто передать входной сигнал Bluetooth на выход I2S: пример этого от Espressif можно найти на Github.
К сожалению, этот пример меня не порадовал, поэтому я решил преобразовать его в простую библиотеку Arduino , которую очень легко использовать из программной среды разработки Arduino.
Как следует из названия этой библиотеки, она поддерживает протокол Bluetooth A2DP, который обеспечивает только потоковую передачу звука!
Он также поддерживает профиль дистанционного управления аудио/видео (AVRCP) вместе с A2DP.
Профиль громкой связи (HFP), профиль гарнитуры (HSP) и автономный AVRCP без A2DP не поддерживаются!
Espressif прекращает использование устаревшего API I2S: поэтому с Arduino v3.0.0 (IDF v5) моя старая интеграция I2S больше не будет доступна. Устаревший синтаксис по-прежнему работает, пока вы не обновите его.
Для поддержки уникального API вывода, независимого от версии, рекомендуется установить и использовать библиотеку AudioTools. Поэтому документация и все примеры были обновлены для использования этого нового подхода.
Однако вы также можете выводить данные в любой другой класс, который наследуется от Arduino Print: например, Arduino ESP32 I2SClass, или вы можете использовать обратный вызов данных, описанный ниже.
Это можно использовать, например, для создания собственного Bluetooth-динамика.
Вот простейший пример, в котором используются правильные настройки по умолчанию:
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
I2SStream i2s;
BluetoothA2DPSink a2dp_sink (i2s);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
При этом будет создано новое устройство Bluetooth с именем «MyMusic», и выходные данные будут отправлены на следующие контакты I2S по умолчанию, которые необходимо подключить к внешнему ЦАП:
Обратите внимание, что эти контакты по умолчанию изменились по сравнению с устаревшим API!
Вы можете легко определить свои собственные булавки перед 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 () {
}
Вы также можете использовать Arduino ESP32 I2S API: для этого не нужно устанавливать какую-либо дополнительную библиотеку.
# 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 () {}
Обратите внимание, что этот API также зависит от установленной версии: приведенный выше пример предназначен для ESP32 >= 3.0.0!
Вы также можете отправить вывод непосредственно на внутренний ЦАП ESP32, используя AnalogAudioStream из AudioTools:
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink (out);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
Выход теперь поступает на контакты ЦАП GPIO25 (канал 1) и GPIO26 (канал 2).
Вы можете быть уведомлены о получении пакета. API использует данные PCM, обычно отформатированные как двухканальные 16-битные данные выборки с частотой дискретизации 44,1 кГц.
// 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 " );
}
Или вы можете получить доступ к пакету:
// 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
}
В методе a2dp_sink.set_stream_reader()
вы можете указать дополнительный параметр, который определяет, хотите ли вы, чтобы вывод в I2S был активным или деактивированным. Таким образом, вы можете использовать этот метод, например, для отключения I2S, просто вызвав a2dp_sink.set_stream_reader(read_data_stream, false)
Вы можете зарегистрировать метод, который будет вызываться, когда система получит любые метаданные AVRC ( esp_avrc_md_attr_mask_t
). Вот пример
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 " );
По умолчанию вы должны получить самую важную информацию, однако вы можете настроить это, вызвав метод set_avrc_metadata_attribute_mask
, например, если вам просто нужен заголовок и время воспроизведения, вы можете вызвать:
set_avrc_metadata_attribute_mask (ESP_AVRC_MD_ATTR_TITLE | ESP_AVRC_MD_ATTR_PLAYING_TIME);
перед запуском приемника A2DP. Обратите внимание, что data2 на самом деле представляет собой строку char*, поэтому, хотя ESP_AVRC_MD_ATTR_PLAYING_TIME
документировано как миллисекунды продолжительности мультимедиа, вам необходимо проанализировать ее, прежде чем выполнять математические операции. Дополнительную информацию см. в примере метаданных.
Подобно avrc_metadata_callback
, ESP IDF v4+ поддерживает выбранные обратные вызовы esp_avrc_rn_param_t
, такие как set_avrc_rn_playstatus_callback
, set_avrc_rn_play_pos_callback
и set_avrc_rn_track_change_callback
, которые можно использовать для получения статуса esp_avrc_playback_stat_t playback
, uint32_t play_pos
позиция воспроизведения и uint8_t elm_id
флаг смены дорожки соответственно. Подробности смотрите в примере play_status_callbacks.
Я добавил следующие команды AVRC, которые вы можете использовать для «управления» вашим источником A2DP:
Это можно использовать, например, для передачи аудиоданных на динамик Bluetooth.
Мы также можем генерировать звук и отправлять его, например, на динамик Bluetooth.
Поддерживаемый аудиокодек в ESP32 A2DP — SBC: API использует данные PCM, обычно отформатированные как частота дискретизации 44,1 кГц, двухканальные 16-битные данные выборки.
Когда вы запускаете BluetoothA2DPSource, вам необходимо передать имя Bluetooth, к которому вы хотите подключиться, и «функцию обратного вызова», которая генерирует звуковые данные:
# 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 () {
}
В примерах вы можете найти реализацию, генерирующую звук с помощью функции sin(). Вы также можете указать несколько альтернативных имен Bluetooth. Система просто подключается к первой доступной:
void setup () {
static std::vector< char *> bt_names = { " MyMusic " , " RadioPlayer " , " MusicPlayer " };
a2dp_source. start (bt_names, get_sound_data);
}
Вы также можете предоставить данные напрямую в виде простого массива 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 () {
}
Массив можно подготовить, например, следующим образом:
Возможно, вы захотите скомпилировать схему разделов: огромное приложение!
В приведенном выше примере мы предоставляем данные по одному каналу. Преимущество этого метода заключается в том, что он занимает гораздо меньше места, чем двухканальная запись, которую вы можете использовать следующим образом:
SoundData *data = new TwoChannelSoundData((Frame*)StarWars10_raw,StarWars10_raw_len/4);
В конструктор можно передать дополнительные параметры:
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);
Эта библиотека использует регистратор ESP32, который вы можете активировать в Arduino в разделе «Инструменты» — «Журнал отладки ядра».
Текущий код полностью зависит от ESP-IDF (который также предоставляется ядром Arduino ESP32). Других зависимостей нет, включая API Arduino!
Поэтому мы поддерживаем:
Однако это ограничение ограничивает приведенные примеры.
Прежде чем клонировать проект, прочтите следующую информацию, которую можно найти в Wiki.
Вы можете использовать эту библиотеку отдельно, но она является частью моего проекта аудиоинструментов. Таким образом, вы можете легко расширить эту функциональность с помощью звуковых эффектов, использовать фильтры, использовать альтернативные приемники звука или источники звука, выполнять БПФ и т. д. Вот простой пример того, как вы можете анализировать аудиоданные с помощью БПФ.
Я потратил много времени, чтобы предоставить исчерпывающую и полную документацию. Поэтому, пожалуйста, сначала прочитайте документацию и проверьте проблемы и обсуждения, прежде чем публиковать новые на Github.
Открывайте проблемы только для ошибок, а если это не ошибка, используйте обсуждение: Предоставьте достаточно информации о
чтобы другие могли понять и воспроизвести вашу проблему.
Наконец, прежде всего, не присылайте мне никаких электронных писем и не задавайте вопросы на моем личном сайте!
Получите вдохновение от проектов, в которых использовалась эта библиотека, и поделитесь своими проектами с сообществом.
Для Arduino вы можете загрузить библиотеку в формате zip и вызвать «Включить библиотеку -> zip-библиотеку». Или вы можете git клонировать этот проект в папку библиотек Arduino, например, с помощью
cd ~ /Documents/Arduino/libraries
git clone https://github.com/pschatzmann/ESP32-A2DP.git
git clone https://github.com/pschatzmann/arduino-audio-tools.git
Для предоставленных примеров вам также потребуется установить библиотеку аудио-инструментов.
Другие фреймворки см. в Wiki.
Историю изменений можно найти в Wiki.
Это программное обеспечение абсолютно бесплатное, но вы можете порадовать меня, наградив угощением.