W skrócie
Skąd pomysł
Mam VW Polo 6R, przy którym modyfikacje stały się moim hobby, więc ten projekt nie wziął się z przypadku. Kilka lat temu zebrałem dostępne opcje w przeglądzie dodatkowych wskaźników do Polo 6R. Od kilku lat jeżdżę też z niemieckim wyświetlaczem CANchecked, który opisałem w recenzji. Działa, ale jak na urządzenie za ponad 650 euro jest wykonany słabo. Obudowa to tani wydruk 3D, a wyświetlacz ma przestarzałe złącze microUSB, które nawet z kątową wtyczką nie mieściło się dobrze, więc producent po prostu je podciął, żeby pasowało. Rozbudowa też kosztuje: sam pasek kilku diod do sygnalizacji zmiany biegu to około 300 zł.
Lubię DIY, więc zrobiłem własne rozwiązanie: otwarte i takie, które mogę dowolnie dostosować i rozwijać. Tak samo podchodzę do stron internetowych, bo zawsze jest coś do poprawienia i ulepszenia.
Gdyby chodziło tylko o klasyczne zegary, wystarczyłyby dodatkowe wskaźniki z Beetle albo Scirocco. Mnie interesował wskaźnik cyfrowy, który da się dowolnie dostosować, a kiedyś może trafi do obudowy środkowych zegarów ze Scirocco. Projekt był przede wszystkim testem możliwości.
Wygląd miał jednak pasować do auta, stąd główna zasada projektu: domyślnie wszystko wygląda jak oryginalne zegary, czyli font VW, czerwone pola z kropek jak w 6R i segmenty LCD jak we wskaźniku paliwa. Każdy dodatek jest opcją, którą się włącza.
Tarcze powstawały metodą prób i błędów. Projektował je Claude Code, a ja wiele razy zlecałem poprawki, porównując wynik z moimi zdjęciami zegarów 6R z bliska: tu przesunąć element, tam pogrubić linię albo zmienić odstęp. Chciałem, żeby wyglądały jak fabryczne, a jednocześnie pozwalały zmienić styl i wyświetlane parametry.
Do tego doszły trzy wymagania:
- Konfiguracja z telefonu. Telefon łączy się ze wskaźnikiem przez Bluetooth i ustawia strony, układy, kanały i kolory, a podgląd w aplikacji zgadza się z wyświetlaczem co do piksela.
- Rozszerzalność. Nowy kanał danych, nowy układ albo nowe źródło (CAN, OBD) nie może oznaczać przepisywania połowy kodu.
- Android Auto. Wartości na żywo, przełączanie stron i kody usterek na ekranie radia.
Co już potrafi
- Strony i układy: klasyczna tarcza, kilka wartości naraz, układ boczny z łukami, zegar i mapa.
- Ikony kanałów w stylu kontrolek VW.
- Kanał napięcia akumulatora, dodany jednego popołudnia.
- Mapę z OpenStreetMap, dodaną trochę dla zabawy: wygląda jak minimapa z GTA, ma motywy kolorów i działa offline.
- Kody usterek (DTC) i Android Auto.
Źródłem danych z auta będzie magistrala CAN. Moduł CAN jest następnym krokiem, więc na razie wartości pochodzą z symulatora wbudowanego w firmware. Cała reszta, czyli wyświetlacz, aplikacja, protokół i Android Auto, działa na prawdziwym sprzęcie.
Tak wygląda wskaźnik podłączony testowo na moim biurku:
Na krótkim filmie z biurka, opublikowanym na kanale polo.blue, wskaźnik przełącza kolejne strony:
Architektura
- Wyświetlacz ESP32-S3 z okrągłym ekranem GC9B72 360×360. C++, LVGL 9, NimBLE, PlatformIO.
- Aplikacja SpokoMeter Kotlin, Material 3, ekrany przenoszone na Jetpack Compose. Usługa łącza BLE, historia jazd, mapy offline, testy UI w Robolectricu.
- Android Auto Car App Library na szablonach: wartości na żywo, sterowanie stronami i lista kodów usterek na ekranie radia.
Wyświetlacz i telefon łączą się przez Bluetooth Low Energy (BLE) i rozmawiają własnym, małym protokołem ramek: ustawienia, strony, wartości na żywo, pozycja i większe paczki dzielone na kawałki z sumą kontrolną (zdjęcia tarcz, obszary mapy). Protokół opisuje jeden dokument, z którego korzystam ja i agenci pracujący nad obiema stronami.
Jak pracuję
Claude Code (plan Max 20x) prowadzi implementację, ale jedno polecenie w czacie by tu nie wystarczyło. Całość opiera się na tym samym procesie, którego używam w projektach webowych.
- Krok 1
Opis i zdjęcie
Mówię po polsku, czego chcę, zwykle ze zdjęciem: fabryczna tarcza 6R z bliska, dodatkowy zegar z Beetle, inne okrągłe wyświetlacze.
- Krok 2
Issue i plan
Claude zamienia to w issue i plan. Przy zmianach wizualnych najpierw robi szybkie podglądy kilku wariantów, a ja wybieram, zanim cokolwiek trafi na sprzęt.
- Krok 3
Agenci równolegle
Sub-agenci pracują osobno nad firmware i aplikacją. Część zadań z GitHuba robi samodzielnie Cezar, runner agentów od Open Mercato, i otwiera pull requesty.
- Krok 4
Lokalne bramki
Build, testy jednostkowe i lint lokalnie, bo nie polegam na GitHub Actions. Do tego test na prawdziwym wyświetlaczu przez port szeregowy: zrzut ekranu skryptem, fps i zużycie pamięci.
- Krok 5
Review i merge
Każdy PR przechodzi code review skillem om-code-review, a przed commitem kodu idzie /simplify. Merge robię dopiero po moim OK, agent nie scala swojej pracy sam.
- Krok 6
Na biurku
Flashuję wyświetlacz, instaluję aplikację na telefonie i oglądam efekt na żywo. Dopiero potem zaczyna się kolejna iteracja.
Część zadań przejmuje Cezar, otwartoźródłowy runner, który uruchamia agentów równolegle, a code review robi skill om-code-review z zestawu Open Mercato. Projekt ma też swoje zasady zapisane w plikach CLAUDE.md, które agenci czytają na starcie: kod i dokumentacja po angielsku, rozmowa po polsku, domyślne wartości jak w oryginalnych zegarach, każde ustawienie obsłużone w obu repozytoriach naraz.

Na zrzucie Cezar pracuje nad układem na szeroki ekran. Zanim otworzy pull request, sam renderuje aplikację w kilku rozmiarach ekranu i ogląda wynik, więc do review trafia zmiana, którą agent już widział. Ostateczną ocenę i tak robię sam, na telefonie.
Bramki jakości, które miały znaczenie
- Testy zasad projektu Nic nie nachodzi na skalę, boczne skale są symetryczne, wskazówka nigdy nie zasłania cyfry, segmenty są równe. Zasady wizualne zapisałem jako testy, więc review nie musi ich pilnować.
- Jedna geometria Podgląd w aplikacji i obraz na wyświetlaczu liczą się z tych samych wymiarów. Porównuję je obok siebie, więc rozjazd widać od razu.
- Budżety na urządzeniu Klatki na sekundę, najwolniejsza klatka i fragmentacja pamięci LVGL mierzone na prawdziwym ESP32. Klasyczna tarcza z kropkami trzyma 44-45 fps.
Trzy historie z projektu
Wierność oryginałowi: kropki i segmenty
Dobrze widać to na dwóch detalach. Czerwone pole na fabrycznej tarczy 6R to raster kropek, które gęstnieją w stronę końca skali. Pierwsza wersja miała trójkąty, druga kropki, a kolejne poprawiały ich kształt i kierunek na podstawie moich zdjęć z bliska. AI dwa razy źle odczytało zdjęcie: odwróciło kierunek gradientu, a raz uznało wskazówkę za odwróconą, choć nie była. Rozstrzygało zestawienie obok siebie w czterokrotnym powiększeniu.

Podobnie było z paliwem. Zwykły łuk zamieniłem na segmenty LCD jak w podstawowym Polo, z wydzieloną rezerwą i jednym czerwonym segmentem, gdy paliwa jest mało.

Diagnoza na dowodach: wolne pobieranie mapy
Pobieranie mapy offline szło bardzo wolno. Zamiast zgadywać, zmierzyłem, gdzie ucieka czas, i okazało się, że hamują je publiczne serwery Overpass. Po przejściu na kafelki wektorowe OpenFreeMap 50 km dróg wokół domu pobiera się w 11 sekund, a wcześniej po kilku minutach było gotowe 2 z 37 fragmentów.

Podgląd na komputerze, zanim coś trafi na wskaźnik
Na początku drobne poprawki wyglądu szły pełnym cyklem: zmiana, build, flash, zrzut z wyświetlacza, więc każda seria poprawek ciągnęła się bardzo długo. Odwróciłem kolejność: najpierw oglądam kilka wariantów obok siebie, renderowanych na komputerze, a na sprzęt trafia tylko wybrany. Tak powstawała większość zmian wyglądu, od pierwszej wersji tarczy do obecnej.
Podgląd i wyświetlacz rysuje ta sama geometria, więc przed każdym mergem kładę je jedno nad drugim. Dwie zmiany z ostatniego etapu sprawdzone w ten sposób:


Na czym się potykałem
- Warstwa wizualna. Najwięcej czasu zajęło dopracowanie wyglądu. Z logiką i testami AI radzi sobie wyraźnie lepiej niż z odwzorowaniem projektu co do piksela.
- Zdjęcia wzorcowe. Model źle odczytuje szczegóły ze zdjęć: kierunek, proporcje, co jest odwrócone. Wątpliwości szybko rozwiewa zestawienie obu wersji obok siebie w powiększeniu.
- Wspólne zasoby. Równolegli agenci dzielili jeden port szeregowy do wyświetlacza. Raz build testowy, który nie znał nowego kanału, skasował moje ustawione strony. Odtworzyłem je z kopii, a w zasadach projektu pojawiła się nowa: odczytaj stan właściciela, przywróć go po teście i sprawdzaj na najnowszym buildzie.
- Uprawnienia. Agent nie scala własnych PR-ów bez mojego wyraźnego OK. To bramka, którą zostawiłem celowo.
Poza kodem: lutownica, suwmiarka, drukarka
Claude Code przyspiesza każdą warstwę projektu, ale kilku rzeczy nie zrobi za mnie.
Elektronika. Moduł wyświetlacza przychodzi z luźną listwą pinów do wlutowania. Pin GND siedzi na dużym polu miedzi, więc łatwo o zimny lut, a objaw jest podstępny: podświetlenie gaśnie, gdy poruszy się przewodami. Potem trzeba połączyć wyświetlacz z ESP32-S3 według tabeli pinów, pamiętając, że moduł bierze 3,3 V, a nie 5 V, i zbudować dzielnik napięcia do pomiaru akumulatora, który kalibruję multimetrem.

Obudowa. Zamiast wyklikać ją w programie CAD, opisałem ją kodem w OpenSCAD. Wymiary modułu wziąłem z rysunku producenta, a tych, których tam nie ma, jak grubość szkła nad płytką czy grubość lameli nawiewu, dopisałem z pomiarów suwmiarką. Model jest parametryczny: grubość ścianki odpowiada dwóm ścieżkom drukarki, a luzy pasowania są dobrane pod wydruk. Każda wersja ma też osobną część testową, czyli sam przód do szybkiego wydruku i przymiarki. To ta sama zasada co z podglądem tarcz na komputerze, tylko w plastiku.

Model napisał Claude, ale pomiar, przymiarka i ocena „pasuje czy nie” zostały po mojej stronie.
Aplikacja i Android Auto
SpokoMeter to moja pierwsza natywna aplikacja. Ma pięć zakładek: podgląd wyświetlacza z danymi z bieżącej jazdy, strony i ich układy, wygląd, ustawienia auta (wersja silnika, skala, pole czerwone, kody usterek) i historię jazd z eksportem do CSV. Każdy element tarczy da się kliknąć w podglądzie i zmienić, a wyświetlacz odświeża się od razu.
Na początku była to po prostu aplikacja: klasyczne widoki Androida, podgląd tarczy i kilka ustawień. Następnego dnia rano doszło Material 3, wieczorem podstawy Jetpack Compose, a po nich obecny układ: pięć zakładek, ustawienia aplikacji pod ikoną koła zębatego i podgląd obok ustawień na szerokim ekranie. Ekrany przechodzą na Compose po kolei, bez przepisywania całości naraz.
Na ekranie radia aplikacja pokazuje wartości na żywo, pozwala przełączać strony wyświetlacza i wyświetla listę kodów usterek.
Co dalej
- Moduł CAN i prawdziwe dane z auta: najpierw pasywny podsłuch magistrali, potem zapytania diagnostyczne o doładowanie i temperaturę oleju.
- Kody usterek z OBD zamiast symulowanych.
- Trzy wyświetlacze na jednym ESP32, w miejscu dodatkowych zegarów ze Scirocco.
- Obsługa platformy MQB, a nie tylko PQ25.
Czego się nauczyłem
Przed tym projektem nie napisałem linijki ani w Kotlinie, ani w C++. Po kilku dniach aplikacja działa razem z wyświetlaczem i Android Auto. Pomogły mi nawyki z pracy nad stronami i aplikacjami webowymi: plan przed kodem, testy, review każdej zmiany i sprawdzanie zamiast zgadywania. AI pisze kod szybciej niż ja, ale to ja decyduję, co wchodzi, i odpowiadam za wynik.
Jeśli masz problem, przy którym trzeba połączyć aplikację, urządzenie albo istniejący system, napisz do mnie. Chętnie doradzę i pomogę go rozwiązać.



























