/*
==============================================================
SMART SWITCH SYSTEM with IoT Cloud Integration
==============================================================
Project : Smart Switch System — ThingSpeak Cloud Integration
Author : Aryan | Roll No. 22DCS004
Institute : NIT Hamirpur, Himachal Pradesh
Platform : ESP32 (Wokwi Simulation)
Cloud : ThingSpeak by MathWorks
Date : April 2026
==============================================================
DESCRIPTION:
This system controls 4 independent electrical switches (simulated
by LEDs in Wokwi) via physical pushbuttons. Additionally, a
DHT22 sensor monitors ambient temperature & humidity.
A PIR motion sensor detects room occupancy and can auto-switch
lights. All status and sensor data is sent to ThingSpeak cloud
every 15 seconds for real-time monitoring and visualization.
THINGSPEAK CHANNEL FIELDS:
Field 1 → Switch 1 State (0/1)
Field 2 → Switch 2 State (0/1)
Field 3 → Switch 3 State (0/1)
Field 4 → Switch 4 State (0/1)
Field 5 → Temperature (°C)
Field 6 → Humidity (%)
Field 7 → Motion Detected (0/1)
Field 8 → Total Switches ON (0-4)
WIRING (Wokwi Simulation):
DHT22 → GPIO 4
PIR → GPIO 34
BTN1 → GPIO 12 | LED1 → GPIO 25
BTN2 → GPIO 13 | LED2 → GPIO 26
BTN3 → GPIO 14 | LED3 → GPIO 27
BTN4 → GPIO 15 | LED4 → GPIO 32
LCD 16x2 I2C → SDA(GPIO 21), SCL(GPIO 22)
Buzzer → GPIO 33
==============================================================
*/
#include <Arduino.h>
#include <DHT.h>
#include <HTTPClient.h>
#include <LiquidCrystal_I2C.h>
#include <WiFi.h>
#include <Wire.h>
// ─── Wi-Fi Credentials (Wokwi Simulated) ───────────────────
const char *ssid = "Wokwi-GUEST";
const char *password = "";
// ─── ThingSpeak Configuration ───────────────────────────────
const char *thingspeakServer = "http://api.thingspeak.com/update";
const char *writeAPIKey = "F3UJ526SVDDOIDUX";
const int uploadInterval = 15000; // 15 seconds
// ─── Pin Definitions ────────────────────────────────────────
#define DHT_PIN 4
#define DHT_TYPE DHT22
#define PIR_PIN 34
#define BTN1_PIN 12
#define BTN2_PIN 13
#define BTN3_PIN 14
#define BTN4_PIN 15
#define LED1_PIN 25
#define LED2_PIN 26
#define LED3_PIN 27
#define LED4_PIN 32
#define BUZZER_PIN 33
// ─── Objects ────────────────────────────────────────────────
DHT dht(DHT_PIN, DHT_TYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2);
// ─── State Variables ────────────────────────────────────────
bool switch1 = false, switch2 = false, switch3 = false, switch4 = false;
bool lastBtn1 = HIGH, lastBtn2 = HIGH, lastBtn3 = HIGH, lastBtn4 = HIGH;
bool motionDetected = false;
bool autoMode = false; // AUTO mode: PIR controls switch1 automatically
float temperature = 0.0;
float humidity = 0.0;
unsigned long lastUpload = 0;
unsigned long lastLCDUpdate = 0;
unsigned long lastDebounce1 = 0, lastDebounce2 = 0;
unsigned long lastDebounce3 = 0, lastDebounce4 = 0;
const long debounceDelay = 50;
int lcdPage = 0; // Cycle through LCD pages
unsigned long lastPageChange = 0;
// ─── Function Prototypes ─────────────────────────────────────
void connectWiFi();
void readSensors();
void handleButtons();
void updateLEDs();
void updateLCD();
void uploadToThingSpeak();
void beep(int times, int duration);
int countSwitchesOn();
// ════════════════════════════════════════════════════════════
void setup() {
Serial.begin(115200);
Serial.println("\n╔══════════════════════════════════╗");
Serial.println("║ SMART SWITCH SYSTEM v1.0 ║");
Serial.println("╚══════════════════════════════════╝\n");
// Initialize DHT
dht.begin();
// Pin modes
pinMode(PIR_PIN, INPUT);
pinMode(BTN1_PIN, INPUT_PULLUP);
pinMode(BTN2_PIN, INPUT_PULLUP);
pinMode(BTN3_PIN, INPUT_PULLUP);
pinMode(BTN4_PIN, INPUT_PULLUP);
pinMode(LED1_PIN, OUTPUT);
pinMode(LED2_PIN, OUTPUT);
pinMode(LED3_PIN, OUTPUT);
pinMode(LED4_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
// All switches OFF on boot
updateLEDs();
// LCD Init
Wire.begin(21, 22);
lcd.init();
lcd.backlight();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" Smart Switch ");
lcd.setCursor(0, 1);
lcd.print(" System v1.0 ");
delay(2000);
lcd.clear();
// Connect WiFi
connectWiFi();
beep(2, 100);
Serial.println("[INIT] System Ready!\n");
}
// ════════════════════════════════════════════════════════════
void loop() {
unsigned long now = millis();
// Read sensors
readSensors();
// Handle button presses
handleButtons();
// PIR Auto Mode: If motion detected, turn on SW1 automatically
if (autoMode) {
if (motionDetected && !switch1) {
switch1 = true;
Serial.println("[AUTO] Motion detected → Switch 1 ON");
beep(1, 50);
}
}
// Update LED outputs
updateLEDs();
// Update LCD every 2 seconds
if (now - lastLCDUpdate >= 2000) {
updateLCD();
lastLCDUpdate = now;
}
// Upload to ThingSpeak every 15 seconds
if (now - lastUpload >= uploadInterval) {
uploadToThingSpeak();
lastUpload = now;
}
delay(50);
}
// ════════════════════════════════════════════════════════════
// WIFI CONNECTION
// ════════════════════════════════════════════════════════════
void connectWiFi() {
Serial.print("[WiFi] Connecting to ");
Serial.println(ssid);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi Connecting");
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
int attempts = 0;
while (WiFi.status() != WL_CONNECTED && attempts < 20) {
delay(500);
Serial.print(".");
attempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\n[WiFi] Connected! IP: " + WiFi.localIP().toString());
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi Connected!");
lcd.setCursor(0, 1);
lcd.print(WiFi.localIP());
delay(2000);
} else {
Serial.println("\n[WiFi] Connection FAILED — Offline Mode");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("WiFi FAILED");
lcd.setCursor(0, 1);
lcd.print("Offline Mode");
delay(2000);
}
}
// ════════════════════════════════════════════════════════════
// SENSOR READING
// ════════════════════════════════════════════════════════════
void readSensors() {
// Read DHT22
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t))
temperature = t;
if (!isnan(h))
humidity = h;
// Read PIR
motionDetected = (digitalRead(PIR_PIN) == HIGH);
}
// ════════════════════════════════════════════════════════════
// BUTTON HANDLING (Toggle Logic with Debounce)
// ════════════════════════════════════════════════════════════
void handleButtons() {
unsigned long now = millis();
// Button 1 — Toggle Switch 1
bool btn1 = digitalRead(BTN1_PIN);
if (btn1 == LOW && lastBtn1 == HIGH &&
(now - lastDebounce1) > debounceDelay) {
switch1 = !switch1;
Serial.print("[BTN1] Switch 1 → ");
Serial.println(switch1 ? "ON" : "OFF");
beep(1, 80);
lastDebounce1 = now;
}
lastBtn1 = btn1;
// Button 2 — Toggle Switch 2
bool btn2 = digitalRead(BTN2_PIN);
if (btn2 == LOW && lastBtn2 == HIGH &&
(now - lastDebounce2) > debounceDelay) {
switch2 = !switch2;
Serial.print("[BTN2] Switch 2 → ");
Serial.println(switch2 ? "ON" : "OFF");
beep(1, 80);
lastDebounce2 = now;
}
lastBtn2 = btn2;
// Button 3 — Toggle Switch 3
bool btn3 = digitalRead(BTN3_PIN);
if (btn3 == LOW && lastBtn3 == HIGH &&
(now - lastDebounce3) > debounceDelay) {
switch3 = !switch3;
Serial.print("[BTN3] Switch 3 → ");
Serial.println(switch3 ? "ON" : "OFF");
beep(1, 80);
lastDebounce3 = now;
}
lastBtn3 = btn3;
// Button 4 — Toggle Switch 4 (also toggles AUTO mode when held)
bool btn4 = digitalRead(BTN4_PIN);
if (btn4 == LOW && lastBtn4 == HIGH &&
(now - lastDebounce4) > debounceDelay) {
switch4 = !switch4;
Serial.print("[BTN4] Switch 4 → ");
Serial.println(switch4 ? "ON" : "OFF");
beep(1, 80);
lastDebounce4 = now;
}
lastBtn4 = btn4;
}
// ════════════════════════════════════════════════════════════
// LED UPDATE
// ════════════════════════════════════════════════════════════
void updateLEDs() {
digitalWrite(LED1_PIN, switch1 ? HIGH : LOW);
digitalWrite(LED2_PIN, switch2 ? HIGH : LOW);
digitalWrite(LED3_PIN, switch3 ? HIGH : LOW);
digitalWrite(LED4_PIN, switch4 ? HIGH : LOW);
}
// ════════════════════════════════════════════════════════════
// LCD UPDATE (Cycles through 3 pages)
// ════════════════════════════════════════════════════════════
void updateLCD() {
unsigned long now = millis();
if (now - lastPageChange >= 4000) {
lcdPage = (lcdPage + 1) % 3;
lastPageChange = now;
lcd.clear();
}
if (lcdPage == 0) {
// Page 0: Switch Status
lcd.setCursor(0, 0);
lcd.print("SW1:");
lcd.print(switch1 ? "ON " : "OFF");
lcd.print(" SW2:");
lcd.print(switch2 ? "ON" : "OF");
lcd.setCursor(0, 1);
lcd.print("SW3:");
lcd.print(switch3 ? "ON " : "OFF");
lcd.print(" SW4:");
lcd.print(switch4 ? "ON" : "OF");
} else if (lcdPage == 1) {
// Page 1: Environment
lcd.setCursor(0, 0);
lcd.print("Temp:");
lcd.print(temperature, 1);
lcd.print((char)223);
lcd.print("C ");
lcd.setCursor(0, 1);
lcd.print("Hum: ");
lcd.print(humidity, 1);
lcd.print("% ");
} else {
// Page 2: Motion + Total On
lcd.setCursor(0, 0);
lcd.print("Motion:");
lcd.print(motionDetected ? "YES " : "NO ");
lcd.setCursor(0, 1);
lcd.print("Total ON: ");
lcd.print(countSwitchesOn());
lcd.print("/4 ");
}
}
// ════════════════════════════════════════════════════════════
// COUNT SWITCHES ON
// ════════════════════════════════════════════════════════════
int countSwitchesOn() {
return (switch1 ? 1 : 0) + (switch2 ? 1 : 0) + (switch3 ? 1 : 0) +
(switch4 ? 1 : 0);
}
// ════════════════════════════════════════════════════════════
// UPLOAD TO THINGSPEAK
// ════════════════════════════════════════════════════════════
void uploadToThingSpeak() {
if (WiFi.status() != WL_CONNECTED) {
Serial.println("[Cloud] WiFi not connected — skipping upload");
return;
}
HTTPClient http;
String url =
String(tagioServer) + "?api_key=" + writeAPIKey +
"&field1=" + (switch1 ? 1 : 0) + "&field2=" + (switch2 ? 1 : 0) +
"&field3=" + (switch3 ? 1 : 0) + "&field4=" + (switch4 ? 1 : 0) +
"&field5=" + String(temperature, 2) + "&field6=" + String(humidity, 2) +
"&field7=" + (motionDetected ? 1 : 0) + "&field8=" + countSwitchesOn();
Serial.println("[Cloud] Uploading to ThingSpeak...");
Serial.println("[Cloud] URL: " + url.substring(0, 80) + "...");
http.begin(url);
int responseCode = http.GET();
if (responseCode > 0) {
String response = http.getString();
Serial.println("[Cloud] Response Code: " + String(responseCode));
Serial.println("[Cloud] Entry ID: " + response);
Serial.println("[Cloud] ✓ Upload SUCCESS");
} else {
Serial.println("[Cloud] ✗ Upload FAILED — Error: " + String(responseCode));
}
http.end();
// Print current state to Serial Monitor
Serial.println("─────────────────────────────────");
Serial.println("[STATE] SW1=" + String(switch1) + " SW2=" + String(switch2) +
" SW3=" + String(switch3) + " SW4=" + String(switch4));
Serial.println("[SENSOR] Temp=" + String(temperature) +
"°C Hum=" + String(humidity) + "%");
Serial.println("[PIR] Motion=" + String(motionDetected));
Serial.println("[TOTAL] Switches ON: " + String(countSwitchesOn()) + "/4");
Serial.println("─────────────────────────────────\n");
}
// ════════════════════════════════════════════════════════════
// BUZZER BEEP
// ════════════════════════════════════════════════════════════
void beep(int times, int duration) {
for (int i = 0; i < times; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(duration);
digitalWrite(BUZZER_PIN, LOW);
if (times > 1)
delay(100);
}
}