Управление отоплением в загородном доме (GSM)

Тема в разделе "Глядите, что я сделал", создана пользователем Вячеслав, 6 дек 2012.

  1. yul-i-an

    yul-i-an Гик

    Вот если цикл взять минут 10 и допустим 5мин горелка вкл, 5 выкл, то это 50%, а если 2.5мин вкл а 7.5 выкл это 25%. Нужно только за 1% взять минимальное допустимое время между вкл и выкл горелки.
    Тут делали но ТЭН.
    http://arduino.ru/forum/programmirovanie/upravlenie-tenami-propuskom-periodov-shim
    От себя замечу что на даче конвектор ч ПИ регуляторм (полный ПИД на отопление не нужен) расходует примерно % на 30 меньше чем тотже конвектор с обычным термостатом и температура ровная не колеблится. Вот http://arduino.ru/forum/proekty/pi-regulyator-otopleniya
     
    Последнее редактирование: 2 ноя 2018
    ИгорьК и Ztepan нравится это.
  2. parovoZZ

    parovoZZ Гуру

    Так и происходит - тэны расположены по краю, термостат в середине. Что мешает сделать также, как в утюге?
     
  3. Ztepan

    Ztepan Нуб

    Я же писал, температуру теплоносителя регулировать не проблема, а вот воздух который он нагревает уже сложнее. Получается пока воздух нагрели, воду перегрели, пока воздух охладили, воду переохладили и так по кругу.
     
  4. parovoZZ

    parovoZZ Гуру

    а почему
    если
     
  5. yul-i-an

    yul-i-an Гик

    При использовании регулятора, как только температура воздуха начинает подниматся, мощность снижается и останавливается на какомто равновесном состоянии.
     
  6. parovoZZ

    parovoZZ Гуру

    У ТС это невозможно в силу того, что горелка может быть только вкл/выкл (без модуляции мощности). Поэтому ПИД может быть реализован только с помощью ШИМ. Но он это отрицает и всё ищет какого-то сферического коня в вакууме.
     
  7. Ztepan

    Ztepan Нуб

    Я конечно плохо понимаю в пидах, но это величина выражена в процентах и регулировать может мощность в зависимости от нагрева. Это так?
     
  8. Йа кагто ради баловства, делал "сторожа" для молока. Датчик помещался в кастрюлю. И был такой алгоритм делалось 10 измерений в секунду и усреднённые значение сохранялось в массив из 60 элементов. Потом брался 0 и 30 элемент массива и из 30 вычитался 0. Потом Пумножался на 2. та же фигня делалась с 0 и 60. Полученные значения усреднялись и получалась типа тенденции роста температуры в минуту. На экранчик выводилась температура и скорость ее роста . Допустим подошли к температуре 90 градусов и скорость роста 5 градусов в минуту. Дожидаемся когда температура дойдет до 95 градусов и отключаем плитку. Дальше начинался фикус. Молоко за счёт инерционности электроплиты как раз доходило до 99-100. Пару рас булькало и начинало уже остывать. Ни разу не сбежало. ( игрушка правда не прижалась и где-то валяется в шкафу) может вам такой алгоритм попробовать? Хотя как писали выше лучше купить готовый контроллер. Потому что как заметил один из гуру. Кто собирался делать на ардуино управление газовыми котлами, больше на форуме не появляется(
     
  9. Ztepan

    Ztepan Нуб

    Почему мы не можем по этому принципу определить температуру воды?
    Скажем разница между минимальным максимальным значениями температур воды 100 градусов и есть 100% мощности.
    И получаем при низкой температуре воздуха система устанавливает мощность 100% = т воды 100 градусов (такую все равно мгновенно не наберет, но можно ограничить 80 градусами), с прогревом помещения должна плавно понижаться мощность в процентах, соответственно мы понижаем заданную температуру воды, тем самым достигая баланса. А за поддержанием заданной темп. воды контроллер проследит.
     
  10. Ztepan

    Ztepan Нуб

    Как раз эта идея и понравилась, только зачем дельта первого замера и 30-го умножается на 2?
     
  11. yul-i-an

    yul-i-an Гик

    Вот выше я писал
    Вот Вам и ШИМ, медленный только, но это не важно т.к. система инерционна.
    Цикл постоянно повторяется и продолжительность горения горелки в этом цикле и ьудет необходимая мощность. Только коэффициенты ПИ подобрать и все.
     
    Ztepan нравится это.
  12. Ztepan

    Ztepan Нуб

    Вот тут описывают подобный алгоритм http://arduino.ru/forum/proekty/pi-regulyator-otopleniya
    yul-i-an, я правильно копаю?
     
  13. 30 это измерение за пол минуты умножить на два получается предполагаемое значение роста за минуту. Разница 0 и 60 складывается с предыдущим результатом. И усреднялись. Получается что то вроде прогноза погоды на Марсе но это работало. Хотя можно проще наверное найти среднее в диапазоне 0 - 30 секунды и 30-60. Вычесть из большего меньшее и получим тенденцию роста температуры. Так вроде проще даже будет
     
  14. yul-i-an

    yul-i-an Гик

    Простой П регулятор Температура заданная - температура текущая * коэффициент (подбирается индивидуально дивидуально)= необходимая мощность. Но прстоянно будет ошибка регулирования.
     
  15. yul-i-an

    yul-i-an Гик

    Ну тот регулятор я делал.
    Вроде правильно все.
    Вам из кода регулятора нужны только P и I составляющая. Разница между текущей ошибкой и прошлой это D составляющая.
     
  16. А пойдет ли на пользу такое частое включение выключение газовому клапану?
     
  17. Ztepan

    Ztepan Нуб

    почему же частое, скажем для набора температуры воды на 2 градуса котлу надо минут 10-15, в морозы за 30 и того больше.
     
  18. parovoZZ

    parovoZZ Гуру

    троянами справить или другой какой живностью?
     
  19. djkohr

    djkohr Нуб

    Код (C++):
    #include <SoftwareSerial.h>
    #include "DHT.h"
    #define DHTPIN 7    // pin на котором висит датчик температуры
    #define DHTTYPE DHT22  // DHT 22 тип датчика
    //Отсылается смс в формате: влажность;температура;устройство1;устройство2;устройство3;устройство4;Контроль периметра;Состояние периметра;Контроль автотемпературы;Установленная температура;STEWARD;
    int greenLed = 13; //Зеленый светодиод на PIN13
    int powerGSM = 6; //Пин включения GSM
    int unit1 = 8; //Управляемое устройство 1
    int unit2 = 9; //Управляемое устройство 2
    int unit3 = 10;//Управляемое устройство 3
    int unit4 = 11;//Управляемое устройство 4
    int perimetr = 12;// Датчик периметра
    String statusHome;
    int t=0;
    unsigned long currentTime;
    unsigned long loopTime;
    boolean unit1status = false; //Управляемое устройство 1
    boolean unit2status = false; //Управляемое устройство 2
    boolean unit3status = false;//Управляемое устройство 3
    boolean unit4status = false;//Управляемое устройство 4
    boolean isPerimetr = false; //Контроль периметра
    boolean sendSMS = false; //Для проверки что смс отсылаем только один раз
    boolean isAuto = false; //Автоматическое управление температурой
    int autoTemp = 0;
    int delta=2;
    DHT dht(DHTPIN, DHTTYPE);
    SoftwareSerial gprsSerial(2, 3);
    boolean prevBtn = LOW;
    String currStr = "";
    // Переменная принимает значение True, если текущая строка является сообщением
    boolean isStringMessage = false;
    void setup()
    {
        pinMode(unit1, OUTPUT);
        pinMode(unit2, OUTPUT);
        pinMode(unit3, OUTPUT);
        pinMode(unit4, OUTPUT);
        pinMode(powerGSM, OUTPUT);
        pinMode(perimetr, INPUT);
        pinMode(greenLed, OUTPUT);
        digitalWrite(unit1, LOW);
        digitalWrite(unit2, LOW);
        digitalWrite(unit3, LOW);
        digitalWrite(unit4, LOW);
        digitalWrite(powerGSM, LOW);
        digitalWrite(greenLed, LOW);
        //Включаю GSM Модуль
        digitalWrite(powerGSM, HIGH);
        delay(1000);
        digitalWrite(powerGSM, LOW);
        delay(5000);
        gprsSerial.begin(19200);
        delay(300);
        // Настраиваем приём сообщений с других устройств
        // Между командами даём время на их обработку
        gprsSerial.print("AT+CMGF=1\r");
        delay(300);
        gprsSerial.print("AT+IFC=1, 1\r");
        delay(300);
        gprsSerial.print("AT+CPBS=\"SM\"\r");
        delay(300);
        gprsSerial.print("AT+CNMI=1,2,2,1,0\r");
        delay(500);
        //Инициализация температурного датчика
        Serial.begin(9600);
        Serial.println("DHTxx test!");
        dht.begin();
        delay(3000);
    // ParseSMS("1;20;0;1;1;1;1");
      currentTime = millis();
      loopTime = currentTime;
    }
    void GetDatchik() {
      // Пол
      // Sensor readings may also be up to 2 seconds 'old' (its a very slow sensor)
      float h = dht.readHumidity();
      float t = dht.readTemperature();
      // check if returns are valid, if they are NaN (not a number) then something went wrong!
      if (isnan(t) || isnan(h)) {
        Serial.println("Failed to read from DHT");
      } else {
        statusHome = int(h)+String(";")+int(t)+String(";")+int(unit1status)+String(";")+int(unit2status)+String(";")+int(unit3status)+String(";")+int(unit4status)+String(";")+int(isPerimetr)+String(";")+digitalRead(perimetr)+String(";")+int(isAuto)+String(";")+int(autoTemp)+String(";STEWARD");
        sendTextMessage(statusHome);
    //  Serial.print("H"); //Влажность
    //  Serial.print(h);
    //  Serial.print(";");
    //  Serial.print("T"); //Температура
    //  Serial.print(t);
    //  Serial.println(";");
    //  Serial.println(statusHome);
      }
    }
    void loop()
    {
    // delay(5000);
    // GetDatchik();
    int sostPerimetr; //Состояние периметра
      if (gprsSerial.available()){
      char currSymb = gprsSerial.read();
    // Serial.print(currSymb);
          //  return;
      //  char currSymb = gprsSerial.read();
      //  Serial.println(currSymb);
        if ('\r' == currSymb) {
            if (isStringMessage) {
                //если текущая строка - SMS-сообщение,
                //отреагируем на него соответствующим образом
                if (!currStr.compareTo("STATUS"))
                {
                  GetDatchik();
                }
                else if (currStr.startsWith("0")||currStr.startsWith("1")) {
                  ParseSMS(currStr);
                }
           
                isStringMessage = false;
            } else {
                if (currStr.startsWith("+CMT")) {
                    //если текущая строка начинается с "+CMT",
                    //то следующая строка является сообщением
                    isStringMessage = true;
                }
            }
            currStr = "";
        } else if ('\n' != currSymb) {
            currStr += String(currSymb);
        }
      }
    //  Контроль периметра!!!
      if (isPerimetr) { //Проверка Включен ли контроль периметра
          digitalWrite(greenLed, HIGH);
          sostPerimetr = digitalRead(perimetr);
            if (sostPerimetr==LOW) { //Проверка на разомкнутый периметр
              digitalWrite(greenLed, LOW);
              delay(200);
              digitalWrite(greenLed, HIGH);
              delay(200);
              //и отсылаем СМС
              if (sendSMS==false){
                sendTextMessage("WARNING! The perimeter of the broken!");
                sendSMS=true;
              }
          }
      } else {
        digitalWrite(greenLed, LOW);
      }
    //  Контроль температуры!!!
      if (isAuto==true) { //Проверка Включен автоуправление батареями
      //Замер температуры можно проводить не чаще раз в 5 минут
          currentTime = millis();
      if(currentTime >= (loopTime + 60000)){
      t = int(dht.readTemperature());
      loopTime = currentTime;
      }
    //
    //  Serial.print("t=");
    //  Serial.println(t);
    //  Serial.print("autoTemp+delta=");
    //  Serial.println(autoTemp+delta);
        if (t > autoTemp+delta) {
            if ( unit1status==true){
            digitalWrite(unit1, LOW);
            unit1status=false;
            }
            if ( unit2status==true){
            digitalWrite(unit2, LOW);
            unit2status=false;
            }
            if ( unit3status==true){
            digitalWrite(unit3, LOW);
            unit3status=false;
            }
            if ( unit4status==true){
            digitalWrite(unit4, LOW);
            unit4status=false;
            }
            } else if (t < autoTemp-delta) {
            if ( unit1status==false){
            digitalWrite(unit1, HIGH);
            unit1status=true;
            }
            if ( unit2status==false){
            digitalWrite(unit2, HIGH);
            unit2status=true;
            }
            if ( unit3status==false){
            digitalWrite(unit3, HIGH);
            unit3status=true;
            }
            if ( unit4status==false){
            digitalWrite(unit4, HIGH);
            unit4status=true;
            }
            }
      }
    }
    void ParseSMS(String currStr) {
      int firstSemicolon;
      //Распарсиваем SMS
      firstSemicolon = 1;//находим первую ;
      //Получаем значение Автоматического режима контроля температуры
      // if (currStr.substring(firstSemicolon+1,currStr.indexOf(';', firstSemicolon + 1 )) == "0"){
        if (currStr.substring(0,1) == "0"){
        isAuto=false;
      }
        else{
          isAuto=true;
        }
      //  Serial.println(currStr.substring(0,1));
    //  Serial.print("isAuto=");
    //  Serial.println(isAuto);
      firstSemicolon = currStr.indexOf(';');
      //Получаем значение Автоматического уровня температуры
      autoTemp = stringToNumber(currStr.substring(firstSemicolon+1,currStr.indexOf(';', firstSemicolon + 1 )));
      firstSemicolon = currStr.indexOf(';',firstSemicolon + 1 );
    // Serial.println(autoTemp);
      //Получаем значение Контроля периметра
      if (currStr.substring(firstSemicolon+1,currStr.indexOf(';', firstSemicolon + 1 )) == "0"){
        isPerimetr=false;
      } else {
          isPerimetr=true;
        }
    //  Serial.println(isPerimetr);
      firstSemicolon = currStr.indexOf(';',firstSemicolon + 1 );
      //Получаем значение Устройства 1
    if ((currStr.substring(firstSemicolon+1,currStr.indexOf(';', firstSemicolon + 1 )) == "0")&&(isAuto==false)){
        unit1status=false;
        digitalWrite(unit1, LOW);
    //  Serial.println("Устройство 1 ВЫКЛ");
      } else{
          unit1status=true;
          digitalWrite(unit1, HIGH);
      //    Serial.println("Устройство 1 ВКЛ");
        }
    firstSemicolon = currStr.indexOf(';',firstSemicolon + 1 );
      //Получаем значение Устройства 2
    if ((currStr.substring(firstSemicolon+1,currStr.indexOf(';', firstSemicolon + 1 )) == "0")&&(isAuto==false)){
        unit2status=false;
        digitalWrite(unit2, LOW);
      } else {
          unit2status=true;
          digitalWrite(unit2, HIGH);
        }
      firstSemicolon = currStr.indexOf(';',firstSemicolon + 1 );
      //Получаем значение Устройства 3
    if ((currStr.substring(firstSemicolon+1,currStr.indexOf(';', firstSemicolon + 1 )) == "0")&&(isAuto==false)){
        unit3status=false;
        digitalWrite(unit3, LOW);
      } else {
          unit3status=true;
          digitalWrite(unit3, HIGH);
        }
      firstSemicolon = currStr.indexOf(';',firstSemicolon + 1 );
      //Получаем значение Устройства 4
    if ((currStr.substring(firstSemicolon+1,currStr.indexOf(';', firstSemicolon + 1 )) == "0")&&(isAuto==false)){
        unit4status=false;
        digitalWrite(unit4, LOW);
      } else {
          unit4status=true;
          digitalWrite(unit4, HIGH);
        }
      }
    /*
    * Функция отправки SMS-сообщения
    */

    void sendTextMessage(String text) {
        // Устанавливает текстовый режим для SMS-сообщений
        gprsSerial.print("AT+CMGF=1\r");
        delay(100); // даём время на усваивание команды
        // Устанавливаем адресата: телефонный номер в международном формате
        gprsSerial.println("AT + CMGS = \"+7909939XXXX\""); //Телефон нужно откорректировать
        delay(100);
        // Пишем текст сообщения
        gprsSerial.println(text);
        delay(100);
        // Отправляем Ctrl+Z, обозначая, что сообщение готово
        gprsSerial.println((char)26);
    }
    //Функция преобразовангия STRING в INT
    int stringToNumber(String thisString) {
      int i, value = 0, length;
      length = thisString.length();
      for(i=0; i<length; i++) {
        value = (10*value) + thisString.charAt(i)-(int) '0';;
      }
      return value;
    }
    Добрый день! Подскажите пожалуйста, как из кода убрать всё лишнее, кроме вкл/выкл реле по gsm? Спасибо.
     
  20. yucan

    yucan Гик

    Просто залить другой скетч, где используется только одно реле