Urządzenie · Projekt własny · 2026
Zdzichu
Toksyczny helpdesk w kieszeni. Obraża Cię z lodowatą elegancją i mimo to rozwiązuje problem.
Fizyczne urządzenie (M5Stack Core2) z pikselową twarzą wypalonego testera IT: słuchawki z mikrofonem, krzywo osadzone okulary, rozluźniony krawat. Stukasz w twarz, zadajesz pytanie, stukasz znowu, nagranie idzie do własnego backendu na VPS-ie, a wraca gotowa odpowiedź głosowa. Aplikacja na Androida ma tę samą twarz i korzysta z tego samego backendu.
Rola: Pomysł, firmware, aplikacja na Androida, backend i wdrożenie, całość solo.

M5Stack Core2
Urządzenie
ESP32
Procesor
Node.js + Gemini
Backend
Postać miała być nieznośna i użyteczna jednocześnie, a te dwa wymagania ciągną w przeciwne strony. Żart przestaje być śmieszny w sekundzie, w której zaczyna przeszkadzać w rozwiązaniu problemu. Do tego cały ciężki pipeline (rozpoznawanie mowy, generowanie odpowiedzi, synteza głosu) nie miał jak zmieścić się na ESP32, a klucze API wgrane do firmware oznaczałyby rozdanie ich każdemu, kto weźmie urządzenie do ręki. I jeszcze ekran: 320×240 pikseli bez wygładzania, na którym emocje muszą być czytelne z drugiego końca pokoju.
Urządzenie jest cienkim klientem: zna wyłącznie adres backendu i jeden współdzielony sekret, zero kluczy API na sprzęcie. Jedna tura rozmowy to jedno żądanie: surowe PCM leci POST-em do backendu w Dockerze na moim VPS-ie, tam Gemini robi transkrypcję, odpowiedź i syntezę mowy, a wraca gotowe audio do natychmiastowego odtworzenia. Emocje rozwiązałem jak manometr, nie jak minę: pięć stanów twarzy, w których wypiek wściekłości wspina się od kołnierzyka w górę z każdą kolejną turą, a pod całym jadem prompt systemowy pilnuje, żeby została działająca odpowiedź, bo to jedyny zawodowy obowiązek Zdzicha.
- 01Twarz jako maszyna stanów: pięć stanów rysowanych tym samym zestawem pikselowych prymitywów wprost na ekranie 320×240, bez wygładzania.
- 02Sterowanie głosem: tap-to-talk na dotykowej twarzy, nagranie 16 kHz / 16-bit mono, drugi tap kończy i wysyła.
- 03Backend w Node.js (Express) w Dockerze: jedno żądanie na turę, sekret urządzenia w nagłówku, surowe PCM w ciele.
- 04Warstwa modelu: Gemini zwraca transkrypcję i odpowiedź w jednym przejściu, potem osobno syntezę mowy, a prompt systemowy trzyma granicę między jadem a treścią.
- 05Port na Androida: te same współrzędne twarzy co w firmware i ten sam protokół, więc aplikacja i urządzenie rozmawiają z tym samym backendem.
- 06Wdrożenie: build i deploy przez GitHub Actions na własny VPS, za Nginx Proxy Managerem.





Tap-to-talk
Stukasz w twarz, mówisz, stukasz znowu. Żadnego przycisku, żadnego słowa aktywującego i żadnego nasłuchu w tle.
Twarz jak manometr
Pięć stanów: spokój, nasłuch, myślenie, mówienie i eskalacja. Wypiek wściekłości wspina się od kołnierzyka w górę z każdą kolejną turą rozmowy.
Zero kluczy na sprzęcie
Urządzenie zna tylko adres backendu i jeden współdzielony sekret. Klucze do modelu zna wyłącznie backend, więc rozkręcenie obudowy nic nie daje.
Jedna tura, jedno żądanie
Nagranie w surowym PCM idzie jednym POST-em, wraca gotowe audio, bez sesji do utrzymywania i bez stanu po stronie urządzenia.
Ta sama twarz na Androidzie
Aplikacja to port 1:1 współrzędnych z firmware, nie przybliżenie i rozmawia z tym samym backendem, tym samym protokołem.
Głos, nie napisy
Odpowiedź wraca jako mowa (synteza Gemini, głos „Charon") i odtwarza się od razu po odebraniu.
Sarkazm z granicą
Na system i świat Zdzichu bluzga jak szewc, ale wobec użytkownika trzyma lodowaty, chirurgicznie precyzyjny sarkazm bez wulgaryzmów, to celowa granica wpisana w prompt.
Własna infrastruktura
Cały pipeline stoi w Dockerze na moim VPS-ie i wdraża się sam przy każdym pushu.
- Urządzenie
- M5Stack Core2 (ESP32, ekran 320×240)
- Firmware
- C++ / M5Unified (PlatformIO)
- Aplikacja
- Android (Kotlin, zero zewnętrznych zależności)
- Backend
- Node.js (Express) w Dockerze, własny VPS
- Model
- Gemini, transkrypcja, odpowiedź i synteza mowy
- Audio
- nagranie 16 kHz / 16-bit mono, odtwarzanie 24 kHz
- Wdrożenie
- GitHub Actions, Nginx Proxy Manager
Urządzenie nie zna żadnego klucza do modelu, ma tylko adres backendu i jeden współdzielony sekret w nagłówku żądania, więc dostęp do sprzętu nie daje dostępu do API. Samo nagranie jest przetwarzane po drodze: idzie do mojego backendu, a stamtąd do Gemini po transkrypcję, odpowiedź i syntezę głosu. Nic nie nagrywa się w tle, mikrofon startuje dopiero po stuknięciu w twarz i kończy po drugim stuknięciu.
Następny projekt
Breath of the Bald