//----------------------------------------------------------------//
//For DHT22 Sensor
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
#define DHTPIN 2
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
//For Sampling Rate Millis
unsigned long currentMillis;
unsigned long previousMillis;
unsigned long samplingRate = 2000; //This will be change to 2sec
//----------------------------------------------------------------//
//----------------------------------------------------------------//
//For Button Pin Assignment and States
#define left_btn A0
#define ok_btn A1
#define right_btn A2
//For Button States
int left_State;
int ok_State;
int right_State;
//For Buttons Flags
int LeftClick;
int RightClick;
//----------------------------------------------------------------//
//----------------------------------------------------------------//
//For Switching Menu
int EnterMode = 0;
int MainMenu = 0;
int SubMenu_2 = 0;
int SubMenu_3 = 0;
int SubMenu_4 = 0;
//For LCD Pins
#include <LiquidCrystal.h>
#define RS 13
#define EN 12
#define D4 11
#define D5 10
#define D6 9
#define D7 8
LiquidCrystal lcd(RS, EN, D4, D5, D6, D7);
//----------------------------------------------------------------//
//----------------------------------------------------------------//
//For PID Control
#include <PID_v1.h>
#define TempSensor 2
#define HeatingElement 3
//For Storing Precious Variables
//float SV_h = 20.0; //incase Humidity PID is used
float PV_h; //For Storing Humidity Input
//For Offset Values
float offset_t;
float offset_h;
//Define Variables we'll be connecting to
double SV_t = 35.0, PV_t, Output;
//Specify the links and initial tuning parameters
double kP_T = 2, kI_T = 5, kD_T = 1;
PID tempPID(&PV_t, &Output, &SV_t, kP_T, kI_T, kD_T, DIRECT);
int OnTimeInterval = 250;
unsigned long OnStartTime;
//----------------------------------------------------------------//
void setup() {
//For Serial Communication
Serial.begin(9600);
//For DHT Sensor to Begin
dht.begin();
sensor_t sensor;
//For PID Library
tempPID.SetOutputLimits(0, OnTimeInterval); //tell the PID to range between 0 and the full window size
tempPID.SetMode(AUTOMATIC); //turn the PID on
OnStartTime = millis();
//For Input Buttons
pinMode(left_btn, INPUT_PULLUP);
pinMode(ok_btn, INPUT_PULLUP);
pinMode(right_btn, INPUT_PULLUP);
//For Outputs
pinMode(HeatingElement,OUTPUT);
// set up the LCD's number of columns and rows:
lcd.begin(16, 2);
}
void loop() {
// set the cursor to column 0, line 1
// (note: line 1 is the second row, since counting begins with 0):
ButtonStates();
Display();
switch (EnterMode) {
case 1:
switch (MainMenu) {
case 1:
//Starts the Loop
sensorData(); //Runs the sensor Data gathering
PID(); //Runs the PID Controlling
break;
}
break;
}
}
void sensorData() {
//Will read DHT sensor every 2 seconds
currentMillis = millis();
if (currentMillis - previousMillis >= samplingRate) {
previousMillis = currentMillis;
PV_t = dht.readTemperature() + offset_t;
}
}
void PID() {
Serial.print("OnStartTime: ");
Serial.println(OnStartTime);
Serial.print("OUTPUT: ");
Serial.println(Output);
tempPID.Compute();
/************************************************
* turn the output pin on/off based on pid output
************************************************/
if (millis() - OnStartTime > OnTimeInterval)
{ //time to shift the Relay Window
OnStartTime += OnTimeInterval;
}
//Output Control
if (Output < millis() - OnStartTime) digitalWrite(HeatingElement, LOW);
else if (Output > millis() - OnStartTime) digitalWrite(HeatingElement, HIGH);
}
void Display() {
//For Ok BTN Selection
switch (EnterMode) {
case 0: //No Menu is selected yet; scanning all the menus
switch (MainMenu) {
case 0: //WELCOME SCREEN
//Prints at the LCD
lcd.setCursor(0,0);
lcd.print("Arduino PID Fish");
lcd.setCursor(0,1);
lcd.print("Tank Temp. Cntrl");
break;
case 1: //DISPLAY INFO
//Turns off the Relay if Display info is exited
digitalWrite(HeatingElement, LOW);
//Prints at the LCD
lcd.setCursor(0,0);
lcd.print(" Show Status ");
lcd.setCursor(0,1);
lcd.print("<- OK ->");
break;
case 2: //SET TARGET VALUES
lcd.setCursor(0,0);
lcd.print("Set Temperature");
lcd.setCursor(0,1);
lcd.print("<- OK ->");
break;
case 3: //CHANGE PID Gain
lcd.setCursor(0,0);
lcd.print("Adjust PID Gain");
lcd.setCursor(0,1);
lcd.print("<- OK ->");
break;
case 4: //CHANGE OFFSET VALUES
lcd.setCursor(0,0);
lcd.print(" Offset Adjust ");
lcd.setCursor(0,1);
lcd.print("<- OK ->");
break;
case 5: //IF SURPASSED THE TOTAL MENUS, GO BACK TO START
MainMenu = 0;
break;
case -1: //IF SURPASSED THE LOWEST VALUE, GOES BACK TO LAST
MainMenu = 4;
break;
}
break;
//When on of the Menu is selected
case 1:
switch (MainMenu) {
case 0: //WELCOME SCREEN
EnterMode = 0; //disabling enter mode in welcome screen
break;
case 1: //DISPLAY INFO
//Prints the data at LCD
//Shows the set Values
//Temperature
lcd.setCursor(0,0);
lcd.print("SET:");
lcd.setCursor(4,0);
lcd.print(SV_t, 1); //This will print the data in one decimal value instead of 2
lcd.setCursor(8,0);
lcd.print("C ");
//Humidity
lcd.setCursor(10,0);
lcd.print("O:");
//For Showing Output Value from 0~100%
double showData;
showData = map(Output, 0, OnTimeInterval, 0, 100); //maps out the value to 0~100%
lcd.setCursor(12,0);
lcd.print(showData, 0); //This will print the data with no decimal value
if (showData != 100 && showData >= 1) {
lcd.setCursor(14,0);
lcd.print(" %");
}
else if (showData == 0) {
lcd.setCursor(13,0);
lcd.print(" %");
}
else {
lcd.setCursor(15,0);
lcd.print("%");
}
//Shows the processed values
//Temperature
lcd.setCursor(0,1);
lcd.print("TankTemp: ");
lcd.setCursor(10,1);
lcd.print(PV_t, 1); //This will print the data in once decimal value instead of 2
lcd.setCursor(14,1);
lcd.print(" C");
break;
case 2: //SET VALUES
//For SETTING TEMPERATURE
switch (SubMenu_2) {
//Set Temp Option
case 0:
lcd.setCursor(0,1);
lcd.print(" Setpoint ");
break;
//For Exiting
case 1:
lcd.setCursor(0,1);
lcd.print(" Exit ");
break;
//LOOPING THE SCREEN
//For Going back to first option
case 2:
SubMenu_2 = 0;
break;
//For Going back to last option
case -1:
SubMenu_2 = 1;
}
break;
case 3: //SET PID VALUES
//For SETTING PID Values
switch (SubMenu_3) {
//Set Proportional Gain
case 0:
lcd.setCursor(0,1);
lcd.print(" Set kP_T ");
break;
//Set Integral Gain
case 1:
lcd.setCursor(0,1);
lcd.print(" Set kI_T ");
break;
//Set Derivative Gain
case 2:
lcd.setCursor(0,1);
lcd.print(" Set kD_T ");
break;
//For Exiting
case 3:
lcd.setCursor(0,1);
lcd.print(" Exit ");
break;
//LOOPING THE SCREEN
//For Going back to first option
case 4:
SubMenu_3 = 0;
break;
//For Going back to last option
case -1:
SubMenu_3 = 3;
break;
}
break;
case 4: //SET OFFSET VALUES
//For OFFSET Values
switch (SubMenu_4) {
//Temperature Offset
case 0:
lcd.setCursor(0,1);
lcd.print(" Temperature: ");
break;
//For Exiting
case 1:
lcd.setCursor(0,1);
lcd.print(" Exit ");
break;
//LOOPING THE SCREEN
//For Going back to first option
case 2:
SubMenu_4 = 0;
break;
//For Going back to last option
case -1:
SubMenu_4 = 1;
break;
}
break;
}
break;
//When a Sub-Menu is Selected
case 2:
//DISPLAY INFO
if (MainMenu == 1) { //if display info is selected,
EnterMode = 0; //EXITS back to MENU SELECTION
}
//SETTING VALUES
else if (MainMenu == 2) {
switch (SubMenu_2) {
//Temp Set
case 0:
lcd.setCursor(0,1);
lcd.print("Set Temp: ");
lcd.setCursor(10,1);
lcd.print(SV_t, 1);
lcd.setCursor(14,1);
lcd.print(" C");
break;
//Exits back to main menu
case 1:
//goes back to main menu
EnterMode = 0;
//resetting back the sub menu to 0
SubMenu_2 = 0;
break;
}
}
//SETTING PID VALUES
else if (MainMenu == 3) {
switch (SubMenu_3) {
//Proportional Gain Set
case 0:
lcd.setCursor(0,1);
lcd.print("Set kP: ");
lcd.setCursor(8,1);
lcd.print(kP_T, 1);
//to clear uncertain spaces
if (kP_T >= 9.50) {
lcd.setCursor(12,1);
lcd.print(" ");
}
else if (kP_T >= 0.0 && kP_T <= 9.50) {
lcd.setCursor(11,1);
lcd.print(" ");
}
break;
//Integral Gain Set
case 1:
lcd.setCursor(0,1);
lcd.print("Set kI: ");
lcd.setCursor(8,1);
lcd.print(kI_T, 1);
//to clear uncertain spaces
if (kI_T >= 9.50) {
lcd.setCursor(12,1);
lcd.print(" ");
}
else if (kI_T >= 0.0 && kI_T <= 9.50) {
lcd.setCursor(11,1);
lcd.print(" ");
}
break;
//Derivative Gain Set
case 2:
lcd.setCursor(0,1);
lcd.print("Set kD: ");
lcd.setCursor(8,1);
lcd.print(kD_T, 1);
//to clear uncertain spaces
if (kD_T >= 9.50) {
lcd.setCursor(12,1);
lcd.print(" ");
}
else if (kD_T >= 0.0 && kD_T <= 9.50) {
lcd.setCursor(11,1);
lcd.print(" ");
}
break;
//Exits back to main menu
case 3:
//goes back to main menu
EnterMode = 0;
//resetting back the sub menu to 0
SubMenu_3 = 0;
break;
}
}
//SETTING OFFSET VALUES
else if (MainMenu == 4) {
switch (SubMenu_4) {
//Temperature OFFSET
case 0:
lcd.setCursor(0,1);
lcd.print("T Offset: ");
lcd.setCursor(10,1);
lcd.print(offset_t, 1);
//to clear unsused spaces
if (offset_t <= -0.50 && offset_t >= -9.50) {
lcd.setCursor(14,1);
lcd.print(" C");
}
else if (offset_t <= -10.0) {
lcd.setCursor(15,1);
lcd.print("C");
}
else if (offset_t >= 0.0 && offset_t <= 9.50) {
lcd.setCursor(13,1);
lcd.print(" C");
}
else if (offset_t >= 10.0) {
lcd.setCursor(14,1);
lcd.print(" C");
}
lcd.setCursor(15,1);
lcd.print("C");
break;
//Exits back to main menu
case 1:
//goes back to main menu
EnterMode = 0;
//resetting back the sub menu to 0
SubMenu_4 = 0;
break;
}
}
break;
//Already in a sub-menu
case 3:
//For exiting menu 2
if (MainMenu == 2) {
EnterMode = 1;
}
//For exiting menu 3
else if (MainMenu == 3) {
EnterMode = 1;
}
//For exiting menu 4
else if (MainMenu == 4) {
EnterMode = 1;
}
break;
}
}
void ButtonStates() {
Serial.println(EnterMode);
//Serial.println(MainMenu);
left_State = digitalRead(left_btn);
ok_State = digitalRead(ok_btn);
right_State = digitalRead(right_btn);
if (ok_State == LOW) {
EnterMode++;
delay(150);
}
//For Changing Left and Right Keys during sub-menu or menu is selected
switch (EnterMode) {
//For MainMenu Switch
case 0:
if (left_State == LOW) {
MainMenu++;
delay(150);
}
else if (right_State == LOW) {
MainMenu--;
delay(150);
}
break;
//For SubMenu Switching
case 1:
switch(MainMenu) {
case 2: //Set Target Values
if (left_State == LOW) {
SubMenu_2++;
delay(150);
}
else if (right_State == LOW) {
SubMenu_2--;
delay(150);
}
break;
case 3: //Set PID Values
if (left_State == LOW) {
SubMenu_3++;
delay(150);
}
else if (right_State == LOW) {
SubMenu_3--;
delay(150);
}
break;
case 4: //Set Offset Values
if (left_State == LOW) {
SubMenu_4++;
delay(150);
}
else if (right_State == LOW) {
SubMenu_4--;
delay(150);
}
break;
}
break;
//For SubMenu Values Change
case 2:
switch (MainMenu) {
case 2: //Set Target Values
//For Selected SubMenu
switch (SubMenu_2) {
case 0: //If TEMPERATURE Set
if (left_State == LOW) {
//MakI_Tng Sure SV is not above the max set value
if (SV_t <= 100.00) {
SV_t = SV_t + 0.5;
delay(150);
}
}
else if (right_State == LOW) {
//MakI_Tng Sure SV is not Below the min set value
if (SV_t > 28.00) {
SV_t = SV_t - 0.5;
delay(150);
}
}
break;
}
break;
case 3: //Set PID Values
//For Selected SubMenu
switch (SubMenu_3) {
case 0: //If Proportional Gain is Set
if (left_State == LOW) {
//MakI_Tng Sure SV is not above the max set value
if (kP_T <= 50.00) {
kP_T = kP_T + 00.5;
delay(150);
}
}
else if (right_State == LOW) {
//MakI_Tng Sure SV is not Below the min set value
if (kP_T > 1.00) {
kP_T = kP_T - 00.5;
delay(150);
}
}
break;
case 1: //If Integral Gain is Set
if (left_State == LOW) {
//MakI_Tng Sure SV is not above the max set value
if (kI_T <= 50.00) {
kI_T = kI_T + 1;
delay(150);
}
}
else if (right_State == LOW) {
//MakI_Tng Sure SV is not Below the min set value
if (kI_T > 1.00) {
kI_T = kI_T - 1;
delay(150);
}
}
break;
case 2: //If Derivative Gain is Set
if (left_State == LOW) {
//MakI_Tng Sure SV is not above the max set value
if (kD_T <= 50.00) {
kD_T = kD_T + 1;
delay(150);
}
}
else if (right_State == LOW) {
//MakI_Tng Sure SV is not Below the min set value
if (kD_T > 1.00) {
kD_T = kD_T - 1;
delay(150);
}
}
break;
}
break;
case 4:
//For Selected SubMenu
switch (SubMenu_4) {
case 0: //If Temperature Offset is selected
if (left_State == LOW) {
//MakI_Tng Sure SV is not above the max set value
if (offset_t <= 50.00) {
offset_t = offset_t + 00.5;
delay(150);
}
}
else if (right_State == LOW) {
//MakI_Tng Sure SV is not Below the min set value
if (offset_t > -50.00) {
offset_t = offset_t - 00.5;
delay(150);
}
}
break;
}
break;
}
//Enters Set PID Valuse Sub Menu
break;
case 4:
EnterMode = 0;
break;
}
}