#include <DHT.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Constants for pin assignments
const int fanPin = 6; // Fan control pin (D6)
const int sprayPin = A0; // Spray control pin (A0)
const int pulsePin = 13; // Pulse control pin (D13)
const int dhtPin = 11; // DHT sensor pin (D11)
const int mat1Pin = 4; // Heating mat 1 control pin (D4)
const int mat2Pin = 5; // Heating mat 2 control pin (D5)
const int interruptFanPin = 3; // Interrupt pin for fan (D3)
const int interruptSprayPin = 2; // Interrupt pin for spray (D2)
const int photoresistorPin = A7; // Photoresistor pin (A7)
const int potentiometerPin = A1;
// Time intervals in milliseconds
const unsigned long fanInterval = 90 * 60 * 1000UL; // 90 minutes in milliseconds
const unsigned long sprayInterval = 60 * 60 * 1000UL; // 45 minutes in milliseconds
const unsigned long fanOnTime = 2 * 60 * 1000UL; // 2 minutes in milliseconds
const unsigned long sprayOnTime = 2 * 60 * 1000UL; // 2 minutes in milliseconds
const unsigned long pulseTime = 100; // 100 milliseconds
const unsigned long mainLoopFrequency = 5000; // 5 seconds
const unsigned long matOnTime = 1 * 60 * 60 * 1000UL; // 1 hour in milliseconds
const unsigned long matOffTime = 3 * 60 * 60 * 1000UL; // 3 hours in milliseconds
// DHT sensor type
#define DHTTYPE DHT22 // DHT 22
// Initialize DHT sensor
DHT dht(dhtPin, DHTTYPE);
// Initialize the LCD (I2C address is usually 0x27 or 0x3F)
LiquidCrystal_I2C lcd(0x27, 16, 2); // Change to your LCD I2C address if different
// Temperature thresholds
float Tmin = 23;
float Tmax = 24;
float Tset = 24;
float potentiometerRead = 500;
const float TrangeLow = 20;
const float TrangeHigh = 40;
// Night mode thresholds
const int nightModeThresholdLow = 2; // Below this value -> Night mode
const int nightModeThresholdHigh = 10; // Above this value -> Exit night mode
// State variables
volatile bool fan_is_on = false;
volatile bool spray_is_on = false;
volatile bool mat_is_on = false;
volatile bool interruptFanTriggered = false;
volatile bool interruptSprayTriggered = false;
bool nightMode = false; // Night mode flag
bool ignoreNM = false;
bool noSensorMode = false;
// Timing variables
unsigned long currentTime = 0;
unsigned long previousFanTime = -fanInterval;
unsigned long previousSprayTime = 0;
unsigned long lastLoopTime = -mainLoopFrequency; // Last time loop was executed
unsigned long matStartTime = 0; // Start time for mats
// Variables to store sensor data
float Tin = 0.0;
float hin = 0.0;
const int bufferLength = 20;
int photoresistorBuffer[bufferLength];
int bufferIndex = 0;
int averagePhotoresistorValue = 250;
int currentRead = 250;
// Custom character for degree symbol (°)
byte degreeSymbol[8] = {
B00110,
B01001,
B01001,
B00110,
B00000,
B00000,
B00000,
B00000,
};
void setup() {
// Initialize pins
pinMode(fanPin, OUTPUT);
pinMode(sprayPin, OUTPUT);
pinMode(pulsePin, OUTPUT);
pinMode(mat1Pin, OUTPUT);
pinMode(mat2Pin, OUTPUT);
pinMode(interruptFanPin, INPUT_PULLUP); // Configure interrupt pin for fan with pull-up resistor
pinMode(interruptSprayPin, INPUT_PULLUP); // Configure interrupt pin for spray with pull-up resistor
// Initialize states
digitalWrite(fanPin, LOW);
digitalWrite(sprayPin, HIGH);
digitalWrite(pulsePin, LOW);
digitalWrite(mat1Pin, HIGH);
digitalWrite(mat2Pin, HIGH);
// Start the DHT sensor
dht.begin();
// Initialize Serial for debugging
Serial.begin(9600);
// Initialize the LCD
lcd.init(); // Initialize the LCD lcd.init(); // Initialize the 16x2 LCD
lcd.backlight();
// Create the degree symbol character
lcd.createChar(0, degreeSymbol);
// Attach interrupts
attachInterrupt(digitalPinToInterrupt(interruptFanPin), handleFanInterrupt, FALLING);
attachInterrupt(digitalPinToInterrupt(interruptSprayPin), handleSprayInterrupt, FALLING);
// Initialize the buffer
for (int i = 0; i < bufferLength; i++) {
photoresistorBuffer[i] = 999;
}
}
void loop() {
currentTime = millis();
if(interruptSprayTriggered) {
// Display temporary message
displayTemporaryMessage("fan in " + String((fanInterval-(currentTime -previousFanTime)) / 1000 / 60) + " min");
delay(2000);
lcd.setCursor(0, 1); // Set cursor to the beginning of the second line
lcd.print(" "); // Print 16 spaces to clear the line
String statusStr = "f:" + String(fan_is_on) + " m:" + String(mat_is_on) + " s:" + String(spray_is_on);
lcd.setCursor(0, 1);
lcd.print(statusStr);
}
// Handle fan interrupt
if (interruptFanTriggered) {
interruptFanTriggered = false;
lastLoopTime = currentTime; // Reset loop timing for fan interrupt
turnOnFan(currentTime);
// reset NightMode
for (int i = 0; i < bufferLength; i++) {
photoresistorBuffer[i] = 999;
}
if (spray_is_on) {
turnOffSpray();
}
}
potentiometerRead = analogRead(potentiometerPin);
Tset = TrangeLow + (potentiometerRead/1023)*(TrangeHigh - TrangeLow);
// Execute the main loop every 5 seconds
if (currentTime - lastLoopTime >= mainLoopFrequency) {
lastLoopTime = currentTime; // Update last loop time
Tmax = Tset;
Tmin = Tmax - 1;
// Handle photoresistor reading
currentRead = analogRead(photoresistorPin);
photoresistorBuffer[bufferIndex] = currentRead;
bufferIndex = (bufferIndex + 1) % bufferLength; // Move to the next buffer position
// Compute the average of the buffer
int sum = 0;
for (int i = 0; i < bufferLength; i++) {
sum += photoresistorBuffer[i];
}
averagePhotoresistorValue = sum / bufferLength;
averagePhotoresistorValue = int(float(averagePhotoresistorValue)/1023 * 999);
// Check for night mode
if (averagePhotoresistorValue < nightModeThresholdLow && !nightMode && !ignoreNM) {
nightMode = true; // Enable night mode
Serial.println("Night mode ON");
} else if (averagePhotoresistorValue > nightModeThresholdHigh && nightMode) {
nightMode = false; // Disable night mode
Serial.println("Night mode OFF");
}
// Handle spray interrupt
if (interruptSprayTriggered) {
interruptSprayTriggered = false;
lastLoopTime = currentTime; // Reset loop timing for spray interrupt
turnOnSpray(currentTime);
if (fan_is_on) {
turnOffFan();
}
}
// Read data from DHT sensor
Tin = dht.readTemperature(); // Read temperature as Celsius
hin = dht.readHumidity(); // Read humidity
// Check if any reads failed and exit early (to try again).
if (isnan(Tin) || isnan(hin)) {
Serial.println("Failed to read from DHT sensor!");
lcd.setCursor(0,0);
lcd.print("NO SENSOR MODE ");
noSensorMode = true;
} else {
noSensorMode = false;
}
// Update the LCD display with the normal status
if (!interruptFanTriggered && !interruptSprayTriggered) {
updateLCD(); // Restore the normal status display
}
// Handle temperature control with hysteresis
if (!noSensorMode) {
if (Tin < Tmin && !mat_is_on) {
// Turn on heating mats
digitalWrite(mat1Pin, LOW);
digitalWrite(mat2Pin, LOW);
mat_is_on = true;
Serial.println("Heating mats ON");
} else if (Tin > Tmax && mat_is_on) {
// Turn off heating mats
digitalWrite(mat1Pin, HIGH);
digitalWrite(mat2Pin, HIGH);
mat_is_on = false;
Serial.println("Heating mats OFF");
}
} else {
// Handle mats in "NO SENSOR MODE"
if (!mat_is_on && currentTime - matStartTime >= matOffTime) {
// Turn on mats for one hour
digitalWrite(mat1Pin, LOW);
digitalWrite(mat2Pin, LOW);
mat_is_on = true;
matStartTime = currentTime;
Serial.println("Mats ON in NO SENSOR MODE");
} else if (mat_is_on && currentTime - matStartTime >= matOnTime) {
// Turn off mats after one hour
digitalWrite(mat1Pin, HIGH);
digitalWrite(mat2Pin, HIGH);
mat_is_on = false;
matStartTime = currentTime;
Serial.println("Mats OFF in NO SENSOR MODE");
}
}
// Handle fan timing only if not in night mode
if (!nightMode) {
if (!spray_is_on && (currentTime - previousFanTime >= fanInterval) && !fan_is_on) {
turnOnFan(currentTime);
}
if (fan_is_on && currentTime - previousFanTime >= fanOnTime) {
turnOffFan();
}
}
// Handle spray timing
if (!fan_is_on && (currentTime - previousSprayTime >= sprayInterval) && !spray_is_on) {
turnOnSpray(currentTime);
}
if (spray_is_on && currentTime - previousSprayTime >= sprayOnTime) {
turnOffSpray();
}
/* // DEBUGGING
Serial.print("Buffer content: ");
for (int i = 0; i < bufferLength; i++) {
Serial.print(photoresistorBuffer[i]);
if (i < bufferLength - 1) {
Serial.print(", ");
}
}
Serial.println();
int summa = 0;
for (int i = 0; i < bufferLength; i++) {
summa += photoresistorBuffer[i];
}
int average = summa / bufferLength;
Serial.print("Average value: ");
Serial.println(averagePhotoresistorValue);
*/
}
lcd.setCursor(12,1);
lcd.print(String(Tset,1));
}
void handleFanInterrupt() {
interruptFanTriggered = true;
}
void handleSprayInterrupt() {
interruptSprayTriggered = true;
}
void turnOnFan(unsigned long currentTime) {
digitalWrite(fanPin, HIGH);
fan_is_on = true;
previousFanTime = currentTime; // Reset timer for fan interval
Serial.println("Fan ON");
}
void turnOffFan() {
digitalWrite(fanPin, LOW);
fan_is_on = false;
Serial.println("Fan OFF");
previousSprayTime = currentTime + sprayInterval;
}
void turnOnSpray(unsigned long currentTime) {
digitalWrite(sprayPin, LOW); // reset spray by cutting power
delay(500);
digitalWrite(sprayPin, HIGH);
pulse(pulsePin, pulseTime); // Pulse D13 once
spray_is_on = true;
previousSprayTime = currentTime; // Reset timer for spray interval
Serial.println("Spray ON");
}
void turnOffSpray() {
digitalWrite(sprayPin, LOW);
spray_is_on = false;
Serial.println("Spray OFF");
delay(500);
digitalWrite(sprayPin, HIGH);
}
void pulse(int pin, int duration) {
digitalWrite(pin, HIGH);
delay(duration);
digitalWrite(pin, LOW);
}
void updateLCD() {
if (!noSensorMode) {
lcd.clear();
// Prepare text to display on the LCD
String tempStr = String(Tin, 1);
String humStr = String(hin,0) + "%";
// Clear the LCD and print the first line
lcd.setCursor(0, 0);
lcd.print(tempStr);
// Display degree symbol
lcd.write(byte(0)); // Display the custom degree symbol
lcd.print("C "); // Add "C" for Celsius
lcd.print(humStr);
lcd.setCursor(13,0);
lcd.print(String(currentRead));
} else {
lcd.setCursor(0, 0);
lcd.print("NO SENSOR MODE ");
}
// Print the second line with fan, mat, and spray states
String statusStr = "f:" + String(fan_is_on) + " m:" + String(mat_is_on) + " s:" + String(spray_is_on) + " " + String(Tset,1);
lcd.setCursor(0, 1);
lcd.print(statusStr);
if (nightMode) {
lcd.setCursor(0, 1);
lcd.print("NGT");
}
}
void displayTemporaryMessage(String message) {
// Display the temporary message on the LCD
lcd.setCursor(0, 1);
lcd.print(message);
}