#include <DHTesp.h> // ESP32 icin DHT kutuphanesi (Wokwi: DHT sensor library for ESPx)
#include <LiquidCrystal_I2C.h> // I2C LCD ekran kutuphanesi
#include <Wire.h> // I2C iletisim protokolu kutuphanesi
#include <string.h> // strlen() icin (LCD ortalama)
#if ARDUINO_ARCH_ESP32
#include <esp_arduino_version.h> // ESP32 Arduino surum secimi (LEDC API v2/v3 ayrimi)
#endif
// =============================================
// PIN TANIMLARI
// =============================================
#define DHTPIN 15 // DHT22 data pini (GPIO 15) - sicaklik ve nem verisi bu pinden okunur
#define DHTTYPE DHTesp::DHT22 // Sensor tipi: DHT22 (AM2302) - DHTesp enum sabiti
#define LDRPIN 34 // LDR analog giris pini (GPIO 34) - ADC1 uzerinden analog deger okunur
#define BTNPIN 18 // Push-button dijital giris pini (GPIO 18) - dahili pull-up ile kullanilir
#define REDPIN 25 // RGB LED kirmizi kanal (GPIO 25) - PWM ile surulen R bacagi
#define GREENPIN 26 // RGB LED yesil kanal (GPIO 26) - PWM ile surulen G bacagi
#define BLUEPIN 27 // RGB LED mavi kanal (GPIO 27) - PWM ile surulen B bacagi
// =============================================
// LDR -> LUX HESAPLAMA SABITLERI
// =============================================
// Bu sabitler fotodirencin (LDR) datasheet verilerine dayanan
// logaritmik lux hesaplama formulunde kullanilir.
const float GAMMA = 0.7; // LDR egrisi gamma degeri (sensor karakteristigi)
const float RL10 = 50.0; // 10 lux isik altindaki LDR direnci (kilo-ohm)
// DHT22 minimum ornekleme suresi: iki okuma arasinda en az ~2 sn beklemek gerekir.
// Daha sik okuma yapilirsa sensor NaN veya 0.00 dondurur.
#define DHT_OKUMA_MS 2500UL // 2500 milisaniye = 2.5 saniye
// =============================================
// NESNE TANIMLARI
// =============================================
DHTesp dht; // DHT22 sensor nesnesi (DHTesp kutuphanesi)
LiquidCrystal_I2C lcd(0x27, 16, 2); // I2C LCD: adres 0x27, 16 sutun x 2 satir
// =============================================
// GLOBAL DEGISKENLER
// =============================================
// --- Sensor olcum degerleri ---
float sicaklik = 0.0; // Son okunan sicaklik degeri (Celsius)
float nem = 0.0; // Son okunan nem degeri (yuzde %)
float lux = 0.0; // Hesaplanan isik yogunlugu (lux)
// --- LCD ekran yonetimi ---
int lcdSuresi = 2; // LCD her ekran icin gosterim suresi (saniye): varsayin 2
int ekranIndex = 0; // Round Robin ekran indeksi: 0=NEM, 1=SICAKLIK, 2=ISIK
// --- Zamanlama degiskenleri (millis() tabanli, non-blocking) ---
unsigned long sonLCDZaman = 0; // Son LCD ekran degisimi zamani (milisaniye)
unsigned long sonSeriZaman = 0; // Son seri port cikis zamani (milisaniye)
unsigned long sonDHTZaman = 0; // Son basarili DHT okuma zamani (milisaniye)
// --- Buton durumu ---
bool oncekiButonDurum = HIGH; // Butonun onceki mantik seviyesi (pull-up: bosken HIGH)
unsigned long sonButonBasma = 0; // Son gecerli buton basma zamani (debounce icin
// =============================================
// LEDC PWM KANALLARI (ESP32'ye ozel)
// =============================================
// ESP32'de analogWrite() desteklenmez; LEDC (LED Control) donanimsal
// PWM modulu kullanilir. Her RGB kanal icin ayri LEDC kanali gerekir.
#if ARDUINO_ARCH_ESP32
#define LEDC_KANAL_R 0 // LEDC kanal 0 -> Kirmizi LED
#define LEDC_KANAL_G 1 // LEDC kanal 1 -> Yesil LED
#define LEDC_KANAL_B 2 // LEDC kanal 2 -> Mavi LED
#define LEDC_PWM_FREQ 5000 // PWM frekans: 5000 Hz (LED icin yeterli)
#define LEDC_PWM_COZ 8 // PWM cozunurluk: 8 bit (0-255 aralik)
#endif
// ============================================
// setup() - Sistem Baslatma
// ============================================
// Bu fonksiyon ESP32 acildiginda bir kez calisir.
// Tum sensor, ekran, buton ve LED baslangic ayarlari burada yapilir.
void setup() {
// --- Seri haberlesme baslatma ---
Serial.begin(115200); // Seri port 115200 baud hizla acilir (monitor ile iletisim)
delay(200); // Serial monitorun hazir olmasi icin kisa bekleme
Serial.println(""); // Bos satir (onceki ciktilari ayirmak icin)
Serial.println("=== SISTEM BASLADI ==="); // Acilis testi mesaji
// --- I2C ve LCD baslatma ---
Wire.begin(21, 22); // I2C hatti baslatilir: SDA=GPIO21, SCL=GPIO22
delay(250); // I2C hattinin kararli hale gelmesi icin bekleme
lcd.init(); // LCD modulu baslatilir (I2C uzerinden)
delay(50); // Init sonrasi kisa stabilizasyon beklemesi
lcd.backlight(); // LCD arka isigi acilir
lcd.display(); // Goruntu modu aktif edilir
lcd.clear(); // Ekrandaki tum karakterler temizlenir
// --- DHT22 sensor baslatma ---
dht.setup(DHTPIN, DHTTYPE); // DHT22 sensoru GPIO 15 uzerinde baslatilir
sonDHTZaman = millis() - DHT_OKUMA_MS; // Ilk okumayi hemen yapabilmek icin zamani geri al
// --- Buton pini ayari ---
pinMode(BTNPIN, INPUT_PULLUP); // GPIO 18: dahili pull-up direnciyle giris modu
// Pull-up: buton basilmamissa pin HIGH, basilinca LOW olur
// --- RGB LED PWM baslatma ---
#if ARDUINO_ARCH_ESP32
// ESP32 icin LEDC (LED Controller) PWM API'si kullanilir
// Arduino Core v3+ ve v2 arasinda API farki vardir
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
// === Core v3+ API: ledcAttach(pin, frekans, cozunurluk) ===
ledcAttach(REDPIN, LEDC_PWM_FREQ, LEDC_PWM_COZ); // GPIO25: 5kHz, 8-bit PWM
ledcAttach(GREENPIN, LEDC_PWM_FREQ, LEDC_PWM_COZ); // GPIO26: 5kHz, 8-bit PWM
ledcAttach(BLUEPIN, LEDC_PWM_FREQ, LEDC_PWM_COZ); // GPIO27: 5kHz, 8-bit PWM
#else
// === Core v2 API: ledcSetup(kanal, frekans, cozunurluk) + ledcAttachPin(pin, kanal) ===
ledcSetup(LEDC_KANAL_R, LEDC_PWM_FREQ, LEDC_PWM_COZ); // Kanal 0 ayarla
ledcSetup(LEDC_KANAL_G, LEDC_PWM_FREQ, LEDC_PWM_COZ); // Kanal 1 ayarla
ledcSetup(LEDC_KANAL_B, LEDC_PWM_FREQ, LEDC_PWM_COZ); // Kanal 2 ayarla
ledcAttachPin(REDPIN, LEDC_KANAL_R); // GPIO25 -> Kanal 0 (kirmizi)
ledcAttachPin(GREENPIN, LEDC_KANAL_G); // GPIO26 -> Kanal 1 (yesil)
ledcAttachPin(BLUEPIN, LEDC_KANAL_B); // GPIO27 -> Kanal 2 (mavi)
#endif
#else
// Arduino UNO vb. kartlar icin standart pin modu
pinMode(REDPIN, OUTPUT);
pinMode(GREENPIN, OUTPUT);
pinMode(BLUEPIN, OUTPUT);
#endif
// --- Baslangic ekran mesaji ---
lcd.setCursor(0, 0); // Imleci 1. satira konumlandir
lcd.print(" Sistem Hazir "); // Ust satir: hosgeldiniz mesaji
lcd.setCursor(0, 1); // Imleci 2. satira konumlandir
lcd.print(" Baslatiliyor "); // Alt satir: yukleniyor mesaji
delay(2000); // 2 saniye boyunca gosterilir
lcd.clear(); // Ekran temizlenir, ana dongüye gecilir
}
// ============================================
// loop() - Ana Program Dongusu
// ============================================
// Bu fonksiyon surekli tekrar eder. Icerisinde:
// 1. Sensor okuma (DHT22 + LDR)
// 2. Buton kontrolu (LCD sure degisimi)
// 3. LCD ekran guncelleme (Round Robin)
// 4. Seri monitor cikisi (her 3 sn)
// 5. RGB LED renk guncelleme
// islemleri non-blocking (bloklama yapmayan) millis() zamanlama ile yapilir.
void loop() {
sensorOku(); // 1) DHT22 ve LDR sensorlerini oku
butonKontrol(); // 2) Butona basildi mi kontrol et
// 3) LCD ekran guncelleme: belirlenen sure (2 veya 4 sn) dolunca ekran degisir
if (millis() - sonLCDZaman >= (unsigned long)lcdSuresi * 1000UL) {
lcdGuncelle(); // Aktif ekrani LCD'ye ciz
sonLCDZaman = millis(); // Zamani sifirla
ekranIndex = (ekranIndex + 1) % 3; // Sonraki ekran (0->1->2->0 dongusel)
}
// 4) Seri cikis: her 3 saniyede bir sensor verilerini terminale yazdir
if (millis() - sonSeriZaman >= 3000UL) {
seriYazdir(); // Odev formatinda seri cikis
sonSeriZaman = millis(); // Zamani sifirla
}
// 5) RGB LED renk guncelleme (her dongude calisir - anlik tepki)
rgbGuncelle();
delay(100); // Dongu kararliligi icin 100ms bekleme (CPU yukunü azaltir)
}
// ============================================
// sensorOku() - Sensor Okuma Fonksiyonu
// ============================================
// DHT22'den sicaklik ve nem, LDR'den isik yogunlugu okunur.
// DHT22 icin minimum 2.5 saniye aralik birakilir (sensor gereksinimidir).
// LDR her dongude okunur (analog okuma hizlidir).
void sensorOku() {
// === DHT22 Okuma (zamanlamali) ===
// Sadece yeterli sure gectiyse oku; aksi halde sensor NaN/0.00 dondurur
if (millis() - sonDHTZaman >= DHT_OKUMA_MS) {
TempAndHumidity th; // DHT okuma sonuc paketi (sicaklik + nem)
bool dhtOk = false; // Okuma basarili mi?
// DHT okumasi bazen ilk denemede timeout verebildigi icin 3 kez dene
for (int i = 0; i < 3; i++) {
th = dht.getTempAndHumidity(); // DHT22'den sicaklik ve nemi oku
// Okumanin gecerli olup olmadigini kontrol et:
if (dht.getStatus() == DHTesp::ERROR_NONE && // Hata kodu yok mu?
!isnan(th.humidity) && // Nem degeri gecerli mi?
!isnan(th.temperature)) { // Sicaklik degeri gecerli mi?
dhtOk = true; // Basarili okuma yapildi
break; // Donguyu birak, tekrar denemeye gerek yok
}
delay(20); // Kisa ara verip tekrar dene (20ms)
}
// Basarisiz okumada hata mesaji yazdir (3 sn'de en fazla bir kez)
static unsigned long sonDHTHataYaz = 0;
if (!dhtOk) {
if (millis() - sonDHTHataYaz >= 3000UL) {
Serial.print("DHT HATA: "); // Hata basligı
Serial.println(dht.getStatusString()); // Hata detayi (TIMEOUT vb.)
sonDHTHataYaz = millis();
}
// Hata durumunda eski degerler korunur, LCD akisi bozulmaz
sonDHTZaman = millis();
return; // LDR okumasini da atla (bir sonraki dongude yapilir)
}
// Basarili okuma: global degiskenleri guncelle
nem = th.humidity; // Nem degerini guncelle (%)
sicaklik = th.temperature; // Sicaklik degerini guncelle (Celsius)
sonDHTZaman = millis(); // Son basarili okuma zamanini kaydet
}
// === LDR Okuma (her dongude) ===
// ESP32 ADC 12-bit: 0 (0V) ile 4095 (3.3V) arasi deger uretir
int analogDeger = analogRead(LDRPIN); // Ham ADC degeri oku
if (analogDeger < 1) analogDeger = 1; // Sifira bolmeyi onle (minimum 1)
// ADC degerini voltaja donustur (3.3V referans gerilimi)
float voltaj = analogDeger / 4095.0f * 3.3f;
// Voltajdan LDR direncini hesapla (gerilim bolucu formulu)
// Devre: VCC --- LDR --- AO --- 2kOhm sabit direnc --- GND
float direnc = 2000.0f * voltaj / (1.0f - voltaj / 3.3f);
// Direncten lux degerini hesapla (logaritmik model)
if (direnc <= 0.0f) {
lux = 0.0f; // Gecersiz direnc -> lux sifir
} else {
// Lux = (RL10 * 10^GAMMA / Direnc) ^ (1/GAMMA)
lux = pow(RL10 * 1e3f * pow(10.0f, GAMMA) / direnc, 1.0f / GAMMA);
}
// Sayisal tutarlilik kontrolleri
if (isnan(lux) || isinf(lux)) lux = 0.0f; // NaN veya sonsuz kontrolu
if (lux > 10000.0f) lux = 10000.0f; // Ust sinir (makul aralik)
}
// ============================================
// butonKontrol() - Buton ile LCD Sure Degisimi
// ============================================
// GPIO 18'e bagli push-button ile LCD gosterim suresi 2 <-> 4 sn arasinda
// toggle edilir. Dusen kenar algilamasi (HIGH->LOW) ve yazilimsal debounce
// (50ms) kullanilir. Her basimda seri monitore bilgi yazdirilir.
void butonKontrol() {
bool suankiDurum = digitalRead(BTNPIN); // Butonun anlik mantik seviyesi
// Dusen kenar algilamasi: onceki HIGH, simdiki LOW ise buton basilmistir
if (oncekiButonDurum == HIGH && suankiDurum == LOW) {
// Yazilimsal debounce: son 50ms icinde basilmissa tekrar sayma
if (millis() - sonButonBasma > 50UL) {
sonButonBasma = millis(); // Son gecerli basma zamanini guncelle
// LCD suresini toggle et: 2 ise 4, 4 ise 2 yap
lcdSuresi = (lcdSuresi == 2) ? 4 : 2;
// Seri monitore bilgi yazdir
Serial.print("LCD suresi degistirildi: ");
Serial.print(lcdSuresi);
Serial.println(" sn");
// LCD ekranin sag ust kosesinde aktif sureyi goster
lcd.setCursor(15, 0); // 16. sutun, 1. satir
lcd.print(lcdSuresi); // 2 veya 4 rakamini yaz
// Ekran gecis zamanlayicisini sifirla
sonLCDZaman = millis();
}
}
oncekiButonDurum = suankiDurum; // Bir sonraki karsilastirma icin durumu sakla
}
// ============================================
// lcdBaslikYaz() - LCD Ust Satira Ortali Baslik Yazma
// ============================================
// LCD'nin 1. satirina (0. indeks) baslik metnini ortali yazar.
// En sag hucreye (16. sutun) aktif LCD suresini (2 veya 4) yazar.
// Ornek cikti: " NEM 2" (15 karakter + 1 sure rakami)
void lcdBaslikYaz(const char *baslik) {
lcd.setCursor(0, 0); // Imleci 1. satir basina al
int uzunluk = (int)strlen(baslik); // Baslik metninin karakter uzunlugu
int solBosluk = (15 - uzunluk) / 2; // Sol taraftaki bosluk sayisi (ortalama)
// Sol bosluk karakterlerini yaz
for (int i = 0; i < solBosluk; i++) lcd.print(" ");
lcd.print(baslik); // Baslik metnini yaz
// Sag bosluk karakterlerini yaz (15. sutuna kadar doldur)
int sagBosluk = 15 - solBosluk - uzunluk;
for (int i = 0; i < sagBosluk; i++) lcd.print(" ");
lcd.print(lcdSuresi); // 16. sutuna aktif sure rakami yaz (2 veya 4)
}
// ============================================
// lcdGuncelle() - LCD Ekran Guncelleme (Round Robin)
// ============================================
// Round Robin mantigi ile 3 farkli ekran sirayla gosterilir:
// ekranIndex = 0 -> NEM ekrani (nem degeri + LOW/HIGH durumu)
// ekranIndex = 1 -> SICAKLIK ekrani (sicaklik degeri + VERY LOW/NORMAL/HIGH)
// ekranIndex = 2 -> ISIK ekrani (KARANLIK veya AYDINLIK yazisi)
//
// Her ekranin ust satiri: ortali baslik + sag kosede sure rakami
// Her ekranin alt satiri: olcum degeri ve durum metni
void lcdGuncelle() {
lcd.clear(); // Ekrani temizle (onceki karakterleri sil)
if (ekranIndex == 0) {
// === NEM EKRANI ===
lcdBaslikYaz("NEM"); // Ust sati
lcd.setCursor(0, 1); // Alt satira gec
String nemDurum = (nem <= 60.0f) ? "LOW" : "HIGH";
lcd.print(" "); // 2 karakter bosluk (hizalama icin)
lcd.print(nem, 2); // Nem degeri, 2 ondalik basamak (ornek: 49.00)
lcd.print("-"); // Deger ile durum arasinda tire ayirici
lcd.print(nemDurum); // LOW veya HIGH
} else if (ekranIndex == 1) {
// === SICAKLIK EKRANI ===
lcdBaslikYaz("SICAKLIK"); // Ust sati
lcd.setCursor(0, 1); // Alt satira gec
// Sicaklik esikleri (odev tablosu 6):
// < 0°C -> VERY LOW
// 0 - 30°C -> NORMAL
// > 30°C -> HIGH
String sicDurum;
if (sicaklik < 0.0f) sicDurum = "VERY LOW"; // Donma altı
else if (sicaklik <= 30.0f) sicDurum = "NORMAL"; // Normal aralik
else sicDurum = "HIGH"; // Yuksek sicaklik
if (sicaklik >= 0.0f) lcd.print(" "); // Pozitif degerlerde bosluk (hizalama)
lcd.print(sicaklik, 2); // Sicaklik degeri, 2 ondalik (ornek: 24.00)
lcd.print("-"); // Tire ayirici
lcd.print(sicDurum); // VERY LOW, NORMAL veya HIGH
} else {
// === ISIK EKRANI ===
lcdBaslikYaz("ISIK"); // Ust sati
lcd.setCursor(0, 1); // Alt satira gec
// Isik esikleri (odev tablosu):
// < 10 lux -> KARANLIK
// >= 10 lux -> AYDINLIK
String isikDurum = (lux < 10.0f) ? "KARANLIK" : "AYDINLIK";
// Metni ekranda ortala
int bosluk = (16 - (int)isikDurum.length()) / 2;
for (int i = 0; i < bosluk; i++) lcd.print(" ");
lcd.print(isikDurum); // KARANLIK veya AYDINLIK
}
}
// ============================================
// seriYazdir() - Seri Monitor Cikisi
// ============================================
void seriYazdir() {
Serial.print("Nem: "); // 1. alan: Nem etiketi
Serial.print(nem, 2); // Nem degeri (2 ondalik basamak)
Serial.print("% || "); // Yuzde isareti ve ayirici
Serial.print("Sicaklik: "); // 2. alan: Sicaklik etiketi
Serial.print(sicaklik, 2); // Sicaklik degeri (2 ondalik basamak)
Serial.print(" degC || "); // Derece Celsius birimi ve ayirici
Serial.print("Isik: "); // 3. alan: Isik etiketi
Serial.print((int)lux); // Isik degeri (tam sayi lux)
Serial.println(" lux");
}
// ============================================
// rgbGuncelle() - RGB LED Renk Guncelleme
// ============================================
// Ortam kosullarina gore RGB LED'in rengini belirler.
void rgbGuncelle() {
int r = 0, g = 0, b = 0; // PWM degerleri (0-255 aralik)
if (lux < 10.0f) {
// KARANLIK: isik 10 lux altinda -> saf mavi
b = 255; // Mavi (R=0, G=0, B=255)
} else {
// AYDINLIK: isik 10 lux ve uzerinde -> sicakliga gore renk sec
if (sicaklik < 0.0f) { g = 150; b = 255; } // LOW -> acik mavi (0,150,255)
else if (sicaklik <= 30.0f) { g = 255; } // NORMAL -> yesil (0,255,0)
else if (sicaklik <= 40.0f) { r = 255; g = 165; } // HIGH -> turuncu (255,165,0)
else { r = 255; } // >40C -> kirmizi (255,0,0)
}
// PWM degerlerini RGB LED pinlerine yaz
#if ARDUINO_ARCH_ESP32
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
// Core v3+: ledcWrite(pin, deger) - dogrudan pine yaz
ledcWrite(REDPIN, r); // Kirmizi PWM degeri
ledcWrite(GREENPIN, g); // Yesil PWM degeri
ledcWrite(BLUEPIN, b); // Mavi PWM degeri
#else
// Core v2: ledcWrite(kanal, deger) - kanala yaz
ledcWrite(LEDC_KANAL_R, r); // Kanal 0: Kirmizi
ledcWrite(LEDC_KANAL_G, g); // Kanal 1: Yesil
ledcWrite(LEDC_KANAL_B, b); // Kanal 2: Mavi
#endif
#else
// Arduino UNO vb.: standart analogWrite
analogWrite(REDPIN, r);
analogWrite(GREENPIN, g);
analogWrite(BLUEPIN, b);
#endif
}