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 biblioteke HTTPClient.
  • 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 preneseš, 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 skicu, između navodnika varijable webhookURL:
const char* webhookURL = "https://webhook.site/0306ed74-f1dd-4ab4-ba2c-74a20c7658e4";
  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 skica š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 RST na pločici da tok zaustaviš i pokreneš, 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.

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.

Skica to tijelo gradi kao jednu liniju teksta:

String postData = "number=" + String(randomNumber);

što izađe kao number=77, ili number=80, ili što god je pločica upravo izmislila. 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 skica to kaže, u zaglavlju:

http.addHeader("Content-Type", "application/x-www-form-urlencoded");

Zaglavlje je jedna linija dodatne informacije o samom zahtjevu, a ne dio podataka. 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.


Kod

Ispod je cijeli kod za ovaj primjer:

/**
**************************************************
*
* @file 6.3_Sending_Data.ino
* @brief Primjer koji pokazuje kako poslati podatke s NULA pločice serveru na internetu pomoću HTTP POST
* zahtjeva. Svakih nekoliko sekundi pločica izmisli slučajan broj i pošalje ga na webhook, a to je
* web adresa koja jednostavno skuplja što god joj se pošalje i pokazuje ti to u tvom pregledniku.
* U primjeru 6.1 čitali smo podatke s interneta, ovdje podatke pišemo na njega.
* Za detalje, shemu spajanja i više, pogledaj dokumentaciju primjera na: <link placeholder>
* @author Soldered
***************************************************
*/

/*
Biblioteka WiFi sadrži sve što je potrebno za pridruživanje Wi-Fi mreži.
*/
#include <WiFi.h>

/*
Biblioteka HTTPClient omogućuje nam da govorimo HTTP, jezik kojim preglednici razgovaraju s web sjedištima.
*/
#include <HTTPClient.h>

/*
Ove dvije varijable drže ime tvoje Wi-Fi mreže (SSID) i njezinu lozinku. Zamijeni tekst između
navodnika svojim mrežnim podacima.
*/
const char* ssid = "your ssid";
const char* 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
*/
const char* webhookURL = "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.
*/
const unsigned long POST_INTERVAL = 5000;

/*
Ova varijabla pamti trenutak kada smo poslali posljednju poruku, da znamo kada je sljedeća na redu.
*/
unsigned long lastPost = 0;

void setup() {

/*
Serial.begin() uspostavlja serijsku komunikaciju između tvoje pločice i tvog računala preko USB kabela. Ovdje ga koristimo
da pratimo proces povezivanja i da vidimo što server odgovara.
*/
Serial.begin(115200);
Serial.println();
Serial.println("Wi-Fi POST Request Example");

/*
randomSeed() daje generatoru slučajnih brojeva početnu točku. Bez njega bi pločica nakon svakog reseta proizvodila
isti niz "slučajnih" brojeva, što je lako zamijeniti za pokvaren program. esp_random() čita
hardverski generator slučajnih brojeva ugrađen u čip, a to nam svaki put daje stvarno drugačiju početnu točku.
*/
randomSeed(esp_random());

/*
WiFi.begin() pokreće pokušaj povezivanja koristeći ime mreže i lozinku koje smo definirali iznad. Funkcija samo
pokreće proces, ne čeka da se dovrši.
*/
WiFi.begin(ssid, password);
Serial.print("Connecting to Wi-Fi");

/*
Zbog toga na vezu čekamo sami. WiFi.status() nam kaže trenutno stanje a WL_CONNECTED je
vrijednost koju prijavljuje kada smo na mreži. Jedna točka ispisuje se svakih pola sekunde da vidimo da pločica još
pokušava.
*/
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}

/*
WiFi.localIP() vraća adresu koju je router dodijelio našoj pločici.
*/
Serial.println();
Serial.println("Connected to Wi-Fi!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
}

void loop() {

/*
millis() je funkcija koja vraća broj milisekundi koje su prošle od kada je pločica počela izvršavati trenutni
program. Koristimo ga umjesto delay() da pločica ostane slobodna za drugi posao između poruka.
*/
unsigned long now = millis();

/*
Ovdje provjeravamo koliko je vremena prošlo od posljednje poruke. Novu šaljemo samo kada prođe
POST_INTERVAL milisekundi, i odmah pamtimo trenutno vrijeme kao novu početnu točku.
*/
if (now - lastPost >= POST_INTERVAL) {
lastPost = 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 (WiFi.status() == WL_CONNECTED) {

/*
Ovdje stvaramo naš HTTP klijentski objekt, koji smo nazvali "http". Stvaramo ga unutar if izjave tako da se
za svaku poruku koristi svježi.
*/
HTTPClient http;

/*
random() je funkcija koja vraća cijeli slučajni broj. Prva vrijednost je najmanji broj koji može vratiti
a druga je prvi broj koji ne može vratiti, pa nam ovaj poziv daje broj od 0 do 100.
U pravom projektu ovdje bi išla vrijednost senzora.
*/
int randomNumber = random(0, 101);

/*
Ovdje gradimo podatke koje ćemo poslati. Format "name=value" isti je onaj koji preglednik koristi kada
predaješ jednostavnu web formu, a String() pretvara naš broj u tekst da ga se može spojiti s ostatkom pomoću plusa.
*/
String postData = "number=" + String(randomNumber);

Serial.println("----------------------------------");
Serial.println("Sending POST request to webhook.site...");
Serial.print("Data: ");
Serial.println(postData);

/*
begin() priprema zahtjev govoreći klijentu koju adresu želimo dosegnuti. Još se ništa ne šalje.
*/
http.begin(webhookURL);

/*
addHeader() dodaje jednu liniju dodatne informacije zahtjevu. Zaglavlje serveru govori nešto o samom
zahtjevu, a ovo određeno mu govori u kojem su formatu naši podaci napisani, da zna kako ih pročitati.
*/
http.addHeader("Content-Type", "application/x-www-form-urlencoded");

/*
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.
Funkcija vraća kod odgovora: brojevi 200 i više znače da je server odgovorio, dok negativan broj
znači da odgovor nikada nismo dobili.
*/
int httpResponseCode = http.POST(postData);

/*
Ovdje provjeravamo je li server odgovorio. Ako je, ispisujemo kod odgovora i tijelo odgovora.
*/
if (httpResponseCode > 0) {
Serial.print("Server response code: ");
Serial.println(httpResponseCode);
String response = http.getString();
Serial.println("Response body:");
Serial.println(response);
} else {

/*
errorToString() negativan kod greške pretvara u kratku rečenicu, koju je mnogo lakše razumjeti od
samog broja.
*/
Serial.print("POST failed. Error: ");
Serial.println(http.errorToString(httpResponseCode));
}

/*
end() zatvara vezu i otpušta memoriju koju je klijent koristio. Uvijek zatvori vezu kada s njom
završiš, inače će program koji pravi mnogo zahtjeva polako ostati bez memorije.
*/
http.end();
} else {

/*
Ako nismo povezani, jednostavno pokrećemo novi pokušaj povezivanja i probamo ponovno u sljedećem prolazu.
*/
Serial.println("Wi-Fi not connected. Trying to reconnect...");
WiFi.begin(ssid, password);
}
}
}
ℹ️
O toj liniji randomSeed(). Na klasičnom Arduinu random() nakon svakog reseta proizvodi isti niz brojeva ako ga prvo ne posiješ, a komentar u skici opisuje to ponašanje. ESP32-C6 tako ne radi: random() na ovom čipu već crpi iz hardverskog generatora slučajnih brojeva, pa se brojevi razlikuju u svakom izvođenju bez obzira posiješ li ga ili ne. Ta linija ne šteti, jer je posijana iz tog istog hardverskog izvora a brojevi ostaju nepredvidljivi, ali je na ovoj pločici možeš izbrisati a da ne promijeniš ništa što bi mogao primijetiti.

Što bi trebao vidjeti

Prenesi skicu, pa otvori Serial Monitor i postavi ga na 115200 bauda. Za razliku od primjera 6.1, gdje se sve dogodilo jednom u setup(), ova skica nastavlja. Novi blok ispisa pojavljuje se svakih pet sekundi dokle god je pločica napojena, pa nema naleta koji možeš propustiti.

Serial Monitor prikazuje slijed povezivanja na Wi-Fi nakon kojeg slijedi POST zahtjev i serverov odgovor
Serial Monitor nakon prvog POST zahtjeva. Pločica i port imenovani su duž vrha; odabirač bauda sjedi na krajnjem desnom dijelu te trake, tek izvan ovog izreza

Ispisan, prvi ciklus čita se:

ESP-ROM:esp32c6-20220919
Build:Sep 19 2022
rst:0x1 (POWERON),boot:0x6e (SPI_FAST_FLASH_BOOT)
SPIWP:0xee
mode:DIO, clock div:2
load:0x40875730,len:0x1278
load:0x4086b910,len:0xc58
load:0x4086e610,len:0x31c0
entry 0x4086b910

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=77
Server response code: 200
Response body:
This URL has no default content configured. <a href="https://webhook.site/#!/edit/0306ed74-f1dd-4ab4-ba2c-74a20c7658e4">Change response in Webhook.site</a>.

Čitajući od vrha:

Prvih devet linija nije tvoja skica. ESP-ROM, SPIWP i tri load: linije su ugrađeni bootloader čipa koji se javlja na dužnost prije nego se tvoj program pokrene. Ispisuju se pri svakom resetu, u svakom primjeru. Tvoja skica počinje na praznoj liniji, koja je tamo jer setup() poziva Serial.println() bez ičega u njemu, isključivo da razdvoji to dvoje.

Prebroji točke da vidiš koliko je povezivanje trajalo. Ovdje ih je dvanaest, po jedna svakih pola sekunde, pa je ova pločica bila na mreži u oko šest sekundi. Red koji nastavlja rasti iznad dvadeset ili trideset znači da su podaci pogrešni ili je mreža 5 GHz.

Server response code: 200 je linija koja je važna. Znači da je webhook.site primio zahtjev i prihvatio ga. Svaki kod od 200 pa gore je server koji odgovara; negativan broj znači da pločica odgovor nikada nije dobila.

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 pet njegovih dijelova 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 koji radi svoj posao, i to je daleko bolji dokaz vremenskog razmaka nego što ti Serial Monitor može dati. Promijeni vrijednost u skici 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 skica 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 Serial Monitor iznad rekao 77. To su dva različita zahtjeva, pet sekundi razmaknuta, svaki nosi svježe izmišljen 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.

user-agent: ESP32HTTPClient je tvoja pločica koja se predstavlja. Svaki HTTP zahtjev kaže što ga je napravilo. Tvoj preglednik tu stavlja svoje ime i verziju, a biblioteka HTTPClient to ispunjava za tebe.

ℹ️
Adresa Host nije adresa koju je tvoja pločica ispisala. Serial Monitor je rekao 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 monitoru, ali ništa u pregledniku. Ili adresa u webhookURL 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.
  • Negativan kod odgovora. Pločica je na mreži ali ne može doći do interneta. Obično mreža nema radnu vezu, ili gostinska mreža blokira izlazne zahtjeve. Rečenica ispisana nakon POST failed. Error: to suzuje.
  • Nečitljivi simboli umjesto riječi. Serial Monitor je na pogrešnom baud rateu. Mora biti 115200.
  • Nikakvog serijskog ispisa uopće. Pločica nije na portu prikazanom na vrhu prozora. Na Windowsu, pločica koja se uopće ne pojavi kao port treba CH340 driver.

Cijeli primjer

Pogledaj cijeli kod primjera na linku ispod:

6.3_Sending_Data.ino

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.