//Прога регулятора нагрева
//Библиотеки
//#include "PWMrelay.h"
//#include "GyverPID.h"
#include "GyverRelay.h"
//#include <EasyButton.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
//Месточисления для статуса
#define Off 0
#define On 1
#define Done 2
#define Err 3
//Объявление констант
//Константы пинов
const byte HeatOn_s_pin = 33; //Пин для включенного положения переключателя
const byte HeatOff_s_pin = 25; //Пин для выключенного положения переключателя (не используется)
const byte Done_pin = 26; //Светодиод готовности (с появлением экрана стал почти не нужен)
const byte Relay_pin = 32; //Управление реле нагрева
//Объявим переменные
byte Status = Off; //Переменная статуса нагрева; 0 - выкл, 1 - вкл, 2 - достигло цели, поддержание температуры
byte i = 0;//Переменная счётчика (для отладки)
unsigned long time_print = 1000; //Время таймера выводы на экран
//Переменные связанные с нагревом
unsigned long hyst_t = 5; //Гистерезис температуры
unsigned long goal_t = 400; //Целевая температура
unsigned long curr_t_pod = 10; //Текущая температура подложки (изменить тип данных на тот, который используется в библиотеке для MAX)
unsigned long curr_t_nag = 10; //Текущая температура нагревателя (изменить тип данных на тот, который используется в библиотеке для MAX)
unsigned long time_relay = 1000; //Время изменения состояния реле в мс
float k_t = 0.5; //Коэффициент обратной связи (нужно подобрать)
//Задание классов
GyverRelay regulator(REVERSE); //Задание класса регулятора
LiquidCrystal_I2C lcd = LiquidCrystal_I2C(0x27, 20, 4); //Задание класса экрана
//Создание клавиатуры
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {15, 2, 0, 4}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {16, 17, 5, 18}; //connect to the column pinouts of the keypad
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS );
class Timer {
/* Класс Таймера
В слассе имеются следующие функции:
1. Старт таймера (принимает на вход период)
2. Старт таймера (без данных на входе просто перезагружает таймер)
2. Стоп таймера (не реализовано так как не используется)
3. Пауза
4. Продолжение после паузы
5. Продолжение после паузы с аргументом на услучай перезапуска
6. Функция срабатывания (ничего не принимает на вход, возвращает кончился таймер или нет)
7. Функция срабатывания (принимает на вход bool на случай если при срабатывании требуется сразу перезапустить таймер)
8. Функция оставшегося времени (ничего не принимает, возвращает сколько времени осталось в мс)
Класс должен быть полностью самодостаточным. Все операции со временем должны производиться через него.*/
public:
Timer () {}
void start (unsigned long length_in) { //Процедура старта таймера с указанием времени
_length_st = length_in;
start();
}
void start () { //Процедура старта таймера с сохранившимся временем последнего старта
_length = _length_st;
_stop_t = millis() + _length;
_is_pause = 0;
}
//void stop () {}
void pause () { //Функция паузы
if (!_is_pause && !ready()) {
_is_pause = 1;
_length = _stop_t - millis();
}
}
void go () { //Функция продолжения после паузы
if (_is_pause) {
_is_pause = 0;
_stop_t = millis() + _length;
} else start();
}
void go (unsigned long length_in_go) { //Функция продолжения после паузы c аргументом на случай старта
if (_is_pause) {
_is_pause = 0;
_stop_t = millis() + _length;
} else start(length_in_go);
}
bool ready () { //Функция срабатывания без перезапуска
if (!length()) {
return 1;
}
return 0;
}
bool ready (bool restart) { //Функция срабатывания с перезапуском
if (!length()) {
if (restart) start();
return 1;
}
return 0;
}
unsigned long length () { //Функция вычисления оставшегося времени (главнее функции срабатывания)
if (!_is_pause) {if (millis() >= _stop_t) {return 0;} else {return (_stop_t - millis());}} else return _length;
}
void add (unsigned long length_add, bool restart_add) {
pause();
_length = _length + (length_add);
if (restart_add) go();
}
private:
//Приватные переменные
unsigned long _length_st = (5L * 60L * 1000L); //Стандартная длина таймера
unsigned long _length = _length_st; //Текущая длина таймера и оставшееся время
unsigned long _stop_t = 0; //время предполагаемого окончания (расчитывается от длины и времени начала, которое не хранится)
bool _is_pause = 0; //Переменная статуса паузы
};
//Создание таймеров
Timer tmr_relay; //Таймер изменения состояния реле
Timer tmr_1; //Таймер на 1 секунду для вывода логов и отладки
Timer tmr_print; //Таймер на 1 секунду для вывода инфы на экран
//Функция проверки статуса
void Status_check() {
if (curr_t_pod > goal_t + 15) {
Status = Err;
digitalWrite(Done_pin, LOW);
} else if (curr_t_pod > goal_t - 5) {
Status = Done;
digitalWrite(Done_pin, HIGH);
} else {
Status = On;
digitalWrite(Done_pin, LOW);
}
}
//Функция управления реле
void regulator_check() {
if (tmr_relay.ready(1)) {
//возможно здесь стоит считывать с датчиков текущую температуру, а может и не здесь
regulator.input = curr_t_pod; // сообщаем регулятору текущую температуру
digitalWrite(Relay_pin, regulator.compute(time_relay/1000)); //отправляем сигнал на реле
}
}
//Функция вывода информации на lcd экран
void print_display() {
lcd.setCursor(7, 0);
lcd.print(curr_t_pod);
lcd.setCursor(7, 1);
lcd.print(curr_t_nag);
}
//Функция приема нажатия клавиш (не используется)
void keypadget() {
char key = keypad.getKey();
if (key) {
Serial.println(key);
}
}
//Функция общения по Serial
void serialget() {
if (Serial.available()) {
int val = Serial.parseInt();
Serial.println(val);
}
}
void setup() {
Serial.begin(115200);
Serial.setTimeout(2);
Serial.println("Hello, ESP32!");
//Задание пинов переферии
pinMode(HeatOn_s_pin, INPUT);
pinMode(HeatOff_s_pin, INPUT);
pinMode(Done_pin, OUTPUT);
pinMode(Relay_pin, OUTPUT);
//Настройка регулятора
regulator.setpoint = goal_t; //Установка целевой температуры
regulator.hysteresis = hyst_t; //Установка значения гистерезиса
regulator.k = k_t;
//Задание таймеров
tmr_relay.start(time_relay);
tmr_print.start(time_print);
tmr_1.start(1000); //Таймер отладки
//Задание начального текста на экране
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("T pod");
lcd.setCursor(0, 1);
lcd.print("T nag");
lcd.setCursor(0, 2);
lcd.print("T goal ");
lcd.print((goal_t));
//lcd.setCursor(0, 3);
lcd.setCursor(11, 2);
lcd.print(" Heat Off");
}
void loop() {
Status_check();
regulator_check(); //Срабатывает по таймеру, чтобы передать температуру и изменить состояние реле
print_display(); //Вывод информации на lcd дисплей
delay(10); // this speeds up the simulation
//keypadget();
serialget();
}