#include #include #include "DFRobot_ENS160.h" #include "Adafruit_AHTX0.h" // Wi-Fi credentials const char* ssid = "HickmanWiFi"; const char* password = "BlackBriar8787@@"; // I2C Communication setup DFRobot_ENS160_I2C ENS160(&Wire, 0x53); // ENS160 I2C address Adafruit_AHTX0 aht; // AHTX0 instance AsyncWebServer server(80); // Buffer to store historical data struct SensorData { time_t timestamp; // Use NTP time uint16_t tvoc; uint16_t eco2; uint8_t aqi; float temperature; float humidity; }; const int BUFFER_SIZE = 60; // Store 60 entries SensorData sensorBuffer[BUFFER_SIZE]; int bufferIndex = 0; // Function to sync time with NTP server void syncTime() { configTime(0, 0, "time.nist.gov", "pool.ntp.org"); Serial.println("Waiting for time synchronization..."); while (!time(nullptr)) { delay(1000); Serial.print("."); } Serial.println("\nTime synchronized!"); } // Function to format the time into a human-readable string String formatTime(time_t rawTime) { char buffer[30]; struct tm* timeInfo = localtime(&rawTime); strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", timeInfo); return String(buffer); } // Function to calibrate ENS160 using AHTX0 readings void calibrateENS160() { sensors_event_t tempEvent, humidityEvent; aht.getEvent(&humidityEvent, &tempEvent); float temperature = tempEvent.temperature; float humidity = humidityEvent.relative_humidity; ENS160.setTempAndHum(temperature, humidity); Serial.printf("Calibrated ENS160 with Temp: %.1f°C, Hum: %.1f%%\n", temperature, humidity); } void prepopulateBuffer() { sensors_event_t tempEvent, humidityEvent; aht.getEvent(&humidityEvent, &tempEvent); uint16_t TVOC = ENS160.getTVOC(); uint16_t ECO2 = ENS160.getECO2(); uint8_t AQI = ENS160.getAQI(); for (int i = 0; i < BUFFER_SIZE; i++) { sensorBuffer[i] = { time(nullptr), TVOC, ECO2, AQI, tempEvent.temperature, humidityEvent.relative_humidity }; } bufferIndex = BUFFER_SIZE; } void setup() { Serial.begin(115200); // Connect to Wi-Fi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to Wi-Fi..."); } Serial.println("Connected to Wi-Fi!"); Serial.print("ESP32 IP Address: "); Serial.println(WiFi.localIP()); // Sync time using NTP syncTime(); delay(1000); // Initialize AHTX0 sensor if (!aht.begin()) { Serial.println("AHTX0 initialization failed. Check the connection!"); while (1) delay(1000); } Serial.println("AHTX0 initialized successfully."); // Initialize ENS160 sensor while (NO_ERR != ENS160.begin()) { Serial.println("Communication with ENS160 failed. Please check connections."); delay(3000); } Serial.println("ENS160 initialized successfully."); ENS160.setPWRMode(ENS160_STANDARD_MODE); // Calibrate ENS160 with AHTX0 readings calibrateENS160(); // Prepopulate the buffer with initial readings prepopulateBuffer(); // Route for the main webpage server.on("/", HTTP_GET, [](AsyncWebServerRequest *request) { String html = R"rawliteral( Air Quality Monitoring

Air Quality Monitoring

Air Quality Index (AQI)

Loading...

Loading recommendation...

eCO2 (ppm)

Loading...

Loading recommendation...

TVOC (ppb)

Loading...

Loading recommendation...

Temperature (°C)

Loading...

Humidity (%)

Loading...

Real-Time Air Quality Graph
)rawliteral"; request->send(200, "text/html", html); }); server.on("/data", HTTP_GET, [](AsyncWebServerRequest *request) { uint16_t TVOC = ENS160.getTVOC(); uint16_t ECO2 = ENS160.getECO2(); uint8_t AQI = ENS160.getAQI(); sensors_event_t tempEvent, humidityEvent; aht.getEvent(&humidityEvent, &tempEvent); sensorBuffer[bufferIndex] = { time(nullptr), // Use current NTP time TVOC, ECO2, AQI, tempEvent.temperature, humidityEvent.relative_humidity }; bufferIndex = (bufferIndex + 1) % BUFFER_SIZE; String json = "{\"latest\":{"; json += "\"tvoc\":" + String(TVOC) + ","; json += "\"eco2\":" + String(ECO2) + ","; json += "\"aqi\":" + String(AQI) + ","; json += "\"temperature\":" + String(tempEvent.temperature) + ","; json += "\"humidity\":" + String(humidityEvent.relative_humidity); json += "},\"history\":{"; json += "\"tvoc\":["; for (int i = 0; i < BUFFER_SIZE; i++) { int index = (bufferIndex + i) % BUFFER_SIZE; if (sensorBuffer[index].timestamp == 0) continue; if (i > 0) json += ","; json += "[\"" + formatTime(sensorBuffer[index].timestamp) + "\"," + String(sensorBuffer[index].tvoc) + "]"; } json += "],\"eco2\":["; for (int i = 0; i < BUFFER_SIZE; i++) { int index = (bufferIndex + i) % BUFFER_SIZE; if (sensorBuffer[index].timestamp == 0) continue; if (i > 0) json += ","; json += "[\"" + formatTime(sensorBuffer[index].timestamp) + "\"," + String(sensorBuffer[index].eco2) + "]"; } json += "],\"temperature\":["; for (int i = 0; i < BUFFER_SIZE; i++) { int index = (bufferIndex + i) % BUFFER_SIZE; if (sensorBuffer[index].timestamp == 0) continue; if (i > 0) json += ","; json += "[\"" + formatTime(sensorBuffer[index].timestamp) + "\"," + String(sensorBuffer[index].temperature) + "]"; } json += "],\"humidity\":["; for (int i = 0; i < BUFFER_SIZE; i++) { int index = (bufferIndex + i) % BUFFER_SIZE; if (sensorBuffer[index].timestamp == 0) continue; if (i > 0) json += ","; json += "[\"" + formatTime(sensorBuffer[index].timestamp) + "\"," + String(sensorBuffer[index].humidity) + "]"; } json += "]}}"; request->send(200, "application/json", json); }); server.begin(); } void loop() { // Periodically calibrate the ENS160 sensor calibrateENS160(); // Delay for the main loop delay(15000); // Calibrate every 10 seconds }