/*
* ESP32 IoT Weather Station with Cloud Integration
* Features:
* - DHT22 Temperature & Humidity Sensor
* - BMP280 Pressure & Altitude Sensor
* - LDR Light Sensor
* - Rain Detection Sensor
* - OLED Display for local readings
* - Web Dashboard
* - ThingSpeak Cloud Integration
* - Data Logging & Visualization
* - Real-time Updates
*/
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_BMP280.h>
// WiFi Credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// ThingSpeak Settings
const char* thingSpeakServer = "api.thingspeak.com";
String thingSpeakAPIKey = "01PCHXCFEQN5RXS5"; // Replace with your ThingSpeak Write API Key
const long thingSpeakChannel = 3254641; // Replace with your channel number
// Pin Definitions
#define DHT_PIN 15
#define LDR_PIN 34 // Analog pin for light sensor
#define RAIN_PIN 35 // Analog pin for rain sensor
#define LED_PIN 2 // Built-in LED for status
// DHT Sensor Setup
#define DHT_TYPE DHT22
DHT dht(DHT_PIN, DHT_TYPE);
// BMP280 Pressure Sensor Setup
Adafruit_BMP280 bmp;
// OLED Display Setup
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Web Server
WebServer server(80);
// Weather Data Structure
struct WeatherData {
float temperature;
float humidity;
float pressure;
float altitude;
int lightLevel;
int rainLevel;
String weatherCondition;
unsigned long timestamp;
};
WeatherData currentWeather;
// Timing variables
unsigned long lastSensorRead = 0;
unsigned long lastCloudUpload = 0;
const long sensorInterval = 2000; // Read sensors every 2 seconds
const long cloudUploadInterval = 20000; // Upload to cloud every 20 seconds
// Data arrays for graphing
const int maxDataPoints = 50;
float tempHistory[50];
float humidityHistory[50];
int dataIndex = 0;
// Status variables
bool cloudConnected = false;
int uploadCount = 0;
void setup() {
Serial.begin(115200);
// Initialize pins
pinMode(LED_PIN, OUTPUT);
pinMode(LDR_PIN, INPUT);
pinMode(RAIN_PIN, INPUT);
// Initialize DHT sensor
dht.begin();
// Initialize BMP280
if (!bmp.begin(0x76)) {
Serial.println("Could not find BMP280 sensor!");
} else {
Serial.println("BMP280 initialized successfully");
// Configure BMP280
bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,
Adafruit_BMP280::SAMPLING_X2,
Adafruit_BMP280::SAMPLING_X16,
Adafruit_BMP280::FILTER_X16,
Adafruit_BMP280::STANDBY_MS_500);
}
// Initialize OLED display
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed"));
} else {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Weather Station");
display.println("Initializing...");
display.display();
delay(2000);
}
// Connect to WiFi
connectWiFi();
// Setup web server routes
server.on("/", handleRoot);
server.on("/data", handleData);
server.on("/history", handleHistory);
server.on("/api/weather", handleAPIWeather);
server.begin();
Serial.println("HTTP server started");
// Initial sensor read
readSensors();
updateDisplay();
Serial.println("\n=== Weather Station Ready ===");
Serial.println("Features:");
Serial.println("- Local Web Dashboard");
Serial.println("- Cloud Data Logging");
Serial.println("- Real-time Monitoring");
Serial.println("=============================\n");
}
void loop() {
server.handleClient();
unsigned long currentMillis = millis();
// Read sensors periodically
if (currentMillis - lastSensorRead >= sensorInterval) {
lastSensorRead = currentMillis;
readSensors();
updateDisplay();
blinkLED();
}
// Upload to cloud periodically
if (currentMillis - lastCloudUpload >= cloudUploadInterval) {
lastCloudUpload = currentMillis;
uploadToCloud();
}
}
void connectWiFi() {
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi");
display.clearDisplay();
display.setCursor(0, 0);
display.println("Connecting WiFi...");
display.display();
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
display.clearDisplay();
display.setCursor(0, 0);
display.println("WiFi Connected!");
display.print("IP: ");
display.println(WiFi.localIP());
display.display();
delay(2000);
} else {
Serial.println("\nWiFi connection failed");
}
}
void readSensors() {
// Read DHT22
currentWeather.temperature = dht.readTemperature();
currentWeather.humidity = dht.readHumidity();
// Read BMP280
currentWeather.pressure = bmp.readPressure() / 100.0F; // Convert to hPa
currentWeather.altitude = bmp.readAltitude(1013.25); // Sea level pressure
// Read LDR (Light sensor)
int ldrValue = analogRead(LDR_PIN);
currentWeather.lightLevel = map(ldrValue, 0, 4095, 0, 100);
// Read Rain sensor
int rainValue = analogRead(RAIN_PIN);
currentWeather.rainLevel = map(rainValue, 0, 4095, 100, 0); // Inverted
// Timestamp
currentWeather.timestamp = millis();
// Determine weather condition
currentWeather.weatherCondition = determineWeatherCondition();
// Check for errors
if (isnan(currentWeather.temperature) || isnan(currentWeather.humidity)) {
Serial.println("Failed to read from DHT sensor!");
currentWeather.temperature = 0;
currentWeather.humidity = 0;
}
// Store in history
tempHistory[dataIndex] = currentWeather.temperature;
humidityHistory[dataIndex] = currentWeather.humidity;
dataIndex = (dataIndex + 1) % maxDataPoints;
// Print to Serial
printWeatherData();
}
String determineWeatherCondition() {
String condition = "Unknown";
if (currentWeather.rainLevel > 60) {
condition = "Rainy";
} else if (currentWeather.rainLevel > 30) {
condition = "Drizzle";
} else if (currentWeather.humidity > 80) {
condition = "Humid";
} else if (currentWeather.lightLevel < 20) {
condition = "Cloudy";
} else if (currentWeather.lightLevel > 80) {
condition = "Sunny";
} else if (currentWeather.temperature > 30) {
condition = "Hot";
} else if (currentWeather.temperature < 15) {
condition = "Cold";
} else {
condition = "Clear";
}
return condition;
}
void printWeatherData() {
Serial.println("\n=== Weather Data ===");
Serial.print("Temperature: "); Serial.print(currentWeather.temperature); Serial.println(" °C");
Serial.print("Humidity: "); Serial.print(currentWeather.humidity); Serial.println(" %");
Serial.print("Pressure: "); Serial.print(currentWeather.pressure); Serial.println(" hPa");
Serial.print("Altitude: "); Serial.print(currentWeather.altitude); Serial.println(" m");
Serial.print("Light Level: "); Serial.print(currentWeather.lightLevel); Serial.println(" %");
Serial.print("Rain Level: "); Serial.print(currentWeather.rainLevel); Serial.println(" %");
Serial.print("Condition: "); Serial.println(currentWeather.weatherCondition);
Serial.println("===================\n");
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setCursor(0, 0);
// Title
display.setTextSize(1);
display.println("WEATHER STATION");
display.drawLine(0, 9, 128, 9, SSD1306_WHITE);
// Temperature
display.setCursor(0, 12);
display.print("Temp: ");
display.print(currentWeather.temperature, 1);
display.println(" C");
// Humidity
display.setCursor(0, 22);
display.print("Humid: ");
display.print(currentWeather.humidity, 1);
display.println(" %");
// Pressure
display.setCursor(0, 32);
display.print("Press: ");
display.print(currentWeather.pressure, 0);
display.println("hPa");
// Light & Rain
display.setCursor(0, 42);
display.print("Light: ");
display.print(currentWeather.lightLevel);
display.print("% ");
// Condition
display.setCursor(0, 52);
display.setTextSize(1);
display.print("Status: ");
display.println(currentWeather.weatherCondition);
display.display();
}
void uploadToCloud() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi not connected. Skipping cloud upload.");
cloudConnected = false;
return;
}
HTTPClient http;
// Build ThingSpeak URL
String url = "http://" + String(thingSpeakServer) + "/update?api_key=" + thingSpeakAPIKey;
url += "&field1=" + String(currentWeather.temperature);
url += "&field2=" + String(currentWeather.humidity);
url += "&field3=" + String(currentWeather.pressure);
url += "&field4=" + String(currentWeather.lightLevel);
url += "&field5=" + String(currentWeather.rainLevel);
url += "&field6=" + String(currentWeather.altitude);
Serial.println("Uploading to ThingSpeak...");
Serial.println(url);
http.begin(url);
int httpCode = http.GET();
if (httpCode > 0) {
String payload = http.getString();
Serial.print("HTTP Response: ");
Serial.println(httpCode);
Serial.print("Payload: ");
Serial.println(payload);
if (httpCode == 200) {
cloudConnected = true;
uploadCount++;
Serial.println("✓ Data uploaded successfully to cloud!");
Serial.print("Total uploads: ");
Serial.println(uploadCount);
}
} else {
Serial.print("HTTP Error: ");
Serial.println(http.errorToString(httpCode));
cloudConnected = false;
}
http.end();
}
void blinkLED() {
digitalWrite(LED_PIN, HIGH);
delay(50);
digitalWrite(LED_PIN, LOW);
}
// Web Server Handlers
void handleRoot() {
String html = "<!DOCTYPE html><html><head>";
html += "<meta name='viewport' content='width=device-width, initial-scale=1.0'>";
html += "<meta http-equiv='refresh' content='5'>"; // Auto refresh every 5 seconds
html += "<style>";
html += "body { font-family: Arial; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); margin: 0; padding: 20px; color: white; }";
html += ".container { max-width: 900px; margin: 0 auto; }";
html += "h1 { text-align: center; font-size: 2.5em; margin-bottom: 10px; text-shadow: 2px 2px 4px rgba(0,0,0,0.3); }";
html += ".subtitle { text-align: center; font-size: 1.2em; margin-bottom: 30px; opacity: 0.9; }";
html += ".grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px; margin-bottom: 20px; }";
html += ".card { background: rgba(255,255,255,0.15); backdrop-filter: blur(10px); padding: 20px; border-radius: 15px; box-shadow: 0 8px 32px rgba(0,0,0,0.1); border: 1px solid rgba(255,255,255,0.18); }";
html += ".card h3 { margin: 0 0 10px 0; font-size: 1.1em; opacity: 0.8; }";
html += ".value { font-size: 2.5em; font-weight: bold; margin: 10px 0; }";
html += ".unit { font-size: 0.6em; opacity: 0.8; }";
html += ".icon { font-size: 2em; margin-bottom: 10px; }";
html += ".status { text-align: center; background: rgba(255,255,255,0.2); padding: 15px; border-radius: 10px; margin-top: 20px; }";
html += ".cloud-status { display: inline-block; padding: 5px 15px; border-radius: 20px; margin: 10px; font-size: 0.9em; }";
html += ".connected { background: #4CAF50; }";
html += ".disconnected { background: #f44336; }";
html += ".weather-condition { font-size: 1.5em; margin: 10px 0; }";
html += "@media (max-width: 600px) { .grid { grid-template-columns: 1fr; } }";
html += "</style></head><body>";
html += "<div class='container'>";
html += "<h1>🌤️ IoT Weather Station</h1>";
html += "<div class='subtitle'>Real-time Environmental Monitoring</div>";
// Weather Condition Card
html += "<div class='card' style='text-align: center; background: rgba(255,255,255,0.25);'>";
html += "<div class='weather-condition'>" + getWeatherIcon() + " " + currentWeather.weatherCondition + "</div>";
html += "</div>";
// Sensor Grid
html += "<div class='grid'>";
// Temperature
html += "<div class='card'>";
html += "<div class='icon'>🌡️</div>";
html += "<h3>Temperature</h3>";
html += "<div class='value'>" + String(currentWeather.temperature, 1) + "<span class='unit'>°C</span></div>";
html += "</div>";
// Humidity
html += "<div class='card'>";
html += "<div class='icon'>💧</div>";
html += "<h3>Humidity</h3>";
html += "<div class='value'>" + String(currentWeather.humidity, 1) + "<span class='unit'>%</span></div>";
html += "</div>";
// Pressure
html += "<div class='card'>";
html += "<div class='icon'>🎚️</div>";
html += "<h3>Pressure</h3>";
html += "<div class='value'>" + String(currentWeather.pressure, 0) + "<span class='unit'>hPa</span></div>";
html += "</div>";
// Altitude
html += "<div class='card'>";
html += "<div class='icon'>⛰️</div>";
html += "<h3>Altitude</h3>";
html += "<div class='value'>" + String(currentWeather.altitude, 0) + "<span class='unit'>m</span></div>";
html += "</div>";
// Light
html += "<div class='card'>";
html += "<div class='icon'>☀️</div>";
html += "<h3>Light Level</h3>";
html += "<div class='value'>" + String(currentWeather.lightLevel) + "<span class='unit'>%</span></div>";
html += "</div>";
// Rain
html += "<div class='card'>";
html += "<div class='icon'>🌧️</div>";
html += "<h3>Rain Level</h3>";
html += "<div class='value'>" + String(currentWeather.rainLevel) + "<span class='unit'>%</span></div>";
html += "</div>";
html += "</div>";
// Status
html += "<div class='status'>";
html += "<p><strong>Station Status</strong></p>";
html += "<span class='cloud-status " + String(cloudConnected ? "connected" : "disconnected") + "'>";
html += cloudConnected ? "☁️ Cloud Connected" : "☁️ Cloud Offline";
html += "</span>";
html += "<span class='cloud-status connected'>📡 WiFi: " + WiFi.localIP().toString() + "</span>";
html += "<p style='margin-top: 10px; font-size: 0.9em;'>Uploads: " + String(uploadCount) + " | Uptime: " + String(millis()/1000) + "s</p>";
html += "</div>";
html += "</div></body></html>";
server.send(200, "text/html", html);
}
String getWeatherIcon() {
if (currentWeather.weatherCondition == "Rainy") return "🌧️";
if (currentWeather.weatherCondition == "Drizzle") return "🌦️";
if (currentWeather.weatherCondition == "Sunny") return "☀️";
if (currentWeather.weatherCondition == "Cloudy") return "☁️";
if (currentWeather.weatherCondition == "Hot") return "🔥";
if (currentWeather.weatherCondition == "Cold") return "❄️";
return "🌤️";
}
void handleData() {
String json = "{";
json += "\"temperature\":" + String(currentWeather.temperature, 2) + ",";
json += "\"humidity\":" + String(currentWeather.humidity, 2) + ",";
json += "\"pressure\":" + String(currentWeather.pressure, 2) + ",";
json += "\"altitude\":" + String(currentWeather.altitude, 2) + ",";
json += "\"light\":" + String(currentWeather.lightLevel) + ",";
json += "\"rain\":" + String(currentWeather.rainLevel) + ",";
json += "\"condition\":\"" + currentWeather.weatherCondition + "\",";
json += "\"timestamp\":" + String(currentWeather.timestamp) + ",";
json += "\"cloudConnected\":" + String(cloudConnected ? "true" : "false");
json += "}";
server.send(200, "application/json", json);
}
void handleHistory() {
String json = "{\"temperature\":[";
for (int i = 0; i < maxDataPoints; i++) {
json += String(tempHistory[i], 1);
if (i < maxDataPoints - 1) json += ",";
}
json += "],\"humidity\":[";
for (int i = 0; i < maxDataPoints; i++) {
json += String(humidityHistory[i], 1);
if (i < maxDataPoints - 1) json += ",";
}
json += "]}";
server.send(200, "application/json", json);
}
void handleAPIWeather() {
handleData(); // Same as /data endpoint
}
Comments
Post a Comment