//This program reads 3 NTC100K thermistors on the Nano's analog inputs and controls 3 SSRs on its digital outputs.
// By rotating the knob of the encoder and pressing the button at each of the 3 stages, a different temperature target can be set from room temperature to 300 °C.
// If the set temperature is higher than the one measured by the corresponding thermistor, then the SSR receives a signal to turn on.
// After setting a target temperature, the measured (rising) temperature is shown instead of the target.
// PID control implementation is yet to be done.
//Libraries used for the program
#include <LiquidCrystal_I2C.h>
#include <math.h>
//LCD screen parameters
#define I2C_ADDR 0x27 //I2C address of the LCD
#define LCD_COLUMNS 16 //Horizontal digits
#define LCD_LINES 2 //Vertical digits
LiquidCrystal_I2C lcd(I2C_ADDR, LCD_COLUMNS, LCD_LINES);
//Menu variables
int menuCounter = 0; //counts the crotations of the rotary encoder between menu items (0-2 in this case)
int menu1_Value = 0; //value within (below) TH1
int menu2_Value = 0;
int menu3_Value = 0;
bool menu1_selected = false; //enable/disable to change the value of menu item
bool menu2_selected = false;
bool menu3_selected = false;
const int RotaryCLK = 2; //CLK pin on the rotary encoder, interrupt pin
const int RotaryDT = 4; //DT pin on the rotary encoder
const int PushButton = 3; //Button to enter/exit menu, interrupt pin
//Statuses for the rotary encoder
int CLKNow;
int CLKPrevious;
int DTNow;
int DTPrevious;
bool refreshLCD = true; //refreshes values
bool refreshSelection = false; //refreshes selection (> / X)
//Temperature measurement variables
#define therm_pin1 = A0;
float T1_approx;
float V_0 = 3.3; // reference voltage
float R_1 = 220000.0; // TH1 resistor value for voltage divider
float a = 283786.2; // fit coefficients
float b = 0.06593;
float c = 49886.0;
int avg_size = 10; // averaging size
//SSR output pins
const int relayPin1 = 8; // SSR1 connected to digital pin 8
const int relayPin2 = 9; // SSR2 connected to digital pin 9
const int relayPin3 = 10; // SSR3 connected to digital pin 10
void setup() {
pinMode( therm_pin1 ,INPUT);
analogReference(EXTERNAL);
pinMode(2, INPUT_PULLUP); //RotaryCLK
pinMode(3, INPUT_PULLUP); //RotaryDT
pinMode(4, INPUT_PULLUP); //Button
pinMode(relayPin1, OUTPUT);
pinMode(relayPin2, OUTPUT);
pinMode(relayPin3, OUTPUT);
lcd.begin(16, 2);
lcd.backlight();
printLCD(); //print the stationary parts on the screen
//Store states of the rotary encoder
CLKPrevious = digitalRead(RotaryCLK);
DTPrevious = digitalRead(RotaryDT);
attachInterrupt(digitalPinToInterrupt(RotaryCLK), rotate, CHANGE); //CLK pin is an interrupt pin
attachInterrupt(digitalPinToInterrupt(PushButton), pushButton, FALLING); //PushButton pin is an interrupt pin
}
//--------------------Temperature measurement--------------------------------------
void loop() {
// loop over several values to lower noise
float T_sum = 0.0;
for (int ii;ii<avg_size;ii++){
// read the input on analog pin 0:
int sensorValue = analogRead(therm_pin1);
// Convert the analog reading (which goes from 0 - 1023) to voltage reference (3.3V or 5V or other):
float voltage = (sensorValue/1023.0)*V_0;
// this is where the thermistor conversion happens based on parameters from fit
T_sum+=(-1.0/b)*(log(((R_1*voltage)/(a*(V_0-voltage)))-(c/a)));
}
// averaging values from loop
T1_approx = T_sum/float(avg_size);
}
if (refreshLCD == true)
{
updateLCD();
if (menu1_selected == true || menu2_selected == true || menu3_selected == true )
{
}
else
{
updateCursorPosition(); //update the position
}
refreshLCD = false; //reset the variable - wait for a new trigger
}
if (refreshSelection == true) //if the selection is changed
{
updateSelection(); //update the selection on the LCD
refreshSelection = false; // reset the variable - wait for a new trigger
}
}
void rotate()
{
if (menu1_selected == true)
{
CLKNow = digitalRead(RotaryCLK); //Read the state of the CLK pin
if (CLKNow != CLKPrevious && CLKNow == 1)
{
if (digitalRead(RotaryDT) != CLKNow)
{
if (menu1_Value < 300)
{
menu1_Value++;
}
else
{
menu1_Value = 0;
}
}
else
{
if (menu1_Value < 1) //we do not go below 0
{
menu1_Value = 300;
}
else
{
menu1_Value--;
}
}
}
CLKPrevious = CLKNow;
}
else if (menu2_selected == true)
{
CLKNow = digitalRead(RotaryCLK);
if (CLKNow != CLKPrevious && CLKNow == 1)
{
if (digitalRead(RotaryDT) != CLKNow)
{
if (menu2_Value < 300)
{
menu2_Value++;
}
else
{
menu2_Value = 0;
}
}
else
{
if (menu2_Value < 1)
{
menu2_Value = 300;
}
else
{
menu2_Value--;
}
}
}
CLKPrevious = CLKNow;
}
else if (menu3_selected == true)
{
CLKNow = digitalRead(RotaryCLK);
if (CLKNow != CLKPrevious && CLKNow == 1)
{
if (digitalRead(RotaryDT) != CLKNow)
{
if (menu3_Value < 300)
{
menu3_Value++;
}
else
{
menu3_Value = 0;
}
}
else
{
if (menu3_Value < 1)
{
menu3_Value = 300;
}
else
{
menu3_Value--;
}
}
}
CLKPrevious = CLKNow;
}
else
{
CLKNow = digitalRead(RotaryCLK);
if (CLKNow != CLKPrevious && CLKNow == 1)
{
if (digitalRead(RotaryDT) != CLKNow)
{
if (menuCounter < 2) //we do not go above 3
{
menuCounter++;
}
else
{
menuCounter = 0;
}
}
else
{
if (menuCounter < 1) //we do not go below 0
{
menuCounter = 2;
}
else
{
menuCounter--;
}
}
}
CLKPrevious = CLKNow;
}
refreshLCD = true;
}
void pushButton()
{
switch (menuCounter)
{
case 0:
menu1_selected = !menu1_selected;
break;
case 1:
menu2_selected = !menu2_selected;
break;
case 2:
menu3_selected = !menu3_selected;
break;
}
refreshLCD = true; //Refresh LCD after changing the value of the menu
refreshSelection = true; //refresh the selection ("X")
}
void printLCD()
{
//These are the values which are not changing the operation
lcd.setCursor(1, 0);
lcd.print("TH1");
lcd.setCursor(7, 0);
lcd.print("TH2");
lcd.setCursor(13, 0);
lcd.print("TH3");
}
void updateLCD()
{
lcd.setCursor(1, 1);
lcd.print(" "); //erase the content by printing space over it
lcd.setCursor(1, 1);
lcd.print(menu1_Value); //print the value of menu1_Value variable
lcd.setCursor(7, 1);
lcd.print(" ");
lcd.setCursor(7, 1);
lcd.print(menu2_Value);
lcd.setCursor(13, 1);
lcd.print(" ");
lcd.setCursor(13, 1);
lcd.print(menu3_Value);
}
void updateCursorPosition()
{
//Clear display's ">" parts
lcd.setCursor(0, 0); //1st line, 1st block
lcd.print(" "); //erase by printing a space
lcd.setCursor(6, 0);
lcd.print(" ");
lcd.setCursor(12, 0);
lcd.print(" ");
//Place cursor to the new position
switch (menuCounter) //this checks the value of the counter (0, 1 or 2)
{
case 0:
lcd.setCursor(0, 0);
lcd.print(">");
break;
//-------------------------------
case 1:
lcd.setCursor(6, 0);
lcd.print(">");
break;
//-------------------------------
case 2:
lcd.setCursor(12, 0);
lcd.print(">");
break;
}
}
void updateSelection()
{
//When a menu is selected ">" becomes "X"
if (menu1_selected == true)
{
lcd.setCursor(0, 0); //1st line, 1st block
lcd.print("X");
}
//-------------------
if (menu2_selected == true)
{
lcd.setCursor(6, 0); //2nd line, 1st block
lcd.print("X");
}
//-------------------
if (menu3_selected == true)
{
lcd.setCursor(12, 0); //3rd line, 1st block
lcd.print("X");
}
if (T1_approx < menu1_Value) {
digitalWrite(relayPin1, HIGH);
lcd.setCursor(1, 1);
lcd.println(T1_approx, 0);
}
else {
digitalWrite(relayPin1, LOW);
}
//if (T2 < menu2_Value) {
digitalWrite(relayPin2, HIGH);
lcd.setCursor(7, 1);
lcd.println(T2, 0);
}
//else {
digitalWrite(relayPin2, LOW);
}
//if (T3 < menu3_Value) {
digitalWrite(relayPin3, HIGH);
lcd.setCursor (13, 1);
lcd.println(T3, 0);
}
//else {
digitalWrite(relayPin3, LOW);
}
}