Smart Weather Station
+ Auto Ventilation
A complete Arduino project — DHT22 sensor, servo vent, LCD display, and alarm system — simulated 100% in your browser using Wokwi. No hardware, no cost, no waiting.
What You'll Build & Learn
Module OverviewThe Smart Weather Station with Auto Ventilation is a real-time environmental monitoring system that automatically controls a servo vent based on temperature and humidity. You'll learn sensor reading, servo control, LCD display, alarm systems, and threshold-based automation — all inside Wokwi.
Sensor Reading
Read temp & humidity from DHT22 in real-time
Servo Control
Auto open/close vent based on temperature
LCD Display
Show live weather data on 16×2 I²C screen
Alarm System
Blink red LED + buzz at critical temperature
IoT Logic
Threshold-based decision making with millis()
Wokwi Sim
Test & debug the full circuit — free, online
Components Used
Step 1All components exist as virtual parts in Wokwi — no shopping, no soldering. Here's what each part does:
How to Set Up the Wokwi Simulation
Step 2 · Most ImportantWokwi is a FREE online Arduino simulator. Run this project at wokwi.com — no account needed to start, no software to install.
🌐 Open Wokwi in Your Browser
Go to wokwi.com in any modern browser (Chrome, Firefox, Edge).
- Click "New Project" on the homepage
- Select "Arduino UNO" from the board list
- You'll see a blank canvas with Arduino UNO already placed
Create a free Wokwi account to save your project and share it with your teacher using a link!
📋 Paste diagram.json — The Fastest Setup Method
Instead of adding components one by one, paste the complete circuit as a diagram.json file to set up all parts AND wires automatically.
- Click the "diagram.json" tab (next to sketch.ino)
- Select all existing text and delete it
- Copy and paste the complete diagram.json below
- Press Ctrl+S to save — the circuit appears automatically ✅
Copy the COMPLETE JSON — it starts with
{
"version": 1,
"author": "Smart Weather Station",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-arduino-uno", "id": "uno", "top": 200, "left": 180, "attrs": {} },
{ "type": "wokwi-dht22", "id": "dht1", "top": 0, "left": 0, "attrs": { "temperature": "-34.9" } },
{ "type": "wokwi-lcd1602", "id": "lcd1", "top": -147, "left": 390, "attrs": { "pins": "i2c" } },
{ "type": "wokwi-servo", "id": "sv1", "top": 0, "left": 450, "attrs": { "horn": "cross" } },
{ "type": "wokwi-led", "id": "ledG", "top": 420, "left": 80, "attrs": { "color": "green" } },
{ "type": "wokwi-led", "id": "ledY", "top": 420, "left": 130, "attrs": { "color": "yellow" } },
{ "type": "wokwi-led", "id": "ledR", "top": 420, "left": 180, "attrs": { "color": "red" } },
{ "type": "wokwi-resistor", "id": "rG", "top": 460, "left": 80, "attrs": { "value": "220" } },
{ "type": "wokwi-resistor", "id": "rY", "top": 460, "left": 130, "attrs": { "value": "220" } },
{ "type": "wokwi-resistor", "id": "rR", "top": 460, "left": 180, "attrs": { "value": "220" } },
{ "type": "wokwi-buzzer", "id": "bz1", "top": 420, "left": 280, "attrs": {} },
{ "type": "wokwi-pushbutton", "id": "btn1", "top": 420, "left": 370, "attrs": { "color": "blue" } }
],
"connections": [
[ "dht1:VCC", "uno:3.3V", "red", [] ],
[ "dht1:SDA", "uno:7", "green", [] ],
[ "dht1:GND", "uno:GND.1", "black", [] ],
[ "lcd1:VCC", "uno:5V", "red", [] ],
[ "lcd1:GND", "uno:GND.2", "black", [] ],
[ "lcd1:SDA", "uno:A4", "blue", [] ],
[ "lcd1:SCL", "uno:A5", "yellow", [] ],
[ "sv1:PWM", "uno:9", "orange", [] ],
[ "sv1:V+", "uno:5V", "red", [] ],
[ "sv1:GND", "uno:GND.3", "black", [] ],
[ "ledG:A", "rG:1", "green", [] ],
[ "rG:2", "uno:2", "green", [] ],
[ "ledG:C", "uno:GND.4", "black", [] ],
[ "ledY:A", "rY:1", "yellow", [] ],
[ "rY:2", "uno:3", "yellow", [] ],
[ "ledY:C", "uno:GND.5", "black", [] ],
[ "ledR:A", "rR:1", "red", [] ],
[ "rR:2", "uno:4", "red", [] ],
[ "ledR:C", "uno:GND.6", "black", [] ],
[ "bz1:1", "uno:5", "purple", [] ],
[ "bz1:2", "uno:GND.7", "black", [] ],
[ "btn1:1.l", "uno:6", "blue", [] ],
[ "btn1:2.l", "uno:GND.8", "black", [] ]
],
"dependencies": {}
}
📚 Install Libraries (One Click in Wokwi)
Create a file called
DHT sensor library
LiquidCrystal I2C
Servo
Wokwi auto-downloads these libraries when you start the simulation. No manual installation needed!
Circuit Wiring Reference
Step 3If you used diagram.json above, wiring is already done! This table is your reference if wiring manually.
| Component | Pin | Arduino | Wire |
|---|---|---|---|
| DHT22 | VCC | 3.3V | Red |
| DHT22 | SDA (Data) | Pin 7 | Green |
| DHT22 | GND | GND | Black |
| LCD 1602 | VCC | 5V | Red |
| LCD 1602 | SDA | A4 | Blue |
| LCD 1602 | SCL | A5 | Yellow |
| Servo Motor | PWM Signal | Pin 9 | Orange |
| Servo Motor | V+ Power | 5V | Red |
| LED Green → 220Ω | Anode (+) | Pin 2 | Green |
| LED Yellow → 220Ω | Anode (+) | Pin 3 | Yellow |
| LED Red → 220Ω | Anode (+) | Pin 4 | Red |
| Buzzer | Positive (+) | Pin 5 | Purple |
| Push Button | Pin 1.l | Pin 6 | Blue |
All LED cathodes (−) and GND pins connect to any GND on the Arduino. Use
The Arduino Code — Full & Explained
Step 4Click the sketch.ino tab in Wokwi, delete existing code, and paste the full sketch below.
① Libraries & Pin Definitions
// ============================================================
// SMART WEATHER STATION + AUTO VENTILATION
// Arduino UNO | Wokwi Simulation | MakeMindz Summer Course
// ============================================================
#include <DHT.h> // Temperature & humidity sensor library
#include <Wire.h> // I2C communication (for LCD)
#include <LiquidCrystal_I2C.h> // LCD display control
#include <Servo.h> // Servo motor control
// ── Pin definitions ──────────────────────────
#define DHT_PIN 7 // DHT22 data wire
#define DHT_TYPE DHT22
#define SERVO_PIN 9 // Servo PWM signal (~pin)
#define LED_GREEN 2 // Green status LED
#define LED_YELLOW 3 // Yellow status LED
#define LED_RED 4 // Red alarm LED
#define BUZZER_PIN 5 // Buzzer
#define BTN_PIN 6 // Push button for LCD screens
// ── Library objects ──────────────────────────
DHT dht(DHT_PIN, DHT_TYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD at I2C address 0x27
Servo ventServo;
// ── State variables ──────────────────────────
float tempC = 0, humidity = 0, heatIndex = 0;
int ventAngle = 0, screen = 0;
bool alarmOn = false, blinkState = false, prevBtn = HIGH;
unsigned long lastRead = 0, lastBlink = 0, lastBuzz = 0, dbBtn = 0;
② The setup() Function — Runs Once on Boot
void setup() {
dht.begin();
lcd.init();
lcd.backlight();
ventServo.attach(SERVO_PIN);
ventServo.write(0); // Start with vent fully CLOSED
pinMode(LED_GREEN, OUTPUT);
pinMode(LED_YELLOW, OUTPUT);
pinMode(LED_RED, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BTN_PIN, INPUT_PULLUP); // Uses internal pull-up resistor
// Splash screen for 1.8 seconds
lcd.setCursor(2, 0); lcd.print("WeatherStation");
lcd.setCursor(3, 1); lcd.print("Auto Vent v1");
delay(1800);
lcd.clear();
// Boot LED test — light each LED one by one
digitalWrite(LED_GREEN, HIGH); delay(200);
digitalWrite(LED_YELLOW, HIGH); delay(200);
digitalWrite(LED_RED, HIGH); delay(200);
digitalWrite(LED_GREEN, LOW);
digitalWrite(LED_YELLOW, LOW);
digitalWrite(LED_RED, LOW);
}
③ The loop() Function — Heart of the System
void loop() {
unsigned long now = millis();
// ── Read sensor every 2 seconds (non-blocking) ──
if (now - lastRead >= 2000) {
lastRead = now;
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t) && !isnan(h)) {
tempC = t; humidity = h;
heatIndex = dht.computeHeatIndex(t, h, false);
}
// ── Smooth servo movement (5° per step) ──
int targetAngle = tempToVent(tempC);
if (ventAngle < targetAngle) ventAngle = min(ventAngle + 5, targetAngle);
else if (ventAngle > targetAngle) ventAngle = max(ventAngle - 5, targetAngle);
ventServo.write(ventAngle);
// ── Update LEDs ──
if (tempC < 28) {
digitalWrite(LED_GREEN, HIGH); digitalWrite(LED_YELLOW, LOW); digitalWrite(LED_RED, LOW);
alarmOn = false;
} else if (tempC < 35) {
digitalWrite(LED_GREEN, LOW); digitalWrite(LED_YELLOW, HIGH); digitalWrite(LED_RED, LOW);
alarmOn = false;
} else {
digitalWrite(LED_GREEN, LOW); digitalWrite(LED_YELLOW, LOW);
alarmOn = true;
}
// ── Refresh LCD ──
lcd.clear();
switch (screen) {
case 0: showScreenTemp(); break;
case 1: showScreenHeatIdx(); break;
case 2: showScreenVent(); break;
}
}
// ── Blink red LED every 300ms when alarm ──
if (alarmOn && now - lastBlink >= 300) {
lastBlink = now; blinkState = !blinkState;
digitalWrite(LED_RED, blinkState ? HIGH : LOW);
}
// ── Sound buzzer every 800ms when alarm ──
if (alarmOn && now - lastBuzz >= 800) {
lastBuzz = now;
tone(BUZZER_PIN, 1200, 120); // 1200Hz for 120ms
}
// ── Button: cycle LCD screens with debounce ──
bool btn = digitalRead(BTN_PIN);
if (prevBtn == HIGH && btn == LOW && now - dbBtn > 200) {
screen = (screen + 1) % 3; // Cycle: 0 → 1 → 2 → 0
dbBtn = now; lcd.clear();
}
prevBtn = btn;
delay(20);
}
④ Helper Functions — Vent Control + LCD Screens
// Maps temperature to vent angle using proportional control
int tempToVent(float t) {
if (t < 25.0) return 0; // Cool → CLOSED
if (t < 30.0) return map(t*10, 250, 300, 0, 60); // Warm → partial
if (t < 38.0) return map(t*10, 300, 380, 60, 160); // Hot → wide open
return 170; // Critical → FULLY open
}
// Screen 0: Temperature + Humidity bar
void showScreenTemp() {
lcd.setCursor(0, 0); lcd.print("Temp: "); lcd.print(tempC, 1); lcd.print("\xDFC");
lcd.setCursor(0, 1); lcd.print("Hum: "); lcd.print((int)humidity); lcd.print("%");
}
// Screen 1: Heat Index + comfort level
void showScreenHeatIdx() {
lcd.setCursor(0, 0); lcd.print("Heat Index:");
lcd.setCursor(0, 1);
if (heatIndex < 27) lcd.print("Comfort ");
else if (heatIndex < 32) lcd.print("Caution ");
else if (heatIndex < 41) lcd.print("Warning! ");
else lcd.print("DANGER!! ");
lcd.print(heatIndex, 1); lcd.print("\xDFC");
}
// Screen 2: Vent status + progress bar
void showScreenVent() {
int pct = map(ventAngle, 0, 170, 0, 100);
lcd.setCursor(0, 0); lcd.print("Vent: "); lcd.print(pct); lcd.print("% ");
lcd.setCursor(0, 1); lcd.print("[");
for (int i = 0; i < 12; i++) lcd.print(i < pct/9 ? "#" : "-");
lcd.print("]");
}
How the Automation Logic Works
Key ConceptThis project uses threshold-based decision making — the same logic used in real IoT and smart building systems.
🔑 Why Use millis() Instead of delay()?
This is one of the most important concepts in Arduino programming:
- delay(2000) — FREEZES the entire program for 2 seconds. Button presses are missed. LEDs can't blink. Servo can't respond.
- millis() — The program keeps running and simply CHECKS if 2 seconds have passed. Everything works in parallel!
This is called Non-Blocking Code — it's how professional Arduino programs are written. Always prefer
Run & Test Your Simulation
The Fun Part!Press the Green Play Button
In Wokwi, click the big green ▶ Play button. You should see:
- LCD shows "WeatherStation / Auto Vent v1" for 1.8 seconds
- All 3 LEDs flash one by one (boot test)
- LCD switches to live temperature and humidity
If you see a compile error, check that your
Change the Temperature Slider
Click the DHT22 sensor in the simulation — a popup shows temperature and humidity sliders. Try:
- Set to 20°C → Green LED lights, servo stays at 0° (closed)
- Set to 28°C → Yellow LED lights, servo starts opening
- Set to 36°C → Red LED blinks fast, buzzer sounds, servo fully open!
- Drag back to 20°C → Watch the servo smoothly close back down
The servo moves smoothly (5° per step) rather than jumping instantly — that's the smoothing code in
Click the Blue Button to Cycle Screens
Click the blue push button in the simulation to switch between 3 LCD displays:
- Screen 0: Temperature + Humidity percentage
- Screen 1: Heat Index + comfort level (Comfort / Caution / Warning / DANGER)
- Screen 2: Vent open percentage + progress bar
Open the Full Simulation on Wokwi
Run this entire project in your browser — no sign-up required to view. Click "Copy and Tinker" to make your own editable version.
Open Wokwi SimulationKnowledge Check Quiz
Test Yourself!Answer these 5 questions to check your understanding before the next module.
.png)
Comments
Post a Comment