Arduino Akıllı Sera Projesi: Sensörler, Sulama, Fan ve Kod
Arduino akıllı sera projesi: DHT22, toprak nemi ve LDR ile ölçüm; röle ve MOSFET ile fan, ısıtıcı, pompa ve LED kontrolü, histerezis, LCD ve ESP32 kodu.
Akıllı sera projesi, bir Arduino ya da ESP32 ile seradaki sıcaklığı, havadaki nemi, toprağın nemini ve ışığı ölçüp fanı, ısıtıcıyı, su pompasını ve bitki yetiştirme LED'lerini kendiliğinden çalıştırdığın bir otomasyon sistemidir. Arduino ile yapılan akıllı sera; bilim fuarlarının, TÜBİTAK 4006 ve 2204 projelerinin ve teknoloji derslerinin en sevilen konularından biridir, çünkü sonucu gözle görülür: toprak kuruyunca pompa çalışır, içerisi ısınınca fan döner, akşam olunca LED'ler yanar. Üstelik bir saklama kutusundan yapılmış maket sera ile balkondaki gerçek bir mini sera arasında devre ve kod açısından neredeyse hiç fark yoktur.
Bu rehberde bitkilerin hangi değerleri istediğinden başlıyoruz; ardından maket serayı, malzeme listesini ve yaklaşık maliyeti, sensörlerin (DHT22, kapasitif toprak nem sensörü, LDR) ve eylemcilerin (röle ve MOSFET ile fan, ısıtıcı, pompa ve LED) bağlantısını, güç planını ve pin tablosunu adım adım kuruyoruz. Sonra rölelerin durmadan tıkırdamasını önleyen eşik ve histerezis mantığını, delay() kullanmayan tam Arduino kodunu ve 16x2 LCD ekranı anlatıyoruz. Son bölümlerde serayı ESP32 ile telefondan izlemeyi, Raspberry Pi Pico ile MicroPython sürümünü, okul ve TÜBİTAK sunumu için ipuçlarını ve sık yapılan hataları bulacaksın.





Akıllı sera nasıl çalışır?
Her akıllı sera dört adımlık aynı döngüyle çalışır: ölç, karar ver, uygula, göster. Sensörler ortamı ölçer: içerisi kaç derece, hava ne kadar nemli, toprak kurudu mu, ışık yeterli mi? Arduino bu değerleri senin belirlediğin kurallarla karşılaştırır: "sıcaklık 30 °C'yi geçtiyse fanı çalıştır", "toprak nemi %35'in altına düştüyse sula". Eylemciler kararı uygular; röle fanı, ısıtıcıyı ve pompayı açıp kapatır, MOSFET ise LED şeridin parlaklığını ayarlar. Son olarak LCD ekran ve telefon, seranın o anki durumunu sana gösterir. Gerçek ticari seralardaki iklim kontrol bilgisayarları da tam olarak bu döngüyle çalışır; yalnızca daha çok sensör ve daha büyük motorlar kullanırlar.
| Ne ölçülür? | Sensör | Eylemci | Kural (örnek) |
|---|---|---|---|
| Hava sıcaklığı | DHT22 | Fan ve ısıtıcı | 30 °C'de fan çalışır, 16 °C'de ısıtıcı açılır |
| Havadaki bağıl nem | DHT22 | Fan | %85'te fan çalışır, %75'te durur |
| Toprak nemi | Kapasitif toprak nem sensörü | Mini dalgıç pompa | %35'in altında 3 saniyelik darbelerle sular |
| Işık | LDR | LED şerit (MOSFET) | Hava kararınca LED'ler en fazla 4 saat yanar |
| Depodaki su | Şamandıralı seviye sensörü | Pompa kilidi | Depo boşsa pompa hiç çalışmaz |
Bitkiler ne ister? Hedef sıcaklık, nem ve ışık
Kuralları yazmadan önce neyi hedeflediğini bilmen gerekir. Bitkilerin çoğu gündüz ve gece farklı sıcaklık ister; gece biraz serinlemek bitkinin dinlenmesine yardım eder. Havadaki nem uzun süre %85'in üstünde kalırsa yaprak yüzeyinde su yoğuşur, küf ve mildiyö gibi mantar hastalıkları hızla yayılır; bu yüzden seralarda fan yalnızca serinletmek için değil, nemi dışarı atmak için de çalışır. Işık tarafında sebze fidelerinin çoğu günde 14–16 saat ışık ister, ama en az 6–8 saatlik bir karanlık dönem de gerekir; LED'leri bütün gece açık bırakmak bitkiye yarardan çok zarar verir. Aşağıdaki değerler başlangıç için iyi bir rehberdir; yetiştirdiğin türe göre ayarlayabilirsin.
| Bitki | Gündüz | Gece | Bağıl nem | Not |
|---|---|---|---|---|
| Domates | 21–27 °C | 15–18 °C | %60–75 | 32 °C'nin üstünde çiçekler dökülebilir |
| Biber | 21–29 °C | 16–20 °C | %60–75 | Soğuğa domatesten daha duyarlıdır |
| Marul ve ıspanak | 15–20 °C | 10–15 °C | %60–70 | Sıcak ve uzun günlerde erkenden tohuma kalkar |
| Çilek | 18–24 °C | 10–13 °C | %60–75 | Yüksek nemde kurşuni küf riski artar |
| Fide ve çimlenme | 20–25 °C | 18–20 °C | %70–85 | Toprak sıcaklığı hava sıcaklığından daha önemlidir |
Maket sera yapımı
En kolay ve en ucuz gövde, şeffaf ve kapaklı bir plastik saklama kutusudur: 40–60 litrelik bir kutu iki ya da üç saksı için yeterlidir, su geçirmez ve maket bıçağıyla ya da ısıtılmış bir uçla kolayca delinir. Daha gerçekçi bir görünüm istersen ahşap çıtalardan bir iskelet kurup 2–3 mm'lik şeffaf pleksi (akrilik) ya da PETG levhalarla kaplayabilirsin; çift eğimli çatısı olan bir sera sunumda çok daha iyi görünür. Boyut olarak yaklaşık 50×35×40 cm iyi bir başlangıçtır. Hangi gövdeyi seçersen seç, kapağın ya da bir yan duvarın açılabilir olması gerekir; bitkiyi sulamak, sensörü taşımak ve kabloları düzeltmek için içeriye elini sokacaksın.
- Elektronik dışarıda: Seranın içi nemli olur ve sabahları duvarlarda su yoğuşur. Arduino'yu, röle modülünü ve güç kaynağını seranın dışına, ayrı bir kutuya koy; içeride yalnızca sensörler, fan, ısıtıcı, pompa hortumu ve LED kalsın.
- Havalandırma: Sıcak hava yükseldiği için fanı kısa duvarlardan birinin üst kısmına, havayı dışarı atacak yönde yerleştir. Karşı duvarın alt kısmına fan çapında bir hava giriş deliği aç ve üzerine sinek teli yapıştır; böylece taze hava alttan girer, sıcak ve nemli hava üstten çıkar.
- Isıtıcı: Isıtıcıyı tabana yakın, bir alüminyum levhanın ya da seramik bir karonun üzerine, plastik duvarlardan ve bitkilerden en az 5 cm uzağa monte et. Isınan hava yükselirken bütün serayı dolaşır.
- LED aydınlatma: LED şeridi kapağın iç yüzüne, bitkilerin 15–30 cm üstüne yapıştır. Şeridi ince bir alüminyum profilin üzerine yapıştırırsan ısısı daha iyi dağılır ve yapışkanı zamanla gevşemez.
- Sensörler: DHT22'yi bitki yüksekliğinde, doğrudan güneş ya da LED ışığı almayacak bir yere koy. Toprak nem sensörünü kök bölgesine, saksı kenarından uzağa göm. LDR ise LED'leri görmemeli: onu seranın dışına, çatıya, gökyüzüne bakacak şekilde yerleştir.
- Su deposu: 1–2 litrelik bir kap, pompa ve şamandıra için yeterlidir. Saksıların altına drenaj tabağı koy; fazla su tabakta toplanır ve kutunun tabanı göle dönmez.
Malzeme listesi ve maliyet
| Parça | Adet | Görevi | Fiyat seviyesi |
|---|---|---|---|
| Arduino Uno R3 ya da ESP32 DevKit | 1 | Seranın beyni | Orta |
| DHT22 (AM2302) sıcaklık ve nem modülü | 1 | Hava sıcaklığı ve nemi | Düşük |
| Kapasitif toprak nem sensörü (v1.2) | 1 (her saksıya 1) | Toprak nemi | Düşük |
| LDR (GL5528) ve 10 kΩ direnç | 1 + 1 | Ortam ışığı | Çok düşük |
| Şamandıralı su seviye sensörü | 1 | Depo boşken pompayı kilitler | Düşük |
| 4 kanallı 5 V röle modülü (optokuplörlü, JD-VCC'li) | 1 | Fan, ısıtıcı ve pompa | Düşük |
| Lojik seviye N-kanal MOSFET (IRLZ44N), 220 Ω ve 100 kΩ direnç | 1 takım | LED şeridin parlaklığı (PWM) | Çok düşük |
| 3–6 V mini dalgıç pompa ve 1 m silikon hortum | 1 | Sulama | Düşük |
| 12 V 80 mm bilgisayar fanı | 1–2 | Havalandırma ve nem atma | Düşük |
| 12 V PTC ısıtıcı (10–30 W) ve termal sigorta | 1 + 1 | Isıtma | Düşük |
| 12 V LED şerit (bitki yetiştirme ya da soğuk beyaz), yaklaşık 1 m | 1 | Ek aydınlatma | Düşük |
| I2C 16x2 LCD ekran | 1 | Durum ekranı | Düşük – orta |
| 12 V 5 A adaptör ve LM2596 voltaj düşürücü | 1 + 1 | Güç | Orta |
| 1N5819 ya da 1N4007 diyot, 470 µF kondansatör, klemens, jumper kablolar | – | Koruma ve bağlantılar | Çok düşük |
| Kapaklı şeffaf saklama kutusu ya da pleksi levha, saksılar, drenaj tabağı | – | Sera gövdesi | Orta |
Fiyatlar satıcıya ve kura göre sık değiştiği için kesin rakam yerine kabaca bir bütçe verelim. Kart hariç temel sürüm (DHT22, toprak nem sensörü, LDR, 2 kanallı röle, pompa ve 5 V fan) genellikle 400–700 TL arasında tutar. Isıtıcı, LED şerit, LCD, 12 V adaptör, LM2596, şamandıra ve gövdeyle tam sürüm ise çoğunlukla 1.200–2.000 TL civarındadır; bütçenin büyük kısmını kart, adaptör, ekran ve gövde oluşturur. Elinde bir Arduino başlangıç seti varsa kart, breadboard, kablolar, LDR, dirençler ve çoğu zaman LCD zaten hazırdır. Bütçe darsa ısıtıcıyı ve LED'i ikinci aşamaya bırak; sulama ve havalandırma tek başına da çalışan, anlamlı bir projedir.
Sensörler: DHT22, toprak nem sensörü ve LDR
Serada DHT22 (AM2302) kullanmanı öneriyoruz. DHT11'den biraz pahalıdır ama sıcaklığı −40 ile 80 °C arasında ±0,5 °C, nemi %0–100 aralığında yaklaşık ±%2 doğrulukla ölçer; DHT11 ise yalnızca 0–50 °C aralığında ±2 °C doğrudur ve %90'ın üstündeki nemi göremez. Sera sabahları %90'ı aşan neme ve kışın sıfırın altındaki sıcaklıklara sahne olabildiği için DHT22 bu işe çok daha uygundur. Sensör en fazla 2 saniyede bir ölçüm yapar, bu yüzden kod onu 2,5 saniyede bir okur. Sensörü güneşin ya da LED'in doğrudan vurduğu bir yere koyarsan gövdesi ısınır ve havadan birkaç derece yüksek okur; delikli beyaz bir kapakla gölgelemek bunu önler. Bağlantı ve kütüphane ayrıntıları DHT11 ve DHT22 sıcaklık ve nem sensörü yazısında.

Toprak nemi için kapasitif sensör seç. Ucuz rezistif sensörlerin (YL-69, FC-28) iki metal ucu toprağa doğrudan değer ve sürekli beslenirse elektroliz yüzünden birkaç haftada aşınır; serada aylarca çalışacak bir sistem için uygun değildir. Kapasitif sensörün elektrotu kaplamalıdır, metal toprağa hiç değmez. Çıkışı analogdur ve toprak ıslandıkça düşer: Uno'da havada tutulan bir sensör yaklaşık 500–600, suya batırılmış hâli 250–300 civarında okur. Bu sayılar her sensörde farklıdır; bu yüzden ana kodu yüklemeden önce kendi sensörünün havadaki ve sudaki değerini Seri Monitör'den okuyup koddaki SOIL_DRY_RAW ve SOIL_WET_RAW sabitlerine yaz. Kod bu iki değeri map() ile %0–100'e çevirir. Ayrıntılar Toprak nem sensörü yazısında.
Işık için kitlerdeki LDR (GL5528) yeterlidir. LDR'yi 10 kΩ'luk bir dirençle gerilim bölücü olarak bağla: 5V → LDR → A1 → 10 kΩ → GND. Işık arttıkça LDR'nin direnci düşer ve A1'deki değer yükselir; kod bu değeri de karanlıkta ve güneş altında ölçtüğün iki sayıya göre %0–100'e çevirir. LDR lüks ölçen hassas bir cihaz değildir, ama "hava karardı mı, bulutlu mu, güneşli mi?" sorusuna güvenilir bir cevap verir. Daha doğru ışık ölçümü istersen BH1750 gibi I2C bir ışık sensörüne geçebilirsin. Bölücü hesabı için Gerilim bölücü aracını, ayrıntılar için LDR ışık sensörü yazısını kullanabilirsin.
Eylemciler: röle mi, MOSFET mi?
Arduino'nun bir pini en fazla yaklaşık 20 mA verebilir; fan, ısıtıcı, pompa ve LED şerit ise yüzlerce miliamper, hatta birkaç amper çeker. Bu yüzden her yük ya bir röle ya da bir MOSFET üzerinden sürülür. Röle mekanik bir anahtardır: yükü tamamen ayrı bir devrede, AC ya da DC fark etmeden açıp kapatır; ama yalnızca aç-kapa yapabilir, her geçişte tık sesi çıkarır ve kontaklarının ömrü sınırlıdır. MOSFET ise sessiz bir elektronik anahtardır; yalnızca DC yüklerde çalışır ama saniyede yüzlerce kez açılıp kapanabildiği için PWM ile parlaklık ve hız ayarı yapabilir. Bu projede ısıtıcı, fan ve pompa için 4 kanallı bir röle modülü, LED şeridin parlaklığı için bir MOSFET kullanıyoruz.
| Yük | Tipik değer | Sürücü | Neden? |
|---|---|---|---|
| 12 V PTC ısıtıcı | 10–30 W, yaklaşık 1–2,5 A | Röle (IN2) | Yüksek akım, yalnızca aç-kapa gerekir |
| 12 V 80 mm fan | 0,1–0,2 A | Röle (IN1) ya da MOSFET | Röle basittir; MOSFET ile PWM hız ayarı da yapılabilir |
| 3–6 V mini dalgıç pompa | 0,1–0,25 A, kalkışta daha fazla | Röle (IN3) ya da MOSFET ve diyot | Kısa darbelerle çalışır; motor paraziti yalıtılmalıdır |
| 12 V LED şerit (5050, 60 LED/m) | Metre başına yaklaşık 1,2 A | Lojik seviye MOSFET | PWM ile parlaklık ayarı ve sessiz çalışma |

Mini dalgıç pompa, hortum yüksekliğine göre dakikada yaklaşık 1–1,5 litre su basar; yani 3 saniyelik bir darbe saksıya 50–75 mL su verir. Bu yüzden pompayı "toprak ıslanana kadar" sürekli çalıştırmak yerine kısa darbelerle çalıştırıyoruz: 3 saniye sula, suyun toprağa yayılması için 60 saniye bekle, sonra yeniden ölç. Sensör suyun ulaşmasını beklemeden yeniden ölçülseydi toprak hâlâ kuru görünür ve pompa saksıyı taşırana kadar çalışırdı. Pompa susuz çalışırsa birkaç dakikada ısınıp bozulur; depodaki şamandıra, su bittiğinde pompayı kilitler. Motor her durduğunda bobininden ters yönde bir gerilim sıçraması gelir; pompanın uçlarına ters yönde bir diyot (1N5819 ya da 1N4007, bantlı ucu + tarafına) bağlamak hem röle kontağını hem de Arduino'yu bu parazitten korur. Pompa seçimi ve sürme yöntemleri mini su pompası yazısında.
Isıtıcı olarak 12 V'luk bir PTC ısıtıcı kullan. PTC (pozitif sıcaklık katsayılı) seramik elemanların direnci ısındıkça artar, bu yüzden yüzey sıcaklıkları kendiliğinden belli bir değerde sınırlanır; açıkta kızaran bir rezistans telinden çok daha güvenlidir. Plastik bir gövdede yüzey sıcaklığı 80 °C civarında sınırlanan düşük sıcaklıklı bir model seç. 24 W'lık bir ısıtıcı, P = V × I formülüne göre 12 V'ta 2 A çeker; soğukken ilk saniyelerde bu akım biraz daha yüksektir. Aydınlatma için kırmızı-mavi bitki yetiştirme LED şeridi ya da soğuk beyaz bir LED şerit kullanabilirsin; bitkiler fotosentezde en çok kırmızı (yaklaşık 660 nm) ve mavi (yaklaşık 450 nm) ışığı kullanır. Şeridi IRLZ44N gibi lojik seviye bir MOSFET ile sür: kapı (gate) 220 Ω'luk dirençle D9'a, ayrıca 100 kΩ'luk dirençle GND'ye bağlanır; şeridin + ucu 12 V'a, − ucu MOSFET'in drain'ine, source ise GND'ye gider. IRF520 gibi standart MOSFET modülleri 5 V'luk kapı geriliminde tam açılmaz ve amperlik yüklerde ısınır; ayrıntılar Arduino ile MOSFET kullanımı yazısında. Akım ve gücü hesaplamak için Ohm Kanunu aracını kullanabilirsin.
Güç planı: 12 V ve 5 V hatları
Akıllı seralarda en sık yaşanan sorunlar ("pompa çalışınca Arduino resetleniyor", "LCD'de garip karakterler çıkıyor") güçten gelir. En temiz çözüm, tek bir 12 V 5 A adaptörden iki hat çıkarmaktır. 12 V hattı ısıtıcıyı, fanları ve LED şeridi doğrudan besler, ayrıca Arduino Uno'nun DC jakına gider. Aynı 12 V, 5 V'a ayarlanmış bir LM2596 voltaj düşürücüye de girer; röle bobinleri, pompa ve LCD bu 5 V hattından beslenir. Röle modülünde JD-VCC jumper'ını çıkarırsan bobin akımı tamamen LM2596'dan gelir, Arduino tarafı yalnızca optokuplörlerin LED'lerini yakar. Sensörleri (DHT22, toprak nem sensörü ve LDR bölücüsü) ise Arduino'nun kendi 5V pininden besle: analogRead() ölçümü kartın kendi 5 V'una göre yaptığı için analog sensörlerin aynı gerilimden beslenmesi okumaları kararlı tutar. Adaptörün, LM2596'nın ve Arduino'nun GND'lerini mutlaka birleştir.
| Yük | Hat | Yaklaşık akım |
|---|---|---|
| PTC ısıtıcı (24 W) | 12 V | 2 A (soğukken ilk anda daha fazla) |
| LED şerit (1 m) | 12 V | 1–1,2 A |
| İki adet 80 mm fan | 12 V | 0,2–0,4 A |
| Arduino Uno ve sensörler (DC jaktan) | 12 V | Yaklaşık 0,06–0,08 A |
| LM2596 girişi (5 V tarafı 1 A çekerken) | 12 V | Yaklaşık 0,5 A |
| Röle modülü (üç röle çekiliyken) | 5 V | 0,2–0,25 A |
| Mini dalgıç pompa | 5 V | 0,1–0,25 A (kalkışta daha fazla) |
| I2C LCD (arka ışık açık) | 5 V | 20–30 mA |
Her şey aynı anda çalışsa bile toplam akım 4–4,5 A civarındadır; 12 V 5 A (60 W) bir adaptör yeterli pay bırakır. Uygulamada ısıtıcı ile sıcaklık fanı hiçbir zaman aynı anda çalışmaz, çünkü eşikleri arasında geniş bir boşluk vardır. LM2596'yı devreye bağlamadan önce çıkışını multimetreyle ölçüp üzerindeki trimpotla 5,0 V'a ayarla; kutudan çıktığı ayarda girişteki 12 V'u neredeyse aynen geçirebilir. Arduino'yu kod yüklerken USB'den, sahada DC jakından besleyebilirsin; ikisinin aynı anda bağlı olması Uno için sorun değildir. Ayrıntılar Arduino güç besleme rehberinde.
Bağlantı ve pin tablosu
Aşağıdaki tablo, bu rehberdeki üç kodun kullandığı pinleri gösterir. Uno'da LED şeridi süren MOSFET'i PWM destekli D9'a bağladık; Uno'da PWM yalnızca D3, D5, D6, D9, D10 ve D11'de vardır. Şamandıra ve röleler için PWM gerekmez. LCD yalnızca I2C hattını (A4 ve A5) kullandığı için çok az pin harcar; Uno'da ikinci bir toprak sensörü ya da buton eklemek için A2, A3 ve birkaç dijital pin boş kalır. ESP32'de analog sensörleri Wi-Fi açıkken de çalışan ADC1 pinlerine (GPIO 34 ve 35) bağladık. Kartının pin yerleşimini Pinout aracında görebilirsin.
| Bağlantı | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| DHT22 DATA | D2 | GPIO 4 | GP15 |
| Şamandıra (diğer ucu GND) | D3 | GPIO 13 | GP14 |
| Röle IN3: pompa | D4 | GPIO 25 | GP18 |
| Röle IN1: fan | D7 | GPIO 26 | GP16 |
| Röle IN2: ısıtıcı | D8 | GPIO 27 | GP17 |
| MOSFET kapısı (220 Ω üzerinden): LED | D9 | GPIO 18 | GP19 |
| Toprak nem sensörü AOUT | A0 | GPIO 34 | GP26 |
| LDR bölücüsünün orta noktası | A1 | GPIO 35 (bölücü 3V3'ten) | GP27 (bölücü 3V3'ten) |
| I2C LCD SDA / SCL | A4 / A5 | GPIO 21 / 22 | GP4 / GP5 |
| Sensör beslemesi | 5V | 3V3 | 3V3 |
Eşik ve histerezis: röleler neden tıkırdar?
Fanı "sıcaklık 30 °C'nin üstündeyse çalıştır, değilse durdur" diye tek bir eşikle kontrol ettiğini düşün. Sıcaklık 29,9 ile 30,1 °C arasında gidip geldikçe (sensör gürültüsü yüzünden bu çok sık olur) röle her ölçümde açılıp kapanır. Hem röle kontakları hem fanın motoru yıpranır; ısıtıcıda ise kablolar ve adaptör her açılışta boşuna zorlanır. Çözüm histerezistir: açma ve kapama için iki ayrı eşik kullanırsın. Fan 30 °C'de çalışır ama ancak sıcaklık 27 °C'ye düşünce durur. Aradaki 3 derecelik ölü bölgede sistem son durumunu korur. Kombilerin oda termostatları ve buzdolapları da aynı mantıkla çalışır.
| Kontrol | Açılır | Kapanır | Ölü bölge |
|---|---|---|---|
| Fan (sıcaklık) | ≥ 30 °C | ≤ 27 °C | 3 °C |
| Fan (nem) | ≥ %85 | ≤ %75 | %10 |
| Isıtıcı | ≤ 16 °C | ≥ 19 °C | 3 °C |
| Sulama | Toprak ≤ %35 | Toprak ≥ %50 | %15 |
| LED aydınlatma | Işık ≤ %25 | Işık ≥ %35 ya da 4 saat dolunca | %10 |
Ölü bölgenin genişliği bir dengedir: çok dar olursa röle yine sık çalışır, çok geniş olursa sıcaklık hedeften fazla uzaklaşır. Sıcaklıkta 2–3 °C, nemde %8–10, toprak neminde %10–15 iyi bir başlangıçtır. Isıtıcının kapandığı 19 °C ile fanın çalıştığı 30 °C arasında da geniş bir boşluk bıraktık; böylece ısıtıcı ile sıcaklık fanı hiçbir zaman birbirine karşı çalışmaz. Kodda histerezis, her kontrol için tekrar tekrar yazılmasın diye küçük bir hysteresis() fonksiyonunda toplanır: açma eşiği kapama eşiğinden büyükse "yükselince aç" (fan), küçükse "düşünce aç" (ısıtıcı, sulama, LED) anlamına gelir. Sensör verisindeki sıçramaları ayrıca yumuşatmak istersen sensör verisi filtreleme yazısına göz at.
Kod yapısı: ölç, karar ver, uygula, göster
Akıllı seranın kodundaki en önemli kural, hiçbir işin diğerini beklememesidir. Pompadan sonra delay(60000) ile bir dakika bekleseydin o süre boyunca ne fan çalışır ne de ekran güncellenirdi. Bu yüzden kod, zamanı millis() ile ölçen ve her biri tek bir işten sorumlu küçük fonksiyonlardan oluşur; loop() bunları saniyede binlerce kez sırayla çağırır, her fonksiyon da yalnızca zamanı geldiyse bir şey yapar. Sulama, üç durumlu küçük bir durum makinesidir: bekle (IDLE), sula (PUMPING) ve suyun yayılmasını bekle (SOAKING). Bütün ayarlar kodun en üstünde sabit olarak durur; eşikleri değiştirmek için kodun içinde dolaşman gerekmez. Bu yaklaşımın mantığını millis() ve delay() ile Arduino'da durum makinesi yazılarında bulabilirsin. Kodda ayrıca şu güvenlik önlemleri var:
- Güvenli açılış: Röle pinlerine pinMode() çağrılmadan önce "kapalı" değeri yazılır; Arduino açılırken röleler bir anlığına bile çekmez.
- Sensör arızası: DHT22 üst üste üç kez okunamazsa kod güvenli duruma geçer: ısıtıcıyı kapatır, fanı çalıştırır ve LCD'de "Sensor hatasi" yazar. Sıcaklığı bilmeyen bir sistemin ısıtıcıyı açık bırakması kabul edilemez.
- Depo koruması: Şamandıra depo boş dediğinde pompa hiç başlamaz, çalışıyorsa hemen durur.
- Taşma koruması: Bir sulama döngüsünde altı darbeye rağmen toprak nemi %50'ye çıkmıyorsa sensör topraktan çıkmış ya da hortum kopmuş olabilir; kod sulamayı durdurur ve "Sulama hatasi" gösterir. Toprak yeniden ıslandığında uyarı kendiliğinden kalkar.
- Veri kaydı: Kod her 10 saniyede bir ölçümleri Seri Monitör'e virgülle ayrılmış (CSV) bir satır olarak yazar; bu satırları kopyalayıp Excel'de grafik çizebilirsin.
Adım adım kurulum ve test
- Gövdeyi hazırla: fan, hava girişi, hortum ve kablo deliklerini aç; elektronik kutusunu seranın dışına yerleştir.
- Güç hattını kur: 12 V adaptörü bir klemense bağla, LM2596'yı multimetreyle 5,0 V'a ayarla ve bütün GND'leri birleştir.
- I2C LCD'yi bağla ve ekrana bir yazı yazdır. Ekran bir şey göstermiyorsa I2C LCD yazısındaki tarayıcı kodunu ve kontrast trimpotunu dene.
- Sensörleri tek tek ekle ve Seri Monitör'de dene: DHT22'nin değerlerini, toprak sensörünün havadaki ve sudaki ham değerini, LDR'nin karanlıktaki ve güneş altındaki değerini not al.
- Röleleri önce yük bağlamadan, LED'lerini ve tık seslerini izleyerek dene. Sonra fanı, pompayı (depo dolu olsun) ve en son ısıtıcıyı bağla.
- LED şeridi MOSFET üzerinden bağla; analogWrite() değerini değiştirerek parlaklığın değiştiğini gör.
- Ana kodu yükle, notlarındaki kalibrasyon değerlerini sabitlere yaz ve sistemi birkaç saat gözetim altında çalıştır: yanlış sulama, sürekli tıklayan bir röle ya da aşırı ısınan bir parça var mı?
Arduino Uno ile tam kod
Kodu derlemek için Kütüphane Yöneticisi'nden Adafruit'in DHT sensor library kütüphanesini (bağımlılığı Adafruit Unified Sensor ile birlikte) ve Frank de Brabander'in LiquidCrystal I2C kütüphanesini kur; Wire, Arduino IDE ile birlikte gelir. LCD'nin adresi çoğunlukla 0x27, bazı modüllerde 0x3F'tir. Yüklemeden önce kendi ölçtüğün kalibrasyon değerlerini SOIL_DRY_RAW, SOIL_WET_RAW, LDR_DARK_RAW ve LDR_BRIGHT_RAW sabitlerine yaz. Şamandıran suyla kapanmak yerine açılıyorsa TANK_OK değerini HIGH yap; röle modülün aktif HIGH ise RELAY_ON ve RELAY_OFF değerlerini yer değiştir.
1#include <Wire.h>
2#include <LiquidCrystal_I2C.h>
3#include <DHT.h>
4
5// Pinler / Pins
6const byte DHT_PIN = 2, FLOAT_PIN = 3, PUMP_RELAY = 4, FAN_RELAY = 7, HEATER_RELAY = 8;
7const byte LED_PIN = 9; // MOSFET gate (PWM)
8const byte SOIL_PIN = A0, LDR_PIN = A1;
9
10// Kalibrasyon: kendi ölçümlerini yaz / Calibration: write your own readings
11const int SOIL_DRY_RAW = 520, SOIL_WET_RAW = 260; // havada ve suda / in air and in water
12const int LDR_DARK_RAW = 60, LDR_BRIGHT_RAW = 900; // karanlık ve güneş / dark and sunlight
13
14// Eşikler (açma, kapama) / Thresholds (on, off)
15const float FAN_ON_C = 30, FAN_OFF_C = 27; // sıcaklıkta fan / fan on heat
16const float HUM_ON = 85, HUM_OFF = 75; // nemde fan / fan on humidity
17const float HEAT_ON_C = 16, HEAT_OFF_C = 19; // ısıtıcı / heater
18const float SOIL_DRY = 35, SOIL_OK = 50; // sulama başla, dur / start, stop watering
19const float DARK_ON = 25, DARK_OFF = 35; // ışık % / light %
20
21// Zamanlar / Timing
22const unsigned long READ_MS = 2500; // DHT22: en az 2 sn / at least 2 s
23const unsigned long PUMP_MS = 3000; // tek sulama darbesi / one watering pulse
24const unsigned long SOAK_MS = 60000; // suyun yayılması / let the water soak in
25const byte MAX_PULSES = 6; // fazlası arıza / more means a fault
26const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL; // günü 4 saat uzat / extend the day by 4 h
27const byte LED_LEVEL = 200; // LED parlaklığı 0-255 / LED brightness 0-255
28const byte RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
29const byte TANK_OK = LOW; // şamandıra suyla kapanır / float closes with water
30
31DHT dht(DHT_PIN, DHT22);
32LiquidCrystal_I2C lcd(0x27, 16, 2);
33
34float tempC = NAN, hum = NAN, soil = 0, light = 0;
35byte dhtErrors = 0, pulses = 0, page = 0;
36bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
37bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
38unsigned long lastRead = 0, lastLcd = 0, lastLog = 0, pumpAt = 0, darkSince = 0;
39
40enum PumpState { IDLE, PUMPING, SOAKING };
41PumpState pumpState = IDLE;
42
43// onAt > offAt: yükselince aç (fan) / turn on when rising (fan)
44// onAt < offAt: düşünce aç (ısıtıcı) / turn on when falling (heater)
45bool hysteresis(bool state, float value, float onAt, float offAt) {
46 if (onAt > offAt) {
47 if (value >= onAt) return true;
48 if (value <= offAt) return false;
49 } else {
50 if (value <= onAt) return true;
51 if (value >= offAt) return false;
52 }
53 return state; // ölü bölgede durumu koru / keep state in the dead band
54}
55
56void setRelay(byte pin, bool on) {
57 digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
58}
59
60float readPercent(byte pin, int raw0, int raw100) {
61 long sum = 0;
62 for (byte i = 0; i < 10; i++) sum += analogRead(pin); // 10 ölçüm ortalaması / average of 10
63 long pct = map(sum / 10, raw0, raw100, 0, 100);
64 return constrain(pct, 0, 100);
65}
66
67void setup() {
68 Serial.begin(9600);
69 setRelay(PUMP_RELAY, false); pinMode(PUMP_RELAY, OUTPUT); // açılışta kapalı / off at boot
70 setRelay(FAN_RELAY, false); pinMode(FAN_RELAY, OUTPUT);
71 setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
72 pinMode(LED_PIN, OUTPUT);
73 pinMode(FLOAT_PIN, INPUT_PULLUP);
74 dht.begin();
75 lcd.init();
76 lcd.backlight();
77 lcd.print(F("BlueGrays Sera"));
78 Serial.println(F("sn,sicaklik,nem,toprak,isik,fan,isitici,pompa,led")); // CSV başlığı / CSV header
79 delay(2000); // DHT22 açılış süresi / DHT22 start-up time
80 readSensors();
81 lastRead = millis();
82}
83
84void loop() {
85 unsigned long now = millis();
86 if (now - lastRead >= READ_MS) {
87 lastRead = now;
88 readSensors();
89 }
90 controlClimate();
91 controlLight(now);
92 controlPump(now);
93 updateLcd(now);
94 logCsv(now);
95}
96
97// 1) Ölç / Measure
98void readSensors() {
99 float t = dht.readTemperature();
100 float h = dht.readHumidity();
101 if (isnan(t) || isnan(h)) {
102 if (dhtErrors < 255) dhtErrors++; // okuma hatası / read error
103 } else {
104 tempC = t;
105 hum = h;
106 dhtErrors = 0;
107 }
108 soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
109 light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
110 tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
111}
112
113// 2) Fan ve ısıtıcı / Fan and heater
114void controlClimate() {
115 if (dhtErrors >= 3) { // sensör yok: güvenli durum / no sensor: safe state
116 heaterOn = false;
117 fanByTemp = true;
118 } else {
119 fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
120 fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
121 heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
122 }
123 fanOn = fanByTemp || fanByHum;
124 setRelay(FAN_RELAY, fanOn);
125 setRelay(HEATER_RELAY, heaterOn);
126}
127
128// 3) LED aydınlatma / LED grow light
129void controlLight(unsigned long now) {
130 bool wasDark = isDark;
131 isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
132 if (isDark && !wasDark) darkSince = now; // akşam oldu / dusk detected
133 ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS);
134 analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
135}
136
137// 4) Sulama: darbe + bekleme / Watering: pulse + soak
138void controlPump(unsigned long now) {
139 needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
140 if (!needWater) {
141 pulses = 0;
142 pumpFault = false; // toprak ıslandı / soil is wet again
143 }
144 switch (pumpState) {
145 case IDLE:
146 if (needWater && tankOk && !pumpFault) {
147 if (pulses >= MAX_PULSES) {
148 pumpFault = true; // nem yükselmiyor / moisture is not rising
149 } else {
150 pumpState = PUMPING;
151 pumpAt = now;
152 pulses++;
153 }
154 }
155 break;
156 case PUMPING:
157 if (now - pumpAt >= PUMP_MS || !tankOk) {
158 pumpState = SOAKING;
159 pumpAt = now;
160 }
161 break;
162 case SOAKING:
163 if (now - pumpAt >= SOAK_MS) pumpState = IDLE;
164 break;
165 }
166 setRelay(PUMP_RELAY, pumpState == PUMPING);
167}
168
169// 5) LCD: üç sayfa / three pages
170void updateLcd(unsigned long now) {
171 if (now - lastLcd < 3000) return;
172 lastLcd = now;
173 page = (page + 1) % 3;
174 lcd.clear();
175 if (page == 0) {
176 lcd.print(F("Sicaklik: "));
177 if (isnan(tempC)) lcd.print(F("--")); else lcd.print(tempC, 1);
178 lcd.print('C');
179 lcd.setCursor(0, 1);
180 lcd.print(F("Hava nemi: %"));
181 if (isnan(hum)) lcd.print(F("--")); else lcd.print(hum, 0);
182 } else if (page == 1) {
183 lcd.print(F("Toprak: %"));
184 lcd.print(soil, 0);
185 lcd.setCursor(0, 1);
186 lcd.print(F("Isik: %"));
187 lcd.print(light, 0);
188 } else {
189 lcd.print(F("F:")); lcd.print(fanOn);
190 lcd.print(F(" I:")); lcd.print(heaterOn);
191 lcd.print(F(" P:")); lcd.print(pumpState == PUMPING);
192 lcd.print(F(" L:")); lcd.print(ledOn);
193 lcd.setCursor(0, 1);
194 if (dhtErrors >= 3) lcd.print(F("Sensor hatasi"));
195 else if (!tankOk) lcd.print(F("Depo bos!"));
196 else if (pumpFault) lcd.print(F("Sulama hatasi"));
197 else lcd.print(F("Sistem normal"));
198 }
199}
200
201// 6) Seri port CSV kaydı / Serial CSV log
202void logCsv(unsigned long now) {
203 if (now - lastLog < 10000) return;
204 lastLog = now;
205 Serial.print(now / 1000); Serial.print(',');
206 Serial.print(tempC, 1); Serial.print(',');
207 Serial.print(hum, 0); Serial.print(',');
208 Serial.print(soil, 0); Serial.print(',');
209 Serial.print(light, 0); Serial.print(',');
210 Serial.print(fanOn); Serial.print(',');
211 Serial.print(heaterOn); Serial.print(',');
212 Serial.print(pumpState == PUMPING); Serial.print(',');
213 Serial.println(ledOn);
214}| Fonksiyon | Görevi | Ne sıklıkla? |
|---|---|---|
| readSensors() | DHT22, toprak nemi, LDR ve şamandırayı okur; analog değerleri 10 ölçümün ortalamasıyla %0–100'e çevirir | 2,5 saniyede bir |
| controlClimate() | Fanı sıcaklık ya da nem histerezisiyle, ısıtıcıyı sıcaklık histerezisiyle sürer; sensör yoksa güvenli duruma geçer | Her döngüde |
| controlLight() | Hava kararınca LED'leri PWM ile yakar, en fazla 4 saat sonra söndürür | Her döngüde |
| controlPump() | Sulama durum makinesi: 3 sn sula, 60 sn bekle, yeniden ölç; depo ve taşma korumalı | Her döngüde |
| updateLcd() | Üç sayfa arasında geçiş yapar: hava ölçümleri, toprak nemi ve ışık, cihaz durumları | 3 saniyede bir |
| logCsv() | Ölçümleri ve durumları CSV satırı olarak Seri Monitör'e yazar | 10 saniyede bir |
LCD'nin ilk sayfasında sıcaklık ve hava nemi, ikinci sayfasında toprak nemi ve ışık yüzdesi, üçüncü sayfasında ise cihazların durumu yer alır: "F:1 I:0 P:0 L:1" satırı fanın çalıştığını, ısıtıcı ile pompanın kapalı olduğunu ve LED'lerin yandığını söyler. Alt satır ise "Sistem normal", "Depo bos!", "Sulama hatasi" ya da "Sensor hatasi" mesajlarından birini gösterir. Bu kod derlendiğinde Uno'nun program belleğinin yaklaşık %31'ini, RAM'in ise %25'ini kullanır; yani ikinci bir toprak sensörü, bir buzzer ya da bir RTC saat modülü eklemek için bol yer vardır. Uno'nun gerçek bir saati olmadığından LED kuralı "akşam olunca en fazla 4 saat" diye çalışır; bu, kışın kısa günleri uzatmak için iyi bir yöntemdir. LED'leri her gün belirli saatlerde yakmak istersen bir RTC modülü ekle ya da aşağıdaki ESP32 sürümüne geç.
ESP32 ile telefondan izleme ve sulama

Uno'nun Wi-Fi'si yoktur. Seranın durumunu telefondan görmek ve uzaktan sulamak istiyorsan beyni bir ESP32 ile değiştir. Aşağıdaki kod aynı kontrol mantığını ESP32'ye taşır ve küçük bir web sunucusu başlatır. Telefonun aynı Wi-Fi ağındayken Seri Monitör'de görünen IP adresini tarayıcıda açtığında sıcaklığı, nemi, toprak nemini, ışığı, depo durumunu ve her cihazın açık mı kapalı mı olduğunu görürsün; sayfa 10 saniyede bir kendini yeniler. Şimdi sula düğmesi, depoda su varsa pompayı bir kez 3 saniye çalıştırır. ESP32 açılışta internetten saati (NTP) alır; böylece LED'ler yalnızca 06:00 ile 20:00 arasında ve ortam karanlıksa yanar, gece ise bitkiler dinlenir.
1#include <WiFi.h>
2#include <WebServer.h>
3#include <DHT.h>
4#include <time.h>
5
6const char* ssid = "AgAdi"; // Wi-Fi adın / your Wi-Fi name
7const char* password = "Sifre123"; // Wi-Fi şifren / your Wi-Fi password
8
9// Pinler / Pins
10const int DHT_PIN = 4, FLOAT_PIN = 13, PUMP_RELAY = 25, FAN_RELAY = 26, HEATER_RELAY = 27;
11const int LED_PIN = 18; // MOSFET gate (PWM)
12const int SOIL_PIN = 34, LDR_PIN = 35; // ADC1: Wi-Fi açıkken çalışır / works with Wi-Fi on
13
14// Kalibrasyon (0-4095): kendin ölç / Calibration (0-4095): measure your own
15const int SOIL_DRY_RAW = 2800, SOIL_WET_RAW = 1200;
16const int LDR_DARK_RAW = 200, LDR_BRIGHT_RAW = 3800;
17
18// Eşikler (açma, kapama) / Thresholds (on, off)
19const float FAN_ON_C = 30, FAN_OFF_C = 27, HUM_ON = 85, HUM_OFF = 75;
20const float HEAT_ON_C = 16, HEAT_OFF_C = 19, SOIL_DRY = 35, SOIL_OK = 50;
21const float DARK_ON = 25, DARK_OFF = 35;
22const int LIGHT_FROM = 6, LIGHT_TO = 20; // LED yalnız 06:00-20:00 / LEDs only 06:00-20:00
23
24const unsigned long READ_MS = 2500, PUMP_MS = 3000, SOAK_MS = 60000;
25const unsigned long EXTRA_LIGHT_MS = 4UL * 3600000UL; // saat yoksa yedek kural / fallback without a clock
26const int MAX_PULSES = 6, LED_LEVEL = 200;
27const int RELAY_ON = LOW, RELAY_OFF = HIGH; // aktif LOW röle / active-LOW relay
28const int TANK_OK = LOW; // şamandıra suyla kapanır / float closes with water
29
30DHT dht(DHT_PIN, DHT22);
31WebServer server(80);
32
33float tempC = NAN, hum = NAN, soil = 0, light = 0;
34int dhtErrors = 0, pulses = 0;
35bool fanByTemp = false, fanByHum = false, fanOn = false, heaterOn = false;
36bool needWater = false, pumpFault = false, isDark = false, ledOn = false, tankOk = true;
37unsigned long lastRead = 0, pumpAt = 0, darkSince = 0;
38
39enum PumpState { IDLE, PUMPING, SOAKING };
40PumpState pumpState = IDLE;
41
42bool hysteresis(bool state, float value, float onAt, float offAt) {
43 if (onAt > offAt) { // yükselince aç / turn on when rising
44 if (value >= onAt) return true;
45 if (value <= offAt) return false;
46 } else { // düşünce aç / turn on when falling
47 if (value <= onAt) return true;
48 if (value >= offAt) return false;
49 }
50 return state;
51}
52
53void setRelay(int pin, bool on) {
54 digitalWrite(pin, on ? RELAY_ON : RELAY_OFF);
55}
56
57float readPercent(int pin, int raw0, int raw100) {
58 long sum = 0;
59 for (int i = 0; i < 16; i++) sum += analogRead(pin); // 16 ölçüm ortalaması / average of 16
60 long pct = map(sum / 16, raw0, raw100, 0, 100);
61 return constrain(pct, 0, 100);
62}
63
64int hourNow() {
65 struct tm t;
66 if (!getLocalTime(&t, 0)) return -1; // saat henüz yok / time not set yet
67 return t.tm_hour;
68}
69
70void readSensors() {
71 float t = dht.readTemperature();
72 float h = dht.readHumidity();
73 if (isnan(t) || isnan(h)) {
74 dhtErrors++;
75 } else {
76 tempC = t;
77 hum = h;
78 dhtErrors = 0;
79 }
80 soil = readPercent(SOIL_PIN, SOIL_DRY_RAW, SOIL_WET_RAW);
81 light = readPercent(LDR_PIN, LDR_DARK_RAW, LDR_BRIGHT_RAW);
82 tankOk = digitalRead(FLOAT_PIN) == TANK_OK;
83}
84
85void controlClimate() {
86 if (dhtErrors >= 3) { // sensör yok: güvenli durum / no sensor: safe state
87 heaterOn = false;
88 fanByTemp = true;
89 } else {
90 fanByTemp = hysteresis(fanByTemp, tempC, FAN_ON_C, FAN_OFF_C);
91 fanByHum = hysteresis(fanByHum, hum, HUM_ON, HUM_OFF);
92 heaterOn = hysteresis(heaterOn, tempC, HEAT_ON_C, HEAT_OFF_C);
93 }
94 fanOn = fanByTemp || fanByHum;
95 setRelay(FAN_RELAY, fanOn);
96 setRelay(HEATER_RELAY, heaterOn);
97}
98
99void controlLight(unsigned long now) {
100 bool wasDark = isDark;
101 isDark = hysteresis(isDark, light, DARK_ON, DARK_OFF);
102 if (isDark && !wasDark) darkSince = now;
103 int h = hourNow();
104 if (h >= 0) ledOn = isDark && h >= LIGHT_FROM && h < LIGHT_TO; // saat kuralı / clock rule
105 else ledOn = isDark && (now - darkSince < EXTRA_LIGHT_MS); // yedek kural / fallback rule
106 analogWrite(LED_PIN, ledOn ? LED_LEVEL : 0);
107}
108
109void controlPump(unsigned long now) {
110 needWater = hysteresis(needWater, soil, SOIL_DRY, SOIL_OK);
111 if (!needWater) {
112 pulses = 0;
113 pumpFault = false;
114 }
115 if (pumpState == IDLE && needWater && tankOk && !pumpFault) {
116 if (pulses >= MAX_PULSES) {
117 pumpFault = true; // nem yükselmiyor / moisture is not rising
118 } else {
119 pumpState = PUMPING;
120 pumpAt = now;
121 pulses++;
122 }
123 } else if (pumpState == PUMPING && (now - pumpAt >= PUMP_MS || !tankOk)) {
124 pumpState = SOAKING;
125 pumpAt = now;
126 } else if (pumpState == SOAKING && now - pumpAt >= SOAK_MS) {
127 pumpState = IDLE;
128 }
129 setRelay(PUMP_RELAY, pumpState == PUMPING);
130}
131
132String row(const char* name, const String& value) {
133 return String("<tr><td>") + name + "</td><td><b>" + value + "</b></td></tr>";
134}
135
136String onOff(bool on) {
137 return on ? "ON" : "OFF";
138}
139
140void handleRoot() {
141 String html = "<!DOCTYPE html><html><head><meta charset='utf-8'>"
142 "<meta name='viewport' content='width=device-width,initial-scale=1'>"
143 "<meta http-equiv='refresh' content='10'><title>BlueGrays Sera</title>"
144 "<style>body{font-family:sans-serif;max-width:420px;margin:auto;padding:12px}"
145 "td{padding:8px;border-bottom:1px solid #ddd}"
146 "a{display:block;margin-top:16px;padding:16px;border-radius:10px;background:#2e7d32;"
147 "color:#fff;text-align:center;text-decoration:none;font-size:18px}</style>"
148 "</head><body><h2>BlueGrays Sera</h2><table>";
149 html += row("Sıcaklık / Temperature", String(tempC, 1) + " °C");
150 html += row("Hava nemi / Humidity", "%" + String(hum, 0));
151 html += row("Toprak nemi / Soil", "%" + String(soil, 0));
152 html += row("Işık / Light", "%" + String(light, 0));
153 html += row("Fan", onOff(fanOn));
154 html += row("Isıtıcı / Heater", onOff(heaterOn));
155 html += row("Pompa / Pump", onOff(pumpState == PUMPING));
156 html += row("LED", onOff(ledOn));
157 html += row("Depo / Tank", tankOk ? "OK" : "BOŞ / EMPTY");
158 if (pumpFault) html += row("Uyarı / Warning", "Sulama hatası / Watering fault");
159 html += "</table><a href='/water'>Şimdi sula / Water now</a></body></html>";
160 server.send(200, "text/html", html);
161}
162
163void handleWater() { // elle tek darbe / one manual pulse
164 if (pumpState == IDLE && tankOk) {
165 pumpState = PUMPING;
166 pumpAt = millis();
167 }
168 server.sendHeader("Location", "/");
169 server.send(303);
170}
171
172void setup() {
173 Serial.begin(115200);
174 setRelay(PUMP_RELAY, false); pinMode(PUMP_RELAY, OUTPUT);
175 setRelay(FAN_RELAY, false); pinMode(FAN_RELAY, OUTPUT);
176 setRelay(HEATER_RELAY, false); pinMode(HEATER_RELAY, OUTPUT);
177 pinMode(LED_PIN, OUTPUT);
178 pinMode(FLOAT_PIN, INPUT_PULLUP);
179 dht.begin();
180 WiFi.begin(ssid, password);
181 while (WiFi.status() != WL_CONNECTED) {
182 delay(500);
183 Serial.print('.');
184 }
185 Serial.println();
186 Serial.println(WiFi.localIP()); // bu adresi telefonda aç / open this address on your phone
187 configTime(3 * 3600, 0, "pool.ntp.org"); // Türkiye UTC+3; kendi saat dilimini yaz / set your own time zone
188 server.on("/", handleRoot);
189 server.on("/water", handleWater);
190 server.begin();
191 readSensors();
192 lastRead = millis();
193}
194
195void loop() {
196 server.handleClient();
197 unsigned long now = millis();
198 if (now - lastRead >= READ_MS) {
199 lastRead = now;
200 readSensors();
201 }
202 controlClimate();
203 controlLight(now);
204 controlPump(now);
205}Uno koduna göre değişenler şunlar: ESP32'nin ADC'si 12 bittir ve 0–4095 arasında değer verir, bu yüzden kalibrasyonu yeniden yapmalısın; sensörleri 3V3'ten beslediğin için ham değerler de farklı olur. Saat dilimi configTime() satırında ayarlanır; Türkiye için 3 × 3600 saniye yazdık. İnternet yoksa ya da saat henüz alınmadıysa LED kuralı Uno'daki "en fazla 4 saat" kuralına geri döner. Sayfa şifresizdir, yani ev ağındaki herkes pompayı çalıştırabilir; WebServer kütüphanesinin authenticate() fonksiyonuyla basit bir parola eklemek iyi bir alışkanlıktır. LCD'yi ESP32'de de kullanmak istersen Uno kodundaki updateLcd() fonksiyonunu olduğu gibi taşıyabilirsin; SDA GPIO 21, SCL GPIO 22'dir.
| Yöntem | Evin dışından erişim | Zorluk | En uygun durum |
|---|---|---|---|
| ESP32 web sunucusu (bu rehberdeki kod) | Hayır, yalnızca aynı ağ | Orta | Okul sunumu, internetsiz gösteri |
| Blynk IoT | Evet | Kolay | Hazır göstergeler, grafik ve bildirim |
| Telegram botu | Evet | Orta | "Depo boş" ve "sıcaklık çok yüksek" uyarıları |
| Google E-Tablolar | Evet | Orta | Haftalarca veri kaydı ve grafik |
| MQTT ve Home Assistant | Evet, ayar gerekir | İleri | Gerçek ev ve bahçe otomasyonu |
Okul sunumu için ESP32 web sunucusu en güvenli seçimdir: bir buluta bağımlı değildir ve salonda internet olmasa bile kendi ağıyla çalışır. Evin dışından da bakmak istersen hazır göstergeleri ve grafikleriyle Blynk IoT en kolay yoldur. Depo boşaldığında ya da sıcaklık tehlikeli seviyeye çıktığında telefona mesaj almak için ESP32 Telegram botu, TÜBİTAK raporun için haftalarca veri toplamak için ESP32 ile Google E-Tablolar, serayı gerçek bir ev otomasyonuna bağlamak için MQTT ve Home Assistant rehberlerine göz at.
Raspberry Pi Pico ile MicroPython sürümü
Aynı serayı Raspberry Pi Pico ile MicroPython'da da yönetebilirsin. Pico'nun MicroPython yazılımında DHT sürücüsü hazır gelir, ek dosya kopyalaman gerekmez. Analog girişler yalnızca GP26, GP27 ve GP28'dir ve read_u16() 0–65535 arası değer verir; kalibrasyon sayılarını buna göre ölç. Pico 3,3 V ile çalıştığı için sensörleri 3V3'ten, röle modülünü ise VBUS'tan (USB'nin 5 V'u) ya da LM2596 hattından besle. Aşağıdaki kod fan, ısıtıcı, LED ve sulama kurallarının tamamını içerir ve değerleri Thonny'nin kabuğuna yazar. Dosyayı Pico'ya main.py adıyla kaydedersen kart bilgisayara bağlı olmadan da çalışır. Wi-Fi'li Pico W kullanıyorsan değerleri telefondan izlemek için Pico W web sunucusu yazısındaki yöntemi ekleyebilirsin.
1from machine import Pin, ADC, PWM
2import dht
3import time
4
5sensor = dht.DHT22(Pin(15)) # GP15: DHT22 DATA
6soil_adc = ADC(26) # GP26: toprak nem AOUT / soil moisture AOUT
7ldr_adc = ADC(27) # GP27: LDR bölücü (3V3) / LDR divider (3V3)
8tank = Pin(14, Pin.IN, Pin.PULL_UP) # şamandıra: suyla 0 / float switch: 0 with water
9fan = Pin(16, Pin.OUT, value=1) # aktif LOW röleler kapalı başlar / active-LOW relays start off
10heater = Pin(17, Pin.OUT, value=1)
11pump = Pin(18, Pin.OUT, value=1)
12led = PWM(Pin(19)) # MOSFET gate
13led.freq(1000)
14
15SOIL_DRY_RAW, SOIL_WET_RAW = 50000, 22000 # kendin ölç / measure your own
16LDR_DARK_RAW, LDR_BRIGHT_RAW = 2000, 60000
17EXTRA_LIGHT_MS = 4 * 3600 * 1000 # günü 4 saat uzat / extend the day by 4 h
18
19
20def percent(adc, raw0, raw100):
21 raw = sum(adc.read_u16() for _ in range(10)) // 10
22 pct = (raw - raw0) * 100 // (raw100 - raw0)
23 return max(0, min(100, pct))
24
25
26def hysteresis(state, value, on_at, off_at):
27 if on_at > off_at: # yükselince aç / turn on when rising
28 if value >= on_at:
29 return True
30 if value <= off_at:
31 return False
32 else: # düşünce aç / turn on when falling
33 if value <= on_at:
34 return True
35 if value >= off_at:
36 return False
37 return state # ölü bölge / dead band
38
39
40def relay(pin, on):
41 pin.value(0 if on else 1)
42
43
44t = h = None
45errors = pulses = 0
46fan_t = fan_h = heat_on = need_water = dark = led_on = False
47pump_state, pump_at, dark_since = "idle", 0, 0
48soil = light = 0
49tank_ok = True
50time.sleep(2) # DHT22 açılış süresi / DHT22 start-up time
51last_read = time.ticks_add(time.ticks_ms(), -2500)
52
53while True:
54 now = time.ticks_ms()
55 if time.ticks_diff(now, last_read) >= 2500: # 2,5 sn'de bir ölç / measure every 2.5 s
56 last_read = now
57 try:
58 sensor.measure()
59 t, h = sensor.temperature(), sensor.humidity()
60 errors = 0
61 except OSError:
62 errors += 1 # okuma hatası / read error
63 soil = percent(soil_adc, SOIL_DRY_RAW, SOIL_WET_RAW)
64 light = percent(ldr_adc, LDR_DARK_RAW, LDR_BRIGHT_RAW)
65 tank_ok = tank.value() == 0
66
67 if errors >= 3 or t is None: # güvenli durum / safe state
68 heat_on, fan_t = False, True
69 else:
70 fan_t = hysteresis(fan_t, t, 30, 27)
71 fan_h = hysteresis(fan_h, h, 85, 75)
72 heat_on = hysteresis(heat_on, t, 16, 19)
73 relay(fan, fan_t or fan_h)
74 relay(heater, heat_on)
75
76 was_dark = dark
77 dark = hysteresis(dark, light, 25, 35)
78 if dark and not was_dark:
79 dark_since = now # akşam oldu / dusk detected
80 led_on = dark and time.ticks_diff(now, dark_since) < EXTRA_LIGHT_MS
81 led.duty_u16(50000 if led_on else 0)
82
83 need_water = hysteresis(need_water, soil, 35, 50)
84 if not need_water:
85 pulses = 0
86 print("T", t, "N", h, "Toprak", soil, "Isik", light, "Pompa", pump_state,
87 "Depo OK" if tank_ok else "Depo BOS", "HATA" if pulses >= 6 else "")
88
89 # Sulama: 3 sn darbe, 60 sn bekleme / Watering: 3 s pulse, 60 s soak
90 if pump_state == "idle" and need_water and tank_ok and pulses < 6:
91 pump_state, pump_at = "pump", now
92 pulses += 1
93 elif pump_state == "pump" and (time.ticks_diff(now, pump_at) >= 3000 or not tank_ok):
94 pump_state, pump_at = "soak", now
95 elif pump_state == "soak" and time.ticks_diff(now, pump_at) >= 60000:
96 pump_state = "idle"
97 relay(pump, pump_state == "pump")
98 time.sleep_ms(100)Okul ve TÜBİTAK sunumu için ipuçları
Akıllı sera çok sevilen bir konu olduğu için jüri onlarca benzer maket görür. Seni öne çıkaracak olan, sistemin çalıştığını göstermekten çok bir soruya ölçümle cevap vermendir. TÜBİTAK 4006 bilim fuarında amaç, yöntem ve beklenen sonuç net olmalıdır; 2204 araştırma projelerinde ise karşılaştırmalı bir deney ve veri beklenir. Bu sera için güzel bir araştırma sorusu şudur: "Toprak nemine göre yapılan sulama, her gün aynı saatte yapılan sulamaya göre ne kadar su tasarrufu sağlar ve bitkinin büyümesini nasıl etkiler?"
- İki grup kur: Aynı türden ve aynı boydaki fideleri iki gruba ayır. Bir grubu sensörlü sistemle, diğerini her gün sabit miktarda elle sula; ışık ve sıcaklık iki grupta da aynı olsun.
- Ölç ve kaydet: Haftalık su miktarını (depoyu ölçerek, mL), bitki boyunu (cm) ve yaprak sayısını bir tabloda tut. Koddaki CSV çıktısı ya da Google E-Tablolar kaydı sıcaklık ve nem grafiğini hazır verir.
- Blok diyagram hazırla: Solda sensörler, ortada Arduino, sağda eylemciler olsun; okları kural cümleleriyle etiketle ("toprak < %35 → pompa"). Jüri kodu okumaz ama bu diyagramı hatırlar.
- Histerezisi anlat: Tek eşikli ve histerezisli fanın bir saatte kaç kez açılıp kapandığını gösteren iki grafik, projenin mühendislik tarafını jüriye en iyi anlatan şeydir.
- Canlı gösteri senaryosu yaz: DHT22'yi avucunla ısıtıp fanı çalıştır, toprak sensörünü kuru bir saksıya taşıyıp pompanın darbelerini göster, LDR'yi elinle kapatıp LED'lerin yandığını göster. Her gösteri 10–20 saniye sürsün.
- Gerçek hayata bağla: Seralardaki su ve enerji tüketimi, aşırı nemin yol açtığı mantar hastalıkları ve üreticinin iş yükü gibi gerçek sorunlardan söz et; sisteminin hangisine cevap verdiğini sayılarla göster.
- Yedek plan hazırla: Çalışan sistemin videosunu çek; yedek kablo, pompa ve adaptör götür; kalibrasyon değerlerini ve Wi-Fi ayarlarını bir kâğıda yaz.
- Jüri sorularına hazırlan: "Sensör bozulursa ne olur?", "Elektrik kesilirse bitkilere ne olur?", "Gerçek bir serada nasıl büyütürsün?" sorularına kısa cevaplar hazırla. Koddaki güvenli durum ve taşma koruması bu soruların en güzel cevabıdır.
Sık yapılan hatalar ve çözümleri
| Belirti | Olası sebep | Çözüm |
|---|---|---|
| Pompa çalışınca Arduino resetleniyor ya da LCD bozuluyor | Pompa ve röleler Arduino'nun 5V pininden besleniyor, motor paraziti | Pompa ve röle bobinlerini LM2596'dan besle, GND'leri birleştir, pompaya ters diyot ve 470 µF kondansatör ekle |
| Röleler sürekli tık tık yapıyor | Tek eşik kullanılıyor ya da ölçüm gürültülü | Açma ve kapama için ayrı eşik (histerezis) kullan, analog ölçümlerin ortalamasını al |
| Pompa durduktan sonra da su akıyor | Sifon etkisi: hortumun ucu depodaki su seviyesinden aşağıda | Depoyu alçağa koy ya da hortumun tepesine küçük bir hava deliği aç |
| Toprak nemi hep %0 ya da hep %100 | Kalibrasyon değerleri yanlış ya da AOUT yanlış pine bağlı | Sensörün havadaki ve sudaki ham değerini ölç, sabitlere yaz |
| LED'ler yanıp sönüyor | LDR, LED'lerin ışığını görüyor | LDR'yi seranın dışına, gökyüzüne bakacak şekilde taşı |
| DHT22 nan değeri veriyor | Yanlış pin, pull-up direnci yok ya da 2 saniyeden sık okunuyor | Pini kontrol et, modül kullan ya da DATA ile VCC arasına 10 kΩ ekle |
| Sıcaklık olması gerekenden yüksek görünüyor | Sensöre güneş ya da LED ışığı doğrudan vuruyor | Delikli beyaz bir kapakla gölgele, ısıtıcıdan uzağa taşı |
| Duvarlarda su damlaları ve küf | Nem çok yüksek, havalandırma yetersiz | Nem eşiğini düşür, hava giriş deliğini büyüt, sulamayı azalt |
| ESP32'de toprak değeri Wi-Fi açılınca bozuluyor | Sensör ADC2 pinine bağlı | GPIO 32–39 arasındaki bir ADC1 pinine taşı |
| ESP32 ile röle hiç bırakmıyor | 3,3 V'luk HIGH sinyali aktif LOW 5 V modülü kapatamıyor | JD-VCC ayrımı yap ya da 3,3 V tetiklemeli röle modülü kullan |
Akıllı sera projesi; sensör okumayı, kalibrasyonu, histerezisle karar vermeyi, röle ve MOSFET sürmeyi, güç planlamayı ve kablosuz izlemeyi tek bir çalışmada birleştirir ve sonunda gerçekten yaşayan bir şeyi, bir bitkiyi büyütür. Önce sulama ve havalandırmayla başla, her parçayı tek tek test et, sonra ısıtıcıyı, LED'leri ve telefonu ekle. BlueGrays uygulamasında DHT11 ile sıcaklık ve nem ölçümünü, LDR ile ışık okumayı, akıllı sulama istasyonunu ve DHT22'li sıcaklık kaydediciyi devre şemaları ve kodlarıyla adım adım bulabilirsin.
Sıkça sorulan sorular
Akıllı sera projesi için hangi malzemeler gerekir?
Temel bir akıllı sera için Arduino Uno ya da ESP32, DHT22 sıcaklık ve nem sensörü, kapasitif toprak nem sensörü, LDR ve 10 kΩ direnç, röle modülü, mini dalgıç pompa ve bir fan yeterlidir. Tam sürüme 12 V PTC ısıtıcı, MOSFET ile sürülen LED şerit, I2C LCD, şamandıralı seviye sensörü, 12 V 5 A adaptör ve LM2596 eklenir. Gövde için kapaklı şeffaf bir saklama kutusu en kolay ve en ucuz çözümdür.
Akıllı sera nasıl çalışır?
Akıllı sera ölç, karar ver, uygula, göster döngüsüyle çalışır. Sensörler sıcaklığı, nemi, toprak nemini ve ışığı ölçer; Arduino bu değerleri eşiklerle karşılaştırır ve röle ya da MOSFET üzerinden fanı, ısıtıcıyı, pompayı ve LED'leri açıp kapatır. Sonuçlar LCD ekranda ve ESP32 kullanılıyorsa telefonda gösterilir.
Sera için DHT11 mi DHT22 mi kullanmalıyım?
DHT22 seç. DHT11 yalnızca 0–50 °C aralığında ±2 °C doğrulukla ölçer ve %90'ın üstündeki nemi göremez; seralarda ise sabahları nem %90'ı aşabilir, kışın sıcaklık sıfırın altına düşebilir. DHT22 −40 ile 80 °C arasında ±0,5 °C, nemde yaklaşık ±%2 doğrulukla ölçer ve kodu DHT11 ile neredeyse aynıdır.
Akıllı sera projesi kaç TL'ye mal olur?
Fiyatlar satıcıya ve kura göre değişse de kart hariç temel sürüm (DHT22, toprak nem sensörü, LDR, röle, pompa ve fan) genellikle 400–700 TL arasındadır. Isıtıcı, LED şerit, LCD, 12 V adaptör ve gövdeyle tam sürüm 1.200–2.000 TL civarına çıkar. Elinde bir Arduino başlangıç seti varsa maliyet belirgin şekilde düşer.
Akıllı serayı telefondan nasıl izlerim?
En kolay yol beyni ESP32 ile değiştirip bir web sunucusu kurmaktır; aynı Wi-Fi ağındaki telefonun tarayıcısından değerleri görür ve pompayı çalıştırırsın. Evin dışından erişmek için Blynk IoT, uyarı mesajları için Telegram botu, uzun süreli veri kaydı için Google E-Tablolar kullanabilirsin.
Akıllı sera TÜBİTAK projesi olarak nasıl sunulur?
Yalnızca çalışan bir maket göstermek yerine ölçülebilir bir araştırma sorusu kur; örneğin sensörlü sulamanın saatli sulamaya göre ne kadar su tasarrufu sağladığını iki grup bitkiyle haftalarca ölç. Su miktarını, bitki boyunu ve sıcaklık-nem grafiklerini tablo ve grafikle sun, sistemi bir blok diyagramla anlat ve canlı gösteri için kısa bir senaryo hazırla.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
DHT11 İle Sıcaklık Ve Nem
Odan kaç derece, hava nemli mi, kuru mu? DHT11 sensörü sıcaklığı ve nemi ölçer, Arduino da bu değerleri iki satırlık LCD ekrana yazar ve birkaç saniyede bir yeniler. Sensöre bir şey olursa ekranda hat

Ldr İle Işık Şiddeti Okuma
Odanın ne kadar aydınlık olduğunu sayıyla görmek ister misin? Işığa duyarlı küçük bir parça olan LDR ortam ışığını ölçer ve Arduino bu değeri yarım saniyede bir bilgisayar ekranına yazar. Elinle üstün

ESP32 LDR Modülü ile Işık Şiddeti Okuma
LDR modülünün ham ADC değerini ve karanlık/aydınlık referanslarına göre hesaplanan göreli ışık yüzdesini Serial Monitor'da gerçek zamanlı izleme. Bu yüzde lux ölçümü değildir. Modülün kalibrasyon mantığını öğretir. Proje 10'un öncesinde yapılması ideal; çünkü eşik değerini belirlemek için kullanılır.

Akıllı Sulama İstasyonu
Tatile gidince çiçeklerin kurumasın diye kendi kendine sulayan bir bahçıvan. Toprak nem sensörü toprağın ne kadar ıslak olduğunu ölçer; toprak kuruyunca Arduino su pompasını birkaç saniye çalıştırır v

ESP32 DHT22 Sıcaklık ve Nem (LCD Ekranlı)
DHT22 sensörü ile ortam sıcaklığını ve nem oranını ölçüp 16x2 I2C LCD ekranda gerçek zamanlı olarak gösteren bir projedir. Adafruit DHT kütüphanesi kullanılarak tek kablo (one-wire) protokolü ile sensörden veri okunur. Bu proje, IoT hava istasyonu, akıllı ev otomasyonu veya seralarda ortam takip sistemlerinin temelini oluşturur. ESP32'nin GPIO 4 pini üzerinden DHT22 ile haberleşme sağlanır.

ESP32 Sıcaklık Loglayıcı (DHT22 + DS1307 RTC + microSD + OLED)
ESP32 ile zamanlı sıcaklık ve nem kayıt sistemi kur! Bu projede DHT22 sensörü ile dakikada bir sıcaklık/nem okuyup DS1307 RTC modülünden aldığın gerçek zaman etiketiyle microSD karta CSV formatında kaydediyoruz. OLED ekranda büyük font ile son sıcaklık değeri, nem yüzdesi, timestamp ve toplam kayıt sayısı canlı gösterilir. SD kart üzerindeki /temp_log.csv dosyası Excel/Google Sheets/Python pandas ile açılıp analiz edilebilir — sera monitörü, ev/oda sıcaklık takibi, laboratuvar veri toplama,

ESP32 Akıllı Aydınlatma (PIR + LDR + Röle + OLED)
ESP32, PIR hareket sensörü, LDR ışık sensörü ve röle modülü ile evinin koridorunu, tuvaletini veya kilerini akıllı aydınlatmaya dönüştürüyoruz. Klasik "karanlıkta hareket algılanınca ışık yansın" senaryosunu inşa ediyoruz — ortam aydınlıksa hareket olsa bile ışık yanmaz, böylece gündüz gereksiz yere yanmaz ve enerji tasarrufu sağlanır. Işık 30 saniye yanık kalır; hareket devam ederse timer otomatik sıfırlanır ve süre uzar. SSD1306 OLED ekranda büyük fontlarla durum (AÇIK / GÜNDÜZ / BEKLE), geri
Diğer yazılar
BlueGrays uygulaması
Bu devreyi telefonunda adım adım kur
Devre şeması, kod ve parça listesi tek ekranda. 200'den fazla Arduino, ESP32 ve Pico projesi seni bekliyor.
Telefonunla tara
Kamerayı koda tut; telefonunun uygulama mağazası açılır.
ya da mağazada aç

