Терморегулятор MAX6675 с таймером (Arduino)

Ранее в статье http://rcl-radio.ru/?p=110938 был показан пример терморегулятора на основе цифрового датчика DS18B20, на этой странице показан аналогичный пример создания терморегулятора, но на базе преобразователя (АЦП) MAX6675 (http://rcl-radio.ru/?p=45839).

При помощи MAX6675 (модуль) можно измерить ТЭДС термопары типа К (ХА), результат измерения выводится в градусах Цельсия и Фаренгейта, так же микросхема MAX6675 содержит встроенный датчик температуры окружающей среды.

MAX6675 в комплекте с термопарой типа К рассчитан на измерение температуры в диапазоне от 0 до 1024 ºС, с разрешением 0.25 ºС (точность 12 бит). Для передачи данных используется SPI интерфейс.

Используя преобразователь температуры MAX6675, можно собрать простой регулятор температуры. MAX6675 имеет диапазон измерения температуры от 0 до + 999 °C (программно ограничено до 999 °C), показания датчика температуры выводятся на дисплей LCD1602 + I2C (I2C модуль на базе микросхем PCF8574 позволяют подключить символьный дисплей 1602 к плате Arduino всего по двум проводам SDA и SCL (А4 и А5), что дает возможность не использовать цифровые выходы Arduino при подключении дисплея.) Органы управления состоят из энкодера KY-040 и двух кнопок.

Терморегулятор может содержать до 10 программ, в каждой программе можно задать температуру регулирования и время (в минутах) поддержания заданной температуры. Так же можно установить гистерезис. Все данные программ и значение гистерезиса сохраняются в энергонезависимой памяти.

Терморегулятор содержит основное меню, меню активации программ и настройки гистерезиса, а так же меню установки параметров (температуры и времени) программ.

Основное меню

На основном меню выводятся:

  1. данные от текущей температуре (от 0 до 999 °С)
  2. номер программы
  3. индикатор работы нагревательного элемента
  4. температура регулирования заданная в данной программе (0…999 °С)
  5. оставшееся время работы в заданной программе программы (в минутах от 0 до 255)

При нажатии на кнопку энкодера (в основном меню) происходит переход в меню активации программ и установки гистерезиса.

В меню активации программ, при установки параметра «ON» происходит запуск таймера, который отсчитывает время от установленного в программе.

Запуск таймера

Остановка таймера

Настройка гистерезиса от 0.0 до 5.0 °С

При запуске таймера происходит отсчет времени исполнения программы P_0 и поддержание заданной в этой программе температуры регулирования, как только время программы P_0 закончится, активируется программа P_1 со своим значение времени и температуры регулирования.

Терморегулятор будет поддерживать температуру по времени заданной в каждой программе до завершения времени работы программы P_9, после чего выход D13 (выход управления нагревательным элементом) становится не активным, происходит переход на программу P_0, которая будет неактивна (отсчет таймера приостанавливается).

Меню установки температуры и времени

На настройки температуры и времени исполнения программ используются кнопки «PROG» и «TEMP / TIMES». При нажатии на кнопку «PROG» происходит переход в меню настройки программ, кнопка энкодера позволяет пролистывать программы P_0…P_9. Кнопка «TEMP / TIMES» позволяет переключать настройки времени и температуры. Параметр меняется при помощи ручки энкодера, температуру можно установить от 0 до 999 °С, время от 0 до 255 минут.

Если время исполнения программы установить на 0 минут (OFF), то эта программа будет неактивна. Например если Вам нужно только 2 программы, то нужно установить в программах P_0 и P_1 время и температуру, а в остальных программа P_2…P_9 время установить на 0 минут (OFF).

Для выхода из режима программирования нужно нажать кнопку «PROG» или подождать 10 секунд для выхода в основное меню.

#include <EEPROM.h>
#include <max6675.h>            // http://rcl-radio.ru/wp-content/uploads/2018/07/max6675.zip
#include <LiquidCrystal_I2C.h>  // http://forum.rcl-radio.ru/misc.php?action=pan_download&item=45&download=1
#include <Wire.h>
#include <Encoder.h>            // http://rcl-radio.ru/wp-content/uploads/2019/05/Encoder.zip       
#include <EEPROM.h>
#include <MsTimer2.h>           // http://rcl-radio.ru/wp-content/uploads/2018/11/MsTimer2.zip 
 LiquidCrystal_I2C lcd(0x27,16,2);
 Encoder myEnc(8, 9);// DT, CLK
 MAX6675 thermocouple(4, 3, 2);// CLK, CS, DO
 byte v1[8] = {0x07,0x07,0x07,0x07,0x07,0x07,0x07,0x07};
 byte v2[8] = {0x07,0x07,0x00,0x00,0x00,0x00,0x00,0x00};      
 byte v3[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x1F,0x1F};
 byte v4[8] = {0x1F,0x1F,0x00,0x00,0x00,0x00,0x1F,0x1F};
 byte v5[8] = {0x1C,0x1C,0x00,0x00,0x00,0x00,0x1C,0x1C};
 byte v6[8] = {0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C,0x1C};
 byte v7[8] = {0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x07};
 byte v8[8] = {0x1F,0x1F,0x00,0x00,0x00,0x00,0x00,0x00};
 byte d1,d2,d3,d4,d5,d6,e1,e2,e3;
 int a[6],x,i_reg,temp_time;
 int reg_t[10],reg_time[10],reg_t0,reg_time0;
 bool prog,w;
 unsigned long times,times1,times_baza,times_cur,oldPosition  = -999,newPosition;
 int i,tic,tac,power,menu,gis=1,nagrev;
 int t_c,t_sum,t_iz; 
 
void setup(){
  Wire.begin();lcd.init();lcd.backlight();
  MsTimer2::set(1, to_Timer);MsTimer2::start();
  if(EEPROM.read(100)!=0){for(int i=0;i<101;i++){EEPROM.update(i,0);}}// очистка памяти при первом включении
  lcd.createChar(1, v1);lcd.createChar(2, v2);lcd.createChar(3, v3);lcd.createChar(4, v4);
  lcd.createChar(5, v5);lcd.createChar(6, v6);lcd.createChar(7, v7);lcd.createChar(8, v8);
  Serial.begin(9600);  
  pinMode(10,INPUT);       // button SW ENCODER 
  pinMode(5,INPUT_PULLUP); // button PROG
  pinMode(6,INPUT_PULLUP); // button PROG_TEMP_TIME
  pinMode(13,OUTPUT);      // OUTPUT REG TEMP
  for(int i=0;i<10;i++){reg_t[i] = EEPROM.read(i)*10+EEPROM.read(i+10);}
  for(int i=20;i<30;i++){reg_time[i-20] = EEPROM.read(i);}if(reg_time[0]==0){reg_time[0]=1;}
  gis = EEPROM.read(30);
  digitalWrite(13,LOW);
  } 
 
void loop(){
////// BUTTON /////////////////////////////////////////////////////////  
  if(digitalRead(5)==LOW&&prog==0){prog=1;menu=0;temp_time=0;times1=millis();w=1;lcd.clear();delay(300);}
  if(digitalRead(5)==LOW&&prog==1){prog=0;i_reg=0;temp_time=0;times1=millis();w=1;lcd.clear();delay(300);}
  if(digitalRead(6)==LOW&&prog==1){temp_time++;if(temp_time>1){temp_time=0;}times1=millis();w=1;delay(300);lcd.clear();}
  if(digitalRead(10)==LOW&&prog==1){i_reg++;temp_time=0;if(i_reg>9){i_reg=0;}times1=millis();w=1;lcd.clear();delay(300);}
  if(digitalRead(10)==LOW&&prog==0){menu++;times1=millis();w=1;lcd.clear();delay(300);if(menu>2){menu=0;}}
 
///////// REG_TEMP ///////////////////////////////
  if(prog==1&&temp_time==0){
   switch(i_reg){
     case 0: reg_t0 = reg_t[0];break;
     case 1: reg_t0 = reg_t[1];break;
     case 2: reg_t0 = reg_t[2];break;
     case 3: reg_t0 = reg_t[3];break;
     case 4: reg_t0 = reg_t[4];break;
     case 5: reg_t0 = reg_t[5];break;
     case 6: reg_t0 = reg_t[6];break;
     case 7: reg_t0 = reg_t[7];break;
     case 8: reg_t0 = reg_t[8];break;
     case 9: reg_t0 = reg_t[9];break;}
 
 if(newPosition != oldPosition){oldPosition = newPosition;reg_t0=reg_t0+newPosition;myEnc.write(0);newPosition=0;times1=millis();;w=1;temp_func();}
 
   switch(i_reg){
     case 0: reg_t[0] = reg_t0;break;
     case 1: reg_t[1] = reg_t0;break;
     case 2: reg_t[2] = reg_t0;break;
     case 3: reg_t[3] = reg_t0;break;
     case 4: reg_t[4] = reg_t0;break;
     case 5: reg_t[5] = reg_t0;break;
     case 6: reg_t[6] = reg_t0;break;
     case 7: reg_t[7] = reg_t0;break;
     case 8: reg_t[8] = reg_t0;break;
     case 9: reg_t[9] = reg_t0;break;}  
  }  
/////// REG_TIME //////////////////////////////////////////////
  if(prog==1&&temp_time==1){
   switch(i_reg){
     case 0: reg_time0 = reg_time[0];break;
     case 1: reg_time0 = reg_time[1];break;
     case 2: reg_time0 = reg_time[2];break;
     case 3: reg_time0 = reg_time[3];break;
     case 4: reg_time0 = reg_time[4];break;
     case 5: reg_time0 = reg_time[5];break;
     case 6: reg_time0 = reg_time[6];break;
     case 7: reg_time0 = reg_time[7];break;
     case 8: reg_time0 = reg_time[8];break;
     case 9: reg_time0 = reg_time[9];break;}
 
 if(newPosition != oldPosition){oldPosition = newPosition;reg_time0=reg_time0+newPosition;myEnc.write(0);newPosition=0;times1=millis();;w=1;time_func();}
 
   switch(i_reg){
     case 0: reg_time[0] = reg_time0;break;
     case 1: reg_time[1] = reg_time0;break;
     case 2: reg_time[2] = reg_time0;break;
     case 3: reg_time[3] = reg_time0;break;
     case 4: reg_time[4] = reg_time0;break;
     case 5: reg_time[5] = reg_time0;break;
     case 6: reg_time[6] = reg_time0;break;
     case 7: reg_time[7] = reg_time0;break;
     case 8: reg_time[8] = reg_time0;break;
     case 9: reg_time[9] = reg_time0;break;}  
  }  
 
  if(prog==1&&menu==0){
    lcd.setCursor(0,0);if(temp_time==0){lcd.print("*REG_TEMP");}else{lcd.print(" REG_TEMP");}lcd.print(i_reg);
    lcd.print(" ");lcd.print(reg_t[i_reg]);lcd.print(" ");lcd.setCursor(15,0);;lcd.print("C");
    lcd.setCursor(0,1);if(temp_time==1){lcd.print("*REG_TIME");}else{lcd.print(" REG_TIME");}lcd.print(i_reg);
    if(reg_time[i_reg]>0){lcd.print(" ");lcd.print(reg_time[i_reg]);lcd.print("  ");lcd.setCursor(15,1);lcd.print("M");}
    else{lcd.print(" ");lcd.print("OFF     ");}
    }
 ///////// READ TEMP /////////////////////////////////////////////
 if(millis()-times>300){times=millis();i++;t_c=thermocouple.readCelsius();t_sum=t_sum+t_c;}
 if(i>4){i=0;t_iz=t_sum/5;t_sum=0;}
 if(t_iz>999){t_iz=999;}if(t_iz<0){t_iz=0;}
 ///////// LCD BIG ///////////////////////////////////////////////
 if(prog==0&&menu==0){
     a[0]=t_iz/100;
     a[1]=t_iz/10%10;
     a[2]=t_iz%10;
     if(t_iz<100){a[0]=10;}
     if(t_iz<10){a[1]=10;}
   for(x=0;x<3;x++){
    switch(x){
        case 0: e1=0;e2=1,e3=2;break;
        case 1: e1=3,e2=4,e3=5;break;
        case 2: e1=6,e2=7,e3=8;break;
 
   }digit();}
   lcd.setCursor(9,1);lcd.print(char(223)); lcd.print("C"); 
   if(power==1){lcd.setCursor(13,1);lcd.print((reg_time[tac]-tic)/100);lcd.print((reg_time[tac]-tic)/10);lcd.print((reg_time[tac]-tic)%10);}
   else{lcd.setCursor(13,1);lcd.print("OFF");}
   lcd.setCursor(10,0);lcd.print("P");lcd.print(tac);
   lcd.setCursor(13,0);lcd.print(reg_t[tac]/100);lcd.print(reg_t[tac]/10%10);lcd.print(reg_t[tac]%10);}
//////////////////////////////////////////////////////////////////
 
////////// MENU /////////////////
if(prog==0&&menu==1){
  if(newPosition != oldPosition){oldPosition = newPosition;power=power+newPosition;myEnc.write(0);newPosition=0;times1=millis();w=1;if(power>1){power=0;}if(power<0){power=1;}}
  lcd.setCursor(3,0);lcd.print("heating");
  if(power==1){lcd.print(" ON ");}
  if(power==0){lcd.print(" OFF");tac=0;tic=0;}
  }
if(prog==0&&menu==2){
  if(newPosition != oldPosition){oldPosition = newPosition;gis=gis+newPosition;myEnc.write(0);newPosition=0;times1=millis();w=1;gis_fun();}
  lcd.setCursor(3,0);lcd.print("hysteresis ");
  lcd.setCursor(6,1);lcd.print((float)gis/10,1);
  lcd.print(char(223)); lcd.print("C"); 
  }  
 
///////// EEPROM ////////////////////////////////////////////////
  if(millis()-times1>10000 && w==1){
   for(int i=0;i<10;i++){EEPROM.update(i,reg_t[i]/10);EEPROM.update(i+10,reg_t[i]%10);}
   for(int i=20;i<30;i++){EEPROM.update(i,reg_time[i-20]);}
   EEPROM.update(30,gis);
   menu=0;prog=0;i_reg=0;w=0;temp_time=0;lcd.clear();} 
 
//////// AVTO REG TIMER /////////////////////////////////////////////
 
  if(millis()-times_baza>60000 && power==1){tic++;times_baza=millis();}
  if(reg_time[tac]==0){tac++;}
  if(tic>reg_time[tac]-1){tic=0;tac++; if(tac>9){tac=0;tic=0;power=0;}}
  if(power==0){times_baza=millis();digitalWrite(13,LOW);}
  if(power==1){
  if(reg_t[tac]*10 >= t_iz*10 + gis){digitalWrite(13,HIGH);nagrev=1;}
  if(reg_t[tac]*10 <= t_iz*10 - gis){digitalWrite(13,LOW);nagrev=0;}  
  }
  if(nagrev==1&&menu==0&&prog==0){lcd.setCursor(12,0);lcd.print("*");}
  if(nagrev==0&&menu==0&&prog==0){lcd.setCursor(12,0);lcd.print(" ");}
 
  Serial.print(reg_t[tac]*10);Serial.print("  ");Serial.print(t_iz*10);Serial.print("  ");Serial.println(gis);
 
  }// LOOP END 
 
void gis_fun(){if(gis<0){gis=0;}if(gis>50){gis=50;}}  
void time_func(){if(reg_time0>255){reg_time0=255;}if(reg_time0<0){reg_time0=0;}}
void temp_func(){if(reg_t0>999){reg_t0=999;}if(reg_t0<0){reg_t0=0;}}
void to_Timer(){newPosition = myEnc.read()/4;}  
 
void digit(){
  switch(a[x]){
    case 0: d1=1,d2=8,d3=6,d4=1,d5=3,d6=6;break;
    case 1: d1=32,d2=2,d3=6,d4=32,d5=32,d6=6;break;
    case 2: d1=2,d2=8,d3=6,d4=1,d5=4,d6=5;break;
    case 3: d1=2,d2=4,d3=6,d4=7,d5=3,d6=6;break;
    case 4: d1=1,d2=3,d3=6,d4=32,d5=32,d6=6;break;
    case 5: d1=1,d2=4,d3=5,d4=7,d5=3,d6=6;break;
    case 6: d1=1,d2=4,d3=5,d4=1,d5=3,d6=6;break;
    case 7: d1=1,d2=8,d3=6,d4=32,d5=32,d6=6;break;
    case 8: d1=1,d2=4,d3=6,d4=1,d5=3,d6=6;break;
    case 9: d1=1,d2=4,d3=6,d4=7,d5=3,d6=6;break;
    case 10:d1=150,d2=150,d3=150,d4=150,d5=150,d6=150;break;
    }
lcd.setCursor(e1,0);lcd.write((uint8_t)d1);
lcd.setCursor(e2,0);lcd.write((uint8_t)d2);lcd.setCursor(e3,0);lcd.write((uint8_t)d3);lcd.setCursor(e1,1);
lcd.write((uint8_t)d4);lcd.setCursor(e2,1);lcd.write((uint8_t)d5);lcd.setCursor(e3,1);lcd.write((uint8_t)d6);}   
 

 

Comments

  1. Добрый день. Собрал схему данного терморегулятора. Понравилась его работа, кроме одного момента: Реле ещё не подключал, но на 13 pin по идее должен быть высокий уровень? Я замеряю вольтаж — нулевой. В чём может быть проблема?

  2. Да, спасибо. Оказался не рабочим 13 выход. Поменял в скетче на свободный 12 — всё заработало. Один момент хотел выяснить: если в процессе работы регулятора, что-то пошло не так и нужно отключить нагреватель, энкодером не получится отключить?(выставляю OFF, программа останавливается, нагреватель работает). Поставить отдельный выключатель на нагреватель?

  3. У меня пока не получается запустить ((
    Валялся LCD1602 и отдельно плата IC2. Напаял плату на LCD, но на экране квадраты.
    Просканировал адрес IC2 оказалось что сидит на 0x3F, поменял сверху в скетче.
    Теперь на экране что то еле еле мелькает как будто плохой контакт, но если заливаю базовый скетч проверки LCD то надпись Нелоу Ворд отображается нормально (((
    Чип на IC2 стоит PCF8574A
    Не знаю куда копать (((

      1. Да вот тоже не пойму причины. delay(10) не помог. Думал что какая то проблема в библиотеке LCD (liquidcrystali2c), так как загрузил скетч соседнего проекта с датчиками DS18B20 и там таже история, а проекты где библиотека Lcd1602_i2c_low работают нормально. Но потом нашел проект «Регулятор громкости и тембра на PT2319 (Arduino)» и в нем похоже эта же библиотека применяется (liquidcrystali2c) но при этом все работает нормально. Записал видео коротенькое, что на экране https://disk.yandex.ru/i/s_CGsxEUYdDBUw

        1. У Вас энкодер не правильно подключен, а если точнее то кнопка энкодера, поэтому происходит постоянное переключение меню. На кнопке энкодера должен быть резистор 10К подключенный к +5В

          1. Энкодер у меня в виде модуля, а нем уже вроде как подтяжка должна быть распаяна,но вот конкретно этот модуль подключал впервые, может и дохлый, проверю плату.

          2. Вы правы. Частично проверил, похоже действительно глючный модуль энкодера. Модуль пока не проверял, а просто отключив его, пины (D8 D9 D10) подтянул резисторами к питанию и все заработало вроде как надо. Единственное возник вопрос, есть ли возможность коррекции термопары?

              1. Приветствую Вас! Еще раз благодарю за проект, очень полезный в быту. С энкодером Вы были полностью правы, на модуле KY-40 тупо не распаян резистор подтяжки кнопки, запаял его на пустое место и все заработало как надо. Перенес проект на плату. Можно я чуть чуть понаглею? ))) Данный терморегулятор нужен мне для управления индукционной плитой которая греет автоклав, так как раз нужно выйти на температуру 115-120С и держать ее определенное время в зависимости от рецепта и терморегулятор как бы полностью отвечает всем задачам, но у меня индукционная плита а не ТЭНы ((( Индукционка у меня с сенсорным управление то есть один раз прикоснулся к кнопке старт, она включилась, еще раз, она выключилась. То есть по сути управляется коротким импульсом, а все существующие терморегуляторы (по гистерезису) держат выход постоянно включенным или выключенным. Ну эту проблему я победил вроде как при помощи лома, кое как наваяв в FLProg мелкий скетчик (первый раз в жизни, после 30мин изучения по Ютубу FLProg) под Arduino которая имитирует нажатие кнопки на 2 секунды после чего отпускает ее. С Вашим терморегулятором все работает, но вылезла большая проблема, поскольку нет обратной связи от плиты, та может спокойно продолжать работать и после остановки таймера терморегулятора. Вот тут и возникает наглый вопрос. На сколько сложно вывести на отдельный пин сигнал о завершении работы таймера, на этот пин можно было бы повесить не только например светодиод сигнализирующий о работе программы но и например пищалку об окончании работы или как мне автоматический выключатель плиты. Таймер отработал, на отдельный пин вышел сигнал на какое то время (например пару минут) заорала пищалка и сработала релюшка которая принудительно выключит нагрузку. P.S. Не знаю, понятно объяснил суть ))))

  4. День добрый! Давно искал такой проект. Большое спасибо автору! Есть маленькая хотелка, как прикрутить буззер, чтобы пищал при окончании программы

  5. Здравствуйте. Поясните (для тупого) ))), программа начинает отсчет времени сразу после запуска? Или, что желательно в моём случае, отсчет оставшегося времени начинается после достижения заданной температуры?

  6. Извините за беспокойство, а можно ли сделать так, что бы например 1я программа работала без таймера, а завершала свою работу по достижении заданной температуры? Тогда было бы возможно во второй программе выставить такое же значение температуры, но уже с таймером. И была бы у меня радость не земная…))))

Добавить комментарий

Войти с помощью: