Fyzikální kabinet FyzKAB

Webový klient v MicroPython
(aneb Hej ESP32, načti web!)

Pomalu ale jistě pokračujeme v našem článkovém seriálu o programování modulu ESP32 v MicroPythonu. Dnes bychom se rádi podívali na to, jak v MicroPythonu vytvořit jednoduchý webový klient, který se přihlásí k Wi-Fi síti a načte nějaký obsah z webového serveru.

V minulých článcích, zejména v ESP32 – jak na Wi-Fi v MicroPython?, jsme si ukázali jak nastavit modul ESP32 do režimu stanice (STA) a jak se připojit k Wi-Fi. Dnes budeme tyto dovednosti používat, ale už si je nebudeme znova vysvětlovat. Bude-li tedy z této části v dále uváděných kódech něco nejasné, vraťte se k předchozímu článku.

Připomeňme si, jak funguje základní komunikace počítačů spojených do sítě Internet. Z našeho pohledu zpravidla platí, že jeden počítač nabízí nějaké své služby (tzv. server), zatímco jiný „počítač“ (tzv. klient) tyto síťově služby na základě svých dotazů využívá. Kupříkladu při načítání této stránky musel Váš počítač/mobil prostřednictvím webového prohlížeče zaslat požadavek na poskytnutí HTML stránky počítači, na kterém jsou hostovány naše stránky. Program, který běží na počítači poskytujícím hosting musel tento požadavek vyhodnotit a pokud vyhovuje zadaným pravidlům, vrátil zpět textový obsah dané HTML stránky. Tím tato relace v podstatě skončila. Ale… Váš webový prohlížeč začal stránku vykreslovat na obrazovku a narazil na požadavek obrázku. Musel tedy odeslat na server nový dotaz, jehož výsledkem byla binární data potřebného obrázku. A tak dále… Jistě je potřeba načíst další obrázky, stejně tak CSS soubor určující vzhled stránek, nějaký ten JavaScript atd. Mezi klientem (webový prohlížeč) a serverem (počítačem hostujícím webové stránky) proběhlo hned několik dotazů a odpovědí. Jakmile je ve Vašem počítači vše, co je potřeba, spojení definitivně končí.

Pro vytvoření programu, který bude zasílat dotazy na nějaký server, budeme v prvé řadě muset umět tyto dotazy vytvořit a odeslat. Stejně tak se budeme muset umět vypořádat s odpovědí serveru – například rozlišit, co jsou tzv. hlavičky, která asi nebudeme chtít zobrazit, a naopak co je relevantní obsah stránky. Postupně si to ukážeme na jednoduchém příkladu. Tak pojďme na to!

Připojení k Wi-Fi síti

První část našeho programu již bude standardní – musíme náš modul ESP32 připojit k Wi-Fi síti, které má přístup k internetu. Základní kód přihlášení k Wi-Fi zde již byl, takže si zde dnes uvedeme jeho trochu upravenou verzi, ať následující kód má aspoň nějakou „přidanou“ hodnotu:

# pripojeni knihoven
import network
import time

# Nastavení Wi-Fi
ssid = 'nazev wi-fi site'
pwd = 'heslo wi-fi site'

connect_status = {
    network.STAT_IDLE: "zadna aktivita",           # STAT_IDLE - žádné připojení a žádná aktivita
    network.STAT_CONNECTING: "pripojeni probiha",  # STAT_CONNECTING - probíhá připojení
    network.STAT_WRONG_PASSWORD: "chybne heslo",   # STAT_WRONG_PASSWORD - selhalo z důvodu nesprávného hesla
    network.STAT_NO_AP_FOUND: "AP nenalezen",      # STAT_NO_AP_FOUND - selhalo, nebyl nalezen přístupový bod
    network.STAT_GOT_IP: "Uspesne pripojeni"       # STAT_GOT_IP - připojení se podařilo
}

# pripojeni k Wi-Fi
sta_if = network.WLAN(network.STA_IF)   # nastaveni rezimu STA
sta_if.active(True)                     # zapnuti sitoveho pripojeni

if not sta_if.isconnected():     # neni-li ESP32 pripojen, zacne se pripojovat
    sta_if.connect(ssid, pwd)    # autorizace pristupu
    print("Pripojuji se k Wi-Fi")
    n = 0
    while (not sta_if.isconnected()) and (n < 10):   # cekaci smycka
        n += 1                                        # timeout
        print(".", end="")
        time.sleep(1)

sta_ifstatus = sta_if.status()
print("\nVysledek pripojeni:", connect_status[sta_ifstatus])
if (sta_ifstatus == network.STAT_GOT_IP):
    STAconf = sta_if.ifconfig()     # nacteni informaci pripojeni
    print("\nSTA-IP:\t\t", STAconf[0], "\nSTA-NETMASK:\t", STAconf[1], \
          "\nSTA-GATEWAY:\t", STAconf[2], "\nSTA-DNS:\t", STAconf[3], sep="")
    print("---")
else:
    print("KONEC pripojovani!")
    sta_if.active(False)
    while True:
        pass

Přidanou hodnotou kódu je zpracování stavu připojení k Wi-Fi síti. K tomu nám slouží základní definice možných výsledků připojení – definováno v proměnné connect_status. Už asi víme, že se jedná o tzv. slovník, tedy strukturu dvojic klíč:hodnota. Zde se jedná o možné návratové hodnoty metody status() a jejich textové podoby určené pro vytištění na výstup.

Samotné přihlášení běží tak, jak jsme kdysi si řekli. Využíváme verzi kódu s proměnnou n, která hlídá desetivteřinový timeout, po kterém připojování skončí. V jednom z minulých článků jsme se zmínili, že by bylo třeba výsledek připojení nějak vyhodnotit. Dokonce to byl domácí úkol pro zvídavé čtenáře. Nyní tedy uvádíme jedno z možných řešení tohoto domácího úkolu.

Zavoláním metody status získáme do proměnné sta_ifstatus výslednou hodnotu, která charakterizuje stav připojení.

sta_ifstatus = sta_if.status()

Tuto hodnotu převede do textové podoby pomocí proměnné connect_status, vypíšeme ji na výstup a následuje podmínka, která testuje, zda úspěšně došlo k připojení k AP:

if (sta_ifstatus == network.STAT_GOT_IP):

V případě bezproblémového připojení se načtou další údaje Wi-Fi připojení:

STAconf = sta_if.ifconfig()

Jejich následné vypsání na výstup jsme tu již měli. Pokud nastal problém, vypne se na modulu ESP32 Wi-Fi a program skončí nekonečnou smyčkou.

Klient, načtení stránky z URL

Jsme-li připojeni k Wi-Fi začneme řešit zaslání dotazu modulu ESP32 na nějaký server a zpracování odpovědi. Pro nás účel zkusíme načíst textový soubor připravený přímo pro tuto ukázku na adrese našeho webu: http://kabinet.fyzika.net/ESP32/MicroPython/jsem-tu.txt.

Pro vytvoření komunikačního kanálu mezi dvěma zařízeními se v Pythonu obecně používá knihovna socket. V MicroPythonu tato knihovna není, ale existuje její „micro“ verze, která se jmenuje usocket. Abychom dosáhli určité univerzálnosti programu, využijeme aliasování knihovny, které jsme si zde již ukazovali u knihovny ntptime (článek: ESP32: Čas z NTP serveru v MicroPythonu). Naimportujeme tedy knihovnu usocket, ale přiřadíme ji alias socket.

import usocket as socket

Jestliže použijeme v MicroPythonu pro knihovnu usocket alias jako je v klasickém Pythonu, bude celý kód napsán tak, že bude kupříkladu použitelný nejen na modulu ESP32, kde běží MicroPython, ale i na Raspberry, kde je plnohodnotný Python. Změna programu tedy proběhne jen na řádku vložení dané knihovny, ale nebudeme muset prohledávat celý kód, kde bychom všude měli měnit název knihovny z usocket na socket (případně obráceně).

Ukázka části kódu, která vytváří klienta, pak bude vypadat následovně. Kód je poměrně hodně doplněn komentáři, neboť už to asi přestává být úplně jednoduché. Pochopitelně důležité části si opět vysvětlíme podrobněji dále.

# tady nekde je prihlaseni k WiFi...

# pokracovani programu: klient a nacteni stranky z URL

import usocket as socket   # 'usocket' v MicroPythonu, v normálním Pythonu, použijeme 'socket'

server = "kabinet.fyzika.net"   # Název serveru, ke kterému se připojíme
port = 80                       # HTTP používá standardní port 80

# 1. Vytvoření socketu
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# AF_INET = Použití IPv4
# SOCK_STREAM = Použití TCP (místo UDP)

# 2. Připojení k serveru
s.connect((server, port))
# connect() přijímá dvojici (IP adresa nebo název domény, číslo portu)
# DNS přeloží např. "example.com" na IP adresu

# 3. Odeslání HTTP GET požadavku
s.send(b"GET /ESP32/MicroPython/jsem-tu.txt HTTP/1.1\r\nHost: kabinet.fyzika.net\r\n\r\n")
# b"" znamená, že posíláme bytový řetězec (důležité pro sockety)
# "GET /ESP32/MicroPython/jsem-tu.txt HTTP/1.1" → požadujeme konkretni stránku
# "Host: kabinet.fyzika.net" → říkáme serveru, že chceme jeho obsah
# "\r\n\r\n" → konec HTTP hlaviček (prázdný řádek)

# 4. Přijetí odpovědi
response = s.recv(1024)
# recv(1024) znamená: přijmi maximálně 1024 bajtů dat od serveru
# Pokud je odpověď delší, přijmeme jen první část

print(response)   # Výpis odpovědi serveru (hlavičky + část HTML)

# 5. Zavření spojení
s.close()
sta_if.active(False)

Důležitými parametry připojení je pochopitelně adresa serveru, ke kterému se připojujeme. Zde to bude náš server http://kabinet.fyzika.net/. Druhým důležitým parametrem připojení je tzv. port.

server = "kabinet.fyzika.net"
port = 80

Co je to Port?

Port je číslo, které se používá k identifikaci konkrétního komunikačního kanálu (nebo služby) na serveru v rámci síťové komunikace. Každý počítač nebo server má svou IP adresu, která je podobná „adresnímu označení“ zařízení v síti, ale pomocí portu se rozlišuje, která konkrétní služba nebo aplikace na daném zařízení je určena pro komunikaci. Každá služba má obvykle předem stanovený port, na kterém běží. Na jednom serveru může běžet více různých služeb (web, databáze, FTP atd.), každá na jiném portu. Bez portu by server nevěděl, jakou konkrétní službu má klient požadovat.

Představme si to takto:

  • IP adresa je jako adresa domu – určuje, kde se zařízení nachází v síti (jako dům ve městě).
  • Port je jako číslo bytu v daném domě – určuje, kterou konkrétní domácnost navštívíme.
    Asi je rozdíl mezi návštěvou „pana HTML“, „paní FTP“ nebo domovníka „HTTPS“. A to nemluvě o tom problémovém nájemníkovi „MySQL“. 😊

Pokud se budeme připojovat k webovému serveru pomocí HTTP, zpravidla použijeme port 80. Pokud bychom se připojovali k HTTPS, musel by to být port 443.

Socket

Známe-li místo, kam se chceme připojit, musíme vytvořit tzv. socket. A co je to ten socket? Učená definice nám odpoví, že TCP socket je síťový komunikační objekt, který umožňuje výměnu dat mezi dvěma zařízeními přes internet nebo lokální síť. Funguje na principu TCP (Transmission Control Protocol), což je protokol, který zajišťuje spolehlivý a uspořádaný přenos dat. TCP socket umožňuje spolehlivou a uspořádanou komunikaci mezi zařízeními.

Jako rádoby ilustrativní příklad takového socketu si zkusíme představit situaci, kdy voláme na informace a položíme nějaký dotaz:

  1. Navázání spojení (Handshake)
    1. Zvedneme telefon a zavoláme na kulinářské informace.
    2. Zaměstnanec hovor přijme a řekne „Dobrý den, jak vám mohu pomoci?“
    3. My řekneme „Chci vědět, recept na pizzu.“
    4. Zaměstnanec odpoví „Dobře, jakou pizzu máte na mysli?“

Tímto procesem jste navázali spolehlivé spojení (proběhl tzv. TCP handshake).

  1. „Objednávka“ (Předání požadavku)
    1. Řekneme: „Chci recept na pizzu Hawai.“
    2. Zaměstnanec potvrdí zadání, aby se ujistil, že to bylo správně („OK, mám Vám najít recept na pizzu Hawai, je to tak?“).
    3. Pokud došlo k chybě, můžeme ji opravit (podobně jako TCP opravuje ztracené pakety).

Tím je zajištěno, že naše „objednávka“ dorazila správně a kompletní.

  1. Získání informací (Odpověď)
    1. Zaměstnanec infolinky nalistuje v kuchařce potřebnou stránku a začne číst recept na pizzu Hawai.
    2. Informace přijmeme a zpracujeme – zapíšeme si je, začneme podle nich vařit…
    3. Poděkujeme a zavěsíte

Spojení je ukončeno (TCP FIN-ACK handshake).

Pro práci se sockety budeme v MicroPythonu, jak už jsme naznačili, používat knihovnu usocket, která poskytuje rozhraní pro práci se síťovými sockety. Metoda socket() připraví nový socket, který může být použit pro síťovou komunikaci.

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

Po provedení tohoto příkazu vznikne objekt s, který představuje TCP socket komunikující přes IPv4.

Význam jednotlivých parametrů

  • AF_INET (Address Family - Internet): Určuje, že socket bude používat IPv4 adresy (například 192.168.1.1); alternativně lze použít socket.AF_INET6 pro IPv6 adresy.
  • SOCK_STREAM: Určuje, že se vytvoří TCP socket, což znamená, že spojení bude spolehlivé, orientované na spojení a bude poskytovat tok dat v přesném pořadí; alternativně lze použít socket.SOCK_DGRAM pro UDP socket, který je méně spolehlivý, ale rychlejší. (Něco jako bychom místo telefonního hovoru poslali na infolinku SMS a pak se divili, že nám nikdo neodpověděl 😉)

Připojení k serveru a metoda send()

Máme-li připravený objekt socketu s, což je nízkoúrovňový objekt pro síťovou komunikaci, připojíme jej na zadaný server a port (Pozor, dvojice server a port musí být ve  společné závorce!):

s.connect((server, port))

a následně hned odešleme dotaz:

s.send(b"GET /ESP32/MicroPython/jsem-tu.txt HTTP/1.1\r\nHost: kabinet.fyzika.net\r\n\r\n")

Zaměřme se na argument metody send(). Tento řetězec je tzv. HTTP GET požadavek, který říká serveru, co klient (aplikace nebo webový prohlížeč) požaduje.

b"GET /ESP32/MicroPython/jsem-tu.txt HTTP/1.1\r\nHost: kabinet.fyzika.net\r\n\r\n"

V tomto případě jde o bytový řetězec (prefix b před uvozovkami označuje, že jde o řetězec obsahující byty, nikoli text). V HTTP komunikaci je důležité posílat data v podobě bytu, protože některé protokoly, včetně HTTP, očekávají binární formát.

Pojďme ale na to trochu podrobněji. Důležité v tomto řetězci jsou mezery a odřádkování (znaky \r\n je CRLF (Carriage Return Line Feed), což je znak pro ukončení řádku v HTTP protokolu.)

První část (1. řádek):

GET /ESP32/MicroPython/jsem-tu.txt HTTP/1.1

  • GET označuje metodu HTTP požadavku, která říká serveru, že klient chce získat (stáhnout) nějaký zdroj (např. stránku nebo soubor).
  • /ESP32/MicroPython/jsem-tu.txt je cesta k souboru nebo zdroji, který si klient žádá. V tomto případě jde o soubor jsem-tu.txt, který se nachází v podadresáři /ESP32/MicroPython na serveru (vztaženo k základní adrese serveru, zde: kabinet.fyzika.net).
  • HTTP/1.1 určuje verzi HTTP protokolu, která je v tomto požadavku použita. Tato verze HTTP byla standardem od roku 1999 a stále je široce používána.

Druhá část: (2. řádek):

Host: kabinet.fyzika.net

  • Host určuje doménu nebo IP adresu serveru, na který je požadavek směřován. Tento konkrétní požadavek je odesílán na server kabinet.fyzika.net.

Poslední znaky:

\r\n\r\n

  • Tento znakový pár \r\n\r\n označuje konec hlaviček HTTP požadavku. První pár \r\n ukončuje poslední hlavičku, a druhý pár \r\n označuje konec všech hlaviček. (Tento formát je součástí specifikace protokolu HTTP.)

Zpracování odpovědi serveru

Dotaz jsme odeslali, nyní je práce na straně serveru. My si jen vyžádáme potřebnou odpověď:

response = s.recv(1024)

Do proměnné response se nám načte kompletní odpověď serveru. Protože taková odpověď může být docela veliká (co kdybychom chtěli třeba načíst soubor nějakého filmu!), je zde parametrem 1024, který odpověď omezuje na 1024 bytů. Předpokládáme načtení krátkého ukázkového textového souboru, tak velikost odpovědi 1 kB bude asi dostačující.

Následně proměnnou response vypíšeme na výstup.

print(response)

Nakonec nezapomeňme „zavěsit“, tedy ukončit socket:

s.close()

Lze i kompletně modul ESP32 odpojit od Wi-Fi (že by jako vytrhnout telefon ze zdi? 😊)

sta_if.active(False)

Abychom získali kompletní (a fungující!) program, musíme dva předešlé kódy spojit, tedy kód pro připojení k Wi-Fi a kód klienta. Ještě než zde uvedeme kompletní kód webového klienta, podíváme se na data, která nám server vrátí. Na zadané adrese je připraven textový soubor jsem-tu.txt, který obsahuje následující text:

AHOJ!
Jsem ukazkovy textovy soubor.
GRATULUJI!
Pokud toto ctes, klient na ESP32 ti v Pythonu funguje.

A nyní se podívejme na následující obrázek, který ukazuje v REPL prostředí Thonny získaný výstup:

nacteni dat klientem 1

Po hlášce o připojení k Wi-Fi ve výstupu vidíme dlouhý řetězec začínající znaky: b'http/1.1 200 OK…

Jak už víme, znak b nám označuje binární řetězec, jehož kódy mají význak znaků zde zobrazené v následných apostrofech. Možná v tomto řetězci zahlédneme i naše „staré známé“ znaky odřádkování (\r\n). Výstup serveru pochopitelně budeme chtít v textové podobě, takže jej do textu převedeme. K tomu použijeme metodu decode().

print(response.decode())

Získáme následující výstup:

nacteni dat klientem 2

Výstup je už v textové podobě, dokonce již došlo k naformátování odpovědi na řádky. Na druhou stranu vidíme, že tam tohoto textu je trochu více, než je jen obsah našeho souboru! Je třeba si uvědomit, že tak jako jsme my zasílali dotaz v podobě jakési hlavičky, tak stejně odpovídá i server. Odpověď má dvě hlavní části (oddělené znaky \r\n\r\n).

První částí je hlavička obsahující např. stavový kód odpovědi 200 OK (úspěšné načtení). Pokud by soubor jsem-tu.txt nebyl nalezen, nebo třeba došlo k odmítnutí jeho požadavku, byla by to odpověď 404 Not Found, resp. 403 Forbidden apod. V hlavičce odpovědi se dovíme i čas odpovědi, typ serveru, ale třeba i datum kdy byl dotázaný soubor naposledy změněn, či velikost těla souboru v bytech. Některé části hlavičky jsou povinné, jiné mohou být volitelné. Na to se podíváme, až budeme z modulu ESP32 vytvářet server, který by takové odpovědi měl generovat a odesílat. Druhou částí odpovědi (zbytek odpovědi za znaky \r\n\r\n) je samotný obsah dotazovaného souboru.

Budeme si tedy pamatovat, že HTTP odpověď má obecně následující formát:

  1. Hlavičky: Oddělené od těla odpovědi prázdným řádkem.
  2. Tělo: To, co následuje po hlavičkách, je obsah, který může být textový soubor, HTML, JSON atd.

Pokud potřebujeme, aby výpis těla souboru obsahoval odřádkování (například pokud je soubor víc než jeden řádek), Python to zvládne přirozeně, protože texty budou odděleny novými řádky, jak to je běžné v textových souborech. Pokud bychom měli jiný typ obsahu (např. HTML nebo JSON), můžeme přidat speciální zpracování podle typu souboru (kontrolou hlavičky Content-Type), ale základní dělení a výpis funguje i pro většinu běžných textových souborů.

Aby byl náš klient kompletní, zkusíme tedy odpověď serveru rozdělit na dvě hlavní části – tj. na hlavičky a na tělo dokumentu.

Příkaz response.split(b'\r\n\r\n', 1)

Pro rozdělení odpovědi na hlavičky a tělo je použito metody split(). Metoda split() je standardní metodou v Pythonu, která rozdělí řetězec nebo bytový řetězec podle zadaného oddělovače a vrátí seznam obsahující oddělené části. V tomto případě je metoda split() aplikována na byty – proto používáme oddělovač b'\r\n\r\n', což je bytová sekvence. Tato metoda vezme obsah proměnné response a rozdělí ho podle první nalezené sekvence dvou prázdných řádků (\r\n\r\n). To je typické oddělení mezi hlavičkami HTTP odpovědi a tělem odpovědi. V HTTP protokolu je mezi hlavičkami a tělem prázdný řádek (což je právě tento znak \r\n\r\n).

Argument 1 v metodě split(b'\r\n\r\n', 1) znamená, že Python má rozdělit řetězec pouze dle prvního výskytu sekvence \r\n\r\n, tedy pouze na dvě části. Pokud bychom použili split(b'\r\n\r\n') bez argumentu 1, výstupem by bylo seznam více než dvou položek (pokud by v odpovědi bylo více oddělení prázdných řádků, což v těle textovém souboru klidně být může).

Následující řádek:

header, body = response.split(b'\r\n\r\n', 1)

odpověď serveru tedy rozdělí a přiřadí ji do dvou proměnných (tento pythonovský trik jsme již také dříve viděli). Proměnná header bude obsahovat vše před prvním prázdným řádkem (\r\n\r\n), tedy hlavičky HTTP odpovědi. Druhá proměnná (body) bude obsahovat vše, co je v druhé části po tomto oddělení, tedy samotné tělo odpovědi (například obsah textového souboru, HTML kód, atd.).

  • Proměnná header bude obsahovat něco jako:
    HTTP/1.1 200 OK
    Content-Type: text/plain
    Content-Length: 106
  • Proměnná body bude obsahovat:
    AHOJ!
    Jsem ukazkovy textovy soubor.
    GRATULUJI!
    Pokud toto ctes, klient na ESP32 ti v Pythonu funguje.

Nyní tedy můžeme vypsat hlavičky a tělo odpovědi zcela odděleně – pochopitelně pomocí metody decode(), která provede převod bytů na text:

print(header.decode())
print(body.decode())

Následující obrázek ukazuje, jak pak bude náš klient fungovat:

nacteni dat klientem 3

A nyní již konečně musíme uvést kompletní kód celého našeho programu:

# pripojeni knihoven
import network
import time
import usocket as socket   # 'usocket' v MicroPythonu, v normálním Pythonu, použijeme 'socket'

# Nastavení Wi-Fi
ssid = 'nazev wi-fi site'
pwd = 'heslo wi-fi site'

connect_status = {
    network.STAT_IDLE: "zadna aktivita",           # STAT_IDLE - žádné připojení a žádná aktivita
    network.STAT_CONNECTING: "pripojeni probiha",  # STAT_CONNECTING - probíhá připojení
    network.STAT_WRONG_PASSWORD: "chybne heslo",   # STAT_WRONG_PASSWORD - selhalo z důvodu nesprávného hesla
    network.STAT_NO_AP_FOUND: "AP nenalezen",      # STAT_NO_AP_FOUND - selhalo, nebyl nalezen přístupový bod
    network.STAT_GOT_IP: "Uspesne pripojeni"       # STAT_GOT_IP - připojení se podařilo
}

# pripojeni k Wi-Fi
sta_if = network.WLAN(network.STA_IF)   # nastaveni rezimu STA
sta_if.active(True)                     # zapnuti sitoveho pripojeni

if not sta_if.isconnected():     # neni-li ESP32 pripojen, zacne se pripojovat
    sta_if.connect(ssid, pwd)    # autorizace pristupu
    print("Pripojuji se k Wi-Fi")
    n = 0
    while (not sta_if.isconnected()) and (n < 10):   # cekaci smycka
        n += 1                                        # timeout
        print(".", end="")
        time.sleep(1)

sta_ifstatus = sta_if.status()
print("\nVysledek pripojeni:", connect_status[sta_ifstatus])
if (sta_ifstatus == network.STAT_GOT_IP):
    STAconf = sta_if.ifconfig()     # nacteni informaci pripojeni
    print("\nSTA-IP:\t\t", STAconf[0], "\nSTA-NETMASK:\t", STAconf[1], \
          "\nSTA-GATEWAY:\t", STAconf[2], "\nSTA-DNS:\t", STAconf[3], sep="")
    print("---")
else:
    print("KONEC pripojovani!")
    sta_if.active(False)
    while True:
        pass

# klient a nacteni stranky z URL

server = "kabinet.fyzika.net"   # Název serveru, ke kterému se připojíme
port = 80                       # HTTP používá standardní port 80

# 1. Vytvoření socketu
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# AF_INET = Použití IPv4
# SOCK_STREAM = Použití TCP (místo UDP)

# 2. Připojení k serveru
s.connect((server, port))
# connect() přijímá dvojici (IP adresa nebo název domény, číslo portu)
# DNS přeloží např. "example.com" na IP adresu

# 3. Odeslání HTTP GET požadavku
s.send(b"GET /ESP32/MicroPython/jsem-tu.txt HTTP/1.1\r\nHost: kabinet.fyzika.net\r\n\r\n")
# b"" znamená, že posíláme bytový řetězec (důležité pro sockety)
# "GET /ESP32/MicroPython/jsem-tu.txt HTTP/1.1" → požadujeme konkretni stránku
# "Host: kabinet.fyzika.net" → říkáme serveru, že chceme jeho obsah
# "\r\n\r\n" → konec HTTP hlaviček (prázdný řádek)

# 4. Přijetí odpovědi
response = s.recv(1024)
# recv(1024) znamená: přijmi maximálně 1024 bajtů dat od serveru
# Pokud je odpověď delší, přijmeme jen první část

# Rozdělení odpovědi na hlavičky a tělo
header, body = response.split(b'\r\n\r\n', 1)

# Vytisknutí hlaviček
print("=== HLAVICKY ===")
print(header.decode())   # Tisk hlaviček v textovém formátu

# Tisk těla odpovědi (souboru)
print("\n=== TELO DOKUMENTU ===")
print(body.decode())     # Tělo souboru (v tomto případě text) s odřádkováním

# 5. Zavření spojení
s.close()
sta_if.active(False)

Závěrem

V dnešním článku jsme si ukázali, jak vytvořit z modulu ESP32 jednoduchý klient, který se připojí k Wi-Fi síti a načte zvolený souhlas, který je umístěn na internetu. Přestože se jedná o poměrně jednoduchý program, bylo potřeba se zaměřit na některá úskalí a vlastnosti základní komunikace v rámci protokolu TCP IP.

Pokud dnešní povídání o síťové komunikaci trochu vyděsilo, možná Vás napadá, zda by to celé nešlo řešit trochu jednodušším způsobem. Například pomocí nějaké knihovny, jako jsme to kupříkladu viděli v případě přístupu k NTP serveru.

Dobrá zpráva je, že ANO!

Rádi bychom se v některém z příštích článků podívali na to, jak model ESP32 (klientem) načítat z internetu data ve formátu JSON – konkrétně načítat aktuálního počasí z API některého z meteorologických serveru. Při té příležitosti bychom si měli ukázat i využití micropythonovské knihovny urequests, která (nejen) kód zjednoduší.


Skoro se chce povzdechnout:
Modul ESP32 stojí kolem dvou stovek, a kolik zábavy jsme s ním už zažili!
A kolik snad ještě zažijeme…“ 😉
UPOZORNĚNÍ:
Nesouhlasíme s vyřazením Newtonových zákonů, Ohmova zákona a zákona zachování energie z učiva fyziky základních škol v České republice!