#include #include #include #include #include // Konfigurasi LCD LiquidCrystal_I2C lcd(0x27, 16, 2); // Konfigurasi DHT11 #define DHTPIN 2 // GPIO2 (D4) #define DHTTYPE DHT11 DHT dht(DHTPIN, DHTTYPE); // Konfigurasi MQ135 #define MQ135_PIN A0 float R0 = 76.63; // Nilai default, akan dikalibrasi ulang // Konfigurasi Buzzer dan LED #define BUZZER_PIN 12 // GPIO12 (D6) #define LED_PIN 14 // GPIO14 (D5) - Pin untuk LED indikator (diubah ke D5) #define CO2_THRESHOLD 90 // Threshold CO2 dalam ppm #define TEMP_THRESHOLD 30 // Threshold suhu dalam °C #define HUMID_THRESHOLD 80 // Threshold kelembaban dalam % // Konfigurasi WiFi dan MQTT const char* ssid = "SKK - STUDENT"; const char* password = ""; const char* mqtt_server = "103.124.138.170"; const int mqtt_port = 1883; const char* mqtt_user = "USER_MQTT"; const char* mqtt_pass = "PASS_MQTT"; const char* topic_temp = "sensor/temperature"; const char* topic_humid = "sensor/humidity"; const char* topic_co2 = "sensor/co2"; const char* topic_control = "sensor/control"; const char* topic_status = "sensor/status"; const char* topic_alert = "sensor/alert"; WiFiClient espClient; PubSubClient client(espClient); // Variabel status sistem bool systemEnabled = true; unsigned long lastSensorUpdate = 0; const long sensorInterval = 60000; // Interval pengiriman data sensor (ms) void setup_wifi() { delay(10); Serial.println(); Serial.print("Connecting to "); Serial.println(ssid); WiFi.begin(ssid, password); lcd.clear(); lcd.print("Connecting WiFi"); while (WiFi.status() != WL_CONNECTED) { for(int i=0; i<10; i++) { delay(500); Serial.print("."); lcd.print("."); } lcd.clear(); lcd.print("Retrying WiFi..."); } Serial.println(""); Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); lcd.clear(); lcd.print("WiFi Connected"); lcd.setCursor(0, 1); lcd.print(WiFi.localIP()); delay(2000); } void reconnect() { while (!client.connected()) { Serial.print("Attempting MQTT connection..."); if (client.connect("ESP8266Client", mqtt_user, mqtt_pass)) { Serial.println("connected"); client.subscribe(topic_control); Serial.println("Subscribed to control topic"); client.publish(topic_status, systemEnabled ? "ON" : "OFF", true); } else { Serial.print("failed, rc="); Serial.print(client.state()); Serial.println(" try again in 5 seconds"); lcd.clear(); lcd.print("MQTT Fail"); lcd.setCursor(0, 1); lcd.print("Retrying..."); delay(5000); } } } void callback(char* topic, byte* payload, unsigned int length) { Serial.print("Message arrived ["); Serial.print(topic); Serial.print("] "); char message[length+1]; for (int i = 0; i < length; i++) { message[i] = (char)payload[i]; } message[length] = '\0'; Serial.println(message); if (String(topic) == topic_control) { if (strcmp(message, "ON") == 0) { systemEnabled = true; noTone(BUZZER_PIN); // Matikan buzzer saat sistem dihidupkan digitalWrite(LED_PIN, LOW); // Matikan LED lcd.clear(); lcd.print("System ON"); client.publish(topic_status, "ON", true); Serial.println("System turned ON"); delay(1000); } else if (strcmp(message, "OFF") == 0) { systemEnabled = false; noTone(BUZZER_PIN); // Matikan buzzer saat sistem dimatikan digitalWrite(LED_PIN, LOW); // Matikan LED lcd.clear(); lcd.print("System OFF"); client.publish(topic_status, "OFF", true); Serial.println("System turned OFF"); delay(1000); } } } //mengubah waktu kalibrasi void calibrateR0() { if (!systemEnabled) return; Serial.println("Kalibrasi MQ135 dimulai..."); lcd.clear(); lcd.print("Kalibrasi MQ135"); lcd.setCursor(0, 1); lcd.print("Udara bersih 5m"); //rubah pada waktu 300 for(int i = 0; i < 300; i++) { delay(1000); if(i % 30 == 0) { Serial.print("."); lcd.print("."); } if (!systemEnabled) { Serial.println("Kalibrasi dibatalkan!"); lcd.clear(); lcd.print("Kalibrasi"); lcd.setCursor(0, 1); lcd.print("Dibatalkan"); delay(2000); return; } } Serial.println("\nMengambil sampel..."); float sum = 0; int validReadings = 0; for (int i = 0; i < 100; i++) { int rawValue = analogRead(MQ135_PIN); if(rawValue < 50 || rawValue > 1000) { continue; } float sensor_volt = rawValue * (3.3 / 1023.0); float RS_air = (3.3 - sensor_volt) / sensor_volt; sum += RS_air; validReadings++; delay(100); if (!systemEnabled) { Serial.println("Kalibrasi dibatalkan!"); lcd.clear(); lcd.print("Kalibrasi"); lcd.setCursor(0, 1); lcd.print("Dibatalkan"); delay(2000); return; } } if(validReadings < 50) { Serial.println("Gagal kalibrasi: Pembacaan tidak stabil"); lcd.clear(); lcd.print("Gagal Kalibrasi!"); delay(3000); return; } R0 = sum / validReadings; Serial.print("R0 terkalibrasi: "); Serial.println(R0); lcd.clear(); lcd.print("Kalibrasi Selesai"); lcd.setCursor(0, 1); lcd.print("R0: "); lcd.print(R0); delay(3000); } void controlBuzzerAndLED(float temperature, float humidity, float co2) { bool alertCondition = false; String alertMessage = ""; if (co2 > CO2_THRESHOLD && co2 > 0) { alertCondition = true; alertMessage += "CO2 tinggi! "; Serial.println("Peringatan: Level CO2 melebihi threshold!"); } if (temperature > TEMP_THRESHOLD && temperature > 0) { alertCondition = true; alertMessage += "Suhu tinggi! "; Serial.println("Peringatan: Suhu melebihi threshold!"); } if (humidity > HUMID_THRESHOLD && humidity > 0) { alertCondition = true; alertMessage += "Kelembaban tinggi! "; Serial.println("Peringatan: Kelembaban melebihi threshold!"); } if (alertCondition && systemEnabled) { tone(BUZZER_PIN, 1000); // Frekuensi 1 kHz digitalWrite(LED_PIN, HIGH); // Nyalakan LED lcd.setCursor(15, 0); lcd.print("!"); if (alertMessage.length() > 0) { client.publish(topic_alert, alertMessage.c_str()); } } else { noTone(BUZZER_PIN); digitalWrite(LED_PIN, LOW); // Matikan LED lcd.setCursor(15, 0); lcd.print(" "); } } void readAndSendSensorData() { if (!systemEnabled) { noTone(BUZZER_PIN); digitalWrite(LED_PIN, LOW); return; } float humidity = dht.readHumidity(); float temperature = dht.readTemperature(); int rawValue = analogRead(MQ135_PIN); float sensor_volt = rawValue * (3.3 / 1023.0); float RS_gas = (3.3 - sensor_volt) / sensor_volt; float ratio = RS_gas / R0; float ppm_CO2 = 116.6020682 * pow(ratio, -2.769034857); float corrected_ppm = ppm_CO2; if (!isnan(temperature) && !isnan(humidity)) { corrected_ppm = ppm_CO2 * (1.0 + 0.0005*(25-temperature) + 0.0002*humidity); } if (isnan(humidity)) humidity = -1; if (isnan(temperature)) temperature = -1; if(rawValue < 50 || rawValue > 1000) corrected_ppm = -1; controlBuzzerAndLED(temperature, humidity, corrected_ppm); lcd.clear(); lcd.setCursor(0, 0); lcd.print("S:"); lcd.print(systemEnabled ? "ON " : "OFF"); lcd.print(" T:"); lcd.print(temperature >= 0 ? temperature : -1, 1); lcd.print("C"); lcd.setCursor(0, 1); lcd.print("H:"); lcd.print(humidity >= 0 ? humidity : -1, 0); lcd.print("% CO2:"); if(corrected_ppm < 0) { lcd.print("Err"); } else { lcd.print(corrected_ppm, 0); } char tempStr[10], humidStr[10], co2Str[10]; dtostrf(temperature, 4, 2, tempStr); dtostrf(humidity, 4, 2, humidStr); dtostrf(corrected_ppm, 4, 0, co2Str); client.publish(topic_temp, tempStr); client.publish(topic_humid, humidStr); client.publish(topic_co2, co2Str); Serial.print("Temperature: "); Serial.print(tempStr); Serial.print("C | Humidity: "); Serial.print(humidStr); Serial.print("% | CO2: "); Serial.print(co2Str); Serial.println("ppm"); } void setup() { Serial.begin(115200); pinMode(BUZZER_PIN, OUTPUT); pinMode(LED_PIN, OUTPUT); digitalWrite(BUZZER_PIN, LOW); digitalWrite(LED_PIN, LOW); lcd.init(); lcd.backlight(); lcd.setCursor(0, 0); lcd.print("Starting System..."); dht.begin(); int testVal = analogRead(MQ135_PIN); if(testVal < 50) { Serial.println("ERROR: Sensor MQ135 tidak terdeteksi!"); lcd.clear(); lcd.print("MQ135 Error!"); while(1) { delay(1000); Serial.println("Periksa koneksi dan reset board"); } } setup_wifi(); client.setServer(mqtt_server, mqtt_port); client.setCallback(callback); calibrateR0(); lcd.clear(); } void loop() { if (!client.connected()) { reconnect(); } client.loop(); lcd.setCursor(3, 0); lcd.print(systemEnabled ? "ON " : "OFF"); unsigned long currentMillis = millis(); if (currentMillis - lastSensorUpdate >= sensorInterval && systemEnabled) { lastSensorUpdate = currentMillis; readAndSendSensorData(); } delay(100); }