Menu

andipanda.dev

CaRadio: FastCarPlay, LIVI i przywracanie działania Carlinkit

W poprzednich artykułach opisałem, jak udało mi się uruchomić CarPlay na Raspberry Pi i jaką rolę w tym systemie odgrywa adapter USB Carlinkit.

Jednak między pierwszym udanym uruchomieniem FastCarPlay a powstaniem działającego CaRadio wydarzyło się jeszcze coś, o czym dotąd prawie nie wspominałem.

Ta historia dotyczy wyboru podstawy programowej, eksperymentów z innym projektem i nieoczekiwanego problemu, po którym CarPlay całkowicie przestał się łączyć.

Dlaczego wybrałem SDL3

Podczas pierwszych eksperymentów z radiem samochodowym korzystałem z Qt i C++.

Qt to potężne narzędzie pozwalające tworzyć rozbudowane aplikacje graficzne. Z czasem zacząłem jednak zastanawiać się, czy rzeczywiście potrzebuję tego wszystkiego w urządzeniu, które ma wykonywać stosunkowo niewielki zestaw zadań.

Kiedy wiosną 2026 roku znalazłem FastCarPlay, jedną z jego zalet było dla mnie to, że projekt napisano w C++ z wykorzystaniem SDL.

Zacząłem poznawać SDL3 i szybko doszedłem do wniosku, że takie podejście odpowiada mi znacznie bardziej.

Aby uruchomić aplikację graficzną, nie trzeba koniecznie uruchamiać X11 ani korzystać z całego frameworka Qt. Można ograniczyć się do znacznie mniejszej liczby komponentów i samodzielnie zorganizować interfejs.

Oczywiście nie oznacza to, że SDL3 jest pod każdym względem lepsze od Qt. Są to narzędzia przeznaczone do różnych zadań.

Jednak w przypadku mojego radia SDL3 lepiej pasowało do tego, co chciałem osiągnąć: stosunkowo niewielkiej aplikacji bez zbędnych warstw oprogramowania.

Poza tym FastCarPlay już korzystał z SDL, więc przejście do własnej implementacji opartej na tym samym rozwiązaniu wydawało się całkiem logiczne.

Pierwsze uruchomienie FastCarPlay

Po zapoznaniu się z kodem źródłowym skompilowałem FastCarPlay i spróbowałem uruchomić go z moim adapterem USB Carlinkit.

I CarPlay rzeczywiście zadziałał.

Nie wszystko było jednak idealne.

Dziś nie pamiętam już wszystkich problemów, na które natrafiłem podczas pierwszych uruchomień. Pamiętam jedynie, że system nie działał do końca tak, jak oczekiwałem, więc zacząłem szukać możliwych przyczyn.

Podczas tych poszukiwań trafiłem na kolejny projekt open source — LIVI.

Postanowiłem wypróbować również jego.

Poznanie LIVI

LIVI okazał się dość dużym projektem w porównaniu z FastCarPlay.

Co więcej, był napisany w języku programowania, którego nie znałem.

Nie oznacza to, że projekt był zły. Wręcz przeciwnie: po instalacji LIVI działał u mnie prawidłowo.

Nie chciałem jednak zagłębiać się w dużą bazę kodu napisaną w nieznanym mi języku, a następnie budować na niej własnego radia.

Wolę C++, a przy tego typu samodzielnie budowanych urządzeniach staram się trzymać jednej zasady: im prościej, tym niezawodniej.

Oczywiście sama prostota niczego nie gwarantuje.

Z własnego doświadczenia zauważyłem jednak, że im bardziej komplikuje się konstrukcję na początkowych etapach, tym więcej pojawia się potencjalnych źródeł problemów.

Dodatkowe zależności, usługi i warstwy oprogramowania — wszystko to trzeba konfigurować, utrzymywać i uwzględniać podczas szukania usterek.

Dlatego chciałem pozostać przy FastCarPlay jako podstawie i stopniowo dostosowywać go do własnych potrzeb.

Jednak po eksperymencie z LIVI pojawił się nieoczekiwany problem.

Kiedy CarPlay przestał się łączyć

Po uruchomieniu LIVI wróciłem do FastCarPlay.

I odkryłem, że teraz program w ogóle przestał wykrywać telefon.

Wcześniej CarPlay przynajmniej się uruchamiał, choć występowały pewne problemy. Teraz połączenie nie było nawiązywane wcale.

Nie udało mi się ustalić dokładnej przyczyny.

Podejrzewałem, że podczas działania LIVI w adapterze Carlinkit mogło zmienić się jakieś ustawienie lub jego stan wewnętrzny, przez co wcześniejsza procedura łączenia przestała działać.

Pozostało to jednak tylko przypuszczeniem.

Faktem było coś innego: jeden projekt nadal poprawnie współpracował z adapterem, a drugi nie potrafił już nawiązać połączenia.

Właśnie ta różnica stała się punktem wyjścia do dalszych poszukiwań.

Przywracanie inicjalizacji Carlinkit

Ponieważ LIVI nadal działał, postanowiłem wykorzystać go jako punkt odniesienia.

Nie chodziło o przepisanie FastCarPlay w innym języku ani o przeniesienie do niego całego kodu LIVI.

Musiałem ustalić, czym różni się procedura inicjalizacji adaptera USB w obu projektach.

Zacząłem analizować odpowiednie fragmenty implementacji i porównywać kolejność komunikacji z Carlinkit.

Stopniowo udało mi się znaleźć różnice, które mogły wpływać na nawiązywanie połączenia.

Na podstawie tych obserwacji wprowadziłem zmiany w procedurze inicjalizacji FastCarPlay.

Po poprawkach telefon ponownie zaczął się łączyć.

Tym razem rezultat był jednak lepszy niż podczas pierwszych eksperymentów: udało mi się uzyskać stabilne połączenie i działanie CarPlay.

Był to dla mnie ważny etap.

Wcześniej przede wszystkim próbowałem uruchamiać i dostosowywać istniejące rozwiązania. Teraz musiałem przyjrzeć się bezpośrednio komunikacji aplikacji z adapterem USB i znaleźć przyczynę niezgodności.

Nie oznacza to jednak, że opracowywałem protokół CarPlay od podstaw.

Korzystałem z istniejących implementacji open source, porównywałem ich działanie i poprawiałem tę część komunikacji z adapterem, która przeszkadzała w prawidłowym działaniu mojego systemu.

Dlaczego mimo wszystko zostałem przy FastCarPlay

Po tych eksperymentach miałem dwa działające punkty wyjścia.

LIVI pokazał, że adapter potrafi poprawnie się łączyć i pracować. Co więcej, jego implementacja pomogła mi zrozumieć problem z inicjalizacją.

Mimo to jako podstawę dalszego rozwoju wybrałem FastCarPlay.

Powodów było kilka.

Przede wszystkim C++. To język, z którym pracuję od dawna i w którym wygodnie mi rozwijać oraz utrzymywać własne projekty.

Drugim powodem było SDL3. Spodobała mi się możliwość zbudowania aplikacji graficznej bez konieczności korzystania z X11 i Qt.

Trzecim — rozmiar i złożoność projektu.

Nie chciałem zamieniać samodzielnie budowanego radia w duży system programowy, zanim jeszcze zaczną działać jego podstawowe funkcje.

Moje podejście jest dość proste: najpierw zapewnić niezawodne działanie minimalnego zestawu niezbędnych komponentów, a dopiero później stopniowo dodawać kolejne możliwości.

Oczywiście wraz z rozwojem CaRadio aplikacja nieuchronnie stawała się coraz bardziej złożona.

Chciałem jednak, aby ta złożoność wynikała z konkretnych potrzeb, a nie pojawiała się z wyprzedzeniem.

Od eksperymentów do własnego CaRadio

Kiedy udało się rozwiązać problem z połączeniem, wreszcie miałem działającą podstawę programową, na której mogłem się oprzeć.

FastCarPlay zapewniał komunikację z Carlinkit, SDL3 nadawało się do stworzenia własnego interfejsu, a C++ pozwalał rozwijać aplikację w dobrze znanym mi środowisku.

Nie było to jeszcze gotowe radio samochodowe.

Pozostało połączyć CarPlay z pozostałymi funkcjami CaRadio, przemyśleć sterowanie aplikacją i dostosować ją do codziennego użytkowania w samochodzie.

Jednak jeden z najbardziej uciążliwych problemów został już rozwiązany: łączenie telefonu przestało być nieprzewidywalnym eksperymentem.

Dopiero wtedy mogłem skupić się na dalszym rozwoju własnego radia, zamiast ciągle próbować doprowadzić do tego, żeby CarPlay w ogóle się uruchomił.

Co dalej

Ta historia po raz kolejny przypomniała mi, dlaczego wolę nie komplikować własnych projektów bez potrzeby.

Czasami niewielki program, który dobrze się rozumie, okazuje się wygodniejszy od dużego gotowego rozwiązania oferującego mnóstwo możliwości.

Jednocześnie bez LIVI znalezienie przyczyny problemu z połączeniem Carlinkit mogłoby zająć mi znacznie więcej czasu.

Dlatego oba projekty odegrały swoją rolę w rozwoju CaRadio.

FastCarPlay stał się podstawą programową, a LIVI pomógł znaleźć i usunąć problem, który nie pozwalał mi ruszyć dalej.

W kolejnych artykułach opowiem o innych elementach CaRadio i zadaniach, które musiałem rozwiązać, aby zmienić działający CarPlay w pełnoprawne radio samochodowe.