Skocz do zawartości

Przeszukaj forum

Pokazywanie wyników dla tagów 'Apache'.

  • Szukaj wg tagów

    Wpisz tagi, oddzielając przecinkami.
  • Szukaj wg autora

Typ zawartości


Kategorie forum

  • Elektronika i programowanie
    • Elektronika
    • Arduino i ESP
    • Mikrokontrolery
    • Raspberry Pi
    • Inne komputery jednopłytkowe
    • Układy programowalne
    • Programowanie
    • Zasilanie
  • Artykuły, projekty, DIY
    • Artykuły redakcji (blog)
    • Artykuły użytkowników
    • Projekty - DIY
    • Projekty - DIY roboty
    • Projekty - DIY (mini)
    • Projekty - DIY (początkujący)
    • Projekty - DIY w budowie (worklogi)
    • Wiadomości
  • Pozostałe
    • Oprogramowanie CAD
    • Druk 3D
    • Napędy
    • Mechanika
    • Zawody/Konkursy/Wydarzenia
    • Sprzedam/Kupię/Zamienię/Praca
    • Inne
  • Ogólne
    • Ogłoszenia organizacyjne
    • Dyskusje o FORBOT.pl
    • Na luzie

Kategorie

  • Quizy o elektronice
  • Quizy do kursu elektroniki I
  • Quizy do kursu elektroniki II
  • Quizy do kursów Arduino
  • Quizy do kursu STM32L4
  • Quizy do pozostałych kursów

Szukaj wyników w...

Znajdź wyniki, które zawierają...


Data utworzenia

  • Rozpocznij

    Koniec


Ostatnia aktualizacja

  • Rozpocznij

    Koniec


Filtruj po ilości...

Data dołączenia

  • Rozpocznij

    Koniec


Grupa


Imię


Strona

Znaleziono 3 wyniki

  1. Na początku jedna uwaga: ten cykl artykułów nie jest podręcznikiem Pythona ani nie rości sobie praw do bycia takim. Używam w przykładach Pythona jako najbardziej popularnego języka programowania na RPi - ale równie dobrze mógłbym użyć Perla, C++ czy nawet Basha, a cykl jest po prostu "boczną odnogą" napisanego wcześniej cyklu o protokole HTTP. Dlatego też wiele kwestii zostało pominiętych i użytkownik musi większość problemów rozwiązać we własnym zakresie (czyli najprawdopodobniej znaleźć właściwe rozwiązanie w innym artykule). Ale przejdźmy do rzeczy. W poprzednim odcinku dowiedzieliśmy się, jak uruchomić z poziomu serwera WWW skrypty pisane w dowolnym języku, i jak pozwolić im na komunikację z GPIO i magistralami SPI/I2C na przykładzie odczytu danych z czytnika RFID. Dzisiaj spróbujemy komunikacji w drugą stronę: z poziomu przeglądarki spróbujemy wysłać coś na magistralę. Ale najpierw trochę teorii. Z artykułu o protokole HTTP (jeśli ktoś go nie przeczytał, to teraz jest najwyższy czas) mogliśmy dowiedzieć się, jak jest zbudowany URL. Widzimy tam, że w najprostszej postaci URL wygląda tak: http://<host><ścieżka>[?<zapytanie>] Możemy się domyślać, że host informuje przeglądarkę (i serwer) z jakim serwerem i jaką stroną ma się połączyć, ścieżka wskazuje na plik który mamy z serwera odczytać (lub - w przypadku skryptu - serwer ma go wykonać i przekazać nam wyniki), ale czym jest to całe zapytanie? Otóż są to dodatkowe dane przekazywane od klienta (czyli w naszym przypadku przeglądarki) do skryptu. Jak uprzednio wspominałem, format zapytania jest dowolny i zależy praktycznie od tego co potrafi klient i serwer, ale w praktyce używa się pewnej ustalonej składni: nazwa=wartość z możliwością przekazania większej ilości parametrów, oddzielając je znakami '&', czyli coś w rodzaju: nazwa1=wartość1&nazwa2=wartość2&nazwa3=wartość3 Zarówno nazwa jak i wartość mają swoje ograniczenia - mogą zawierać wyłącznie drukowalne siedmiobitowe znaki ASCII bez spacji (czyli z zakresu od 0x21 do 0x7E włącznie) z wyłączeniem niektórych znaków zastrzeżonych (np. trudno by było zastosować nazwę zawierającą w sobie znak równości, czy też umieścić znak '&' wewnątrz wartości). Dlatego też opracowano pewien sposób kodowania (tzw. "urlencode"), pozwalający na przekazanie dowolnych danych. Sposób jest bardzo prosty: znak spacji zamieniamy na '+' znaki które nie kolidują ze składnią zapytania pozostawiamy bez zmian pozostałe znaki (oraz znak + występujący w wartości) zamieniamy na ciąg %XX, gdzie 'XX' to szesnastkowa reprezentacja znaku. Tak więc np. ciąg znaków 'ala+ma kota' zostanie zakodowany jako: ala%2Bma+kota Oczywiście nie ma żadnych innych ograniczeń (poza oczywistymi, wynikającymi z maksymalnej długości URL-a którą może przyjąć serwer oraz takim, że nazwa nie może mieć aerowej długości). W szczególności przesłanie kilku wartości (np. informacji o zaznaczonych kilku możliwościach wyboru) nie wymaga jakichś specjalnych zabiegów, po prostu wystarczy zapytanie w stylu: nazwa=wybór1&nazwa=wybór3&nazwa=wybór7 O, i tu słyszę głosy programistów PHP - jak to? Przecież aby przesłać kilka wartości nazwa musi kończyć się znakami '[]' wskazującymi na tablicę, a dopiero wtedy w tablicy $_GET w pozycji wskazywanej przez 'nazwa[]' znajdzie się nie pojedyncza wartość, a tablica wartości! Niestety - dotyczy to wyłącznie PHP i jest to zaszłość historyczna (pochodzi z czasów, gdy nikt z twórców PHP nie wpadł na to że można przesyłać więcej niż jedną wartość; w związku z tym trzeba było dorobić na szybko jakąś protezę - i to taką, która nie wysadzi w kosmos wszystkich dotychczas napisanych skryptów w tym języku). No - ale dość już teoretyzowania, przejdźmy do praktyki. Serwer mamy już skonfigurowany, spróbujmy na początek napisać prosty program, wpisujący otrzymane zapytanie do pliku/ Utwórzmy więc w katalogu html plik "napis.cgi" z następującą zawartością: #!/usr/bin/env python3 import cgitb, os print("Content-Type: text/plain; charset=UTF-8\n") cgitb.enable(format="text") napis=os.environ.get("QUERY_STRING") if not napis: print ("A gdzie zapytanie?") else: open("dane.txt","w").write(napis) print ("Zapisałem:", napis) Po zmianie uprawnień na 0755 i wpisaniu w przeglądarkę np: http://<ip_malinki>/napis.cgi?Hello powinna ukazać się odpowiedź "Zapisane: Hello", a w pliku html/dane.txt znajdzie się otrzymany napis Dobra - ale przecież wspominałem o jakichś nazwach, wartościach, urlencode... a tymczasem przy próbie wpisania: http://<ip_malinki>/napis.cgi?Cześć, świecie pokazuje się zakodowana treść zapytania, i taka też ląduje w pliku! Powód jest prosty: Python - w przeciwieństwie do PHP - nie ma żadnych superglobali ani innych automagicznych zaklęć będących częścią języka i zajmujących się dekodowaniem tego, co przysłał serwer. To tego służy biblioteka o nazwie - jak łatwo zgadnąć - cgi. Do pobierania i udostępniania w programie parametrów przesłanych do skryptu służy klasa FieldStorage. Zainteresowanych odsyłam do dokumentacji, nas będą interesować jedynie dwie metody: getlist(name) zwracająca listę wszystkich wartości związanych z daną nazwą oraz getfirst(name, default=None) zwracająca pierwszą napotkaną wartość związaną z daną nazwą lub wartość określoną przez parametr default (domyślnie None), jeśli dana nazwa nie wystąpiła w zapytaniu. Przeróbmy nieco nasz skrypt - niech napisem będzie wartość skojarzona z nazwą "tresc": #!/usr/bin/env python3 import cgitb, cgi print("Content-Type: text/plain; charset=UTF-8\n") cgitb.enable(format="text") form = cgi.FieldStorage() napis = form.getfirst("tresc", '').strip() if not napis: print ("A gdzie treść?") else: open("dane.txt","w").write(napis) print ("Zapisałem:", napis) Teraz wysłanie zapytania typu: http://<ip_malinki>/napis.cgi?tresc=Cześć, świecie spowoduje pojawienie się prawidłowej odpowiedzi oraz zapisanie prawidłowego tekstu do pliku. Ponieważ wpisywanie tego typu poleceń do pola adresu przeglądarki nie jest specjalnie wygodne, możemy zrobić prosty formularz, z którego dane będziemy przesyłać do naszego skryptu: #!/usr/bin/env python3 import cgitb, cgi, html print("Content-Type: text/html; charset=UTF-8\n") cgitb.enable(format="html") form = cgi.FieldStorage() napis = form.getfirst("tresc", '').strip() wynik="""<!DOCTYPE html> <html> <head> <title>Prosty formularz</title> </head> <body> %s <form method="post"> <input type="text" name="tresc" value="%s"> <input type="submit"> </form> </body> </html> """ if not napis: komunikat = "A gdzie treść?" else: open("dane.txt","w").write(napis) komunikat = "Zapisałem: "+ napis print(wynik % (html.escape(komunikat), html.escape(napis, True))) Teraz mamy już prawdziwy formularz. Spróbujmy więc przekazać dane do jakiegoś urządzenia podłączonego do naszej malinki. Jako przykład wybrałem popularny wyświetlacz tekstowy LCD 16x2 z interfejsem I2C. Połączenie jest w tym przypadku dużo prostsze, niż było to przy czytniku RFID. Interfejs I2C wymaga podłączenia tylko dwóch przewodów poza zasilaniem. Pamiętać należy, że wyświetlacz wymaga zasilania 5V! A więc układ połączeń jest taki: Pin 2 (+5V) do VCC wyświetlacza Pin 6 (GND) do GND wyświetlacza Pin 3 (SDA1) do SDA wyświetlacza pin 5 (SCL1) do SCL wyświetlacza Spróbujmy najpierw uruchomić wyświetlacz. Ciekawy artykuł na ten temat można znaleźć w serwisie Circuit Basics, ale dla niecierpliwych przygotowałem krótki sposób uruchomienia. Przede wszystkim potrzebny będzie moduł obsługi szyny I2C: sudo apt install python3-smbus Niestety - programu obsługi wyświetlacza w repozytorium nie ma. Na szczęście wspomniany artykuł pokazuje prosty, niewielki moduł który to umożliwia. Moduł wraz z programem demonstracyjnym można ściągnąć z githuba, na wszelki wypadek sam plik RPi_I2C_driver.py dodałem do załącznika. Plik ten należy umieścić w naszym katalogu html wraz z programem. Sprawdźmy najpierw, czy nasz wyświetlacz działa. Napiszemy krótki program (umieśćmy go przykładowo w pliku nasz_program.py): #!/usr/bin/env python3 import RPi_I2C_driver lcd=RPi_I2C_driver.lcd() lcd.lcd_clear() lcd.lcd_display_string_pos("Witaj swiecie!!!",1,0) Po uruchomieniu poleceniem: python3 nasz_program.py wyświetlacz powinien wyświetlić tekst "Witaj swiecie!!!". Co się jednak stanie, jeśli wyświetlacz nie będzie potrafił wyświetlić zadanego napisu? Przeróbmy nieco nasz program, aby można było podawać mu bezpośrednio napis: #!/usr/bin/env python3 import RPi_I2C_driver, sys lcd=RPi_I2C_driver.lcd() lcd.lcd_clear() napis = ' '.join(sys.argv[1:]) lcd.lcd_display_string_pos(napis,1,0) Jeśli teraz wywołamy go z parametrem: python3 nasz_program.py żółć zobaczymy na wyświetlaczu coś, co w żadnym przypadku z żółcią nie ma nic wspólnego... Jak to rozwiązać? Można oczywiście stworzyć własne znaczki i je wyświetlać w miejsce brakujących. Jest to jednak nieco żmudne, poza tym w języku polskim występuje 18 dodatkowych liter, a mamy do dyspozycji tylko 8 możliwości. Najprostszym rozwiązaniem będzie takie przekształcenie tekstu, aby mógł być bez problemu pokazany nawet na wyświetlaczach nie pozwalających na wprowadzanie dodatkowych znaków. Do tego posłuży nam moduł unidecode. Zaczynamy od instalacji, czyli: sudo apt install python3-unidecode Moduł zawiera jedną tylko interesującą nas funkcję unidecode, tak więc nasz program będzie teraz wyglądać następująco: #!/usr/bin/env python3 import RPi_I2C_driver, sys from unidecode import unidecode lcd=RPi_I2C_driver.lcd() lcd.lcd_clear() napis = unidecode(' '.join(sys.argv[1:])) lcd.lcd_display_string_pos(napis,1,0) Po uruchomieniu możemy zobaczyć, że program tym razem zamiast krzaków wyświetla po prostu literki pozbawione znaków diakrytycznych. Teraz możemy już przerobić nasz skrypt tak, aby wpisywane wartości zamiast do pliku wypisywane były na wyświetlaczu: #!/usr/bin/env python3 import cgitb, cgi, html, RPi_I2C_driver from unidecode import unidecode print("Content-Type: text/html; charset=UTF-8\n") cgitb.enable(format="html") form = cgi.FieldStorage() napis = form.getfirst("tresc", '').strip() wynik="""<!DOCTYPE html> <html> <head> <title>Prosty formularz</title> </head> <body> %s <form method="post"> <input type="text" name="tresc" value="%s"> <input type="submit"> </form> </body> </html> """ if not napis: komunikat = "A gdzie treść?" else: napis2 = unidecode(napis) lcd=RPi_I2C_driver.lcd() lcd.lcd_clear() lcd.lcd_display_string_pos(napis2,1,0) komunikat = "Wyświetlam: "+ napis2 print(wynik % (html.escape(komunikat), html.escape(napis, True))) Jak widać, nasz skrypt radzi sobie bez problemu nawet z takimi alfabetami jak chiński czy koreański! Tyle na dziś - następnym razem postaram się pokazać kilka przydatnych (szczególnie dla początkujących) dyrektyw konfiguracyjnych Apacza i spróbuję rozwiązać problem jednoczesnego dostępu do GPIO. W załączniku driver wyświetlacza: driver.tgz
  2. Z poprzedniej części dowiedzieliśmy się, jak w prosty i wygodny sposób skonfigurować serwer Apache. Uruchomiliśmy nawet prosty program w PHP, który działa z uprawnieniami zwykłego użytkownika. Jednak PHP nie jest jedynym językiem, w którym możemy tworzyć skrypty. Już przed jego powstaniem opracowano znormalizowany interfejs CGI (czyli Common Gateway Interface), który umożliwiał pisanie programów wykonywanych przez serwer w dowolnym języku. PHP zyskał dużą popularność — szczególnie po ukazaniu się wersji 4 — przede wszystkim dlatego, że był zintegrowany z serwerem i interpreter rezydował w pamięci, podczas gdy wykonanie skryptu np. w Perlu wymagało za każdym razem załadowania interpretera, co trochę trwało. Jednak o ile na przełomie tysiącleci, w epoce dość wolnych dysków o niezbyt wielkiej pojemności było to bardzo ważne, dzisiaj traci to znaczenie - musimy pamiętać, że nasza malinka to demon prędkości w porównaniu z serwerami z lat 90-tych. Nawet programy w PHP są czasem z różnych przyczyn wykonywane jako CGI, a duże pojemności pamięci, szybkie dyski oraz (w przypadku niektórych języków) automatyczna kompilacja do kodu pośredniego pozwalają zmniejszyć ten czas do niezauważalnego minimum. Oprócz CGI powstały inne jeszcze metody komunikacji programów z serwerem, od zarzuconego już dawno SSI, poprzez interfejsy FastCGI, WSGI aż do tworzenia kompletnych serwerów w innych językach programowania i używania głównego serwera jedynie jako reverse proxy. My jednak zajmiemy się najprostszą metodą - czyli właśnie CGI. Interfejs jest bardzo prosty. W sumie wystarczy wiedzieć kilka rzeczy: w czasie wysyłania nagłówków wszystkie pojedyncze znaki '\n' są zamieniane na wymagane przez protokół HTTP pary '\r\n'; dodatkowy nagłówek Status zamieniany jest na linię odpowiedzi serwera i może być stosowany np. do przekazania kodu błędu lub po prostu innej odpowiedzi serwera niż domyślne (w przypadku jego braku) "200 OK"; nagłówki zapytania dostępne są w postaci zmiennych środowiskowych. Program po prostu musi wypisać na wyjście wszystkie potrzebne nagłówki wraz z kończącą je pustą linią, a dopiero wtedy wypisać właściwą odpowiedź (np. kod HTML). Z podobnym zachowaniem spotkaliśmy się zresztą już wcześniej przy tworzeniu serwerów HTTP na Arduino czy ESP, więc nie powinno to sprawiać trudności. Niestety, serwery nie są domyślnie skonfigurowane do obsługi CGI. Na szczęście w przypadku Raspbiana konfiguracja jest bardzo prosta. Zaczniemy od tego, że moduł odpowiedzialny za obsługę CGI co prawda został dostarczony razem z serwerem, ale nie jest domyślnie włączony. Musimy więc go włączyć poleceniem: sudo a2enmod cgi Co prawda dostaniemy informację o konieczności restartu serwera, ale na razie nie musimy tego robić bo i tak będziemy dopisywać obsługę CGI do naszej donyślnej strony. W tym celu otwieramy w edytorze plik konfiguracyjny, np. przez: sudo nano /etc/apache2/sites-available/000-default.conf i dopisujemy dosłownie dwie linijki do części Directory. Linijka AddHandler informuje serwer, że pliki o rozszerzeniu .cgi mają być traktowane jak skrypty, a Options zezwala na wykonywanie skryptów w ogóle w danym katalogu: AddHandler cgi-script .cgi Options ExecCGI Tak więc konfiguracja będzie wyglądać następująco: Po restarcie serwera możemy już spróbować napisać pierwszy skrypt. Musi mieć on rozszerzenie .cgi i mieć prawo do wykonywania. A więc do dzieła! Napiszmy pierwszy program, robiący dokładnie to samo co poprzedni skrypt w PHP. Ponieważ najbardziej popularnym językiem stosowanym na RPi jest python, będziemy pisać skrypty właśnie w tym języku. Umieśćmy więc w katalogu html plik test.cgi z następującą zawartością: #!/usr/bin/env python3 print("Content-Type: text/plain; charset=UTF-8\n") try: open("pytest.txt","w").write("Cos tam\n") print("Zapisane") except: print("Nie da się zapisać") Ponieważ funkcja print dodaje nową linię na końcu, znaczek '\n' w nagłówku wymusza po prostu pustą linię za jedynym interesującym nagłówkiem Content-Type. Teraz musimy nadać plikowi prawa do wykonania: chmod 755 html/test.cgi i po skierowaniu przeglądarki na adres http://<ip_malinki>/test.cgi powinniśmy zobaczyć wynik "Zapisane" lub informację, że zapisać się nie dało z jakichś przyczyn (z jakich — o tym później). Jako że najprawdopodobniej bardziej nas będą interesować zastosowania serwera w IoT czy robotyce — spróbujmy zaprząc Apacza do jakichś bliższych sercu zadań. Ostatnio ktoś pytał o możliwość odczytu kart RFID i pokazywania tego w przeglądarce... spróbujmy! Użyjemy do tego popularnego czytnika RC522, do którego biblioteki są dostępne bez zbytniego szukania. Zacznijmy od tego, że musimy zainstalować podstawowe biblioteki do Pythona, które umożliwią nam operowanie GPIO oraz magistralą SPI. A więc: sudo apt install python3-rpi.gpio python3-smbus python3-pip Biblioteka RPi.GPIO zapewne jest większości znana, smbus pozwala na komunikację właśnie z magistralą SPI, a pip pozwala na instalację pakietów, których nie ma w repozytorium Raspbiana. Upewnijmy się jeszcze, czy mamy włączoną obsługę SPI (w raspi-config), oraz czy użytkownik pi (lub ten, na którego się logujemy) ma dostęp do wszystkiego co potrzebne bez używania sudo. A więc wydajemy polecenie: groups Powinno wyświetlić się kilkanaście grup, do których należy użytkownik pi, czyli coś w rodzaju: pi adm dialout cdrom sudo audio video plugdev games users input netdev bluetooth gpio i2c spi Nas interesują tylko trzy ostatnie. Jeśli któraś nie występuje, musimy ją dopisać: sudo usermod -a -G spi,i2c,gpio pi (oczywiście wpisujemy tylko brakujące) i po przelogowaniu powinniśmy już widzieć użytkownika pi dopisanego do właściwych grup, umożliwiających manipulację pinami GPIO oraz magistralami SPI i I2C. Niestety, biblioteki obsługi RC522 w repozytorium nie ma, ale możemy ją zainstalować używając polecenia pip3: sudo pip3 install mfrc522 Kolej na podłączenie czytnika do naszej malinki. Podłączamy tylko siedem z ośmiu pinów, bo nie używamy pinu IRQ, a więc: SDA do pinu 24 SCK do pinu 23 MOSI do pinu 19 MISO do pinu 21 GND do pinu 6 RST do pinu 22 3.3v do pinu 1 Po włączeniu malinki (pamiętajmy, że wszelkie połączenia musimy wykonywać z odłączonym zasilaniem) powinna zapalić się dioda na czytniku. Napiszmy więc prosty program, umożliwiający odczyt kart: #!/usr/bin/env python3 import RPi.GPIO as GPIO from mfrc522 import SimpleMFRC522 reader = SimpleMFRC522() try: id=reader.read_id_no_block() finally: GPIO.cleanup() print("ID=%s" % id) Użyjemy tu nieblokującej metody, gdyż w przypadku skrypty CGI nie możemy blokować połączenia — przeglądarka przerwie połączenie, jeśli nie doczeka się danych w określonym czasie. Po uruchomieniu programu (bez sudo oczywiście) powinniśmy móc odczytać numerek zbliżonej karty lub pusty napis, jeśli karty nie ma. Czyżby to było takie proste? Spróbujmy teraz przerobić nasz program na skrypt CGI. Wystarczy do tego dopisać linijki odpowiadające za wysłanie nagłówków, i powinno działać! Tworzymy więc w katalogu html plik o nazwie (przykładowo) czytnik.cgi i wpisujemy do niego: #!/usr/bin/env python3 import RPi.GPIO as GPIO from mfrc522 import SimpleMFRC522 print("Content-Type: text/plain; charset=UTF-8\n") reader = SimpleMFRC522() try: id=reader.read_id_no_block() or '' finally: GPIO.cleanup() print("ID=%s" % id) Nadajemy plikowi prawa do wykonywania: chmod 755 html/czytnik.cgi Na wszelki wypadek sprawdźmy, czy działa wydając po prostu polecenie: html/czytnik.cgi Powinno działać! A więc przeglądarka w ruch, wpisujemy adres http://<ip_malinki>/czytnik.cgi i... ...i nic. Zaglądając do logu błędów Apacza zobaczymy, że program nie został wykonany. Dlaczego? Ponieważ ciągłe zaglądanie do logów nie jest zbyt wygodne, zmuśmy nasz skrypt do reakcji na błędy i wyświetlania ich w przeglądarce. Użyjemy do tego modułu cgitb, który pozwala właśnie na takie manipulacje. A więc nowa wersja skryptu będzie wyglądać tak: #!/usr/bin/env python3 import RPi.GPIO as GPIO from mfrc522 import SimpleMFRC522 import cgitb print("Content-Type: text/plain; charset=UTF-8\n") cgitb.enable(format="text") # włączamy wyświetlanie błędów w formacie tekstowym reader = SimpleMFRC522() try: id=reader.read_id_no_block() or '' finally: GPIO.cleanup() print("ID=%s" % id) Po uruchomieniu możemy zobaczyć, że skrypt nie ma uprawnień do smbusa i gpio! Ale przecież użyliśmy wcześniej modułu ruid2, który powinien uruchomić nasz program jako użytkownik pi! Ki diabeł? Musimy to sprawdzić. Dodajmy do skryptu linijki, które pokażą nam z jakimi uprawnieniami wykonywany jest nasz program: #!/usr/bin/env python3 import RPi.GPIO as GPIO from mfrc522 import SimpleMFRC522 import cgitb, sys, os print("Content-Type: text/plain; charset=UTF-8\n") sys.stdout.flush() # aby upewnić się, że nagłówki nie zostaną zbuforowane os.system("groups") Po uruchomieniu widzimy, że program uruchomił się z uprawnieniami grupy pi, ewentualnie (zależnie od konfiguracji) dodatkowo www-data. A gdzie nasze gpio? Gdzie spi? Spójrzmy do konfiguracji modułu ruid2. O właśnie — kazaliśmy uruchomić program z uprawnieniami użytkownika pi i grupy pi, bez żadnych dodatkowych grup. Ale od czego jest linijka RGroups? Właśnie od tego, aby dopisać dodatkowe grupy! Poprzednio mieliśmy tam wpisane @none. Dopiszmy więc dodatkowe grupy, zmieniając tę linijkę na: RGroups gpio spi i2c i zrestartujmy Apacza. Powinno być dobrze... Ale nie jest. Przeglądarka uparcie pokazuje nam, że Apacz nic nie wie o żadnych dodatkowych grupach... dlaczego? Po prostu zadziałało zabezpieczenie. Moduł nie pozwala nam na uruchamianie programu z uprawnieniami grup systemowych, czyli w przypadku Raspbiana z numerkami grupy mniejszymi niż 1000. Na szczęście i na to jest sposób. W konfiguracji możemy podać nazwę grupy o najmniejszym ID, który jest dozwolony. Musimy w tym celu przejrzeć plik grup: cat /etc/groups lub lepiej, aby ograniczyć wyświetlanie do najbardziej istotnych rzeczy: egrep '(spi|i2c|gpio)' /etc/group i znaleźć, która z tych grup ma najniższy numerek. W moim przypadku jest to grupa gpio o numerze 997, a więc dopisujemy dodatkową linię do konfiguracji ruid2: RMinUidGid pi gpio W efekcie nasza konfiguracja wygląda tak: Teraz restartujemy Apacza — i w efekcie po przyłożeniu karty do czytnika i wczytaniu strony do przeglądarki zobaczymy wreszcie odczytany ID! Ponieważ chcielibyśmy, aby dane były pokazywane na bieżąco, możemy po prostu dopisać odpowiedni nagłówek wymuszający na przeglądarce odświeżenie strony. Tak więc po usunięciu niepotrzebnych już linii i delikatnym "upiększeniu" nasz kod będzie wyglądać tak: #!/usr/bin/env python3 import RPi.GPIO as GPIO from mfrc522 import SimpleMFRC522 import cgitb print("Content-Type: text/plain; charset=UTF-8\nRefresh: 5\n") cgitb.enable(format="text") reader = SimpleMFRC522() try: id=reader.read_id_no_block() or 'Brak karty' finally: GPIO.cleanup() print("ID=%s" % id) Prawda, jakie to proste? Tyle na dziś — w następnej części dowiemy się, jak w pythonowym skrypcie CGI możemy dobrać się do przesyłanych danych.
  3. Serwer WWW jest jednym z podstawowych programów używanych na Raspberry Pi. Ponieważ instalacja serwera jest dokładnie omówiona w kursie, a i pierwsze spotkanie z Apache najprawdopodobniej mamy już za sobą - nie będę opisywać podstaw, a przejdę od razu do trochę (naprawdę tylko trochę) bardziej zaawansowanej konfiguracji, ułatwiającej nam pracę i zabawę z serwerem. Pierwszą przykrą rzeczą, na którą się na pewno natkniemy będzie fakt, że nie mamy dostępu do katalogu /var/www/html, gdzie znajdują się pliki strony serwowanej przez Apacza. Większość radzi sobie z tym w ten sposób, że po prostu zmienia właściciela owego katalogu na użytkownika, na którego się loguje i teoretycznie wszystko działa... Niestety, tylko teoretycznie. Operowanie jakimiś danymi poza własnym katalogiem domowym jest niewygodne, a czasami (np. w przypadku ftp/sftp i ustawionego chroota) wręcz niemożliwe. W dodatku istnieje niebezpieczeństwo, że przy jakimś upgradzie system po prostu nadpisze zawartość tego katalogu i nasza strona będzie istniała tylko w przepastnych czeluściach /dev/null... Tymczasem Apache jest chyba najbardziej konfigurowalnym programem na naszej malince, i najprostszym sposobem będzie umieszczenie katalogu ze stroną w naszym katalogu domowym (czyli np. /home/pi/). Apache potrafi serwować różne zawartości z różnych katalogów poprzez mechanizm tzw. hostów wirtualnych, ale jako że konfiguracja tego jest na tyle obszerna, że zasługuje co najmniej na jeden oddzielny artykuł - na razie interesuje nas wyłącznie konfiguracja domyślnego katalogu. Zacznijmy od utworzenia katalogu ze stroną i umieszczeniu tam jakiegoś najprostszego testowego pliku: mkdir ~/html echo "To tylko test" > ~/html/index.html Następnie musimy poprawić konfigurację domyślnej strony serwera. W tym celu otwieramy w edytorze plik konfiguracyjny: sudo nano /etc/apache2/sites-enabled/000-default.conf Znajduje się tam linijka: DocumentRoot /var/www/html informująca serwer o położeniu domyślnej strony. Musimy zmienić ją na: DocumentRoot /home/pi/html Ale to jeszcze nie wszystko. Należy również poinformować serwer, co może z tym katalogiem robić. Pod tą linijką dopisujemy więc: <Directory /home/pi/html> AllowOverride all Require all granted </Directory> Dyrektywa AllowOverride pozwala na późniejsze dokonfigurowywanie poprzez plik .htaccess, Require otwiera nielimitowany dostęp do strony. Fragment pliku odpowiedzialny za naszą konfigurację powinien wyglądać więc tak: Teraz możemy zrestartować Apacza: sudo systemctl restart apache2 lub po prostu kazać mu jeszcze raz wczytać konfigurację: sudo apachectl graceful Po prawidłowym przeprowadzeniu wszystkich operacji powinniśmy po wpisaniu do przeglądarki adresu malinki ujrzeć na ekranie napis "To tylko test". Sytuację z położeniem katalogu ze stroną mamy więc już naprawioną - ale za chwilę natkniemy się na następną, niemniej przykrą rzecz: otóż Apacz mimo że już wie o tym, że pliki ze stroną są w naszym katalogu domowym - wciąż działa z uprawnieniami użytkownika www-data. To może doprowadzić do absurdalnej sytuacji, kiedy w naszym katalogu domowym znajdują się pliki i subkatalogi nie będące naszą własnością, z którymi nie bardzo możemy cokolwiek zrobić (np. pliki upload WordPressa). Stwórzmy więc testowy plik html/test.php: <?php if (file_put_contents('test.txt','To jest test php')) { echo "Zapisane"; } else { echo "Nie dało się zapisać"; } ?> Po wpisaniu w przeglądarkę adresu http://ip_malinki/test.php zobaczymy niestety napis "Nie dało się zapisać", a w logu błędów (czyli /var/log/apache2/error.log) komunikat podobny do tego: PHP Warning: file_put_contents(test.txt): failed to open stream: Permission denied in /home/pi/html/test.php on line 2 Widzimy, że nasz wielce ambitny program nie ma uprawnień do zapisu 😞 Ale i na to jest sposób: musimy przekonać Apacza, aby naszą stronę serwował jako użytkownik pi a nie www-data! W tym celu instalujemy moduł ruid2, odpowiedzialny za zmianę "w locie" uprawnień: sudo apt install libapache2-mod-ruid2 -y Po instalacji moduł zostanie włączony w Apaczu, pozostanie nam więc tylko dodanie linijek odpowiedzialnych za zmianę użytkownika. W tym celu znów otwieramy w edytorze plik 000-default.conf i przed sekcją <Directory> dopisujemy: <IfModule mod_ruid2.c> RMode config RUidGid pi pi RGroups @none </IfModule> (co oznacza, że ręcznie podajemy dane, serwer ma działać z uprawnieniami użytkownika pi.pi bez żadnych dodatkowych grup). Po restarcie lub reloadzie serwera, jeśli nic się nie będzie działo, możemy spróbować czy nasze działania przyniosły spodziewany efekt. Jeśli teraz skierujemy przeglądarkę na adres ip_malinki/test.php - powinniśmy zobaczyć napis 'Zapisane'. Po wydaniu polecenia ls -l zobaczymy, że plik test.txt został utworzony z właściwymi uprawnieniami, czyli coś w rodzaju: $ ls -l html razem 12 -rw-r--r-- 1 pi pi 5 kwi 22 06:11 index.html -rw-r--r-- 1 pi pi 84 kwi 22 10:30 test.php -rw-r--r-- 1 pi pi 15 kwi 22 10:35 test.txt Tyle na dziś - w następnej części zobaczymy, jak można uruchamiać poprzez serwer programy w Pythonie (w rzeczywistości w dowolnym języku programowania) oraz w jaki sposób udostępnić tym programom GPIO czy też magistrale SPI oraz I2C.
×
×
  • Utwórz nowe...

Ważne informacje

Ta strona używa ciasteczek (cookies), dzięki którym może działać lepiej. Więcej na ten temat znajdziesz w Polityce Prywatności.