
Przeznaczenie i zasada działania
Istnieją biblioteki do Arduino służące do sterowania urządzeniami poprzez IR. Postanowiłem jednak rozwiązać to w inny sposób – chciałem stworzyć układ uczący się, tzn. zapamiętujący kody z oryginalnego pilota. Umożliwia to sterowanie całym szeregiem urządzeń typu klimatyzatory, łańcuch świetlne, kule dyskotekowe itp. Głównym zadaniem układu miała być praca na zasadzie włącz/wyłącz, ale oczywiście można nagrywać i odtwarzać dowolne sekwencje impulsów i tym samym symulować naciśnięcie dowolnego przycisku pilota lub sekwencji naciśnięć. Układ ma także dodatkową funkcjonalność – sygnalizację, czy urządzenie jest załączone, czy też nie. Czasami jesteśmy daleko od sterowanego układu i chcemy mieć pewność, że np. klimatyzator został wyłączony. Przy okazji tworzenia oprogramowania na Arduino postanowiłem użyć chat GPT do stworzenia części kodu. Co z tego wynikło przeczytacie dalej.
Trochę historii, czyli dlaczego powstał układ?
W domu mam kominek i DGP. Postanowiłem dorobić DZP czyli Dystrybucję Zimnego Powietrza. W tym celu na strych dałem klimatyzator pokojowy typu monoblok, który podłączyłem do wentylatora kominkowego oraz dodatkowego wentylatora tłoczącego powietrze do pokoi. Układ był sterowany pilotem, ale przecież nie będę wchodził na strych za każdym razem, gdy trzeba wyłączyć lub załączyć klimatyzator. Należało stworzyć jakiś przedłużacz. Pierwsza wersja była przewodowa (odbiornik TSOP oraz generator 38 kHz. Działało to dobrze, ale po zmianie umeblowania bardzo brzydko wyglądał kabel łączący odbiornik z nadajnikiem. Kolejne rozwiązanie opierało się o gotowy przedłużacz radiowy na 433 MHz (taki w kształcie UFO). Nie działał on perfekcyjnie – czasami były kłopoty z wyłączeniem klimatyzatora. Jednak największym problemem było samoczynne załączanie klimatyzacji (na całe szczęście w trybie wentylacji). Winny był odbiornik umieszczony na strychu, który łapał olbrzymią liczbę sygnałów radiowych i bez przerwy coś nadawał w podczerwieni. Pozbyłem się wiec tego układu i zastąpiłem go prezentowanym rozwiązaniem – przesyłanie komend pilota przez Wi-Fi, a w połączeniu z resztą infrastruktury inteligentnego domu – możliwość sterowania z dowolnego miejsca z dostępem do internetu.
Budowa układu
Ponieważ wszystko robię na ESP8266 i Arduino, wybór platformy był oczywisty. Pozostało stworzyć taki układ, a w zasadzie dwa układy – jeden do uczenia (odczytu), a drugi do nadawania. Myślałem o samodzielnej budowie, albo o przeróbce SONOFF Basic. Znalazłem jednak taki moduł:

Ma on w sobie zarówno odbiornik jak i nadajnik i to połączony z ESP8266. Piny konieczne do programowania są łatwo dostępne i dobrze opisane:

Zdecydowałem się zatem na zakup takiego modułu. Kupiłem 3 sztuki, gdyż chciałem jeden układ przerobić na czytnik, a drugi na nadajnik (trzeci to zapas). Później zmieniłem koncepcję i każdy moduł może pracować zarówno w trybie odbiornika, jak i nadajnika. Układ nie jest idealny – brakuje przede wszystkim diody pracującej w świetle widzialnym. Taka dioda okazuje się bardzo przydatna przy testowaniu – można migać w self teście i mrugać podczas nadawania. Po prostu wiadomo co się dzieje. Było to dla mnie bardzo istotne, gdyż po pierwszym programowaniu „z kabelków” od razu przechodzę na OTA i nie bawię się RX,TX, dlatego mój układ został doposażony w taką diodę:


Użyłem czerwonej „jasnej” diody 3 mm w przezroczystej obudowie połączonej rezystorem 220R z ESP. Polecam takie diody – świecą jasno już przy prądzie 1-2 mA, a bezbarwna obudowa nie zostawia wątpliwości, czy w danym momencie dioda świeci, czy nie.
Odczyt klawiszy pilotów
Odczytane sekwencje z przycisków pilotów należało gdzieś zapamiętać. Postanowiłem, że „bazą danych” będzie pojedynczy plik json, w którym będą zapisane poszczególne piloty, a w każdym pilocie – przyciski. Wygląda to mniej więcej tak (sekwencje liczb zostały skrócone w celu prezentacji):
Code: json
Każdy pilot (identyfikowany przez nazwę) ma dwa parametry globalne: częstotliwość (w Hz) oraz wypełnienie (w %) oraz oczywiście listę przycisków z czasami. Jednostką czasu jest 10 µs, czyli liczba 290 odpowiada 2,9 ms. Liczby nie mogą być większe niż 16 bit bez znaku, czyli ok. 655 ms. Pierwsza liczba to czas sygnału, kolejna - czas przerwy i tak dalej. Ostatnia liczba zawsze jest czasem sygnału. Samą sekwencję liczb (jako 16 bitowe dane binarne) dostarcza ESP. Powstał problem jak dopisać to do „bazy”. W tym celu powstało oprogramowanie pomocnicze w postaci strony WWW i skryptu PHP:
Plik "przycisk.htm":
Code: html
przycisk.php:
Code: php
Strona i skrypt umożliwiają wygodne zapisanie niezbędnych danych w tymczasowym pliku irs.json. Plik ten jest interpretowany przez skrypt PHP odbierający dane z ESP, a następnie jest kasowany. Sekwencja programowania jest zatem następująca: wprowadzamy nazwę pilota, nazwę przycisku i naciskamy „Wyślij”. Teraz kierujemy pilota w stronę odbiornika TSOP i naciskamy właściwy przycisk pilota. To dosyć prosta sekwencja umożliwiająca wygodne zaprogramowanie dużej liczby przycisków. Nazwy pilotów i przycisków najlepiej nadawać z użyciem wyłącznie łacińskich liter i cyfr – chodzi o to, aby nie było kłopotu przy wysyłaniu pilota oraz kodu klawisza poprzez zapytania GET.
Tu drobna uwaga – w wielu miejscach kodu (zarówno PHP jak i C) będzie występować sekwencja: „192.168” Chodzi o to, że skrypty PHP działają u mnie na serwerze dostępnym także z zewnątrz i muszę się zabezpieczać przed ich wywołaniem spoza sieci domowej.
Kod Arduino
Code: arduino
Kod niestety trochę się rozrósł. Wynika to z faktu, że zaszyta jest w nim zarówno funkcjonalność odczytu jak i zapisu. Urządzenie wybiera właściwy tryb na podstawie zworki. Jeśli piny RX i TX są zwarte w momencie startu, to układ pracuje jako odbiornik, a jeśli są rozwarte, to działa jako nadajnik. Po uruchomieniu nie można zmienić trybu. Kod odpowiedzialny za odczyt jest stosunkowo prosty – mierzone są czasy nadawania (niski stan na wyjściu odbiornika IR) oraz czas przerwy. Jeśli przerwa trwa dłużej niż 0,5 s proces odczytu jest przerywany, a cała sekwencja jest wysyłana do skryptu IR.php, który zapisuje ją w bazie (pliku json).
Działanie w drugą stronę polega na odbiorze sekwencji liczb będących kodem przycisku pilota. Jak widać moduły nadawcze wysyłają w postaci błysków IR to co otrzymały i nie mają żadnej pamięci jeśli chodzi o sekwencję sterowania diodą IR. Takie sterowanie postanowiłem zrobić w oparciu o serwer WWW na ESP. Ponieważ jednak wcześniej nie robiłem serwera na ESP z odczytem danych przesyłanych metodą POST, postanowiłem się wspomóc chatGPT.
Przygody z chatGPT
Moja prośba o pomoc:
"Podaj kod Arduino dla ESP8266 do utworzenia serwera WWW odbierającego binarne dane metodą POST"
Odpowiedź:
Code: arduino
Muszę przyznać, że wygląda całkiem sensownie. Dodałem kolejny warunek:
Serwer musi jeszcze kontrolować, czy połączenia są z sieci lokalnej 192.168.x.x. Połączenia spoza sieci lokalnej powinny być odrzucane.
Code: arduino
Od razu widoczny jest błąd – dane będą odbierane z dowolnego IP tylko nie własnego. Upomniałem chata, który jak zwykle przeprosił i zaproponował poprawiony kod:
Code: arduino
Zatem, czy kod zaproponowany przez bota działa? Niestety nie. Znalezienie błędów zajęło mi trochę czasu i musiałem się posiłkować tradycyjnie Google.
Pierwszy problem – odczyt nagłówków. Aby był możliwy należy zadeklarować je wcześniej:
Code: arduino
Bez tej deklaracji po prostu nie działa odczyt zawartości nagłówków. Po naprawie tego błędu oprogramowanie jednak nadal nie działało. Zaproponowana metoda odczytu strumienia zawsze zwracała pusty ciąg. Aby odczytać surowe dane odebrane metodą POST należy użyć:
server.arg("plain")
Bardzo dziwna sekwencja – odczyt argumentu „plain”, którego po prostu nie ma. Nie dziwię się, że bot na to nie wpadł 😊
Generalnie chatGPT pomoże nam, jeśli potrafimy szybko zweryfikować poprawność wygenerowanego kodu. Jeśli jednak czegoś nie wiemy, to możemy długo szukać błędów, a osoba, która nie zna się na programowaniu po prostu ich nie znajdzie.
Taka drobna dygresja – na stronie stackoverflow.com (chyba najlepsza strona dla programistów) pojawił się zapis „Używanie tekstu generowanego przez ChatGPT w treściach na Stack Overflow jest tymczasowo zabronione”
I wyjaśnienie „Podstawowy problem polega na tym, że chociaż odpowiedzi generowane przez ChatGPT mają wysoki wskaźnik niepoprawności, zazwyczaj wyglądają na dobre”. Tu link do oryginału
Nic dodać, nic ująć – dokładnie tak było w moim przypadku.
Powiadamianie o stanie kontrolowanego urządzenia
Pin RX (STANWE) służy dodatkowo do detekcji stanu sterowanego urządzenia. Stanem aktywnym jest stan niski. Można to rozwiązać na wiele sposobów – pin ten możemy zwierać do masy za pomocą transoptora, gdzie dioda transoptora będzie podłączona gdzieś do sterowanego urządzenia (np. elektroniki klimatyzatora, wyjścia USB telewizora itp.) Najprostszym jednak rozwiązaniem jest dodanie fotorezystora, który jest oświetlany diodą Power urządzenia. Należy jednak pamiętać, że stan niski musi być odpowiednio rozpoznany przez ESP, a wewnętrzny rezystor podciągający ma stosunkowo małą wartość (poniżej 50 kΩ). Fotorezystor podłączony do masy nie „zbije” wystarczająco napięcia. Rozwiązaniem jest odkomentowanie linii pinMode(STANWE, INPUT); i stworzenie własnego podciągania do napięcia zasilania rezystorem o większej wartości. Oczywiście zamiast pinu RX możemy użyć inny wolny lub podłączyć się do wejścia analogowego ESP.
ESP raportuje stan co 30 s i dodatkowo natychmiast po zmianie (wyłączeniu i załączeniu). Skrypt IR.php zapisuje stan w pliku logu (dodaje wpisy), a dodatkowo ostatni stan jest zapisany wraz z sygnaturą czasową w oddzielnym pliku. Ten plik jest używany później przez przeglądarkę do wyświetlania stanu urządzenia (załączony/wyłączony).
Poniżej skrypt IR.php przyjmujący dane z ESP i zapisujący je w bazie:
Code: php
Skrypt IR.php jest swojego rodzaju interfejsem pomiędzy układami, a „światem zewnętrznym”. Poniżej opis jego wykorzystania w testowym pilocie oraz bezpośrednio w zapytaniu GET.
Testowy „pilot”
Code: php
Skrypt działa jako demonstracyjny pilot. Możemy wysłać dowolny kod przycisku do dowolnego ESP. Funkcja wyślij() pokazuje jak wysłać kod. Jednym z parametrów jest nrir – to nazwa konkretnego układu i skompilowanego dla niego kodu. W przedstawionym przykładzie jest to „IRX1” (z APname).
Jeśli mamy kilka układów, to musimy skompilować kod wielokrotnie za każdym razem zmieniając APname na IRX2, IRX3 itd. U mnie każdy układ przy starcie pyta się o aktualizację OTA, ale to zapytanie odnotowuje także APname i IP po stronie serwera. Dlatego zawsze wiem pod jakim IP jest w danym momencie konkretny układ:
Code: php
Wszystko to można zmienić – wystarczy po prostu nadać układom konkretne stałe IP.
Polecenia pilota można także wysyłać za pomocą GET np.:
http://192.168.1.20/IR.php?akcja=IRget&nrir=IRX1&remote=TV-SONY&button=Power
Podsumowanie
Zaproponowane rozwiązanie jest bardzo uniwersalne. Umożliwia sterowanie wieloma urządzeniami przez wiele nadajników IR. Kody można wgrywać do bazy także po uruchomieniu całości. Każdy z nadajników może wysyłać dowolną sekwencję i nie wymaga w tym celu przeprogramowania. Niestety taka uniwersalność skomplikowała rozwiązanie pod kątem prezentacji na elektrodzie. Na koniec kilka uwag odnoście prezentowanego układu nadajnika/odbiornika. Otóż jeszcze przed zakupem zauważyłem, że dioda IR jest sterowana tranzystorem bez jakiegokolwiek rezystora ograniczającego prąd. Jest to o tyle ciekawe, że całość zasilana jest ze stabilizatora mogącego dać nawet 1 A, co jest stanowczo za dużo jak na diodę IR. Układ jednak działa, a dioda się nie spaliła. Prawdopodobnie za ogranicznik prądu robi tranzystor, którego beta mocno spada wraz z prądem, co przy rezystorze 1 kΩ na bazie widocznie wystarcza. Układ ma jeszcze jedną wadę – umieszczenie diody IR. Praktycznie nie da się tego zabudować (dioda jest niższa niż płytka z ESP). Należy zatem rozwiązać to jakoś inaczej – albo diodę przelutować na drugą stronę, albo wyprowadzić ją na przewodzie. W każdym razie jej oryginalne miejsce jest niefortunne.
Cool? Ranking DIY