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

Контроль температуры нагревателя печи, с таймером на 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(); }              // <- если выключить будильник 


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

Публикации

Истории

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