26 września 2026
Tania taśma LED i protokół bez dokumentacji
Sterownik taśmy LED w moim aucie przyjmował każdą ramkę Bluetooth i nic z nią nie robił. Opisuję, jak doszedłem do tego, czego oczekuje, i jak z tego wyrosła aplikacja WireAmbi z trybem muzycznym, w którym od wykrycia uderzenia do zapisu Bluetooth mija średnio 6 ms.

Taśmą LED w moim aucie steruje MELK-OC21, sterownik z rodziny ELK-BLEDOM. Takie kontrolery są tanie i popularne, ale nie mają żadnej dokumentacji, a aplikacja producenta nie działa na ekranie Android Auto. Chciałem zmieniać światło z ekranu auta, więc napisałem własną aplikację, WireAmbi. Najwięcej czasu zeszło mi przy tym nie na interfejsie, tylko na samym sterowniku, który długo przyjmował wszystko i nie robił nic.
Pierwsze połączenie
Po połączeniu sterownik pokazuje jedną usługę GATT, FFF0. Komendy zapisuje się do charakterystyki FFF3 bez potwierdzenia, a FFF4 w teorii wysyła powiadomienia. Mój sterownik nigdy nic przez nią nie przysłał, nawet po ramce z zapytaniem o stan. Aplikacja musi więc sama pamiętać, co ustawiła.
Na starcie złapały mnie jeszcze dwie rzeczy. Sterownik nie ogłasza usługi FFF0 w pakiecie rozgłoszeniowym, więc skan z filtrem po UUID nie znajdował niczego i musiałem filtrować po nazwie. Do tego przyjmuje tylko jedno połączenie naraz: dopóki trzyma je inna aplikacja albo brama w aucie, telefon się nie połączy.
Ramka, na którą taśma nie reagowała
Każda komenda ma 9 bajtów: nagłówek 7E, stopkę EF i siedem bajtów pośrodku. Pierwsza wersja WireAmbi wysyłała kolor jako 7E 07 05 03 R G B 10 EF. Każdy zapis Bluetooth kończył się sukcesem, a taśma nie zmieniała koloru. Wyglądało to na kłopot z łączem albo błąd w aplikacji, tymczasem winna była sama ramka: takiego formatu koloru nie zna żaden model z publicznej listy wariantów.
Tą listą jest tabela modeli z integracji elkbledom dla Home Assistant, najszerzej przetestowane publiczne zestawienie tych sterowników. Wynika z niej, że kontrolery sprzedawane jako ELK-BLEDOM, MELK czy LEDBLE mają ten sam szkielet ramki, ale różnią się bajtem po nagłówku, wypełnieniem, a czasem nawet wartościami komend. Wzorce wziąłem z tej tabeli, a wariant MELK sprawdziłem na swoim sterowniku:
| Komenda | ELK-BLEDOM | MELK (mój) | MELK-Ox |
|---|---|---|---|
| Włącz | 7E 00 04 F0 00 01 FF 00 EF |
7E 00 04 01 00 00 00 00 EF |
7E 04 04 F0 00 01 FF 00 EF |
| Kolor | 7E 00 05 03 RR GG BB 00 EF |
to samo | to samo |
| Jasność | 7E 00 01 i FF 00 FF 00 EF |
7E 04 01 i FF 00 FF 00 EF |
7E 04 01 i 01 FF FF 00 EF |
Z tego wzięła się warstwa protokołu w WireAmbi. Ma trzy warianty i wybiera jeden z nich po nazwie urządzenia, według najdłuższego pasującego prefiksu. MELK-OC21 nie pasuje do MELK-OC10, więc trafia do ogólnego wariantu MELK. Tu czekała na mnie jeszcze jedna pułapka. Po połączeniu BluetoothDevice.getName() potrafi zwrócić null dla urządzenia, którego system nie ma sparowanego, więc nazwę trzeba brać z wyniku skanu albo z zapamiętanego urządzenia. U mnie dialekt przez jakiś czas wybierał się dobrze tylko dlatego, że MELK jest wariantem domyślnym.
Oddychanie liczone w telefonie
Kolor okazał się jedyną komendą, co do której wszystkie warianty są zgodne. Wbudowane efekty każda rodzina numeruje po swojemu: 48 to „oddychanie” w MELK-Ox, a w zwykłym MELK nie znaczy nic. Mój sterownik dostawał właśnie ten numer i dlatego tryb oddychania nie działał.
Zrezygnowałem więc z efektów wbudowanych i oddychanie liczy telefon. Aplikacja wysyła 20 ramek koloru na sekundę, a jasność zmienia się po sinusoidzie i nie schodzi poniżej 12%. Suwak ustawia okres od 6 do 1,2 s. Na MELK-OC21 zmierzyłem odstępy 49–51 ms, 19,8 ramki na sekundę i ani jednego odrzuconego zapisu.
Żeby taki strumień działał, aplikacja musi pilnować tempa zapisów. Stos Bluetooth przyjmuje jeden zapis naraz, a zapis przy pełnym buforze kończy się odmową „busy” i ramka po cichu przepada. WireAmbi czeka więc na potwierdzenie ze stosu, zanim wyśle następną ramkę, a po odmowie ponawia zapis po 15 ms, do czterech prób. Kolory liczone na bieżąco trafiają do bufora na jeden element, w którym zostaje tylko najnowszy. Wcześniej była tam kolejka bez limitu i im dłużej grała muzyka, tym starsze kolory pokazywała taśma. Po poprawce policzyłem zapisy wokół pauzy w utworze: 167 przed pauzą i zero po niej, czyli w kolejce nic już nie zalegało.
Kolejność kanałów
Tanie taśmy bywają połączone w innej kolejności niż RGB i wtedy „czerwony” świeci na zielono. Sterownik ma na to osobną komendę, 7E 06 81 P1 P2 P3 FF 00 EF, a WireAmbi zna wszystkie sześć permutacji. Wbudowałem to w kalibrację: test kolorów od razu pokazuje przestawienie, a jedno stuknięcie ustawia właściwą kolejność, na przykład GRB, bez przelutowywania czegokolwiek.
Tryb muzyczny
Protokół nie ma komendy, która włączałaby w sterowniku reakcję na dźwięk albo ustawiała czułość. Muzyki musi więc słuchać telefon i to on na bieżąco wysyła kolejne kolory.
Pierwsza wersja słuchała przez mikrofon telefonu. Przy Android Auto dźwięk idzie jednak do radia w aucie (a przy testach do Desktop Head Unit), a nie do głośnika telefonu, więc mikrofon łapał tylko echo. Poziom basu wynosił 0,00–0,03, a wykryte tempo skakało od 87 do 182 BPM. Pomogło AudioPlaybackCapture, które przechwytuje odtwarzany dźwięk, zanim system go dokądkolwiek skieruje. Po tej zmianie poziom basu wzrósł do 0,08–0,51, a tempo ustaliło się na 116 BPM. Przy okazji opóźnienie Android Auto zaczęło działać na moją korzyść: analiza dostaje dźwięk wcześniej, niż zagrają go głośniki auta.
Tor analizy to FFT z okna 1024 próbek przy 22 050 Hz, logarytmiczny bank filtrów z 24 pasmami na oktawę, detektor początków dźwięków SuperFlux i śledzenie tempa na autokorelacji. Tempo służy do przewidywania uderzeń, więc błysk wychodzi także wtedy, gdy siatka tempa mówi „teraz”, choć detektor jeszcze nic nie zobaczył. Przewidywanie wprowadziło jednak nowy błąd: to samo uderzenie potrafiło odpalić się dwa razy, raz jako przewidziane i raz jako wykryte kilkadziesiąt milisekund później, a na taśmie wyglądało to jak podwojone tempo. Teraz po każdym błysku obowiązuje okres ochronny równy 0,55 okresu uderzenia i przechodzi tylko pierwszy błysk.
Opóźnienie zmierzyłem w logach. Od wykrycia uderzenia do zapisu Bluetooth mija średnio 6 ms (od 3 do 13 ms w dziewięciu pomiarach), więc wysyłka nie jest źródłem opóźnienia. Resztę daje analiza: okno FFT obejmuje około 46 ms dźwięku, co średnio oznacza około 23 ms spóźnienia, a SuperFlux dokłada dwie ramki, czyli około 20 ms. Część tego nadrabia przewidywanie uderzeń. Przy utworze 123 BPM (okres 488 ms) mediana odstępów między błyskami wyniosła 487 ms, a w ciągu 20 sekund taśma błysnęła 40 razy wobec 41 oczekiwanych.
Na tej samej analizie opiera się dziewięć stylów reakcji, od pulsu po iskry. Styl decyduje tylko o tym, jak wyniki analizy zamieniają się w kolor, więc dodanie nowego nie wymaga zmian w torze dźwięku.
Ekran Android Auto
Android Auto pozwala na pięć szablonów na zadanie. Każda zmiana typu szablonu zużywa jeden, a po wyczerpaniu limitu host zamyka aplikację. Moje pierwsze ekrany przełączały się między siatką a komunikatem zależnie od stanu Bluetooth, więc przy niestabilnym połączeniu limit znikał w kilka sekund. Teraz każdy ekran ma jeden typ szablonu, a błędy i puste stany pokazuję w nim jako zwykły kafelek. Druga niespodzianka: kontekst ekranu auta nie dziedziczy języka aplikacji, więc teksty dla auta tłumaczę osobno. Wszystko to sprawdzam w Desktop Head Unit.
Jak to wygląda dziś
Na ekranie Android Auto mam teraz siatkę kolorów, zapisane sceny i kafle skrótów, które układam na telefonie. Polecenia głosowe rozpoznaje Vosk, bez dostępu do internetu. Tryb muzyczny słucha tego, co akurat gra, więc działa także wtedy, gdy dźwięk idzie do głośników auta.
W aucie połączenie z taśmą trzyma jednak nie telefon, tylko brama led-auto na Raspberry Pi, bo sterownik przyjmuje jedno połączenie naraz. Telefon rozmawia z bramą przez szyfrowany kanał WireLink. Brama jest wtedy jedynym urządzeniem, które pisze do sterownika, i sama zgłasza stan, więc przy okazji znika kłopot z taśmą, która o niczym nie informuje. Samego łącza do taśmy zaszyfrować się nie da i dokumentacja projektu mówi to wprost.
Gdy telefon jest sparowany z bramą, ekran Android Auto przestaje być samym pilotem taśmy i staje się panelem auta z zakładkami Auto, Światło i Dziennik. Na telefonie panel auta pokazuje stan zamków, drzwi, szyb i akumulatora, razem z modelem Golfa.
Filmy z działania i opis projektu: WireAmbi w portfolio.