Router Wi-Fi tworzy lokalną sieć, w ramach której mogą komunikować się różne urządzenia. Każde z nich, łącznie z routerem, ma swój unikalny adres IP w obrębie lokalnej sieci (nie należy go mylić z publicznym adresem IP, który jest przydzielany przez dostawcę usług internetowych). Taki adres składa się z czterech bajtów, a zwykle pierwsze dwa bajty mają wartości 192 i 168.
Zdecydowana większość urządzeń w sieci lokalnej komunikuje się z routerem Wi-Fi po to, by uzyskać dostęp do Internetu. Jednak urządzenia w ramach tej samej sieci mogą także przesyłać pomiędzy sobą różne informacje, również za pośrednictwem routera. Typowym przykładem jest drukarka bezprzewodowa, do której polecenia wydruku przesyłać mogą komputery lub smartfony – pod warunkiem, że pracują w tej samej sieci lokalnej.
Każde urządzenie w sieci Wi-Fi może mieć swój własny serwer HTTP, dzięki któremu jest w stanie generować strony internetowe, które mogą być odczytane w przeglądarce internetowej na innym urządzeniu. Tutaj klasycznym przykładem jest strona konfiguracyjna routera Wi-Fi, którą możemy otworzyć wpisując w pasku adresu przeglądarki adres http://192.168.0.1. Jeżeli ten adres nie działa, sprawdź instrukcję na obudowie Twojego routera, bo najprawdopodobniej należy wywołać inny adres.
W tym odcinku kursu opracujemy w MicroPythonie prosty serwer HTTP. Jego zadaniem będzie udostępnianie strony index.html, zapisanej w pamięci ESP32. Widzimy ją na rysunku 1. Jest ona bardzo prosta i ma za zadanie zademonstrować, w jaki sposób możemy wyświetlać jakieś dane, np. temperaturę procesora czy moc sygnału, a także jak można zbudować interfejs człowiek-maszyna (HMI) za pomocą takiej technologii. Przy użyciu ośmiu przycisków na stronie WWW użytkownik może wybrać kolor diody WS2812, która znajduje się na devboardzie ESP32-S3-DevKit-C. Oczywiście możemy stworzyć bardziej zaawansowany interfejs webowy z użyciem plików CSS i JavaScript. Jedyne, co nas ogranicza, to miejsce w pamięci – ale nic nie stoi na przeszkodzie, by wykorzystać także kartę MicroSD, co omówiliśmy już w 4. odcinku kursu MicroPythona (EP 08/2025).
Strona index.html
Zacznijmy od omówienia kodu strony internetowej zaprezentowanego na listingu 1. Ponieważ jest to kurs MicroPythona, a nie HTML, chciałem, by strona była możliwie jak najprostsza, ale przy zachowaniu ładnego wyglądu. Omówimy skrótowo najważniejsze elementy naszej strony.
<head>
<title>MicroPython ESP32</title>
<meta charset="UTF-8">
<link rel="icon" href="data:image/png;base64,iVBORw0KG
goAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12P
4//8/AAX+Av7czFnnAAAAAElFTkSuQmCC">
<style>
html {
font-family: Arial;
display: inline-block;
margin: 0px auto;
text-align: center;
color: white;
}
body {
background: -webkit-radial-gradient(#7b0191, #26002f);
}
h1 {
font-size: 250%;
padding: 20px;
}
h2 {
font-size: 200%;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
h3 {
font-size: 175%;
margin-top: 0.5em;
margin-bottom: 0.5em;
}
button {
border: 1px solid;
background-color: #3b0043;
border-radius: 40px;
color: white;
font-size: 200%;
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
text-decoration: none;
display: inline-block;
padding: 20px 20px 20px 20px;
margin: 0px 10px 0px 10px;
cursor: pointer;
width: 100%;
-webkit-box-shadow: 0 0 5px 5px #fcf584;
}
button:hover {
background-color: #5b0467;
-webkit-box-shadow: 0 0 5px 5px #ffffff;
}
.row {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
max-width: 1000px;
margin: 20px auto;
}
.cell {
display: flex;
flex-direction: column;
align-items: center;
border: 1px solid;
border-radius: 40px;
padding: 0px 20px 0px 20px;
margin: 0px 10px 0px 10px;
width: 100%;
-webkit-box-shadow: 0 0 5px 5px #fcf584;
}
</style>
</head>
<body>
<h1>MicroPython ESP32</h1>
<div class="row">
<div class="cell">
<h2>Temperatura CPU</h2>
<h3>AA °C</h3>
</div>
<div class="cell">
<h2>Moc sygnału WIFI</h2>
<h3>BBB RSSI</h3>
</div>
</div>
<div class="row">
<div class="cell">
<h2>Aktualny kolor diody WS2812</h2>
<h3>CCCCCCCCC</h3>
</div>
</div>
<form class="row">
<button type="submit" name="color" value="red">Czerwony</button>
<button type="submit" name="color" value="yellow">Żółty</button>
<button type="submit" name="color" value="green">Zielony</button>
<button type="submit" name="color" value="white">Biały</button>
</form>
<form class="row">
<button type="submit" name="color" value="cyan">Błękitny</button>
<button type="submit" name="color" value="blue">Niebieski</button>
<button type="submit" name="color" value="magenta">Fioletowy</button>
<button type="submit" name="color" value="black">Czarny</button>
</form>
</body>
</html>
Listing 1. Kod pliku index.html – cd.
Strona ma układ tabelaryczny. Każdy wiersz rozpoczyna się instrukcją <div class="row"> i kończy </div>, a wewnątrz każdego wiersza możemy umieścić dowolną liczbę komórek objętych tagami <div class="cell"> oraz </div>. Szerokość komórek dostosuje się automatycznie w zależności od tego, ile ich jest w wierszu.
Pola, które mają się zmieniać, tzn. odczyty temperatury, mocy sygnału oraz koloru diody, w kodzie są reprezentowane przez znaki AA, BBB i CCCCCCCCC. Dlaczego tak dziwnie? Zanim strona zostanie przesłana przez Wi-Fi, w miejsce tych znaków zostaną bowiem wstawione rzeczywiste wartości. Jest to metoda bardzo prymitywna, bo odświeżenie tych zmiennych wymaga wygenerowania i przesłania całej strony od nowa. Bardziej doświadczeni programiści mogliby tu zastosować JavaScript, który cyklicznie wysyła zapytania o konkretne zmienne i aktualizuje tylko wybrane fragmenty strony.
Komunikacja od użytkownika do ESP32 zrealizowana jest przy pomocy formularza, który zaczyna się od <form class="row">. Przyciski tworzone są za pomocą instrukcji o następującej budowie:
Kliknięcie takiego przycisku sprawi, że aktualne wyświetlana strona zostanie załadowana ponownie, ale do adresu strony zostaną doklejone wartości name i value z klikniętego przycisku. W tym przypadku zostałby wywołany adres index.html?color=red. Jak już zapewne się domyślasz, program w MicroPythonie będzie sprawdzał, czy w adresie strony występuje słowo-klucz color oraz jaka jest mu przypisana wartość i na podstawie tego odpowiednio wysteruje diodę WS2812.
Serwer HTTP
„Własny serwer” – choć brzmi to poważnie, to w rzeczywistości całość będzie ograniczała się tylko do jednego pliku z kodem. Nasz program jest zrealizowany w sposób wielowątkowy, przy pomocy tasków z modułu _thread. Taki sposób pisania programu ułatwia podział zadań – różne czynności mogą być dzięki temu wykonywane równolegle i niezależnie od siebie. W naszym przykładzie jeden task będzie pełnił funkcjonalność serwera HTTP, a cztery taski zajmą się mruganiem diodami LED z różną częstotliwością.
Serwer HTTP powinien jak najszybciej odpowiadać na zapytania. Jednak pewne czynności, jak np. uruchomienie pomiaru w czujniku i pobranie wyniku, mogą zająć pewien czas. W takiej sytuacji dobrze jest zastosować taski. W jednym z nich umieszczamy czasochłonny, cykliczny proces – task przetwarza dane i ich wynik zapisuje w pamięci, aby był zawsze dostępny. Jeżeli drugi task będzie potrzebował tych danych, wówczas po prostu je odczyta, a tymczasem pierwszy może kontynuować pracę zupełnie niezależnie.
Przejdźmy do analizy pliku http_server.py, którego kod pokazano na listingu 2. Przeskoczmy od razu do linii 31, gdzie rozpoczynamy inicjalizację naszego programu, tworząc instancję klasy obsługującej diody WS2812 (Neopixel to nazwa handlowa firmy Adafruit), a następnie ustawiając wszystkie składowe koloru RGB na zero, co daje kolor czarny (czyli całkowite wygaszenie diody). Przy pomocy metody write wysyłamy do diody żądany kolor.
import _thread # 1
import esp32
import gc
import neopixel
import network
import socket
import sys
import time
import wifi_config
from machine import Pin
def led_task(gpio_num, delay_ms): # 2
led = Pin(gpio_num, Pin.OUT) # 3
while True: # 4
led(not led()) # 5
time.sleep_ms(delay_ms) # 6
def wifi_connect(): # 7
global station
station = network.WLAN(network.STA_IF)
station.active(True)
if not station.isconnected():
print("Łączenie z siecią", end="")
station.connect(wifi_config.ssid, wifi_config.password)
while not station.isconnected():
print(".", end="")
time.sleep_ms(250)
print()
global ip # 8
ip = station.ifconfig()[0]
print(f"Adres IP: {ip}")
def index_html(): # 9
gc.collect() # 10
content = ""
with open("index.html", encoding="utf-8") as file: # 11
content += file.read()
if led[0] == (0x10, 0x00, 0x00): # 12
color = "Czerwony"
elif led[0] == (0x10, 0x10, 0x00):
color = "Żółty"
elif led[0] == (0x00, 0x10, 0x00):
color = "Zielony"
elif led[0] == (0x00, 0x10, 0x10):
color = "Błękitny"
elif led[0] == (0x00, 0x00, 0x10):
color = "Niebieski"
elif led[0] == (0x10, 0x00, 0x10):
color = "Fioletowy"
elif led[0] == (0x10, 0x10, 0x10):
color = "Biały"
else:
color = "Czarny"
content = content.replace("AA", str(esp32.mcu_temperature())) # 13
content = content.replace("BBB", str(station.status("rssi")))
content = content.replace("CCCCCCCCC", color)
return content
def http_task(): # 14
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 15
sock.bind(("", 80)) # 16
sock.listen()
while True:
try: # 17
gc.collect() # 18
conn, addr = sock.accept() # 19
request = conn.recv(1024) # 20
if request == b"": # 21
conn.send("HTTP/1.1 400 Bad Request\r\n")
conn.send("Connection: close\r\n")
conn.sendall("\r\n")
conn.close()
continue
request = request.splitlines()[0] # 22
print(f"HTTP Request from {addr[0]}: {request}")
if b"GET / HTTP" in request: # 23
conn.send(f"HTTP/1.1 307 Temporary Redirect\r\n")
conn.send(f"Location: http://{ip}/index.html\r\n")
conn.send("Connection: close\r\n")
conn.sendall("\r\n")
elif b"index.html " in request: # 24
conn.send("HTTP/1.1 200 OK\r\n")
conn.send("Content-Type: text/html\r\n")
conn.send("Connection: close\r\n")
conn.send("\r\n")
conn.sendall(index_html()) # 25
elif b"color" in request: # 26
if b"red" in request:
led[0] = (0x10, 0x00, 0x00)
elif b"yellow" in request:
led[0] = (0x10, 0x10, 0x00)
elif b"green" in request:
led[0] = (0x00, 0x10, 0x00)
elif b"cyan" in request:
led[0] = (0x00, 0x10, 0x10)
elif b"blue" in request:
led[0] = (0x00, 0x00, 0x10)
elif b"magenta" in request:
led[0] = (0x10, 0x00, 0x10)
elif b"white" in request:
led[0] = (0x10, 0x10, 0x10)
else:
led[0] = (0x00, 0x00, 0x00)
conn.send("HTTP/1.1 200 OK\r\n")
conn.send("Content-Type: text/html\r\n")
conn.send("Connection: close\r\n")
conn.send("\r\n")
response = index_html()
conn.sendall(response)
led.write() # 27
else: # 28
print("Unknown request")
conn.send("HTTP/1.1 404 Not Found\r\n")
conn.sendall("\r\n")
conn.close() # 29
except Exception as e: # 30
sys.print_exception(e)
led = neopixel.NeoPixel(Pin(38, Pin.OUT), 1) # 31
led[0] = (0, 0, 0)
led.write()
wifi_connect() # 32
_thread.start_new_thread(led_task, [21, 1000]) # 33
_thread.start_new_thread(led_task, [47, 500])
_thread.start_new_thread(led_task, [48, 250])
_thread.start_new_thread(led_task, [45, 100])
_thread.start_new_thread(http_task, ()) # 34
Listing 2. Kod pliku http_server.py
W linii 32 uruchamiamy funkcję wifi_connect, której zadaniem jest połączenie się z routerem Wi-Fi. Po połączeniu modułu z siecią, funkcja wyświetla uzyskany adres IP na konsoli. Ta funkcja jest bardzo podobna do używanej w poprzednim odcinku kursu, a jedyną różnicą jest to, że adres IP zapisywany jest także do zmiennej globalnej, aby dało się go użyć w innych miejscach programu.
W linii 33 rozpoczynamy dodawanie tasków przy pomocy funkcji start_new_thread, które mają się wykonywać w trakcie pracy programu. Pierwszym argumentem tej funkcji jest nazwa tasku, a drugim – krotka lub lista, która jest przekazywana do wskazanej wcześniej funkcji jako argument. W taki sposób tworzymy cztery wątki led_task, które mają mrugać diodami oraz wątek serwera http_task.
Przejdźmy do linii 2, gdzie tworzymy led_task. W gruncie rzeczy jest to zwykła funkcja, która przyjmuje dwa argumenty – numer pinu, który ma sterować diodą LED oraz czas oczekiwania pomiędzy włączeniem a wyłączeniem diody. Te dwa argumenty funkcja otrzymuje z listy, którą przekazaliśmy do funkcji start_new_thread. W linii 3 inicjalizujemy wskazany pin jako wyjście. Następnie mamy pętlę nieskończoną (linia 4), w której zmieniamy jedynie stan diody LED (linia 5) i usypiamy task funkcją sleep_ms (linia 6).
W linii 14 rozpoczynamy funkcję, która realizuje funkcjonalność serwera HTTP. W tej funkcji będziemy wykorzystywać sockety odpowiedzialne za nasłuchiwanie zapytań, które mogą zostać przesłane poprzez port o wskazanym numerze. Następnie musimy wygenerować odpowiedź i odesłać ją do nadawcy przy pomocy socketa.
ssid = ""
password = ""
Listing 3. Kod pliku config.py
Tworzymy instancję klasy socket i zapisujemy ją do zmiennej sock. W konstruktorze podajemy dwa argumenty. AF_INET oznacza, że zamierzamy stosować „stare” adresy IP w standardzie IPv4. Drugi argument SOCK_STREAM oznacza, że skorzystamy z protokołu TCP. Aby użyć w tym miejscu protokołu UDP, należałoby wpisać SOCK_DGRAM.
W linii 16 konfigurujemy socket tak, aby nasłuchiwał zapytań przychodzących ze wskazanego adresu i portu. W naszym przypadku interesują nas wszystkie adresy, zatem pierwszym argumentem jest pusty ciąg znaków "". Drugi argument to numer portu – w przypadku serwerów HTTP domyślnym portem jest ten o numerze 80. Pozostaje już zatem tylko wywołać metodę listen, która uaktywnia socket.
Rozpoczynamy pętlę nieskończoną while True. Znajduje się w niej duży blok try-except (linia 17). Jest to spowodowane tym, że niektóre operacje związane z socketami mogą zgłaszać wyjątki. Jeżeli nie zostaną obsłużone, wówczas task zakończy się, czyli w rezultacie serwer HTTP przestanie działać. W razie wystąpienia jakiegoś wyjątku, po prostu wyświetlimy informację na konsoli i go ignorujemy (linia 30).
W linii 18 uruchamiamy funkcję collect z modułu gc. Jest to tzw. garbage collector, czyli funkcja, która wyszukuje w pamięci nieużywane zmienne i je kasuje. Zwykle proces ten uruchamia się automatycznie, gdy w pamięci mamy już dużo różnych zmiennych, jednak oczyszczanie jej zajmuje trochę czasu. Uruchomienie odśmiecacza w trakcie generowania odpowiedzi spowodowałoby, że klient czekałby na odpowiedź serwera dłużej niż powinien. Dlatego też uruchamiamy go ręcznie na początku pętli, aby później pracować na pamięci odciążonej z niepotrzebnych zmiennych.
W linii 19 wywołujemy metodę accept. Domyślne działanie tej funkcji polega na zawieszeniu wykonywania tasku i oczekiwaniu tak długo, aż do wskazanego portu zostanie przesłane jakieś zapytanie. Kiedy to nastąpi, metoda zwróci dwie zmienne: conn zawierającą kolejny socket z otrzymanymi danymi (i którym trzeba przesłać odpowiedź) oraz addr, w której znajdzie się adres IP urządzenia przysyłającego zapytanie.
Interesuje nas otrzymanie samego tekstu zapytania. Pobieramy go za pomocą metody recv z obiektu conn (linia 20). Poprzez argument podajemy maksymalną liczbę bajtów, jaką chcemy odczytać. W taki sposób zapisujemy zapytanie w postaci tekstu do zmiennej request.
Może się zdarzyć, że otrzymamy puste zapytanie. Sprawdzamy to w linii 21. W takiej sytuacji wysyłamy odpowiedź 400 Bad Request. Zwróć uwagę, że do formułowania odpowiedzi używamy metody send dla każdej linijki odpowiedzi, za wyjątkiem ostatniej, gdzie korzystamy z sendall. Na koniec musimy jeszcze wywołać metodę close.
Zobaczmy teraz, jak może wyglądać zapytanie, które otrzymuje ESP32 od przeglądarki internetowej. Poniżej przykład zapytania, którego celem jest pobranie pliku index.html.
Host: 192.168.0.112\r\n
Connection: keep-alive\r\n
Upgrade-Insecure-Requests: 1\r\n
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36\r\n
Accept: text/html,application/xhtml+xml,application/xml;
q=0.9,image/avif,image/webp,image/apng,*/*;
q=0.8,application/signed-exchange;v=b3;q=0.7\r\n
Accept-Encoding: gzip, deflate\r\n
Accept-Language: pl,en-US;q=0.9,en;q=0.8,pl-PL;q=0.7\r\n
\r\n
Jest tu całkiem sporo różnych informacji, ale wystarczy nam tylko pierwsza linijka zapytania. Dlatego w 22 linii kodu wywołujemy metodę splitlines, która powyższy kod konwertuje na listę. Każdym jej elementem są osobne linie, czyli ciągi znaków rozdzielone znakami \r\n. Skoro nas interesuje tylko pierwszy z nich, możemy od razu wybrać go przy pomocy operatora [ ], gdzie podajemy indeks 0, czyli pierwszy element listy. Tak otrzymaną pierwszą linijkę zapytania zapisujemy do zmiennej request.
Następnie sprawdzamy zawartość zapytania, stosując instrukcję a in b. Sprawdza ona, czy badany string a znajduje wewnątrz b. W takiej sytuacji funkcja zwraca True, a w przeciwnym wypadku False.
W linii 23 sprawdzamy, czy w treści zapytania znajduje się ciąg znaków „GET / HTTP”. Takie zapytanie otrzymamy wtedy, gdy użytkownik wpisze w przeglądarce adres IP naszego urządzenia. W takiej sytuacji możemy od razu odesłać mu plik index.html lub zastosować przekierowanie. Aby program miał więcej walorów edukacyjnych, postanowiłem pokazać, w jaki sposób można wygenerować odpowiedź 307, zawierającą przekierowanie do strony http://{ip}/index.html, gdzie oczywiście w miejscu {ip} wstawiamy adres IP modułu ESP32, jaki został nadany przez router Wi-Fi. Kiedy przeglądarka otrzyma taką odpowiedź, automatycznie wyśle kolejne zapytanie o adres podany w odpowiedzi.
W linii 24 szukamy w zapytaniu napisu „index.html „. Zwróć uwagę na to, że ostatnim znakiem w badanym stringu jest spacja. To zabieg celowy, ponieważ przeglądarka internetowa może chcieć pobrać plik index.html bez żadnych dodatkowych parametrów lub z nimi. Zatem pierwsza linia zapytania może przyjmować jedną z następujących postaci:
- GET /index.html HTTP/1.1\r\n
- GET /index.html?color=red HTTP/1.1\r\n
Póki co interesuje nas wykrywanie tego pierwszego przypadku. Formułując odpowiedź na zapytanie, odpowiadamy kodem 200 OK, po czym wstawiamy kilka dodatkowych informacji i przesyłamy treść pliku index.html (linia 25). Generujemy go funkcją index_html, która rozpoczyna się w linii 9. Zadaniem funkcji jest odczytanie pliku index.html (linia 9), który zapisany jest w systemie plików MicroPythona tak samo, jak wszystkie pliki z kodem. Zawartość tego pliku ładujemy do zmiennej content. Musimy w niej podmienić trochę danych. Są to: aktualnie ustawiony kolor diody LED, temperatura procesora oraz moc sygnału RSSI.
Kolor diody odczytujemy ze zmiennej led, która jest listą, przechowującą składowe RGB wszystkich diod WS2812. Każdy z nich składa się z krotek, zawierających trzy składowe: czerwoną, zieloną i niebieską, w zakresie od 0 do 255. Mamy tylko jedną taką diodę, więc sprawdzamy zerowy element tej listy, czyli led[0]. Badamy te składowe przy pomocy drzewka decyzyjnego if-elif-else i słowną nazwę wybranego koloru zapisujemy do zmiennej color.
W kodzie HTML naszej strony pozostawiliśmy napisy takie jak „AA”, „BBB” oraz „CCCCCCCCC”. W ich miejsce musimy wstawić odpowiednio temperaturę procesora, moc sygnału oraz kolor diody WS2812. Aby to zrobić, wykorzystamy metodę replace. Jej działanie polega na znalezieniu napisu, podanego w pierwszym argumencie i zastąpieniu go przez napis z drugiego argumentu. Nowy string jest zwracany przez wartość, ale nic nie stoi na przeszkodzie, by zapisać go w zmiennej, która przechowywała modyfikowany napis.
W linii 13 korzystamy z metody replace, aby w miejscu „AA” wstawić wartość temperatury procesora zwróconą przez funkcję mcu_temperature z modułu esp32. Należy pamiętać, że replace operuje na stringach, a temperatura procesora jest liczbą całkowitą. Dlatego musimy skorzystać jeszcze z funkcji str, która konwertuje liczbę na string. W kolejnych dwóch liniach analogicznie wstawiamy moc sygnału oraz kolor diody do treści kodu HTML, który finalnie funkcja zwraca instrukcją return.
Wróćmy teraz do serwera HTTP, a dokładniej do linii 26. W tym miejscu generujemy odpowiedź na zapytanie, które zawiera informację o tym, jaki kolor diody WS2812 ma zostać ustawiony. Najpierw sprawdzamy, czy w zapytaniu pojawia się ciąg znaków „color”, a następnie analizujemy po kolei wszystkie możliwe kolory. Składowe RGB wybranego koloru zapisujemy do zerowego elementu listy led. Pamiętaj, że taka operacja nie powoduje jeszcze zmiany koloru. Przesłanie informacji o kolorze do diody WS2812 zajmuje trochę czasu, a zależy nam na tym, aby odpowiedź na zapytanie odesłać jak najszybciej. Generujemy ją dokładnie tak samo, jak odpowiedź na pytanie o plik index.html. Dopiero po odesłaniu odpowiedzi wywołujemy metodę write z obiektu led, która inicjuje transmisję i aktualizuje kolor diody (linia 27).
Jeżeli do serwera przyjdzie zapytanie, jakiego nasz program nie potrafi obsłużyć, wówczas w linii 28 generujemy odpowiedź 404 Not Found. Na zakończenie każdego zapytania musimy jeszcze wywołać metodę close (linia 29) z obiektu conn.
Testujemy!
Pamiętaj, aby najpierw przegrać do pamięci ESP32 pliki index.html, a także wifi_config.py, gdzie musisz podać nazwę oraz hasło sieci Wi-Fi, do której nasz mikrokontroler ma się podłączyć. Dopiero wtedy możesz uruchomić plik http_server.py, wciskając przycisk F5 w edytorze Thonny.
Na konsoli powinieneś zobaczyć takie komunikaty:
MPY: soft reboot
Łączenie z siecią................
Adres IP: 192.168.0.112
>>>
Adres IP oczywiście może być inny. Skopiuj go i wklej w pasku adresu przeglądarki internetowej na komputerze, telefonie, tablecie lub jakimkolwiek innym urządzeniu zdolnym do połączenia z Wi-Fi i obsługi stron internetowych (może być nawet smart TV). Pamiętaj, że urządzenie oraz ESP32 muszą być połączone z tą samą siecią Wi-Fi. Powinieneś zobaczyć stronę taką, jaką widać na rysunku 1. Tymczasem w konsoli pokażą się następujące komunikaty:
HTTP Request from 192.168.0.173: b’GET /index.html HTTP/1.1’
To znaczy, że komputer chciał pobrać stronę „/” czyli stronę główną. W odpowiedzi dostał informację, że ma wysłać ponowne zapytanie o plik index.html. W kolejnej linii widzimy, że urządzenie faktycznie wysłało kolejne zapytanie, tym razem o plik index.html.
Na stronie, która ukazała się w przeglądarce, wybierz jeden z kolorów. Dioda powinna zaświecić się na wybrany kolor – wtedy w konsoli zobaczysz, że nazwa koloru przekazywana jest jako parametr zmiennej color, która znajduje się w zapytaniu zaraz po nazwie żądanego pliku.
b’GET /index.html?color=green HTTP/1.1’
HTTP Request from 192.168.0.173:
b’GET /index.html?color=red HTTP/1.1’
HTTP Request from 192.168.0.173:
b’GET /index.html?color=blue HTTP/1.1’
Podsumowanie
W tym odcinku kursu MicroPythona zaprezentowaliśmy, w jaki sposób można stworzyć bardzo prosty HMI w oparciu o interfejs webowy. Nic nie stoi na przeszkodzie, by zrobić to w sposób bardziej profesjonalny, korzystając z JavaScript, AJAX czy innych technologii.
W kolejnym odcinku odwrócimy role. To nie ESP32 będzie łączył się do access pointa, lecz sam stanie się access pointem, do którego będzie mógł się podłączyć komputer lub telefon. Aby było ciekawiej, opracujemy serwer DNS i wykorzystamy Captive Portal, aby otworzyć stronę internetową na komputerze lub telefonie samoczynnie zaraz po tym, jak urządzenie nawiąże połączenie przez Wi-Fi.
Dominik Bieczyński
leonow32@gmail.com
• Repozytorium kursu na GitHubie https://github.com/leonow32/micropython
• Dokumentacja modułu network https://docs.micropython.org/en/latest/library/network.html