ESP32는 휴대폰에서 사운드 데이터를 수신하고 콜백 메서드를 통해 사용할 수 있도록 하는 데 사용할 수 있는 Bluetooth A2DP용 API를 제공하는 마이크로컨트롤러입니다. 출력은 SBC 형식에서 디코딩된 PCM 데이터 스트림입니다. 문서는 여기에서 찾을 수 있습니다.
I2S는 디지털 오디오 장치를 함께 연결하는 데 사용되는 전기 직렬 버스 인터페이스 표준입니다. 전자 장치의 집적 회로 간에 PCM 오디오 데이터를 통신하는 데 사용됩니다.
따라서 Bluetooth의 입력을 I2S 출력으로 공급할 수 있습니다. Espressif의 이에 대한 예는 Github에서 찾을 수 있습니다.
불행하게도 이 예제는 마음에 들지 않아서 Arduino 소프트웨어 IDE에서 사용하기 매우 쉬운 간단한 Arduino 라이브러리 로 변환하기로 결정했습니다.
이 라이브러리의 이름에서 알 수 있듯이 오디오 스트리밍만 제공하는 A2DP Bluetooth 프로토콜을 지원합니다!
또한 A2DP와 함께 AVRCP(Audio/Video Remote Control Profile)를 지원합니다.
HFP(핸즈프리 프로필), HSP(헤드셋 프로필) 및 A2DP가 없는 독립형 AVRCP는 지원되지 않습니다 !
Espressif는 레거시 I2S API를 중단합니다. 따라서 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 () {
}
그러면 "MyMusic"이라는 이름의 새 Bluetooth 장치가 생성되고 출력은 외부 DAC에 연결해야 하는 다음 기본 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에 대한 것입니다!
AudioTools의 AnalogAudioStream을 사용하여 출력을 ESP32의 내부 DAC로 직접 보낼 수도 있습니다.
# include " AudioTools.h "
# include " BluetoothA2DPSink.h "
AnalogAudioStream out;
BluetoothA2DPSink a2dp_sink (out);
void setup () {
a2dp_sink. start ( " MyMusic " );
}
void loop () {
}
이제 출력은 DAC 핀 GPIO25(채널 1) 및 GPIO26(채널 2)으로 이동합니다.
패킷이 수신되면 알림을 받을 수 있습니다. API는 일반적으로 44.1kHz 샘플링 속도, 2채널 16비트 샘플 데이터 형식의 PCM 데이터를 사용합니다.
// 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에 대한 출력을 활성화할지 또는 비활성화할지 정의하는 선택적 매개변수를 제공할 수 있습니다. 따라서 이 메소드를 사용하여 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
set_avrc_rn_track_change_callback
유사하게 ESP IDF v4+ set_avrc_rn_playstatus_callback
esp_avrc_rn_param_t
esp_avrc_playback_stat_t playback
재생 상태, uint32_t play_pos
재생 위치 set_avrc_rn_play_pos_callback
uint8_t elm_id
트랙 변경 플래그입니다. 자세한 내용은 Playing_status_callbacks 예시를 참조하세요.
A2DP 소스를 '제어'하는 데 사용할 수 있는 다음 AVRC 명령을 추가했습니다.
이는 예를 들어 Bluetooth 스피커에 오디오 데이터를 공급하는 데 사용할 수 있습니다.
또한 소리를 생성하여 Bluetooth 스피커 등으로 보낼 수도 있습니다.
ESP32 A2DP에서 지원되는 오디오 코덱은 SBC입니다. API는 일반적으로 44.1kHz 샘플링 속도, 2채널 16비트 샘플 데이터로 형식화된 PCM 데이터를 사용합니다.
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 () {
}
예를 들어 다음과 같은 방법으로 배열을 준비할 수 있습니다.
파티션 구성표: 거대한 앱으로 컴파일하고 싶을 수도 있습니다!
위의 예에서는 하나의 채널로 데이터를 제공합니다. 이는 다음과 같은 방법으로 사용할 수 있는 2채널 녹음보다 훨씬 적은 공간을 사용한다는 장점이 있습니다.
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);
이 라이브러리는 Arduino의 도구 - 코어 디버그 로그에서 활성화할 수 있는 ESP32 로거를 사용합니다.
현재 코드는 ESP-IDF(Arduino ESP32 코어에서도 제공됨)에만 의존합니다. 다른 종속성은 없으며 여기에는 Arduino API가 포함됩니다!
따라서 우리는 다음을 지원합니다:
그러나 이 제한은 제공된 예를 제한합니다.
프로젝트를 복제하기 전에 Wiki에서 찾을 수 있는 다음 정보를 읽어보세요.
이 라이브러리는 독립형으로 사용할 수 있지만 내 오디오 도구 프로젝트의 일부입니다. 따라서 사운드 효과를 사용하여 이 기능을 쉽게 향상시키고, 필터를 사용하고, 대체 오디오 싱크 또는 오디오 소스를 사용하고, FFT 등을 수행할 수 있습니다. 다음은 FFT를 사용하여 오디오 데이터를 분석하는 방법에 대한 간단한 예입니다.
저는 포괄적이고 완전한 문서를 제공하기 위해 많은 시간을 보냈습니다. 따라서 Github에 새로운 문서를 게시하기 전에 먼저 문서를 읽고 문제와 토론을 확인하십시오.
버그에 대해서만 문제를 공개하고 버그가 아닌 경우 토론을 사용하십시오.
다른 사람들이 귀하의 문제를 이해하고 재현할 수 있도록 합니다.
마지막으로 무엇보다도 나에게 이메일을 보내거나 내 개인 웹사이트에 질문을 게시 하지 마세요 !
이 라이브러리를 사용하는 프로젝트에서 영감을 얻고 프로젝트를 커뮤니티와 공유하세요.
Arduino의 경우 라이브러리를 zip으로 다운로드하고 include Library -> zip 라이브러리를 호출할 수 있습니다. 또는 이 프로젝트를 Arduino 라이브러리 폴더에 git clone할 수 있습니다.
cd ~ /Documents/Arduino/libraries
git clone https://github.com/pschatzmann/ESP32-A2DP.git
git clone https://github.com/pschatzmann/arduino-audio-tools.git
제공된 예제의 경우 audio-tools 라이브러리도 설치해야 합니다.
다른 프레임워크에 대해서는 Wiki를 참조하세요.
변경 내역은 Wiki에서 찾을 수 있습니다.
이 소프트웨어는 완전히 무료입니다. 하지만 저에게 보상으로 보상을 주시면 저를 행복하게 해줄 수 있습니다.