#include <DHT.h> // DHT sensor library
#include <Wire.h> // I2C communication
#include <LiquidCrystal_I2C.h> // I2C LCD control
#include <WiFi.h> // WiFi connectivity
#include <PubSubClient.h> // MQTT for cloud comms
#include <RTClib.h> // RTC library for time control
// --------RTC configuration------------
RTC_DS3231 rtc; // RTC object to talk to hardware
bool timeControlEnabled = true; // Master ON/OFF for time restriction
int heaterStartHour = 18; // Heater ON time (6PM in 24hr format)
int heaterEndHour = 22; // Heater OFF time (10PM)
bool heaterAllowed = false; // Current status: true=heater can run now
// ----------Display configuration----------------
LiquidCrystal_I2C lcd(0x27, 16, 2); // LCD: address 0x27, 16 cols, 2 rows
#define DHTTYPE DHT22 // Sensor type: DHT22
// -----------I/O Pin Assignments----------------------
//#define DHT22_1PIN 4 // Sensor 1 (PID control) on GPIO4
#define DHT22_2PIN 2 // Sensor 2 (safety) on GPIO2
#define set_point_pin 34 // Potentiometer for temp adjustment
#define HEATER_PIN 16 // MOSFET gate for heater control
#define HEATER_SAFETY_PIN 17 // MOSFET for safety cutoff
#define temp_alarm 18 // LED/buzzer for high temp
#define humid_alarm 19 // LED/buzzer for high humidity
// --------------Process Control Alarm Setpoints-----------------------
#define temp_h 30 // High temp alarm threshold (°C)
#define humid_h 65 // High humidity alarm threshold (%)
// ----------------Safety Protection set points--------------------------
#define safety_temp_hh 35 // Safety high temp cutoff
#define safety_humid_hh 70 // Safety high humidity cutoff
#define safety_temp_ll 15 // Safety low temp cutoff
#define safety_humid_ll 50 // Safety low humidity cutoff
// -------------------Create DHT sensor objects-----------------------------
//DHT DHT22_PID(DHT22_1PIN, DHTTYPE); // Primary sensor for control
DHT DHT22_Safety(DHT22_2PIN, DHTTYPE); // Backup sensor for safety
// ------------------- VARIABLES----------------------------------------------
//float pid_temp, pid_humid; // Sensor 1 readings
float safety_temp, safety_humid; // Sensor 2 readings
// ------------------------Timing Variables---------------------------------
unsigned long lastDisplayTime = 0; // Last display update time
const long displayInterval = 2000; // Update display every 2 seconds
WiFiClient wifiClient; // Create WiFi client object
PubSubClient client(wifiClient); // Create MQTT client using WiFi
// --------------------------WiFi credentials------------------------------------------
const char* ssid = "Wokwi-GUEST"; // Your WiFi network name
const char* password = ""; // Your WiFi password (empty for Wokwi)
const char* mqtt_broker = "industrial.api.ubidots.com"; // Ubidots server
const int mqtt_port = 1883; // For unencrypted connection(std MQTT port)
const char* UBIDOTS_TOKEN ="BBUS-FLBitJEEOSUACNY1IaB1SjsL2nUTGQ"; // User ID from ubidits
unsigned long lastReconnectAttempt = 0; // Track last MQTT reconnect time
const long reconnectInterval = 5000; // Try to reconnect every 5 seconds
// ---------------------------------Function prototypes (declare before use)-----------------------
void readSensors(); // Reads both DHT sensors
void checkWiFiAndMQTT(); // Manages cloud connection
boolean attemptReconnect(); // Tries to reconnect to MQTT
bool isHeaterTimeAllowed(); // Tell compiler about this function
// ---------------------------------SETUP FUNCTION (runs once at start)---------------------------
void setup() {
Serial.begin(115200); // Start serial monitor at 115200 baud
// -----------------------------Initialize RTC----------------------------------------
if (!rtc.begin()) { // Try to connect to RTC via I2C
Serial.println("RTC not found! Heater will run without time restriction."); // RTC missing
timeControlEnabled = false; // Continue without RTC
} else { // RTC connected successfully
Serial.println("RTC found!"); // Confirm RTC detected
if (rtc.lostPower()) { // Check if RTC battery died
Serial.println("RTC lost power, setting to compile time"); // RTC power lost
rtc.adjust(DateTime(F(__DATE__), F(__TIME__))); // Set RTC to PC compile time
}
// Display current time
DateTime now = rtc.now(); // Read current time from RTC
Serial.print("Current time: "); // Show current time in serial monitor
Serial.print(now.hour()); Serial.print(":"); //Extract hour from time
Serial.print(now.minute()); Serial.print(":"); //Extract minute from time
Serial.println(now.second()); //Extract second from time
}
// -------------Configure pins as inputs/outputs--------------------
pinMode(HEATER_SAFETY_PIN, OUTPUT); // Safety protection control
pinMode(temp_alarm, OUTPUT); // Temperature warning LED/buzzer
pinMode(humid_alarm, OUTPUT); // Humidity warning LED/buzzer
pinMode(set_point_pin, INPUT); // Manual setpoint adjustment for potentiometer at PIN 34
ledcAttach(HEATER_PIN, 5000, 8); // Setup PWM for heater (5kHz, 8-bit = 0-255) so called limitation
digitalWrite(HEATER_SAFETY_PIN, HIGH); // Enable heater safety circuit
// ---------------Initialize both DHT22 sensors---------------------
//DHT22_PID.begin(); // Primary sensor for PID control
DHT22_Safety.begin(); // Backup sensor for safety monitoring
delay(2000); // Wait for sensors to stabilize (DHT22 requires 2s warm-up)
// ---------------LCD dispaly setup--------------------
lcd.init(); // Turn on the LCD so it can show text
lcd.backlight(); // Light up the screen so you can see it
lcd.setCursor(0, 0); // (column, row) - starts at 0
lcd.print("PART 1: TEST"); // MODIFIED: Title for Part 1
lcd.setCursor(0, 1); // Move cursor to second line (row 1, column 0)
lcd.print("TMR+SAFE+CLOUD"); // MODIFIED: Show test mode
delay(2000);
// -------------------------WiFi and Cloud set up-----------------------------
Serial.println("Connecting to WiFi..."); // Status message
WiFi.begin(ssid, password); // Start WiFi connection
while (WiFi.status() != WL_CONNECTED) { // Wait for connection
delay(1000); // Wait 1 sec
Serial.print("."); // Print dot for each attempt
}
Serial.print("\nWiFi connected, IP:"); // Success message
Serial.println(WiFi.localIP()); // Show assigned IP
client.setServer(mqtt_broker, mqtt_port); //Set MQTT broker
}
// --------------------TIME CHECK FUNCTION------------------------
bool isHeaterTimeAllowed() { // Check if current hour is between 18-22
if (!timeControlEnabled) return true; // If no RTC, always allow
DateTime now = rtc.now(); // Get current time from RTC
int currentHour = now.hour(); // Extract just the hour (0-23)
// Return TRUE only between 18:00 - 22:00
return (currentHour >= heaterStartHour && currentHour < heaterEndHour); //When to start heater (18 = 6PM) && When to stop heater (22 = 10PM)
}
// -------------------LOOP FUNCTION----------------------------
void loop() {
unsigned long now = millis(); // Get current time in ms
// Handle display (runs every 2 seconds)
if (now - lastDisplayTime >= displayInterval) { //Checks if 1000ms has passed since last display run
lastDisplayTime = now; //Updates timer for next interval
// 1. READ SENSORS
readSensors(); //Get temp/humid from both sensors
lcd.clear(); // Clear screen
lcd.setCursor(0, 0); // Top row
lcd.print("P:T1-"); // Label for sensor1 temp
//lcd.print(pid_temp,1); // Show temp value
lcd.print(",H1-"); // Label for sensor1 humidity
//lcd.print(pid_humid,1); // Show humidity value
lcd.setCursor(0, 1); // Bottom row
lcd.print("S:T2-");
lcd.print(safety_temp,1);
lcd.print(",H2-");
lcd.print(safety_humid,1);
// Show time on second row instead
lcd.setCursor(9, 1); // Move to column 9 on bottom row
if (timeControlEnabled) { // If RTC is working
DateTime nowTime = rtc.now(); // Get current time
char timeStr[7]; // Buffer for "HH:MM" + null terminator (5 chars + null = 6, but 7 for safety)
sprintf(timeStr, "%02d:%02d", nowTime.hour(), nowTime.minute()); // Format as 14:30
lcd.print(timeStr); // Show time on LCD
} else {
lcd.print("No RTC"); // Show error if RTC missing
}
// ---------------------Process alarms (check if thresholds exceeded)---------------------
//if(pid_temp > temp_h) { digitalWrite(temp_alarm, HIGH); } // Turn on temp alarm
else { digitalWrite(temp_alarm, LOW); } // Turn off temp alarm
//if(pid_humid > humid_h) { digitalWrite(humid_alarm, HIGH); } // Turn on humid alarm
else { digitalWrite(humid_alarm, LOW); } // Turn off humid alarm
// ---------------------Safety protection (emergency shutdown)--------------------------
if(safety_temp > safety_temp_hh || safety_humid < safety_humid_ll) {
digitalWrite(HEATER_SAFETY_PIN, LOW); // Kill heater (LOW = off)
Serial.println("Heater OFF: Safety shutdown");
} else {
heaterAllowed = isHeaterTimeAllowed(); // Check if time is allowed
if (!heaterAllowed) {
digitalWrite(HEATER_SAFETY_PIN, LOW); // 18:00-22:00? NO → turn heater OFF
Serial.println("Heater OFF: Outside 18:00-22:00 window");
} else { // Heater allowed
digitalWrite(HEATER_SAFETY_PIN, HIGH); // 18:00-22:00? YES → enable heater
// PART 1: NO PID CONTROL - Fixed output for testing
ledcWrite(HEATER_PIN, 128); // Fixed 50% output for testing
//Serial.println("Heater ON: Fixed 50% (PID disabled in Part 1)");
}
}
}
// 2. Check WiFi and MQTT connection
checkWiFiAndMQTT(); // Ensure cloud connection
// 3. Upload to Ubidots Cloud
String topic = String("/v1.6/devices/iot_22-2-2026"); // Ubidots device path
// Build JSON payload for Ubidots
String payload = String("{\"temperature-1\":") +
(isnan(pid_temp) ? "\"nan\"" : String(pid_temp, 1)) + // Temp1 or "nan"
"," + String("\"humidity-1\":") +
(isnan(pid_humid) ? "\"nan\"" : String(pid_humid, 1)) + // Humid1 or "nan"
"," + String("\"heater_status\":") +
(heaterAllowed ? "1" : "0") + // Heater on/off status
"," + String("\"timer_allowed\":") +
(heaterAllowed ? "1" : "0") + // Timer status
"," + String("\"safety_status\":") +
((safety_temp > safety_temp_hh || safety_humid < safety_humid_ll) ? "0" : "1") + // Safety status
"," + String("\"temperature-2\":") +
(isnan(safety_temp) ? "\"nan\"" : String(safety_temp, 1)) + // Temp2 or "nan"
"," + String("\"humidity-2\":") +
(isnan(safety_humid) ? "\"nan\"" : String(safety_humid, 1)) + "}"; // Humid2 or "nan"
client.publish(topic.c_str(), payload.c_str()); // Send to cloud
Serial.println("Published: " + payload); // Confirm in serial monitor
delay(5000); // Wait 5 seconds before next publication
}
// -------------------------FUNCTION TO CHECK WIFI AND MQTT CONNECTION----------------------------
void checkWiFiAndMQTT() {
unsigned long now = millis(); // Current time
// A. Check WiFi connection
if (WiFi.status() != WL_CONNECTED) {
Serial.println("WiFi disconnected!"); // Alert
WiFi.reconnect(); // Try to reconnect
}
// B. Check MQTT connection (if WiFi is OK)
else {
if (!client.connected()) { // MQTT disconnected?
// Time to try reconnecting?
if (now - lastReconnectAttempt > reconnectInterval) {
lastReconnectAttempt = now; // Update timer
if (attemptReconnect()) { // Try to connect
lastReconnectAttempt = 0; // Reset timer on success
}
}
} else {
client.loop(); // Maintain MQTT connection
}
}
}
// ---------------------------MQTT RECONNECT FUNCTION-------------------------------
boolean attemptReconnect() {
Serial.println("Connecting to Ubidots MQTT..."); // Status
// Connect using token as username, empty password
if (client.connect("ESP32_Client", UBIDOTS_TOKEN, "")) {
Serial.println("Connected to Ubidots"); // Success
return true; // Report success
} else {
Serial.print("Failed, rc="); // Show error code
Serial.println(client.state());
return false; // Report failure
}
}
// ------------------------READ SENSORS FUNCTION--------------------------
void readSensors() {
// Read Sensor 1 (PID control)
//pid_temp = DHT22_PID.readTemperature(); // Get temp in °C
//pid_humid = DHT22_PID.readHumidity(); // Get humidity in %
// Read Sensor 2 (Safety protection)
safety_temp = DHT22_Safety.readTemperature(); // Get temp in °C
safety_humid = DHT22_Safety.readHumidity(); // Get humidity in %
// Check for sensor errors (NaN = Not a Number)
//if (isnan(pid_temp) || isnan(pid_humid) || isnan(safety_temp) || isnan(safety_humid)) {
// Serial.println("ERROR: Sensor reading failed!"); // Alert
//return; // Exit function
}
// ---------------------------Print readings to serial monitor--------------------------------
//Serial.println("PID DHT"); // Label for sensor1
//Serial.print("Temperature : "); Serial.println(pid_temp,1);
//Serial.print("Humidity : "); Serial.println(pid_humid,1);
Serial.println("Safety DHT"); // Label for sensor2
Serial.print("Temperature : "); Serial.println(safety_temp,1);
Serial.print("Humidity : "); Serial.println(safety_humid,1);
}