Model hlađenja: podesivi temperaturni regulator

Poveži DHT11, OLED, potenciometar i relej u model regulatora. Razlikuj temperaturni zahtjev od stvarnog izlaza, primijeni histerezu i minimalni interval prebacivanja, a pri grešci senzora ugasi signalnu LED.

Zvuk, releji i interaktivni projektiSrednji nivo105 min

Ciljevi učenja

  • Zadati cilj od 20 do 30 °C potenciometrom.
  • Objasniti pragove uključivanja i isključivanja sa histerezom.
  • Razlikovati zahtjev regulatora, stvarni relej i oznaku HOLD.
  • Obraditi nevažeće očitavanje uz trenutno isključivanje izlaza.
  • Povezati relejni modul preko dodatnog tranzistora i provjeriti kontaktni LED model.

Prije početka

Završi kurseve 10, 11 i 16: DHT11 očitavanje, OLED adresa i relejni modul sa tranzistorskim drajverom.

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 model temperaturnog regulatora: potenciometar zadaje željenu temperaturu, DHT11 mjeri uslove, OLED prikazuje stanje, a relej uključuje signalnu LED. LED predstavlja stvarno uključen izlaz za hlađenje; u ovom sklopu nema ventilatora, pumpe, grijača ni mrežnog napona. Zato uključivanje ne snižava stvarnu sobnu temperaturu.

Šta ćeš naučiti

  • Podesiti temperaturni cilj analognim ulazom.
  • Primijeniti histerezu i minimalni razmak između promjena releja.
  • Razlikovati zahtjev regulatora od stvarnog izlaza.
  • Dati grešci senzora prednost nad vremenskim ograničenjem.
  • Povezati odvojene zadatke mjerenja, odlučivanja i prikaza.

Predznanje

Završi kurseve 10, 11 i 16. Potrebni su DHT11 očitavanje, OLED adresa i tranzistorsko upravljanje relejnim modulom. Referenca je klasični ESP32/ESP32-WROOM-32. GPIO oznake nisu fizički redni brojevi izvoda; druge ESP32 porodice zahtijevaju prilagođavanje.

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.
Dvokanalni relejski modul 5 V — aktivno LOW1Referentni modul ima povlačenje IN na HIGH, ugrađene drajvere i diode zavojnica. Tačne osobine provjeri; na fotografiji je potvrđena samo kategorija 5 V/dva kanala. Koristi se jedan kanal.
Dodatni NPN tranzistor PN2222A1Obavezno dodatno — nije na slici kompletaDODATNO, nije naveden u kompletu. Identifikuj B/C/E prema tačnom proizvođaču; PN2222A i P2N2222A mogu imati različit raspored.

DODATNO: tranzistor nije naveden na fotografiji kompleta.

Otpornik 1 kΩ1Otpornik baze; u kursu 14 i paralelni otpornik piezo elementa. Količina ove vrijednosti u kompletu nije potvrđena.
Otpornik 10 kΩ2R2 je pull-down baze Q1 prema GND; R4 je pull-up DHT DATA prema 3V3.
Crvena LED dioda1Anoda A i katoda K; provjeriti polaritet stvarne komponente.
Otpornik 220 Ω1Po jedan otpornik za svaku LED granu; 220 Ω iz kompleta.
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.
OLED displej 0,96″ — referentni I2C SSD1306 128×641VCC/GND/SDA/SCL, napajanje 3,3 V, pull-up otpornici na modulu prema 3V3. Kontroler, interfejs i rezolucija sa fotografije nisu potvrđeni.
Potenciometar 10 kΩ1Dva krajnja izvoda i klizač W; raspored utvrditi prema stvarnoj komponenti.
Spojni kablovi22M-M ili F-M prema priključcima ploče.

Okvirna količina; zavisi od fizičkog rasporeda.

PN2222A tranzistor je dodatna obavezna komponenta koja nije prikazana u kompletu. Provjeri B, C i E za tačnog proizvođača i kućište; sličan naziv tranzistora ne potvrđuje isti raspored. onsemi — PN2222A

Relejni modul mora biti deklarisan za 5 V, aktivan na LOW, sa podizanjem ulaza na HIGH kada je otpušten i ugrađenim drajverima i zaštitom zavojnica. Fotografija ne potvrđuje njegovu šemu. DHT11 modul i SSD1306 OLED 128 × 64 moraju podržavati napajanje i logiku 3,3 V. OLED sa SH1106 kontrolerom zahtijeva drugi drajver.

Zašto postoje dva praga

Za zadati cilj od 24 °C zahtjev za hlađenjem uključuje se na 25 °C ili više, a isključuje na 23 °C ili manje. Između pragova ostaje prethodno stanje zahtjeva. To je histereza: male promjene oko cilja ne izazivaju stalno prebacivanje.

Drugi mehanizam je MIN_SWITCH_MS = 5000: između običnih promjena stvarnog releja mora proći najmanje pet sekundi. Zahtjev se može promijeniti prije isteka, ali izlaz tada čeka. Ovo je nastavna vrijednost, a ne zaštita ili specifikacija za kompresor ili drugi stvarni uređaj.

Šema i redoslijed spajanja

Šema povezivanja

Isti relejski LED krug kao u kursu 16; nijedan stvarni potrošač hlađenja se ne spaja. DHT11 i OLED su na 3,3 V, a relejski modul na provjerenom USB5V vodu. R2 je pull-down baze, R4 je DATA pull-up. Q1 je dodatna komponenta.

Šema povezivanja — električne veze navedene u tabeli ispod
Šema povezivanjaUvećajSVGPNG
Veze · Šema povezivanja
OdDoOpis veze
ESP32.GPIO25R1.1Otpornik R1 od 1 kΩ ograničava struju baze.
R1.2Q1.BBaza dodatnog PN2222A tranzistora.
Q1.BR2.1R2 od 10 kΩ drži bazu nisko dok GPIO ne upravlja.
R2.2ESP32.GNDDrugi kraj pull-down otpornika.
Q1.EESP32.GNDEmiter prema zajedničkoj masi; provjeriti stvarni B/C/E raspored.
Q1.CK1.IN1Kolektor obara IN1 na LOW kada je GPIO25 HIGH.
ESP32.USB5VK1.VCCNapajanje referentnog modula 5 V; JD-VCC kratkospojnik ako postoji prema dokumentaciji.
ESP32.GNDK1.GNDZajednička masa modula i upravljačkog tranzistora.
ESP32.USB5VK1.IN2Neiskorišćeni aktivno-LOW ulaz drugog kanala drži se HIGH, odnosno isključeno.
ESP32.3V3K1.COM1Zajednički kontakt prenosi samo 3,3 V LED krug.
K1.NO1R3.1Normalno otvoreni kontakt prema R3 od 220 Ω.
R3.2D1.ASerijski otpornik prema anodi LED.
D1.KESP32.GNDKatoda indikacione LED prema masi; NC1 ostaje nepovezan.
ESP32.3V3U1.VCCDHT11 modul potvrđen za rad na 3,3 V.
ESP32.GNDU1.GNDZajednička masa.
U1.DATAESP32.GPIO26DHT11 DATA prema GPIO26.
ESP32.3V3R4.1R4 od 10 kΩ povlači DATA na 3V3.
R4.2U1.DATADrugi kraj R4 na DATA/GPIO26 čvor.
ESP32.3V3OLED1.VCCReferentni I2C SSD1306 128×64 na 3,3 V.
ESP32.GNDOLED1.GNDZajednička masa.
ESP32.GPIO21OLED1.SDAI2C podaci.
ESP32.GPIO22OLED1.SCLI2C takt.
ESP32.3V3RV1.END_AJedan krajnji izvod potenciometra 10 kΩ.
ESP32.GNDRV1.END_BDrugi krajnji izvod.
RV1.WESP32.GPIO34Klizač prema ulazu ADC1.
  1. Isključi USB. Poveži zajedničku masu ESP32, senzora, ekrana i relejnog modula.
  2. Spoji GPIO25 preko R1 od 1 kΩ na Q1.B; R2 od 10 kΩ između B i GND; Q1.E na GND; Q1.C na IN1 modula.
  3. VCC modula i neiskorišćeni IN2 spoji na provjereni USB 5 V izvod ploče. Ako postoji JD-VCC, zadrži samo dokumentovani spoj za zajedničko napajanje.
  4. Kontaktnu granu spoji: 3V3 na COM1, NO1 preko R3 od 220 Ω na LED anodu, a katodu na GND. NC1 i kontakti drugog kanala ostaju prazni.
  5. DHT11 VCC spoji na 3V3, GND na GND, DATA na GPIO26. R4 od 10 kΩ ide između DATA i 3V3, i kada modul već ima svoj pull-up.
  6. OLED: VCC na 3V3, GND na GND, SDA na GPIO21, SCL na GPIO22. Njegovi pull-up otpornici moraju voditi na 3,3 V.
  7. Krajeve potenciometra spoji na 3V3 i GND, a klizač na GPIO34. Pregledaj sve grane prije USB napajanja.

GPIO HIGH uključuje Q1, Q1 spušta IN1 na LOW i relej zatvara COM1–NO1. Tranzistor upravlja ulazom modula; ne upravlja golom zavojnicom. Ne dodaj zaštitnu diodu preko IN1. Provjeri ukupan USB kapacitet, uključujući struju zavojnice; ne spajaj drugo 5 V napajanje paralelno USB-u. Espressif — napajanje ploče

Priprema Arduino okruženja

Koristi Arduino IDE 2.x, esp32 by Espressif Systems 3.3.12, ploču ESP32 Dev Module i Serial Monitor na 115200 baud. Instaliraj sljedeće verzije kroz Library Manager:

BibliotekaVerzija
DHT sensor library by Adafruit1.4.7
Adafruit Unified Sensor1.1.15
Adafruit SSD13062.5.17
Adafruit GFX Library1.12.6
Adafruit BusIO1.17.4

Wire dolazi sa ESP32 paketom. Raspakuj Arduino ZIP, otvori cooling_controller.ino iz istoimenog foldera, izaberi port i pokreni Verify pa Upload.

Library Manager — dokumentovane verzije i zavisnosti
Biblioteka i izdanjeVerzijaZavisnosti
DHT sensor library1.4.7Adafruit Unified Sensor 1.1.15
Adafruit Unified Sensor1.1.15Nema
Adafruit SSD13062.5.17Adafruit GFX Library 1.12.6
Adafruit GFX Library1.12.6Adafruit BusIO 1.17.4
Adafruit BusIO1.17.4Nema

Wire je uključen u ESP32 core; ne instalira se zasebno kroz Library Manager.

Kompletan program

cooling_controller.ino Arduino / C++
Preuzmi .ino

#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <DHT.h>
#include <math.h>

constexpr uint8_t RELAY_DRIVER_PIN = 25;
constexpr uint8_t DHT_PIN = 26;
constexpr uint8_t POT_PIN = 34;
constexpr uint8_t SDA_PIN = 21;
constexpr uint8_t SCL_PIN = 22;
constexpr uint8_t OLED_ADDRESS = 0x3C;
constexpr uint32_t SAMPLE_MS = 2500;
constexpr uint32_t POT_SAMPLE_MS = 100;
constexpr uint32_t DISPLAY_MS = 250;
constexpr uint32_t MIN_SWITCH_MS = 5000;
Adafruit_SSD1306 display(128, 64, &Wire, -1, 100000UL, 100000UL);
DHT dht(DHT_PIN, DHT11);
bool ready = false;
bool sampleAttempted = false;
bool currentValid = false;
bool coolingDemand = false;
bool relayOn = false;
float currentTemperature = NAN;
float currentHumidity = NAN;
uint16_t rawValue = 0;
uint8_t targetC = 20;
uint32_t lastSampleAt = 0;
uint32_t lastPotAt = 0;
uint32_t lastDisplayAt = 0;
uint32_t lastRelayChangeAt = 0;

void scanI2CBus() {
  Serial.println("I2C scan: ACK identifies an address, not the display controller.");
  uint8_t found = 0;
  for (uint8_t address = 1; address < 127; ++address) {
    Wire.beginTransmission(address);
    if (Wire.endTransmission() == 0) {
      Serial.print("ACK at 0x"); Serial.println(address, HEX);
      ++found;
    }
  }
  if (found == 0) Serial.println("No I2C device acknowledged.");
}

bool initializeDisplay() {
  if (!Wire.begin(SDA_PIN, SCL_PIN)) {
    Serial.println("OLED ERROR: I2C initialization failed; reset to retry.");
    return false;
  }
  Wire.setClock(100000);
  Wire.setTimeOut(50);
  scanI2CBus();
  Wire.beginTransmission(OLED_ADDRESS);
  if (Wire.endTransmission() != 0) {
    Serial.println("OLED ERROR: configured address did not ACK; check wiring/address and reset.");
    return false;
  }
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS, false, false)) {
    Serial.println("OLED ERROR: buffer initialization failed; reset to retry.");
    return false;
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setTextWrap(false);
  return true;
}

uint8_t targetFromRaw(uint16_t raw) {
  return static_cast<uint8_t>(20UL + static_cast<uint32_t>(raw) * 10UL / 4095UL);
}

void evaluateDemand() {
  if (!currentValid) coolingDemand = false;
  else if (currentTemperature >= static_cast<float>(targetC) + 1.0f) coolingDemand = true;
  else if (currentTemperature <= static_cast<float>(targetC) - 1.0f) coolingDemand = false;
}

void setRelay(bool on, uint32_t now) {
  if (on == relayOn) return;
  relayOn = on;
  digitalWrite(RELAY_DRIVER_PIN, relayOn ? HIGH : LOW);
  lastRelayChangeAt = now;
}

void applyRelay(uint32_t now) {
  if (!ready || !currentValid) {
    coolingDemand = false;
    setRelay(false, now);  // A fault overrides the normal minimum interval.
    return;
  }
  if (coolingDemand != relayOn && static_cast<uint32_t>(now - lastRelayChangeAt) >= MIN_SWITCH_MS) {
    setRelay(coolingDemand, now);
  }
}

void sampleEnvironment() {
  sampleAttempted = true;
  const float humidity = dht.readHumidity();
  const float temperature = dht.readTemperature();
  if (!isfinite(humidity) || !isfinite(temperature)) {
    currentValid = false;
    currentTemperature = currentHumidity = NAN;
  } else {
    currentValid = true;
    currentTemperature = temperature;
    currentHumidity = humidity;
  }
}

void reportSample() {
  if (!currentValid) Serial.print("SENSOR ERROR: no current reading");
  else {
    Serial.print("T_C="); Serial.print(currentTemperature, 1);
    Serial.print(" RH_pct="); Serial.print(currentHumidity, 1);
  }
  Serial.print(" target_C="); Serial.print(targetC);
  Serial.print(" demand="); Serial.print(coolingDemand ? "ON" : "OFF");
  Serial.print(" relay="); Serial.print(relayOn ? "ON" : "OFF");
  Serial.println(currentValid && coolingDemand != relayOn ? " HOLD" : "");
}

void drawController() {
  display.clearDisplay();
  display.setCursor(0, 0); display.print("Cooling LED model");
  display.setCursor(0, 12);
  if (!sampleAttempted) display.print("Waiting for DHT11");
  else if (!currentValid) display.print("SENSOR ERROR");
  else {
    display.print("T: "); display.print(currentTemperature, 1); display.print(" C");
    display.setCursor(0, 22); display.print("RH: "); display.print(currentHumidity, 1); display.print(" %");
  }
  display.setCursor(0, 34); display.print("Target: "); display.print(targetC); display.print(" C");
  display.setCursor(0, 44); display.print("Relay: "); display.print(relayOn ? "ON" : "OFF");
  display.setCursor(0, 54);
  if (currentValid && coolingDemand != relayOn) display.print("HOLD: minimum 5 s");
  else if (!currentValid) display.print("Relay forced OFF");
  else display.print("Demand: ");
  if (currentValid && coolingDemand == relayOn) display.print(coolingDemand ? "ON" : "OFF");
  display.display();
}

void setup() {
  Serial.begin(115200);
  pinMode(RELAY_DRIVER_PIN, OUTPUT);
  digitalWrite(RELAY_DRIVER_PIN, LOW);
  analogReadResolution(12);
  analogSetPinAttenuation(POT_PIN, ADC_11db);
  rawValue = analogRead(POT_PIN);
  targetC = targetFromRaw(rawValue);
  dht.begin();
  ready = initializeDisplay();
  if (!ready) return;
  drawController();
  Serial.println("Cooling LED model: first DHT sample after 2.5 s; initial relay OFF for at least 5 s.");
  const uint32_t now = millis();
  lastSampleAt = lastPotAt = lastDisplayAt = lastRelayChangeAt = now;
}

void loop() {
  if (!ready) return;
  uint32_t now = millis();
  if (static_cast<uint32_t>(now - lastPotAt) >= POT_SAMPLE_MS) {
    lastPotAt = now;
    rawValue = analogRead(POT_PIN);
    targetC = targetFromRaw(rawValue);
  }
  bool newSample = false;
  if (static_cast<uint32_t>(now - lastSampleAt) >= SAMPLE_MS) {
    lastSampleAt = now;
    sampleEnvironment();
    newSample = true;
  }
  now = millis();
  evaluateDemand();
  applyRelay(now);
  if (newSample) reportSample();
  if (newSample || static_cast<uint32_t>(now - lastDisplayAt) >= DISPLAY_MS) {
    lastDisplayAt = now;
    drawController();
  }
}

initializeDisplay() koristi I2C na 100 kHz, skenira adrese i provjerava OLED_ADDRESS, podrazumijevano 0x3C. Vrijednost 0x3D postavi samo ako je potvrđena. Odgovor na adresi ne dokazuje tip kontrolera. Pri neuspješnoj pripremi ekrana redovni rad ne počinje i relej ostaje isključen.

Od potenciometra do zadate temperature

Program očitava GPIO34 na 100 ms, uz 12-bitni ADC i ADC_11db. targetFromRaw() računa 20 + raw * 10 / 4095: sirovo 0 daje 20 °C, 2048 daje 25 °C, a 4095 daje 30 °C. To je korisničko podešavanje, ne mjerenje napona niti temperature.

Promjena cilja odmah se razmatra uz posljednju važeću temperaturu, bez čekanja narednog DHT ciklusa. Cilj blizu granice koraka može oscilovati zbog šuma ADC-a; histereza i vremensko ograničenje zato imaju različite uloge.

Mjerenje, zahtjev i stvarni izlaz

sampleEnvironment() očitava DHT na 2500 ms, prvi put nakon tog intervala od pripreme prikaza. drawController() osvježava OLED približno na 250 ms. Brže osvježavanje ne stvara nov uzorak. Razlika vremena millis() omogućava raspoređivanje zadataka bez dugih delay() pauza.

evaluateDemand() računa coolingDemand iz važećeg T/RH para; applyRelay() upravlja stvarnim stanjem relayOn. U međuprostoru pragova zahtjev zadržava svoje stanje čak i kada stvarni relej još čeka. Oznaka HOLD znači da se važeći zahtjev razlikuje od izlaza, ali minimalni interval nije istekao. Početni OFF period takođe traje najmanje pet sekundi.

Šta se događa pri grešci

Ako temperatura ili vlažnost nije konačan broj, npr. NaN, currentValid postaje netačan, zahtjev se gasi, a relej se isključuje odmah po obradi tog pokušaja. Pet sekundi ne odlaže ovo isključivanje. Greška se otkriva tokom očitavanja, ne samim trenutkom nastanka fizičkog kvara. Adafruit — obrada očitavanja

Ako je relej stvarno prešao ON→OFF, od tog trenutka počinje novi minimalni OFF period. Ponavljane greške pri već ugašenom releju ne pokreću taj period iznova. Oporavak vraća mjerenja; ponovno uključivanje zahtijeva odgovarajući temperaturni prag i proteklo vrijeme. Nula se ne prikazuje kao zamjena za neuspjelo mjerenje.

Prvo pokretanje i praktični eksperiment

Posmatraj čekanje prvog uzorka, izmjerenu temperaturu i cilj. Polako okreni potenciometar ispod, zatim iznad sobne temperature, svaki put sačekaj više od pet sekundi. Prati kontaktno povezanu LED, a ne samo lampicu na relejnom modulu. Zapiši svoja opažanja; tabela sadrži očekivanja.

Uslov uz važeći uzorakOčekivanjeTvoje opažanje
T ≥ cilj + 1; OFF traje najmanje 5 sZahtjev ON, relej ONDopuni
T ≤ cilj − 1; ON traje kraće od 5 sZahtjev OFF, relej ON, HOLDDopuni
Isti zahtjev nakon isteka minimumaRelej OFFDopuni
T između pragovaZahtjev zadržava prethodno stanjeDopuni
Nema važećeg uzorkaČekanje/greška; relej OFFDopuni

Grešku početnog očitavanja provjeri isključivanjem USB-a, odspajanjem DHT DATA, pa ponovnim uključivanjem. Vrati spoj uz ugašeno napajanje. Ovaj postupak resetuje tajmere; ne dokazuje vremenski oporavak tokom istog rada.

Najčešći problemi

ProblemProvjera
Relej reaguje suprotnoPotvrdi aktivni LOW modul i tranzistorsku inverziju.
Modul klikne, LED ne svijetliProvjeri COM1, NO1, R3 i polaritet LED.
Temperatura nedostajeProvjeri DHT11 tip, GPIO26, R4 i 3,3 V varijantu.
OLED ne počinje radProvjeri 21/22, adresu, kontroler i napajanje.
Cilj se mijenja, relej mirujeProvjeri oba praga, važenje uzorka i HOLD.
Ploča se resetuje pri prebacivanjuProvjeri USB kapacitet, masu i priključke modula.

Samostalni izazov i razrađeno rješenje

Postavi cilj 24 °C. Pretpostavi važeće uzorke i početni OFF od t = 0: u t = 2,5 s temperatura je 26 °C; u t = 5 s i dalje 26 °C; u t = 7,5 s je 22 °C; u t = 10 s ostaje 22 °C. Odredi zahtjev i relej.

Rješenje: 2,5 s — zahtjev ON, relej OFF/HOLD; 5 s — ON/ON; 7,5 s — OFF/ON/HOLD; 10 s — OFF/OFF. Ako se umjesto niske temperature u 7,5 s pojavi greška, relej se gasi tada. Za novi ON mora proći najmanje do 12,5 s, uz važeći uzorak i ON zahtjev. Ovo su zadati primjeri, ne izmjerene vrijednosti.

Preuzimanja

Sačuvaj dnevnik opažanja. Računarska provjera logike, stvarna kompilacija i fizički eksperiment predstavljaju odvojene rezultate.

Primarni izvori