/*
Проект терморегулятора с четырехстраничным меню, есть возможность менять уставку температуры, значение гистерезиса.
3 кнопки предназначены(по идее можно заменить на энкодер):
- ButtonUp_PIN - для увеличения какого либо значения.
- ButtonDown_PIN - для уменьшения какого либо значения.
- ButtonОк_PIN - для перелистывания меню.
- Выключение подсветки через BACKL_TOUT секунд, включение кнопкой - ОК.
- Реализована защита от обрыва датчика температуры(NTC), но надо проверить.
*/
// -------- НАСТРОЙКИ ---------
#define Therm_PIN A3 // Пин подключения NTC
#define Relay_PIN 11 // Пин подключения реле
#define ButtonDown_PIN 2 // Пин подключения кнопки прибавить значение
#define ButtonUp_PIN 3 // Пин подключения кнопки убавить значение
#define ButtonOk_PIN 4 // Пин подключения кнопки перелистывание меню
#define BACKL_TOUT 60 // таймаут отключения дисплея, секунды
// ------- БИБЛИОТЕКИ -------
#include <GyverNTC.h>
GyverNTC ntc(Therm_PIN, 9740, 4300, 25, 10000); //пин подкл.NTC, Rрезистора, бета коэф., t NTC, R NTC
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27, 16, 2); // Устанавливаем дисплей адрес может быть 0x27 или 0x3f
#include "GyverButton.h"
GButton ButtUp(ButtonUp_PIN);
GButton ButtDown(ButtonDown_PIN);
GButton ButtOk(ButtonOk_PIN);
// ------- ПЕРЕМЕННЫЕ -------
bool relayState = false; //состояние реле
float SETPOINT = 29; //переменная температуры
float HYSTER = 0.2; //переменная гистерезиса
float temp;
uint8_t punktMenu = 0; // Переменная отвечает за пункты меню, значение меняется при нажатии на кнопку ОК
uint8_t Other = 0;
boolean backlState = true;
uint32_t backlTimer;
bool ntcState = false;
// --------- SETUP ----------
void setup() {
//Serial.begin(9600);
pinMode(Relay_PIN, OUTPUT); // пин реле
// дисплей
lcd.init(); // Инициализация дисплея
lcd.backlight(); // Включаем подсветку дисплея
lcd.clear(); // Очищаем дисплей
}
// ---------- LOOP ----------
void loop() {
relay_regulator();
buttons();
//debug(temp);
reDraw();
backlTick();
sensorError();
}
// --------------------- ФУНКЦИИ -----------------------
//Ошибка(Е0) при обрыве датчика
void sensorError() {
if(analogRead(Therm_PIN) > 949 || analogRead(Therm_PIN) < 129) {
ntcState = false;
} else {
ntcState = true;
}
}
//Выключение дисплея через заданное время - BACKL_TOUT
void backlTick() {
if (backlState && millis() - backlTimer >= BACKL_TOUT * 1000) {
backlState = false;
lcd.noBacklight();
}
}
//Включение дисплея при нажатии на кнопки
void backlOn() {
backlState = true;
backlTimer = millis();
lcd.backlight();
}
//Обработка кнопок
void buttons() {
ButtUp.tick();
ButtDown.tick();
ButtOk.tick();
//Включение дисплея при нажатиии на кнопку ОК, при первом нажатии включается дисплей и только потом кнопка ОК начинает листать строки
if (ButtOk.isClick()) {
if (backlState) {
punktMenu++;
backlTimer = millis(); // сбросить таймаут дисплея
} else {
backlOn(); // включить дисплей
}
}
if (punktMenu > 3 or punktMenu < 0) punktMenu = 0; //Если достигли последней строки, переход на строку 0
}
//Обновление дисплея и меню
void reDraw() {
static uint16_t tmr;
if (millis() - tmr > 50) {
tmr = millis();
SETPOINT = constrain(SETPOINT, 0, 90 ); //Ограничение диапазона уставки t,C
HYSTER = constrain(HYSTER, 0, SETPOINT); //Ограничение диапазона гистерезиса t,C
switch (punktMenu) { // смотрим, какая опция сейчас выбрана
case 0:
lcd.setCursor(1, 0); lcd.print("Temp t,C:"); lcd.print(" ");
lcd.setCursor(1, 1); lcd.print("SetP t,C:"); lcd.print(" ");
// Вместо темпер. будет показывать Е0 если датчик в обрыве
if(ntcState == true) { lcd.setCursor(11, 0); lcd.print(ntc.getTempAverage(), 1); lcd.print(" "); }
else {lcd.setCursor(11, 0); lcd.print("E0 "); lcd.print(" "); }
lcd.setCursor(11, 1); lcd.print(SETPOINT, 1); lcd.print(" ");
// стереть предыдущую стрелочку и нарисовать новую
lcd.setCursor(0, 0); lcd.write(126); lcd.setCursor(0, 1); lcd.print(" ");
break;
case 1:
// Вместо темпер. будет показывать Е0 если датчик в обрыве
if(ntcState == true) { lcd.setCursor(11, 0); lcd.print(ntc.getTempAverage(), 1); lcd.print(" "); }
else {lcd.setCursor(11, 0); lcd.print("E0"); lcd.print(" ");}
lcd.setCursor(11, 1); lcd.print(SETPOINT, 1); lcd.print(" ");
if (ButtUp.isClick()) { SETPOINT +=0.1; /*backlOn();*/ }
if (ButtDown.isClick()) { SETPOINT -=0.1; }
if (ButtUp.isStep()) { SETPOINT +=2; }
if (ButtDown.isStep()) { SETPOINT -=2; }
// стереть предыдущую стрелочку и нарисовать новую
lcd.setCursor(0, 1); lcd.write(126); lcd.setCursor(0, 0); lcd.print(" ");
break;
case 2:
lcd.setCursor(1, 0); lcd.print("Hyster t,C:"); //lcd.print(" ");
lcd.setCursor(1, 1); lcd.print("Other:"); lcd.print(" ");
lcd.setCursor(12, 0); lcd.print(HYSTER, 1); lcd.print(" ");
lcd.setCursor(8, 1); lcd.print(Other); lcd.print(" ");
if (ButtUp.isClick()) { HYSTER +=0.1; }
if (ButtDown.isClick()) { HYSTER -=0.1; }
if (ButtUp.isStep()) { HYSTER +=1; }
if (ButtDown.isStep()) { HYSTER -=1; }
// стереть предыдущую стрелочку и нарисовать новую
lcd.setCursor(0, 0); lcd.write(126); lcd.setCursor(0, 1); lcd.print(" ");
break;
case 3:
lcd.setCursor(8, 1); lcd.print(Other); lcd.print(" ");
if (ButtUp.isClick()) { Other +=1; }
if (ButtDown.isClick()) { Other -=1; }
if (ButtUp.isStep()) { Other +=5; }
if (ButtDown.isStep()) { Other -=5; }
// стереть предыдущую стрелочку и нарисовать новую
lcd.setCursor(0, 1); lcd.write(126); lcd.setCursor(0, 0); lcd.print(" ");
break;
}
}
}
//Алгоритм поддержания температуры(релейный)
void relay_regulator() {
static uint32_t tmr;
if (millis() - tmr > 100) {
tmr = millis();
temp = ntc.getTempAverage();
if(ntcState == true) {
if (temp < (SETPOINT - HYSTER)) {
relayState = HIGH;
}
else if (temp > (SETPOINT + HYSTER)) {
relayState = LOW;
}
}
if (ntcState == false) relayState = LOW;
digitalWrite(Relay_PIN, relayState);
}
}
//Отладка
void debug(float temp) {
static uint32_t tmr;
if (millis() - tmr > 500) {
tmr = millis();
Serial.print(temp); // фактическая
Serial.print(',');
Serial.print(SETPOINT); // установка
Serial.print(',');
Serial.print(SETPOINT + HYSTER); // гистерезис
Serial.print(',');
Serial.print(SETPOINT - HYSTER); // гистерезис
Serial.print(',');
Serial.println(relayState * 2 + 40); // сост. реле
}
}
//