Контроллер (терморегулятор) холодильника Stinol 104 NoFrost

Побитый временем холодильник Stinol 104 потерял термостат, таймер, и все термодатчики. Длительное время работал на китайском терморегуляторе, разморозка морозильной камеры производилась вручную перемыканием контактов. Ввиду отсутствия понимания принципов работы плачущего испарителя терморегулятор был настроен неправильно, из-за чего приходилось периодически доставать из холодильного отделения брусок льда… Было принято решение восстановить полную функциональность при помощи Arduino, благо нашлось свободное время и желание заняться медитативным программированием. Да, можно купить термоконтроллер на 2 датчика и закрыть вопрос, но…

Что из себя представляет холодильник Stinol 104 с NoFrost: Морозильное отделение содержит испаритель оснащенный контуром размораживания и принудительным обдувом вентилятором, холодильное содержит плачущий испаритель. Все это означает что через 6-12 часов должна автоматически запускаться разморозка испарителя морозильной камеры. Размораживать холодильное отделение нет никакой необходимости, так как плачущий испаритель оттаивает после каждого цикла работы компрессора без участия нагревателя.

Железо:

  1. Arduino Nano - 1шт.
  2. LCD 8X2 - 1шт.
  3. Энкодер - 1шт.
  4. Модуль реле 4 канала - 1шт.
  5. Термодатчик LM75A - 2шт.
  6. Блок питания 7.5V 2A - 1шт.

Термодатчики расположены на испарителях морозильного и холодильного отделения. По термодатчику холодильного отделения происходит терморегуляция, а по термодатчику морозильного отделения, происходить контроль включения вентилятора и терморегуляция нагревателя. Вентилятор включается только когда температура испарителя морозильного отделения опустится до -10С, далее включается и выключается вместе с компрессором.

Циклограммы:

// нормальный режим работы
10 пауза 5 минут
20 режим заморозки 12 часов
30 режим разморозки 25 минут
40 goto 20

// режим в случае ошибки любого термодатчика
10 пауза 30 минут
20 включить компрессор на 30 минут
30 goto 10

Установка температуры осуществляется в “попугаях”. При нажатии на энкодер появляется возможность установить температуру, чем больше блоков тем сильнее морозит. Полный аналог механического регулятора. Минимальное значение -14С максимальное -21С. В верхней строке отображается температура испарителя холодильного отделения. Дальше птичками отображается текущее состояние реле в последовательности: fan, frost, defrost (вентилятор, компрессор и нагреватель). Нижняя строка отображает текщий режим работы.

Лицевая панель вмонтирована в боковую панель холодильника прямо в тело утеплителя. Было прорезано окно, просверлены отверстия, нарезана резьба М3. Блок питания и релейный модуль расположены под верхней крышкой холодильника так-же в теле утеплителя. Ардуино расположена за дисплеем. Там все соединено проводами. Для термодатчиков были изготовлены маленькие коробочки в которые были залиты прозрачным силиконовым гелем готовые модули с термодатчиками LM75A.

В процессе тестирования периодически в моменты отключения компрессора, Arduino сбрасывался, помогла решить проблему только снаберная цепочка подключенная параллельно компрессору. Резистор 5 Ом, и конденсатор на 5 Мкф 400 В. Конденсатор пусковой, резистор проволочный 5 Вт.

Важно! Возможно кому-то будет предостережением. Один раз я недоглядел и от нагревателя оплавился пластик, нагрелось очень сильно. Планирую восстановить термопредохранитель на 72С во избежании повторения ситуации на случай если залипнет реле.

#include <LiquidCrystal.h>
#include <EEPROM.h>
#include <LM75A.h> // https://github.com/QuentinCG/Arduino-LM75A-Temperature-Sensor-Library
#include <avr/wdt.h>

const int8_t s1 = 7; // энкодер
const int8_t s2 = 8; // энкодер
const int8_t key = 9; // кнопка энкодера
const int8_t fan = A1; // вентилятор
const int8_t frost = A2; // компрессор
const int8_t defrost = A3; // нагреватель
LiquidCrystal lcd(12, 10, 5, 4, 3, 2); // lcd 8x2
LM75A sensor_1(0, 0, 0); // lm75a_sensor_1 0x48 I2C: A4 (SDA), A5 (SCL)
LM75A sensor_2(1, 0, 0); // lm75a_sensor_2 0x49 I2C: A4 (SDA), A5 (SCL)

const int8_t on_temp = 4; // температура включения компрессора
const int8_t off_temp = -14; // температура отключения компрессора
int8_t set_temp = 0; // установка температуры отключения компрессора от -21 до -14 (off_temp)
int8_t batton = 0; // переменная кнопки
bool error = 0; // переменная ошибки
uint16_t val_action = 200; // режим работы
float temp_1 = 0; // температура испарителя холодильной камеры
float temp_2 = 0; // температура испарителя морозильной камеры
bool relay[3] = {0, 0, 0}; // fan, frost, defrost
const uint32_t second = 1000;
const uint32_t minute = 60000;
const uint32_t hour = 3600000;

void setup() {
  Serial.begin(9600);
  pinMode(fan, OUTPUT); // вентилятор
  pinMode(frost, OUTPUT); // компрессор
  pinMode(defrost, OUTPUT); // нагреватель
  set_relay(0, 0, 0); // все выключаем
  lcd.begin(8, 2);
  lcd.clear();
  set_temp_check(); // проверка и установка температуры из EEPROM
  Serial.println("Copyright (c) 2024 RasselFast");
  Serial.print("set temperature: ");
  Serial.println(set_temp);
  wdt_enable (WDTO_2S);
}

void loop() {
  wdt_reset(); // Сбрасывам счетчик wdt
  if (!batton) if (!digitalRead(key)) batton = 1;
  encoder(batton); // установка температуры отключения компрессора
  static uint32_t previous_millis = 0;
  uint32_t current_millis = millis();
  if (current_millis - previous_millis > second) { // выполнять раз в секунду
    previous_millis = current_millis;
    serial_print_telemetry(); // печать телеметрии в Serial
    get_sensor(); // читаем данные с термодатчиков
    action(val_action); // режим работы
    print_temperature(); // печать температуры испарителя холодильной камеры
    print_relay_state(); // печать состояния реле: fan, frost, defrost
    if (!batton) print_action(val_action); // печать текущего режима работы
  }
}

/// установка температуры отключения компрессора относительно off_temp ///
void encoder(int8_t val) {
  static uint32_t previous_millis;
  static uint32_t current_millis;
  static int8_t bar_temp = 0;
  const int8_t min_bar = 1;
  const int8_t max_bar = 8;
  switch (val) {
    case 1:
      previous_millis = millis();
      bar_temp = (set_temp - off_temp - 1) * -1; // прыжок с переворотом
      print_progress_bar(bar_temp); // отображение температуры в "попугаях" от 1 до 8
      batton = 2;
      break;
    case 2:
      static int8_t pos = 0;
      static int8_t last_state = 0b11;
      static int8_t increment[16] = {0, -1, 1, 0, 1, 0, 0, -1, -1, 0, 0, 1, 0, 1, -1, 0};
      int8_t state = digitalRead(s1) | (digitalRead(s2) << 1);
      if (state != last_state) {
        pos += increment[state | (last_state << 2)];
        if (!(pos % 4) and pos) { // реагируем только на полный щелчек энкодера
          previous_millis = millis();
          bar_temp += increment[state | (last_state << 2)];
          bar_temp = constrain(bar_temp, min_bar, max_bar);
          print_progress_bar(bar_temp); // отображение температуры в "попугаях" от 1 до 8
          pos = 0;
        }
        last_state = state;
      }
      current_millis = millis();
      if (current_millis - previous_millis > second * 5) { // через 5 секунд сохраняем
        set_temp = off_temp - bar_temp + 1; // обратный прыжок с переворотом
        EEPROM.write(0, set_temp);
        batton = 0;
        Serial.print("set temperature: ");
        Serial.println(set_temp);
      }
      break;
  }
}

/// селектор режимов работы ///
void action(uint16_t val) {
  static uint32_t previous_millis;
  static uint32_t current_millis;
  switch (val) {
    case 100: // режим работы без термодатчиков 30\30 -----ERROR
      Serial.println("thermal sensors off");
      previous_millis = millis();
      action_error(); // переключатель
      val_action = 101;
      break;
    case 101:
      current_millis = millis();
      if (current_millis - previous_millis > (minute * 30)) {
        val_action = 100;
      }
      break;

    case 200: // пауза перед включением 5 минут -----PAUSE
      Serial.println("pause: 5 min");
      previous_millis = millis();
      val_action = 201;
      break;
    case 201:
      current_millis = millis();
      if (current_millis - previous_millis > (minute * 5)) {
        val_action = 300;
      }
      break;

    case 300: // запуск заморозки на 12 часов -----FROST
      Serial.println("frost: 12 hour");
      previous_millis = millis();
      val_action = 301;
      break;
    case 301:
      action_frost(); // терморегулятор заморозки
      current_millis = millis();
      if (current_millis - previous_millis > (hour * 12)) {
        set_relay(0, 0, 0); // все выключаем
        val_action = 400;
      }
      break;

    case 400: // запуск разморозки на 25 минут -----DEFROST
      Serial.println("defrost: 25 min");
      previous_millis = millis();
      val_action = 401;
      break;
    case 401: //
      action_defrost(); // терморегулятор разморозки
      current_millis = millis();
      if (current_millis - previous_millis > (minute * 25)) {
        set_relay(0, 0, 0); // все выключаем
        val_action = 300;
      }
      break;
  }
}

/// переключатель  ///
void action_error() {
  static bool flag = 0;
  if (!flag) set_relay(0, 0, 0); // все выключаем
  else set_relay(1, 1, 0); // включаем вентилятор и компрессор
  flag = !flag;
}

/// терморегулятор заморозки ///
void action_frost() {
  const int8_t temp_fan_on = -10; // температура включения вентилятора
  if (temp_1 >= on_temp) {
    set_relay((temp_2 < temp_fan_on), 1, 0); // включаем компрессор и вероятно вентилятор
  }
  else if (temp_1 <= set_temp) {
    set_relay(0, 0, 0); // все выключаем
  }
  if (relay[1]) {
    if (temp_2 < temp_fan_on) {
      set_relay(1, 1, 0); // включаем вентилятор и компрессор
    }
  }
}

/// терморегулятор разморозки ///
void action_defrost() {
  const int8_t temp_defrost_off = 6; // температура выключения нагревателя
  const int8_t hysteresis = 2; // гистерезис
  if (temp_2 < (temp_defrost_off - hysteresis)) {
    set_relay(0, 0, 1); // включаем нагреватель
  }
  else if (temp_2 > temp_defrost_off) {
    set_relay(0, 0, 0); // все выключаем
  }
}

/// печать текущего режима работы ///
void print_action(uint16_t val) {
  lcd.setCursor(0, 1);
  switch (val) { // Список параметров
    case 101: lcd.print("ERROR   "); break; // ошибка
    case 201: lcd.print("PAUSE   "); break; // пауза
    case 301: lcd.print("FROST   "); break; // заморозка
    case 401: lcd.print("DEFROST "); break; // разморозка
  }
}

/// печать температуры испарителя холодильной камеры ///
void print_temperature() {
  lcd.setCursor(0, 0);
  if (!error) lcd.print(temp_1, 1);
  else lcd.print("---");
  lcd.print("  ");
}

/// печать состояния реле: fan, frost, defrost ///
void print_relay_state() {
  lcd.setCursor(5, 0);
  for (int8_t i = 0; i < 3; i++) {
    if (relay[i]) lcd.print("^");
    else lcd.print(" ");
  }
}

/// печать сегментов прогрессбара ///
void print_progress_bar(int8_t val) {
  lcd.setCursor(0, 1);
  for (int8_t i = 0; i < 8; i++) {
    if (i < val) lcd.write(0xFF);
    else lcd.print(" ");
  }
}

/// установка состояний реле ///
void set_relay (bool set_fan, bool set_frost, bool set_defrost) { // 1 - on, 0 - off
  digitalWrite(fan, !set_fan);
  digitalWrite(frost, !set_frost);
  digitalWrite(defrost, !set_defrost);
  relay[0] = set_fan;
  relay[1] = set_frost;
  relay[2] = set_defrost;
}

/// получение температуры ///
void get_sensor() {
  if (!error) {
    temp_1 = sensor_1.getTemperatureInDegrees();
    temp_2 = sensor_2.getTemperatureInDegrees();
    if (temp_1 == INVALID_LM75A_TEMPERATURE) error_sensor(1);
    if (temp_2 == INVALID_LM75A_TEMPERATURE) error_sensor(2);
  }
}

/// ошибка датчика температуры ///
void error_sensor(int8_t sensor) {
  Serial.print("error sensor: ");
  Serial.println(sensor);
  val_action = 100; // режим работы без термодатчиков
  error = 1;
}

/// печать телеметрии в Serial раз в минуту ///
void serial_print_telemetry() {
  static int8_t count = 0;
  count++;
  if (count == 60) {
    Serial.print("sensors: ");
    if (!error) Serial.print(temp_1); // temp_1
    else Serial.print("---");
    Serial.print(" ");
    if (!error) Serial.print(temp_2); // temp_2
    else Serial.print("---");
    Serial.print(" ");
    for (int8_t i = 0; i < 3; i++) { // fan, frost, defrost
      if (relay[i]) Serial.print("1");
      else Serial.print("0");
    }
    Serial.println(" ");
    count = 0;
  }
}

/// проверка и установка температуры из EEPROM ///
void set_temp_check() {
  set_temp = EEPROM.read(0);
  if (set_temp < (off_temp - 7) or set_temp > off_temp) {
    set_temp = off_temp;
    EEPROM.write(0, off_temp);
  }
}

4 лайка

Мне понравилось.
Мельком пробежался глазами по коду - Почему для val_action не применили enum? Это же удобно, наглядно и не нужно будет через год вспоминать что значат коды 200 / 101 / 100 и тп… ))

Но это моё личное мнение.

ЗЫ: А еще схему принципиальную не помешало бы…

Посмотрел в словарь.

Медитативный – «Проникнутый философскими, элегическими мотивами о поэзии»

Ради такого дела решить читать пост дальше :slight_smile:

обычного реле оттайки было недостаточно?
PS и таки да, БЕЗ СХЕМЫ НЕПОНЯТНО
http://stinol-repair.ru/img/2.gif

Вместо снабберной коробки лучше бы монтаж с Ардуино показали.

Схему добавил. До enum пока не добрался, код написал на основе того что знаю.

1 лайк

Холодильник был лишен всего что отвечает за терморегуляцию. Восстановить до заводской схемы тоже можно, но для этого нужно купить: термостат, реле оттайки и термопредохранитель. Либо просто залезть в коробку со всякими модулями и собрать свой вариант, попутно разбираясь в деталях того как работает холодильник - это интересно.

Arduino закреплена сразу за модулем дисплея на вспененый скотч. Все соединено проводами, выглядит не очень…

поставить ESP и передавать данные к себе на смартфон, индикация охлаждения напитков )))

И автоматический заказ доставки, когда пиво заканчивается.

1 лайк

Рекомендую! Очень сильно жизнь упростится (особенно если код писать “набегами”, или изменять имеющийся, или … )

Скопилось более двух десятков контроллеров! Как простых, так и посложнее. А сколько переделано…) В основном горят реле. Делают все кто как горазд. Встречал вообще, минимум - минимор. Одна платка, 16-ти ногий МК, 2 симистора, тут же лампа, тут же NTC, бестрансформаторное питание. Это класс!
Вот. Песня.)

1 лайк

Простенько, всё в одной куче. Для сельской местности сойдёт.)
Давеча рассматривал один контроллер. Только пользовательских 25 параметров. Плюс 35 админских, итого 60 где то.) Понятно, что это универсал, но без установок это тоже как то…)

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

по мне и имеюшиеся - лишнее, должна быть всего одна крутёлка, установил температуру, чтобы в холодильном отделении был необходимый градиент по полкам и вуаля, сам размораживается сам замораживается и работает как автомат Калашникова )))

Аааапсалютна согласен.
Я вообще считаю, что достаточно только одной кнопки - «Сделай за#бись!». Других не надо.

Для юзера - да, а для оборудования? К примеру, мототит у тебя компрессор безостановочно, а потом бац, и помер… А юзер этих всех нюансов не видит.

Если это сообщение мне, то отвечу так - Значит «за#бись» не сделал ))

image

Никак секиру получил? Теперь можно обращаться, когда нужно вызвать констебля?

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