ESP32 prikuplja podatke za učenje
Model uči iz podataka – a najbolji podaci su oni iz tvoje učionice. U ovoj lekciji ESP32 meri temperaturu, vlažnost i svetlo, ti mu kažeš kakvo je stanje („prijatno“, „zagušljivo“…), a Python sve to čuva u tabelu. To je postupak označavanja podataka iz lekcije Podaci – hrana za AI, samo sa pravim senzorima.
Povezivanje
| Deo | Pin | ESP32 |
|---|---|---|
| DHT11 | VCC / GND / DATA | 3V3 / GND / GPIO4 |
| LDR modul | VCC / GND / AO | 3V3 / GND / GPIO34 |
Isto kao u lekcijama DHT11 i merenje osvetljenja. Ako si napravio pametnu učionicu, DHT11 je već na pravom mestu.
Program za ESP32: merenje i oznaka
// ESP32 – prikupljanje podataka za veštačku inteligenciju
// DHT11 (DATA) -> GPIO4, LDR modul (AO) -> GPIO34
// Svake 3 sekunde šalje red: temperatura,vlaznost,svetlo,stanje
// Stanje (oznaku) zadaješ slovom u Serial Monitoru: h = hladno, p = prijatno,
// t = toplo, z = zagušljivo, x = pauza (bez upisa)
#include <DHT.h>
const int DHT_PIN = 4;
const int LDR_PIN = 34;
DHT dht(DHT_PIN, DHT11);
String stanje = ""; // trenutna oznaka; prazno = ne beležimo
unsigned long poslednje = 0;
void setup() {
Serial.begin(115200);
dht.begin();
Serial.println("temperatura,vlaznost,svetlo,stanje"); // zaglavlje CSV tabele
}
void loop() {
// 1) da li je korisnik poslao novu oznaku?
if (Serial.available()) {
char c = Serial.read();
if (c == 'h') stanje = "hladno";
else if (c == 'p') stanje = "prijatno";
else if (c == 't') stanje = "toplo";
else if (c == 'z') stanje = "zagusljivo";
else if (c == 'x') stanje = "";
}
// 2) merenje na 3 s, bez delay() – da bi Serial stalno mogao da prima slova
if (millis() - poslednje >= 3000) {
poslednje = millis();
float t = dht.readTemperature();
float h = dht.readHumidity();
int svetlo = analogRead(LDR_PIN);
if (isnan(t) || isnan(h) || stanje == "") {
return; // neuspelo merenje ili pauza – ne upisujemo
}
Serial.print(t, 1);
Serial.print(",");
Serial.print(h, 0);
Serial.print(",");
Serial.print(svetlo);
Serial.print(",");
Serial.println(stanje);
}
}
- ESP32 svake 3 sekunde šalje jedan red CSV tabele, npr.
24.1,71,1930,zagusljivo. - Oznaku zadaješ ti: u Serial Monitoru upiši slovo
p(prijatno),h(hladno),t(toplo) iliz(zagušljivo) i pritisni Enter. Slovoxpauzira beleženje. - Merenje ide preko
millis(), pa ESP32 stalno „sluša“ Serial i odmah prihvata novu oznaku.
Kako da skupiš dobre podatke
- Raznovrsno: beleži ujutru i posle podne, sa otvorenim i zatvorenim prozorom, pre i posle odmora, kad je u učionici mnogo i malo ljudi.
- Pošteno označavanje: oznaku menjaj tek kada se stanje zaista promeni; neka o oznaci odluči nekoliko učenika, a ne jedan.
- Dovoljno primera svake vrste: bar 15–20 redova za svako stanje (vidi zadatak 1).
- Ako neko stanje ne možeš da dobiješ prirodno, pažljivo ga „napravi“: dah ili dlan na senzoru (toplo), čaša tople vode pored senzora (vlažno).
Python čuva podatke u fajl
Redove iz Serial Monitora možeš da iskopiraš u fajl ručno. Lakše je da to radi Python, pomoću biblioteke pyserial (pip install pyserial). Pre pokretanja zatvori Serial Monitor – port može da koristi samo jedan program.
# Čuvanje redova koje šalje ESP32 u fajl merenja.csv (Python + biblioteka pyserial)
# Instalacija: pip install pyserial · Pre pokretanja zatvori Serial Monitor u Arduino IDE-u!
import serial
PORT = "COM3" # na Windows-u npr. COM3 (vidi Arduino IDE → Tools → Port); na Linux-u /dev/ttyUSB0
BRZINA = 115200
BROJ_REDOVA = 100 # koliko merenja da sačuvamo
veza = serial.Serial(PORT, BRZINA, timeout=10)
with open("merenja.csv", "w", encoding="utf-8") as f:
sacuvano = 0
zaglavlje_upisano = False
while sacuvano < BROJ_REDOVA:
red = veza.readline().decode("utf-8", errors="ignore").strip()
if not red:
continue # ništa nije stiglo (isteklo vreme čekanja)
print(red)
if red.startswith("temperatura"): # zaglavlje upisujemo samo jednom
if not zaglavlje_upisano:
f.write(red + "\n")
zaglavlje_upisano = True
continue
f.write(red + "\n")
sacuvano += 1
veza.close()
print("Sačuvano", sacuvano, "merenja u merenja.csv")
Oznake i dalje šalješ iz Serial Monitora? Ne možeš – port je zauzet. Zato je jednostavnije da merenja radiš u dva dela: prvo sve sa oznakom „prijatno“ (pošalješ p, zatvoriš Serial Monitor, pokreneš Python), pa zatim sledeće stanje. ESP32 pamti poslednju oznaku sve dok ne isključiš napajanje – a pri otvaranju porta se resetuje, pa oznaku pošalji ponovo. U zadatku 2 je bolje rešenje: taster na ploči.
Primer podataka i prvi pogled
Za sledeću lekciju pripremili smo primer tabele ucionica.csv sa 84 merenja (temperatura, vlažnost, stanje). Vrednosti su realne za učionicu, ali su pripremljene za vežbu; tvoja merenja će izgledati drugačije – i to je dobro, jer će tvoj model naučiti baš tvoju učionicu.
Prikaži tabelu ucionica.csv (iskopiraj je u fajl)
temperatura,vlaznost,stanje
20.5,44,hladno
17.0,69,hladno
17.3,72,hladno
16.8,67,hladno
19.0,40,hladno
22.1,39,prijatno
19.4,70,hladno
21.9,71,zagusljivo
17.7,49,hladno
24.8,72,zagusljivo
29.3,71,toplo
24.2,38,prijatno
29.7,37,toplo
23.8,43,prijatno
20.1,44,prijatno
24.0,70,zagusljivo
27.4,46,toplo
17.4,71,hladno
24.9,58,prijatno
17.4,80,hladno
16.9,38,hladno
24.7,66,zagusljivo
25.5,62,prijatno
22.5,64,prijatno
21.1,50,prijatno
27.1,79,toplo
26.9,40,toplo
24.0,68,zagusljivo
22.9,56,prijatno
26.2,53,toplo
17.0,67,hladno
21.9,83,zagusljivo
20.8,66,zagusljivo
21.9,77,zagusljivo
17.1,70,hladno
24.0,55,prijatno
20.8,57,prijatno
24.3,72,zagusljivo
27.2,39,toplo
27.8,52,toplo
22.6,77,zagusljivo
16.9,81,hladno
25.8,76,zagusljivo
25.5,63,prijatno
28.4,57,toplo
16.3,64,hladno
21.0,74,zagusljivo
17.6,38,hladno
19.1,53,hladno
17.8,50,hladno
21.6,66,zagusljivo
17.1,63,hladno
21.6,52,prijatno
28.4,62,toplo
28.1,52,toplo
25.9,57,prijatno
21.3,49,prijatno
18.1,46,hladno
18.1,77,hladno
19.3,66,hladno
27.6,46,toplo
19.7,35,prijatno
24.5,55,prijatno
29.3,79,toplo
28.0,74,toplo
25.2,82,zagusljivo
16.8,84,hladno
29.3,78,toplo
27.2,60,toplo
21.6,60,prijatno
17.4,75,hladno
21.6,47,prijatno
16.9,48,hladno
22.2,42,prijatno
20.8,38,prijatno
17.4,71,hladno
18.1,41,hladno
29.3,74,toplo
16.4,48,hladno
24.6,44,prijatno
24.9,57,prijatno
24.4,65,zagusljivo
17.7,66,hladno
29.9,64,toplo
# Pregled prikupljenih podataka: koliko ima primera i kako izgledaju na grafiku
import csv
import matplotlib.pyplot as plt
podaci = []
with open("ucionica.csv", encoding="utf-8") as f:
for red in csv.DictReader(f):
podaci.append((float(red["temperatura"]), float(red["vlaznost"]), red["stanje"]))
boje = {"hladno": "royalblue", "prijatno": "green", "toplo": "red", "zagusljivo": "purple"}
for stanje, boja in boje.items():
t = [p[0] for p in podaci if p[2] == stanje]
h = [p[1] for p in podaci if p[2] == stanje]
print(f"{stanje:11} primera: {len(t):3} prosečno {sum(t) / len(t):.1f} °C, {sum(h) / len(h):.0f} %")
plt.scatter(t, h, c=boja, label=stanje, edgecolors="black")
plt.xlabel("temperatura (°C)")
plt.ylabel("vlažnost vazduha (%)")
plt.title("Stanje u učionici")
plt.legend()
plt.grid(alpha=0.3)
plt.show()
Ispis programa:
hladno primera: 28 prosečno 17.7 °C, 61 %
prijatno primera: 23 prosečno 22.9 °C, 51 %
toplo primera: 18 prosečno 28.2 °C, 59 %
zagusljivo primera: 15 prosečno 23.3 °C, 72 %

Odredite „dežurne za podatke“: na svakom odmoru pokrenu beleženje i zajedno odluče o oznaci. Posle nedelju dana odeljenje ima svoju tabelu za sledeću lekciju.
Česte zablude
- „Što više redova, to bolje – bez obzira kakvi su“ – 1000 redova snimljenih u istih 10 minuta manje vredi od 100 redova iz različitih dana i situacija.
- „Senzor uvek meri tačno“ – DHT11 ima grešku oko ±2 °C i ponekad ne uspe da izmeri; zato program proverava isnan(), a podaci se posle čiste.
Zadaci za vežbu
Pokušaj prvo sam, pa tek onda otvori rešenje.
- Napiši program koji proverava da li su podaci uravnoteženi: ispiši broj primera po stanju i upozori ako neko stanje ima manje od 10 primera.
- Umesto slova iz Serial Monitora, oznaku menjaj tasterom na GPIO15 (svaki pritisak: sledeće stanje). Šta treba promeniti u programu za ESP32?
- Napiši program koji iz podataka izbacuje nemoguća merenja (temperatura van −10…50 °C, vlažnost van 0…100 %, prazna polja) i čuva ostala u novi fajl.
Rešenje 1: uravnoteženost
# Zadatak: da li su podaci uravnoteženi? Upozori ako neka klasa ima manje od 10 primera
import csv
broj = {}
with open("ucionica.csv", encoding="utf-8") as f:
for red in csv.DictReader(f):
broj[red["stanje"]] = broj.get(red["stanje"], 0) + 1
for stanje, n in sorted(broj.items(), key=lambda p: -p[1]):
upozorenje = " <- premalo primera, snimi još!" if n < 10 else ""
print(f"{stanje:11} {n:3}{upozorenje}")
Ispis programa:
hladno 28
prijatno 23
toplo 18
zagusljivo 15
Rešenje 2: taster umesto Serial Monitora
Dodaj const int TASTER = 15;, u setup() pinMode(TASTER, INPUT_PULLUP);, i niz const char* OZNAKE[] = {"hladno", "prijatno", "toplo", "zagusljivo"}; sa promenljivom int k = 1;. U loop() umesto čitanja Seriala proveravaj prelaz tastera HIGH → LOW (kao u lekciji LED dioda, taster i PWM) i radi k = (k + 1) % 4; stanje = OZNAKE[k];. Tako Python može da drži port otvoren, a ti menjaš oznaku tasterom. Trenutnu oznaku možeš da pokažeš i bojom svetlećeg prstena.
Rešenje 3: čišćenje podataka
# Zadatak: čišćenje podataka – izbaci nemoguća merenja i sačuvaj ostala
import csv
ulaz = [ # deo merenja sa greškama (npr. labav kontakt na senzoru)
{"temperatura": "22.5", "vlaznost": "48", "stanje": "prijatno"},
{"temperatura": "-127.0", "vlaznost": "48", "stanje": "prijatno"}, # greška senzora
{"temperatura": "27.1", "vlaznost": "155", "stanje": "toplo"}, # vlažnost preko 100 %
{"temperatura": "", "vlaznost": "60", "stanje": "hladno"}, # nedostaje vrednost
{"temperatura": "18.2", "vlaznost": "62", "stanje": "hladno"},
]
def ispravno(red):
try:
t = float(red["temperatura"])
h = float(red["vlaznost"])
except ValueError: # prazno polje ili tekst umesto broja
return False
return -10 <= t <= 50 and 0 <= h <= 100
ocisceno = [red for red in ulaz if ispravno(red)]
print("Ostalo", len(ocisceno), "od", len(ulaz), "merenja")
with open("merenja_ociscena.csv", "w", encoding="utf-8", newline="") as f:
pisac = csv.DictWriter(f, fieldnames=["temperatura", "vlaznost", "stanje"])
pisac.writeheader()
pisac.writerows(ocisceno)
Ispis programa:
Ostalo 2 od 5 merenja
Proveri znanje
Otvori kviz
Kviz: prikupljanje podataka
1. Ko u ovoj lekciji određuje oznaku (stanje)?
2. Zašto ESP32 meri preko millis(), a ne delay(3000)?
3. Zašto treba zatvoriti Serial Monitor pre pokretanja Python programa?
4. Koji su podaci bolji za učenje?
