LED termostat: hladno, u opsegu, toplo

Poveži DHT11, potenciometar i tri LED u model termostata. Podesi cilj od 18 do 30 °C, primijeni ulazne pragove i povratak na cilj te odvoji važeće temperaturno stanje od greške senzora.

Integrisani projekti i korisničke komandeSrednji nivo105 min

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

KomponentaKoličinaSpecifikacija i napomene
ESP32 razvojna ploča sa klasičnim ESP32-WROOM-32 modulom1GPIO nazivi moraju odgovarati referentnom profilu; fizički položaj izvoda provjeriti na ploči.
USB kabl za podatke i napajanje1Priključak prilagoditi ploči; komplet na slici navodi Micro-USB.
Eksperimentalna ploča1Komplet navodi 830 kontaktnih tačaka. Provjeriti eventualni prekid naponskih vodova.
DHT11 modul temperature i relativne vlažnosti1Tropriključ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Ω1Dva krajnja izvoda i klizač W; raspored utvrditi prema stvarnoj komponenti.
Crvena LED dioda1Anoda A i katoda K; provjeriti polaritet stvarne komponente.
Zelena LED dioda1Anoda A i katoda K.
Žuta LED dioda1Anoda A i katoda K.
Otpornik 220 Ω3Po jedan otpornik za svaku LED granu; 220 Ω iz kompleta.
Otpornik 10 kΩ1R4 je pull-up od DHT DATA prema 3V3; provjeri i postojeći pull-up na modulu.
Spojni kablovi17M-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.

Šema povezivanja — električne veze navedene u tabeli ispod
Šema povezivanjaUvećajSVGPNG
Veze · Šema povezivanja
OdDoOpis veze
ESP32.3V3U1.VCCDHT11 modul potvrđen za rad na 3,3 V.
ESP32.GNDU1.GNDZajednička masa.
U1.DATAESP32.GPIO26DHT DATA prema GPIO26.
ESP32.3V3R4.1R4 od 10 kΩ povlači DATA prema 3V3.
R4.2U1.DATADrugi kraj R4 na DATA/GPIO26 čvor.
ESP32.3V3RV1.END_AJedan krajnji izvod potenciometra 10 kΩ.
ESP32.GNDRV1.END_BDrugi krajnji izvod.
RV1.WESP32.GPIO34Klizač W prema ADC1 ulazu GPIO34.
ESP32.GPIO25R1.1Izlaz za LED (crvena) prema otporniku 220 Ω.
R1.2D1.ASerijski otpornik prema anodi A.
D1.KESP32.GNDKatoda K prema masi.
ESP32.GPIO27R2.1Izlaz za LED (zelena) prema otporniku 220 Ω.
R2.2D2.ASerijski otpornik prema anodi A.
D2.KESP32.GNDKatoda K prema masi.
ESP32.GPIO33R3.1Izlaz za LED (žuta) prema otporniku 220 Ω.
R3.2D3.ASerijski otpornik prema anodi A.
D3.KESP32.GNDKatoda K prema masi.
  1. Isključi USB i spoji GND ploče na zajedničku masu prototipske pločice.
  2. Poveži DHT11 VCC na 3V3, GND na GND, DATA na GPIO26. Postavi R4 od 10 kΩ između DATA i 3V3.
  3. Krajeve potenciometra spoji na 3V3 i GND; njegov klizač vodi na GPIO34.
  4. Crvenu granu spoji GPIO25 → R1 od 220 Ω → D1 anoda; D1 katodu na GND.
  5. Zelenu granu spoji GPIO27 → R2 od 220 Ω → D2 anoda; D2 katodu na GND.
  6. Žutu granu spoji GPIO33 → R3 od 220 Ω → D3 anoda; D3 katodu na GND.
  7. 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.

Library Manager — dokumentovane verzije i zavisnosti
Biblioteka i izdanjeVerzijaZavisnosti
DHT sensor library1.4.7Adafruit Unified Sensor 1.1.15
Adafruit Unified Sensor1.1.15Nema

Kompletan program

led_thermostat.ino Arduino / C++
Preuzmi .ino

#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 redomNovo stanjeObjašnjenje
22,9 °CHEAT, crvenaPređen donji ulazni prag
23,5 °CHEAT, crvenaPovratak do cilja još nije dostignut
24,0 °CIN_RANGE, zelenaDostignut cilj
25,0 °CCOOL, žutaDostignut gornji ulazni prag
24,5 °CCOOL, žutaJoš smo iznad cilja
24,0 °CIN_RANGE, zelenaDostignut 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ćuDHT11 tip, DATA26, R4, napajanje i masu
Jedna boja nikada ne svijetliNjen GPIO, otpornik, polaritet i odabrani cilj
Dvije boje stalno svijetleZamijenjene grane ili kratki spoj; važeća odluka bira jednu
Cilj ostaje na krajnjoj vrijednostiKlizač 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 opsegaProvjeri prethodno stanje i povratak na cilj

Samostalni izazovi

  1. 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.
  2. 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

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.