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
| 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. |
| Otpornik 10 kΩ | 1 | R2: pull-up DATA linije na 3V3. Količina po vrijednosti u kompletu nije potvrđena. |
| OLED displej 0,96″ — referentni I2C SSD1306 128×64 | 1 | VCC/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 dioda | 1 | Anoda A i katoda K; provjeriti polaritet stvarne komponente. |
| Otpornik 220 Ω | 1 | Po jedan otpornik za svaku LED granu; 220 Ω iz kompleta. |
| Spojni kablovi | 12 | M-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.
| Od | Do | Opis veze |
|---|---|---|
ESP32.3V3 | DHT1.VCC | DHT11 modul koji pouzdano radi na 3,3 V. |
ESP32.GND | DHT1.GND | Zajednička masa. |
DHT1.DATA | ESP32.GPIO26 | Dvosmjerna podatkovna linija DHT11. |
ESP32.3V3 | R2.1 | R2 je pull-up otpornik 10 kΩ prema 3V3. |
R2.2 | DHT1.DATA | Drugi kraj R2 na isti DATA čvor kao GPIO26. |
ESP32.3V3 | OLED1.VCC | I2C SSD1306 modul 128×64, napajanje i pull-up otpornici za 3,3 V. |
ESP32.GND | OLED1.GND | Zajednička masa. |
ESP32.GPIO21 | OLED1.SDA | I2C podaci; pratiti oznaku SDA. |
ESP32.GPIO22 | OLED1.SCL | I2C takt; pratiti oznaku SCL. |
ESP32.GPIO25 | R1.1 | Digitalni izlaz za LED. |
R1.2 | D1.A | Serijski otpornik R1, 220 Ω, prema anodi LED. |
D1.K | ESP32.GND | Katoda prema zajedničkoj masi. |
- Isključi USB. Poveži OLED VCC na 3V3, GND na GND, SDA na GPIO21 i SCL na GPIO22.
- Poveži DHT11 VCC na 3V3, GND na GND i DATA na GPIO26.
- 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.
- Poveži GPIO25 preko R1 od 220 Ω na anodu LED, a katodu na GND.
- 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:
| Biblioteka | Verzija |
|---|---|
| DHT sensor library by Adafruit | 1.4.7 |
| Adafruit Unified Sensor | 1.1.15 |
| Adafruit SSD1306 | 2.5.17 |
| Adafruit GFX Library | 1.12.6 |
| Adafruit BusIO | 1.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.
| Biblioteka i izdanje | Verzija | Zavisnosti |
|---|---|---|
| DHT sensor library | 1.4.7 | Adafruit Unified Sensor 1.1.15 |
| Adafruit Unified Sensor | 1.1.15 | Nema |
| Adafruit SSD1306 | 2.5.17 | Adafruit GFX Library 1.12.6 |
| Adafruit GFX Library | 1.12.6 | Adafruit BusIO 1.17.4 |
| Adafruit BusIO | 1.17.4 | Nema |
Wire je uključen u ESP32 core; ne instalira se zasebno kroz Library Manager.
Kompletan program
#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.
| Uslov | Očekivani rezultat |
|---|---|
| Prije prvog pokušaja | Čekanje; LED OFF; nema istorije |
| Prvi uspješan uzorak | Prikaz T/RH; minimum = maksimum = temperatura |
| Temperatura dostigne 28 °C | LED ON |
| Temperatura dostigne 27 °C | LED OFF |
| Neuspješno očitavanje | Greška; LED OFF; postojeća istorija ostaje |
Najčešći problemi
| Problem | Provjera i korekcija |
|---|---|
| Nema OLED odgovora | Provjeri napajanje, SDA/SCL, adresu i tip kontrolera. |
Stalno SENSOR ERROR | Provjeri DHT11 tip, GPIO26, masu, napajanje i R2. |
| LED ne prati očekivanja | Provjeri polaritet, GPIO25, R1 i oba praga. |
| Minimum ostaje star | To je istorija od resetovanja, ne trenutno očitavanje. |
| Ekran se osvježava bez nove temperature | Mjerenje 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.
