#include <WiFi.h>
#include <PubSubClient.h>
#include "DHTesp.h"
#include <NTPClient.h>
#include <ESP32Servo.h>
#include <math.h>
// Pin definitions
#define DHT_PIN 15 // Digital pin connected to DHT22 temperature/humidity sensor
#define BUZZER 12 // Digital pin connected to buzzer
#define LDR_PIN 34 // Analog pin connected to Light Dependent Resistor (LDR)
#define SERVO_PIN 13 // Digital pin connected to servo motor for shade control
// Network clients
WiFiClient espClient; // WiFi client instance
PubSubClient mqttClient(espClient); // MQTT client for IoT communication
// NTP client for getting current time from internet
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
// Character arrays to store string representations of sensor readings
char tempAr[6]; // Array to store temperature as string
char lightIntensityAr[6]; // Array to store light intensity as string
// Sensor and actuator objects
DHTesp dhtSensor; // DHT temperature and humidity sensor
Servo shadeServo; // Servo motor to control shade angle
// Scheduling variables
bool isScheduledON = false; // Flag to indicate if schedule is active
unsigned long scheduledOnTime; // Timestamp when scheduled event should occur
// Light intensity tracking variables
float lightIntensitySum = 0; // Running sum of light readings for averaging
int lightSampleCount = 0; // Count of samples taken for averaging
float lastAverageLightIntensity = 0; // Last calculated average light intensity
// System parameters - can be updated via MQTT
int samplingInterval = 5; // Time between samples in seconds
int sendingInterval = 120; // Time between sending MQTT updates in seconds
float minAngle = 30; // Minimum angle for shade in degrees
float controllingFactor = 0.75; // Factor to adjust shade response sensitivity
float idealTemp = 30; // Ideal temperature in Celsius
// Timing variables for non-blocking operation
unsigned long lastSampleTime = 0; // Timestamp of last light intensity sample
unsigned long lastSendTime = 0; // Timestamp of last MQTT data transmission
void setup() {
Serial.begin(115200); // Initialize serial communication at 115200 baud rate
setupWifi(); // Connect to WiFi network
setupMqtt(); // Configure MQTT broker connection
dhtSensor.setup(DHT_PIN, DHTesp::DHT22); // Initialize DHT22 temperature sensor
// Initialize NTP client for time synchronization
timeClient.begin();
timeClient.setTimeOffset(5.5 * 3600); // Set timezone offset (UTC+5:30)
// Configure buzzer pin as output and ensure it's off initially
pinMode(BUZZER, OUTPUT);
digitalWrite(BUZZER, LOW);
// Initialize servo for shade control
shadeServo.attach(SERVO_PIN);
shadeServo.write(minAngle); // Set initial position to minimum angle
pinMode(LDR_PIN, INPUT); // Configure light sensor pin as input
}
void loop() {
// Ensure MQTT connection is maintained
if (!mqttClient.connected()) {
connectToBroker();
}
mqttClient.loop(); // Process incoming MQTT messages
// Read and publish current temperature
updateTemperature();
Serial.println(tempAr);
mqttClient.publish("CSE3-ADMIN-TEMP", tempAr);
// Check if any scheduled events need to be triggered
checkSchedule();
// Get current time for non-blocking operations
unsigned long currentTime = millis();
// Sample light intensity at regular intervals
if (currentTime - lastSampleTime >= samplingInterval * 1000) {
float lightIntensity = readLightIntensity();
lightIntensitySum += lightIntensity;
lightSampleCount++;
lastSampleTime = currentTime;
}
// Process and send averaged light intensity at longer intervals
if (currentTime - lastSendTime >= sendingInterval * 1000) {
if (lightSampleCount > 0) {
// Calculate average light intensity from collected samples
lastAverageLightIntensity = lightIntensitySum / lightSampleCount;
String(lastAverageLightIntensity, 2).toCharArray(lightIntensityAr, 6);
mqttClient.publish("CSE3-ADMIN-LIGHT-INTENSITY", lightIntensityAr);
Serial.print("Average Light Intensity: ");
Serial.println(lastAverageLightIntensity);
// Adjust shade position based on environmental conditions
updateServoAngle();
}
// Reset accumulators for next averaging period
lightIntensitySum = 0;
lightSampleCount = 0;
lastSendTime = currentTime;
}
delay(1000); // Main loop delay (1 second)
}
// Function to control the buzzer state
void buzzerOn(bool on) {
if (on) {
tone(BUZZER, 256); // Turn on buzzer at 256Hz frequency
} else {
noTone(BUZZER); // Turn off buzzer
}
}
// Initialize MQTT client settings
void setupMqtt() {
mqttClient.setServer("broker.hivemq.com", 1883); // Set MQTT broker address and port
mqttClient.setCallback(receiveCallback); // Set function for incoming messages
}
// Callback function to handle incoming MQTT messages
void receiveCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("Message arrived [");
Serial.print(topic);
Serial.print("] ");
// Convert payload bytes to char array
char payloadCharAr[length];
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
payloadCharAr[i] = (char)payload[i];
}
Serial.println();
// Process different MQTT topics to control various aspects of the system
if (strcmp(topic, "CSE3-ADMIN-MAIN-ON-OFF") == 0) {
// Control buzzer based on payload ('1' = on, '0' = off)
buzzerOn(payloadCharAr[0] == '1');
} else if (strcmp(topic, "CSE3-ADMIN-SCH-ON") == 0) {
// Handle scheduling commands
if (payloadCharAr[0] == 'N') {
isScheduledON = false; // Cancel scheduled event
} else {
isScheduledON = true; // Set scheduled event
scheduledOnTime = atol(payloadCharAr); // Convert payload to timestamp
}
} else if (strcmp(topic, "CSE3-ADMIN-SAMPLE-INTERVAL") == 0) {
// Update sampling interval parameter
samplingInterval = atoi(payloadCharAr);
Serial.print("Sampling Interval Updated: ");
Serial.println(samplingInterval);
} else if (strcmp(topic, "CSE3-ADMIN-SEND-INTERVAL") == 0) {
// Update sending interval parameter
sendingInterval = atoi(payloadCharAr);
Serial.print("Sending Interval Updated: ");
Serial.println(sendingInterval);
} else if (strcmp(topic, "CSE3-ADMIN-MIN-ANGLE") == 0) {
// Update minimum shade angle parameter
minAngle = atoi(payloadCharAr);
Serial.print("Minimum Angle Updated: ");
Serial.println(minAngle);
} else if (strcmp(topic, "CSE3-ADMIN-CONTROLLING-FACTOR") == 0) {
// Update shade control sensitivity factor
controllingFactor = atof(payloadCharAr);
Serial.print("Controlling Factor Updated: ");
Serial.println(controllingFactor);
} else if (strcmp(topic, "CSE3-ADMIN-IDEAL-TEMP") == 0) {
// Update ideal temperature parameter
idealTemp = atof(payloadCharAr);
Serial.print("Ideal Temperature Updated: ");
Serial.println(idealTemp);
}
}
// Connect or reconnect to MQTT broker
void connectToBroker() {
while (!mqttClient.connected()) {
Serial.print("Attempting MQTT connection...");
if (mqttClient.connect("ESP32-12345")) { // Connect with client ID
Serial.println("connected");
// Subscribe to all relevant control topics
mqttClient.subscribe("CSE3-ADMIN-MAIN-ON-OFF"); // Main power control
mqttClient.subscribe("CSE3-ADMIN-SCH-ON"); // Schedule setting
mqttClient.subscribe("CSE3-ADMIN-SAMPLE-INTERVAL"); // Update sampling rate
mqttClient.subscribe("CSE3-ADMIN-SEND-INTERVAL"); // Update sending rate
mqttClient.subscribe("CSE3-ADMIN-MIN-ANGLE"); // Update minimum angle
mqttClient.subscribe("CSE3-ADMIN-CONTROLLING-FACTOR"); // Update control sensitivity
mqttClient.subscribe("CSE3-ADMIN-IDEAL-TEMP"); // Update ideal temperature
} else {
Serial.print("failed ");
Serial.print(mqttClient.state());
delay(5000); // Wait 5 seconds before retrying
}
}
}
// Read temperature from DHT sensor and format as string
void updateTemperature() {
TempAndHumidity data = dhtSensor.getTempAndHumidity();
String(data.temperature, 2).toCharArray(tempAr, 6); // Format with 2 decimal places
}
// Connect to WiFi network
void setupWifi() {
Serial.println();
Serial.print("Connecting to ");
Serial.println("Wokwi-GUEST");
WiFi.begin("Wokwi-GUEST", ""); // Connect to network (no password for Wokwi simulation)
// Wait for connection to establish
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
// Display connection details once connected
Serial.println("WiFi connected");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
}
// Get current time from NTP server
unsigned long getTime() {
timeClient.update(); // Update NTP client
return timeClient.getEpochTime(); // Return epoch time (seconds since Jan 1, 1970)
}
// Check and handle scheduled events
void checkSchedule() {
if (isScheduledON) {
unsigned long currentTime = getTime();
if (currentTime > scheduledOnTime) {
// Activate system when scheduled time is reached
buzzerOn(true);
isScheduledON = false;
mqttClient.publish("CSE3-ADMIN-MAIN-ON-OFF-ESP", "1"); // Notify system is on
mqttClient.publish("CSE3-ADMIN-SCH-ESP-ON", "0"); // Reset schedule flag
Serial.println("Scheduled ON");
}
}
}
// Read light intensity from LDR sensor (0.0 to 1.0 scale)
float readLightIntensity() {
int ldrValue = analogRead(LDR_PIN); // Raw analog reading (0-4095)
float intensity = (float)ldrValue / 4095.0; // Normalize to 0.0-1.0 range
return intensity;
}
// Calculate and set the appropriate shade angle based on environmental conditions
void updateServoAngle() {
TempAndHumidity data = dhtSensor.getTempAndHumidity();
float currentTemp = data.temperature; // T (measured temperature)
// Extract system parameters for clarity
float I = lastAverageLightIntensity; // Normalized light intensity (0-1)
float T = currentTemp; // Current temperature in Celsius
float T_med = idealTemp; // Target/ideal temperature
float ts = (float)samplingInterval; // Time between samples
float tu = (float)sendingInterval; // Time between updates
float gamma = controllingFactor; // System response sensitivity factor
float theta_offset = minAngle; // Minimum shade angle
// Calculate optimal shade angle using complex formula
// This formula adjusts shade based on light intensity, temperature ratio, and system parameters
float angle = theta_offset + (180 - theta_offset) * I * gamma * log(ts / tu) * (T / T_med);
// Ensure angle stays within physical limits
if (angle < 0) angle = 0;
if (angle > 180) angle = 180;
// Apply calculated angle to servo motor
shadeServo.write((int)angle);
Serial.print("Servo Angle: ");
Serial.println(angle);
}