Sterowanie żarówkami Tuya za pomocą Arduino i ESP8266

Stworzyłem fizyczny panel sterujący najtańszym oświetleniem RGB. Przedstawiam Wam opis wykonania, kod oraz schematy elektryczne.

Wstęp

Posiadam 3 żarówki RGB zarządzane za pomocą aplikacji Tuya Smart. Sterowanie odbywa się poza siecią lokalną, za pośrednictwem serwerów producenta. Ogólnodostępne API umożliwia zmianę kolorów, jasności oraz stanu żarówki za pomocą zwykłych zapytań. Schemat działania budzi we mnie pewien niepokój. Urządzenia IoT stanowią w tym przypadku zbyteczny wektor ataku na sieć. Czy potrzebuję kontrolować oświetlenie poza zasięgiem Wi-Fi? Zdecydowanie nie - niemniej, postanowiłem wykorzystać sytuację i oskryptować żarówki ;)

Głównym bodźcem do stworzenia projektu była powolna aplikacja Tuya Smart, która nie wspiera predefiniowanych scen ani gaszenia wszystkich świateł jednocześnie. Postawiłem sobie za cel przyspieszenie zmiany kolorów z kilkunastu do zaledwie jednego kliknięcia. Stworzyłem w dwie godziny prowizoryczną aplikację desktopową opartą o Pythona oraz biblioteki tinytuya i eel.

Kontrolowanie oświetlenia z poziomu desktopowego niestety nie jest poręczne. Aplikacja pełniła swoją rolę wyłącznie gdy korzystałem z laptopa, więc w głównej mierze byłem zdany na toporne Tuya Smart. Wpadłem zatem na pomysł, aby stworzyć fizyczny panel do sterowania światłem :)

Zastosowana architektura

Założeniem projektowym było przesłanie kilku komend do API po naciśnięciu przycisku. Sceny różnią się w zależności od wyboru oraz powinny być łatwo modyfikowalne. Projekt został oparty o wcześniej posiadane komponenty, stąd napotkałem miejscami na ograniczenia.

Do połączenia z siecią wykorzystałem moduł ESP-01 (oparty o SoC ESP8266EX), który umożliwia transfer danych za pośrednictwem Wi-Fi. Na tym kończy się jego rola. Niewielka liczba pinów GPIO uniemożliwia chociażby podłączenie wejścia, czyli klawiatury 4x4. Z tego powodu musiałem wykorzystać również Arduino UNO.

Komunikacja pomiędzy Arduino oraz ESP-01 jest możliwa dzięki wykorzystaniu portów seryjnych. Skrzyżowanie pinów TX/RX sprawia, że urządzenia nawiązują ze sobą transmisję. Po naciśnięciu przycisku Arduino przesyła wybrane przez użytkownika znaki do ESP-01 za pośrednictwem portu seryjnego, a także wyświetla informację zwrotną przez kontrolowanie trzech LEDów.

W celu uproszczenia konfiguracji wykorzystałem wcześniej stworzony skrypt. Przystosowałem jego strukturę oraz napisałem wrapper w PHP umożliwiający zmianę sceny zwykłym GET requestem. Zaimplementowałem proste zabezpieczenie w postaci zhardcodowania hasła zarówno po stronie ESP-01 jak i skryptu PHP. Dla skryptu wydzieliłem domenę genesis13.kaszkowiak.org - niech stanie się światłość ;)

Modyfikacja scen polega na zmianie kodu po stronie serwera, przez co kod sieciowy na ESP-01 nie wymaga zmian. Wymusza to dodatkową zależność w postaci uptime kaszkowiak.org - jest to minimalny koszt w stosunku do zalet.

Programowanie ESP8266 przez Arduino

Pierwszym krokiem do stworzenia projektu jest zaprogramowanie ESP-01. Wykorzystałem do tego połączenie z PC przez Arduino. Po połączeniu pinów TX oraz RX bezpośrednio ze sobą możemy programować moduł za pomocą aplikacji Arduino.

Układ wymaga kilku istotnych komentarzy. Wprowadzenie ESP-01 w tryb programowania wymaga uziemienia pinu GPIO-0. Dodatkowo przed każdym wgraniem programu musimy wprowadzić stan niski na pin RST. W tym celu wykorzystałem rezystor pull-up o impedancji 10k. Naciśnięcie przycisku spowoduje zresetowanie modułu.

Uziemiłem również stałym połączeniem pin RESET w Arduino. Umożliwia to bezpośrednią komunikację pomiędzy komputerem oraz ESP8266.

Układ do zaprogramowania ESP8266
Układ do zaprogramowania ESP8266

Po skonstruowaniu układu możemy zaprogramować moduł ESP-01 za pomocą aplikacji Arduino. W tym celu musimy wejść w ustawienia oraz wkleić następujący URL w pole “Dodatkowe adresy URL do menadżera płytek”. Po zrestartowaniu aplikacji wchodzimy w menu Narzędzia > Płytka > Menadżer płytek oraz instalujemy “esp8266” w wersji 2.5.0. Nowsze wersje posiadają mniej czytelne komunikaty błędów oraz powodują problemy z wgrywaniem kodu wg. dyskusji na GitHubie. Na końcu należy wybrać “Generic ESP8266 Module” w menu Narzędzia > Płytka oraz ustawić wartość Builtin Led na “1”.

Możemy sprawdzić, czy poprawnie skonfigurowaliśmy układ poprzez wgranie aplikacji Blink z menu Plik > Przykłady. Po naciśnięciu przycisku Reset wgranie programu powinno zakończyć się sukcesem, a wbudowana dioda w ESP-01 zacznie mrugać.

Kod źródłowy dla ESP-01

Powyższy program wykorzystuje port szeregowy do komunikacji, więc możemy się z nim komunikować za pośrednictwem Narzędzia > Monitor portu szeregowego. Warto zwrócić uwagę na wybór poprawnej szybkości transmisji, czyli 115200 baud. W skrócie: program czeka za otrzymaniem dużej litery bądź cyfry. Otrzymany znak następnie jest dopisywany do zdefiniowanego adresu URL jako parametr. ESP-01 wykonuje zapytanie GET, a następnie zwraca status połączenia w postaci pierwszej litery koloru (Red/Yellow/Green).

Po zaprogramowaniu ESP-01 możemy zająć się programowaniem Arduino - w tym wypadku wystarczy zwykłe połączenie Arduino <-> PC bez komunikacji z ESP-01. Transmisja odbywa się po porcie szeregowym, więc możemy ręcznie symulować input pochodzący z modułu.

Kod źródłowy dla Arduino

W tym przypadku Arduino przetwarza input z podłączonego keypada 4x4, przesyła go przez port seryjny oraz zapala LEDy w oparciu o przychodzące sygnały R/G/Y. Sterowanie LEDami odbywa się asynchronicznie, aby nie pominąć przychodzących danych.

Po zaprogramowaniu Arduino możemy przejść do połączenia wszystkich komponentów.

Schemat kompletnego projektu

Schemat kompletnego projektu
Schemat kompletnego projektu

Warto zwrócić uwage na drobne różnice w stosunku do układu programującego ESP-01. Przede wszystkim - usunąłem przycisk RESET, aby przypadkowo nie wyczyścić wgranego programu. Odłączyłem również uziemienie pinu RESET w Arduino oraz pinu GPIO0 w ESP-01. Piny TX/RX są skrzyżowane, przez co output (TX) Arduino trafia na input (RX) ESP-01.

Po zasileniu układu momentalnie powinien zapalić się czerwony LED, a po kilku sekundach wszystkie kolory.

Przetwarzanie zapytania na serwerze

Kod źródłowy - PHP

Rola skryptu PHP przetwarzającego zapytanie to wyłącznie autoryzacja na podstawie wbudowanego hasła oraz przekazanie wybranej sceny do skryptu tuyalights. Scena może przyjąć jedynie wartość numeryczną, aby w prosty sposób zapobiec RCE.

Kod działa poprawnie, jednak należy zauważyć, że typ żądania GET jest niewłaściwie użyty - POST z reguły zadziałałby lepiej. W tym konkretnym przypadku nie powinno to wyrządzić żadnej szkody, ale jako przykład - przeglądarki mogą samoistnie duplikować lub preloadować żądania GET (Safari z tego słynie). Nie chcielibyśmy, aby nasze światła zmieniały się losowo, prawda? ;)

Kod źródłowy - Python 3

Sceny są zdefiniowane oraz przypisane do identyfikatorów w skrypcie tuyalights. Na ich podstawie powstaje przesyłane zapytanie do API. Drobny disclaimer - skrypt został zlepiony na podstawie istniejącego kodu, można go ulepszyć poprzez czytanie scen oraz sekretów API z zewnętrznego źródła danych.

W moim deploymencie umieściłem plik w katalogu /usr/bin znajdującym się w zmiennej PATH, aby wywołanie skryptu nie wymagało podania bezpośredniej ścieżki. Pominięcie tego kroku może wywołać błąd w skrypcie PHP. Nie zalecam umieszczać pliku w tym samym katalogu co index.php, ponieważ bez uprzedniej konfiguracji serwera skrypt stanie się możliwy do pobrania - a co za tym idzie wyciekną klucze API oraz ID urządzeń.

Skrypt wymaga zainstalowanej biblioteki tinytuya (python3 -m pip install tinytuya). Na koniec dodam, że w jej dokumentacji został opisany sposób otrzymania kluczy oraz ID urządzeń.

Zapraszam również na mojego Githuba, na którym umieściłem pełen kod projektu włącznie z schematami w formacie PDF.

Możesz śledzić mój blog za pomocą dowolnego RSS czytnika RSS!

Bloguję o projektach, sporcie i programowaniu - zobacz wszystkie posty.

Miniaturka

Embeddingi w pigułce

Czym są embeddingi, z których prawdopodobnie korzystasz na codzień? Dowiedz się w artykule - wiele buzzwordów przestanie być czarną magią 🧙