Перейти до публікації
Пошук в
  • Додатково...
Шукати результати, які містять...
Шукати результати в...

Автоматизація системи вентиляції, ESPHome, Home Assistant

TaurosRMK

Рекомендовані повідомлення

Не оч понятно, в вас ніяк не відрізняється зима/літо? Бо бачу ви орієнтуєтесь на температуру вихлопу? Він жеж дуже залежить від сезону

Посилання на коментар
Поділитися на інших сайтах

2 години тому, standov сказав:

Не оч понятно, в вас ніяк не відрізняється зима/літо? Бо бачу ви орієнтуєтесь на температуру вихлопу? Він жеж дуже залежить від сезону

* Варто напевно виправити, але це температура не вихлопу на вулицю, а температура повітря з будинку (Extract Air), тобто на вході в ТО.

На момент написання цього коду визначення сезону ще не було. Хоча там і не дуже складний код, можна було дописати, але вирішив упустити цю перевірку. Частково по датчику повітря витяжки з будинку можна "визначити" сезон зима/літо, бо при вуличній приблизно нижче 15С при довгому простої температури в ПВУ падають, в порівнянні коли ПВУ працює. Відповідно чим нижча температура на вулиці, тим більше остиває ПВУ.

Для прикладу, на вулиці 18С і вище - остивання в ПВУ мінімальне або навіть відсутнє, при 15-18С - остивання 1-2 градуси, при 10-15С - 2-4 градуси, при 0-10С - 4-7 градусів, і тд. Тобто якщо в момент запуску по датчику з будинку буде 15С, при типових 20-22С, то остивання на 5-7С може свідчити що на вулиці точно не літо.

Поки що час прогрівання і діапазони умов просто фіксовані, щоб перевірити чи код працює і щоб не вдаватися в якісь складніші розрахунки. В цілому і того вистачить, трохи прогріти, якщо ТО холодний. Тривалість і діапазони треба підкоригувати.

Змінено користувачем TaurosRMK
Посилання на коментар
Поділитися на інших сайтах

Поки що не розібрався чи то так має бути, чи потрібно якось зберігати значення деяких сенсорів. Для прикладу деякі бінарні сенсори які приймають значення True при виконанні деяких умов, а умови можуть мати затримки щоб не спрацьовувати часто. Якщо такий сенсор має затримку 10 хв, а від нього залежить ще якись код, який має свою затримку 5 хв, то при кожному оновленні прошивки все скидається в початковий стан і відповідно треба чекати знову, поки спливуть всі затримки і виконаютсья умови.

Наприклад, сезон Зима/Літо, якщо Т вулична нижче 15С протягом 15 хв, то сезон Зима. Система працює протягом дня, визначила що сезон Зима, але прийшлося оновити прошивку і після оновлення знову очікування 15 хв щоб визначився сезон.

Посилання на коментар
Поділитися на інших сайтах

Ще один кусок автоматизації - запуск витяжного вентилятора на максимальну швидкість для зливу конденсату з теплообмінника.

Перший бінарний сенсор визначає чи працює система не менше 30 хв (поки для прикладу), наступний перевіряє всі умови для запуску автоматизації - приточний вентилятор запущений / заслонка відкрита, аналогічно і витяжний, сезон Зима, на вулиці нижче +10С (для прикладу) і статус системи "В роботі". Потім через інтервал кожні 3 години буде відпрацьовувати автоматизація - вимкнення балансування вентиляторів, витяжний на 100% на 90 сек (для прикладу), повернення витяжного на попередньо швидкість, вмикання балансування.

# Запуск витяжного вентилятора на 100% для зливу конденсату
binary_sensor:
  - platform: template
    name: "System Running 30min"
    id: system_is_running_30min
    lambda: |-
      return id(system_is_running).state;
    filters:
      - delayed_on: 30min # 30min
      - delayed_off: 0s

  - platform: template
    name: "Condensation Drain"
    id: condensation_drain
    lambda: |-
      if (id(system_is_running_30min).state &&
          id(do_1).state &&
          id(do_2).state &&
          id(outdoor_temp).state < 10.0 &&
          id(mvhr_status) == "running" &&
          id(season_winter).state) {
        return true;
      } else {
        return false;
      }

interval:
  - interval: 3h # 3h
    then:
      - if:
          condition:
            binary_sensor.is_on: condensation_drain
          then:
            - switch.turn_off: balancing_on
            - lambda: |-
                id(previous_exhaust_speed) = id(exhaust_fan).speed;  // Зберегти попередню швидкість
                auto call = id(exhaust_fan).turn_on();
                call.set_speed(100);  // Увімкнути на 100%
                call.perform();
                ESP_LOGI("MHRV", "Exhaust Fan set to 100%");
            - delay: 90s
            - lambda: |-
                auto call = id(exhaust_fan).turn_on();
                call.set_speed(id(previous_exhaust_speed)); // Повернути на попередню швидкість
                call.perform();
                ESP_LOGI("MHRV", "Exhaust Fan returned to previous speed: %f", id(previous_exhaust_speed));
            - delay: 1min
            - switch.turn_on: balancing_on

 

Посилання на коментар
Поділитися на інших сайтах

  • 2 тижні потому...

В темі про вентиляцію показував як за допомогою сенсорів SDP810, які маряють перепад тиску, зробив сенсори щоб розраховувати об'єм повітря і в подальшому балансувати систему. Про самі DIY сенсори описано в темі про вентиляцію, тут залишу код для ESPHome.

Сенсори поки що підключені до окремих ESP32, які заведені в НА, тому в основний контролер ESP32 підтягуємо значення сенсорів.

Прихований текст
sensor:
  - platform: homeassistant
    entity_id: sensor.sdp810_supply_sdp810_supply
    name: "sdp supply"
    id: sdp810_supply
    internal: true
  
  - platform: homeassistant
    entity_id: sensor.sdp810_exhaust_sdp810_exhaust
    name: "sdp exhaust"
    id: sdp810_exhaust
    internal: true

Далі на прикладі одного сенсору. Потрібно розрахувати падіння тиску в трубках якими під'єднані сенсори і вирахувати це значення з показань сенсора (так ніби трубок там немає). В документах до сенсору є формула, яка виглядає трохи страшно, але ШІ допоміг то все швидко записати 😁

Прихований текст

image.png.82387d59f539a03abb4300109c46dc50.png

image.png.5ac2634e06c99258790d5495654b4baf.png

Спочатку по сенсорах температури і вологості розраховуєтсья густина повітря.

Прихований текст
  # Густина приточного повітря, кг/м³
  - platform: template
    name: "Supply Air Density"
    id: supply_air_density
    lambda: |-
      // Температура повітря (°C)
      float temp_celsius = id(sht30_sup_temp).state;
      // Відносна вологість (%)
      float humidity = id(sht30_sup_hum).state;
      // Газова константа сухого повітря (Дж/(кг*К))
      float R_dry_air = 287.05;
      // Газова константа водяної пари (Дж/(кг*К))
      float R_water_vapor = 461.495;
      // Насичений парціальний тиск водяної пари (Па)
      float p_sat = 611.2 * exp((17.62 * temp_celsius) / (243.12 + temp_celsius));
      // Парціальний тиск водяної пари (Па)
      float p_vapor = humidity / 100.0 * p_sat;
      // Атмосферний тиск в Па
      float atm_pressure = 102800;
      // Парціальний тиск сухого повітря (Па)
      float p_dry = atm_pressure - p_vapor;
      // Температура повітря в Кельвінах
      float T_kelvin = temp_celsius + 273.15;
      // Розрахунок густини повітря (кг/м³)
      float air_density = (p_dry / (R_dry_air * T_kelvin)) + (p_vapor / (R_water_vapor * T_kelvin));
      return air_density;
    state_class: "measurement"
    unit_of_measurement: "kg/m³"
    accuracy_decimals: 4
    update_interval: 60s

Розрахунок падіння тиску в трубках і компенсація показів сенсору на ці значення.

Прихований текст
  # Падіння тиску в трубках, Па (приток)
  - platform: template
    name: "Supply Tube Pressure Drop"
    id: supply_tube_pressure_drop
    lambda: |-
      float dp_sensor = id(sdp810_supply).state;
      float temperature = id(sht30_sup_temp).state;
      float rho_air = id(supply_air_density).state;
      float L = 1;  // довжина трубок в метрах
      float D = 0.004;  // внутрішній діаметр трубки в метрах
      float q_c = 6.17 * pow(10, -7);
      float dp_c = 62;
      // Формула розрахунку в'язкості повітря (η_air)
      float air_viscosity = (18.205 + 0.0484 * (temperature - 20)) * pow(10, -6);
      // Формула розрахунку ε
      float epsilon = (-64 / 3.14159) * (L / pow(D, 4)) * (air_viscosity / rho_air) * (q_c / dp_sensor) * (std::sqrt(1 + (8 * dp_sensor / dp_c)) - 1);
      return dp_sensor * ((1 / (1 + epsilon)) - 1);
    unit_of_measurement: "Pa"
    accuracy_decimals: 2
    update_interval: 5s
      
  # Перепад тиску з коригуванням на падіння тиску в трубках (приток)
  - platform: template
    name: "Supply Differential Pressure"
    id: supply_dp
    lambda: |-
      float dp_sensor = id(sdp810_supply).state;
      float dp_tube = id(supply_tube_pressure_drop).state;
      return dp_sensor - dp_tube;
    unit_of_measurement: "Pa"
    accuracy_decimals: 2
    update_interval: 5s

По формулі розраховується середня швидкість через перепад тиску який віддає сенсор і густину повітря. Де CF - коефіцієнт, який потрібно лабораторно визначати, або ж робити заміри швидкості іншим пристроєм (наприклад анемометр) і вирахувати з формули цей коефіцієнт. Поки що прийнято за 1. Оновлено код з врахуванням калібрування по коефіцієнту.

image.png.c6b83586727f0ad6ed685ad2318e9ea9.png

Прихований текст
  # Швидкість приточного повітря, м/с
  - platform: template
    name: "Supply Air Velocity"
    id: supply_air_velocity
    lambda: |-
      // Перепад тиску (Па)
      float delta_p = id(supply_dp).state;
      // Густина повітря (кг/м³)
      float air_density = id(supply_air_density).state;
      // Швидкість повітря (м/с)
      float velocity = std::sqrt((2 * delta_p) / air_density);
      return velocity;
    filters:
      - calibrate_polynomial:
          degree: 2
          datapoints:
            # Map 0.0 (from sensor) to 0.0 (true value)
            - 1.0 -> 0.7
            - 1.98 -> 1.44
            - 3.83 -> 3.17
            - 5.89 -> 5.22
            - 7.93 -> 6.98
    device_class: "speed"
    state_class: "measurement"
    icon: mdi:gauge
    unit_of_measurement: "m/s"
    accuracy_decimals: 2
    update_interval: 5s

На основі швидкості розраховується об'єм повітря в повітроводі.

Прихований текст
  # Об'ємний потік приточного повітря, м³/год
  - platform: template
    name: "Supply Air Volume Flow"
    id: supply_air_volume_flow
    lambda: |-
      // Швидкість повітря (м/с)
      float velocity = id(supply_air_velocity).state;
      // Площа перерізу каналу (м²)
      float area = 3.14159 * pow((0.15 / 2), 2);
      // Об'ємний потік повітря (м³/с)
      float volume_flow_rate = area * velocity;
      // Об'єм повітря за годину (м³/год)
      float volume_m3_h = volume_flow_rate * 3600; // 3600 секунд
      return volume_m3_h;
    device_class: "volume_flow_rate"
    state_class: "measurement"
    icon: mdi:gauge
    unit_of_measurement: "m³/h"
    update_interval: 30s
    accuracy_decimals: 0
Змінено користувачем TaurosRMK
  • Лайк 1
Посилання на коментар
Поділитися на інших сайтах

Балансування вентиляторів.

При виборі пресету зі списку швидкість вентиляторів встановлюється на обрану. Так вийшло що в мене опір притоку і витяжки майже однаковий, тому вентилятори видають майже однакову продуктивність при плюс-мінус однакових обертах. Тому встановлюю швидкість на обидва вентилятори рівну, а дальше при потребі відбувається коригування швидкостей вентиляторів щоб вирівняти потоки. Якщо вручну змінити швидкість якогось вентилятора, то контролер буде її вирівнювати, поки потоки не будуть приблизно рівними. Наче працює нормально.

select:
  - platform: template
    id: air_flow_select
    name: "Air Flow Select"
    options:
      - "Min (90 m³/h)"
      - "Low (120 m³/h)"
      - "Medium (160 m³/h)"
      - "Default (210 m³/h)"
      - "High (300 m³/h)"
      - "Max (400 m³/h)"
    initial_option: "Medium (160 m³/h)"
    restore_value: true
    optimistic: true
    on_value:
      - lambda: |-
          float new_fan_speed = 0.0;

          if (id(air_flow_select).state == "Min (90 m³/h)") {
            id(target_air_flow) = 90.0;
            new_fan_speed = 35.0;
          } else if (id(air_flow_select).state == "Low (120 m³/h)") {
            id(target_air_flow) = 120.0;
            new_fan_speed = 42.0;
          } else if (id(air_flow_select).state == "Medium (160 m³/h)") {
            id(target_air_flow) = 160.0;
            new_fan_speed = 50.0;
          } else if (id(air_flow_select).state == "Default (210 m³/h)") {
            id(target_air_flow) = 210.0;
            new_fan_speed = 61.0;
          } else if (id(air_flow_select).state == "High (300 m³/h)") {
            id(target_air_flow) = 300.0;
            new_fan_speed = 75.0;
          } else if (id(air_flow_select).state == "Max (400 m³/h)") {
            id(target_air_flow) = 400.0;
            new_fan_speed = 94.0;
          }

          auto call_supply = id(supply_fan).turn_on();
          call_supply.set_speed(new_fan_speed);
          call_supply.perform();

          auto call_exhaust = id(exhaust_fan).turn_on();
          call_exhaust.set_speed(new_fan_speed);
          call_exhaust.perform();

switch:
  - platform: template
    id: fan_balancing
    name: "Fan Balancing"
    icon: mdi:tune
    optimistic: true

interval:
  - interval: 2min
    then:
      - lambda: |-
          if (id(fan_balancing).state) {
            float supply_speed = id(supply_fan).speed;
            float exhaust_speed = id(exhaust_fan).speed;

            float supply_flow = id(supply_air_volume_flow).state;
            float exhaust_flow = id(exhaust_air_volume_flow).state;

            auto call_supply = id(supply_fan).turn_on();
            auto call_exhaust = id(exhaust_fan).turn_on();

            float difference = supply_flow - exhaust_flow;
            float tolerance = 5;
            float speed_change = (abs(difference) > 20) ? 5 : 1;

            if (abs(difference) > tolerance) {
              float new_supply_speed = supply_speed + (difference > 0 ? -speed_change : speed_change);
              new_supply_speed = constrain(new_supply_speed, 0, 100);
              call_supply.set_speed(new_supply_speed);
              call_supply.perform();

              float new_exhaust_speed = exhaust_speed + (difference > 0 ? speed_change : -speed_change);
              new_exhaust_speed = constrain(new_exhaust_speed, 0, 100);
              call_exhaust.set_speed(new_exhaust_speed);
              call_exhaust.perform();
            }
          }

Подумав що можна трохи допрацювати код. Якщо потоки рівні (+ похибка), то можна не робити балансування, а запускати тільки коли потоки відрізняються.

Посилання на коментар
Поділитися на інших сайтах

  • 3 місяці потому...

Підкажіть як правильно записати умови для такої автоматимзації. Тобто саме умови, логіку.

Суть задачі - балансування швидкості вентиляторів (приток/витяжка). Вхідні дані: два датчики потоку (м3/год) і два вентилятори. 

Має працювати якось так. З випадаючого списку вибирається пресет потрібного об'єму повітря (120, 160, 210 м3/год та ін). Ручним методом було знайдено швидкості вентиляторів які плюс-мінус відповідають цим пресетам, тому зразу при виборі пресету швидкісь вентиляторів встановлюється на відповідні значення. А дальше вступає в роботу балансування, яке має робити наступне:

  • Перевіряти по датчиках чи потоки відповідають вибраному пресету (±похибка 10 м3/год). 
  • Якщо потоки вентиляторів менше/більше пресету (±похибка), то швидкість збільшується/зменшується на 1.
  • При цьому також має перевірятися умова приток > витяжка.

Тобто з цими умовами потоки мають бути в "балансі" близькими до пресету і щоб не виходили за межі похибки, а також щоб приток завжди був більше витяжки. Можливо для цього треба похибку трохи збільшити, бо самі датчики можуть давати похибку в 10-15 м3/год при незмінних обертах вентиляторів. Але це вже інше питання.

Допомагав в цьому звісно AI друг Copilot і вийшов такий код, але щось він працює трохи не коректно. Спроби переробити були безуспішні, AI починає фігню пропонувати, пишу одне, пропонує зовсім інше.

В цілому наче працює добре, але є моменти, коли напевно умови перевіряються одночасно и одна іншій заважає, тобто буває що приточний потік виходить за межі похибки, але при цьому він стає більшим за витяжний потік. Тобто одна умова виконалася. А потім швидкість зменшується, потік входить в допустимі межі і тут знову спрацьовує умова що притік < витяжки, і знову починається коригування туда-сюда.

interval:
  - interval: 2min
    then:
      - lambda: |-
          if (id(fan_balancing).state) {
            float supply_flow = id(supply_air_volume_flow).state;
            float exhaust_flow = id(exhaust_air_volume_flow).state;
            float target_flow = id(target_air_flow);
            
            auto call_supply = id(supply_fan).turn_on();
            auto call_exhaust = id(exhaust_fan).turn_on();

            float flow_difference = abs(supply_flow - target_flow);
            float tolerance = 10;
            float speed_step = (abs(flow_difference) > 20) ? 5 : 1;

            // Коригуємо приточний вентилятор
            float new_supply_speed = id(supply_fan).speed;
            if (abs(supply_flow - target_flow) > tolerance) {
              new_supply_speed += (supply_flow < target_flow) ? speed_step : -speed_step;
            }

            // Коригуємо витяжний вентилятор
            float new_exhaust_speed = id(exhaust_fan).speed;
            if (abs(exhaust_flow - target_flow) > tolerance) {
              new_exhaust_speed += (exhaust_flow < target_flow) ? speed_step : -speed_step;
            }

            // Переконуємось, що потік приточного повітря більший за витяжний
            if (supply_flow <= exhaust_flow) {
              if (exhaust_flow >= target_flow) {
                new_exhaust_speed -= 1;
                new_supply_speed += 1;
              } else {
                new_supply_speed += 1;
              }
            }
            
            new_supply_speed = constrain(new_supply_speed, 0, 100);
            call_supply.set_speed(new_supply_speed);
            call_supply.perform();

            new_exhaust_speed = constrain(new_exhaust_speed, 0, 100);
            call_exhaust.set_speed(new_exhaust_speed);
            call_exhaust.perform();
          }

 

Посилання на коментар
Поділитися на інших сайтах

25.04.2025 в 21:27, TaurosRMK сказав:

Підкажіть як правильно записати умови для такої автоматимзації. Тобто саме умови, логіку.

Один з вентиляторів треба зафіксувати по пресету, а іншим вентилятором вже коригувати. Коригування треба робити з допомогою PID контроллера. На вхід контроллера подаєте різницю між потоками з додаванням константи для виконання умови приток > витяжка, і контроллер має звести її до нуля.

Посилання на коментар
Поділитися на інших сайтах

2 години тому, volomoto сказав:

Один з вентиляторів треба зафіксувати по пресету, а іншим вентилятором вже коригувати. Коригування треба робити з допомогою PID контроллера. На вхід контроллера подаєте різницю між потоками з додаванням константи для виконання умови приток > витяжка, і контроллер має звести її до нуля.

Я й не думав тут про ПІД, бо напевно він не зможе вийти на уставку і тримати її. Все тому що показання датчиків потоку плавають, навіть без зміни швидкості вентилятора. Знизу графік обертів вентилятора, вони трималися на 1895-1905 об/хв, отой стрибок напевно якраз спрацював код який вище показано, швидкість збільшилася на 1 одиницю. Але по датчиках потоку (зверху) коливання були від 205 до 215 м3/год, і під час збільшення швидкості - до 225 м3/год. Інтервал оновлень датчиків потоку 60с. Тобто в один момент може бути так, что приток > витяжка, а вже через 60 сек потоки можуть бути рівні, або наоборот приток < витяжка.

Тому не знаю як буде себе поводити ПІД, напевно буде сходити з розуму. Хоча якщо збільшити різницю приток-витяжка десь до 30 м3/год, то напевно  має бути краще.

image.thumb.png.ff63b46986df5768f3c904a2e623357f.png

Посилання на коментар
Поділитися на інших сайтах

21 хвилину тому, TaurosRMK сказав:

Я й не думав тут про ПІД, бо напевно він не зможе вийти на уставку і тримати її.

Якраз він і був створений для того, щоб вийти на уставку і тримати її, але якщо виконується умова, що максимальна частота зміни похибки є в два рази меншою, ніж смуга пропускання ПІД контроллера. ПІД – це фільтр нижніх частот, тому вихідний сигнал не може змінюватись швидше, ніж дані із сенсорів.

У вас частота оновлення даних 60 с, а фізично швидкості потоків змінюються набагато швидше, тому в межах тих двох хвилин швидкості вентиляторів будуть плавати туди-сюди і у вас нема ніякого контролю над тим. Зробіть оновлення кожних 5 секунд, налаштуйте правильно коефіцієнти і тоді все буде правильно працювати.

  • Лайк 1
Посилання на коментар
Поділитися на інших сайтах

Створіть акаунт або увійдіть у нього для коментування

Ви маєте бути користувачем, щоб залишити коментар

Створити акаунт

Зареєструйтеся для отримання акаунта. Це просто!

Зареєструвати акаунт

Увійти

Вже зареєстровані? Увійдіть тут.

Увійти зараз
×
×
  • Створити...