ESP32 عبارة عن وحدة تحكم دقيقة توفر واجهة برمجة التطبيقات (API) لـ Bluetooth A2DP والتي يمكن استخدامها لتلقي البيانات الصوتية، على سبيل المثال، من هاتفك المحمول وإتاحتها عبر طريقة رد الاتصال. الإخراج عبارة عن دفق بيانات PCM، تم فك تشفيره من تنسيق SBC. الوثائق يمكن العثور عليها هنا.
I2S هو معيار واجهة ناقل تسلسلي كهربائي يستخدم لتوصيل أجهزة الصوت الرقمية معًا. يتم استخدامه لتوصيل بيانات صوت PCM بين الدوائر المتكاملة في جهاز إلكتروني.
لذلك يمكننا فقط تغذية الإدخال من البلوتوث إلى مخرج I2S: يمكن العثور على مثال على ذلك من Espressif على Github.
لسوء الحظ، هذا المثال لم يسعدني لذلك قررت تحويله إلى مكتبة Arduino بسيطة وسهلة الاستخدام للغاية من Arduino Software IDE.
كما يوحي اسم هذه المكتبة، فهي تدعم بروتوكول A2DP Bluetooth الذي يوفر تدفق الصوت فقط!
كما أنه يدعم ملف التحكم عن بعد في الصوت/الفيديو (AVRCP) مع A2DP.
لا يتم دعم ملف تعريف التحدث الحر (HFP) وملف تعريف سماعة الرأس (HSP) وAVRCP المستقل بدون A2DP!
ستتوقف Espressif عن استخدام واجهة برمجة تطبيقات I2S القديمة: لذلك مع Arduino v3.0.0 (IDF v5) لن يكون تكامل I2S القديم متاحًا بعد الآن. لا يزال بناء الجملة القديم يعمل طالما لم تقم بالترقية.
من أجل دعم واجهة برمجة تطبيقات الإخراج الفريدة المستقلة عن الإصدار، يوصى بتثبيت مكتبة 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 الافتراضية التالية والتي تحتاج إلى الاتصال بـ DAC خارجي:
يرجى ملاحظة أن هذه الدبابيس الافتراضية قد تغيرت مقارنة بواجهة برمجة التطبيقات القديمة!
يمكنك تحديد الدبابيس الخاصة بك بسهولة قبل 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 () {}
يرجى ملاحظة أن واجهة برمجة التطبيقات هذه تعتمد أيضًا على الإصدار المثبت: المثال أعلاه مخصص لـ ESP32 >= 3.0.0!
يمكنك أيضًا إرسال الإخراج مباشرة إلى DAC الداخلي لـ ESP32 باستخدام AnalogAudioStream من AudioTools:
# 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) بيانات PCM المنسقة عادةً بمعدل أخذ عينات يبلغ 44.1 كيلو هرتز، وبيانات عينة ثنائية القناة 16 بت.
// 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 هي في الواقع سلسلة أحرف*، لذلك على الرغم من أن 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
موضع التشغيل 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(). يمكنك أيضًا الإشارة إلى أسماء بلوتوث بديلة متعددة. يتصل النظام فقط بالنظام الأول المتاح:
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). لا توجد تبعيات أخرى وهذا يشمل Arduino API!
ولذلك نحن نؤيد:
لكن هذا القيد يحد من الأمثلة المقدمة.
قبل استنساخ المشروع، يرجى قراءة المعلومات التالية التي يمكن العثور عليها في ويكي.
يمكنك استخدام هذه المكتبة بمفردها، ولكنها جزء من مشروع الأدوات الصوتية الخاص بي. لذلك يمكنك بسهولة تحسين هذه الوظيفة باستخدام المؤثرات الصوتية، واستخدام المرشحات، واستخدام منافذ الصوت البديلة أو مصادر الصوت، وإجراء التحويل السريع (FFT)، وما إلى ذلك. فيما يلي مثال بسيط عن كيفية تحليل البيانات الصوتية باستخدام التحويل السريع (FFT).
لقد قضيت الكثير من الوقت لتقديم وثائق شاملة وكاملة. لذا يرجى قراءة الوثائق أولاً والتحقق من المشكلات والمناقشات قبل نشر أي مشكلات جديدة على Github.
افتح المشكلات فقط للأخطاء وإذا لم تكن خطأ، فاستخدم المناقشة: قدم معلومات كافية عنها
لتمكين الآخرين من فهم مشكلتك وإعادة إنتاجها.
أخيرًا، قبل كل شيء، لا ترسل لي أي رسائل بريد إلكتروني أو تنشر أسئلة على موقع الويب الخاص بي!
احصل على بعض الإلهام من المشاريع التي كانت تستخدم هذه المكتبة وشارك مشاريعك مع المجتمع.
بالنسبة لـ Arduino، يمكنك تنزيل المكتبة بتنسيق ZIP والاتصال بما في ذلك Library -> Zip Library. أو يمكنك استنساخ هذا المشروع في مجلد مكتبات 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
هذا البرنامج مجاني تمامًا، ولكن يمكنك أن تجعلني سعيدًا بمكافأتي بمكافأة