Przedstawię tutaj poprawki, uruchomienie i rozwinięcie biblioteki ArduinoHttpServer na platformie Arduino R4 WiFi. Celem tematu będzie ułatwienie nam operacji związanych z protokołem HTTP, a dokładniej parsowania żądań HTTP (przetworzenie nagłówka, ścieżki zasobu oraz argumentów GET) oraz wysyłania odpowiedzi na te żądania. Przy okazji też pokażę prosty system autoryzacji HTTP oferowanej przez tę bibliotekę.
Temat zakłada, że użytkownik ma podstawową wiedzę i wie mniej więcej czym jest HTTP, czym jest żądanie GET, czym jest serwer, adres IP i tak dalej.
Motywacja projektu
Arduino R4 WiFi posiada nieco gotowych przykładów projektów sieciowych:
Ale po ich otwarciu, ku naszemu zaskoczeniu, okazuje się, że parsing HTTP realizowany jest w bardzo uproszczony sposób i nie jest upakowany w osobne klasy:
W związku z tym rozpocząłem poszukiwania biblioteki, która mi to zaoferuje.
Zainteresowała mnie praca QuickSander:
https://github.com/QuickSander/ArduinoHttpServer
Jednakże, jak szybko się okazało, jest z nią parę problemów:
- nie kompiluje się pod Arduino R4 WiFi
- przykłady są dość ubogie
- nie wspiera parsingu argumentów GET, tj. linku w stylu:
https://www.elektroda.pl/rtvforum/search.php?search_id=egosearch&user=1234
W linku powyżej są zawarte dwa argumenty: jeden o kluczu "search_id" i wartości "egosearch", a drugi o kluczu "user" i wartości "1234". Warto by było móc je łatwo wyciągnąć z tego adresu....
W tym temacie wszystkie te trzy kwestie postaram się naprawić.
Instalacja i poprawki ArduinoHttpServer
ArduinoHttpServer pobieramy z Libraries Manager, przy okazji też trzeba zainstalować jedną dependencję, a mianowicie Base64:
Potem można spróbować skompilować ich przykład z Githuba, ale się to nie powiedzie:
Kod: C / C++
Otrzymamy taki błąd kompilacji:
Brakuje nagłówka pgmspace.h. Zaraz to naprawimy. Nie jest on w rzeczywistości potrzebny w tym przypadku.
Otwieramy kod biblioteki, u mnie jest on pod:
C:\Users\Admin\Documents\Arduino\libraries\Base64\src\Base64.cpp
Wystarczy zakomentować ten blok (albo dodać define dla R4):
Teraz przykład powinien działać, ale jeśli czasem otrzymujemy taki komunikat:
To warto zajrzeć do:
C:\Users\Admin\Documents\Arduino\libraries\ArduinoHttpServer\src\internals\StreamHttpRequest.hpp
Problemem jest ten fragment kodu:
Po prostu wykrywa on błąd timeout. Początkowo chciałem zwiększyć wartość stałej MAX_RETRIES_WAIT_DATA_AVAILABLE:
ale ostatecznie po prostu cały blok zakomentowałem. Od tego momentu całość zaczęła działać nieco stabilniej.
Nie wiem, na ile jest to poprawne podejście ale po tej zmianie ta biblioteka działa u mnie nieco stabilniej.
Przykład użycia od autorów ArduinoHttpServer
Poświęćmy chwilkę na przetestowanie przykładowego kodu ArduinoHttpServer dostarczonego przez autora biblioteki. Ten kod oferuje prostą autoryzację HTTP opartą o mechanizm wbudowany w przeglądarkę:
Kod: C / C++
Rzeczywiście, po wejściu na stronę mamy okienko z pytaniem o dane:
Po wpisaniu złych danych w logu otrzymujemy:
Natomiast po wpisaniu poprawnych:
Autoryzacja może i jest przydatna, ale o dziwo nie widzę nigdzie czegoś bardziej fundamentalnego, czyli możliwości wyłuskania argumentów z żądania GET. Zaraz się tym zajmiemy.
Implementacja parsingu argumentów
Krótka analiza kodu biblioteki pokazuje, że za parsing adresu zasobu odpowiada klasa HttpResource. Autor w tej klasie trzyma tylko jeden napis i na bieżąco w razie potrzeby go rozdziela:
Kod: C / C++
Operator [] pozwala się dostać do konkretnych folderów w adresie zasobu:
Kod: C / C++
Skoro już robimy w ten sposób, to dopisałem swoją metodę dla pobierania tekstowej wartości danego argumentu GET, który też wyszukujemy po nazwie:
Kod: C / C++
Moja implementacja:
Kod: C / C++
Implementacja najpierw szuka znaku zapytania, który oznacza początek argumentów, a potem wyszukuje nazwy klucza danego argumentu wraz z dodanym znakiem równości, by chociaż pomijać część potencjalnych błędów związanych z wyszukiwaniem nazwy która stanowi substring innej nazwy klucza dostępnego w stringu. Ten problem nie jest tu w pełni rozwiązany, ale założyłem, że i tak raczej będą używana jeden czy dwa klucze, więc zostawiłem tak jak jest.
Czytelnik może ulepszyć ten kod we własnym zakresie.
Napisałem dodatkowo przykładowy kod, który korzysta z nowej funkcjonalności:
Kod: C / C++
Cała magia się dzieje we fragmencie ze zmienną textField . Kod sprawdza, czy jest argument GET o takiej nazwie i jeśli tak, to w HTMLu uwzględnia komunikat z wyświetleniem wartości textField . Jeśli nie, to wyświetlenie pomija. W obu przypadkach dalej tworzę formularz HTML z polem właśnie o takiej nazwie textField , by użytkownik mógł łatwo wysyłać zapytanie GET z nową wartością tej zmiennej.
Po otwarciu strony widzimy:
Po wpisaniu tekstu "ArduinoFan" i wysłaniu:
Wygląda na to, że moja obsługa argumentów GET działa.
Wskazówka - budowa ArduinoHttpServer i strumienie
Teraz chciałbym jeszcze zwrócić na pewną przydatną cechę omawianej biblioteki wynikającej z obiektowości i dziedziczenia dostępnego w użytym środowisku.
Rozważmy konstruktor klasy StreamHttpReply, która reprezentuje odpowiedź HTTP:
Kod: C / C++
Pierwszym argumentem jest obiekt klasy Stream, czyli strumień. Po klasie Stream dziedzicą różne klasy, w tym strumień sieciowy. Użycie StreamHttpReply wygląda wtedy tak:
Kod: C / C++
To było w umieszczonym przykładzie. Ale strumieniem też jest obiekt Serial, czyli port szeregowy Arduino. Pozwala to nam na taką konstrukcję:
Kod: C / C++
Może wydawać się to dziwne, ale nic nie stoi na przeszkodzie by używać protokołu HTTP na strumieniu UART, zamiast na strumieniu TCP. Rozważmy większy przykład:
Kod: C / C++
Rezultat w konsoli:
Tak jest, ten kod po prostu wydrukował wysłany HTML z doklejonym nagłówkiem HTTP. To bardzo ładnie pokazuję hermetyczną i modułową budowę tej biblioteki.
Podsumowanie
Całkiem fajna i przydatna ta biblioteka. Nie było dużych problemów z portowaniem jej na R4, co nie jest zaskoczeniem, gdyż jej mechanizmy opierają się o klasę Stream, która może stanowić wspólny interfejs dla wielu strumieni.
Troszkę dziwne, że na ten moment pisania tego tematu ta biblioteka nie wspierała łatwego dostępu do argumentów GET, ale wystarczyło dopisać jedną funkcję by to zmienić.
Jeszcze troszkę potestuję mój kod a potem otworzę pull request na Github by zaproponować moje zmiany i ulepszenia, być może autor biblioteki je przyjmie i doda do swojego kodu.
To na razie tyle. Czy ktoś z czytelników tworzył już serwer HTTP na Arduino R4? Widzicie jakieś potencjalne zastosowania dla tej biblioteki?
PS: Poniżej macie łatkę którą wysłałem do autora biblioteki: https://github.com/QuickSander/ArduinoHttpServer/pull/23
Fajne? Ranking DIY Pomogłem? Kup mi kawę.