W poprzednim artykule opisałem prosty serwer WWW zbudowany w oparciu o układy ESP8266. Był to jedynie wstęp pokazujący jak za pomocą kilku poleceń zbudować prostą stronę internetową wyświetlającą dane z układu ESP. Po przeczytaniu artykułu i przetestowaniu programu można odnieść wrażenie, że układ ESP w roli serwera WWW to raczej ciekawostka, niż użyteczne narzędzie. W poniższym artykule postaram się przedstawić kilka użytecznych drobiazgów, które spowodują, że nasz układ będzie mógł pełnić całkiem poważną funkcję w świecie IoT.
Przekazywanie parametrów
Przeglądając strony internetowe możemy zauważyć, że większość przekierowań zawiera dodatkowe parametry przekazywane do serwera. Może to być identyfikator artykułu, czy jakakolwiek inna informacja. Ważne, że wywołujemy tą samą stronę (np. index.php) z różnymi parametrami. W poprzednim artykule włączenie diody możliwe było poprzez wywołanie jednej "strony", a wyłączenie przez wywołanie drugiej. Poniżej spróbujemy wykonać obsługę funkcji włącz/wyłącz z wykorzystaniem jednej strony naszego serwera.
Do obsługi argumentów przekazywanych do naszego serwera służą metody: args(), argName(), arg().
Pierwsza z nich zwraca ilość podanych argumentów. Jeżeli wywołamy stronę naszego serwera
ip/nazwa_strony?arg1=3&arg2=4&arg3="arduino"
metoda args() zwróci nam wartość 3. Dysponując wiedzą o ilości podanych przez użytkownika argumentów możemy odczytać ich wartości
for (uint8_t i = 0; i < server.args(); i++) {
Serial.print("Parametr: ");
Serial.print(server.argName(i));
Serial.print(" wartość: ");
Serial.println(server.arg(i));
}
Po wykonaniu powyższego kodu otrzymamy:
Parametr: arg1 wartość 3
Parametr: arg2 wartość 4
Parametr: arg3 wartość arduino
Jak widać mamy dostęp zarówno do nazwy podanego parametru jak i jego wartości. W tym momencie możemy wykorzystać podane przez użytkownika dane do własnych celów. Korzystając z programu z poprzedniego artykułu rozbudujemy go o funkcję włącz/wyłącz w ramach jednej wywoływanej strony WWW serwera. W tym celu należy dodać nową funkcję
void handleOnOff(){
for (uint8_t i = 0; i < server.args(); i++){
if(server.argName(i)=="stan"){
if(server.arg(i)=="On") digitalWrite(LED,LOW);
if(server.arg(i)=="Off") digitalWrite(LED,HIGH);
}
}
}
Przed server.begin() dopisujemy
server.on("/ustawienie",handleOnOff);
Po wykonaniu powyższego możemy przetestować działanie naszego układu. Wpisanie
ip_serwera/ustawienie?status=On
włączy diodę, natomiast
ip_serwera/ustawienie?status=Off
wyłączy diodę. Jak widać korzystanie z parametrów przekazywanych do naszego układu ESP jest bardzo proste. Kilka słów komentarza do powyższego kodu. Odrębna instrukcja warunkowo dla On i Off to nie przypadek. Założyłem, że użytkownik może wpisać w tym miejscu inne wartości parametrów i będą one ignorowane. W przypadku użycia "else" jedna z akcji np. wyłączenie diody wykonywana będzie zawsze, gdy wpiszemy coś innego niż On. Omówienia wymaga również pętla, w której sprawdzamy parametry. W naszym przykładzie podawany jest jeden parametr. Moglibyśmy założyć, że ten parametr podawany jest zawsze i odczytać wartość parametru. Nie jest to rozwiązanie eleganckie, ponieważ użytkownik może podać większą ilość parametrów, a na dodatek może ie podać parametru o wymaganej przez nas nazwie. Zastosowanie pętli do odczytu wartości parametrów umożliwia nam kontrolę nad tym, co zostanie przekazane do naszego układu. Ostatnia kwestia to wartość jaką przyjmuje zmienna "i". W informatyce liczymy od zera. Zero est pierwszą wartością. Z tego powodu również przy liście argumentów mamy również argument zerowy(czyli pierwszy).
Skrócony kurs HTML-a
Strona przekazywana przez utworzony serwer WWW jest interpretowana w przeglądarce w sposób identyczny ze stronami internetowymi. Oznacza to, że wszystko co dotyczy formatowania strony należy przekazać do przeglądarki tak jak podczas tworzenia tradycyjnego serwisu WWW. Nie będę tutaj opisywał w jaki sposób przygotowuje się strony internetowe, podpowiem jednak, jak podejść do tematu z wykorzystaniem pakietu ArduinoIDE.
Warto przygotować sobie nagłówek strony, który będziemy doklejać do właściwej treści. Dodatkowo dla zachowania zgodności ze standardem wypadałoby pozamykać znaczniki czyli utworzyć również stopkę.
String naglowek="<html><head><meta charset='UTF-8'><title>Nazwa projektu</title>
</head><body>";
String stopka="</body></html>";
W sekcji nagłówkowej umieszczamy wszystko, co jest nam potrzebne. W przykładzie ustawiłem tylko kodowanie i nazwę, która będzie wyświetlała się na pasku przeglądarki. Możemy skorzystać z dowolnych znaczników HTML, możemy również stworzyć własne arkusze stylów. dysponując gotowym nagłówkiem i stopką możemy połączyć je z resztą strony.
server.send(200, "text/html", naglowek+"<h2>Dioda włączona</h2>"+stopka);
W sytuacji, gdy zależy nam na wyglądzie naszej strony warto przygotować sobie szablon strony wykorzystując odpowiednie narzędzia do tworzenia stron WWW, a następnie pociąć i skopiować przygotowaną stronę do naszego projektu. Dysponując własnym serwerem HTTP możemy skonstruować stronę internetową, która będzie łączyła się z naszym układem ESP i wyświetlała pobrane wyniki. W takim wypadku nie jesteśmy ograniczeni sposobem prezentacji danych.
Prosta autoryzacja
Budując urządzenia sterujące podłączone do sieci Internet należy pamiętać o ich zabezpieczeniu. Nie można sobie pozwolić na pozostawienie otwartego urządzenia, do którego każdy ma nieograniczony dostęp. Istnieje wiele metod zabezpieczających. Poniżej zaprezentuję jedno z prostszych w implementacji rozwiązań, które zapewni minimum bezpieczeństwa. Najłatwiej zabezpieczyć dostęp do urządzenia poprzez token/klucz zabezpieczający. Implementacja sprowadza się do kilku kroków. W pierwszej kolejności musimy ustalić tajny klucz, czyli ciąg znaków, który będzie potwierdzeniem dostępu do urządzenia. Następnie modyfikujemy wszystkie wywołania obsługiwane przez serwer w taki sposób, aby określone akcje wykonywane były wyłącznie po sprawdzeniu poprawności klucza. Znamy już teorię, pora na wdrożenie. Zmodyfikujemy jedną funkcję przedstawioną powyżej.
void handleOnOff(){
if(server.arg(0)=="superTajnyKlucz")
{
for (uint8_t i = 1; i < server.args(); i++){
if(server.argName(i)=="stan"){
if(server.arg(i)=="On") digitalWrite(LED,LOW);
if(server.arg(i)=="Off") digitalWrite(LED,HIGH);
}
}
else
server.send(401, "text/plain","Brak autoryzacji");
}
}
Jak widać w powyższym przykładzie założono, że klucz autoryzacyjny podawany jest jako pierwszy parametr. Nie sprawdzono nawet nazwy parametru. Po takiej modyfikacji dostęp do opcji włącz/wyłącz będzie możliwy po podaniu klucza
ip_serwera/ustawienie?klucz=superTajnyKlucz&status=Off
Jeżeli klucz nie będzie się zgadał otrzymamy komunikat błędu "Brak autoryzacji". Dla nieznacznego zwiększenia poziomu bezpieczeństwa sprawdzimy jeszcze nazwę pierwszego parametru. Wystarczy w tym celu rozbudować wiersz ze sprawdzeniem klucza
if((server.argName(0)=="key")&&(server.arg(0)=="superTajnyKlucz"))
Od tego momentu pierwszy argument musi nazywać się key i mieć przypisaną wartość superTajnyKlucz
ip_serwera/ustawienie?key=superTajnyKlucz&status=Off
Identycznie modyfikujemy wszystkie inne obsługiwane strony naszego serwera. W celu zwiększenia bezpieczeństwa można dodatkowo zastosować różne klucze dla poszczególnych stron. Wadą zaprezentowanego rozwiązania jest możliwość podsłuchania transmisji i upublicznienia klucza. Rozwiązaniem tego problemu może być szyfrowanie połączenia oraz zmienny klucz. Jest to jednak sposób wymagający dużo więcej zaangażowania po stronie programisty. W przypadku gdy budowane przez nas urządzenie służy do odczytu temperatury, czy włączania lampek choinkowych możemy poprzestać na prostym przedstawionym powyżej sposobie zabezpieczenia dostępu. W zastosowaniach komercyjnych należy zastosować pewniejsze, bardziej skomplikowane zabezpieczenie przed dostępem osób niepowołanych.