Ciljevi učenja
- Pretvoriti 12-bitno ADC očitavanje u cilj od 18 do 30 °C.
- Objasniti HEAT, IN_RANGE i COOL uz pragove ±1 °C i povratak na cilj.
- Prikazati samo jedan važeći temperaturni zahtjev LED indikacijom.
- Poništiti nevažeći T/RH uzorak i raniju odluku pri grešci senzora.
- Provjeriti promjenu cilja, oporavak i histerezu tabelom prelaza.
Prije početka
Završi kurseve 5, 10 i 17: potenciometar i ADC, DHT11 očitavanje i princip histereze u temperaturnom regulatoru.
Referentna postavka
- Ploča u Arduino IDE-u
- ESP32 Dev Module
- Arduino-ESP32
- 3.3.12
- Serial Monitor
- 115200 baud
Ovo je referentni profil, a ne identifikacija ploče sa fotografije. Za ESP32-C3/S2/S3 ili drugačije označenu ploču treba prilagoditi GPIO i provjeriti šemu prije spajanja.
Šta ćemo napraviti
Napravićemo LED model termostata sa tri moguća zahtjeva: crvena LED znači da je hladno i da se traži grijanje, zelena označava opseg oko cilja, a žuta znači da je toplo i da se traži hlađenje. DHT11 daje temperaturu i vlažnost, a potenciometar bira cilj između 18 i 30 °C. Serial Monitor prikazuje mjerenje, cilj i stanje.
Ovaj sklop ne grije i ne hladi prostoriju. Boje predstavljaju odluku programa, pa možemo bez stvarnog grijača ili ventilatora istražiti pragove, prethodno stanje i grešku senzora. Za razliku od jednostavnog poređenja sa jednom granicom, odluka u dijelu opsega zavisi i od toga odakle smo došli.
Šta ćeš naučiti
- Prevesti sirovo ADC očitavanje u zadatu temperaturu.
- Razlikovati prag ulaska u stanje od praga povratka.
- Napraviti tri međusobno isključiva temperaturna stanja.
- Ponovo razmotriti posljednje važeće mjerenje kada se cilj promijeni.
- Prepoznati nevažeći uzorak i poništiti raniju odluku.
- Dokumentovati očekivano ponašanje tabelom prelaza.
Predznanje
Završi kurseve 5, 10 i 17: potenciometar na analognom ulazu, DHT11 očitavanje i princip histereze. Potrebni su rad sa Serial Monitorom, serijski otpornik za LED i razumijevanje millis() intervala. Referenca je klasični ESP32 sa modulom ESP32-WROOM-32. Oznaka GPIO34 znači broj signala, a ne trideset četvrti fizički izvod ploče. Ovaj raspored se ne prenosi automatski na ESP32-C3 ili ESP32-S3.
Potrebna oprema i provjera varijanti
| Komponenta | Količina | Specifikacija i napomene |
|---|---|---|
| ESP32 razvojna ploča sa klasičnim ESP32-WROOM-32 modulom | 1 | GPIO nazivi moraju odgovarati referentnom profilu; fizički položaj izvoda provjeriti na ploči. |
| USB kabl za podatke i napajanje | 1 | Priključak prilagoditi ploči; komplet na slici navodi Micro-USB. |
| Eksperimentalna ploča | 1 | Komplet navodi 830 kontaktnih tačaka. Provjeriti eventualni prekid naponskih vodova. |
| DHT11 modul temperature i relativne vlažnosti | 1 | Tropriključni VCC/GND/DATA modul potvrđen za 3,3 V; DATA pull-up samo prema 3V3. Neke varijante ne rade pouzdano na 3,3 V. |
| Potenciometar 10 kΩ | 1 | Dva krajnja izvoda i klizač W; raspored utvrditi prema stvarnoj komponenti. |
| Crvena LED dioda | 1 | Anoda A i katoda K; provjeriti polaritet stvarne komponente. |
| Zelena LED dioda | 1 | Anoda A i katoda K. |
| Žuta LED dioda | 1 | Anoda A i katoda K. |
| Otpornik 220 Ω | 3 | Po jedan otpornik za svaku LED granu; 220 Ω iz kompleta. |
| Otpornik 10 kΩ | 1 | R4 je pull-up od DHT DATA prema 3V3; provjeri i postojeći pull-up na modulu. |
| Spojni kablovi | 17 | M-M ili F-M prema priključcima ploče. Okvirna količina; zavisi od rasporeda i vrste priključaka. |
Pripremi tri odvojene LED: crvenu D1, zelenu D2 i žutu D3. Svaka dobija svoj otpornik od 220 Ω; jedan zajednički otpornik ne odgovara ovoj šemi. Potenciometar je referentno 10 kΩ. DHT11 modul mora biti potvrđen za napajanje i DATA logiku od 3,3 V. Fotografija kompleta ne potvrđuje njegov unutrašnji raspored.
R4 od 10 kΩ je pull-up između DATA i 3V3. Ako modul već ima svoj pull-up, provjeri da i on vodi na 3,3 V; spoljašnji R4 u referentnoj šemi ostaje na istim čvorovima. U ovom kursu nema releja, tranzistora ni zaštitne diode. Provjeri količine otpornika po vrijednostima prije sastavljanja.
Tri stanja i dva načina povratka
Za cilj od 24 °C program ulazi u HEAT na 23 °C ili manje, odnosno u COOL na 25 °C ili više. Iz HEAT se vraća u IN_RANGE kada temperatura dostigne cilj od 24 °C; iz COOL se vraća kada temperatura padne na cilj. Unutar međuprostora može, dakle, ostati ranije stanje. Na 23,5 °C prethodni HEAT ostaje HEAT, a prethodni IN_RANGE ostaje IN_RANGE.
Redoslijed poređenja je namjeran: prvo donji prag, zatim gornji, pa zadržavanje HEAT ispod cilja i COOL iznad cilja. Ako uzorak naglo pređe sa 22 na 26 °C, stanje neposredno prelazi iz HEAT u COOL. U važećem režimu svijetli samo jedna LED. Ovakva histereza smanjuje promjene odluke oko granica; ne popravlja tačnost senzora. DHT11 je osnovni senzor ograničene tačnosti. Adafruit — pregled DHT senzora
Šema i redoslijed spajanja
Šema povezivanja
Sve komponente su na 3,3 V. R4 je DHT DATA pull-up prema 3V3. Crvena LED prikazuje HEAT, zelena IN_RANGE, žuta COOL; u stanju FAULT sve trepću kao posebna indikacija greške. Povezuju se samo LED pokazatelji, bez grijača, hlađenja ili releja.
| Od | Do | Opis veze |
|---|---|---|
ESP32.3V3 | U1.VCC | DHT11 modul potvrđen za rad na 3,3 V. |
ESP32.GND | U1.GND | Zajednička masa. |
U1.DATA | ESP32.GPIO26 | DHT DATA prema GPIO26. |
ESP32.3V3 | R4.1 | R4 od 10 kΩ povlači DATA prema 3V3. |
R4.2 | U1.DATA | Drugi kraj R4 na DATA/GPIO26 čvor. |
ESP32.3V3 | RV1.END_A | Jedan krajnji izvod potenciometra 10 kΩ. |
ESP32.GND | RV1.END_B | Drugi krajnji izvod. |
RV1.W | ESP32.GPIO34 | Klizač W prema ADC1 ulazu GPIO34. |
ESP32.GPIO25 | R1.1 | Izlaz za LED (crvena) prema otporniku 220 Ω. |
R1.2 | D1.A | Serijski otpornik prema anodi A. |
D1.K | ESP32.GND | Katoda K prema masi. |
ESP32.GPIO27 | R2.1 | Izlaz za LED (zelena) prema otporniku 220 Ω. |
R2.2 | D2.A | Serijski otpornik prema anodi A. |
D2.K | ESP32.GND | Katoda K prema masi. |
ESP32.GPIO33 | R3.1 | Izlaz za LED (žuta) prema otporniku 220 Ω. |
R3.2 | D3.A | Serijski otpornik prema anodi A. |
D3.K | ESP32.GND | Katoda K prema masi. |
- Isključi USB i spoji GND ploče na zajedničku masu prototipske pločice.
- Poveži DHT11 VCC na 3V3, GND na GND, DATA na GPIO26. Postavi R4 od 10 kΩ između DATA i 3V3.
- Krajeve potenciometra spoji na 3V3 i GND; njegov klizač vodi na GPIO34.
- Crvenu granu spoji GPIO25 → R1 od 220 Ω → D1 anoda; D1 katodu na GND.
- Zelenu granu spoji GPIO27 → R2 od 220 Ω → D2 anoda; D2 katodu na GND.
- Žutu granu spoji GPIO33 → R3 od 220 Ω → D3 anoda; D3 katodu na GND.
- Provjeri polaritet LED i položaj klizača, odsustvo spoja 3V3–GND i zajedničku masu. Tek zatim uključi USB.
GPIO34 je samo ulaz i ovdje služi potenciometru. Spoljašnji sklop koristi 3,3 V; nijedan signal ne vodi na 5 V. Kod četvoropinskog DHT kućišta i trospojnog modula raspored može biti različit, zato poveži oznake prema dokumentaciji konkretnog dijela. Adafruit — povezivanje DHT senzora
Priprema Arduino okruženja
Koristi Arduino IDE 2.x, paket esp32 by Espressif Systems 3.3.12, ploču ESP32 Dev Module i Serial Monitor na 115200 baud. U Library Manageru instaliraj DHT sensor library 1.4.7 i Adafruit Unified Sensor 1.1.15.
Raspakuj primjer, otvori led_thermostat.ino iz istoimenog foldera, izaberi serijski port, pokreni Verify pa Upload. Provjeri da je odabran tip DHT11, ne DHT22. Verzije su referentno okruženje ovog paketa; uspješna provjera logike na računaru nije dokaz stvarne ESP32 kompilacije ili rada konkretne biblioteke.
| Biblioteka i izdanje | Verzija | Zavisnosti |
|---|---|---|
| DHT sensor library | 1.4.7 | Adafruit Unified Sensor 1.1.15 |
| Adafruit Unified Sensor | 1.1.15 | Nema |
Kompletan program
#include <Arduino.h>
#include <DHT.h>
#include <math.h>
constexpr uint8_t DHT_PIN = 26;
constexpr uint8_t POT_PIN = 34;
constexpr uint8_t HEAT_LED_PIN = 25;
constexpr uint8_t RANGE_LED_PIN = 27;
constexpr uint8_t COOL_LED_PIN = 33;
constexpr uint32_t SAMPLE_MS = 2500;
constexpr uint32_t POT_SAMPLE_MS = 100;
constexpr float BAND_C = 1.0f;
DHT dht(DHT_PIN, DHT11);
enum ThermostatState { WAITING, HEAT, IN_RANGE, COOL, FAULT };
ThermostatState state = WAITING;
bool currentValid = false;
float currentTemperature = NAN, currentHumidity = NAN;
uint16_t rawValue = 0;
uint8_t targetC = 18;
uint32_t lastSampleAt = 0, lastPotAt = 0, faultSince = 0;
void setState(ThermostatState next, uint32_t now);
uint8_t targetFromRaw(uint16_t raw) {
return static_cast<uint8_t>(18UL + static_cast<uint32_t>(raw) * 12UL / 4095UL);
}
void setState(ThermostatState next, uint32_t now) {
if (next == state) return;
state = next;
if (state == FAULT) faultSince = now;
Serial.print("State=");
switch (state) {
case WAITING: Serial.println("WAITING"); break;
case HEAT: Serial.println("HEAT (red)"); break;
case IN_RANGE: Serial.println("IN_RANGE (green)"); break;
case COOL: Serial.println("COOL (yellow)"); break;
case FAULT: Serial.println("FAULT"); break;
}
}
void evaluateThermostat(uint32_t now) {
if (!currentValid) return;
const float target = static_cast<float>(targetC);
if (currentTemperature <= target - BAND_C) setState(HEAT, now);
else if (currentTemperature >= target + BAND_C) setState(COOL, now);
else if (state == HEAT && currentTemperature < target) return;
else if (state == COOL && currentTemperature > target) return;
else setState(IN_RANGE, now);
}
void sampleEnvironment(uint32_t now) {
const float humidity = dht.readHumidity();
const float temperature = dht.readTemperature();
const bool wasValid = currentValid;
currentValid = isfinite(temperature) && isfinite(humidity);
if (!currentValid) {
currentTemperature = currentHumidity = NAN;
setState(FAULT, now); // Discard the previous hysteresis state.
} else {
currentTemperature = temperature;
currentHumidity = humidity;
if (!wasValid) setState(IN_RANGE, now);
evaluateThermostat(now);
}
if (currentValid) {
Serial.print("T_C="); Serial.print(currentTemperature, 1);
Serial.print(" RH_pct="); Serial.print(currentHumidity, 1);
} else Serial.print("SENSOR ERROR: no current reading");
Serial.print(" target_C="); Serial.println(targetC);
}
void updateLeds(uint32_t now) {
const bool blink = state == FAULT && static_cast<uint32_t>(now - faultSince) % 500UL < 250UL;
digitalWrite(HEAT_LED_PIN, state == HEAT || blink ? HIGH : LOW);
digitalWrite(RANGE_LED_PIN, state == IN_RANGE || blink ? HIGH : LOW);
digitalWrite(COOL_LED_PIN, state == COOL || blink ? HIGH : LOW);
}
void setup() {
Serial.begin(115200);
pinMode(HEAT_LED_PIN, OUTPUT); digitalWrite(HEAT_LED_PIN, LOW);
pinMode(RANGE_LED_PIN, OUTPUT); digitalWrite(RANGE_LED_PIN, LOW);
pinMode(COOL_LED_PIN, OUTPUT); digitalWrite(COOL_LED_PIN, LOW);
analogReadResolution(12);
analogSetPinAttenuation(POT_PIN, ADC_11db);
rawValue = analogRead(POT_PIN);
targetC = targetFromRaw(rawValue);
dht.begin();
lastSampleAt = lastPotAt = millis();
Serial.println("LED thermostat: first DHT11 sample after 2.5 s.");
}
void loop() {
uint32_t now = millis();
if (static_cast<uint32_t>(now - lastPotAt) >= POT_SAMPLE_MS) {
lastPotAt = now;
rawValue = analogRead(POT_PIN);
const uint8_t newTarget = targetFromRaw(rawValue);
if (newTarget != targetC) {
targetC = newTarget;
Serial.print("Target_C="); Serial.println(targetC);
evaluateThermostat(now); // Reuse only a current valid sample.
}
}
if (static_cast<uint32_t>(now - lastSampleAt) >= SAMPLE_MS) {
lastSampleAt = now;
sampleEnvironment(now);
}
now = millis();
updateLeds(now);
}
Nakon uključivanja stanje WAITING drži sve LED ugašene do prvog pokušaja očitavanja. Prvi uzorak traži se poslije približno 2500 ms, a naredni u istom rasporedu. Ako je temperatura ili vlažnost nevažeća, stanje FAULT briše tekuće podatke i temperaturnu memoriju. Sve tri LED tada zajedno trepću: 250 ms uključene, 250 ms isključene. To je poseban vizuelni obrazac greške, a ne istovremeni zahtjev za grijanjem i hlađenjem.
Kako rade podešavanje i odlučivanje
ADC je podešen na 12 bita i ADC_11db. Potenciometar se čita na 100 ms, a targetFromRaw() računa cilj cjelobrojno: 18 + raw * 12UL / 4095. Vrijednosti 0, 2048 i 4095 daju 18, 24 i 30 °C. Ovo je mapiranje broja u korisničko podešavanje. Ne predstavlja kalibrisano mjerenje napona, niti obećava ravnomjerne stepene po uglu okretanja; ADC može dostići vrh prije mehaničkog kraja potenciometra. Espressif — ADC API
Promjena cilja kroz evaluateThermostat() odmah se razmatra uz posljednji važeći uzorak, i između DHT očitavanja. Isti stvarni uslovi zato mogu promijeniti boju samo zbog pomjeranja cilja. sampleEnvironment() zahtijeva konačne vrijednosti i temperature i vlažnosti, iako vlažnost ne ulazi u pragove. Čitanja ovog para uglavnom potiču iz istog DHT okvira, ne iz dva nezavisna svježa mjerenja. Adafruit — DHT implementacija
Poslije greške prvi važeći uzorak polazi od IN_RANGE, pa se zatim poredi sa oba praga. Na primjer, oporavak na 23,5 °C uz cilj 24 °C daje zelenu, čak i ako je prije greške svijetlila crvena. Greška se utvrđuje pri pokušaju čitanja; fizički prekid nije detektovan istog trenutka. LED modelu nije uvedeno vremensko ograničenje releja iz kursa 17.
Praktični eksperiment i očekivanja
Počni sa potenciometrom blizu sredine. Sačekaj prvi uzorak i uporedi cilj sa sobnom temperaturom. Zatim polako pomjeraj cilj iznad i ispod te temperature. Zapiši cilj, temperaturu, prethodno stanje i novu LED; samo posljednja kolona ne objašnjava histerezu.
Sljedeći niz je računski primjer sa ciljem 24 °C, a ne obećanje rezolucije DHT11 niti zabilježena mjerenja. Početno stanje je IN_RANGE.
| Temperatura redom | Novo stanje | Objašnjenje |
|---|---|---|
| 22,9 °C | HEAT, crvena | Pređen donji ulazni prag |
| 23,5 °C | HEAT, crvena | Povratak do cilja još nije dostignut |
| 24,0 °C | IN_RANGE, zelena | Dostignut cilj |
| 25,0 °C | COOL, žuta | Dostignut gornji ulazni prag |
| 24,5 °C | COOL, žuta | Još smo iznad cilja |
| 24,0 °C | IN_RANGE, zelena | Dostignut cilj sa gornje strane |
Za početnu grešku isključi USB, odspoji DATA pa ponovo uključi ploču. Očekuj kratko čekanje i zatim zajedničko treptanje. Spoj vrati uz ugašeno napajanje. Ovaj postupak resetuje program, pa ne dokazuje oporavak tokom istog rada; taj slučaj analiziraj iz logike ili kontrolisanim zadavanjem uzoraka u testnom okruženju.
Najčešći problemi
| Problem | Šta provjeriti |
|---|---|
| Sve tri LED trepću | DHT11 tip, DATA26, R4, napajanje i masu |
| Jedna boja nikada ne svijetli | Njen GPIO, otpornik, polaritet i odabrani cilj |
| Dvije boje stalno svijetle | Zamijenjene grane ili kratki spoj; važeća odluka bira jednu |
| Cilj ostaje na krajnjoj vrijednosti | Klizač GPIO34 i oba kraja potenciometra |
| Cilj povremeno preskoči jedan stepen | Šum oko ADC granice koraka; mirno postavi potenciometar |
| Boja se ne mijenja tačno oko sredine opsega | Provjeri prethodno stanje i povratak na cilj |
Samostalni izazovi
- Proširi razmak ulaznih pragova sa ±1 °C na ±2 °C, ali zadrži povratak na cilj. Za cilj 24 °C obradi niz 21,9; 23,0; 24,0; 26,0; 25,0; 24,0 °C.
- Odredi stanje poslije greške i oporavka na 23,5 °C za prvobitni opseg ±1 °C. Objasni zašto ne treba vratiti raniji HEAT samo zato što je bio aktivan prije greške.
Razrađena rješenja
U prvom zadatku promijeni BAND_C sa 1.0f na 2.0f. Time oba ulazna poređenja postaju target - 2 i target + 2; poređenja zadržavanja sa samim ciljem ostaju ista. Dobijaš HEAT, HEAT, IN_RANGE, COOL, COOL, IN_RANGE. Mijenjanje i povratnih poređenja napravilo bi drugi regulator.
U drugom zadatku greška poništava ranije stanje. Oporavak kreće od IN_RANGE; 23,5 °C nije ni ≤23 ni ≥25, pa ostaje IN_RANGE. Tako odluka koristi potvrđene podatke i jasno pravilo ponovnog pokretanja. Nevažeći uzorak ne zamjenjuje se nulom, jer bi nula pogrešno izazvala zahtjev HEAT.
Pitanja sa odgovorima
Zašto 23,5 °C može dati dvije različite boje? Zbog prethodnog stanja: HEAT se zadržava do cilja, dok IN_RANGE nema razlog da se uključi prije donjeg praga.
Da li histereza povećava tačnost DHT11? Ne. Ona mijenja pravilo odlučivanja, ne karakteristike mjerenja.
Zašto provjeravamo i vlažnost? U ovoj lekciji važeći uzorak definisan je kao potvrđen T/RH par; oštećen ili nevažeći dio obara cijeli uzorak.
Zašto tri LED smiju zajedno treptati? Samo poseban FAULT obrazac koristi sve tri; to nije važeće temperaturno stanje niti izlaz za stvarne uređaje.
Primarni izvori
- Adafruit — DHT pregled i ograničenja
- Adafruit — povezivanje DHT senzora
- Adafruit — izvorni kod DHT biblioteke
- Espressif — ADC API
- Espressif — GPIO API
Preuzimanja
Sačuvaj tabelu očekivanja i svoje opažanje uz program. Provjera programske logike, stvarna kompilacija za ESP32 i fizička proba predstavljaju odvojene rezultate.
