logo elektroda
logo elektroda
X
logo elektroda

Arduino R4 WiFi i ArduinoHttpServer - poprawki, uruchomienie, przykłady użycia

p.kaczmarek2  16 4164 Cool? (+5)
Płytka Arduino R4 WiFi z napisem ArduinoHTTPServer
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:
Zrzut ekranu z menu Arduino z wybranym przykładem WiFi WebServer.
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:
Fragment kodu Arduino przedstawiający obsługę żądań HTTP.
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:
Biblioteka ArduinoHttpServer z wyskakującym oknem brakujących zależności
Potem można spróbować skompilować ich przykład z Githuba, ale się to nie powiedzie:
Code: C / C++
Log in, to see the code

Otrzymamy taki błąd kompilacji:
Zrzut ekranu błędu kompilacji Arduino R4 WiFi.
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):
Edytowany kod źródłowy w pliku Base64.cpp z zaznaczonym blokiem warunkowym.
Teraz przykład powinien działać, ale jeśli czasem otrzymujemy taki komunikat:
Komunikat błędu 400: Timeout podczas oczekiwania na dane.
To warto zajrzeć do:

C:\Users\Admin\Documents\Arduino\libraries\ArduinoHttpServer\src\internals\StreamHttpRequest.hpp

Problemem jest ten fragment kodu:
Fragment kodu ArduinoHttpServer z pętlą sprawdzającą dostępność danych w strumieniu.
Po prostu wykrywa on błąd timeout. Początkowo chciałem zwiększyć wartość stałej MAX_RETRIES_WAIT_DATA_AVAILABLE:
Fragment kodu w edytorze tekstowym z zaznaczoną linią dotyczącą 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.
Kod w języku C++ z zakomentowanym blokiem sprawdzającym timeout.
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ę:
Code: C / C++
Log in, to see the code

Rzeczywiście, po wejściu na stronę mamy okienko z pytaniem o dane:
Okno logowania HTTP na stronie lokalnej z prośbą o nazwę użytkownika i hasło.
Po wpisaniu złych danych w logu otrzymujemy:
Zrzut ekranu z konsoli z wiadomościami o połączeniu klienta i błędach autoryzacji.
Natomiast po wpisaniu poprawnych:
Ekran konsoli z logami działania programu Arduino
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:
Code: C / C++
Log in, to see the code

Operator [] pozwala się dostać do konkretnych folderów w adresie zasobu:
Code: C / C++
Log in, to see the code

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:
Code: C / C++
Log in, to see the code

Moja implementacja:
Code: C / C++
Log in, to see the code

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:
Code: C / C++
Log in, to see the code

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:
Strona w przeglądarce z formularzem do wpisania tekstu i przyciskiem Submit.
Po wpisaniu tekstu "ArduinoFan" i wysłaniu:
Strona HTML z polem formularza i powitaniem użytkownika.
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:
Code: C / C++
Log in, to see the code

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:
Code: C / C++
Log in, to see the code

To było w umieszczonym przykładzie. Ale strumieniem też jest obiekt Serial, czyli port szeregowy Arduino. Pozwala to nam na taką konstrukcję:
Code: C / C++
Log in, to see the code

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:
Code: C / C++
Log in, to see the code

Rezultat w konsoli:
Zrzut ekranu z wynikami HTML i nagłówka HTTP 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

About Author
p.kaczmarek2
Inżynier programista z wieloletnim doświadczeniem embedded i full stack developer. Has specialization in: embedded, Full-Stack Developer p.kaczmarek2 wrote 14444 posts with rating 12414 , helped 650 times. Been with us since 2014 year.

Comments

ripnetru 28 May 2024 19:42

Witam, Mam R4 Uno i przy kompilacji dostaję błąd Błąd kompilacji: 'const class ArduinoHttpServer::HttpResource' nie ma członka o nazwie 'getArgument' Dodano po 3 [minutach]: String ssid =... [Read more]

p.kaczmarek2 28 May 2024 20:17

To dlatego, że musisz użyć mojej wersji biblioteki. Kod można zobaczyć tutaj: https://github.com/QuickSander/ArduinoHttpServer/pull/23/files Można go po prostu skopiować i wkleić. Daj mi znać, jeśli... [Read more]

ripnetru 28 May 2024 21:05

Dobra, teraz działa, też mam błąd 400 i naprawiam tak jak piszesz. Skonfigurowałem kredyty wifi i działa, po ponownym uruchomieniu beginSTA z SSID pass Jest ok, ale nie rozumiem, jak działające łącze... [Read more]

ripnetru 30 May 2024 17:10

Proszę o pomoc, jeśli to w ogóle możliwe. W formularzu mamy ssid i pass, i chcę, aby jeśli ssid i pass zostały zapisane, aby wyświetlić je w formularzu internetowym w polu wprowadzania. " <label... [Read more]

p.kaczmarek2 30 May 2024 17:35

Czy możesz podać pełny kod, nad którym obecnie pracujesz? Jeśli odnosisz się do generowania strony HTML, to możesz zrobić coś takiego: <input type="text" id="ssid" name="ssid" value="MySSID"><br><br> ... [Read more]

ripnetru 30 May 2024 17:42

Nie mam kodu, pracuję z oryginalnym kodem https://www.elektroda.com/rtvforum/topic4000214.html chcę coś takiego: " <label for=\"ssid\">Enter SSID:</label><br>\n" " <input... [Read more]

p.kaczmarek2 30 May 2024 18:16

Rozumiem, więc to jest mój stary fragment kodu: reply += " <form>\n" " <label for=\"ssid\">Enter SSID:</label><br>\n" " ... [Read more]

ripnetru 30 May 2024 18:49

Naprawdę to doceniam. Właśnie sprawdziłem, działa tak, jak chciałem. Dziękuję! [Read more]

p.kaczmarek2 30 May 2024 21:27

Jasne, daj mi znać, jeśli potrzebujesz dalszej pomocy [Read more]

ripnetru 02 Jun 2024 23:54

Witam, Tak, mam jeszcze jedno pytanie, podczas pracy w trybie STA, czasami traci połączenie, może to zakłócenia (2,4 GHz) (00:43:00.872 -> Przechodzę do odczytu ustawień.... 00:43:00.872 -> Ustawienia... [Read more]

ripnetru 03 Jun 2024 20:54

Próbuję zrobić tak: void loop() { int status = WL_IDLE_STATUS; while (status != WL_CONNECTED) { Serial.print("Próba połączenia z SSID: "); Serial.println("Pixel"); status = WiFi.begin("Pixel",... [Read more]

ripnetru 11 Jun 2024 19:37

Zrozumiałem to, nie wiedziałem, że zmienne mogą być wyższe niż pętla i konfiguracja i zrobiłem je podobnie do twojego przykładu #include <ArduinoHttpServer.h> const char *ssidglobal; const... [Read more]

electromechpro 15 Jul 2025 22:24

Dziękuję za odpowiedź. Udało mi się skompilować i pobrać przykład, ale R4 nie pokazał ssid, który wprowadziłem w kodzie. Zgodnie z postem #3 21098991 28 maja 2024 20:17, utknąłem, próbując dowiedzieć... [Read more]

p.kaczmarek2 15 Jul 2025 22:30

Jaki jest twój aktualny kod? Post #3 pokazuje moje zmiany w bibliotece HTTP. Możesz zastosować je ręcznie do kodu HttpResource. Jeśli nie chcesz ręcznie łatać tej biblioteki, oto moja wersja (już... [Read more]

electromechpro 15 Jul 2025 23:00

Załatana biblioteka zadziałała! Nie chodzi o to, że nie chciałem łatać istniejącej biblioteki, ale o to, że nie rozumiałem, jak to zrobić. Najpierw usunąłem istniejący podkatalog biblioteki z katalogu... [Read more]

electromechpro 17 Jul 2025 19:15

Jeszcze raz dziękuję za pomoc z biblioteką i przesłany przykład kodu. Badałem, jak wdrożyć ją w niektórych moich projektach oprzyrządowania. W ArduinoR4DIYWiFiManagerExample.ino, który wysłałeś, pętla... [Read more]

%}