Odbiornik A2DP w oparciu o Raspberry Pi

Odbiornik A2DP w oparciu o Raspberry Pi

Pomysł

Zapewne wielu z Was ma w swoim samochodzie gniazdo AUX – ja również. Jednak zestaw głośnomówiący Bluetooth, który oprócz tego posiadam, umożliwia wyłącznie prowadzenie rozmów (MY 2008 – dziwny rocznik). Wobec tego np. słuchanie muzyki ze Spotify’a za każdym razem wymaga podpięcia kabla do telefonu, co jest bardzo niepraktyczne w codziennym użytkowaniu.

Oczywiście istnieją dedykowane urządzenia, które wpina się w tym celu w instalację radia. Niemniej jednak, chcąc przesiąść się na nowy model Raspberry Pi 3B+, postanowiłem zbudować samodzielnie urządzenie o podobnej funkcjonalności w oparciu o moje stare Raspberry Pi 2 model B.  Postaram się szczegółowo przedstawić sposób budowy oraz wszystkie za i przeciw takiego rozwiązania. Do budowy projektu można użyć dowolnego modelu Raspberry Pi jednak polecam starsze egzemplarze ze względu na niższe wymagania napięciowo-prądowe. 

Niezbędne elementy

  1. Raspberry Pi + obudowa,
  2. Adapter Bluetooth, jeśli nasze RPi < model 3. Ja kupiłem najtańszy adapter bluetooth firmy 4world – działa doskonale,
  3. Karta SD min. klasy 10 (nie polecam instalacji np. na karcie klasy 4, ogromna różnica w szybkości działania systemu),
  4. Dobrej klasy ładowarka samochodowa + kabel zasilający,
  5. Przycisk chwilowy, np. od guzika reset z obudowy PC.

Zasilanie

Pierwszą kwestią, którą chciałbym omówić jest sposób zasilania przedmiotowego urządzenia. Parkuję samochód w garażu podziemnym i zdecydowałem się na rozwiązanie, które należy całkowicie wyłączyć przed pozostawieniem samochodu na noc. Odradzam budowę automatycznych wyłączników, detektorów ładowania akumulatora, ups na bateriach Li-Ion i innych tego typu urządzeń. Mam tutaj na myśli przede wszystkim względy bezpieczeństwa. Mimo wszystko w ciągu roku na skutek zwarć pewna ilość samochodów ulega całkowitemu spaleniu.

W moim samochodzie jest gniazdko zapalniczki, w którym cały czas jest napięcie (Skoda Fabia II – większość starszych aut z koncernu VAG jest w tym zakresie podobna). W tym wypadku jest to bardzo korzystne, ponieważ w trakcie krótkiego postoju urządzenie może pozostać włączone. W samochodach, w których napięcie zanika po wyłączeniu zapłonu można pomyśleć np. o wyprowadzeniu dodatkowego gniazdka zapalniczki podłączonego pod inny obwód bezpiecznikowy.

Do zasilania malinki wykorzystałem ładowarkę samochodową Nokia DC-20. Poniżej znaleziony na YouTube’ie szczegółowy test tejże ładowarki, napięcia są stabilne w pełnym zakresie deklarowanego obciążenia oraz w trakcie rozruchu silnika. Swojej ładowarki nie badałem w podobny sposób jednak odpalenie silnika w trakcie rozruchu Raspbiana nie powoduje żadnych negatywnych objawów. Nadmienię, że mam na myśli rozruch całkowicie zimnego diesla w temperaturze bliskiej zera, czyli duży pobór prądu i spadek napięcia w instalacji. Aby mimo wszystko zabezpieczyć się przed uszkodzeniem systemu plików karty SD, w jednej z ostatnich części artykułu przygotujemy pełny backup systemu.

Instalacja oprogramowania

Pierwszym krokiem, który należy zrobić przed zainstalowaniem systemu będzie oczywiście sformatowanie karty SD. Następnie musimy zainstalować system Raspbian. Ściągamy obraz systemu ze strony: 

https://downloads.raspberrypi.org/raspbian_lite/images/

Wybieramy najnowszą wersję Raspbiana Jessie Lite 2017-07-05. Wybór wersji Jessie podyktowany jest faktem, że jest to zalecana dystrybucja Raspbiana do użycia z resztą software’u wykorzystanego w tym artykule. Z Raspbianem Strech również powinno zadziałać po zaaplikowaniu dodatkowego patcha jednak nie testowałem tego.

Do nagrania obrazu na kartę SD wykorzystujemy Win32 Disk Imager

W moim przypadku, po nagraniu obrazu pojawia się komunikat o konieczności sformatowania karty. Komunikat ten oczywiście ignorujemy. Wkładamy kartę do Raspberry, podłączamy kabel sieciowy, monitor, klawiaturę oraz adapter Bluetooth. Po włączeniu wyświetli nam się okno konfiguracji systemu. Przechodzimy do zakładki Interfacing Options -> SSH -> enable, a następnie logujemy się z użyciem domyślnych danych:

Jeśli nie włączymy SSH w trakcie pierwszego uruchomienia systemu, możemy powrócić do okna konfiguracji komendą:

Po włączeniu SSH dla pewności zrestartujmy system komendą:

Włączenie SSH nie jest obligatoryjne. Jednak możliwość zdalnego podłączenia do systemu bez monitora i klawiatury w tym przypadku jest bardzo wygodna i należy mieć tę opcję włączoną. Tym bardziej że w dalszej części artykułu wyłączymy wyjście HDMI (w samochodzie jest całkowicie niepotrzebne, wyłączenie go minimalnie zmniejszy pobór prądu), a bez właczonego SSH utracilibyśmy jakikolwiek kontakt z naszym Raspberry.

Po restarcie sprawdzamy, czy adapter Bluetooth został prawidłowo rozpoznany przez system:

Adapter Bluetooth w moim przypadku jest prawidłowo widoczny na liście urządzeń USB bez żadnej dodatkowej konfiguracji ani instalowania sterowników.

Tak jak pisałem na początku, posiadam najtańszy z możliwych adapterów bluetooth firmy 4world. Działa bardzo dobrze, jest stabilny i ma zasięg w każdym miejscu mieszkania 60m2.

Po zainstalowaniu systemu, włączeniu SSH (po szczegóły jak podłączyć się do SSH odsyłam do podstawowych tutoriali nt. Raspbiana) oraz sprawdzeniu czy adapter bluetooth jest widoczny w systemie, przechodzimy do właściwej części opracowania, czyli uruchomienia odbiornika audio.

W tym celu wykorzystamy projekt użytkownika GitHub’a BaReinhard’a:

https://github.com/BaReinhard/Super-Simple-Raspberry-Pi-Audio-Receiver-Install

Wywołujemy następujące komendy:

Po uruchomieniu instalacji wybieramy następujące opcje:

oraz

Po potwierdzeniu nazwy odbiornika musimy uzbroić się w cierpliwość, instalacja od tego momentu trwa około 45 minut. Czas ten zależy przede wszystkim od szybkości naszej karty SD. Ja wykorzystałem kartę średniej klasy: Sandisk Ultra 16gb Class 10. Po zakończonej instalacji uruchamiamy ponownie system. Po restarcie w widocznych urządzeniach bluetooth powinien pojawić się nasz nowy odbiornik. Łączymy się z nim wpisując kod 0000. Tym samym pierwszy etap konfiguracji mamy za sobą.

W następnym kroku musimy zmienić kilka ważnych ustawień w systemie malinki:

  • edytujemy plik /boot/config.txt – dodane linijki na pomiędzy znacznikami z potrójnym hashem (### początek i koniec):

Bardzo ważnym krokiem jest zmiana trybu audio, bez tego dźwięk z wyjścia jack jest po prostu bardzo kiepskiej jakości i praktycznie nie da się słuchać muzyki. Zmiana wyjścia na jack w tym miejscu również jest istotna, odbiornik audio wykorzystuje PulseAudio, które nie reaguje na zmianę w innym miejscu (np. sudo raspi-config) i zawsze kieruje audio przez HDMI.

  • Zmiana domyślnej głośności

zmieniamy głośność urządzenia PCM z 40 na 100%.

  • edytujemy plik /etc/rc.local
  • #edit 01.05.2018r – poprawka – usuwamy z pliku rc.local wywołanie do skryptu “python /home/pi/bin/shutdownButton/shutdown.py”. Jest to skrypt pracujący w nieskończonej pętli i blokuje zakończenie rc.local. Dostrzegłem to w momencie, gdy chciałem powtórnie włączyć HDMI
  • ciąg dalszy [oniżej sekcji nt. rc.local

  • #edit – 01.05.2018 – Dodanie skryptu shutdown.py do crontaba

Zakładamy nowy plik crontaba dla użytkownika root. Wybieramy <2> (domyślny edytor nano). Na samym dole pliku dopisujemy:

& na końcu jest bardzo ważne, należy o tym pamiętać. Zapisujemy plik we wskazanej lokalizacji (/tmp/xxitp. -> później ten plik automatycznie przeniesie się do docelowego katalogu, po weryfikacji poprawności działania – po prostu go zapiszmy). Po ponownym uruchomieniu system powinien się w pełni uruchomić (nie zatrzyma się na wykonaniu rc.local jak pierwotnie), a guzik wyłaczania będzie regować prawidłowo.

  • Tworzymy skrypt shutdown.py

Generalnie zalecaną metodą w celu obsługi przycisków jest wykorzystanie przerwań jednak tutaj wykorzystałem ,,klasyczne” dwukrotne sprawdzenie stanu wyjścia przycisku, bezpośrednio po naciśnięciu i po raz drugi po upływie dwóch sekund. Napisałem również skrypt wykorzystujący przerwania jednak Raspberry miało tendencję do samoczynnego wyłączania się co kilka godzin (samoczynnie wywoływana była komenda shutdown) – nie wiem dlaczego. W skrypcie wykorzystujemy pin GPIO nr 12. Podłączamy przycisk do dwóch pinów oznaczonych na rysunku – GPIO 12 oraz masy. Dobrze jest wykorzystać przycisk chwilowy wyjęty z obudowy PC, który najczęściej od razu ma już zamontowaną wtyczkę wraz z wiązką.

Po restarcie systemu skrypt będzie śledzić stan na wejściu pinu GPIO 12, a po przytrzymaniu przycisku przez dłużej niż 2 s miękko zamknie system komendą shutdown. Po zamknięciu będziemy mogli bezpiecznie wyłączyć zasilanie. Zamknięcie objawi się jako rozłączenie telefonu z odbiornikiem Bluetooth, nie musimy widzieć diod na malince (po konfiguracji i testach urządzenia rozsądnie by było całkowicie je wyłączyć). Na tym etapie konfigurację odbiornika Bluetooth można uznać za zakończoną.

#Edit 03.05.2018 – Automatyczne połączenie z telefonem (Raspberry inicjatorem)

Po dłuższych testach postanowiłem dodać jeszcze jedną funkcjonalność. Mój zestaw głośnomówiący Nokia CK-7W automatycznie wysyła żądanie połączenia do sparowanego urządzenia, następuje to dokładnie w momencie rozruchu. Jest to bardzo wygodna opcja. Należy jedynie pilnować, aby bluetooth było włączone przed odpaleniem samochodu. W przeciwnym razie będziemy musieli połaczyć się ręcznie.

Dodałem do istniejącej konfiguracji mechanizm, który łączy się z telefonem podstawowym co 30s. Zatem od momentu wejścia w zasięg Raspberry, na połączenie zaczekamy maksymalnie 30s.

Na początku utwórzmy katalog:

Utworzymy dwa skrypty: skrypt w Pythonie wywołujący żądanie połączenia oraz skrypt expect realizujący to żądanie. W następnym kroku zainstalujmy moduł expect:

Następnie utwórzmy skrypt odpowiadający za zainicjowanie połączenia z telefonem (zaznaczmy rozszerzeniem, że nie jest to skrypt bashowy):

Powyższy skrypt uruchamia interaktywną linię komend “bluetoothctl” do obsługi urządzeń bluetooth. Następnie próbuje nawiązać połączenie i bada reakcję. Na początek adres MAC naszego telefonu możemy ustalić następująco:

W widocznych urządzeniach powinien być widoczny nasz telefon, adres wpisujemy do powyższego skryptu. Na tym etapie możemy przetestować działanie skryptu:

Musimy jeszcze utworzyć skrypt pythona:

Następnie należy dodać ten skrypt do cron’a:

Jak wyżej, dopisujemy kolejną linijkę:

Zanim dopiszemy skrypt do cron’a, polecam uruchomić go klasycznie i przetestować jak to działa.

Nie wczytywałem się w dokumentację bluetooth jednak z moim telefonem działa to następująco:

  1. Świeżo włączone bluetooth – połączenie bezproblemowe,
  2. Jeśli rozłączyliśmy się ręcznie z poziomu telefonu (urządzenie nadal sparowane i właczone), telefon przez pewien czas będzie odrzucać połączenie od Raspberry (skrypt auto. poł. dalej działa). Skutkiem jest zawieszenie się malinki, które mija po pewnym czasie jednak reset przyspiesza powrót do sprawności. Przynajmniej mniej-więcej tak to działa z moim telefonem Samsung Galaxy S7 Edge. Po 1-2min. telefon przestaje odrzucać połączenie od RPi – nie wiem dokładnie jaki jest algorytm i jak to wygląda w innych telefonach. Po zawieszeniu się połączenia malinka zaczyna zwracać komunikat “Failed to connect: org.bluez.ErrorInProgress”. Jeśli taki komunikat się pojawi, skrypt expect uruchomi ponownie  system i po paru chwilach połączenie zostanie powtórne nawiązane. Podsumowując, jeśli chcemy się rozłączyć – wyłączmy RPi, a unikamy rozłączania z poziomu telefonu, bo rodzi to problemy.
  3.  Słuchamy muzyki z głównego telefonu i chcemy ,,oddać” połączenie innej osobie w samochodzie. Rozłączamy się z poziomu telefonu jak wyżej, druga osoba niezwłocznie łączy się z Raspberry. Zawieszenie opisane powyżej następuje w trakcie drugiej próby aut. poł. od momentu rozłaczenia telefonu. Mamy zatem około 30s. na nawiązanie połączenia – całkiem bezpiecznie. Automatyczne nawiązywanie połączenia dalej działa w tle jednak dopóki trwa połaczenie z innym telefonem, nie ma żadnego skutku. Po rozłaczeniu, RPi automatycznie połączy się z ,,głównym telefonem”.

Polecam przetestować opcje opisane w tym rozdziale – znacząco zwiększają one wygodę użytkowania.

Kopia bezpieczeństwa

Ostatnią rzeczą, którą obligatoryjnie musimy zrobić jest kopia zapasowa zawartości karty SD. Do tworzenia kopii zapasowych wykorzystuję program Clonezilla. Można go znaleźć np. w zestawie narzędzi Hiren’s BootCD. Poniżej przedstawiam krok po kroku jak można zrobić Backup z użyciem Clonezill’i.

Pobór prądu

Na sam koniec wykonałem pomiary poboru prądu. Prąd mierzyłem na linii 12V, przed ładowarką USB. Generalnie wyniki są typowe dla Raspberry Pi 2 model B, wykorzystując starszy model albo model Zero pobór prądu będzie niższy.

  • Urządzenie w trakcie pracy albo w trybie idle (bez podłączonego urządzenia Bluetooth) -> 150-170 mA

  • Raspberry Pi po komendzie shutdown, podłączone do ładowarki -> około 45mA

  • Ładowarka w gnieździe 12V bez żadnych urządzeń -> około 5 mA

Generalnie jak widać nie jest to urządzenie, które można zostawić włączone na 3 dni w nieużywanym samochodzie. Przez 3 dni zużyłoby około 0,16A x 24h x 3 = 11,5 Ah. Jest to główna wada tego rozwiązania, automatyczny wyłącznik z cała pewnością poprawiłby komfort użytkowania. Niemniej jednak całość działa u mnie bardzo stabilnie i w 100% spełnia założoną funkcję.

Ocena: 5/5 (głosów: 6)

Podobne posty

5 komentarzy do “Odbiornik A2DP w oparciu o Raspberry Pi

  • Fajny pomysł. Bardzo porządny opis.
    Ostatnio zastanawiałem się jak coś podobnego zrobić, ale ostatecznie, z braku nadmiarowych płytek, postawiłem na transmiter FM roidmi.

    Odpowiedz
  • Fajny projekt, podoba mi sie opracowanie, ale wg. mnie to jest trochę przerost formy nad treścią. Na rynku dostępne są małe urządzenia dedykowane do transmisji dźwięku przez bt do aux. koszt raspi kilkukrtonie pzewyzsza koszt takiego odbiornika…

    Odpowiedz
  • Ciekawy projekt.
    Zmieniłbym shutdown.py dodając reboot. Niestety plik connectS7.ex wykrzacza się na Raspi ZeroW.
    Mój plik shutdown.y wyglądałby tak:
    wciskam klawisz: po dwóch błyskach diody -aktywuję program “print.py” który mi zgasł
    po 4 błyskach puszczam klawisz i mam reboot systemu
    a po 6 mam halt systemu :)
    Dlaczego uzywam screena, chyba nie muszę tłumaczyć. Odpowied: Zachowuję wielowątkowość procesów

    Odpowiedz

Odpowiedz

anuluj

Masz uwagi?