// uncomment the line below if the lcd you are using is the i2c type,
// if its normal 16x2 you can leave the code as it is
// #define uselcd
#include <DHT.h>
/*
- For an ultra-detailed explanation of why the code is the way it is, please visit:
http://brettbeauregard.com/blog/2011/04/improving-the-beginners-pid-introduction/
- For function documentation see: http://playground.arduino.cc/Code/PIDLibrary
*/
#include <PID_v1.h>
#ifdef uselcd
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLS, LCD_ROWS);
#else
/*
The circuit:
* LCD RS pin to digital pin 12
* LCD Enable pin to digital pin 11
* LCD D4 pin to digital pin 5
* LCD D5 pin to digital pin 4
* LCD D6 pin to digital pin 3
* LCD D7 pin to digital pin 2
* LCD R/W pin to ground
* LCD VSS pin to ground
* LCD VCC pin to 5V
* 10K resistor:
* ends to +5V and ground
* wiper to LCD VO pin (pin 3)
*/
#include <LiquidCrystal.h>
const int rs = 12, en = 13, d3 = 14, d4 = 15, d5 = 16, d6 = 17, d7 = 18;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
#endif
#include <Encoder.h> // Include Encoder library
#include <HX711.h> // Include the HX711 library
#include <EEPROM.h>
// Define constants for pins and I2C address
#define DHTPIN 2
#define DHTTYPE DHT22
#define HEATER_PIN 3
#define FAN_PIN 4
#define HUMIDIFIER_PIN 5
#define BUTTON_PIN 6
#define BACK_BUTTON_PIN 7 // Define back button pin
#define LOADCELL_DOUT_PIN 9
#define LOADCELL_SCK_PIN 10
#define buttonPin 11
#define LCD_COLS 16
#define LCD_ROWS 2
#define I2C_ADDR 0x27
// Define rotary encoder pins
#define ENCODER_PINA 8
#define ENCODER_PINB 7
long lastPosition = -999;
// bool buttonState = HIGH;
// bool lastButtonState = HIGH;
#define DIRECTION_CW 0 // clockwise direction
#define DIRECTION_CCW 1 // counter-clockwise direction
int counter = 0;
int direction = DIRECTION_CW;
int CLK_state;
int prev_CLK_state;
// Define global variables for PID control
double SetpointTemp, InputTemp, OutputTemp;
double SetpointHum, InputHum, OutputHum;
PID TempPID(&InputTemp, &OutputTemp, &SetpointTemp, 1, 1, 1, DIRECT);
PID HumPID(&InputHum, &OutputHum, &SetpointHum, 1, 1, 1, DIRECT);
HX711 scale;
// Define global variables for user interface
enum Mode
{
TEMPERATURE,
HUMIDITY,
WEIGHT,
SET_TEMP,
SET_HUM,
CALIBRATE_WEIGHT
};
Mode currentMode = TEMPERATURE; // Default mode
bool enterButtonPressed = false;
bool backButtonPressed = false;
float weight;
DHT dht(DHTPIN, DHTTYPE);
Encoder myEncoder(ENCODER_PINA, ENCODER_PINB); // Define encoder object
float tempIncrement = 0.5; // Temperature increment
float minTemp = 34.0; // Minimum temperature
float maxTemp = 38.0; // Maximum temperature
float currentTemp = SetpointTemp; // Store current temperature
float humIncrement = 0.5; // Humidity increment
int minHum = 30; // Minimum humidity
int maxHum = 95; // Maximum humidity
float currentHum = SetpointHum; // Store current humidity
void setup()
{
pinMode(HEATER_PIN, OUTPUT);
pinMode(FAN_PIN, OUTPUT);
pinMode(HUMIDIFIER_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(BACK_BUTTON_PIN, INPUT_PULLUP); // Set back button pin as input
pinMode(ENCODER_PINA, INPUT);
pinMode(ENCODER_PINB, INPUT);
pinMode(buttonPin, INPUT_PULLUP);
TempPID.SetMode(AUTOMATIC);
HumPID.SetMode(AUTOMATIC);
dht.begin();
#ifdef uselcd
lcd.init();
lcd.backlight();
#else
lcd.begin(16, 2);
#endif
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN); // Initialize the load cell
Serial.begin(115200);
currentTemp = EEPROM.read(0);
currentHum = EEPROM.read(1);
}
void loop()
{
InputTemp = dht.readTemperature();
InputHum = dht.readHumidity();
weight = scale.get_units(); // Read weight from load cell
displayValues(InputTemp, InputHum);
// Check button press for mode selection
if (digitalRead(BUTTON_PIN) == LOW)
{
changeMode();
delay(200);
}
// Handle user interface
// handleUI();
// Control temperature and humidity based on mode
TempPID.Compute();
controlOutput(OutputTemp, HEATER_PIN);
HumPID.Compute();
controlOutput(OutputHum, HUMIDIFIER_PIN);
delay(500);
}
void controlOutput(double output, int pin)
{
if (output > 0)
{
digitalWrite(pin, HIGH);
}
else
{
digitalWrite(pin, LOW);
}
}
void handleUI()
{
if (true)
{
switch (currentMode)
{
case TEMPERATURE:
SetpointTemp = readSetpoint(34, 38, "Temp");
break;
case HUMIDITY:
SetpointHum = readSetpoint(30, 95, "Humidity");
break;
case SET_TEMP:
Serial.println("Setting temp");
setTemperature();
break;
case SET_HUM:
setHumidity();
break;
case CALIBRATE_WEIGHT:
calibrateWeight();
break;
}
enterButtonPressed = false;
}
if (backButtonPressed)
{
// Handle back button press
// Go back to default mode
currentMode = TEMPERATURE;
backButtonPressed = false;
}
}
void displayValues(double temp, double hum)
{
lcd.clear();
switch (currentMode)
{
case TEMPERATURE:
lcd.print("Temperature:");
lcd.setCursor(0, 1);
lcd.print("C=");
lcd.print(temp);
lcd.print(" T=");
lcd.print(SetpointTemp);
break;
case HUMIDITY:
lcd.print("Humidity:");
lcd.setCursor(0, 1);
lcd.print("C=");
lcd.print(hum);
lcd.print(" T=");
lcd.print(SetpointHum);
break;
case WEIGHT:
lcd.print("Weight:");
lcd.setCursor(0, 1);
lcd.print(weight);
lcd.print("kg"); // Placeholder for weight value
break;
case SET_TEMP:
// lcd.init();
lcd.print("Set Temp:");
lcd.setCursor(0, 1);
lcd.print("Set = ");
lcd.print(SetpointTemp);
Serial.println("Setting temp");
setTemperature();
break;
case SET_HUM:
lcd.print("Set Hum:");
lcd.setCursor(0, 1);
lcd.print("Set = ");
lcd.print(SetpointHum);
setHumidity();
break;
case CALIBRATE_WEIGHT:
lcd.print("Weight:");
lcd.print(weight);
lcd.print("kg");
lcd.setCursor(0, 1);
lcd.println("Calibrate weight");
calibrateMenu();
break;
}
}
void calibrateMenu()
{
while (1)
{
// Read button state
if (digitalRead(BUTTON_PIN) == LOW)
{
calibrateWeight();
enterButtonPressed = true; // Confirm setpoint
break;
}
// Check for back button press
if (digitalRead(buttonPin) == LOW)
{
changeMode();
backButtonPressed = true; // Go back without changing setpoint
break;
}
}
}
double readSetpoint(double minVal, double maxVal, String mode)
{
double setpoint = minVal;
lcd.clear();
lcd.print(mode);
lcd.print(": ");
lcd.setCursor(0, 1);
lcd.print(" Set = ");
lcd.print(SetpointHum);
while (!enterButtonPressed && !backButtonPressed)
{
// Read encoder position
long newPosition = myEncoder.read();
// Calculate new setpoint value based on encoder position
setpoint = map(newPosition, 0, 100, minVal * 10, maxVal * 10) / 10.0;
lcd.setCursor(0, 1);
lcd.print(" "); // Clear previous value
lcd.setCursor(0, 1);
lcd.print(setpoint);
// Read button state
if (digitalRead(BUTTON_PIN) == LOW)
{
enterButtonPressed = true; // Confirm setpoint
break;
}
// Check for back button press
if (digitalRead(BACK_BUTTON_PIN) == LOW)
{
backButtonPressed = true; // Go back without changing setpoint
break;
}
}
delay(200);
return setpoint;
}
void setTemperature()
{
lcd.clear();
lcd.print("Set Temp:");
lcd.setCursor(0, 1);
lcd.print("Set = ");
lcd.print(currentTemp);
while (1)
{
// Serial.println("setting temp");
long newPosition = myEncoder.read(); // Read encoder position
if (newPosition != lastPosition)
{
int set = 0;
if (encoder())
{
// currentTemp = currentTemp + tempIncrement; // Increment temperature
set = 1;
delay(500);
}
else if (!encoder())
{
// currentTemp -= tempIncrement; // Decrement temperature
delay(500);
}
currentTemp = constrain(currentTemp, minTemp, maxTemp); // Ensure temperature stays within limits
lcd.clear();
lcd.print("Set Temp:");
lcd.setCursor(0, 1);
lcd.print("Set = ");
lcd.print(currentTemp);
EEPROM.update(0, currentTemp);
lastPosition = newPosition;
}
if (digitalRead(BUTTON_PIN) == LOW)
{
SetpointTemp = currentTemp; // Save the set temperature
enterButtonPressed = true; // Confirm temperature setting
break;
}
}
delay(200);
}
void setHumidity()
{
while (1)
{
long newPosition = myEncoder.read(); // Read encoder position
if (newPosition != lastPosition)
{
lastPosition = newPosition;
int set = 0;
if (encoder())
{
// currentTemp = currentTemp + tempIncrement; // Increment temperature
set = 1;
delay(500);
} // else if (!encoder() ) {
// currentTemp -= tempIncrement; // Decrement temperature
// delay(500);
//}
lcd.clear();
currentHum = constrain(currentHum + humIncrement, minHum, maxHum); // Adjust humidity
lcd.print("Set Hums:");
lcd.setCursor(0, 1);
lcd.print("Set = ");
lcd.print(currentHum);
EEPROM.update(1, currentHum);
}
if (digitalRead(BUTTON_PIN) == LOW)
{
SetpointHum = currentHum; // Save the set humidity
enterButtonPressed = true; // Confirm humidity setting
break;
}
}
delay(200);
}
void calibrateWeight()
{
lcd.clear();
lcd.print("Calibrating...");
// Perform calibration using the library function
scale.set_scale(); // Reset the scale to 0
scale.tare(); // Set the tare weight as zero
delay(2000); // Simulate calibration process (adjust as needed)
// Get the calibration value
double calibrationValue = scale.get_units(10); // Get the average value of 10 readings
lcd.clear();
lcd.print("Calibrated!");
lcd.setCursor(0, 1);
lcd.print("Value: ");
lcd.print(calibrationValue); // Display calibrated value
delay(3000); // Show calibrated value for 3 seconds
}
void changeMode()
{
// Change mode when button is pressed
currentMode = static_cast<Mode>((currentMode + 1) % 6);
Serial.println(currentMode);
}
bool encoder()
{
// read the current state of the rotary encoder's CLK pin
CLK_state = digitalRead(ENCODER_PINA);
int set = 0;
counter = 0;
// If the state of CLK is changed, then pulse occurred
// React to only the rising edge (from LOW to HIGH) to avoid double count
if (CLK_state != prev_CLK_state && CLK_state == HIGH)
{
// if the DT state is HIGH
// the encoder is rotating in counter-clockwise direction => decrease the counter
if (digitalRead(ENCODER_PINB) == HIGH)
{
counter--;
direction = DIRECTION_CCW;
}
else
{
// the encoder is rotating in clockwise direction => increase the counter
counter++;
direction = DIRECTION_CW;
}
Serial.println(counter);
Serial.println("DIRECTION: ");
if (direction == DIRECTION_CW)
Serial.print("Clockwise");
else
{
Serial.println("Counter-clock");
set = 1;
switch (currentMode)
{
case SET_TEMP:
currentTemp = currentTemp + tempIncrement;
break;
case SET_HUM:
currentHum = currentHum + humIncrement;
break;
}
}
// Serial.print(" | COUNTER: ");
// Serial.println(counter);
}
if (set == 0 && CLK_state == HIGH)
{
direction = DIRECTION_CW;
Serial.println("Clockwise");
switch (currentMode)
{
case SET_TEMP:
currentTemp = currentTemp - tempIncrement;
break;
case SET_HUM:
currentHum = currentHum - humIncrement;
break;
}
// currentTemp = currentTemp - tempIncrement;
}
// save last CLK state
prev_CLK_state = CLK_state;
return direction;
}