Skip to main content

6.3 Slanje podataka

U primjeru 6.1 pločica je od web sjedišta zatražila njegov sadržaj i ispisala odgovor. Taj promet išao je u jednom smjeru: internet je govorio, pločica slušala. Ovaj primjer to prevrće. NULA MINI izmišlja broj svakih pet sekundi i šalje ga serveru na internetu, a ti gledaš kako svaki od njih dolazi u kartici preglednika (engl. browser).

To je ona polovica razgovora koja treba svakom povezanom projektu. Termometar koji zapisuje u tablicu, zvono na vratima koje šalje obavijest, senzor za bilje koji ti pošalje mail kada se zemlja osuši: svi oni rade ono što radi ovaj primjer, samo s pravim mjerenjem umjesto slučajnog broja.

U ovoj dokumentaciji naučit ćeš:

  • Razliku između HTTP GET i HTTP POST zahtjeva.
  • Kako poslati podatke s pločice pomoću urequests.post().
  • Što je zaglavlje (header), i zašto jedna njegova linija mijenja kako su tvoji podaci prikazani.
  • Kako uhvatiti i pregledati te podatke pomoću webhook.site.
  • Zašto server vidi drugačiju IP adresu od one koju tvoja pločica ispisuje.

Potreban hardver

  • 1× Soldered NULA MINI pločica
  • 1× USB-C kabel
  • 1× breadboard
  • 2.4 GHz Wi-Fi mreža s pristupom internetu
ℹ️
Nema komponenti, nema otpornika, nema jumper žica. Točno kao u primjeru 6.1, na pinove pločice ne spaja se ništa. USB-C kabel je jedina uključena žica, a sav posao odvija se u softveru.
ℹ️
Ovom primjeru treba prava veza s internetom. Primjeru 6.2 trebali su samo tvoja pločica i tvoj mobitel na istoj mreži, i radio je savršeno dobro na routeru bez interneta uopće. Ovome ne: webhook.site je javni server vani na internetu, pa mreža mora moći doći do njega. Gostinska mreža koja blokira izlazne veze ovdje će pasti.
ℹ️
Mreža mora biti 2.4 GHz. ESP32-C6 čip nema 5 GHz radio, pa mu je mreža koja radi samo na 5 GHz nevidljiva. Ako tvoj router razdvaja dva pojasa u odvojena imena, odaberi onaj na 2.4 GHz.

Spajanje komponenti

1. Umetni NULA MINI pločicu na breadboard

Utisni pločicu u jedan kraj breadboarda tako da njezina dva reda pinova premoste središnji kanal.

NULA MINI pločica postavljena na breadboard
Korak 1: pločica postavljena na breadboard

2. Spoji USB-C kabel

Utisni USB-C kabel u konektor na rubu pločice, a drugi kraj u računalo. PWR LED-ica se upali, i sklop je gotov.

NULA MINI pločica na breadboardu sa samo priključenim USB-C kabelom i upaljenom PWR LED-icom
Korak 2: gotov sklop. Ni jedna rupica breadboarda nije u upotrebi
ℹ️
Breadboard samo drži pločicu stabilno. Ne koristi se ni jedna rupica, a primjer radi jednako dobro s pločicom koja slobodno leži na stolu.

Postavljanje webhook.site

Prije nego bilo što pokreneš, treba ti mjesto kamo će podaci ići. webhook.site je besplatan servis koji ti daje vlastitu web adresu i onda ti pokazuje, uživo, sve što joj se pošalje. Tako ne moraš napisati server prije nego što slanje možeš testirati.

Otvori webhook.site u pregledniku. Daje ti svježu adresu u trenutku kada se stranica učita. Nema se za što registrirati.

Početna stranica webhook.site prikazuje svježe generirani jedinstveni URL i prazan inbox
Svježe otvorena webhook.site stranica. Adresa pod Your unique URL je ona koju treba kopirati
  1. Kopiraj adresu prikazanu pod Your unique URL. Izgleda kao https://webhook.site/0306ed74-f1dd-4ab4-ba2c-74a20c7658e4. Dugačak kod na kraju je samo tvoj, a tvoj će se razlikovati od onog na slici.
  2. Zalijepi je u skriptu, između navodnika varijable webhook_url:
webhook_url = "https://webhook.site/your-unique-id"
  1. Ostavi karticu preglednika otvorenom. To je stranica na kojoj će se podaci pojaviti, a osvježava se sama, pa je nikada ne moraš ponovno učitavati.
ℹ️
Drži oko na brojaču zahtjeva. Neposredno pod alatnom trakom stranica piše INBOX (0/50) dok čeka. Besplatna webhook.site adresa drži 50 zahtjeva, a ova skripta šalje jedan svakih pet sekundi, pa se napuni u nešto više od četiri minute, nakon čega se novi zahtjevi prestaju bilježiti. Ako tvoj utihne, to je gotovo uvijek razlog. Pritisni Stop i Run ponovno da tok krene iznova, ili klikni New na vrhu stranice za svježu adresu s praznim inboxom. Oznaka 7d uz adresu znači da se drži sedam dana.
ℹ️
Svatko tko zna tvoju jedinstvenu adresu može joj poslati podatke, a svatko tko je ima može pročitati što je poslano. Za učenje sa slučajnim brojevima je u redu, ali ne šalji joj ništa privatno.
ℹ️
Primijeti da adresa počinje s https://, a ne http://. Za razliku od Arduino HTTPClient, urequests kriptiranu adresu rješava bez ičega dodatnog od tebe, pa se ovdje nema što konfigurirati, ali mu dok je zahtjev u zraku treba osjetno više memorije.

GET i POST

HTTP, jezik koji preglednici i serveri govore, ima nekoliko metoda, riječ na samom početku zahtjeva koja kaže kakva se stvar traži. Dvije od njih pokrivaju gotovo sve:

  • GET znači daj mi nešto. Tvoj preglednik šalje GET svaki put kada otvoriš stranicu. Primjer 6.1 poslao je GET na example.com i dobio natrag stranicu HTML-a.
  • POST znači evo nečega, uzmi. To je ono što tvoj preglednik pošalje kada ispuniš formu i pritisneš predaj, i to je ono što ovaj primjer koristi.

Praktična razlika je u tome gdje podaci putuju. GET zahtjev je u suštini samo adresa, pa nema gdje puno čega staviti. POST zahtjev nosi tijelo, zaseban paket podataka priložen pod adresom, i tamo ide naš broj.

Skripta to tijelo gradi kao jednu liniju teksta:

post_data = "number=" + str(random_number)

što izađe kao number=77, ili number=80, ili što god je pločica upravo izmislila.

ℹ️
str() pretvara broj u tekst. Python neće spojiti broj sa stringom pomoću +. Probaj bez str() i dobit ćeš TypeError: can't convert 'int' object to str implicitly. To je jedna od najčešćih malih grešaka kada poruku gradiš iz dijelova.

Oblik name=value isti je onaj koji preglednik koristi za jednostavnu formu, i ima ime: form encoding. Server nema načina da pogodi koji si format odabrao, pa mu skripta to kaže, u zaglavlju:

response = urequests.post(
webhook_url,
data=post_data,
headers={"Content-Type": "application/x-www-form-urlencoded"},
)

Zaglavlje je jedna linija dodatne informacije o samom zahtjevu, a ne dio podataka. Argument headers= prima rječnik parova imena i vrijednosti unutar vitičastih zagrada, pa ih možeš predati nekoliko odjednom ako treba.

Upravo to zaglavlje je ono što webhook.siteu dopušta da tvoj broj prikaže kao uredno označeno polje, a ne kao sirovu grudu teksta, kako ćeš vidjeti niže.


Slanje na brojaču, i prethodna provjera veze

Skripta koristi isti neblokirajući brojač kao 5.1: time.ticks_ms() s time.ticks_diff(), pa pločica nikada nije zamrznuta čekajući sljedeće slanje.

Unutar toga, prije nego se bilo što pošalje, događa se još jedna provjera:

if wlan.isconnected():
# ...pošalji podatke...
else:
print("Wi-Fi not connected. Trying to reconnect...")
wlan.connect(ssid, password)

Wi-Fi veza može pasti u svakom trenutku. Router se restarta, ili pločica odluta izvan dosega. Bez ove provjere skripta bi ipak pokušala poslati, čekala istek vremena i ispisala grešku. S njom pala veza umjesto toga pokreće svježi pokušaj connect(), a sljedeći prolaz kroz petlju pokušava ponovno.

ℹ️
Ovo je prvi primjer u kitu koji se snalazi kada nešto pođe po zlu nakon pokretanja, a ne pretpostavlja da veza uspostavljena na početku traje zauvijek. Svakom projektu koji treba raditi bez nadzora ovo je potrebno.

Sam slučajni broj dolazi iz:

random.seed(time.ticks_us())
random_number = random.randint(0, 100)

randint(0, 100) vraća cijeli broj, i oba kraja su uključena, pa su i 0 i 100 mogući. seed() daje generatoru početnu točku, a predavanje mikrosekundnog sata znači da se niz razlikuje od pokretanja do pokretanja, umjesto da se ponavlja. U pravom projektu ovdje bi išla vrijednost senzora.


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 urequests omogućuje nam da govorimo HTTP, jezik kojim preglednici razgovaraju s web sjedištima.
import urequests

# Modul random može za nas izmišljati brojeve, što u ovom primjeru stoji umjesto prave vrijednosti senzora.
import random
import time

# Ove dvije varijable drže ime tvoje Wi-Fi mreže (SSID) i njezinu lozinku.
ssid = "your ssid"
password = "your password"

# Ova varijabla drži adresu na koju šaljemo naše podatke. Otvori https://webhook.site u pregledniku, kopiraj jedinstveni
# link koji ti pokaže na vrhu stranice, i zalijepi ga između navodnika ispod. Drži tu karticu preglednika otvorenom
# i vidjet ćeš kako se u njoj pojavljuje svaka vrijednost koju pločica pošalje.
# Primjer: https://webhook.site/your-unique-id
webhook_url = "your unique url"

# Ova varijabla definira koliko vremena prođe između dvije poruke, u milisekundama. 5000 milisekundi je pet
# sekundi. Slobodno eksperimentiraj s ovom vrijednošću, ali imaj na umu da je vrlo često slanje podataka neuljudno prema
# serveru koji ih prima.
POST_INTERVAL_MS = 5000

# Ova varijabla pamti trenutak kada smo poslali posljednju poruku, da znamo kada je sljedeća na redu.
last_post = 0

# seed() daje generatoru slučajnih brojeva početnu točku. Predavanje trenutne vrijednosti mikrosekundnog sata
# znači da se brojevi razlikuju nakon svakog reseta, umjesto da ponavljaju isti niz.
random.seed(time.ticks_us())

print()
print("Wi-Fi POST Request Example")

# 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)

# connect() pokreće pokušaj povezivanja. Ne čeka da se povezivanje dovrši, pa na njega čekamo
# sami u petlji ispod, ispisujući jednu točku svakih pola sekunde da vidimo da pločica još pokušava.
wlan.connect(ssid, password)
print("Connecting to Wi-Fi", end="")
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.
print()
print("Connected to Wi-Fi!")
print("IP Address:", wlan.ifconfig()[0])

while True:

# time.ticks_ms() vraća broj milisekundi koje su prošle od uključivanja pločice. Koristimo ga umjesto
# običnog sleepa da pločica ostane slobodna za drugi posao između poruka.
now = time.ticks_ms()

# Ovdje provjeravamo koliko je vremena prošlo od posljednje poruke. Novu šaljemo samo kada
# prođe POST_INTERVAL_MS milisekundi, i odmah pamtimo trenutno vrijeme kao novu početnu točku.
if time.ticks_diff(now, last_post) >= POST_INTERVAL_MS:
last_post = now

# Prije nego bilo što pošaljemo, provjeravamo jesmo li još na mreži. Wi-Fi veza može pasti u svakom trenutku, a
# pokušaj slanja podataka bez nje samo bi potrošio vrijeme i ispisao greške.
if wlan.isconnected():

# randint() vraća cijeli slučajni broj, a obje vrijednosti koje mu damo su uključene, pa nam ovo daje
# broj od 0 do 100. U pravom projektu ovdje bi išla vrijednost senzora.
random_number = random.randint(0, 100)

# Ovdje gradimo podatke koje ćemo poslati. Format "name=value" isti je onaj koji preglednik koristi
# kada predaješ jednostavnu web formu, a str() pretvara naš broj u tekst da ga se može spojiti.
post_data = "number=" + str(random_number)

print("----------------------------------")
print("Sending POST request to webhook.site...")
print("Data:", post_data)

# Zahtjev omotavamo u try blok jer sve što ide preko mreže može pasti, a bez toga bi
# neuspjeli zahtjev zaustavio cijeli program greškom.
try:

# post() šalje zahtjev zajedno s našim podacima i čeka odgovor. POST je HTTP metoda
# koja se koristi za slanje podataka serveru, dok je GET, koji smo koristili u primjeru 6.1, ona koja se
# koristi za čitanje podataka s njega.
# Zaglavlja koja prilažemo opisuju format u kojem su naši podaci napisani, da server zna kako ih
# pročitati. Zaglavlje je jedna linija dodatne informacije o samom zahtjevu.
response = urequests.post(
webhook_url,
data=post_data,
headers={"Content-Type": "application/x-www-form-urlencoded"},
)

# status_code je broj kojim server odgovara. 200 znači "izvoli", a sve od 400
# pa gore znači da je nešto pošlo po zlu. text je tijelo odgovora.
print("Server response code:", response.status_code)
print("Response body:")
print(response.text)

# close() otpušta memoriju koju je odgovor koristio. Uvijek zatvori odgovor kada s njim završiš,
# inače će program koji pravi mnogo zahtjeva polako ostati bez memorije.
response.close()

except Exception as e:

# Ako je bilo što pošlo po zlu, ispiši razlog umjesto da dopustiš programu da se zaustavi.
print("POST failed. Error:", e)

else:

# Ako smo izgubili vezu, pokreni novi pokušaj i probaj ponovno u sljedećem prolazu.
print("Wi-Fi not connected. Trying to reconnect...")
wlan.connect(ssid, password)

# Vrlo kratka pauza ostavlja procesoru trenutak da obavi svoj posao u pozadini.
time.sleep_ms(10)

Što bi trebao vidjeti

Pritisni Run. Za razliku od primjera 6.1, gdje se sve dogodilo jednom i skripta završila, ovaj nastavlja. Novi blok ispisa pojavljuje se svakih pet sekundi dokle god radi, pa nema naleta koji možeš propustiti.

Wi-Fi POST Request Example
Connecting to Wi-Fi....
Connected to Wi-Fi!
IP Address: 192.168.75.75
----------------------------------
Sending POST request to webhook.site...
Data: number=90
Server response code: 200
Response body:
This URL has no default content configured. <a href="https://webhook.site/#!/edit/18274021-66b8-4ebf-971a-cb83c662cdb4">Change response in Webhook.site</a>.

Čitajući od vrha:

Prebroji točke da vidiš koliko je povezivanje trajalo. Ovdje ih je četiri, po jedna svakih pola sekunde, pa je ova pločica bila na mreži u oko dvije sekunde. Četiri do šest točaka je uobičajen rezultat, iako nesretan početak može potrajati desetak. Red koji nastavlja rasti iznad dvadeset ili trideset znači da su podaci pogrešni ili je mreža 5 GHz.

ℹ️
Ne budi iznenađen ako ne dobiješ ni jednu točku. Wi-Fi hardver ostaje pridružen mreži između pokretanja, pa pritisak na Run drugi put obično nađe pločicu već povezanu, a petlja čekanja završi prije nego išta uspije ispisati. Točke se stvarno pojavljuju samo pri prvom pokretanju nakon što se pločica upali ili resetira.

Server response code: 200 je linija koja je važna. Znači da je webhook.site primio zahtjev i prihvatio ga.

Response body: slijedi rečenica koja izgleda kao pritužba, a nije. This URL has no default content configured. je samo webhook.site koji ti govori da mu nikada nisi rekao čime da odgovori, pa je odgovorio time. Tvoji podaci su ipak došli. POST se tiče onoga što šalješ, a ne onoga što se vraća, a većina pravih servera odgovara nečim jednako nezanimljivim, često ničim.

Podaci koji dolaze u preglednik

Sada se prebaci na karticu webhook.site. Zahtjevi se pojavljuju sami kako ih pločica šalje.

webhook.site prikazuje kolonu primljenih POST zahtjeva, s jednim otvorenim da se vidi form vrijednost number
Dvanaest primljenih POST zahtjeva, s jednim od njih otvorenim. Klijentska IP adresa na ovoj je slici prekrivena

Kolona s lijeve strane je svaki zahtjev koji je došao, najnoviji na vrhu. Klikni bilo koji od njih da ga otvoriš s desne strane. Na tom ekranu je mnogo toga, a četiri njegova dijela vrijedi naći:

Vremenske oznake su točno pet sekundi razmaknute: 10:26:43, 10:26:38, 10:26:33, i tako dalje niz kolonu. To je POST_INTERVAL_MS koji radi svoj posao, i to je daleko bolji dokaz vremenskog razmaka nego što ti konzola može dati. Promijeni vrijednost u skripti i razmak u ovoj koloni mijenja se s njom.

Form values: number 80 su tvoji podaci, razloženi i označeni. To je ono što zaglavlje Content-Type donosi: budući da je skripta deklarirala format, webhook.site je znao pročitati number=80 kao polje imena number koje drži vrijednost 80. Ukloni to zaglavlje i isti bajtovi ipak dolaze, ali ovaj odsjek nestaje i ostaje ti da vrijednost sam vadiš iz sirovog teksta.

Raw Content: number=80 je to isto tijelo točno kako je otišlo s pločice, prije nego ga je bilo što interpretiralo. Primijeti da piše 80 dok je konzola iznad rekla 90. To su dva različita zahtjeva, svaki nosi svoj svježe izmišljeni broj.

9 bytes, u svakom redu. To je cijela poruka: sedam znakova od number= plus dvije cifre. Jednocifreni broj prikazao bi se kao 8 bajtova. Vrijedi se zaustaviti na tome kako je to malo podataka uz kilobajt HTML-a koji je primjer 6.1 povukao.

ℹ️
Adresa Host nije adresa koju je tvoja pločica ispisala. Konzola je rekla IP Address: 192.168.75.75, ali je zahtjev došao od nečega potpuno drugog (prekriveno na slici iznad). Ni jedno nije pogrešno. 192.168.75.75 je adresa pločice unutar tvoje vlastite mreže, koju je dodijelio tvoj router i besmislena je bilo gdje drugdje. Svaki primjer do sada ispisao je jednu od tih. Kada zahtjev otiđe prema internetu, tvoj router tu privatnu adresu zamijeni jedinom javnom koju cijelo tvoje domaćinstvo dijeli. Zato webhook.site vidi tvoj router, a ne tvoju pločicu, a zato i linija Location imenuje grad blizu tebe, a ne tvoj stol.

Ako ne radi

  • Točke koje nikada ne prestaju. Podaci su pogrešni, ili je mreža 5 GHz, ili je router izvan dosega. Provjeri SSID i lozinku za velika slova i zalutale razmake.
  • Server response code: 200 u konzoli, ali ništa u pregledniku. Ili adresa u webhook_url nije ona koju otvorena kartica prikazuje (provjeri dugačak kod na kraju znak po znak), ili je inbox dosegao svoje ograničenje od 50 zahtjeva. Klikni New za svježu adresu.
  • Zahtjevi dolaze nekoliko minuta i onda prestanu. To je ograničenje od 50 zahtjeva. Pločica nastavlja slati i dalje dobiva svoj 200; webhook.site je samo prestao bilježiti.
  • POST failed. Error: -2. Pločica je na mreži, ali nije mogla pronaći adresu, što obično znači da mreža nema radnu vezu s internetom. Gostinska mreža koja blokira izlazne zahtjeve izgleda isto.
  • POST failed. Error: [Errno 12] ENOMEM. Pločici je nestalo memorije usred zahtjeva. https:// adresi treba dobar dio nje, pa je to ono što treba očekivati ako skripti dodaš još mnogo toga.
  • POST failed. Error: need more than 1 values to unpack. To dobiješ ako nikada nisi zamijenio "your unique url" svojom adresom. Poruka URL ne spominje uopće, zbog čega ju je teško pogoditi: urequests pokušava adresu razlomiti na :// koje https odvaja od ostatka, a linija običnih riječi nema što razlomiti. Zalijepi svoju webhook adresu i poruka nestaje.

Cijeli primjer

Pogledaj cijeli kod primjera na linku ispod:

6.3_Sending_Data.py

Primjer koji pokazuje kako poslati podatke s NULA MINI pločice serveru na internetu pomoću HTTP POST zahtjeva, i kako gledati njihov dolazak na webhook.site.