Tam Otomatik Havalandırma Sisteminin Sorunları

Otomatik havalandırma sistemi ile ilgili bir önceki yazıda, evinizdeki düzeni bozmadan nasıl aktif havalandırma yapabileceğinizi gösteren bir proje yapmıştım. Tasarımda her şey ideal olsa da, pratikte ihmal edilen gerçekler insanın önüne geliyor. Ben tasarımımda gerçek dünyayı ihmal ettim, sorun ortaya çıktı. Çözüm yine dünyanın kendisinden geldi.

Kartaca Yazılım Firmasının Linkedin için hazırladığı görsel.

Eğlenceli bir lise projesi gibi başlayan bu tasarım, daha iyi bir duruma evrildi gözümde. Çünkü kullanıldı, sorunları ve eksiklikleri ortaya çıktı.

Yaklaşık bir buçuk haftadır sistemi kullanıyorum. İlk olarak 20 dk çalışıp 15 dakika dinlenme süreci harika çalışıyor. Nem oranı yükseldiğinde düzgün şekilde çalışıyor. Banyoda ayna buharlanmadan içerideki su buharı tahliye ediliyor. Ben çalışmasını istemediğim zamanları da düşünerek sistemin girişine bir on/off anahtar koydum. Çalıştırmak istemediğim zaman manuel olarak kapatıyorum. Onun dışında açık olduğu zaman her şeyi kendisi yapıyor. Yani “tam otomatik havalandırma sistemi”ni manuel kullanmak, oksimorona örnek gibi dursa da, manuel’den kasıt aç/kapa işlemi olduğu için “tam otomatik olmasına” halel getirmiyor.

Buraya kadar her şey güzel. Fakat yaz kendini hissettirmeye başladıkça başka bir sorunla yüzleştim:

DHT11 yani kullandığım sıcaklık ve nem algılayan sensör bağıl nemi ölçüyor ve ortam ayırt etmiyor. İlk sorunum burada başladı. Nemli bir bölgede oturduğum için havanın bağıl nemi %65’i geçebiliyor. Bu gibi durumlarda fan sürekli 20 dk çalışıp 15 dk dinleniyor. İçerideki su buharı tahliye olsa bile yaşadığım semtteki su buharını tahliye etmeye çalışıyor. Tabi bu da imkansız bir fenomen.

İkinci sorunum, enerji ile ilgili. Rölem esp8266 ve kendisini tek prizden besleyemiyor. Bu nedenle esp8266’yı beslemek için ve oradaki sistemimi bozmamak için 4400 mah powerbank kullandım. Mikrodenetleyici low-power olduğu için haftalarca çalışır diye umut ediyordum fakat öyle olmadı. Üç gün boyunca çalışıyor sonra pil bitiyor. Bu sorunlar manuel olarak giderilebiliyor fakat tam otomatik bir sistem olmuyor.

İki temel sorunumuz var. İkinci sorun yani enerji tüketim sorunu için takip edilmesi gereken iki yol var. İlk olarak cihazın üzerindeki ledleri çıkarmak. Sonra da modelinize göre buradaki adımları takip etmek veya doğrudan 5v bir enerji kaynağına bağlamak.

İlk sorunumuzda ise bizi, mikrodenetleyici seçerken “internete ihtiyacımız olabilir” öngörüsü kurtarıyor. Aslında ben esp8266’yı uzaktan aç/kapa yaparım diye almıştım fakat daha kritik bir sorunun çözülmesine yardımcı olacak: Bağıl nemin internetten alınması… Evet, 65 değerini ben insan sağlığı açısından seçmiştim. Ayrıca cihazım fazla çalışmasın diye 50-60 normundan biraz fazlaya sabitlemiştim ama bu değer dünya şartlarına uygun değilmiş. Bunu da deneyerek gördüm. Bu nedenle buradaki kaynağı kullanarak ve yeni bir algoritma tasarlayarak bu problemi çözdüm.

  • Öncelikle internete bağlan ve open weather api’den bağıl nem bilgisini al.
  • İnternette bağıl nem bilgisi 65’ten küçük mü? O zaman eskisi gibi çalış.
  • Bağıl nem bilgisi 65’ten büyükse o zaman 90’e eşit veya büyük mü? Evetse, o zaman fan çalışmayı durdursun. 90’den küçükse o zaman, fan alt sınır o değer oluncaya kadar çalışsın.

(90 değerini DHT11’in ölçebileceği maksimum nem miktarı bu olduğu için seçtik.)

İlk sorunumuza, algoritmadan bağımsız ikinci bir çözümümüz daha var. O da dışarıya bir tane daha DHT11’li esp8266 koyup dışarıdaki bağıl nem bilgisini ölçmek ve ıslak hacimdeki sensörün dışarıdaki sensörden, bilgi almasını sağlamak.

Algoritmaya tekrar dönersek, özetle en küçük alt sınır 65. Eğer 65’ten yukarı ve 90’den aşağı bir bağıl nemimiz varsa alt değerimiz bu değer olacak. Eğer 90’dan da yukarı bir değerse, fanı kapatıp, fanın çalışmaması için o değeri alt değer belirleyeceğiz.

Nihai kodumuz aşağıdakine benzer şekilde olacak.

#include <ArduinoJson.h>
#include <SimpleDHT.h>
#include <ESP8266WiFi.h>
#include <math.h>

char ssid[] = "REPLACE_WITH_YOUR_SSID";
char pass[] = "REPLACE_WITH_YOUR_PASSWORD";

WiFiClient client;

// Open Weather Map API server name
const char server[] = "api.openweathermap.org";

// Replace the next line to match your city and 2 letter country code
String nameOfCity = "REPLACE_WITH_YOUR_CITY,REPLACE_WITH_YOUR_COUNTRY_CODE";
// How your nameOfCity variable would look like for Lagos on Nigeria
//String nameOfCity = "İstanbul,TR"; 

// Replace the next line with your API Key
String apiKey = "REPLACE_WITH_YOUR_API_KEY";

String text;

int jsonend = 0;
boolean startJson = false;
#define JSON_BUFF_DIMENSION 2500


#define PIN_DHT 2
#define HUM_TEMP_LIMIT 65
//dht11 measures max 90 rh
#define HUM_SUP_LIMIT 90
#define MINUTE_IN_MILLISECOND 60*1000*1
#define RUN_DURATION_LIMIT (20*MINUTE_IN_MILLISECOND)
#define WAIT_TIME (1*MINUTE_IN_MILLISECOND)


const byte miBufferON[] = {0xA0, 0x01, 0x01, 0xA2}; //The hex code opens relay.
const byte miBufferOFF[] = {0xA0, 0x01, 0x00, 0xA1}; //The hex code closes relay.


SimpleDHT11 dht11(PIN_DHT);

bool isRelayOn = false;
unsigned int humSubLimit = HUM_TEMP_LIMIT;
unsigned int humidityInternet = HUM_TEMP_LIMIT;
unsigned long currentTime = 0;
unsigned long elapsedTime = 0;
unsigned long startTime = 0;
unsigned long lastConnectionTime = 10 * MINUTE_IN_MILLISECOND; // last time you connected to the server, in milliseconds

void setup() {
    Serial.begin(9600); // for relay
    delay(10);
    // relay initial status
    Serial.write(miBufferOFF, sizeof (miBufferOFF));
    Serial.println();
    text.reserve(JSON_BUFF_DIMENSION);

    WiFi.begin(ssid, pass);
    Serial.println("connecting");
    while (WiFi.status() != WL_CONNECTED) {
        delay(500);
        Serial.print(".");
    }
    Serial.println("WiFi Connected");
}

void loop() {

    //check if 10mins has passed then conect again and pull
    if (millis() - lastConnectionTime > (10 * MINUTE_IN_MILLISECOND)) {
        // note the time that the connection was made:
        lastConnectionTime = millis();
        makehttpRequest();
    }


    if (humidityInternet > HUM_SUB_LIMIT) {
        if (humidityInternet >= HUM_SUP_LIMIT) {
            Serial.write(miBufferOFF, sizeof (miBufferOFF));
            isRelayOn = false;
            elapsedTime = 0;
        }        
        humSubLimit = humidityInternet;
    }


    byte temp, humi = 0;

    int err = SimpleDHTErrSuccess;
    //Serial.print("sicaklik degerleri okunacak...");
    while ((err = dht11.read(&temp, &humi, NULL)) != SimpleDHTErrSuccess) {
        Serial.print("Read DHT11 failed, err=");
        Serial.println(err);
        delay(1000);
        //return;
    }

    if (humi > humSubLimit) {
        // humidity is more than limit
        if (!isRelayOn) {
            // Relay is not on
            if (elapsedTime > RUN_DURATION_LIMIT) {
                // and fan running time is more than RUN_DURATION_LIMIT then
                // fan must be turn off (relay is already off this statement is not sense)
                Serial.write(miBufferOFF, sizeof (miBufferOFF));
                isRelayOn = false;
            } else {
                // fan running time is less than RUN_DURATION_LIMIT but relay is not on then
                // Turn on must be turn on
                Serial.write(miBufferON, sizeof (miBufferON));
                // start time must be set
                startTime = millis();
                // relay flag is true
                isRelayOn = true;
            }

        } else {
            // relay is on
            if (elapsedTime > RUN_DURATION_LIMIT) {
                // fan running time is more than RUN_DURATION_LIMIT
                Serial.write(miBufferOFF, sizeof (miBufferOFF));
                // set related flag is false
                isRelayOn = false;
                elapsedTime = 0;
                delay(15 * MINUTE_IN_MILLISECOND); //
            } else {
                // fan running time is less than RUN_DURATION_LIMIT
                // do nothing cause relay has already turned on.
                // measure time
                currentTime = millis();
                elapsedTime = currentTime - startTime;
            }

        }

    } else {
        // humidity is less than HUM_TEMP_LIMIT
        if (isRelayOn) {
            // this is adverse(undesirable) condition so hum is less than limit and relay is on.
            // nevertheless we close the relay
            Serial.write(miBufferOFF, sizeof (miBufferOFF));
            // the following flag must be false.
            isRelayOn = false;
            elapsedTime = 0;
        }
    }

    delay(WAIT_TIME);


}

// to request data from OWM

void makehttpRequest() {
    // close any connection before send a new request to allow client make connection to server
    client.stop();

    // if there's a successful connection:
    if (client.connect(server, 80)) {
        // Serial.println("connecting...");
        // send the HTTP PUT request:
        client.println("GET /data/2.5/forecast?q=" + nameOfCity + "&APPID=" + apiKey + "&mode=json&units=metric&cnt=2 HTTP/1.1");
        client.println("Host: api.openweathermap.org");
        client.println("User-Agent: ArduinoWiFi/1.1");
        client.println("Connection: close");
        client.println();

        unsigned long timeout = millis();
        while (client.available() == 0) {
            if (millis() - timeout > 5000) {
                Serial.println(">>> Client Timeout !");
                client.stop();
                return;
            }
        }

        char c = 0;
        while (client.available()) {
            c = client.read();
            // since json contains equal number of open and close curly brackets, this means we can determine when a json is completely received  by counting
            // the open and close occurences,
            //Serial.print(c);
            if (c == '{') {
                startJson = true; // set startJson true to indicate json message has started
                jsonend++;
            }
            if (c == '}') {
                jsonend--;
            }
            if (startJson == true) {
                text += c;
            }
            // if jsonend = 0 then we have have received equal number of curly braces 
            if (jsonend == 0 && startJson == true) {
                parseJson(text.c_str()); // parse c string text in parseJson function
                text = ""; // clear text string for the next time
                startJson = false; // set startJson to false to indicate that a new message has not yet started
            }
        }
    } else {
        // if no connction was made:
        Serial.println("connection failed");
        return;
    }
}

//to parse json data recieved from OWM

void parseJson(const char * jsonString) {
    //StaticJsonBuffer<4000> jsonBuffer;
    const size_t bufferSize = 2 * JSON_ARRAY_SIZE(1) + JSON_ARRAY_SIZE(2) + 4 * JSON_OBJECT_SIZE(1) + 3 * JSON_OBJECT_SIZE(2) + 3 * JSON_OBJECT_SIZE(4) + JSON_OBJECT_SIZE(5) + 2 * JSON_OBJECT_SIZE(7) + 2 * JSON_OBJECT_SIZE(8) + 720;
    DynamicJsonBuffer jsonBuffer(bufferSize);

    // FIND FIELDS IN JSON TREE
    JsonObject& root = jsonBuffer.parseObject(jsonString);
    if (!root.success()) {
        Serial.println("parseObject() failed");
        return;
    }

    JsonArray& list = root["list"];
    JsonObject& nowT = list[0];
    JsonObject& later = list[1];

    humidityInternet = round(nowT["main"]["humidity"]);

    Serial.println();
}

Çözüme ulaşmış gibi gözüksek de bu bir yolculuk. Hataları düzelte düzelte en iyi çözüme ulaşacağız.