logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Głośnik Bluetooth na Raspberry Pi Pico 2W z I2S i MAX98357

bulek01 19 Lut 2025 09:59 1533 4

TL;DR

  • Powstał prosty głośnik Bluetooth na Raspberry Pi Pico 2W z modułem MAX98357, zbudowany na bazie gotowego przykładu A2DP Sink z Arduino.
  • Sygnał audio wyprowadzono po I2S zamiast PWM: użyto pBCLK 15, pWS 16 i pDOUT 17 oraz klasy BluetoothAudioConsumerI2S.
  • MAX98357 dostaje zasilanie 3.3V, a wzmacniacz klasy D ma moc około 1W; do połączenia wystarcza pięć kabelków.
  • Urządzenie działa stabilnie, rozgłasza się po Bluetooth i odtwarza dźwięk z kodekiem SBC; po kilku dniach testów gra dobrze.
Wygenerowane przez model językowy.
REKLAMA
📢 Słuchaj (AI):
  • Raspberry Pi Pico 2W z przymocowanymi przewodami i wzmacniaczem na obudowie głośnika.
    Głośnik bluetooth na Raspberry PI Pico 2W z I2S
    Widok boczny prowizorycznego głośnika Bluetooth z Raspberry PI Pico 2W i kablem.
    Głośnik stereo mini na stole.
    Moduł Raspberry Pi Pico 2W połączony z wzmacniaczem MAX98357.

    Przedstawiam prosty w montażu projekt głośnika bluetooth zbudowany na Raspberry PI Pico 2W, który posiada moduł WIFI oraz Bluetooth.
    Oprogramowanie bazuje na przykładzie z środowiska Arduino, zmodyfikowanym, tak aby odtwarzanie strumienia audio odbywało się po I2S, do którego podłączony jest DAC ze wzmacniaczem klasy D MAX98357 o mocy około 1W.

    Jako bazę konstrukcji stanowi głośnik, do którego przykleiłem na taśmie dwustronnej płytkę Raspberry oraz wzmacniacza. I2S jest połączony z wykorzystaniem kabelków.
    Konstrukcja jest zasilana z USB, gdzie źródłem zasilania może być cokolwiek np: powerbank.

    Opis działania

    Jeśli chodzi o oprogramowanie to nie napisałem prawie nic, bo wszystko było gotowe i zadziałało od razu.
    Użyłem przykładu z środowiska Arduino, który można znaleźć pod linkiem: https://github.com/earlephilhower/arduino-pic...BluetoothAudio/examples/A2DPSink/A2DPSink.ino

    Przedstawiony przykład, używa klasy PWMAudio. Chcąc użyć I2S, musiałem zrobić małą modyfikację. Usunąłem obiekt pwm i dodałem i2s:
    
    // GPIO pin numbers
    #define pBCLK 15
    #define pWS (pBCLK+1)
    #define pDOUT 17
    
    // Create the I2S port using a PIO state machine
    I2S i2s(OUTPUT, pBCLK, pDOUT);
    


    Konieczna jest jeszcze podmiana inicjalizacji w funkcji setup():
    
      // a2dp.setConsumer(new BluetoothAudioConsumerPWM(pwm));
      a2dp.setConsumer(new BluetoothAudioConsumerI2S(i2s));
    


    W przedstawionym kodzie zdefiniowałem 3 piny, które używane są na potrzeby transmisji strumienia audio.
    W praktyce do połączenia z układem MAX98357 potrzebne jest 5 kabelków, bo dochodzi jeszcze masa i zasilanie(zasilam z 3.3V).
    Pinu SD_MODE nie podpinałem, jest on na płytce podciągnięty do zasilania przez rezystor 1Mom.

    Schemat układu MAX98357A/B przedstawiający interfejs cyfrowy, interpolator, DAC i stopień wyjściowy.
    Screen przedstawia wnętrze układu MAX. Który posiada DACa oraz wzmacniacz. Dane przesyłane są jako sample 16 bitowe. Na wyjściu jest jeden głośnik.
    Scalak sumuje oba kanały dzieląc wynik przez 2(Left/2 + Right/2) tworząc w ten sposób pojedyncze wyjście mono.
    Schemat transmisji danych audio przez interfejs I2S.

    Dane po I2S wysyłane są w ten sposób, że sygnał BCLK jest zegarem, na którego narastającym zboczu próbkowane są bity z sygnału DIN.
    Sygnał LRCLK(WCLK) informuje czy aktualnie przesyłany jest lewy czy prawy kanał audio, jest on także referencją zegara dla DACa audio.

    Jeśli chodzi o kod do obsługi I2S to znajduje się on pod linkiem https://github.com/earlephilhower/arduino-pico/blob/master/libraries/I2S/src/I2S.cpp
    Do wysyłania danych używana jest maszynka PIO(https://github.com/earlephilhower/arduino-pico/blob/master/libraries/I2S/src/pio_i2s.pio), która zapewnia stabilną częstotliwość sygnału LRCLK. Dane kopiowane są z pamięci z wykorzystaniem DMA(Direct Memory Access). Dzięki czemu procesor nie zajmuje się kopiowaniem danych, a realizowane jest to automatycznie w tle. Pozostała część, czyli odbieranie danych z bluetooth, dekodowanie odbywa się już programowo.

    Bluetooth

    Schemat Raspberry Pi Pico 2W dostępny jest pod linkiem https://datasheets.raspberrypi.com/picow/pico-2-w-schematic.pdf
    Projekt mi się podoba, bo działa to bardzo dobrze, na PCB jest umieszczony nawet filtr na 2.4GHz, gdzie na przykład moduły ESP tego nie mają.
    Na płycie umieszczony jest układ Infineona CYW43439KUBG. Integruje w sobie Bluetooth 5.2 oraz WIFI IEEE 802.11b/g/n.
    Bardzo dobra jest dokumentacja tego układu pod względem radiowym, gdzie inne alternatywne układy są bardzo ubogie i prawie nic nie ma.
    Diagram blokowy układu CYW43439 z podziałem na podsystemy WLAN i Bluetooth.
    Komunikacja między RP2350 a tym układem realizowana jest z wykorzystaniem SDIO w trybie 1bitowym.
    CYW43439KUBG posiada w swoim wnętrzu mikrokontroler z rdzeniem Cortex-M3(a chyba nawet 2), którego oprogramowanie musi być wgrane podczas startu. Jest to binarka dostarczona przez Infineona. W przypadku arduino kod do konfiguracji dostarczany jest jako biblioteka statyczna libipv4-bt.a(https://github.com/earlephilhower/arduino-pico/blob/master/lib/rp2350/libipv4-bt.a). Źródła sterownika dostępne są pod linkiem https://github.com/georgerobotics/cyw43-driver/tree/main. Kod tego bloba jest zdefiniowany jako tablica i można zobaczyć pod linkiem https://github.com/georgerobotics/cyw43-driver/blob/main/firmware/cyw43_btfw_43439.h.
    Repozytorium z driverem do cyw43 zawiera trochę mniej kodu niż ta biblioteka statyczna, choćby to, że obsługa SDIO zrealizowana jest z wykorzystaniem PIO w pliku libipv4-bt.a, a w podanym repo nie ma tego kodu. Zorientowałem się, że implementacja tego jest w PicoSDK https://github.com/raspberrypi/pico-sdk/tree/master/src/rp2_common/pico_cyw43_driver, który jest skompilowany do biblioteki statycznej.
    Ciekawa jest też licencja tego drivera. Jest on tylko do zastosowań nie komercyjnych. Ale gdy jest używany wraz z urządzeniami Raspberry, to nie ma tego ograniczenia.
    Ciekawa polityka, której nie rozumiem.

    W środowisku Arduino należy podczas kompilacji wskazać że używamy stosu Bluetooth oraz ip. Wtedy dodawana jest do kompilacji wcześniej wspomniana biblioteka statyczna.
    Interfejs Arduino IDE z podświetloną opcją IP/Bluetooth Stack.

    Wracając do projektu, to głośnik zrealizowany jest z wykorzystaniem A2DP (Advanced Audio Distribution Profile). Dokumentację można znaleźć na stronie bluetooth:
    https://www.bluetooth.com/specifications/specs/advanced-audio-distribution-profile-1-4/
    Specyfikacja ta opisuje protokół do przesyłania audio z użyciem bluetooth.
    W Arduino jest reprezentowana przez bibliotekę BluetoothAudio(https://github.com/earlephilhower/arduino-pico/tree/master/libraries/BluetoothAudio)
    W prezentowanym przykładzie na potrzeby głośnika używany jest objekt:
    A2DPSink a2dp;

    Implementacja ta dostarcza interfejs do startowania odtwarzania, regulacji głośności, informacji o podłączeniu, nazwę obecnie odtwarzanego utworu. A także dekoduje i przekazuje strumień audio do objektu I2S.
    Kod znajduje się pod linkiem https://github.com/earlephilhower/arduino-pic...ter/libraries/BluetoothAudio/src/A2DPSink.cpp
    Szybka analiza kodu, oraz pliku wynikowego map po kompilacji z Arduino pokazuje, że implementacja ta korzysta bezpośrednio z funkcji udostępnianych przez wcześniej wspomnianą bibliotekę statyczną. Przy okazji zauważyłem, że jest tam zawarty stos bluetootha bstack( https://github.com/bluekitchen/btstack/tree/master).
    A przy okazji znalazłem, że kod a2dp sink jest bazowany na przykładzie z tej biblioteki https://github.com/bluekitchen/btstack/blob/master/example/a2dp_sink_demo.c
    Przy okazji można zauważyć, że biblioteka ta działa z wykorzystaniem interfejsu HCI(Host Controller Interface) zdefiniowany w specyfikacji Bluetootha(https://www.bluetooth.com/wp-content/uploads/Files/Specification/HTML/Core-54/out/en/host-controller-interface/host-controller-interface-functional-specification.html).
    Czyli driver cyw43 implementuje interfejs HCI z użyciem SDIO.

    Audio nie jest przesyłane w postaci surowej przez Bluetooth. Wykorzystywana jest kompresja kodekiem SBC(https://en.wikipedia.org/wiki/SBC_%28codec%29), który powstał na potrzeby transmisji bluetooth. A2DP wspiera też inne kodeki, ale implementacja arduino wspiera tylko SBC. Doprecyzowując, dekoder SBC jest częścią btstack(https://github.com/bluekitchen/btstack/blob/master/3rd-party/bluedroid/decoder/srce/decoder-sbc.c) i jest to chyba jedyny zaimplementowany dekoder patrząc po plikach.

    Drugim elementem klasy A2DPSink jest implementacja profilu bluetooth Audio/Video Remote Control Profile (AVRCP), który realizuje funkcjonalność sterowania źródłem dźwięku. Czyli zatrzymaj, odtwórz, regulacja głośności, informacje o odtwarzanej ścieżce. Funkcjonalność tego profilu dostarczana jest przez btstack(https://github.com/bluekitchen/btstack/blob/master/src/classic/avrcp.c).

    Po zaprogramowaniu, urządzenie bluetooth rozgłasza się, co można zobaczyć na poniższym screenie:

    Lista urządzeń Bluetooth z widocznym PicoW Boom



    Ekran połączeń Bluetooth z urządzeniem PicoW Boo.
    Po podłączeniu widać, że działa z kodekiem SBC.

    Podsumowując
    Projekt jest prosty w uruchomieniu, dzięki gotowej bazie oprogramowania Arduino. Jeśli chodzi o działanie to testowałem kilka dni i gra dobrze.
    Z pomysłów jakie mi przychodzą do głowy to rozbudować o klawiaturę z regulacją głośności i wyświetlaczem.
    Sprawdzić czy da się uruchomić to przerobić jako zestaw głośnomówiący z mikrofonem na I2S.

    Fajne? Ranking DIY
    O autorze
    bulek01
    Poziom 17  
    Offline 
    bulek01 napisał 337 postów o ocenie 294, pomógł 12 razy. Jest z nami od 2006 roku.
  • REKLAMA
  • #2 21447220
    TechEkspert
    Redaktor
    Posty: 7162
    Pomógł: 16
    Ocena: 5532
    Ciekawe, ciekawe, głośnik fatalny :) ale pomysł na wykorzystanie Pico super!
  • REKLAMA
  • #3 21448027
    rb401
    Poziom 39  
    Posty: 3001
    Pomógł: 750
    Ocena: 983
    Tak po prawdzie to od strony ekonomicznej ten projekt sensu nie ma, z uwagi na sporą dostępność tanich modułów BT do głośników, w cenach kilkuzłotowych.
    Ale domyślam się że z pewnością w tym projekcie właśnie nie o to chodzi. I fajnie że że jest pokazany tutaj ze szczegółami, w celu powiedzmy, edukacyjnym oraz jako baza do jakiś projektów o już bardziej nietypowych właściwościach, których gotowe rozwiązania nie mają. Też plus za popularyzację fajnego systemu Pico.
  • REKLAMA
  • #4 21450103
    Robco
    Poziom 11  
    Posty: 85
    Ocena: 3
    ja nie szedł bym w tą stronę
  • #5 21469534
    bulek01
    Poziom 17  
    Posty: 337
    Pomógł: 12
    Ocena: 294
    >>21448027
    Tak są gotowce bardzo tanie, ale jest wiele problemów z nimi. Albo mają kiepskie radia, które łatwo zakłócić. Od niemal całkowitego braku filtrów w torze RF do tragicznego IP3(https://en.wikipedia.org/wiki/Third-order_intercept_point), więc to po prostu działa gdy nie ma żadnych zakłóceń w otoczeniu. Jeden z takich tanich układów jaki mam ma taki problem, że jak nie ma ciągłej transmisji audio i nagle się pojawia(np nawigacja) to pierwsze 1s-2s jest wycięte. Dodatkowo nie są one możliwe w modyfikacji, całkowity brak dokumentacji użytych scalaków. I tak tutaj prezentuję moje pierwsze podejście do tematu i rozpoznanie. Ale dzięki temu, że mam coś programowalnego mogę zrobić sobie własną logikę, zmienić nazwę urządzenia, dorobić nowe profile bluetooth. Chciałbym jeszcze dorobić profil Headsetu w połączeniu z profilem głośnika. Tak aby mieć zestaw głośnomówiący z możliwością odtwarzania muzyki.
📢 Słuchaj (AI):
REKLAMA