Skip to main content

6.2 Upravljanje LED-icom preko WiFija

U 6.1 Povezivanje i dohvat podataka pločica je bila ta koja pita: pridružila se tvojoj Wi-Fi mreži, zatražila stranicu s web sjedišta i ispisala odgovor. Ovaj primjer to prevrće. Pločica je sada ona koju se pita. Postaje mali web server za sebe, a stranica koju otvoriš na mobitelu ili računalu pali i gasi pravu LED-icu.

Ništa oko same LED-ice nije novo. To je ista LED-ica, isti otpornik od 330 Ω i isti value() koje koristiš od 1.2 Blinkanje LED-ice. Novo je odakle uputa dolazi. Umjesto da je unaprijed upisana u skriptu, dolazi preko mreže onog trenutka kada netko pritisne tipku.

U ovoj dokumentaciji naučit ćeš:

  • Razliku između klijenta i servera, i zašto je ovaj primjer obrnut od 6.1.
  • Kako izgraditi web server iz sirovog socketa, jer MicroPython nema gotov.
  • Kako iz zahtjeva pročitati traženu adresu i sam odraditi usmjeravanje (routing).
  • Što su linije zaglavlja HTTP odgovora, i zašto ih mora slijediti prazna linija.
  • Zašto te SO_REUSEADDR spašava od greške svaki put kada ponovno pokreneš skriptu.
  • Kako nekoliko linija JavaScripta unutar stranice same drže liniju statusa svježom.

Potreban hardver:

  • 1x Soldered NULA MINI pločica
  • 1x Breadboard
  • 1x LED-ica (bilo koje boje)
  • 1x otpornik od 330 Ω
  • 3x Jumper žice
  • 1x USB-C kabel
  • Wi-Fi mreža, i mobitel ili računalo pridruženo njoj
ℹ️
Otpornik od 330 Ω nije neobavezan. LED-ica gotovo da nema vlastitog otpora, pa bez ičega što bi ograničilo struju vuče daleko više nego što su LED-ica ili pin građeni izdržati, i oboje se može oštetiti. Otpornik ide serijski s LED-icom, jedan za drugim u istom lancu, i nije važno kojim je krajem okrenut. Prstenovi mu se čitaju narančasti-narančasti-smeđi.
⚠️
Ovom primjeru ne treba veza s internetom. Za razliku od 6.1, ovdje ništa ne poseže prema web sjedištu. Pločica i uređaj s kojeg pregledavaš moraju biti samo na istoj lokalnoj mreži, razgovarajući međusobno preko tvog routera. Ta razlika je u praksi važna: gostinska mreža, ili bilo koja mreža s uključenom izolacijom klijenata, namjerno sprječava uređaje da vide jedan drugoga. Pločica će se povezati, konzola će prijaviti uspjeh, a stranica će se i dalje odbijati otvoriti. Ako se to dogodi, stavi oboje na običnu kućnu mrežu ili hotspot mobitela.

Spajanje komponenti

ℹ️
Ovaj primjer doseže samo dva pina na pločici: IO4 u redu 27 i GND u redu 30, oba na f–j strani. Tijelo pločice prekriva stupce f–i, pa su j27 i j30 rupice u koje stvarno možeš uvesti žicu. Sve ostalo odvija se dalje duž iste f–j strane, dobro odmaknuto od pločice.
⚠️
Red 27 postoji s obje strane pločice. Na f–j strani red 27 je IO4, a to je pin koji ovom primjeru treba. Na a–e strani taj isti red 27 je IO19, potpuno drugi pin. Nisu spojeni međusobno. Broji svoje redove na f–j strani, onoj gdje silkscreen piše IO2 IO3 IO4 IO5 3V3 GND.

1. Umetni NULA MINI pločicu na breadboard

ℹ️
Ovaj korak pretpostavlja da znaš kako je breadboard spojen iznutra i što su njegove naponske vodilice (power rails). Za uvod pogledaj stranicu dokumentacije Osnove breadboarda.

Utisni pločicu u jedan kraj breadboarda tako da njezina dva reda pinova sjednu s obje strane središnjeg kanala, s čipom okrenutim prema dolje. Zauzet će redove 25 do 30.

NULA MINI pločica postavljena na breadboard, zauzima redove 25 do 30
Korak 1: pločica postavljena na breadboard, s IO4 u redu 27 i GND u redu 30

2. Izvedi IO4 u slobodan red

IO4 je pin koji će pobuđivati LED-icu, a pločica prekriva cijeli red 27 osim krajnje rupice. Zato prva žica samo nosi taj pin nekamo gdje možeš raditi: jumper žica iz j27 duž do j19.

Plava jumper žica ide duž stupca j iz reda 27 u red 19
Korak 2: IO4 izveden iz j27 u j19

3. Dodaj otpornik od 330 Ω

Otpornik je sljedeći član lanca. Postavi ga tako da premosti red 19, red u koji je jumper žica upravo došla, i red 14, oba na f–j strani.

Otpornik od 330 ohma premošćuje redove 19 i 14 na strani f do j
Korak 3: otpornik od 330 Ω premošćuje redove 19 i 14
ℹ️
Važan je samo broj reda, a ne slovo. Svih pet rupica jednog reda na f–j strani su jedna jedina spojna točka unutar breadboarda. Zato fotografija pokazuje jumper žicu u j19 a otpornik u i19, različite rupice u istom redu, pa su već spojeni. Odaberi onu rupicu do koje nožice tvoje komponente ugodno dosežu.
ℹ️
Otpornik nema polaritet, pa nije važno kojim je krajem okrenut. Nožice su mu dugačke i lako se savijaju, i zato se izvijaju preko površine umjesto da idu ravno. To je u redu, dokle god je svaki kraj čvrsto utisnut u svoju rupicu.

4. Dodaj LED-icu

LED-ica nastavlja lanac: njezina duga nožica (anoda) u red 14, isti red u kojem otpornik završava, i njezina kratka nožica (katoda) u red 12.

LED-ica dodana, s jednom nožicom u redu 14 uz otpornik i drugom u redu 12
Korak 4: LED-ica na mjestu, duga nožica prema otporniku
⚠️
Postavi LED-icu na pravu stranu. LED-ica propušta struju samo u jednom smjeru. Duga nožica mora biti okrenuta prema otporniku i IO4; kratka nožica mora biti okrenuta prema masi. Okrenuta obrnuto, jednostavno se neće upaliti. Ništa se neće pokvariti, ali se ni ništa neće dogoditi. Ako nožice ne možeš razlikovati po dužini, potraži ravni zarez na rubu plastičnog tijela: nožica na toj strani je kratka.

5. Spoji LED-icu i pločicu na masu

Dvije jumper žice zaokružuju krug, i obje idu na plavu vodilicu, koja djeluje kao dijeljena linija mase:

  • Iz reda 12, gdje sjedi kratka nožica LED-ice, do plave vodilice.
  • Iz j30 (GND) do te iste vodilice.
Dvije narančaste jumper žice, jedna iz reda 12 i jedna iz j30, obje idu na negativnu vodilicu
Korak 5: LED-ica i GND pin pločice oboje vezani na plavu − vodilicu
ℹ️
Obje žice moraju ići u isti stupac vodilice, onaj koji ide uz plavu liniju. Vodilica uz ružičastu liniju je zaseban pojas i nije spojena s njom.

Krug je sada jedan neprekinut lanac: IO4 → red 19 → otpornik → red 14 → LED-ica → red 12 → vodilica → GND. Struja može teći samo kada skripta dovede IO4 u visoko stanje, a to je točno ono što će tipka ON napraviti.

6. Spoji pločicu na računalo

Utisni USB-C kabel u pločicu i u računalo. PWR LED-ica na pločici odmah se upali, ali zelena LED-ica na breadboardu ostaje ugašena. Skripta je namjerno gasi pri pokretanju, tako da se stranica i hardver poklapaju od prvog trenutka.

Gotov krug sa spojenim USB-C kabelom, upaljenom PWR LED-icom i ugašenom zelenom LED-icom
Korak 6: napojeno. PWR LED-ica je upaljena; zelena LED-ica je ugašena, kako skripta i namjerava

Kako pločica postaje web server

U 6.1 pločica je bila klijent. Odabrala je web sjedište, zatražila stranicu i čekala odgovor. Sve je počelo od pločice.

Ovdje je server, što znači da radi obrnuto: sjedi i čeka, a razgovor pokreće nešto drugo. Kada u preglednik (engl. browser) upišeš adresu pločice, preglednik pošalje zahtjev a pločica odgovori. Pločica nikada ne odlučuje kada se to dogodi. Tvoj prst na tipki ON odlučuje.

Nema modula za web server, pa ga gradimo

To je najveća razlika u odnosu na Arduino verziju ovog primjera. Arduino skica uključi WebServer.h, stvori WebServer server(80), registrira funkciju obrađivača za svaku adresu pomoću server.on(), i pusti biblioteku da obavi ostalo.

MicroPython ne dolazi s takvom bibliotekom. Ono što ti umjesto toga daje je socket, sirovi mehanizam ispod svega, a web server izgrađen iz njega je oko dvadeset linija tvog vlastitog koda. To zvuči kao pogoršanje, i po udobnosti jest, ali znači da ništa nije skriveno. Svaka linija razgovora između preglednika i pločice je nešto što si ti napisao.

Otvaranje vrata

Serveru treba port, koji možeš zamisliti kao numerirana vrata na pločici. Port 80 su standardna vrata za web stranice, i zato možeš jednostavno upisati adresu a da nakon nje ništa ne dodaš. Preglednici po zadanom već pokušavaju vrata 80.

Ta vrata otvaraju četiri poziva:

server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("", 80))
server.listen(2)

socket() ga stvara, bind() ga vezuje na port 80 na svakom mrežnom sučelju koje pločica ima, a listen(2) počinje prihvaćati veze, držeći do dvije na čekanju u redu.

⚠️
setsockopt s SO_REUSEADDR je linija koja će ti nedostajati kada je nema. Port ostaje zauzet dokle god postoji socket koji ga je zauzeo. Uljudno zaustavljanje skripte izvršava except KeyboardInterrupt blok na dnu, koji poziva server.close() i vraća port, ali skripta koja je odsječena prije nego dođe do toga ostavlja svoj socket za sobom, još držeći port 80, i on ne istječe i ne pušta ga. Bez ove linije sljedeće pokretanje tada odmah padne s OSError: [Errno 112] EADDRINUSE, a čekanje ne pomaže; samo zatvaranje tog socketa ili resetiranje pločice oslobađa port. S ovom linijom bind() ipak uspije. Košta jednu liniju a poštedi te resetiranja gotovo svaki put kada urediš i ponovno pokreneš.

Čekanje, pa čitanje zahtjeva

connection, address = server.accept()
request = connection.recv(1024).decode()

accept() čeka dok se preglednik ne poveže. Vraća dvije stvari: vezu preko koje se razgovara i adresu onoga koji se povezao. recv(1024) zatim čita do 1024 znaka onoga što su poslali, a decode() te sirove bajtove pretvara u tekst.

ℹ️
accept() blokira. Skripta se zaustavi na toj liniji i čeka, ponekad minutama, dok zahtjev ne dođe. Zato petlja izgleda toliko drugačije od Arduino verzije, gdje se server.handleClient() vraća odmah, bilo da je nešto čekalo ili ne. Ovdje je pločica stvarno parkirana na toj liniji, što je za ovaj primjer u redu, jer joj je odgovaranje na stranicu jedini posao.

Sam odrađuješ usmjeravanje

Prva linija svakog HTTP zahtjeva izgleda ovako:

GET /led/on HTTP/1.1

Tri dijela odvojena razmacima: metoda, adresa i verzija protokola. Skripta izvlači srednji:

first_line = request.split("\r\n")[0]
parts = first_line.split(" ")
path = parts[1] if len(parts) > 1 else "/"

split("\r\n")[0] uzima prvu liniju zahtjeva, split(" ") je lomi na njezina tri dijela, a [1] je adresa. if len(parts) > 1 else "/" na kraju je sigurnosna mreža za neispravno oblikovan zahtjev koji nema drugi dio, a koji bi inače srušio server.

Lanac if i elif zatim odlučuje što svaka adresa znači. To se naziva usmjeravanje (routing), i to je ista lista koju Arduino obrađivači implementiraju, samo ispisana na otvorenom:

AdresaŠto skripta radi
/led/ondovodi IO4 u visoko stanje i odgovara on
/led/offdovodi IO4 u nisko stanje i odgovara off
/led/statusčita IO4 i prijavljuje njegovo trenutno stanje
sve drugo, uključujući /šalje samu web stranicu
ℹ️
"on" if led.value() else "off" je uvjetni izraz: uzima prvu vrijednost kada je uvjet istinit i drugu kada je lažan, sve u jednoj liniji. I primijeti što čita: led.value() na izlaznom pinu čita natrag ono što pin trenutno piše, pa je status pravo mjerenje pravog pina, a ne varijabla za koju se skripta nada da je još točna.

Ručno odgovaranje

HTTP odgovor nije samo sadržaj. Počinje s nekoliko linija koje ga opisuju, a skripta ih piše sama:

connection.send("HTTP/1.1 200 OK\r\n")
connection.send("Content-Type: " + content_type + "\r\n")
connection.send("Connection: close\r\n\r\n")
connection.sendall(body)

Prva linija nosi kod odgovora. 200 OK je "izvoli", isti onaj broj koji je 6.1 bio zadovoljan primiti. Content-Type govori pregledniku dobiva li web stranicu (text/html) ili čisti tekst (text/plain).

⚠️
\r\n\r\n na kraju posljednjeg zaglavlja radi pravi posao. Prazna linija je ono što razdvaja zaglavlja od sadržaja, a preglednik nastavlja čitati zaglavlja dok ne nađe jednu. Pošalji tamo samo \r\n i preglednik početak tvog HTML-a tretira kao još zaglavlja, i stranica se nikada ne pojavi.

Zatim:

connection.close()

Ovo završava taj jedan razgovor. Bez toga preglednik nastavlja čekati ostatak odgovora koji nikada ne dolazi, a pločici polako nestaje memorije.

Stranica živi unutar skripte

Cijela web stranica spremljena je u skripti kao jedan dugačak dio teksta, omotan u trostruke navodnike. To je Python koji dopušta da se string proteže kroz mnogo linija i sadrži pojedinačne navodnike bez znaka za izbjegavanje pred svakim. To je isti posao koji u Arduino skici radi R"rawliteral( ... )rawliteral".

Stranica je HTML, isti jezik koji nam je web sjedište u 6.1 poslalo natrag. Sada je pločica ta koja ga šalje.

Unutar stranice je kratak dio JavaScripta, koji se izvršava u pregledniku a ne na pločici. On od /led/status traži trenutno stanje i upisuje ga u liniju statusa, pa to ponavlja svake dvije sekunde:

updateStatus();
setInterval(updateStatus, 2000);

Zato linija statusa ostaje iskrena i ako netko drugi pritisne tipku na drugom mobitelu, a i zato može zaostajati: stranica može biti do dvije sekunde iza LED-ice.

nulamini.local umjesto IP adrese

try:
network.hostname(HOSTNAME)
except Exception:
print("Setting the hostname is not supported on this firmware, use the IP address instead.")

network.hostname() zauzima ime na tvojoj lokalnoj mreži, tako da pločica može odgovarati na http://nulamini.local/ umjesto na red brojeva. Omotano je u try i except jer tu funkciju ne nudi svaki MicroPython build, a funkcija koje nema ne bi trebala zaustaviti cijeli primjer.

ℹ️
Ako se nulamini.local ne otvori, koristi IP adresu. Razrješavanje imena na lokalnoj mreži nije podržano svugdje. Windows 10 i 11, macOS i iOS svi razrješavaju .local imena bez ikakvog namještanja, ali neke verzije Androida i neki routeri ne. Točno zato skripta ispisuje IP address:. Upiši te brojeve u svoj preglednik i doći ćeš na istu stranicu. Ime je udobnost, a ne zahtjev.

Čisto zaustavljanje

Cijela petlja sjedi unutar try bloka koji hvata KeyboardInterrupt:

except KeyboardInterrupt:
print("Server stopped")
server.close()

KeyboardInterrupt je ono što pritisak na Stop u tvom editoru, ili Ctrl+C u terminalu, diže unutar skripte. Njegovo hvatanje daje skripti trenutak da zatvori socket, što oslobađa port za sljedeće pokretanje umjesto da ga ostavi zauzetim.


Postavljanje Wi-Fi podataka

Prije pokretanja skripte promijeni ove dvije linije da odgovaraju tvojoj mreži:

ssid = "your ssid"
password = "your password"
⚠️
Pobrini se da su ime mreže i lozinka napisani točno kako se pojavljuju, jer se razlikuju velika i mala slova.

Kod

Ispod je cijeli kod za ovaj primjer:

# Modul network sadrži sve što je potrebno za pridruživanje Wi-Fi mreži.
import network

# Modul socket je najniža razina mrežne komunikacije: dopušta nam da slušamo druge uređaje koji nam žele
# razgovarati. MicroPython nema gotov web server, pa sami gradimo mali iz socketa.
import socket

# Pin upravlja LED-icom, a time nam daje pauze koje trebamo dok se povezujemo.
from machine import Pin
import time

# Ove dvije varijable drže ime tvoje Wi-Fi mreže (SSID) i njezinu lozinku. Tvoj mobitel ili računalo mora
# biti na istoj mreži kao pločica, inače neće moći doći do stranice.
ssid = "your ssid"
password = "your password"

# Ovo je varijabla kojoj predajemo broj pina na koji smo spojili LED-icu.
# Zapamti da LED-ici treba otpornik od 330 ohma u seriji s njom.
LED_PIN = 4

# Ovo je ime koje će pločica pokušati zauzeti na tvojoj mreži. Ako ga tvoja mreža podržava, onda u pregledniku možeš
# otvoriti http://nulamini.local/ umjesto da moraš pamtiti adresu sastavljenu od brojeva.
HOSTNAME = "nulamini"

# Ovo je sama web stranica, napisana u HTML-u i spremljena kao jedan dugačak dio teksta. Tri navodnika dopuštaju nam
# da napišemo mnogo linija teksta odjednom, uključujući pojedinačne navodnike, a da svaki od njih ne izbjegavamo.
# Stranica sadrži dvije tipke i mali dio JavaScripta, koda koji se izvršava unutar preglednika i koji od pločice traži
# trenutno stanje LED-ice svake dvije sekunde, pa prikazani status sam ostaje svjež.
HTML_PAGE = """<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>NULA MINI LED Control</title>
<style>
body { font-family: Arial; text-align: center; margin-top: 50px; }
button { padding: 15px 30px; margin: 10px; font-size: 20px; }
.status { font-size: 24px; margin-top: 20px; }
</style>
</head>
<body>
<h1>NULA MINI LED Control</h1>
<button onclick="fetch('/led/on').then(()=>updateStatus())">ON</button>
<button onclick="fetch('/led/off').then(()=>updateStatus())">OFF</button>
<div class="status" id="status">Loading status...</div>

<script>
// Function that requests LED status from the NULA MINI and updates the page
async function updateStatus() {
let res = await fetch('/led/status');
let text = await res.text();
document.getElementById('status').innerHTML = 'LED is ' + text.toUpperCase();
}

// Run immediately after page load and update every 2 seconds
updateStatus();
setInterval(updateStatus, 2000);
</script>
</body>
</html>"""

# Ovdje stvaramo naš Pin objekt za LED-icu i odmah u njega pišemo 0, tako da LED-ica počne ugašena
# a status prikazan na stranici odgovara stvarnosti.
led = Pin(LED_PIN, Pin.OUT)
led.value(0)

# Ovdje stvaramo naš mrežni objekt i uključujemo Wi-Fi hardver. network.STA_IF znači da se pločica pridružuje tuđoj
# mreži, na isti način kao tvoj mobitel.
wlan = network.WLAN(network.STA_IF)
wlan.active(True)

# Ovdje zauzimamo naše ime na mreži. Omotavamo to u try blok jer ne svaki build firmwarea nudi ovu
# funkciju, a funkcija koje nema ne bi trebala zaustaviti cijeli primjer.
try:
network.hostname(HOSTNAME)
except Exception:
print("Setting the hostname is not supported on this firmware, use the IP address instead.")

# connect() pokreće pokušaj povezivanja. Ne čeka da se povezivanje dovrši, pa na njega čekamo
# sami u petlji ispod. isconnected() nam kaže jesmo li već na mreži.
print("Connecting to WiFi", end="")
wlan.connect(ssid, password)
while not wlan.isconnected():
time.sleep(0.5)
print(".", end="")

# ifconfig() kao svoju prvu vrijednost vraća adresu koju je router dodijelio našoj pločici. Upisivanje te adrese u
# preglednik na istoj mreži otvara stranicu koju smo pripremili iznad.
print()
print("WiFi connected!")
print("IP address:", wlan.ifconfig()[0])
print("Access the board in your browser at: http://" + HOSTNAME + ".local/")


def send_response(connection, content_type, body):
# Ovo je funkcija koju smo napisali sami. Odgovara na jedan zahtjev.
# Svaki HTTP odgovor počinje s nekoliko linija koje ga opisuju prije samog sadržaja: kod odgovora, gdje
# 200 znači "izvoli", i tip sadržaja koji šaljemo. Prazna linija zatim razdvaja te linije
# od sadržaja.
connection.send("HTTP/1.1 200 OK\r\n")
connection.send("Content-Type: " + content_type + "\r\n")
connection.send("Connection: close\r\n\r\n")
connection.sendall(body)


# Ovdje otvaramo vrata i počinjemo slušati. socket() stvara socket, setsockopt() nam dopušta da ponovno koristimo isti
# port odmah kada ponovno pokrenemo program umjesto da čekamo da ga sustav otpusti, bind() nas vezuje
# na port 80, a listen() počinje prihvaćati veze.
# Port je kao broj vrata na pločici: port 80 su standardna vrata za web stranice, i zato ih preglednici
# pokušavaju po zadanom i zato ih ne moramo upisivati u adresnu traku.
server = socket.socket()
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server.bind(("", 80))
server.listen(2)
print("HTTP server started")

# Cijelu petlju omotavamo u try blok tako da zaustavljanje programa s Ctrl+C, ili tipkom za zaustavljanje tvog
# editora, propisno zatvori socket umjesto da ga ostavi zauzetim.
try:
while True:

# accept() čeka dok se preglednik ne poveže na nas. Vraća nam dvije stvari: vezu preko koje možemo razgovarati,
# i adresu onoga koji se povezao.
connection, address = server.accept()

# recv() čita ono što nam je preglednik poslao, do 1024 znaka toga. Prva linija svakog zahtjeva imenuje
# adresu koja se traži, a to nam treba da odlučimo kako odgovoriti.
request = connection.recv(1024).decode()
first_line = request.split("\r\n")[0]

# Linija zahtjeva izgleda kao "GET /led/on HTTP/1.1", pa nam njezino lomljenje po razmacima i uzimanje srednjeg
# dijela daje samu adresu.
parts = first_line.split(" ")
path = parts[1] if len(parts) > 1 else "/"

# Ovdje odlučujemo što raditi sa svakom adresom. To se naziva usmjeravanje: svaka adresa dobiva svoj odgovor.
# Adrese koje počinju s /led su one koje traže tipke i JavaScript u stranici, a one
# odgovaraju kratkim dijelom čistog teksta, a ne cijelom stranicom.
if path == "/led/on":
led.value(1)
print("LED ON")
send_response(connection, "text/plain", "on")

elif path == "/led/off":
led.value(0)
print("LED OFF")
send_response(connection, "text/plain", "off")

elif path == "/led/status":

# value() čita natrag ono što pin trenutno piše, što nam kaže je li LED-ica upaljena. if i
# else u jednoj liniji je kratak način odabira između dvije vrijednosti.
send_response(connection, "text/plain", "on" if led.value() else "off")

else:

# Svaka druga adresa, uključujući obični "/", dobiva samu web stranicu.
send_response(connection, "text/html", HTML_PAGE)

# close() završava taj jedan razgovor. To je važno: bez toga bi preglednik nastavio čekati a pločici
# bi polako nestalo memorije.
connection.close()

except KeyboardInterrupt:

# Zaustavljanje programa zatvara vrata za nama, pa je port slobodan sljedeći put kada startamo.
print("Server stopped")
server.close()

Što bi trebao vidjeti

Pritisni Run. Pločica prijavljuje cijeli svoj slijed pokretanja i onda utihne, čekajući preglednik:

Connecting to WiFi......
WiFi connected!
IP address: 192.168.75.75
Access the board in your browser at: http://nulamini.local/
HTTP server started

Čitajući to od vrha:

  • Točke su pokazatelj napretka. Svaka je jedan prolaz petlje čekanja, a svaki prolaz traje 500 ms, pa je red točaka štoperica. Šest točaka ovdje znači oko tri sekunde do pridruživanja mreži. Očekuj negdje između četiri i osam; točan broj mijenja se od pokretanja do pokretanja, jer ovisi o tome kako brzo tvoj router odgovara.
  • IP address: 192.168.75.75 je adresa koju je router dodijelio. Tvoja će biti drugačija. Zadrži je. To je tvoje rezervno rješenje ako se nulamini.local ne bude htio otvoriti.
  • HTTP server started je posljednja linija. Odavde je pločica parkirana na accept(), slušajući.
⚠️
Petlja čekanja nema vremenskog ograničenja. Ako je ime mreže ili lozinka pogrešna, isconnected() nikada ne postane True, pa pločica ispisuje točke unedogled i nikada ne dođe do toga da prijavi grešku. S imenom mreže koje ne postoji veselo će ispisati stotinu točaka i nastaviti. Budući da uspješno povezivanje traži manje od deset, red točaka koji još raste nakon dvadeset nije spora mreža. To je gotovo uvijek tipfeler u podacima. Primijeti da se ime mora točno poklapati s mrežom, uključujući razmake i velika slova.

Otvaranje stranice

Kada pločica prijavi HTTP server started, otvori http://nulamini.local/ na bilo kojem mobitelu ili računalu na istoj mreži. Stranica je namjerno obična:

Preglednik prikazuje stranicu NULA MINI LED Control s tipkama ON i OFF i linijom statusa koja piše LED is OFF
Stranica kako se prvi put otvori. Linija statusa već odražava stvarnost: LED-ica je ugašena

Linija statusa nije nagađanje. Prije nego se stranica do kraja učitala, JavaScript je već pitao /led/status, a pločica je pročitala IO4 i odgovorila. LED is OFF je pravo mjerenje pravog pina, a breadboard se slaže:

Gotov krug s ugašenom zelenom LED-icom, što odgovara statusu LED is OFF prikazanom u pregledniku
Hardverska strana istog trenutka: zelena LED-ica je ugašena, točno kako stranica prijavljuje

Sada pritisni ON:

Ista stranica nakon pritiska na ON, s linijom statusa koja piše LED is ON
Nakon pritiska na ON, linija statusa slijedi

I LED-ica na breadboardu se upali:

Zelena LED-ica na breadboardu upaljena nakon pritiska na tipku ON u pregledniku
Isti krug, nepromijenjen od koraka 6, sada s IO4 dovedenim u visoko stanje iz preglednika

Između tih dviju fotografija na spajanju se ništa nije pomaknulo. Jedina razlika je napon na IO4, a on je došao od pritiska tipke negdje drugdje na tvojoj mreži.

Pritisak putuje dugačak put za nešto što izgleda tako jednostavno. Tvoj prst pokrene fetch('/led/on') u pregledniku, koji pošalje HTTP zahtjev preko tvog routera do pločice. accept() se vrati, recv() pročita liniju zahtjeva, lanac if se poklopi s /led/on, i led.value(1) se izvrši. Struja zatim teče iz IO4 kroz otpornik, kroz LED-icu i u vodilicu. Pritisak na OFF radi isto putovanje obrnuto.

Konzola o tome također vodi zapis, jer svaka grana ispisuje kada se pokrene:

LED ON
LED OFF
LED ON
ℹ️
Linija statusa može biti do dvije sekunde zastarjela. Osvježava se na dvosekundnom brojaču, pa ako pritisneš tipku na jednom mobitelu dok stranicu gledaš na drugom, drugi se sustiže pri svojem sljedećem propitivanju, a ne trenutno. Sama LED-ica mijenja se odmah. Samo tekst zaostaje.
ℹ️
Budući da stranica propituje svake dvije sekunde, pločica odgovara na zahtjev svake dvije sekunde dokle god ijedan preglednik ima stranicu otvorenu. Zato grana /led/status ne ispisuje ništa: konzola bi se time napunila.

Ako ne radi

  • OSError: [Errno 112] EADDRINUSE pri drugom pokretanju. Prethodno pokretanje nije otpustilo port 80, i neće ga otpustiti samo od sebe. Linija SO_REUSEADDR je tu da bind ipak uspije, pa ako vidiš ovu grešku, prvo provjeri je li ta linija još u tvojoj skripti. Ako to ne pomogne, resetiraj pločicu, što zasigurno očisti stari socket.
  • Stranica se ne otvara, a konzola piše HTTP server started. Pločica sluša a preglednik ne može do nje doći, što znači da ta dva nisu na istoj mreži, ili mreža izolira svoje klijente. Probaj IP adresu prije .local imena, i probaj hotspot mobitela ako si na gostinskoj mreži.
  • Točke koje nikada ne prestaju. Pogrešni podaci, ili 5 GHz mreža. C6 nema 5 GHz radio.

Cijeli primjer

Pogledaj cijeli kod primjera na linku ispod:

6.2_Wi-Fi_LED_Control.py

Primjer koji pokazuje kako upravljati LED-icom na NULA MINI s web stranice, koju pločica sama servira iz sirovog socketa i koja je dostupna na nulamini.local.