Как стать автором
Обновить

Контроль температуры нагревателя печи, с таймером на Arduino

Время на прочтение 8 мин
Количество просмотров 11K
Нужна была печь, для запекания полимерной глины. После не долгих поисков выбор пал на электрическую печь для кухни «КЕДР». Мощностью 600 ватт, с максимальной температурой 250 градусов, без регулятора. На первое время был установлен термомеханический регулятор, так как температура для работы требовалась в диапазоне 100-130 градусов. Но вся проблема заключалась в том, что у печи очень большой разгон(после отключения нагревателя температура продолжала расти еще на 20-50 градусов), а у регулятора очень большой диапазон включения и отключения. То есть устанавливая температуру в 130 градусов я получал диапазон 100 — 160 градусов, что не является допустимым.

После нескольких месяцев разбора принципов работы с Arduino IDE и C++ родился проект, который полностью удовлетворяет требованиям. Устройство умеет удерживать установленную температуру от 100 до 150 градусов, по достижению которой срабатывает установка таймера на 5-35 минут, в зависимости от установки, по истечению срабатывает будильник.

Здесь блок-схема работы программы.



В состав устройства входит: четыре последовательных нагревателя установленных в печи по умолчанию, которые можно заменить на любой другой нагреватель соответственной мощности, твердотельное реле SSR-25 DA, Arduino Pro Mini, инкрементальный энкодер с кнопкой подключенного через инвертирующим триггер Шмитта, дисплей WH1602D и два NTC терморезисторов MF58 на 100кОм.

Здесь код скетча, с комментариями.

Листинг
#include <LiquidCrystal.h>
#include "timer-api.h"
const int rs = 12, en = 11, d4 = 5, d5 = 4, d6 = 3, d7 = 6; // сюда подключаем дисплей
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);
int encCLK = 2 ;    // сюда подключаем энкодер 
int encDT = 7;      // сюда подключаем энкодер 
int button = 8 ;    // сюда подключаем кнопку

// в схеме энкодер подключен через инвертирующий триггер Шмитта!!!

volatile bool button_not_press = true; // здесь храним состояние кнопки

volatile int pinClkValue = 0; // переменные для хранения состояния энкодера
volatile int pinDtValue = 0;  // переменные для хранения состояния энкодера

uint8_t temp_cel[8] = {B00111,B00101,B00111,B00000,B00000,B00000,B00000};  // символ градуса

int temp_upside_pin = 14;   // сюда подключаем первый термодатчик  MF58 100k, расчет идет под него
int temp_downside_pin = 15; // сюда второй термодатчик. Вторые ноги вместе и на землю(-), так же
                            // два резисор делителя 100k на (+5v).  
volatile float temp_upside;   // здесь хранятся временные значения с термодатчиков
volatile float temp_downside; // здесь хранятся временные значения с термодатчиков

#define B 3950 // B-коэффициент для рсчета температуры

int heater_pin = 10; // сюда подключаем твердотельное реле SSR-25DA.
volatile bool preheating_state = false; // состояние преднагрева, с учетом энертности нагреваемого объекта
volatile bool heater_state = true;      // состояние нагревателя


volatile int hyst = 40;        // учетом энертности нагреваемого объекта подбор опытным путем
volatile int changeTemp;       // состояние изменения температуры за 60 сек.
volatile long timeHyst = 0;    // переменная для хранения времени
volatile long normalModeTime;  // переменная для хранения времени

volatile int curTemp = 0;      // здесь храниться текущая температура, после всех вычислений
volatile int setTemp = 0;      // здесь храниться установленная температура
int *pCurTemp = &curTemp;      // указатели, может они и не нужны...?
int *pSetTemp = &setTemp;      // указатели, может они и не нужны...?
volatile bool alarm;           // будильник
volatile int count = 0;        // шаг изменения параметров state*5
volatile int state = 0;        // счетчик импульсов энкодера
volatile int setTimeMinute = 0;       // переменная для хранения времени таймера в минутах
volatile int second = 0;              // переменная для хранения времени таймера в секундах
int *pMinute = &setTimeMinute; // указатели, может они и не нужны...?

volatile long currentTime = millis(); // переменная для хранения времени

//=======================================================

void setup() {
// установка режима работы библиотеки timer-api.h, генерит прерывание один раз за секунду
timer_init_ISR_1Hz(TIMER_DEFAULT);  

// режим работы заданного вход/выхода(pin)
pinMode(encCLK, INPUT);            // вход CLK энкодера
pinMode(encDT, INPUT);             // вход DT энкодера
pinMode(button, INPUT);            // вход button энкодера
pinMode(temp_upside_pin, INPUT);   // средняя точка делителя напряжения с термодатчика 1
pinMode(temp_downside_pin,INPUT);  // средняя точка делителя напряжения с термодатчика 2
pinMode(heater_pin, OUTPUT);       // выход на реле

attachInterrupt(0, clkEnc, CHANGE);// прерывание для управление энкодером

lcd.createChar(1, temp_cel);       // создаем символ градуса
// вывод на дисплей приветствия!!!
lcd.begin(16, 2);                  
lcd.setCursor(2, 0);
lcd.print("Polimer Clay");
lcd.setCursor(0, 1);
lcd.print("BURNER ver. 1.01");
delay(2000);
// вывод на дисплей меню
lcd.clear();
lcd.print("Set"); 
lcd.setCursor(11, 0);
lcd.print("Timer");
lcd.setCursor(0, 1);
lcd.print("Cur");
lcd.setCursor(9, 1);
lcd.print(" NotSet");

setupTemp(); // заходим в режим установки температуры
}
//=====================================================

void clkEnc(){ // обработчик прерывания энкодера типовой, взят на просторах

pinClkValue = digitalRead(encCLK); 
pinDtValue = digitalRead(encDT);
cli(); 
if (!pinClkValue && pinDtValue) state = 1; 
if (!pinClkValue && !pinDtValue) state = -1; 
if (pinClkValue && state != 0) {
if (state == 1 && !pinDtValue || state == -1 && pinDtValue) { 
count += state*5; 
state = 0; }}
sei(); }

//=====================================================

void setupTemp(){                      // функция установки температуры

count = 0;                             // обнулятор счетчика
button_not_press = true;               // без этого условия, сразу выходит из цикла while, когда заходим по нажатию кнопки
while(button_not_press){               // пока кнопка не нажата...
if (count <= 100) count = 100;           // ограничиваем диаппазон изменения температуры минимум 100
if (count >= 151) count = 150;         // ограничиваем диаппазон изменения температуры максимум 150
setTemp = count;                       // сохраняем значение температуры
// вывод на дисплей
lcd.setCursor(4, 0);
if(*pSetTemp<10) lcd.print("  ");
else if(*pSetTemp<100) lcd.print(" ");
lcd.print(*pSetTemp);
lcd.print("\1");
lcd.print("C");
lcd.print(" "); 
if(*pSetTemp !=0)button_not_press = digitalRead(button); // фиксируем установку
preheating_state = true;}}                               // включаем нагрев с учетом инертности

//=====================================================

void setupTimer(){               // установка таймера

lcd.setCursor(11, 0);            //устанавливаем курсор в нужное положение
lcd.print("Timer");              //вывод на дисплей
count = 0;                       // обнулятор счетчика
button_not_press = true;
lcd.setCursor(9, 1);             //стираем надпись 
lcd.print("       ");            //    NotSet
while(button_not_press){         // пока кнопка не нажата...
if (count <= -1) count = 0;      // ограничиваем диаппазон изменения времени в минутах - минимум 0
if (count >= 36) count = 35;     // ограничиваем диаппазон изменения времени в минутах - максимум 35
setTimeMinute = count;           // сохраняем значение времени
lcd.setCursor(11, 1);            //устанавливаем курсор в нужное положение
if(*pMinute<10)lcd.print("0");
lcd.print(*pMinute);             //вывод на дисплей МИНУТЫ
lcd.print(":00");                //вывод на дисплей СЕКУНДЫ формат 00:00
tempMonitor();                   // мониторим температуру
if(*pMinute !=0)button_not_press = digitalRead(button);} // фиксируем установку
if (*pMinute != 0){second = 0; timerStart();} // если таймер установлен, переходим к счетчику времени
timeEnd(); } // если время закончилось, будильник!!!

//======================================================

void tempMonitor(){                  //Тут вся соль проекта!!! 
// управление мощностью нагревателя путем изменения формы и времени подаваемого напряжения.
if(millis() > normalModeTime + 10){ heater_state = !heater_state; normalModeTime = millis();} 
// измерение и вывод температуры на дисплей каждые пол секунды
if(millis() > currentTime + 500){
lcd.setCursor(4, 1);
if(*pCurTemp<10) lcd.print("  ");
else if(*pCurTemp<100) lcd.print(" ");
lcd.print(*pCurTemp);
lcd.print("\1");
lcd.print("C");
lcd.print(" ");  

int US = analogRead(temp_upside_pin);     // считываем значение 1 датчика
int DS = analogRead(temp_downside_pin);   // считываем значение 2 датчика
int MID = (US+DS)/2;                      // считаем среднее значение
float tr = (5 / 1023.0) * MID ;           // преобразовываем в напряжение
float VR = 5 - tr;                        // считаем среднее напряжение
float RT = tr/(VR / 99000);               // считаем среднее сопротивление
float ln = log(RT / 100000);               
float TX = (1 / ((ln / B) + (1 / (25+273.15)))); // упрощенное уровнение Стейнхарта — Харта 
TX = round(TX - 273.15);                  // пересчет в градусы по Цельсию и округление
*pCurTemp = int(TX);                      // сохраняем значение температуры
currentTime = millis(); }


//поднимаем тепмературу до температуры с компенсацией (установленная - hyst)
if(preheating_state){ if(*pCurTemp < (*pSetTemp - hyst)) digitalWrite(heater_pin, HIGH); 
//если достигли температуры с компенсацией (установленная - hyst), 
//выключаем и ждем пока температура не достигнет установленного значения (pSetTemp). 
else if(*pCurTemp > (*pSetTemp - hyst)) digitalWrite(heater_pin, LOW); 
if (*pCurTemp >= *pSetTemp){ preheating_state = false; Serial.print(preheating_state); }

//если негрев был не достаточен и температура начала падать не достигнув нужного значения
//то включаем снова нагреватель, который будет выключаться условием предыдущего цикла
//в результате чего напряжение будет подаваться импульсами, что не даст быстрого
//роста температуры. Этот процес будет происходить,
//пока температура снова не начнет увеличиваться. 
if (millis() > timeHyst + 30000){  
changeTemp = *pCurTemp;           //один раз в 30 сек, сохраняем температуру для сравнения
timeHyst = millis(); }
if(changeTemp >= *pCurTemp){ 
if(!digitalRead(heater_pin)){
digitalWrite(heater_pin, HIGH);}}}

//если температура достигла указанного(pSetTemp) значения
//переходим в режим поддержания температуры включая нагрев с ограничением мощности нагревателя
//при падении температуры ниже 5 градусов от установленной 
if(!preheating_state){             
if(*pCurTemp < *pSetTemp - 5) {    
digitalWrite(heater_pin, heater_state);}
else digitalWrite(heater_pin, LOW); }}

//====================================================

void timer_handle_interrupts(int timer){second--;} 
  
//====================================================

//таймер выводит на дисплей установленное время и осуществляет обратный отсчет
//используя переменную second, которая уменьшаеться по прерыванию
//по истечению установленного времени включаеться будильник.

void timerStart(){

while(*pMinute+second != 0){  
if (second == 0){(*pMinute)--; second = 59;} 
lcd.setCursor(11, 1);
if(*pMinute<10)lcd.print(" ");
lcd.print(*pMinute);
lcd.print(":");
if(second<10)lcd.print("0");
lcd.print(second);
lcd.print(" ");
if(alarm){
  noTone(9);
  if(!digitalRead(button)){ alarm = false; noTone(9); return;}}
tempMonitor();
if(alarm) tone(9,100); }}

//===================================================

// Будильник !!! 
void timeEnd(){
alarm = true;  
lcd.setCursor(11, 0);
lcd.print("Alarm");
*pMinute = 1; //second = 30;   //будильник работает установленное <- время, если его не 
  timerStart();                //выключить, то произойдет сброс установленной температуры в 0
if(alarm){                     //и запуститься режим установки температуры, нагреватель будет отключен
*pSetTemp = 0;                 //если в течении работы будильника нажать на кнопку, снова включиться
digitalWrite(heater_pin, LOW); //режим установки таймера, с поддержкой установленной ранее температурой
lcd.setCursor(4, 0);
if(*pSetTemp<10) lcd.print("  ");
else if(*pSetTemp<100) lcd.print(" ");
lcd.print(*pSetTemp);
lcd.print("\1");
lcd.print("C");
lcd.print(" "); 
alarm = false; }}

//====================================================

//основной цикл почти ни чего не делает

void loop() { 
tempMonitor();  // <- запуск мониторинга температуры
if(*pSetTemp == 0 && !digitalRead(button)) setupTemp(); // <- если будильник дотикал до 0
if(*pCurTemp == *pSetTemp) setupTimer(); }              // <- если выключить будильник 


Так как это один из первых проектов, прошу побольше критики!
Теги:
Хабы:
+8
Комментарии 18
Комментарии Комментарии 18

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн
PG Bootcamp 2024
Дата 16 апреля
Время 09:30 – 21:00
Место
Минск Онлайн
EvaConf 2024
Дата 16 апреля
Время 11:00 – 16:00
Место
Москва Онлайн