Tani ekran dotykowy SPI (3.5", 480×320, ILI9488+ADS7846) jako fizyczny panel kontrolny do dowolnego headless RPi — od gołych pinów, przez sterownik którego nie ma w jądrze, po własną apkę w Pythonie. Opisane tak, żeby dało się to powtórzyć bez dyplomu z elektroniki.
Co to w ogóle jest ten panelik?
Masz gdzieś headless-a — Raspberry Pi bez monitora i klawiatury, który siedzi cicho w kącie i coś robi. Żeby sprawdzić, czy żyje, zawsze musisz się do niego dołączyć po SSH. panelik to sposób, żeby ten sam RPi miał własny, mały ekran dotykowy: pokazuje status na żywo i pozwala coś przełączyć bez wpisywania jednej komendy.
To nie jest gotowy produkt do kupienia — to metoda i kod do powtórzenia na swoim sprzęcie. Ekran, który tu opisujemy, jest tani i popularny, ale ma dwie pułapki, o które rozbija się większość poradników w internecie: kernelowy sterownik fbtft go nie obsługuje, a dotyk trzeba samemu skalibrować. Ten tekst prowadzi przez obie od razu.
Dla kogo to jest
Nie dla profesjonalistów
Nie zakładamy, że kiedykolwiek lutowałeś. Każde połączenie na schemacie niżej jest opisane po ludzku — co to jest i do czego służy, a nie tylko numer nóżki GPIO.
Dla ludzi z headless RPi
Jeśli masz jakikolwiek Raspberry Pi pracujący w tle i choć raz pomyślałeś „szkoda, że nie widzę na oko czy to działa" — to dokładnie ten problem rozwiązuje panelik.
Z czego to jest zbudowane
Lista rzeczy, które musisz mieć, zanim zaczniesz cokolwiek podłączać.
| element | co robi |
|---|---|
| Raspberry Pi (testowane: 3 Model B Plus) | mózg panelu — starczy dowolny model z pełnym złączem GPIO 40-pin |
| Ekran SPI 3.5", ILI9488, 480×320 | goły breakout (nie HAT z automatycznym mapowaniem) — obraz |
| Kontroler dotyku ADS7846 (rezystancyjny) | zwykle wlutowany na tej samej płytce co ekran — dotyk |
| Karta microSD, min. 8GB | Raspberry Pi OS (Debian, testowane na trixie) |
| Zasilacz 5V (micro-USB lub USB-C, wg modelu RPi) | zasilanie całości |
| Przewody żeńsko-żeńskie (jumper wires) | to goły breakout — każdy pin łączysz ręcznie |
| Obudowa dopasowana do panelu 3.5" (opcjonalnie) | uwaga: wąski bezel potrafi przyciąć obraz — patrz „Pułapki" |
Zaprojektowałem też pasującą obudowę 3D (snap-fit, bez śrub) — plik do druku jest publiczny: Raspberry Pi 3 snap-fit case 4in display na MakerWorld.
Co gdzie podłączyć
RPi jest w środku. To goły breakout, nie HAT — nic się nie mapuje samo, każdy pin łączysz ręcznie zgodnie ze schematem niżej. Zasilanie modułu ekranu: VCC → 3.3V, GND → dowolna masa RPi.
Schemat: RPi w środku, a wokół trzy grupy sygnałów. Kolor linii = do czego prowadzi, numery w ramkach = konkretne piny GPIO do wpięcia przewodu.
Dotyk dzieli z ekranem trzy linie magistrali (CLK/DIN/DO) — osobny ma tylko wybór chipu (T_CS) i przerwanie (T_IRQ). To normalne dla SPI: wiele urządzeń może siedzieć na jednej magistrali, dopóki każde ma własny CS.
Sterownik, którego nie ma w jądrze
To jest ten etap, na którym większość poradników w internecie wysyła cię w złą stronę. Tu jest droga, która faktycznie działa.
Zanim zaczniesz kombinować z konfiguracją — sprawdź jedną rzecz. Kernelowy moduł fbtft (ten, którego używa większość tutoriali do tanich ekranów SPI) obsługuje sporo podobnych chipów — ILI9486, ILI9481, ILI9341 — ale nie ILI9488. Sprawdzisz to sam:
Jeśli Twój ekran to naprawdę ILI9488 (sprawdź naklejkę/dokumentację modułu), dtoverlay=fbtft,...,ili9488,... nigdy nie zadziała — to nie kwestia złych pinów, sterownik po prostu nie istnieje w tym jądrze. Nie trać na to czasu.
Rozwiązanie: sterowanie z userspace, nie z kernela. Biblioteka luma.lcd ma gotową klasę dokładnie pod ten chip — nie substytut, prawdziwy match.
Brakujący /dev/spidev0.0 — i dlaczego, naprawdę. Fora sugerują, że overlay ads7846 globalnie wyłącza SPI0 dla innych urządzeń. To nieprecyzyjne. W drzewie urządzeń magistrala spi0 ma domyślnie już zdefiniowane dzieci spidev@0 i spidev@1, oba wyłączone — ads7846 po prostu włącza własny węzeł na cs=1, nigdy nie dotyka spidev@0. Po prostu nikt go wcześniej nie włączył.
Ślepa uliczka do ominięcia: dtoverlay=spi0-1cs. Wygląda na oczywistą poprawkę — jest w wielu poradnikach. Dopisanie tej linii przed ads7846 w config.txt i restart spowodowały, że RPi całkowicie zniknął z sieci — nawet ping z routera odpowiadał „Destination Host Unreachable". Ratunek wymagał fizycznego wyjęcia karty SD i przywrócenia config.txt z backupu na innym komputerze (partycja bootowa to zwykły FAT32, widoczny bez specjalnych narzędzi). Przyczyna: ten overlay przedefiniowuje piny CS dla całej magistrali, kolidując z tym, co już zajął ads7846. Nie używaj go, gdy ads7846 już jest załadowany na tej samej magistrali.
Działające rozwiązanie: chirurgiczny custom overlay. Zamiast globalnie przedefiniowywać magistralę, minimalny overlay, który tylko włącza już istniejący węzeł spidev@0 i nic więcej nie rusza:
Złota zasada, wyniesiona z bólu: zawsze testuj nowy overlay komendą sudo dtoverlay <plik.dtbo> na żywym systemie, zanim dopiszesz go do config.txt i zrobisz restart. Live-loading patchuje działające jądro i jest w pełni odwracalne (sudo dtoverlay -r <nazwa>) bez ryzyka utraty dostępu. Merge overlayów przy starcie dzieje się w bootloaderze, przed startem jądra — zły overlay potrafi tam zawiesić maszynę tak, że znika z sieci całkowicie, a jedyny ratunek to fizyczny dostęp do karty SD. I zawsze rób cp config.txt config.txt.bak-<data> przed każdą zmianą.
Kalibracja — nie ufaj domyślnym wartościom
Surowe odczyty z ADS7846 nie mają nic wspólnego z orientacją ekranu, dopóki im tego nie każesz.
Zależnie od fizycznego montażu konkretnej płytki (jak driver siedzi względem panelu), surowe osie X/Y bywają zamienione miejscami i odwrócone względem tego, co widzisz na ekranie. Nie zakładaj, że Twoja płytka zachowa się tak samo jak nasza — zawsze kalibruj dotykiem czterech rogów i wylicz transformację samodzielnie, zamiast ufać domyślnym xmin/xmax/ymin/ymax z dtoverlaya.
Metoda: dotknij po kolei każdy z czterech rogów ekranu, zapisz surowe wartości ABS_X/ABS_Y z evtest lub własnego loggera, a potem policz liniową transformację między surowym zakresem a pikselami ekranu. U nas skończyło się to tak (Twoje liczby będą inne — to tylko przykład metody):
Pułapki przy budowie apki
Ekran już działa i dotyk jest skalibrowany — teraz zaczynają się pułapki na poziomie samej aplikacji.
-
01
pkill -f <wzorzec> potrafi zabić własną sesję SSH. Jeśli wzorzec (np. nazwa skryptu) pojawia się w treści samej komendy SSH, dopasuje też proces-rodzica bash -c "..." uruchomiony przez sshd. Efekt: rodzic ginie w trakcie wykonania, SSH rozłącza się z „Broken pipe", a polecenie wygląda jakby losowo nie działało. Namierzaj PID przez ps aux | grep i zabijaj po numerze, albo użyj systemctl stop.
-
02
evdev wysyła ABS_X/ABS_Y tylko wtedy, gdy wartość faktycznie się zmienia. Jeśli łapiesz pozycję w momencie BTN_TOUCH DOWN i używasz jej przy UP, stare odczyty „kleją się" do nowych dotknięć przy szybkich, kolejnych tapnięciach różnych miejsc. Naprawa: trackuj bieżącą pozycję przy każdym evencie EV_ABS, ale odczytuj ją dopiero w momencie zwolnienia (BTN_TOUCH UP) — wtedy odczyt jest zawsze świeży.
-
03
luma.lcd cicho rezerwuje GPIO18 pod podświetlenie — nawet gdy nic tam fizycznie nie podłączyłeś. To udokumentowany domyślny pin biblioteki (gpio_LIGHT=18), nie przypadek. Jeśli szukasz „GPIO busy" i podejrzewasz usługi systemowe czy kernel — sprawdź najpierw samą bibliotekę wyświetlacza.
-
04
Subtelne animacje jasności migoczą, zamiast pulsować. Na ekranie SPI, gdzie cała klatka jest wypychana co ułamek sekundy, sinusoidalna zmiana jasności (np. „oddychająca" kropka statusu) wygląda jak miganie, nie płynny puls — nawet po spowolnieniu okresu animacji. Prawdziwie płynna animacja wymagałaby dużo szybszego pipeline'u renderowania (rzędu 15-30fps), co dla pełnoklatkowego SPI push na RPi jest zwykle za drogie.
-
05
Przy anizotropowym skalowaniu (dopasowanie do obudowy) nie używaj jednej uśrednionej skali. Jeśli obudowa przycina panel niesymetrycznie w X i Y, każdy wymiar pionowy musi konsekwentnie iść przez własną skalę Y, a każdy poziomy przez własną skalę X — inaczej elementy dekoracyjne (np. pigułka statusu) zaczynają się „rozjeżdżać" względem reszty layoutu przy mocniejszym ściśnięciu.
Co można na tym zbudować
U nas panelik pokazuje i przełącza most VPN+WiFi na tym samym RPi — to tylko jeden z możliwych przykładów, ale pokazuje wzorce warte powtórzenia.
Ten sam wzorzec — ekran + dotyk + status z systemd/kernela + usługa systemowa — działa równie dobrze do sterowania czymkolwiek innym na headless RPi. Sprzęt i sterownik zostają te same, zmienia się tylko to, co rysujesz i co przełączasz. Kilka pomysłów na start:
Przenośny punkt dostępu VPN
Dokładnie to, co u nas — RPi wystawia własne WiFi i tuneluje ruch do domowej sieci, więc w podróży jednym dotknięciem łączysz się ze swoimi zasobami tak, jakbyś siedział w domu.
Panel dla innej usługi
Serwer plików, drukarka 3D, stacja pogodowa, kolejny host w home labie — ten sam ekran + dotyk, inny systemctl pod spodem.
Uruchomienie jako usługa
Apka ma przetrwać restart RPi i wracać sama po awarii — nie ma jej sensu odpalać ręcznie w terminalu.
Jedna pułapka: skoro usługa działa jako root, a nie jako Twój zwykły user, biblioteki trzeba doinstalować osobno dla roota (sudo pip install --break-system-packages ...) — inaczej dostaniesz ModuleNotFoundError mimo że wszystko działało w Twojej sesji.