#include // ===================================================== // ESP32 + MQ-135 + MQ-7 + OLED SSD1306 + GPS (BN-220) // WiFi Version // ===================================================== #include #include #include #include #include #include #include // ================== WIFI ================== const char* ssid = "KARAN"; const char* password = "12345679"; // Server const char server[] = "http://leman.k-dummy.my.id"; const int port = 80; // ================= OLED CONFIG ================= #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 #define OLED_ADDR 0x3C Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); // ================= GPS CONFIG ================== static const int GPS_RX_PIN = 16; // GPS TX -> ESP32 GPIO 16 static const int GPS_TX_PIN = 17; // GPS RX -> ESP32 GPIO 17 static const uint32_t GPS_BAUD = 9600; TinyGPSPlus gps; HardwareSerial gpsSerial(2); // ================= MQ CONFIG =================== #define BOARD "ESP-32" #define VOLTAGE 3.3 #define ADC_BITS 12 #define PIN_MQ135 34 #define PIN_MQ7 35 #define RATIO_CLEAN_MQ135 3.6 #define RATIO_CLEAN_MQ7 27.5 MQUnifiedsensor MQ135(BOARD, VOLTAGE, ADC_BITS, PIN_MQ135, "MQ-135"); MQUnifiedsensor MQ7(BOARD, VOLTAGE, ADC_BITS, PIN_MQ7, "MQ-7"); // ================== HELPER ================== void oledWiFiStatus(const String &msg) { display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("STATUS WIFI:"); display.println(msg); display.display(); delay(500); } void oledMQStatus( const String &msg1, const String &msg2, const String &msg3, const String &msg4, String msg5 = "") { display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("MQ STATUS:"); display.println(msg1); display.println(msg2); display.println(msg3); display.println(msg4); display.println(msg5); display.display(); delay(500); } // =============== GPS HELPERS =================== // Feed any pending GPS data to the library (non-blocking) void updateGPS() { while (gpsSerial.available() > 0) { gps.encode(gpsSerial.read()); } } // Returns "lat,lng" if a valid fix exists, otherwise "-" String getGPSCoordinates() { updateGPS(); if (gps.location.isValid() && gps.location.age() < 2000) { return String(gps.location.lat(), 6) + "," + String(gps.location.lng(), 6); } return "-"; } // Returns a status string for the OLED String getGPSStatus() { if (gps.charsProcessed() < 10) return "NO DATA"; if (gps.location.isValid() && gps.location.age() < 2000) return "FIX " + String(gps.satellites.value()) + " sats"; return "NO FIX"; } // ================= HELPER ====================== float readAverage(MQUnifiedsensor &sensor, int samples = 5) { float sum = 0; for (int i = 0; i < samples; i++) { sensor.update(); sum += sensor.readSensor(); updateGPS(); // keep GPS buffer drained during sampling delay(50); } return sum / samples; } // ===================================================== // WIFI CONNECTION // ===================================================== bool connectWiFi() { Serial.println("[WiFi] Connecting..."); Serial.print("[WiFi] SSID: "); Serial.println(ssid); oledWiFiStatus("Menghubungkan..."); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); unsigned long startAttempt = millis(); while (WiFi.status() != WL_CONNECTED && millis() - startAttempt < 30000) { delay(500); Serial.print("."); } Serial.println(); if (WiFi.status() == WL_CONNECTED) { Serial.println("[WiFi] CONNECTED"); Serial.print("[WiFi] IP: "); Serial.println(WiFi.localIP()); oledWiFiStatus("WIFI TERHUBUNG"); delay(1000); return true; } Serial.println("[WiFi] Connection failed!"); oledWiFiStatus("WIFI GAGAL"); return false; } // ===================================================== // ENSURE WIFI // ===================================================== bool ensureWiFi() { if (WiFi.status() == WL_CONNECTED) return true; Serial.println("[WiFi] Connection lost!"); oledWiFiStatus("WIFI PUTUS"); WiFi.disconnect(); delay(1000); return connectWiFi(); } // ===================================================== void setup() { Serial.begin(115200); delay(500); // ================= GPS INIT ================== gpsSerial.begin(GPS_BAUD, SERIAL_8N1, GPS_RX_PIN, GPS_TX_PIN); Serial.println("[GPS] Serial initialized on GPIO16/17"); // ================= OLED INIT ================= Wire.begin(21, 22); if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) { Serial.println("OLED tidak ditemukan!"); while (true) delay(1000); } // ================= WIFI INIT ================= Serial.println("[WiFi] Init WiFi..."); if (!connectWiFi()) { Serial.println("[ERROR] WiFi gagal!"); oledWiFiStatus("RESTART ESP32"); delay(2000); ESP.restart(); } // ================= MQ INIT ================= Serial.println("[MQ] Init MQ135 & MQ7"); oledMQStatus( "Init MQ135 & MQ7", "Mulai kalibrasi sensor...", "Pastikan udara bersih", "(±1-2 menit)"); // ================= KALIBRASI ================= Serial.println("Mulai kalibrasi sensor..."); Serial.println("Pastikan udara bersih (±1-2 menit)"); // ================= MQ135 ================= MQ135.setRegressionMethod(1); MQ135.init(); float r0_135 = 0; for (int i = 0; i < 10; i++) { MQ135.update(); r0_135 += MQ135.calibrate(RATIO_CLEAN_MQ135); updateGPS(); // keep GPS data flowing delay(600); } r0_135 /= 10; MQ135.setR0(r0_135); // ================= MQ7 ================= MQ7.setRegressionMethod(1); MQ7.init(); float r0_7 = 0; for (int i = 0; i < 10; i++) { MQ7.update(); r0_7 += MQ7.calibrate(RATIO_CLEAN_MQ7); updateGPS(); delay(600); } r0_7 /= 10; MQ7.setR0(r0_7); // ================= CHECK CALIBRATION ================= if (r0_135 <= 0 || r0_7 <= 0) { Serial.println("Kalibrasi gagal!"); oledMQStatus("KALIBRASI GAGAL", "", "", "RESTART ESP32..."); delay(2000); ESP.restart(); } Serial.println("Kalibrasi selesai!"); Serial.print("MQ135 R0 = "); Serial.println(r0_135); Serial.print("MQ7 R0 = "); Serial.println(r0_7); oledMQStatus("KALIBRASI OK", "...", "Mulai Monitoring", "..."); delay(1000); } // ===================================================== // LOOP // ===================================================== void loop() { // =================================================== // CHECK WIFI // =================================================== if (!ensureWiFi()) { Serial.println("[WiFi] Recover failed!"); oledWiFiStatus("WIFI ERROR"); delay(5000); return; } // =============== MQ135 ================= MQ135.setA(110.47); MQ135.setB(-2.862); float co2 = readAverage(MQ135); if (co2 < 0) co2 = 0; MQ135.setA(605.18); MQ135.setB(-3.937); float benzene = readAverage(MQ135); if (benzene < 0) benzene = 0; // =============== MQ7 =================== MQ7.setA(99.042); MQ7.setB(-1.518); float co = readAverage(MQ7); if (co < 0) co = 0; // ========== AIR QUALITY ESTIMATION ===== int aq = 100; if (co2 > 700) aq -= map(co2, 700, 2000, 0, 50); if (co2 > 2000) aq -= map(co2, 2000, 5000, 50, 80); if (co > 10) aq -= map(co, 10, 50, 0, 30); if (co > 50) aq -= 30; if (benzene > 5) aq -= map(benzene, 5, 50, 0, 40); aq = constrain(aq, 0, 100); // ================= STATUS ================= String status; if (aq >= 90) status = "Sangat Baik"; else if (aq >= 70) status = "Baik"; else if (aq >= 50) status = "Sedang"; else if (aq >= 30) status = "Buruk"; else status = "Sangat Buruk"; // =============== GPS READ ================== String gps_coords = getGPSCoordinates(); String gps_status = getGPSStatus(); // =============== OLED DISPLAY ========== // 6 lines fit on 128x64 with textSize(1) display.clearDisplay(); display.setTextSize(1); display.setTextColor(SSD1306_WHITE); display.setCursor(0, 0); display.println("CO2:" + String(co2, 1) + " CO:" + String(co, 1)); display.println("BZ :" + String(benzene, 1) + " AQ:" + String(aq) + "%"); display.println(status); display.println("GPS: " + gps_status); String lat = "-3.9891687826423334"; String lng = "119.65221465458342"; if (gps_coords != "-") { // Split lat/lng to two lines for readability int commaIdx = gps_coords.indexOf(','); display.println("Lat:" + gps_coords.substring(0, commaIdx)); display.println("Lng:" + gps_coords.substring(commaIdx + 1)); lat = gps_coords.substring(0, commaIdx); lng = gps_coords.substring(commaIdx + 1); } else { display.println("Lat: -"); display.println("Lng: -"); } display.display(); // =============== SERIAL ================= Serial.println("===== STATUS UDARA ====="); Serial.print("CO2 : "); Serial.print(co2); Serial.println(" ppm"); Serial.print("CO : "); Serial.print(co); Serial.println(" ppm"); Serial.print("BZ : "); Serial.print(benzene); Serial.println(" ppm"); Serial.print("AQ : "); Serial.print(aq); Serial.print("% "); Serial.println(status); Serial.print("GPS : "); Serial.println(gps_coords); Serial.print("GPS status: "); Serial.println(gps_status); Serial.println("========================\n"); delay(5000); // ================================================= // HTTP REQUEST // ================================================= if (WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = String(server) + "/api/update?"; url += "co2=" + String(co2); url += "&co=" + String(co); url += "&bz=" + String(benzene); url += "&aq=" + String(aq); url += "&id=2"; url += "&lat=" + lat; url += "&lng=" + lng; Serial.print("[HTTP] GET "); Serial.println(url); oledWiFiStatus("MENGIRIM DATA..."); http.begin(url); int httpCode = http.GET(); if (httpCode > 0) { Serial.print("[HTTP] Response code: "); Serial.println(httpCode); String response = http.getString(); Serial.println("----- HTTP RESPONSE -----"); Serial.println(response); Serial.println("-------------------------"); int jsonStart = response.indexOf('{'); if (jsonStart >= 0) { String json = response.substring(jsonStart); Serial.print("[JSON] "); Serial.println(json); oledWiFiStatus("RESP OK"); } else { Serial.println("[ERROR] No JSON found!"); oledWiFiStatus("RESP ERROR"); } } else { Serial.print("[ERROR] HTTP failed: "); Serial.println(http.errorToString(httpCode)); oledWiFiStatus("GAGAL KIRIM"); } http.end(); } else { Serial.println("[ERROR] WiFi disconnected!"); oledWiFiStatus("WIFI PUTUS"); } Serial.println("===== LOOP END ====="); delay(10000); }