#include <LiquidCrystal_I2C.h>
#include "DHT.h"
// Incubator Controller
const String versionNumber = "V0.1";
// Pins used
const int relayPin = 15;
const int dhtPin = 23;
const int upButtonPin = 19;
const int downButtonPin = 18;
const int ledRed = 0;
const int ledGreen = 2;
const int ledBlue = 4;
// Variables
float setHumidity = 60.0;
float currentHumidity = 0.0;
float setTemp = 37.6;
float currentTemp = 0.0;
bool upButtonState = false;
bool downButtonState = false;
// Pages
int pageNumber = 3;
int pageState = 0;
int previousPageState = 0;
int systemState = 0;
// Percentage buffers for temperature and humidity
const int temperatureBufferPercentage = 5;
const int humidityBufferPercentage = 5;
// LED state when pulled high and set low to turn on
const int ledStateWhenOn = LOW; // Define the state for turning on the LED
// To control the LED based on a pin state:
// digitalWrite(led, digitalRead(Pin) ? ledStateWhenOn : !ledStateWhenOn);
// Timers
unsigned long pressStartTime = 0; // Time when both buttons are pressed
const unsigned long pressDuration = 3000; // Duration in milliseconds
unsigned long releaseStartTime = 0; // Time when both buttons were last released
const unsigned long releaseDuration = 5000; // Duration in milliseconds
unsigned long intervalStartTime = 0; // Time when both buttons were last released
const unsigned long intervalDuration = 50; // Duration in milliseconds
// LCD
#define I2C_ADDR 0x27
#define LCD_COLUMNS 20
#define LCD_LINES 4
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
// DHT
#define DHTTYPE DHT22 // DHT 22 (AM2302), AM2321
DHT dht(dhtPin, DHTTYPE);
void setup()
{
Serial.begin(115200);
Serial.println("Hello, ESP32!");
dht.begin();
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Incubator");
lcd.setCursor(0, 1);
lcd.print("Controller "+versionNumber);
// Outputs
pinMode(relayPin, OUTPUT);
pinMode(ledBlue, OUTPUT);
pinMode(ledGreen, OUTPUT);
pinMode(ledRed, OUTPUT);
// Inputs
pinMode(upButtonPin, INPUT_PULLUP); // Enable internal pull-up resistor for 'up' button
pinMode(downButtonPin, INPUT_PULLUP); // Enable internal pull-up resistor for 'down' button
// Variables
previousPageState = pageState; // Initialize previousPageState to match pageState
delay(2000);
lcd.clear();
}
void loop()
{
updateInputs();
updateSystemState();
updateOutputs();
updateLeds();
updateLcd();
delay(10); // this speeds up the simulation
}
void updateInputs()
{
// DHT
currentHumidity = dht.readHumidity();
currentTemp = dht.readTemperature();
// Buttons
upButtonState = !digitalRead(upButtonPin); // Invert because the button is pulled high
downButtonState = !digitalRead(downButtonPin); // Invert because the button is pulled high
// Check if both buttons are pressed
if (upButtonState && downButtonState)
{
// If both buttons are pressed store the time
if (pressStartTime == 0)
{
pressStartTime = millis(); // Record the start time
}
// Check if the press duration has elapsed, if so update the set points
if (millis() - pressStartTime >= pressDuration)
{
updateSetPoint();
}
}
// If the up or down button is pressed then change the page number
else if (upButtonState) { pageState = (pageState + 1) % pageNumber; } // Increment pageState and wrap around at pageNumber
else if (downButtonState) { pageState = (pageState - 1 + 2) % pageNumber; } // Decrement pageState and wrap around at pageNumber
else
{
// Reset the press start time if either button is released
pressStartTime = 0;
}
}
void updateSetPoint()
{
// If the press duration has elapsed update the set point
lcd.clear();
while (upButtonState && downButtonState) // Wait until both buttons are released
{
upButtonState = !digitalRead(upButtonPin); // Invert because the button is pulled high
downButtonState = !digitalRead(downButtonPin); // Invert because the button is pulled high
}
while (true) // Loop for setting the humidity set point
{
// Read the state of the buttons
upButtonState = !digitalRead(upButtonPin); // Invert because the button is pulled high
downButtonState = !digitalRead(downButtonPin); // Invert because the button is pulled high
// If the up button is pressed increment the humidity set point
if (upButtonState) {setHumidity+=0.1;} // Increment by 0.1
// If the down button is pressed decrement the humidity set point
if (downButtonState) {setHumidity-=0.1;} // Decrement by 0.1
lcd.setCursor(0, 0);
lcd.print("Humidity Set:");
lcd.setCursor(0, 1);
lcd.print(setHumidity,1);
// If both buttons is not pressed, start counting time
if (!upButtonState && !downButtonState)
{
if (releaseStartTime == 0)
{
releaseStartTime = millis(); // Record the start time
}
// If both buttons are released for 5 seconds, break the loop
if (millis() - releaseStartTime >= releaseDuration)
{
lcd.clear();
break;
}
}
else
{
// Reset the release start time if both buttons are pressed
releaseStartTime = 0;
}
}
}
void updateOutputs()
{
// If humidity is lower than set point then turn the pump on
if (currentHumidity < setHumidity * (100 - humidityBufferPercentage) / 100) // If humidity low turn the pump on
{
digitalWrite(relayPin, HIGH); // Pump on
}
else
{
digitalWrite(relayPin, LOW); // Pump off
}
}
void updateSystemState()
{
// Update systemState based on temperature and humidity conditions
// Check if current temperature and humidity are within the percentage buffers of the set values
if (currentTemp < setTemp * (100 - temperatureBufferPercentage) / 100 ||
currentTemp > setTemp * (100 + temperatureBufferPercentage) / 100 ||
currentHumidity < setHumidity * (100 - humidityBufferPercentage) / 100 ||
currentHumidity > setHumidity * (100 + humidityBufferPercentage) / 100)
{
systemState = 0; // System bad
}
else
{
systemState = 1; // System good
}
}
void updateLeds()
{
digitalWrite(ledBlue, digitalRead(relayPin) ? ledStateWhenOn : !ledStateWhenOn); // ledBlue on when relayPin is HIGH
digitalWrite(ledRed, systemState ? !ledStateWhenOn : ledStateWhenOn); // ledRed on when systemState LOW
digitalWrite(ledGreen, systemState ? ledStateWhenOn : !ledStateWhenOn); // ledGreen on when systemState HIGH
}
void updateLcd()
{
// Check if intervalDuration have passed since the last LCD update
if (millis() - intervalStartTime >= intervalDuration)
{
intervalStartTime = millis(); // Update the last LCD update time
if (pageState != previousPageState) { lcd.clear(); }
previousPageState = pageState; // Update previousPageState when pageState changes
// Update LCD display based on the current page state
switch (pageState)
{
case 0:
{
lcd.setCursor(0, 0);
lcd.print("State: ");
lcd.print(systemState ? "Good" : "Bad ");
lcd.setCursor(0, 1);
lcd.print("H:");
lcd.print(currentHumidity,1);
lcd.print(" | ");
lcd.print("T:");
lcd.print(currentTemp,1);
break;
}
case 1:
{
lcd.setCursor(0, 0);
lcd.print("Humidity: ");
lcd.print(currentHumidity,1);
lcd.setCursor(0, 1);
lcd.print("Target: ");
lcd.print(setHumidity,1);
break;
}
case 2:
{
lcd.setCursor(0, 0);
lcd.print("Temp: ");
lcd.print(currentTemp,1);
lcd.setCursor(0, 1);
lcd.print("Target: ");
lcd.print(setTemp,1);
break;
}
}
}
}