Wpis z mikrobloga

Tak jak obiecałem wrzucam kolejny wpis dotyczący budowy mojego boxa :) W dzisiejszym wpisie napiszemy sobie prosty skrypt w Pythonie który obsłuży nam gaszenie i zapalanie lampy w godzinach które podamy przy inicjowaniu programu.

Na obrazku w komentarzu 1 można zobaczyć jak hardware'owo jest podłączona lampa do Raspberry Pi. Nie będę we wpisie wchodził w detale ale jeśli są jakieś pytania to zapraszam do zadawania na pw / w komentarzach.

Zacznijmy zatem programowanie ( ͡° ͜ʖ ͡°) na wstępie uprzedzam - jest to mój pierwszy skrypt w Py więc jestem mocno otwarty na wszelką konstruktywną krytykę.

Pierwsze założenie mojego skryptu to sterowanie przekaźnikiem prądowym przez port GPIO więc potrzebna będzie libka do Pythona - RPi.GPIO która nam to umożliwi.
Drugie założenie - chcę aby program przyjmował przy inicjowaniu czas zapalenia i zgaszenia lampy jako dwa stringi o formacie "HH:MM"
Zatem stworzyłem sobie prostą klasę LightManager która przy inicjowaniu obiektu tego typu przyjmuje adres portu GPIO do którego podłączony jest input przekaźnika, godzinę zapalenia lampy, oraz godzinę o której ta ma być zgaszona. Adres portu od razu przypisuję do zmiennej lokalnej jednak zanim to samo zrobię z podanymi godzinami, muszę je pierw dostosować do typu który będzie mi nieco bardziej potrzebny, a dlaczego dowiemy się czytając dalej ten wpis. Tak więc podane łańcuchy dzielę po znaku ":" i rzutuję na typ int, dopiero dane typu int przypisuje do zmiennych.Musimy też powiedzieć naszej bibliotece GPIO, że podany adres portu jest w standarcie BCM (patrz https://www.pinout.xyz).

Dwie podstawowe funkcje tego obiektu to zapalanie i gaszenie światła (no shit sherlock!) dlatego zdefiniowałem metody turn_light_on() i turn_light_off(). Podstawowe założenie:
metoda odpowiadająca za włączenie światła ma dać sygnał wysoki na zadany port GPIO, metoda gasząca światło adekwatnie ma dać sygnał niski na zadany port.

No dobrze, wywołałem metodę od zapalania i moja lampa się zapaliła ale co dalej? Przecież sama nie zgaśnie. A no nie, dlatego teraz musimy policzyć za ile powinna zgasnąć i odwlec te operację w czasie o ten interwał czasowy.

Jak policzyć ten interwał? Potrzebujemy w tym celu zaimportować kolejną libkę - datetime. Stworzyłem dwie pomocnicze metody które nazwałem get_turn_off_date() i get_turn_on_date(). Ich zadanie jest proste - podstawić pod dzisiejszą / jutrzejszą datę podane przez użytkownika godziny i taką spreparowaną datę zwrócić jako wynik. To dlatego wcześniej zrzutowaliśmy nasze dane na typ integer, po prostu ten typ jest wymagany przy podstawianiu do daty. Na obiektach typu datetime możemy przeprowadzić operację odejmowania która zwróci nam różnicę między tymi dwoma datami. Ta różnica będzie nam potrzebna w sekundach więc dodatkowo po operacji odejmowania wykonujemy .total_seconds(). Ta różnica może jednak wyjść ujemna, w sytuacji gdzie będziemy odejmować przyszłą datę od teraźniejszej, tak więc musimy wyciągnąć z niej wartość bezwzględną. W Pythonie służy do tego wbudowana operacja abs(x). Obliczenia tego interwału możemy wydzielić do kolejnych funkcji pomocniczych get_turn_on_interval() i get_turn_off_interval().

Skoro już mamy ten interwał czasowy teraz musimy zaplanować kolejny krok. Do "odwlekania" operacji w Pythonie służy wbudowana klasa Timer z biblioteki threading, którą sobie importujemy. Po zapaleniu światła tworzymy sobie obiekt typu Timer, podajemy mu wyliczony wcześniej interwał i mówimy, że po tym czasie ma wywołać metodę turn_light_off() (i odpowiednio w drugą stronę przy gaszeniu). Pamiętamy aby na obiekcie Timer wywołać funkcję start(). Teraz nasz skrypt działa rekurencyjnie (tj. metoda a odwołuje się do b, i b odwołuje się do a co tworzy nieskończoną pętlę odwoływań).

Ok, ale co jeśli uruchomię program w momencie kiedy lampa powinna być włączona?
W tym celu stworzyłem dwie nowe metody:
- should_be_turned_on_now() która zwraca nam boola mówiącego czy obecna godzina mieści się w zakresie kiedy lampa powinna być włączona
oraz
- run() którą wywołujemy na dopiero co stworzonym obiekcie naszego typu aby ten zaczął swoją prace. W tej metodzie wywołujemy w/w metodę i jeśli lampa powinna być włączona wywołujemy metodę turn_light_on(), w przeciwnym wypadku gasimy lampę. I jedna i druga operacja skutkuję wejściem w naszą rekurencję.

Teraz tworzymy drugi prosty skrypt w Pythonie, (nazwijmy go main.py) który będzie zawierał odwołanie do naszej klasy LightManager. Stworzymy sobie obiekt tego typu i podamy mu godziny - dla przykładu 5:00 i 21:00, a port niech będzie 4 tak jak na schemacie z komentarza. Na stworzonej instancji naszej klasy wywołujemy metodę run(). I voila!

Jeszcze jako smaczek - aby nasz program uruchomił się automatycznie po tym jak zrobimy reboot malinki edytujemy plik /etc/rc.local. Dodajemy do niego linijkę uruchomiającą nasz program czyli

sudo python /home/pi/main.py &

na końcu linii widnieje znak ampersand który informuje o tym, że nasz program będzie działał w niekończącej się pętli więc system nie oczekuje jego zakończenia.

Link do githuba

#papryczki #hobby #borgonbox #raspberrypi #python #programowanie
  • 3
@Egribikaver: a co ile miałby odczytywać ten czas? :D co sekundę? minutę? stwierdziłem, że optymalniej wyliczyć raz interwał i zaplanować operację. Do czasu jej wykonania nie zużywam w ogóle zasobów a tak to czytając godzinę dajmy na to co minutę musiałbym wykonać operację porównania.