fbpx
devstyle.pl - Blog dla każdego programisty
devstyle.pl - Blog dla każdego programisty
15 minut

Z JavaScriptem w nadprzestrzeń!


23.05.2019

console.log(‘Siema!’); Z tej strony Maciek Korsan, nowy kapitan Sokoła Millennium™. 😎 W tym wpisie pokażę, jak za pomocą technologii frontendowych przerobić zwyczajny model z Lego na zdalnie sterowany świecący/grający statek. Gotowi? Zapnijcie pasy, polecimy krótszym niż 12 parseków skrótem!

Do wykonania zdalnie sterowanego Sokoła Millennium™ potrzebujemy:

  • Sokoła Millennium™ z Lego,
  • Raspberry Pi Zero W,
  • lutownicy (jeśli tak samo jak ja nie ogarniecie, że jest wersja Raspberry Pi Zero W z wlutowanymi pinami),
  • karty microSD,
  • plątaniny kabli do prototypowania,
  • tranzystorów i rezystorów,
  • kilku małych białych diód LED,
  • niebieskiego paska ledowego na baterie do oświetlania schowków samochodowych z Biedronki,
  • baterii 12V,
  • zasilania na USB,
  • głośnika bluetooth z Auchan za 9 zł (może być bez bluetooth),
  • przejściówki HDMI–VGA + Audio (nie pytajcie…),
  • przejściówki miniHDMI–HDMI,
  • zdolnej koleżanki / zdolnego kolegi, którzy wytłumaczą, jak przygotować obwód do zasilania paska do napędu,
  • konta na github.com,
  • konta na balena.io.

*UWAGA* robicie to na własną rękę. Jestem skrajnie nieprofesjonalny, jeśli chodzi o budowanie fizycznych rzeczy. Co prawda wszystkie moje produkcje działały i nigdy nie doprowadziłem do pożaru / poważnego porażenia elektrycznością, ale wolę Was ostrzec, żeby uniknąć odpowiedzialności. Jeśli mamy to już za sobą, przejdźmy do realizacji. Projekt wygląda na skomplikowany, ale nie dajcie się zwieść pozorom – jest to prostsze, niż mogłoby się wydawać!

Epizod I: Mroczno – widno

Mając pod ręką wszystkie potrzebne elementy, możemy przystąpić do pracy. Pomyślałem, że na początku warto zamontować napęd ledowy pod istniejący wężyk i przetestować, czy w ogóle to dobrze wygląda.

Po udanym teście musiałem znaleźć sposób na podłączenie napędu pod Raspberry Pi. Niestety moja znajomość teorii obwodów jest na poziomie mniej-niż-zero, dlatego poprosiłem o pomoc znajomego – doktoranta z Wojskowej Akademii Technicznej. Konsultując się z nim, przypomniałem sobie, jak działają tranzystory i jak wykorzystać zewnętrzną baterię (pasek ledowy wymaga napięcia 12V, a z Raspberry Pi wychodzi maksymalnie 5V). W rezultacie powstał profesjonalny schemat.

Raspberry Pi wyposażone jest w piny GPIO pozwalające m.in. na wysyłanie sygnałów / ustawianie stanów za pomocą kodu.

Wrzuciłem na kartę najprostszą wersję Raspbiana (to taka dystrybucja Linuxa specjalnie na Raspberry Pi) i uruchomiłem system. Połączyłem się przez SSH po sieci lokalnej i nie pozostało mi nic innego jak wybranie jednego GPIO i powierzenie mu roli włącznika hipernapędu Sokoła™ – padło na GPIO nr 3.

– Korsan, ale jak Ty sterujesz tym pinem?
– JavaScriptem!
– Ale JavaScript to ten śmieszny język do robienia stronek inter…
– You must unlearn, what you have learned…

Pierwsze sterowanie tym pinem zrobiłem za pomocą Node.js, czyli środowiska wykonywania kodu javascriptowego poza przeglądarką! Podobnie jak przy budowaniu nowoczesnych aplikacji www dysponujemy tu npm, więc instalujemy paczkę o nazwie onoff i mamy już dostęp do naszych wyjść/wejść na Raspberry Pi. Jak odpalić napęd, używając trzech linijek kodu? Bardzo proszę:

(I cyk, dwójeczka).

Ustawienie stanu wysokiego na pinie nr 3 (zgodnie z naszym schematem) puszcza prąd do paska LED hipernapędu. W podobny sposób sterujemy pozostałymi pinami, do których podpiąłem małe białe diody LED. Kokpit z Chewbaccą i Hanem Solo zyskał podświetlenie, dzięki czemu nie siedzą oni już po ciemku. Dodatkowo umieściłem z przodu dwie diody – latanie z włączonymi światłami jest bezpieczniejsze. Epizod I skończył się ustawieniem możliwości ręcznej zmiany oświetlenia.

Lubię łączyć kabelki

Epizod II: Atak kontenerów

Mając już jakąkolwiek kontrolę nad oświetleniem, pomyślałem o dodaniu zdalnego sterowania. Oczywiście obsługa świateł z terminala poprzez wykonywanie poleceń była całkiem spoko, ale konieczność odpalania komputera tylko po to, żeby włączyć/wyłączyć światło, to już lekka przeginka. Potrzebowałem czegoś więcej. Zanim zabrałem się do tworzenia pilota do Sokoła™, stwierdziłem, że dobrze byłoby znaleźć jakiś wygodny sposób na aktualizowanie kodu wykonywanego na komputerze pokładowym. Po krótkim researchu wybór padł na balena.io – opartą na konteneryzacji platformę do deploymentu aplikacji IoT. Co prawda Balena (dawniej Resin) powstała do obsługi całych flot urządzeń, oferuje jednak darmowy plan do 10 urządzeń. Jedyne, co musiałem zrobić, to wgrać na kartę wygenerowany przez stronę nowy obraz systemu BalenaOS. Po tej operacji mój Falcon pojawił się w balenowym dashboardzie.

Kolejnym krokiem było ustawienie dodatkowego remote w repozytorium z kodem, który miał lądować na Sokole™.
git remote add balena <USERNAME>@git.balena-cloud.com:<USERNAME>/<APPNAME>.git. Od tej chwili mogłem za pomocą prostego git push balena master deployować swój kod z dowolnego miejsca w galaktyce. Push do repozytorium powoduje odpalenie skryptów budujących obraz uruchamiany w BalenaOS na Raspberry Pi. Po udanym procesie buildowania obraz jest ściągany automatycznie na urządzenie i następuje restart kontenera.

Najfajniejszy feature Baleny – rysowanie jednorożców w terminalu

Balena korzysta z dobrodziejstw udostępnianych przez Dockera. Jako frontendowiec na potrzeby Sokoła™ wybrałem obraz o wdzięcznej nazwie node:8-stretch.

Epizod III: Zemsta bluetooth

Kolejną rzeczą, którą sobie wymarzyłem, była obsługa dźwięku przez bluetooth. Chciałem przez głośnik usłyszeć Chewbaccę czy odgłos silnika (jak w tesli!). Mogłoby się wydawać, że nic prostszego: parujemy urządzenia, dodajemy jakiś skrypt grający i pozamiatane. Teoretycznie tak, niestety praktyka okazała się brutalna. Wyłożyłem się tu na kilku frontach (he, he). Po pierwsze, Balena, uruchamiając kontener, ogranicza komunikację z systemem hostem i wymaga to sporo dodatkowej konfiguracji. Po drugie, Linux ma kilka serwerów dźwięku, m.in. pulse/alsa, a do komunikacji przez bluetooth potrzebują one uzupełniających paczek (i żadna z nich w wersji Raspberry nie działała poprawnie). Po trzecie, okazało się, że BalenaOS posiada domyślnie zbugowane paczki, które nawet nie zwracały błędów, przez co nie wiedziałem, co się dzieje. Long story short – po 3 dniach walki z konfiguracją dockerfile’a oraz ręcznej kompilacji paczek, po godzinach spędzonych w bluetoothctl i po przejrzeniu 1000 issuesów na GitHubie udało mi się uruchomić mój tani głośnik z Auchan. Niestety proces przez ułomność łączności za pomocą bluetooth wymagał ręcznej konfiguracji po każdorazowym wgraniu nowej wersji kodu. Stwierdziłem, że raczej się nie polubimy i zrezygnowałem z możliwości sterowania dźwiękiem w sposób bezprzewodowy. Włączyłem sobie wtedy Dumkę na dwa serca i śpiewałem:

Mój sokole chmurnooki
Zagraj dla mnie ton wysoki
Zagraj dla mnie po blutaczu
I nie wkurwiaj mnie
(mój miły…)

Epizod IV: Nowa nadzieja

Kilka dni później olśniło mnie. Przypomniałem sobie, że z czasów bycia wykładowcą została mi przejściówka HDMI–VGA z wyjściem audio 3.5 mm. Elegancko! Pojawił się jednak kolejny problem – Raspberry Pi Zero ma wyjście HDMI, ale w wersji mini. Szybkie zakupy i tak powstała prawdopodobnie jedyna na świecie taka konstrukcja do przesyłania sygnałów audio.

Całe życie na patencie

Dźwięk po kablu zadziałał praktycznie od razu. Musiałem tylko ustawić do konfiguracji urządzenia w dashboardzie Baleny zmienną `RESIN_HOST_CONFIG_dtparam` na `”i2c_arm=on”,”spi=on”,”audio=on”`, by po chwili usłyszeć krzyczącego Chewbaccę.

– Powiedz coś jak Wookie.

W tym momencie miałem już gotowe klocki do zbudowania skryptu do sterowania. Pozostała jedna zagadka: w jaki sposób komunikować się z Sokołem™?

Epizod V: WebSocket kontratakuje

Przed realizacją tego projektu nie miałem za bardzo okazji do wdrożenia tego typu komunikacji. Chciałem sterować Sokołem™, niekoniecznie będąc w zasięgu tej samej sieci. Wyobraziłem sobie taki schemat:

Bardzo lubię rysować schematy

Potrzebowałem czegoś co „wystawi” mi dostęp do Raspberry na zewnątrz. Nie brałem pod uwagę korzystania z publicznego IP. W grę wchodziło tutaj tylko prawilne proxy. Skorzystałem z modułu messages w Syncano (taki backend dla frontendowców) opartym na WebSocketach. Stworzyłem prosty endpoint, który w momencie wykonania żądania wysyłał za pomocą WebSocketu prostą wiadomość zawierającą informację o rodzaju świateł, które mają być włączone, oraz status 0 (wyłącz) / 1 (włącz). Wystarczyło to do stworzenia komunikacji telefon–Sokół™. Aby włączyć światło, musiałem po prostu odpytać endpoint pod adresem mojatajnastrona.syncano.site/chewbacca/cockpit i przekazać odpowiednie parametry.

Kod po stronie Syncano

Kod po stronie Sokoła™

Powyżej możecie zobaczyć główną część kodu odpowiedzialną za przyjmowanie wiadomości i wykonywanie konkretnych akcji (jest też ponowne łączenie, gdyby coś się rozłączyło). Dodatkowo, nie chcąc narażać na szybkie rozładowanie baterii, którą zasilany jest hipernapęd, dodałem prostą funkcję czasową odcinającą zasilanie po 10 sekundach. Tym sposobem właściwie zakończyłem prace okołobackendowe.

Epizod VI: Powrót frontendu

Pora wyjść z ciemnego terminala i jaskini Node’a do kolorowego świata HTML-a i CSS-ów. Założenie było proste – chcę mieć klikalną makietę Sokoła™. Bez większych problemów znalazłem fajny blueprint Sokoła™ i wrzuciłem go jako tło. Kolejną warstwę utworzyły trzy „niewidzialne” przyciski wypozycjonowane absolutnie. Podpiąłem pod nie bardzo prosty JavaScript (bez frameworków ;)).

Ot i wsio. Całość budowałem przy pomocy Parcel.js, jednak nie była to jakaś supermiła znajomość i więcej z tego bundlera już nie korzystałem. Ma za dużo gotowej „magii” i wykazuje niską odporność na błędy w kodzie.

Epizod VII: Przebudzenie PWA

Pewnego pięknego dnia, siedząc w biurze i testując różne rzeczy ze świata frontendowego, szukałem jakiegoś małego projektu, w którym mógłbym wykorzystać feature’y Progressive Web Apps – czyli aplikacji internetowej, która udaje natywną aplikację systemową, zarówno mobilną, jak i desktopową (o czym często się zapomina). PWA wnosi do świata frontendu dużo możliwości. Mi wystarczyły opcja dodania ikony aplikacji do kontroli Sokoła™ na ekranie głównym, jakiś prosty splashscreen i brak paska adresu. Aby tego dokonać, musiałem mieć stronę z kontrolerem wystawioną przez https, wygenerować odpowiednie ikony oraz stworzyć prosty plik JSON o nazwie manifest.json (Parcel.js niestety domyślnie je przekształca; żeby to obejść, musiałem zmienić nazwę na manifest.webmanifest).

Plik dołączamy w sekcji head w naszym HTML-u (razem z masą definicji splashscreenów dla Apple <3 – spoko, nie pisałem tego ręcznie, są do tego fajne generatory).

Działająca PWA

Po wykonaniu całej operacji i opublikowaniu strony kontrolera na serwerze ostatnim krokiem było dodanie aplikacji do ekranu głównego i jej uruchomienie.

Epizod VIII: Ostatni Webhook

Wiecie, że GitHub pozwala na „budowanie” aplikacji bez pisania kodu? Jak by co, ja nie wiedziałem do momentu, aż stwierdziłem, że chciałbym dostawać powiadomienia za pomocą zbudowanego przeze mnie systemu. Wchodzimy w nasz profil na GitHubie w Settings / Developer Settings / GitHub Apps. Tworzymy nową aplikację, klikając przycisk New GitHub App. Pola, które musimy skonfigurować, to nazwa aplikacji, Webhook URL – tutaj podałem dokładnie ten sam adres, który odpytuję z aplikacji frontendowej. W sekcji Permissions wybrałem pozycję Repository metadata i wartość read-only, dzięki czemu aplikacja miała dostęp do odczytywania danych o moich repozytoriach. Ostatnim elementem do skonfigurowania było zaznaczenie checkboxa Watch w sekcji Subscribe to events. Aplikacja gotowa! Za każdym razem, gdy ktoś zostawi gwiazdkę na moim koncie, GitHub wyśle informację na mój endpoint. W samym endpoincie musiałem dodać obsługę nowych danych:

…i stworzyć nową akcję na Raspberry Pi:

Każda nowa gwiazdka na moim githubowym koncie = odpalenie hipernapędu + odegranie dźwięku R2D2.

Epizod IX: Korsan. Odrodzenie.

Ożywienie Sokoła™ pozwoliło mi odpocząć od codziennych projektów. Dobrze jest czasem porobić coś z łamaniem wszelkich zasad, wywalając się co chwila przez nieoczekiwane zwroty akcji i spotkania z nieznaną technologią. Gdyby nie Sokół™, nie miałbym szansy pobawić się konteneryzacją aplikacji napisanych w Node.js uruchamianych na Raspberry Pi Zero. Straciłbym też szansę na zbudowanie prostej PWA, która ma jakiś sens i działa. Nie wiedziałbym, że można szybko wyklikać prostą aplikację githubową wysyłającą webhooki z fajnymi informacjami (bez pisania kodu).

Nie jest to na pewno projekt idealny, ale też nie zakładałem, że taki ma być. Widzę miejsca, w których mogłem zrobić coś lepiej/inaczej, np. poprawnie zlutować wszystkie przewody. Moim głównym celem była dobra zabawa i bezkarna eksploracja nowych obszarów w świecie moich ulubionych technologii. Pracując nad tym projektem, poczułem się jak nastoletni Korsan (kogo ja chcę oszukać, przecież ja jeszcze nie dorosłem :D), który pisał swoje pierwsze koślawe konsolowe boty do Gadu-Gadu. Tekst zakończę krótko: obyśmy jak najczęściej znajdowali czas na luźne i bezsensowne zabawy z technologią, czego Wam i sobie życzę!

Niech Moc będzie z Wami!

PS Cały kod dostępny jest w tym repozytorium.

Pamiętajcie też, że tylko do piątku (do 21:00) trwa nabór to Programu Skutecznej Nauki Podstaw Frontendu “WTF: Co Ten Frontend”! Z kim jak z kim, ale z Korsanem warto się uczyć! :)

Nie przegap kolejnych postów!

Dołącz do ponad 9000 programistów w devstyle newsletter!

Tym samym wyrażasz zgodę na otrzymanie informacji marketingowych z devstyle.pl (doh...). Powered by ConvertKit

Comments are closed.

Moja książka

Facebook

Zobacz również