Термошкаф и контроллер температуры на базе Arduino nano

Ghost_d
Offline
Зарегистрирован: 22.04.2014

Решил поделиться наработками.... и получить ХОРОШИЕ советы...

Поступил тут мне небольшой заказ (для товарища, т.е., бесплатно) на контроллер температуры в шкафу на балконе (там человек хранит всякие закатки/варенья/соленья и т.п.) До недавнего времени он использовал в качестве контроллера терморегулятор от холодильника. конструкция выглядела примерно так:

 

 

 

 

 

 

 

 

 

 

 

Щкаф разделен на два несвязанных отсека. В каждом отсеке установлен "нагреватель+вентилятор". Причем разведенная проводка подразумевает одновременное включение нагревателей. Нагреватель - это обычные лампочки на 220 вольт, а  вентиляторы - от БП на 12 вольт.

Сразу мне показалось как-то простовато все. Ставим два термодатчика (терморезисторы), переменный резистор для установки температуры, реле с обвязкой и полевик для управления вентиляторами.

Достаточно быстро все было размещено в корпус

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Провод с терморезисторами выносной, клеммники для подключения нагревателя, входного напряжения и вентиляторов. Три светодиода - индикаторы состояния и сетевой блок питания 220-> 12 Вольт.

Все проверено, реле включается, вентилятор включается, есть некие показания от терморезисторов.

Мой первоначальный план написания скетча выглядел достаточно просто...

Типа такого алгоритма:

1) проверяем датчики температуры Один раз в секунду. И выбираем минимальное значение (в разных отсеках шкафа может быть разная)

Если с датчиком что-то не так (не подключен, в обрыве и ли еще что-то) ставим код ошибки (1 - первый датчик не работает, 2 - второй не работает, 3- оба датчика не работают)

2) если минимальное значение температуры меньше некого установленного пользователем (с помощью переменника) и код ошибки не равен 3,  включаем подогрев и запоминаем запоминаем температуру с которого начинается нагрев.

Чтобы реле не колбасилось (на граничном значении) греем пока минимальная температура с датчиков не станет выше установленной на несколько (допустим 7-10) градусов.

3) Если нагрев произошел  - выключаем подогреватели, но оставляем на некоторое время работать вентиляторы...

4) если нагрев не произошел за некое установленное время - ставим код ошибки 4 (проблема с нагревателем).

Попутно, между делом сигналим светодиодом о наличии ошибки (вспышками, количество которых равно коду ошибки)

Получился такой вот код:

/*
Терморегулятор на базе Ардуино нано
концепция вер 3. Кратко. 31-10-2015
Даже описывать нечего.
Есть процедура получения температуры с датчиков. Минимальная из них, но не =0
Если минимальное значение ниже некого установленного пользователем предела, включаем нагрев и греем пока не будет достигнута некая установленная
планка относительно установленной температуры
после этого еще немного работает вентилятор, "для утряски"
===================================
термодатчики по 10 кОм
Таблица значений:
  T    ->   ADC      Middle
=======|==========|==========
4  С  ->               274
7  С  -> 332..345      338
12 С  -> 355..367      361
14 С  -> 372..386      379
21 С  -> 443..457      450
22 С  -> 458..477      467
24 С  -> 513..526      519
*/

//отладочный вывод в ком-порт
#define debug 1
//======== Определение пинов =========
#define relePin 12
#define ledRele 10
#define fanPin 11
#define ledFan 14
#define ledPower 3
#define t1Pin A1
#define t2Pin A2
#define adjPin A3

//интервал времени, если в течении которого не произошел нагрев датчика, то выдаем код ошибки 4
#define timeWorkTen 10000
long int startWorkTen = 0; //переменная содержит значение времени включения ТЭНа

//Предопределяем интервал времени включения вентилятора
#define timeWorkFan 5000
long int startWorkFan=0; //переменная содержит значение времени включения FAN


long int timeReadTemperature = 0; //переменная содержит значение времени для считывания температуры
//Предопределяем интервал времени считывания значения температуры 1 раз в секунду
#define interval_read_temp 1000

// константы температурных диапазонов для регулировки
#define min_set_temp 270
#define max_set_temp 350
// довесок ПЛЮС к температуре установленной пользователем до которого греем
#define set_delta_temp 10


boolean releStatus = 0; //текущий статус реле
boolean fanStatus = 0; //текущий статус вентилятора

unsigned int heatStartTemp; //значение температуры  ДО включения нагревателя
unsigned int d_min_temp; // глобальная переменная "min температуры"
unsigned int userSetTemp; // установленное пользователем (Алексом) значение температуры, которого следует держаться

volatile byte errorCode = 0; //Код ошибки (1 - Первый датчик-Х, 2- 2-ой датчик -Х, 3 - оба датчика - Х, 4- нагреватель - Х)
volatile long int changeTime = 0; //глобальная переменная следующего вызова процедуры отображения ошибки светодиодом
#define pauseFlash 400 // пауза для красивого вывода кода ошибки

// выполняется только при старте программы
void setup() {
#if debug
  Serial.begin(9600);
#endif

  // инициализация пинов
  pinMode(relePin, OUTPUT);
  pinMode(ledRele, OUTPUT);
  pinMode(fanPin, OUTPUT);
  pinMode(ledFan, OUTPUT);
  pinMode(ledPower, OUTPUT);
  releOff();
  fanOff();

  changeTime = millis();
  d_min_temp = get_min_temp(); // сразу делаем замер значений температуры, что бы понимать ГДЕ МЫ


}

//=============================
// Основной цикл программы
void loop()
{

  if ((millis() - timeReadTemperature) >= interval_read_temp) // делаем замер температуры через некие интервалы времени
  {
    d_min_temp = get_min_temp();

#if debug
    Serial.print("Min_temperature: ");
    Serial.println(d_min_temp);
    Serial.print(" Set temp= ");
    Serial.println(userSetTemp);
    Serial.print(" errorCode= ");
    Serial.println(errorCode);
    Serial.print("heatStartTemp ");
    Serial.println(heatStartTemp);
    Serial.println("=============");
#endif
  }
  //=============
  // если есть ошибки, то нужно сигнализировать об этом
  //errorCode=0;
  if (millis() >= changeTime) showError(errorCode); //если есть ошибки и наступило время для след. этапа отображения ошибки
  //==============
  // считываем значение с переменника и пересчитываем его в заданный диапазон
  userSetTemp = map(analogRead(adjPin), 0, 1023, min_set_temp, max_set_temp);
 // userSetTemp = 560;
  //============
  // если миним. температура с датчиков ниже заданной и подогрев не включен и есть РЕАЛЬНЫЕ показания с датчиков, включаем подогрев
  if ((d_min_temp < userSetTemp) && (!releStatus) && (errorCode != 3))
  {
    heatStartTemp = d_min_temp; //запоминаем температуру с которой стартовали
    if (errorCode = 4) errorCode = 0;
    fanOn();
    releOn();
    startWorkTen = millis(); // запоминаем время старта

#if debug
    Serial.print("-------- Start HEAT. Time ");
    Serial.println(startWorkTen);
#endif
  }
  //==================
  //выключаем если нагрели
  if ((releStatus) && (d_min_temp > (userSetTemp + set_delta_temp)))
  {
    releOff();
    //fanOff();
    startWorkFan=millis();
#if debug
    Serial.println("-------- Stop HEAT.");
#endif
  }
  //==================
  //анализ ошибки нагревателя
  // если ТЭН включен, а температура осталась такой же (или еще хуже уменьшилась) в течении заданного промежутка времени
  // значит ТЭН не работает
  if ((releStatus) && (d_min_temp <= heatStartTemp) && (millis() - startWorkTen > timeWorkTen))
  {
    errorCode = 4;
  }

  //============
//Если был включен ТОЛЬКО вентилятор (без реле) и он отработал нужный интервал времени - выключаем его
 if ((fanStatus) and (!releStatus) and ((millis()-startWorkFan)>=timeWorkFan)) 
 {
   fanOff();
   
    #if debug
       Serial.println("----------- Stop Fan.");
     #endif
 }

} //end main loop
//===============================
void releOn() //включение реле
{
  digitalWrite (relePin, HIGH);
  digitalWrite (ledRele, HIGH);
  releStatus = true;
}

void releOff()//выключение реле
{
  digitalWrite (relePin, LOW);
  digitalWrite (ledRele, LOW);
  releStatus = false;
}

void fanOn() //включение вентилятора
{
  digitalWrite (fanPin, HIGH);
  digitalWrite (ledFan, HIGH);
  fanStatus = true;
}

void fanOff()//выключение вентилятора
{
  digitalWrite (fanPin, LOW);
  digitalWrite (ledFan, LOW);
  fanStatus = false;
}
//===================
// Процедура получения значения температуры с датчиков
// возвращаем только минимальное значение датчика
// и тут же происходит определение ошибок (коды:1..3)
int get_min_temp()
{
  unsigned int t1, t2;
  t1 = readTemp(t1Pin); //читаем значение с первого датчика
  t2 = readTemp(t2Pin);
  timeReadTemperature = millis(); //
  if (errorCode < 4) errorCode = 0; //если не проблема с нагревателем, то пока ставим код ошибки 0, а далее посмотрим
  /*
  #if debug
       Serial.print("D1:");
       Serial.println(t1);
       Serial.print("D2:");
       Serial.println(t2);
     #endif
   */
  if ((t1 == 0) and (t2 != 0)) //если РАБОТАЕТ только второй датчик
  {
    errorCode = 1;
    return t2;
  }
  if ((t1 != 0) and (t2 == 0)) //если РАБОТАЕТ только первый датчик
  {
    errorCode = 2;
    return t1;
  }

  if ((t1 == 0) and (t2 == 0)) //если НЕ РАБОТАЮТ ОБА датчика
  {
    errorCode = 3;
    return 0;
  }

  return (min(t1, t2)); //если предыдущие проверки не прошли - возвращаем минимальное значение температуры
}

//===================
// Функция считывания "усредненного" значения с нужного входа АЦП
unsigned int readTemp(byte pin)
{
  unsigned int tmpRead = 0;
  for (byte i = 0; i < 8; i++)
  {
    tmpRead += analogRead(pin);
  }
  tmpRead = tmpRead / 8;
  return tmpRead;
}


//====== Show error code =========
void showError(byte errorCode)
{
  static byte showErrorCount; //Глобальная переменная, видимая только этой процедурой
  if (errorCode == 0) 
    {
    digitalWrite(ledPower, HIGH); //Если нет ошибок, просто включаем светодиод
    changeTime = (millis() + pauseFlash);
    }
  else
  {
    if (showErrorCount == 0) //если попади сюда первый раз
    {
      showErrorCount = errorCode * 2; //ставим значение счетчика равное удвоенному коду ошибки
      digitalWrite (ledPower, LOW); //гасим светодиод
      changeTime = (millis() + 4 * pauseFlash); //следующий вызов через
    }
    else
    {
      if (showErrorCount % 2) //если внутренний счетчик кратен 2
      {
        digitalWrite (ledPower, HIGH); //включаем светодиод
        showErrorCount = showErrorCount - 1; //уменьшаем значение счетчика
        changeTime = (millis() + pauseFlash / 4); //для красоты, след. вызов через половину заданного интервала
      }
      else
      {
        digitalWrite(ledPower, LOW); //если попадаем на нечетное значение, гасим светодиод
        showErrorCount = showErrorCount - 1;
        changeTime = (millis() + pauseFlash);
      }
    }
  }
} //end show error

Однако не все так просто, как казалось на первый взгляд...

Кроме косяков с отображением кодов ошибок (в случае переполнения millis()) -  этот момент надеюсь всорости решить) что-то меня еще беспокоит.

Как то неправильно выглядит принудительный сброс ошибки №4 при старте нагрева :(

Если внимательно отнестись к исходным данным, а именно нарисовать и рассмотреть все возможные комбинации, то получается много чего интересного:

 

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Все таки, это достаточно ответственное устройство, которое должно работать без присмотра.... Со слов товарища, если не работает вентилятор - то пару минут работы ламп и деревянная полка сверху начинает дымить.... Плохо! 
Я рассматривал вариант ориентироваться по средней температуре между датчиками - не катит.  Мало ли человек поставил в один отсек только что закатанную банку (пусть 40 градусов), а в соседнем шкафу -  около 5. Средняя - вроде 22. Все вроде как ок, греть не нужно...  Но в одном отсеке все стопудово замерзнет.
С минимальной температурой вроде как бы получше.... Но что будет, если одновременно выйдет из строя темодатчик T1 и правый нагреватель (см. картинку)?
В этом случае, минимальная температура берется от датчика Т2 и греем... на датчике Т2 температура остается на том же уровне а слева уже пожароопасная ситуация. Значит нужно блокировать работу устройства не только при коде ошибки №3 (не работают оба термодатчика), но и при ошибке №4 (не работает нагреватель, так как увеличение температуры не происходит).... Но тогда тоже можно все заморозить нафиг.
Подумываю о некой адаптивной системе, типа, пока все хорошо и работает, определяем время, за которое происходит существенный нагрев хотя бы на пару градусов.  И по этому времени в дальнейщем ориентируемся.
Включили, подождали пусть удвоенное время, нет эффекта. Пауза на некое время.  Опять пробуем....
Наверное можно попробовать вариант отдельного контроля левого и правого термодатчика....
 
Вот так, "простой" проект оказывается может нести в себе много нюансов.
У кого какие мысли? Добавлять дополнительные элементы в схему КРАЙНЕ не желательно.
 
 
 
ATkRnr
Offline
Зарегистрирован: 22.10.2013

Для подогрева можно испльзовать что нибудь другое, лампочки  галогенки небольшие на 12V  (батарея из лампочек соединеных параллельно) , подобрать такое количиство и мощность что бы ничего не дымило даже при отключенном вентиляторе. ТАкие испольщуют  для подсветки в мебельных шкафах на полках или в кухонных козырьках - встраиваемые, от них тепла достаточно. Или можно использовать сушилки  для обуви , которые вставляются в обувь. Датчики по температуре для надежности  три штуки в отсек,работают по схеме "два из трех" если два верное значение- то норма,  иначе если меньше двух то авария. Можно контроллировать ток на нагревательном элементе- если есть ток то норма иначе авария или неисправность. Схема должна работать даже если не включится ветилятор, при помощи естевственной циркуляции воздуха. Сделать дополнительные отверстия для естевственной циркуляци воздуха и нагреватели раположить соответственно.

Много можно накрутить, предусматреть дополнителные резервные нагреватели которые будут включаться если первые вышли из строя. Так же сигнализацию, как минимум, есть нет напряжения питания (~220В)

bwn
Offline
Зарегистрирован: 25.08.2014

Кодовое слово: "Диммер и пропорциональное регулирование".

Ghost_d
Offline
Зарегистрирован: 22.04.2014

К сожалению, что есть - то и нужно использовать. Таковы, если хотите, условия игры. Есть торчащие из стены три пары проводов: питание 220, подключение ламп нагрева и тонкие - вентиляторы.  Количество датчиков понятное дело можно было бы увеличить, но тут уже я сам себя ограничил (разведя и изготовя) плату под две штуки.

Насчет вентиляторов... Из моего опыта работы с вентиляторами, как правило полному выходу из строя предшествуют достаточно громкие и противные завывания...  Одновременный выход из строя и темодатчика и вентилятора в одном отсеке возможен, но маловероятен. Я уверен, что из имеющихся кубиков вполне возможно соорудить НАДЕЖНУЮ и эффективную систему. Понятное дело, что "Диммер и пропорциональное регулирование" - хорошо, но не в моем случае. 

Основные тезисы: 

- Выход из строя нагревателей полностью (лампы соеденены паралельно-последовательно) КРАЙНЕ МАЛОВЕРОЯТЕН

- вентиляторы тянут холодный воздух через нагреватель далее в шкаф

- Выход из строя вентиляторА  вполне можно отловить непропорциональным нагревом одного из отсеков шкафа (нагреватели в обоих отсеках работают одновременно).

- выход из строя термодатчика можно определить программно (в эл. схеме притяжка к земле)

- выход из строя (ну вдруг!) нагревателя - тоже можно определить программно (типа, 5 минут греем - а температура не равна нулю, но и не поднимается)

Пока не проверял, но уже существующих код (при полностью рабочих компонентах) уже должен работать. Главное предусмотреть возможные нештатные варианты и логику поведения при их возникновении

 

bwn
Offline
Зарегистрирован: 25.08.2014

Все вышеуказанные диммеры уместятся вместо платы с реле. У меня 4 штуки на плате 60х55 и это неплотно. Плюсы - нет резкого включения и соответствено перегорания ламп, я для своего "погребка" 2,5х3х1,8м использую 4 галогеновых прожектора по 100Вт. Второй сезон без нареканий и без замен. Прожектор удобен тем, что никак горячей поверхностью ни к чему не прикоснуться, да и лампу повредить сложно. Весь диапазон диммирования разбит на 7 ступенек, сакрального смысла нет, просто прибавлял 40 единиц на ступеньку.
Алгоритм - включаем на минимум, через три минуты проверка, температура ниже предыдущей или равна, следующая ступенька и так до максимума. Если на максимуме тоже не нагревает, тогда уже alarm. Пока не было)))).
Диммеров два канала для надежности. Кстати, почему вентиляторы не задублировать?

bodriy2014
bodriy2014 аватар
Offline
Зарегистрирован: 12.05.2015

Самое главное настройте автоматическую перезагрузку процессора при зависании а то повиснет и все погорит.(((

Соединить 2 или 3 сампы последовательно, чтоб даже при круглосуточной работе не горело дерево и вокруг проложите паранит или другое что не горючее. Пожар это страшная беда, никому не желаю!

И не зачем все усложнять упала температура ниже Т включили нагреватель и вентилятор до Т+5градусов.И так циклично.

Все эти ошибки и перепроверки как доп плюшки главное обеспечить базовые принцыпы пажаробезопасности!!!

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Много букв, не осилил....

НО!

1. Термосвич ( или несколько) от пожара обязательно.

2. Нагреватель - две-три соединённые последовательно лампы.

3. Вентиляция работает всегда ( можно снижать обороты при отключнии нагрева)

4. ПИД регулятор. И лучше возьмите готовый

Ghost_d
Offline
Зарегистрирован: 22.04.2014

Всем спасибо за полезные советы. Пока реализовал следующее:

"вычищена и причесана" процедура вывода кода ошибок

Основная концепция, которая должна предотвратить ситуации с диагональным выходом из строя двух компонентов: включаем нагрев по минимальной (из двух) температуре, а выключаем по максимальной. Попутно (пока все хорошо и равномерно) замеряем промежутки времени доя нагрева и время остывания системы. 

Если получили код ошибки 4 (ошибка нагревателя) переходим в аварийный режим работы - т.е. Греем по времени и остываем по времени.

во время тестирования на столе (с реальной лампочкой) все более менее ровно. Иногда происходила лажа с треском реле (при значительных расхождениях в показаниях датчиков сопоставимых с необходимой рзницей температур). Получалось, что сперва срабатывало условие включения (мин. Температура) и тут же выключение (по макс. Температуре). Решил вопрос пришлепкой-проверкой. 

Думаю, что такой же эффект дал бы бОльший интервал между чтением показаний датчиков. Учитывая кучу неплановых пришлепок и довесков, Появилось острое желание нарисовать алгоритм на бумажке и еще раз вдумчиво рассмотреть все возможные варианты

Если кому интересно, текущий код могу показать.

но чувствую, что данный проект- это то, как не нужно проектировать. 

inspiritus
Offline
Зарегистрирован: 17.12.2012

Обязательно нагревательные элементы необходимо подключать последовательно с термопредохранителями

Я ставлю три штуки на нагреватель, все соединяю последовательно.

можно применять и другой форм-фактор , благодаря наличию фланца лучше тепловой контакт. 

Обязательно применять не менее трех штук, соединенных последовательно .

Ghost_d
Offline
Зарегистрирован: 22.04.2014

Вот спасибо. А чего-то про них и не в помнил. Я такие видел в утюге ;) кстати, забыл упомянуть, что отловил еще один аппаратный глюк. А именно, при чтении показаний с АЦП значения как то сильно прыгали. Решилось установкой небольшого кондесатора паралелльно резистору (притяжка к земле)

Ach
Offline
Зарегистрирован: 25.12.2015

Делал себе на балконе нечто подобное, овощехранилище. В качестве нагревателя использовал термокабель, наподобие того, что в теплых полах используют, несколько витков на полу положил а над ним уже располагаются полки. Из электроники - обычный терморегулятор, через который в 220 включил термокабель. Установил температуру включения/выключения, и запускаю это всё в работу зимой, чтобы овощи хранилась/ не мерзли. Результат превзошел ожидания - лет 5 уже полет отличный. Термокабель в отличии от ламп накаливания пожаробезопаснее во много раз, как мне кажется, ну, и в плане перегорания, тоже.

flamantino
Offline
Зарегистрирован: 13.06.2014

как более подробно - где ставили конедесатор  какой номинал ? - и какие именно показания прыгают - Интересно - у меня что то тоже плавают показания - На макете все ОК - как в среду эксплуатации начинается ))