8 Channel Relay Control With Timer
What is Home Automation System
A Home Automation System is a technology that allows you to automatically control household devices and systems β such as lights, fans, air conditioners, security cameras, and doors β using a smartphone, computer, or voice commands.
Β
Simple Definition:
Home automation means making your home βsmartβ by connecting devices to a network so they can be controlled remotely or work automatically without manual effort.
Β
Main Components:
- Sensors β Detect changes (like motion, light, or temperature).
- Controllers β Devices or apps (like a smartphone or smart hub) used to send commands.
- Actuators β Devices (like motors, switches, or relays) that carry out the actions.
Β
Common Functions:
- Lighting control β Lights turn on/off automatically.
- Temperature control β Smart thermostats adjust room temperature.
- Security β Cameras, smart locks, and alarms protect the home.
- Appliance control β Turn appliances on or off remotely.
Examples of Use:
- Turning on lights with your phone.
- Automatically locking doors when you leave.
- Scheduling the air conditioner to turn off at night.
Β
Advantages:
- Convenience and comfort
- Energy savings
- Improved safety and security
- Remote access and contr
8 Channel Relay With Timer Projects Programing Code
Projects Cost Rs 65,200/ (Developer Saikat Biswas)
#include
#include
#include
#include
#include
// --- 8-Channel Relay and Schedule Definitions ---
#define MAX_RELAYS 8
#define MAX_SCHEDULES 8
#define MAX_HISTORY 20
const int RELAY_PINS[MAX_RELAYS] = {2, 12, 14, 27, 26, 25, 33, 32};
const char* RELAY_NAMES[MAX_RELAYS] = {"Relay 1", "Relay 2", "Relay 3", "Relay 4", "Relay 5", "Relay 6", "Relay 7", "Relay 8"};
// Schedule Structure
struct ScheduledTask {
int relay; // Relay number (1-8)
int startHour;
int startMinute;
int endHour;
int endMinute;
String date; // "YYYY-MM-DD" or empty for daily
String stateDuring; // "ON" or "OFF"
};
ScheduledTask schedules[MAX_SCHEDULES];
int scheduleCount = 0;
// Event history
String historyLog[MAX_HISTORY];
int historyCount = 0;
// General ESP32 and WebServer variables
Preferences preferences;
const char* ssid = "saikat sumita";
const char* password = "tiger@7700";
WebServer server(80);
String sessionToken = "";
unsigned long sessionStartTime = 0;
const unsigned long SESSION_TIMEOUT = 30 * 60 * 1000;
bool isLoggedIn = false;
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 19800;
const int daylightOffset_sec = 0;
// --- Function Prototypes ---
String generateSessionToken();
bool isSessionValid();
String loginPage();
String dashboardPage();
String getFormattedTime();
void handleRoot();
void handleDashboard();
void handleLogin();
void handleLogout();
void handleRelayToggle();
void handleGetState();
void handleSaveSchedule();
void handleSchedule();
void addHistoryEntry(String event);
void loadSchedules();
void saveSchedules();
void handleTime();
void handleDeleteSchedule();
int calculateRelayState(int relay, int nowInMinutes, const String& currentDate);
const char* tailwindCSS = R"rawliteral(
)rawliteral";
String generateSessionToken() {
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
String token = "";
for (int i = 0; i < 16; i++) {
token += chars[random(0, chars.length())];
}
return token;
}
String loginPage() {
String page = String(R"rawliteral(
Saikat Smart Home - Login
)rawliteral") + tailwindCSS + R"rawliteral(
Saikat Smart Home
Enter your credentials to access dashboard
Created by Saikat Biswas
)rawliteral";
return page;
}
String dashboardPage() {
String page = String(R"rawliteral(
Saikat Smart Home Dashboard
)rawliteral") + tailwindCSS + R"rawliteral(
Saikat Smart Home
Advanced IoT Control System
β‘ Device Controls
π‘ Light 1:
π‘ Light 2:
π‘ Light 3:
π‘ Light 4:
β Fan 5:
β Fan 6:
π‘ Light 7:
π wPump 8:
Set Timer Schedule
Current Schedules
Event History
)rawliteral";
return page;
}
bool isSessionValid() {
if (server.hasArg("token")) {
String token = server.arg("token");
if (token == sessionToken && !sessionToken.isEmpty()) {
if (millis() - sessionStartTime < SESSION_TIMEOUT) {
sessionStartTime = millis();
return true;
}
}
}
return false;
}
void handleRoot() {
isLoggedIn = isSessionValid();
if (!isLoggedIn) {
server.send(200, "text/html", loginPage());
} else {
server.sendHeader("Location", "/dashboard?token=" + sessionToken);
server.send(303);
}
}
void handleDashboard() {
if (!isSessionValid()) {
server.sendHeader("Location", "/");
server.send(303);
return;
}
String page = dashboardPage();
server.send(200, "text/html", page);
}
void handleLogin() {
if (server.method() == HTTP_POST) {
String user = server.arg("username");
String pass = server.arg("password");
if (user == "admin" && pass == "tiger@7700") {
sessionToken = generateSessionToken();
sessionStartTime = millis();
isLoggedIn = true;
server.sendHeader("Location", "/dashboard?token=" + sessionToken);
server.send(303);
} else {
String errorPage = String(R"rawliteral(
Login Failed )rawliteral") + tailwindCSS + R"rawliteral(
)rawliteral";
server.send(200, "text/html", errorPage);
}
}
}
void handleLogout() {
sessionToken = "";
isLoggedIn = false;
server.sendHeader("Location", "/");
server.send(303);
}
void handleRelayToggle() {
if (!isSessionValid()) {
server.send(401, "text/plain", "Unauthorized");
return;
}
if (server.hasArg("ch")) {
int ch = server.arg("ch").toInt();
if (ch >= 1 && ch <= MAX_RELAYS) {
int pin = RELAY_PINS[ch - 1];
bool isOn = digitalRead(pin) == LOW; // true if ON (active LOW)
bool newIsOn = !isOn;
digitalWrite(pin, newIsOn ? LOW : HIGH);
addHistoryEntry("Relay " + String(ch) + " manually " + (newIsOn ? "ON" : "OFF"));
StaticJsonDocument<64> doc;
doc["status"] = "OK";
doc["state"] = newIsOn ? "ON" : "OFF";
String output;
serializeJson(doc, output);
server.send(200, "application/json", output);
} else {
server.send(400, "text/plain", "Invalid relay number.");
}
} else {
server.send(400, "text/plain", "Missing relay parameter.");
}
}
void handleGetState() {
DynamicJsonDocument doc(2048);
JsonArray relayStatesArray = doc.createNestedArray("relayStates");
for (int i = 0; i < MAX_RELAYS; i++) {
relayStatesArray.add(digitalRead(RELAY_PINS[i]) == LOW ? "ON" : "OFF");
}
JsonArray schedulesArray = doc.createNestedArray("schedules");
for (int i = 0; i < scheduleCount; i++) {
JsonObject obj = schedulesArray.createNestedObject();
obj["relay"] = schedules[i].relay;
obj["startHour"] = schedules[i].startHour;
obj["startMinute"] = schedules[i].startMinute;
obj["endHour"] = schedules[i].endHour;
obj["endMinute"] = schedules[i].endMinute;
obj["date"] = schedules[i].date;
obj["stateDuring"] = schedules[i].stateDuring;
}
JsonArray historyArray = doc.createNestedArray("history");
for (int i = 0; i < historyCount; i++) {
historyArray.add(historyLog[i]);
}
String output;
serializeJson(doc, output);
server.send(200, "application/json", output);
}
void handleSaveSchedule() {
if (!isSessionValid()) {
server.send(401, "text/plain", "Unauthorized");
return;
}
if (server.hasArg("relay") && server.hasArg("startTime") && server.hasArg("endTime") && server.hasArg("state")) {
int relayNum = server.arg("relay").toInt();
String startTimeStr = server.arg("startTime");
String endTimeStr = server.arg("endTime");
String stateStr = server.arg("state");
if (stateStr != "ON" && stateStr != "OFF") {
server.send(400, "text/plain", "Invalid state.");
return;
}
String dateStr = server.hasArg("date") ? server.arg("date") : "";
if (scheduleCount < MAX_SCHEDULES) {
schedules[scheduleCount].relay = relayNum;
schedules[scheduleCount].startHour = startTimeStr.substring(0, 2).toInt();
schedules[scheduleCount].startMinute = startTimeStr.substring(3, 5).toInt();
schedules[scheduleCount].endHour = endTimeStr.substring(0, 2).toInt();
schedules[scheduleCount].endMinute = endTimeStr.substring(3, 5).toInt();
schedules[scheduleCount].stateDuring = stateStr;
schedules[scheduleCount].date = dateStr;
scheduleCount++;
saveSchedules();
String scheduleDesc = "Schedule added for " + String(RELAY_NAMES[relayNum-1]) + ": " + stateStr + " from " + String(schedules[scheduleCount-1].startHour) + ":" + String(schedules[scheduleCount-1].startMinute) + " to " + String(schedules[scheduleCount-1].endHour) + ":" + String(schedules[scheduleCount-1].endMinute);
if (!dateStr.isEmpty()) scheduleDesc += " on " + dateStr;
addHistoryEntry(scheduleDesc);
server.send(200, "text/plain", "Schedule saved successfully!");
} else {
server.send(400, "text/plain", "Error! Max schedules reached.");
}
} else {
server.send(400, "text/plain", "Error! Invalid data.");
}
}
int calculateRelayState(int relay, int nowInMinutes, const String& currentDate) {
bool hasOnPeriod = false;
bool hasOffPeriod = false;
for (int i = 0; i < scheduleCount; i++) {
if (schedules[i].relay != relay) continue;
if (!schedules[i].date.isEmpty() && schedules[i].date != currentDate) continue;
int startInMinutes = schedules[i].startHour * 60 + schedules[i].startMinute;
int endInMinutes = schedules[i].endHour * 60 + schedules[i].endMinute;
bool inPeriod;
if (startInMinutes < endInMinutes) {
inPeriod = (nowInMinutes >= startInMinutes && nowInMinutes < endInMinutes);
} else {
inPeriod = (nowInMinutes >= startInMinutes || nowInMinutes < endInMinutes);
}
if (inPeriod) {
if (schedules[i].stateDuring == "ON") hasOnPeriod = true;
else hasOffPeriod = true;
}
}
if (hasOnPeriod || hasOffPeriod) {
return hasOnPeriod ? 1 : 0;
}
return -1; // no active schedule
}
void handleSchedule() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return;
}
int currentHour = timeinfo.tm_hour;
int currentMinute = timeinfo.tm_min;
int nowInMinutes = currentHour * 60 + currentMinute;
char date_buf[11];
strftime(date_buf, sizeof(date_buf), "%Y-%m-%d", &timeinfo);
String currentDate = date_buf;
static bool firstCall = true;
static int previousMinutes = -1;
static String previousDate = "";
if (firstCall) {
firstCall = false;
previousMinutes = nowInMinutes;
previousDate = currentDate;
for (int r = 1; r <= MAX_RELAYS; r++) {
int isState = calculateRelayState(r, nowInMinutes, currentDate);
bool shouldBeOn;
if (isState == -1) {
shouldBeOn = false; // default OFF
digitalWrite(RELAY_PINS[r-1], HIGH);
addHistoryEntry(String(RELAY_NAMES[r-1]) + " initialized to OFF");
} else {
shouldBeOn = (isState == 1);
digitalWrite(RELAY_PINS[r-1], shouldBeOn ? LOW : HIGH);
addHistoryEntry(String(RELAY_NAMES[r-1]) + " initialized to " + (shouldBeOn ? "ON" : "OFF") + " by schedule");
}
}
return;
}
if (nowInMinutes == previousMinutes && currentDate == previousDate) return;
for (int r = 1; r <= MAX_RELAYS; r++) {
int isState = calculateRelayState(r, nowInMinutes, currentDate);
if (isState != -1) {
bool targetOn = (isState == 1);
bool currentOn = (digitalRead(RELAY_PINS[r-1]) == LOW);
if (targetOn != currentOn) {
digitalWrite(RELAY_PINS[r-1], targetOn ? LOW : HIGH);
addHistoryEntry(String(RELAY_NAMES[r-1]) + " turned " + (targetOn ? "ON" : "OFF") + " by schedule");
}
}
}
previousMinutes = nowInMinutes;
previousDate = currentDate;
}
void addHistoryEntry(String event) {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return;
}
char time_str[30];
strftime(time_str, sizeof(time_str), "%H:%M:%S", &timeinfo);
String newEntry = String(time_str) + " -> " + event;
for (int i = MAX_HISTORY - 1; i > 0; i--) {
historyLog[i] = historyLog[i-1];
}
historyLog[0] = newEntry;
if (historyCount < MAX_HISTORY) {
historyCount++;
}
}
void loadSchedules() {
preferences.begin("schedules", false);
String jsonString = preferences.getString("schedule_data", "[]");
preferences.end();
DynamicJsonDocument doc(2048);
DeserializationError error = deserializeJson(doc, jsonString);
if (!error) {
JsonArray array = doc.as();
scheduleCount = 0;
for (JsonObject obj : array) {
if (scheduleCount < MAX_SCHEDULES) {
schedules[scheduleCount].relay = obj["r"];
schedules[scheduleCount].startHour = obj["sh"];
schedules[scheduleCount].startMinute = obj["sm"];
schedules[scheduleCount].endHour = obj["eh"];
schedules[scheduleCount].endMinute = obj["em"];
schedules[scheduleCount].date = obj["d"] | "";
schedules[scheduleCount].stateDuring = obj["s"].as();
scheduleCount++;
}
}
}
}
void saveSchedules() {
DynamicJsonDocument doc(2048);
JsonArray array = doc.to();
for (int i = 0; i < scheduleCount; i++) {
JsonObject obj = array.createNestedObject();
obj["r"] = schedules[i].relay;
obj["sh"] = schedules[i].startHour;
obj["sm"] = schedules[i].startMinute;
obj["eh"] = schedules[i].endHour;
obj["em"] = schedules[i].endMinute;
obj["d"] = schedules[i].date;
obj["s"] = schedules[i].stateDuring;
}
String jsonString;
serializeJson(doc, jsonString);
preferences.begin("schedules", false);
preferences.putString("schedule_data", jsonString);
preferences.end();
}
String getFormattedTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return "Time unavailable";
}
char timeString[64];
strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", &timeinfo);
return String(timeString);
}
void handleTime() {
server.send(200, "text/plain", getFormattedTime());
}
void handleDeleteSchedule() {
if (!isSessionValid()) {
server.send(401, "text/plain", "Unauthorized");
return;
}
if (server.hasArg("id")) {
int id = server.arg("id").toInt();
if (id >= 0 && id < scheduleCount) {
for (int i = id; i < scheduleCount - 1; i++) {
schedules[i] = schedules[i+1];
}
scheduleCount--;
saveSchedules();
addHistoryEntry("Schedule deleted.");
server.send(200, "text/plain", "Schedule deleted successfully.");
} else {
server.send(400, "text/plain", "Invalid schedule ID.");
}
} else {
server.send(400, "text/plain", "Missing ID parameter.");
}
}
void setup() {
Serial.begin(115200);
randomSeed(analogRead(0));
preferences.begin("schedules", false);
loadSchedules();
preferences.end();
for (int i = 0; i < MAX_RELAYS; i++) {
pinMode(RELAY_PINS[i], OUTPUT);
digitalWrite(RELAY_PINS[i], HIGH); // Initial OFF (active LOW)
}
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
}
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
// Call handleSchedule to set initial states
delay(2000); // Wait for time sync
handleSchedule();
server.on("/", handleRoot);
server.on("/dashboard", handleDashboard);
server.on("/login", HTTP_POST, handleLogin);
server.on("/logout", handleLogout);
server.on("/relay", handleRelayToggle);
server.on("/getState", handleGetState);
server.on("/saveSchedule", HTTP_POST, handleSaveSchedule);
server.on("/time", handleTime);
server.on("/deleteSchedule", handleDeleteSchedule);
server.onNotFound([]() {
String notFoundPage = String(R"rawliteral(404 - Not Found )rawliteral") + tailwindCSS + R"rawliteral()rawliteral";
server.send(404, "text/html", notFoundPage);
});
server.begin();
}
void loop() {
server.handleClient();
handleSchedule();
}
8 Channel Realy Manual With Sensor Programing Code
#include
#include
#include
#include
#include
#include
#define TRIG_PIN 5
#define ECHO_PIN 18
#define PIR_PIN 21
#define LED1 2
#define LED2 4
#define RELAY1 13
#define RELAY2 12
#define RELAY3 14
#define RELAY4 27
#define DHT_PIN 22
#define DHT_TYPE DHT11 // Change to DHT22 if using DHT22 sensor
// New sensors and LEDs
#define SMOKE_PIN 23
#define RAIN_PIN 25
#define LDR_PIN 34 // Analog pin for LDR
#define LED3 15 // For Smoke
#define LED4 16 // For Rain
#define LED5 17 // For LDR
// Additional relays
#define RELAY5 26
#define RELAY6 19
#define RELAY7 32
#define RELAY8 33
// LDR detection threshold (adjust as needed; higher value might mean darker depending on circuit)
#define LDR_THRESHOLD 2000 // Detect "dark" if analogRead > 2000
// Water tank constants
#define TANK_HEIGHT 200.0 // Total tank height in cm
#define TANK_CAPACITY 1000.0 // Tank capacity in liters
Preferences preferences;
DHT dht(DHT_PIN, DHT_TYPE);
const char* ssid = "saikat sumita";
const char* password = "tiger@7700";
WebServer server(80);
String sessionToken = "";
unsigned long sessionStartTime = 0;
const unsigned long SESSION_TIMEOUT = 30 * 60 * 1000; // 30 minutes in milliseconds
bool isLoggedIn = false;
int totalDetections = 0;
bool midnightReset = false;
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 19800; // GMT+5:30 for India
const int daylightOffset_sec = 0;
// Separate trigger times for each sensor
unsigned long lastTriggerUltra = 0;
unsigned long lastTriggerPIR = 0;
unsigned long lastTriggerSmoke = 0;
unsigned long lastTriggerRain = 0;
unsigned long lastTriggerLDR = 0;
// Minimal Tailwind CSS with essential classes
const char* tailwindCSS = R"rawliteral(
)rawliteral";
// Generate a simple random session token
String generateSessionToken() {
String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
String token = "";
for (int i = 0; i < 16; i++) {
token += chars[random(0, chars.length())];
}
return token;
}
// HTML Login Page
String loginPage() {
String page = String(R"rawliteral(
Yarana Smart Home - Login
)rawliteral") + tailwindCSS + R"rawliteral(
Yarana Smart Home
Enter your credentials to access dashboard
Created by Abhishek Maurya
)rawliteral";
Serial.println("Serving login page, CSS length: " + String(strlen(tailwindCSS)) + ", HTML length: " + String(page.length()));
return page;
}
// Enhanced Dashboard Page with Relay State
String dashboardPage() {
String logs = "";
String chartData = "";
String tempChartData = "";
String humidityChartData = "";
String timeLabels = "";
// Get sensor history - reverse to have oldest first for charts
for (int i = 9; i >= 0; i--) {
float distance = preferences.getFloat(("d" + String(i)).c_str(), 0);
float temperature = preferences.getFloat(("temp" + String(i)).c_str(), 0);
float humidity = preferences.getFloat(("hum" + String(i)).c_str(), 0);
String timestamp = preferences.getString(("t" + String(i)).c_str(), "");
if (distance > 0) {
logs += "";
logs += "" + timestamp + "";
logs += "" + String(distance, 1) + " cm | " + (isnan(temperature) ? "N/A" : String(temperature, 1) + "Β°C") + " | " + (isnan(humidity) ? "N/A" : String(humidity, 1) + "%") + "";
logs += "";
}
chartData += String(distance, 1);
tempChartData += isnan(temperature) ? "0" : String(temperature, 1);
humidityChartData += isnan(humidity) ? "0" : String(humidity, 1);
timeLabels += "'" + (timestamp.length() > 0 ? timestamp.substring(11, 16) : String(9 - i)) + "'";
if (i > 0) {
chartData += ",";
tempChartData += ",";
humidityChartData += ",";
timeLabels += ",";
}
}
// Calculate water level percentage
float currentDistance = preferences.getFloat("d0", 0);
float waterLevel = ((TANK_HEIGHT - currentDistance) / TANK_HEIGHT) * 100;
if (waterLevel < 0) waterLevel = 0;
if (waterLevel > 100) waterLevel = 100;
float waterVolume = (waterLevel / 100) * TANK_CAPACITY;
// Get detection stats
int todayDetections = preferences.getInt("todayDet", 0);
String lastDetection = preferences.getString("lastDet", "Never");
// Check for DHT issues
String dhtWarning = "";
float temp = dht.readTemperature();
if (isnan(temp)) {
dhtWarning = "" +
String("Warning
DHT sensor not responding. Check connections to GPIO ") + String(DHT_PIN) + ".
";
}
// Get relay states
String relay1State = digitalRead(RELAY1) == LOW ? "ON" : "OFF";
String relay2State = digitalRead(RELAY2) == LOW ? "ON" : "OFF";
String relay3State = digitalRead(RELAY3) == LOW ? "ON" : "OFF";
String relay4State = digitalRead(RELAY4) == LOW ? "ON" : "OFF";
String relay5State = digitalRead(RELAY5) == LOW ? "ON" : "OFF";
String relay6State = digitalRead(RELAY6) == LOW ? "ON" : "OFF";
String relay7State = digitalRead(RELAY7) == LOW ? "ON" : "OFF";
String relay8State = digitalRead(RELAY8) == LOW ? "ON" : "OFF";
String relay1Class = digitalRead(RELAY1) == LOW ? "text-green-500" : "text-red-500";
String relay2Class = digitalRead(RELAY2) == LOW ? "text-green-500" : "text-red-500";
String relay3Class = digitalRead(RELAY3) == LOW ? "text-green-500" : "text-red-500";
String relay4Class = digitalRead(RELAY4) == LOW ? "text-green-500" : "text-red-500";
String relay5Class = digitalRead(RELAY5) == LOW ? "text-green-500" : "text-red-500";
String relay6Class = digitalRead(RELAY6) == LOW ? "text-green-500" : "text-red-500";
String relay7Class = digitalRead(RELAY7) == LOW ? "text-green-500" : "text-red-500";
String relay8Class = digitalRead(RELAY8) == LOW ? "text-green-500" : "text-red-500";
// Get token for dashboard links
String tokenParam = sessionToken.isEmpty() ? "" : "?token=" + sessionToken;
String page = String(R"rawliteral(
Saikat Smart Home Dashboard
)rawliteral") + tailwindCSS + R"rawliteral(
Saikat Smart Home
Advanced IoT Control System
)rawliteral" + dhtWarning + R"rawliteral(
Water Level
)rawliteral" + String(waterLevel, 0) + R"rawliteral(%
π§
Distance
...
π
Motion
...
ποΈ
Temperature
...
π‘οΈ
Humidity
...
π§
Smoke
...
π₯
Rain
...
π§οΈ
Light
...
π‘
π Water Tank Monitor
)rawliteral" + String(waterLevel, 0) + R"rawliteral(%
Volume
)rawliteral" + String(waterVolume, 0) + R"rawliteral(L
Capacity
)rawliteral" + String(TANK_CAPACITY, 0) + R"rawliteral(L
β‘ Device Controls
)rawliteral" + relay1State + R"rawliteral(
)rawliteral" + relay2State + R"rawliteral(
)rawliteral" + relay3State + R"rawliteral(
)rawliteral" + relay4State + R"rawliteral(
)rawliteral" + relay5State + R"rawliteral(
)rawliteral" + relay6State + R"rawliteral(
)rawliteral" + relay7State + R"rawliteral(
)rawliteral" + relay8State + R"rawliteral(
π Distance History
π‘οΈ Environment Monitor
π¨ Motion Detection Stats
Today's Detections:
)rawliteral" + String(todayDetections) + R"rawliteral(
Last Detection:
)rawliteral" + lastDetection + R"rawliteral(
Security Status:
Active
π Recent Activity Logs
)rawliteral" + logs + R"rawliteral(
)rawliteral";
Serial.println("Serving dashboard page, CSS length: " + String(strlen(tailwindCSS)) + ", HTML length: " + String(page.length()));
return page;
}
// Check if session is valid
bool isSessionValid() {
if (server.hasArg("token")) {
String token = server.arg("token");
Serial.println("Received Token: " + token + " for URI: " + server.uri());
if (token == sessionToken && !sessionToken.isEmpty()) {
if (millis() - sessionStartTime < SESSION_TIMEOUT) {
sessionStartTime = millis();
Serial.println("Session valid, token: " + sessionToken + ", extended timeout");
return true;
} else {
Serial.println("Session expired for token: " + sessionToken);
}
} else {
Serial.println("Invalid token. Expected: " + sessionToken);
}
} else {
Serial.println("No token received for URI: " + server.uri());
}
return false;
}
// Handle Root
void handleRoot() {
isLoggedIn = isSessionValid();
if (!isLoggedIn) {
Serial.println("Root accessed, no valid session, serving login page");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
String page = loginPage();
server.send(200, "text/html", page);
Serial.println("Login page sent, size: " + String(page.length()) + " bytes");
} else {
Serial.println("Root accessed, valid session, redirecting to dashboard");
server.sendHeader("Location", "/dashboard?token=" + sessionToken);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(303);
}
}
// Handle Dashboard
void handleDashboard() {
if (!isSessionValid()) {
Serial.println("Dashboard accessed, no valid session, redirecting to login");
server.sendHeader("Location", "/");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(303);
return;
}
Serial.println("Dashboard accessed, serving dashboard page");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
String page = dashboardPage();
server.send(200, "text/html", page);
Serial.println("Dashboard page sent, size: " + String(page.length()) + " bytes");
}
// Handle Login
void handleLogin() {
if (server.method() == HTTP_POST) {
String user = server.arg("username");
String pass = server.arg("password");
Serial.println("Login attempt - Username: " + user + ", Password: " + pass);
if (user == "admin" && pass == "1234") {
sessionToken = generateSessionToken();
sessionStartTime = millis();
isLoggedIn = true;
Serial.println("Login successful, token: " + sessionToken);
server.sendHeader("Location", "/dashboard?token=" + sessionToken);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(303);
} else {
String errorMessage = user.isEmpty() || pass.isEmpty() ? "Username or password field is empty" : "Invalid username or password";
Serial.println("Login failed: " + errorMessage);
String errorPage = String(R"rawliteral(
Login Failed )rawliteral") + tailwindCSS + R"rawliteral(
β
Access Denied!
)rawliteral" + errorMessage + R"rawliteral(. Please try again.
If login fails repeatedly, ensure you are accessing via http:// and try a different browser.
Try Again
)rawliteral";
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(200, "text/html", errorPage);
Serial.println("Error page sent, size: " + String(errorPage.length()) + " bytes");
}
} else {
Serial.println("Invalid request method for /login");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(405, "text/plain", "Method Not Allowed");
}
}
// Get formatted time
String getFormattedTime() {
struct tm timeinfo;
if (!getLocalTime(&timeinfo)) {
return "Time unavailable";
}
char timeString[64];
strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", &timeinfo);
return String(timeString);
}
// Parse datetime-local to time_t
time_t parseDateTime(String dt) {
struct tm tm;
sscanf(dt.c_str(), "%d-%d-%dT%d:%d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min);
tm.tm_year -= 1900;
tm.tm_mon -= 1;
tm.tm_sec = 0;
tm.tm_isdst = -1;
return mktime(&tm) + gmtOffset_sec;
}
// Handle Relay
void handleRelay() {
if (!isSessionValid()) {
Serial.println("Relay accessed, no valid session, unauthorized");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(401, "text/plain", "Unauthorized");
return;
}
if (!server.hasArg("ch")) {
Serial.println("Relay accessed, missing channel parameter");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(400, "text/plain", "Missing channel");
return;
}
int ch = server.arg("ch").toInt();
int pin;
String deviceName;
switch (ch) {
case 1: pin = RELAY1; deviceName = "Light 1"; break;
case 2: pin = RELAY2; deviceName = "Pump"; break;
case 3: pin = RELAY3; deviceName = "Heater"; break;
case 4: pin = RELAY4; deviceName = "Fan"; break;
case 5: pin = RELAY5; deviceName = "Device 5"; break;
case 6: pin = RELAY6; deviceName = "Device 6"; break;
case 7: pin = RELAY7; deviceName = "Device 7"; break;
case 8: pin = RELAY8; deviceName = "Device 8"; break;
default:
Serial.println("Relay accessed, invalid channel: " + String(ch));
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(400, "text/plain", "Invalid channel");
return;
}
digitalWrite(pin, !digitalRead(pin));
String state = digitalRead(pin) == LOW ? "ON" : "OFF";
String timestamp = getFormattedTime();
Serial.println("Relay " + String(ch) + " (" + deviceName + ") toggled to " + state + " at " + timestamp);
StaticJsonDocument<200> doc;
doc["status"] = "OK";
doc["state"] = state;
String json;
serializeJson(doc, json);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(200, "application/json", json);
}
// Handle Sensor Data
void handleSensor() {
if (!isSessionValid()) {
Serial.println("Sensor accessed, no valid session, unauthorized");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(401, "text/plain", "Unauthorized");
return;
}
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
long duration = pulseIn(ECHO_PIN, HIGH, 30000);
float distance = duration > 0 ? duration * 0.034 / 2 : 999;
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
if (isnan(temperature) || isnan(humidity)) {
temperature = 0;
humidity = 0;
Serial.println("Failed to read from DHT sensor on GPIO " + String(DHT_PIN));
}
String currentTime = getFormattedTime();
for (int i = 9; i > 0; i--) {
float val = preferences.getFloat(("d" + String(i - 1)).c_str(), 0);
float tempVal = preferences.getFloat(("temp" + String(i - 1)).c_str(), 0);
float humVal = preferences.getFloat(("hum" + String(i - 1)).c_str(), 0);
String time = preferences.getString(("t" + String(i - 1)).c_str(), "");
preferences.putFloat(("d" + String(i)).c_str(), val);
preferences.putFloat(("temp" + String(i)).c_str(), tempVal);
preferences.putFloat(("hum" + String(i)).c_str(), humVal);
preferences.putString(("t" + String(i)).c_str(), time);
}
preferences.putFloat("d0", distance);
preferences.putFloat("temp0", temperature);
preferences.putFloat("hum0", humidity);
preferences.putString("t0", currentTime);
bool motion = digitalRead(PIR_PIN);
Serial.println("PIR Sensor value: " + String(motion) + " at " + currentTime);
// New sensor readings
bool smokeDetect = digitalRead(SMOKE_PIN) == HIGH; // Changed to HIGH assuming active HIGH to fix reverse issue
bool rainDetect = digitalRead(RAIN_PIN) == LOW;
int ldrValue = analogRead(LDR_PIN);
bool ldrDetect = ldrValue > LDR_THRESHOLD;
Serial.println("LDR Sensor value: " + String(ldrValue) + ", Detect: " + String(ldrDetect) + " at " + currentTime);
// Handle ultrasonic detection for LED1
if (distance > 0 && distance <= 6 && millis() - lastTriggerUltra > 3000) {
digitalWrite(LED1, HIGH);
lastTriggerUltra = millis();
Serial.println("Ultrasonic detected distance <= 6cm, LED1 ON at " + currentTime);
}
// Handle motion detection for LED2
if (motion && millis() - lastTriggerPIR > 3000) {
totalDetections++;
int todayDetections = preferences.getInt("todayDet", 0);
preferences.putInt("todayDet", todayDetections + 1);
preferences.putString("lastDet", currentTime);
digitalWrite(LED2, HIGH);
lastTriggerPIR = millis();
Serial.println("Motion detected, LED2 ON at " + currentTime);
}
// Handle smoke detection for LED3
if (smokeDetect && millis() - lastTriggerSmoke > 3000) {
digitalWrite(LED3, HIGH);
lastTriggerSmoke = millis();
Serial.println("Smoke detected, LED3 ON at " + currentTime);
}
// Handle rain detection for LED4
if (rainDetect && millis() - lastTriggerRain > 3000) {
digitalWrite(LED4, HIGH);
lastTriggerRain = millis();
Serial.println("Rain detected, LED4 ON at " + currentTime);
}
// Handle LDR detection for LED5
if (ldrDetect && millis() - lastTriggerLDR > 3000) {
digitalWrite(LED5, HIGH);
lastTriggerLDR = millis();
Serial.println("Dark detected (LDR), LED5 ON at " + currentTime);
}
StaticJsonDocument<600> doc;
doc["distance"] = distance;
doc["temperature"] = isnan(temperature) ? 0 : temperature;
doc["humidity"] = isnan(humidity) ? 0 : humidity;
doc["pir"] = motion;
doc["timestamp"] = currentTime;
doc["detections"] = preferences.getInt("todayDet", 0);
doc["lastDetection"] = preferences.getString("lastDet", "Never");
doc["relay1State"] = digitalRead(RELAY1) == LOW ? "ON" : "OFF";
doc["relay2State"] = digitalRead(RELAY2) == LOW ? "ON" : "OFF";
doc["relay3State"] = digitalRead(RELAY3) == LOW ? "ON" : "OFF";
doc["relay4State"] = digitalRead(RELAY4) == LOW ? "ON" : "OFF";
doc["relay5State"] = digitalRead(RELAY5) == LOW ? "ON" : "OFF";
doc["relay6State"] = digitalRead(RELAY6) == LOW ? "ON" : "OFF";
doc["relay7State"] = digitalRead(RELAY7) == LOW ? "ON" : "OFF";
doc["relay8State"] = digitalRead(RELAY8) == LOW ? "ON" : "OFF";
doc["smokeDetect"] = smokeDetect;
doc["rainDetect"] = rainDetect;
doc["ldrDetect"] = ldrDetect;
JsonArray history = doc.createNestedArray("history");
JsonArray tempHistory = doc.createNestedArray("tempHistory");
JsonArray humidityHistory = doc.createNestedArray("humidityHistory");
for (int i = 9; i >= 0; i--) { // Reverse for oldest first
history.add(preferences.getFloat(("d" + String(i)).c_str(), 0));
tempHistory.add(preferences.getFloat(("temp" + String(i)).c_str(), 0));
humidityHistory.add(preferences.getFloat(("hum" + String(i)).c_str(), 0));
}
String json;
serializeJson(doc, json);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(200, "application/json", json);
Serial.println("Sensor data sent, JSON size: " + String(json.length()) + " bytes");
}
// Handle Logout
void handleLogout() {
sessionToken = "";
isLoggedIn = false;
Serial.println("User logged out at " + getFormattedTime());
server.sendHeader("Location", "/");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(303);
}
// Handle System Reset
void handleReset() {
if (!isSessionValid()) {
Serial.println("Reset accessed, no valid session, unauthorized");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(401, "text/plain", "Unauthorized");
return;
}
preferences.clear();
totalDetections = 0;
Serial.println("System data reset at " + getFormattedTime());
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(200, "text/plain", "System reset completed");
}
// Handle System Stats
void handleStats() {
if (!isSessionValid()) {
Serial.println("Stats accessed, no valid session, unauthorized");
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(401, "text/plain", "Unauthorized");
return;
}
StaticJsonDocument<300> doc;
doc["uptime"] = millis();
doc["freeHeap"] = ESP.getFreeHeap();
doc["chipModel"] = ESP.getChipModel();
doc["cpuFreq"] = ESP.getCpuFreqMHz();
doc["wifiRSSI"] = WiFi.RSSI();
doc["todayDetections"] = preferences.getInt("todayDet", 0);
doc["totalDetections"] = totalDetections;
String json;
serializeJson(doc, json);
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(200, "application/json", json);
}
void setup() {
Serial.begin(115200);
Serial.println("\n=== Yarana Smart Home System Starting ===");
Serial.println("Created by: Abhishek Maurya");
Serial.println("=========================================");
randomSeed(analogRead(0));
dht.begin();
Serial.println("DHT sensor initialized on GPIO " + String(DHT_PIN));
preferences.begin("yarana", false);
totalDetections = preferences.getInt("totalDet", 0);
pinMode(RELAY1, OUTPUT);
pinMode(RELAY2, OUTPUT);
pinMode(RELAY3, OUTPUT);
pinMode(RELAY4, OUTPUT);
pinMode(RELAY5, OUTPUT);
pinMode(RELAY6, OUTPUT);
pinMode(RELAY7, OUTPUT);
pinMode(RELAY8, OUTPUT);
pinMode(LED1, OUTPUT);
pinMode(LED2, OUTPUT);
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
pinMode(PIR_PIN, INPUT);
pinMode(SMOKE_PIN, INPUT);
pinMode(RAIN_PIN, INPUT);
// No pinMode for analog LDR_PIN
pinMode(LED3, OUTPUT);
pinMode(LED4, OUTPUT);
pinMode(LED5, OUTPUT);
digitalWrite(RELAY1, HIGH);
digitalWrite(RELAY2, HIGH);
digitalWrite(RELAY3, HIGH);
digitalWrite(RELAY4, HIGH);
digitalWrite(RELAY5, HIGH);
digitalWrite(RELAY6, HIGH);
digitalWrite(RELAY7, HIGH);
digitalWrite(RELAY8, HIGH);
digitalWrite(LED1, LOW);
digitalWrite(LED2, LOW);
digitalWrite(LED3, LOW);
digitalWrite(LED4, LOW);
digitalWrite(LED5, LOW);
Serial.println("GPIO pins initialized");
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi: " + String(ssid));
int wifiAttempts = 0;
while (WiFi.status() != WL_CONNECTED && wifiAttempts < 30) {
delay(500);
Serial.print(".");
wifiAttempts++;
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi Connected Successfully!");
Serial.println("IP Address: " + WiFi.localIP().toString());
Serial.println("Signal Strength: " + String(WiFi.RSSI()) + " dBm");
Serial.println("Access dashboard at: http://" + WiFi.localIP().toString());
} else {
Serial.println("\nWiFi Connection Failed!");
Serial.println("Starting in AP mode...");
WiFi.softAP("Yarana_SmartHome", "yarana123");
Serial.println("AP IP: " + WiFi.softAPIP().toString());
Serial.println("Access dashboard at: http://" + WiFi.softAPIP().toString());
}
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
Serial.println("NTP time synchronization started");
server.on("/", handleRoot);
server.on("/dashboard", handleDashboard);
server.on("/login", handleLogin);
server.on("/logout", handleLogout);
server.on("/relay", handleRelay);
server.on("/sensor", handleSensor);
server.on("/reset", handleReset);
server.on("/stats", handleStats);
server.onNotFound([]() {
Serial.println("Not Found: " + server.uri());
String notFoundPage = String(R"rawliteral(
404 - Not Found )rawliteral") + tailwindCSS + R"rawliteral(
)rawliteral";
server.sendHeader("Cache-Control", "no-cache, no-store, must-revalidate");
server.sendHeader("Pragma", "no-cache");
server.sendHeader("Expires", "0");
server.send(404, "text/html", notFoundPage);
Serial.println("404 page sent, size: " + String(notFoundPage.length()) + " bytes");
});
server.begin();
Serial.println("Web server started successfully");
Serial.println("Default credentials: admin / 1234");
Serial.println("CSS length: " + String(strlen(tailwindCSS)) + " bytes");
Serial.println("=========================================");
for (int i = 0; i < 3; i++) {
digitalWrite(LED1, HIGH);
digitalWrite(LED2, HIGH);
delay(200);
digitalWrite(LED1, LOW);
digitalWrite(LED2, LOW);
delay(200);
}
}
void loop() {
server.handleClient();
// Turn off LEDs individually after timeout
if (millis() - lastTriggerUltra > 3000) {
digitalWrite(LED1, LOW);
}
if (millis() - lastTriggerPIR > 3000) {
digitalWrite(LED2, LOW);
}
if (millis() - lastTriggerSmoke > 3000) {
digitalWrite(LED3, LOW);
}
if (millis() - lastTriggerRain > 3000) {
digitalWrite(LED4, LOW);
}
if (millis() - lastTriggerLDR > 3000) {
digitalWrite(LED5, LOW);
}
// Check schedules every second
static unsigned long lastScheduleCheck = 0;
if (millis() - lastScheduleCheck > 1000) {
lastScheduleCheck = millis();
time_t now = time(nullptr) + gmtOffset_sec;
for (int relay = 1; relay <= 8; relay++) {
String key = "sched" + String(relay);
String sched = preferences.getString(key.c_str(), "");
if (!sched.isEmpty()) {
int colon1 = sched.indexOf(':');
int colon2 = sched.indexOf(':', colon1 + 1);
if (colon1 > 0 && colon2 > 0) {
String startStr = sched.substring(0, colon1);
String endStr = sched.substring(colon1 + 1, colon2);
String action = sched.substring(colon2 + 1);
time_t startTime = parseDateTime(startStr);
time_t endTime = parseDateTime(endStr);
int pin;
switch (relay) {
case 1: pin = RELAY1; break;
case 2: pin = RELAY2; break;
case 3: pin = RELAY3; break;
case 4: pin = RELAY4; break;
case 5: pin = RELAY5; break;
case 6: pin = RELAY6; break;
case 7: pin = RELAY7; break;
case 8: pin = RELAY8; break;
}
if (now >= startTime && now < endTime) {
digitalWrite(pin, action == "ON" ? LOW : HIGH);
Serial.println("Schedule activated for relay " + String(relay) + " to " + action);
} else if (now >= endTime) {
preferences.remove(key.c_str()); // Remove expired schedule
}
}
}
}
}
static unsigned long lastMidnightCheck = 0;
if (millis() - lastMidnightCheck > 60000) {
lastMidnightCheck = millis();
struct tm timeinfo;
if (getLocalTime(&timeinfo)) {
if (timeinfo.tm_hour == 0 && timeinfo.tm_min == 0) {
if (!midnightReset) {
preferences.putInt("todayDet", 0);
midnightReset = true;
Serial.println("Daily detection count reset at midnight");
}
} else {
midnightReset = false;
}
}
}
static unsigned long lastStatusLog = 0;
if (millis() - lastStatusLog > 300000) {
lastStatusLog = millis();
Serial.println("=== System Status ===");
Serial.println("Uptime: " + String(millis() / 1000) + " seconds");
Serial.println("Free Heap: " + String(ESP.getFreeHeap()) + " bytes");
Serial.println("WiFi Signal: " + String(WiFi.RSSI()) + " dBm");
Serial.println("Today's Detections: " + String(preferences.getInt("todayDet", 0)));
Serial.println("Total Detections: " + String(totalDetections));
Serial.println("====================");
}
}