Sobna stanica: temperatura, vlažnost i OLED

Sastavi sobnu stanicu sa DHT11 senzorom, OLED prikazom, istorijskim minimumom i maksimumom te temperaturnom LED indikacijom sa histerezom.

Senzori i prikaz mjerenjaPočetni nivo90 min

Ciljevi učenja

  • Povezati DHT11, OLED i LED prema zajedničkoj šemi za 3,3 V.
  • Razdvojiti mjerenje na 2,5 sekunde od osvježavanja prikaza.
  • Pratiti minimum i maksimum uspješnih temperaturnih očitavanja od resetovanja.
  • Upravljati LED pomoću dva temperaturna praga i prethodnog stanja.
  • Obraditi neuspjelo očitavanje bez prikazivanja zastarjelih trenutnih vrijednosti.

Prije početka

Kursevi 02, 10 i 11: LED sa otpornikom, DHT11 očitavanje i SSD1306 I2C prikaz.

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

Spojićemo DHT11, OLED i LED u malu sobnu stanicu. Ekran prikazuje temperaturu, relativnu vlažnost, najnižu i najvišu do sada očitanu temperaturu. LED označava prelazak izabranog temperaturnog praga. Uređaj radi lokalno, bez Wi-Fi veze i bez računara kao ekrana.

Šta ćeš naučiti

  • Povezati senzor, ekran i signalnu LED u cjelinu.
  • Razdvojiti interval mjerenja i interval osvježavanja prikaza.
  • Sačuvati minimum i maksimum iz uspješnih očitavanja.
  • Primijeniti dva temperaturna praga, odnosno histerezu.
  • Prikazati grešku bez predstavljanja starog podatka kao trenutnog.

Predznanje

Završi kurseve 02, 10 i 11. Treba da poznaješ polaritet LED, DHT11 biblioteku i provjeru I2C adrese. Referentna ploča je klasični ESP32/ESP32-WROOM-32. GPIO oznake nisu redni brojevi fizičkih nožica; druge ESP32 porodice zahtijevaju prilagođavanje.

Potrebna oprema i ograničenja modula

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.
Otpornik 10 kΩ1R2: pull-up DATA linije na 3V3. Količina po vrijednosti u kompletu nije potvrđena.
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.
Crvena LED dioda1Anoda A i katoda K; provjeriti polaritet stvarne komponente.
Otpornik 220 Ω1Po jedan otpornik za svaku LED granu; 220 Ω iz kompleta.
Spojni kablovi12M-M ili F-M prema priključcima ploče.

Okvirna količina, zavisno od rasporeda i tipa priključaka.

Koristimo tropinski DHT11 modul deklarisan za 3,3 V i četvoropinski I2C SSD1306 OLED, 128 × 64, takođe za 3,3 V. Provjeri oznake konkretnih modula: fotografija ne potvrđuje kontroler ni raspored izvoda. SH1106 zahtijeva drugi drajver, a sam DHT11 sa četiri izvoda drugačiju fizičku šemu. Adafruit: DHT povezivanje, SSD1306

Šta mjerimo, a šta pamtimo

Kada očitavanje uspije, trenutna temperatura i vlažnost potiču iz tog ciklusa. Minimum i maksimum predstavljaju istoriju uspješnih temperatura od resetovanja. Ne čuvaju se nakon isključenja napajanja i ne predstavljaju nužno dnevni minimum i maksimum.

Ovo je edukativna sobna stanica. DHT11 i njegov smještaj ograničavaju rezultat; broj decimala na ekranu ne povećava tačnost senzora. Ne postavljaj ga uz zagrijanu ploču ako želiš uporedivo sobno očitavanje. Adafruit: pregled DHT senzora

Šema i redoslijed povezivanja

Šema povezivanja

DHT11 i OLED koriste 3,3 V i zajedničku masu. R2 od 10 kΩ je pull-up za DATA, a R1 od 220 Ω ograničava struju LED. Važe provjere DHT11 i SSD1306 modula iz kurseva 10 i 11.

Šema povezivanja — električne veze navedene u tabeli ispod
Šema povezivanjaUvećajSVGPNG
Veze · Šema povezivanja
OdDoOpis veze
ESP32.3V3DHT1.VCCDHT11 modul koji pouzdano radi na 3,3 V.
ESP32.GNDDHT1.GNDZajednička masa.
DHT1.DATAESP32.GPIO26Dvosmjerna podatkovna linija DHT11.
ESP32.3V3R2.1R2 je pull-up otpornik 10 kΩ prema 3V3.
R2.2DHT1.DATADrugi kraj R2 na isti DATA čvor kao GPIO26.
ESP32.3V3OLED1.VCCI2C SSD1306 modul 128×64, napajanje i pull-up otpornici za 3,3 V.
ESP32.GNDOLED1.GNDZajednička masa.
ESP32.GPIO21OLED1.SDAI2C podaci; pratiti oznaku SDA.
ESP32.GPIO22OLED1.SCLI2C takt; pratiti oznaku SCL.
ESP32.GPIO25R1.1Digitalni izlaz za LED.
R1.2D1.ASerijski otpornik R1, 220 Ω, prema anodi LED.
D1.KESP32.GNDKatoda prema zajedničkoj masi.
  1. Isključi USB. Poveži OLED VCC na 3V3, GND na GND, SDA na GPIO21 i SCL na GPIO22.
  2. Poveži DHT11 VCC na 3V3, GND na GND i DATA na GPIO26.
  3. Dodaj R2 od 10 kΩ između DATA i 3V3. U ovoj referentnoj šemi koristi se i kada modul ima svoj pull-up; nijedan pull-up ne smije ići na 5 V.
  4. Poveži GPIO25 preko R1 od 220 Ω na anodu LED, a katodu na GND.
  5. Provjeri zajedničku masu, oznake izvoda i OLED pull-up otpornike prema 3,3 V. Zatim priključi USB.

Ako konkretni DHT modul ne radi pouzdano na 3,3 V, provjeri njegovu specifikaciju i spoj. Prelazak napajanja na 5 V bez provjere DATA nivoa nije odgovarajuća korekcija.

Priprema Arduino okruženja

Koristi Arduino IDE 2.x, esp32 by Espressif Systems 3.3.12 i ESP32 Dev Module. U Library Manageru instaliraj ove verzije:

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 je dio ESP32 paketa. Raspakuj Arduino ZIP i otvori indoor_station.ino iz istoimenog foldera. Izaberi port, pokreni Verify i Upload, a Serial Monitor postavi na 115200 baud.

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

indoor_station.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 SDA_PIN = 21;
constexpr uint8_t SCL_PIN = 22;
constexpr uint8_t DHT_PIN = 26;
constexpr uint8_t LED_PIN = 25;
constexpr uint8_t OLED_ADDRESS = 0x3C;
constexpr uint16_t OLED_WIDTH = 128;
constexpr uint16_t OLED_HEIGHT = 64;
constexpr uint32_t SAMPLE_MS = 2500;
constexpr uint32_t DISPLAY_MS = 250;
constexpr float HOT_ON_C = 28.0f;
constexpr float HOT_OFF_C = 27.0f;
Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, -1, 100000UL, 100000UL);
DHT dht(DHT_PIN, DHT11);

bool ready = false;
bool sampleAttempted = false;
bool currentValid = false;
bool hasHistory = false;
bool hotLed = false;
float currentTemperature = NAN;
float currentHumidity = NAN;
float minTemperature = NAN;
float maxTemperature = NAN;
uint32_t lastSampleAt = 0;
uint32_t lastDisplayAt = 0;

void scanI2CBus() {
  Serial.println("I2C scan (an ACK alone does not identify 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 bus initialization failed.");
    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; reset to retry.");
    return false;
  }
  if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS, false, false)) {
    Serial.println("OLED ERROR: display buffer initialization failed; reset to retry.");
    return false;
  }
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setTextWrap(false);
  return true;
}

void drawStation() {
  display.clearDisplay();
  display.setCursor(0, 0); display.print("WB Maker Bridge");
  display.setCursor(0, 12);
  if (!sampleAttempted) {
    display.print("Waiting for DHT11");
    display.setCursor(0, 22); display.print("First read: 2.5 s");
  } else if (!currentValid) {
    display.print("SENSOR ERROR");
    display.setCursor(0, 22); display.print("No current reading");
  } 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, 32); display.print(hotLed ? "Hot LED: ON" : "Hot LED: OFF");
  display.setCursor(0, 44);
  if (hasHistory) {
    display.print("Hist min: "); display.print(minTemperature, 1); display.print(" C");
    display.setCursor(0, 54);
    display.print("Hist max: "); display.print(maxTemperature, 1); display.print(" C");
  } else {
    display.print("History: no data");
  }
  display.display();
}

void sampleEnvironment() {
  sampleAttempted = true;
  const float humidity = dht.readHumidity();
  const float temperature = dht.readTemperature();
  if (isnan(humidity) || isnan(temperature)) {
    currentValid = false;
    currentTemperature = NAN;
    currentHumidity = NAN;
    hotLed = false;
    digitalWrite(LED_PIN, LOW);
    Serial.println("SENSOR ERROR: no current reading; LED OFF; valid min/max history retained.");
    return;
  }
  currentValid = true;
  currentTemperature = temperature;
  currentHumidity = humidity;
  if (!hasHistory) {
    minTemperature = temperature;
    maxTemperature = temperature;
    hasHistory = true;
  } else {
    if (temperature < minTemperature) minTemperature = temperature;
    if (temperature > maxTemperature) maxTemperature = temperature;
  }
  if (temperature >= HOT_ON_C) hotLed = true;
  else if (temperature <= HOT_OFF_C) hotLed = false;
  digitalWrite(LED_PIN, hotLed ? HIGH : LOW);
  Serial.print("T_C="); Serial.print(temperature, 1);
  Serial.print(" RH_pct="); Serial.print(humidity, 1);
  Serial.print(" min_C="); Serial.print(minTemperature, 1);
  Serial.print(" max_C="); Serial.print(maxTemperature, 1);
  Serial.print(" LED="); Serial.println(hotLed ? "ON" : "OFF");
}

void setup() {
  Serial.begin(115200);
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);
  dht.begin();
  ready = initializeDisplay();
  if (!ready) return;
  lastSampleAt = millis();
  lastDisplayAt = lastSampleAt;
  drawStation();
  Serial.println("Indoor station: waiting 2.5 seconds for the first DHT11 sample.");
}

void loop() {
  if (!ready) return;
  const uint32_t now = millis();
  bool newSample = false;
  if (static_cast<uint32_t>(now - lastSampleAt) >= SAMPLE_MS) {
    lastSampleAt = now;
    sampleEnvironment();
    newSample = true;
  }
  if (newSample || static_cast<uint32_t>(now - lastDisplayAt) >= DISPLAY_MS) {
    lastDisplayAt = now;
    drawStation();
  }
}

Provjeri da OLED_ADDRESS odgovara rezultatu skeniranja iz kursa 11: podrazumijevano je 0x3C, a 0x3D koristi samo kada je potvrđen. ACK ne identifikuje kontroler. Ako inicijalizacija OLED-a ne uspije, program prijavljuje razlog i ne pokreće redovni rad.

Mjerenje i osvježavanje

SAMPLE_MS je 2500 ms. Nakon uspješne pripreme ekrana program čeka taj interval do prvog mjerenja. Do tada prikazuje Waiting for DHT11, ugašenu LED i odsustvo istorije. DHT biblioteka koristi minimalni interval od dvije sekunde; naš razmak je 2,5 sekunde. Izvorni DHT kod

sampleEnvironment() očitava temperaturu i vlažnost. drawStation() crta prikaz na svakih 250 ms, kao i odmah nakon mjernog pokušaja. Češće crtanje ne znači novo mjerenje: vrijednost ostaje iz posljednjeg ciklusa. Raspored koristi razliku vrijednosti millis().

Minimum, maksimum i histereza

Prvo uspješno očitavanje postavlja i minTemperature i maxTemperature. Naredna uspješna očitavanja proširuju taj raspon. hasHistory razlikuje stvarnu istoriju od početnog stanja bez podataka.

LED prati dva praga: temperatura najmanje 28 °C postavlja hotLed na uključeno, a najviše 27 °C na isključeno. Između pragova zadržava prethodno stanje. HOT_ON_C i HOT_OFF_C tako sprječavaju promjenu stanja pri svakom prelasku samo jednog praga. Početno stanje je isključeno.

Kako se obrađuje greška

Ako je bilo temperatura bilo vlažnost NaN, cijeli trenutni par se odbacuje. currentValid postaje netačan, ekran prikazuje SENSOR ERROR i No current reading, a LED se gasi. Raniji minimum i maksimum ostaju uz oznake Hist min i Hist max.

Program nastavlja pokušaje očitavanja. Prvim uspješnim oporavkom ponovo dobija trenutne vrijednosti; histereza tada polazi od ugašene LED. Zato oporavak unutar međuprostora pragova ostavlja LED ugašenu. Isključena LED tokom greške ne znači potvrđenu nisku temperaturu.

Pokretanje i provjera ponašanja

Prati početno čekanje, nekoliko očitavanja i zapis minimuma/maksimuma. Zabilježi stvarna opažanja. Za provjeru greške isključi USB, odspoji samo DHT DATA sa GPIO26, pa ponovo uključi uređaj; nakon mjernog pokušaja očekuj grešku. Vrati spoj uz isključeno napajanje. Ovaj test počinje bez istorije zbog ponovnog pokretanja. Zadržavanje istorije u tabeli odnosi se na grešku tokom istog rada.

UslovOčekivani rezultat
Prije prvog pokušajaČekanje; LED OFF; nema istorije
Prvi uspješan uzorakPrikaz T/RH; minimum = maksimum = temperatura
Temperatura dostigne 28 °CLED ON
Temperatura dostigne 27 °CLED OFF
Neuspješno očitavanjeGreška; LED OFF; postojeća istorija ostaje

Najčešći problemi

ProblemProvjera i korekcija
Nema OLED odgovoraProvjeri napajanje, SDA/SCL, adresu i tip kontrolera.
Stalno SENSOR ERRORProvjeri DHT11 tip, GPIO26, masu, napajanje i R2.
LED ne prati očekivanjaProvjeri polaritet, GPIO25, R1 i oba praga.
Minimum ostaje starTo je istorija od resetovanja, ne trenutno očitavanje.
Ekran se osvježava bez nove temperatureMjerenje i crtanje imaju različite intervale.

Samostalni izazov

Promijeni pragove na 30 °C za uključivanje i 28 °C za isključivanje. Na papiru prođi kroz niz uspješnih temperatura 27, 30, 29, 28, zatim grešku i uspješno očitavanje 29 °C. Odredi LED i istorijske ekstreme nakon svakog koraka. Niz je zadat za analizu, nije izmjeren rezultat.

Pitanja i razrađeni odgovori

Zašto ne upisujemo nulu pri grešci? Nula bi predstavljala izmišljeno mjerenje i mogla pokvariti minimum. Zašto ne brišemo istoriju? Ona i dalje opisuje ranije uspješne uzorke i jasno je označena kao istorijska.

Za izazov postavi HOT_ON_C = 30.0f i HOT_OFF_C = 28.0f. Redoslijed LED je OFF, ON, ON, OFF, OFF, OFF. Minimum je 27 °C; od drugog uzorka maksimum je 30 °C i ostaje takav. Greška ne mijenja ekstreme. Resetovanje ponovo briše istoriju.

Preuzimanja

Sačuvaj sopstvena opažanja uz program. Kompajliranje i fizička provjera predstavljaju odvojene rezultate.

Primarni izvori