Weather Station: Complete IoT Project
Complete Weather Station Project
This project combines all the concepts from previous tutorials to build a complete IoT weather station with cloud integration, real-time monitoring, and historical data analysis.
Project Scope: Build a weather station that measures temperature, humidity, pressure, and light intensity, stores data in the cloud, and provides real-time visualization through a web dashboard.
Learning Objectives:
- Integrate multiple sensors into a single system
- Implement cloud connectivity
- Create data visualization dashboards
- Handle data storage and retrieval
- Deploy production-ready IoT applications
System Architecture Overview
Complete System Architecture:
Layer 1 (Edge): ESP32 + Sensors (DHT22, BMP280, LDR)
Layer 2 (Gateway): WiFi connectivity + MQTT
Layer 3 (Cloud): InfluxDB (time-series DB) + Node.js API
Layer 4 (Frontend): React Dashboard + Grafana
Data Flow:
| Component | Function | Update Rate |
|---|---|---|
| DHT22 | Temperature & Humidity | Every 30s |
| BMP280 | Pressure & Altitude | Every 30s |
| LDR | Light Intensity | Every 10s |
| InfluxDB | Data Storage | Real-time |
| Dashboard | Visualization | WebSocket (live) |
Hardware Assembly
Required Components:
- ESP32 (main microcontroller)
- DHT22 (temperature & humidity)
- BMP280 (pressure & altitude)
- LDR + 10k ohm resistor (light sensor)
- 0.96" OLED Display (local display)
- Solar Panel 5V (optional, for outdoor use)
- 18650 Battery (power backup)
- TP4056 Charging Module (battery charging)
- Weatherproof Enclosure (outdoor housing)
Wiring Diagram:
ESP32 3.3V -> DHT22 VCC, BMP280 VCC, LDR circuit
ESP32 GPIO 21 (SDA) -> DHT22 Data, BMP280 SDA, OLED SDA
ESP32 GPIO 22 (SCL) -> BMP280 SCL, OLED SCL
ESP32 GPIO 34 (ADC0) -> LDR + 10k divider
All GND pins connected to common ground
Power Considerations: When using a battery, ensure proper voltage regulation (min 3V, max 3.6V for ESP32).
ESP32 Firmware Development
Complete Arduino Sketch:
#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>
#include <Adafruit_BMP280.h>
#include <Wire.h>
// WiFi credentials
const char* ssid = "YOUR_SSID";
const char* password = "YOUR_PASSWORD";
// MQTT Server
const char* mqtt_server = "192.168.1.100";
const char* mqtt_user = "weather";
const char* mqtt_pass = "password123";
WiFiClient espClient;
PubSubClient client(espClient);
// Sensors
DHT dht(21, DHT22);
Adafruit_BMP280 bmp280;
const int ldrPin = 34;
unsigned long lastSensorRead = 0;
const long sensorReadInterval = 30000; // 30 seconds
void setup() {
Serial.begin(115200);
// Initialize sensors
dht.begin();
if (!bmp280.begin(0x76)) {
Serial.println("BMP280 not found!");
}
// Connect to WiFi
connectWiFi();
client.setServer(mqtt_server, MQTT_PORT);
}
void connectWiFi() {
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("WiFi connected!");
}
void reconnect() {
while (!client.connected()) {
if (client.connect("WeatherStation", mqtt_user, mqtt_pass)) {
Serial.println("MQTT connected");
} else {
delay(5000);
}
}
}
void publishSensorData() {
// Read temperature and humidity
float temp = dht.readTemperature();
float humidity = dht.readHumidity();
// Read pressure
float pressure = bmp280.readPressure() / 100.0;
// Read light intensity
int lightRaw = analogRead(ldrPin);
float lightPercent = (lightRaw / 4095.0) * 100;
// Publish to MQTT
char buffer[50];
snprintf(buffer, sizeof(buffer), "%.2f", temp);
client.publish("weather/temperature", buffer);
snprintf(buffer, sizeof(buffer), "%.2f", humidity);
client.publish("weather/humidity", buffer);
snprintf(buffer, sizeof(buffer), "%.2f", pressure);
client.publish("weather/pressure", buffer);
snprintf(buffer, sizeof(buffer), "%.1f", lightPercent);
client.publish("weather/light", buffer);
Serial.printf("T:%.2f H:%.2f P:%.2f L:%.1f\n",
temp, humidity, pressure, lightPercent);
}
void loop() {
if (!client.connected()) {
reconnect();
}
client.loop();
unsigned long currentTime = millis();
if (currentTime - lastSensorRead >= sensorReadInterval) {
publishSensorData();
lastSensorRead = currentTime;
}
}
Cloud Integration with InfluxDB
Setup InfluxDB:
# Install InfluxDB
wget https://dl.influxdata.com/influxdb/releases/influxdb_1.8.0_amd64.deb
sudo dpkg -i influxdb_1.8.0_amd64.deb
sudo systemctl start influxdb
# Create database
influx
> CREATE DATABASE weather_data
> CREATE USER weather WITH PASSWORD 'weather123'
> GRANT ALL ON weather_data TO weather
> EXIT
Node.js Backend to Store Data:
const mqtt = require('mqtt');
const influx = require('influx');
const client = mqtt.connect('mqtt://localhost:1883', {
username: 'weather',
password: 'password123'
});
const influxDB = new influx.InfluxDB({
host: 'localhost',
database: 'weather_data',
username: 'weather',
password: 'weather123',
schema: [
{
measurement: 'weather',
fields: {
temperature: influx.FieldType.FLOAT,
humidity: influx.FieldType.FLOAT,
pressure: influx.FieldType.FLOAT,
light: influx.FieldType.FLOAT
},
tags: ['location']
}
]
});
client.on('message', (topic, message) => {
const topicParts = topic.split('/');
const measurementType = topicParts[1];
const value = parseFloat(message.toString());
// Store in InfluxDB
influxDB.writePoints([{
measurement: 'weather',
tags: { location: 'outside' },
fields: {
[measurementType]: value
}
}]);
});
client.subscribe('weather/#');
Real-Time Dashboard
Express API to Query Data:
const express = require('express');
const app = express();
app.get('/api/current', async (req, res) => {
const results = await influxDB.query(`
SELECT LAST(*) FROM weather
`);
res.json(results[0]);
});
app.get('/api/history', async (req, res) => {
const results = await influxDB.query(`
SELECT * FROM weather WHERE time > now() - 24h
`);
res.json(results);
});
app.listen(3000, () => {
console.log('API server running on port 3000');
});
React Dashboard Component:
import React, { useState, useEffect } from 'react';
import { LineChart, Line, XAxis, YAxis } from 'recharts';
function WeatherDashboard() {
const [current, setCurrent] = useState({});
const [history, setHistory] = useState([]);
useEffect(() => {
// Fetch current data
fetch('/api/current')
.then(r => r.json())
.then(data => setCurrent(data[0]));
// Fetch historical data
fetch('/api/history')
.then(r => r.json())
.then(data => setHistory(data[0]));
}, []);
return (
<div style={{padding: '20px'}}>
<h1>Weather Station Dashboard</h1>
<div>
<p>Temperature: {current.temperature}°C</p>
<p>Humidity: {current.humidity}%</p>
<p>Pressure: {current.pressure} mb</p>
<p>Light: {current.light}%</p>
</div>
<LineChart width={800} height={300} data={history}>
<XAxis dataKey="time" />
<YAxis />
<Line type="monotone" dataKey="temperature" stroke="#ff7300" />
</LineChart>
</div>
);
}
export default WeatherDashboard;
Deployment & Testing
Pre-Deployment Checklist:
- ✓ Test all sensors individually
- ✓ Verify WiFi connectivity
- ✓ Test MQTT communication
- ✓ Verify InfluxDB data storage
- ✓ Test dashboard connectivity
- ✓ Check power consumption
- ✓ Test in outdoor conditions
Outdoor Installation:
1. Place in weather-protected enclosure (allow air circulation)
2. Ensure proper sensor orientation (avoid direct sun on temp sensor)
3. Mount on pole or elevated surface (away from obstacles)
4. Verify WiFi signal strength (minimum -70 dBm)
5. Set up automatic data logging and archiving
6. Configure alerts for extreme weather conditions
Performance Metrics:
| Metric | Expected Value | Acceptable Range |
|---|---|---|
| Update Frequency | 30 seconds | 20-60 seconds |
| Data Accuracy | ±0.5°C | ±1°C |
| Uptime | 99.5% | >95% |
| Power Consumption | 50-100 mW | <200 mW |
Congratulations! You've built a complete production-ready IoT weather station! Next steps: Add more sensors, implement machine learning for predictions, or deploy multiple stations for regional monitoring.