Двухступенчатое водоснабжение загородного дома

Тема в разделе "Глядите, что я сделал", создана пользователем Angbor, 18 дек 2015.

  1. alexys5

    alexys5 Нерд

    Я делал аналогичную систему в загорордном доме, т.к. колодец, во-первых из 12-ти колец в земле и еще он на расстоянии 30м от дома. Вот только определение уровня воды в накопительном баке (у меня Aquatech на 200л) сделал на ультразвуковом датчике измерения расстояния HC-SR04. Устанавливается в отверстии верхней крышки бака, калибруется один раз на нижний уровень зеркала воды, далее, зная размеры бака расчетным методом определяются и уровень воды в мм и объем воды в литрах и процентах - и вот оно счастье. За год работы не было ни одного сбоя при температурах от +6 до +40. Очень важно, что нет никаких контактов, которые могут окислиться, никаких подвижных частей. Обрабатываются не 3 датчика, а показания одного датчика. 20150802_153343_1301x732.jpg 20150802_153507_1301x2313.jpg
     
    Jorimar и Angbor нравится это.
  2. ИгорьК

    ИгорьК Гуру

    Вам везёт. Во влажной среде ультрасоник не выходит из строя. У меня на лестнице они работают по полгода. На мой взгляд - только герконы. Пара для надёжности.
     
  3. Angbor

    Angbor Нерд

    шшшш.... сссс...пппп..ррррр..ууууу..ыыыыыыыыы
    короче я не знаю, что случилось - на скетч был непонятным образом удален с компа (понятно что как-то моими собственными руками), причем все архивные варианты тоже...:eek::eek::eek:
    ну, зато лучше узнаю С++

    я думал об этом, но что-то кто-то где-то говорил, что для воды может быть глюк...
    но вот ваш опыт говорит об обратном
    но у меня уже усеравно поплавковые выключатели пришли с Китаю.
    а скетчик не выложите? или может уже тема на форуме есть?
    так он выходит или не выходит?
    не совсем понятно, вы шутите или сообщаете факт
     
    Последнее редактирование: 4 янв 2016
  4. ИгорьК

    ИгорьК Гуру

    Я не шучу. У меня на лестнице, то есть в сухих, хороших условиях датчики работают по полгода.
    Не могу представить как они, будучи подвешенными над водой, в каком-нибудь служебном помещении усердно трудятся надёжно и без сбоев. Я бы никогда такой вариант даже не рассматривал. Но готов признать что чудеса бывают.
     
  5. Angbor

    Angbor Нерд

    а, вот теперь вас понял
    я, в общем-то как-то спокоен своим выбором поплавковых выключателей.
    код почти восстановил, осталось дисплей прикрутить, но это завтра
    на память закину код сюда
    Код (C++):
    // определяем пины для переключателей
    #define BOTTOM_SWITCH   2
    #define MIDDLE_SWITCH   3
    #define TOP_SWITCH      4
    #define OVERFLOW_SWITCH 5
    #define BLINK_DELAY   200

    #define PUMP            9
    #define PUMP_ON         1
    #define PUMP_OFF        0
    #define THREE_WAY_VALWE 10
    #define TWV_ON          1
    #define TWV_OFF         0

    // устанавливаем управление сдвиговым регистром
    int latchPin  = 8;
    int clockPin  = 12;
    int dataPin   = 11;
    uint8_t ledsBurn = 0; //переменная для передачи в регистр
    //long led_reg_millis = 0; // храним значение с последнего

    // реле
    bool pumpState = false;

    // остальное
    byte switchState[] = {1, 0, 0, 0}; // состояние переключателей
    byte lastSwitchState[] = {0, 0, 0, 0}; // предыдущее состояние
    byte blinkF[] = {1, 0, 0, 1}; /*флаги для включения blink, здесь указываем по умолчанию какие диоды будут мигать (1),
                                  а какие гореть постоянно (0)*/

    bool flagT[] = {false, false, false, false}; // временный флаг для цикла blink

    String ledSName[] = {"Bottom switch", "Middle switch", "Top switch", "OverFlow switch"}; // названия для вывода в порт
    byte  ledS[] = {B00000001, B00000010, B00000100, B00001000}; //тут храним значения для "зажигания" диодов. Пока их три

    void setup() {
      // настройка сдвигового регистра
      pinMode(latchPin, OUTPUT);
      pinMode(clockPin, OUTPUT);
      pinMode(dataPin, OUTPUT);

      // инициализация реле
      pinMode(PUMP, OUTPUT);
      pinMode(THREE_WAY_VALWE, OUTPUT);

      // инициализация кнопок
      for (int i = 2; i < 6; i++) {
        pinMode(i, INPUT);
      }
      // инициализируем порт
      Serial.begin(9600);
    }

    void pumpOnOff (int onOrOff) {
      if ((onOrOff == 1) && !pumpState) {
        digitalWrite(PUMP, PUMP_ON);
        pumpState = true;
      }
      if ((onOrOff == 0) && pumpState) {
        digitalWrite(PUMP, PUMP_OFF);
        pumpState = false;
      }
    }

    void led_switching (int switchNumber) {
      if (switchState[switchNumber] == HIGH) {
        // если состояние HIGH и флаг не был установлен
        // включаем
        Serial.println(ledSName[switchNumber] + " is ON");
        bitWrite(ledsBurn, switchNumber, 1);
      }
      else {
        if (switchState[switchNumber] == LOW) {
          // если состояние LOW
          // выключаем
          Serial.println(ledSName[switchNumber] + " is OFF");
          bitWrite(ledsBurn, switchNumber, 0);
        }
      }
      // немного ждем
      delay(10);
      lastSwitchState[switchNumber] = switchState[switchNumber];//обновляем значение последнего состояния переключателя
    }

    void blinkS() {
      // мигаем
      for (int i = 0; i < 4 ; i++) {
        if ((blinkF[i] == 1) && (switchState[i]) == 1) {
          if ((millis() / BLINK_DELAY) % 2 == 1)   {
            if (!flagT[i]) {
              /* если не устанавливать этот флаг, то диод будет мелко мигать пока
                 значение millis() в допустимом диапазоне значений
                 можно использовать переменную и делать типа:
                 if ((millis() - lastMillis) <= BLINK_DELAY) THEN ->

              */

              ledsBurn = ledsBurn ^ ledS[i];
              //попробовать использовать сдвиг влево в байте 00000001 [i-1] вместо хранения всех вариантов в массиве
              flagT[i] = true;
            }
          }
          else
          {
            flagT[i] = false;
          }
        }
      }
    }

    bool checkOverflow() {
      if (switchState[3] == 1) {
        Serial.println("OVERFLOW");
        return false;
      }
      if (switchState[3] == 0) {
        // Serial.println("NOT OVERFLOW");
        return true;
      }
    }

    bool checkSwitchesCombination() {
      if (checkOverflow()) {// если не переполнение
        if (((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 1)) ||
            ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 1)) ||
            ((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 0))) {
          Serial.println("WRONG COMBINATION");
          pumpOnOff(0);
          return false;
        }
        else { // если комбинация не ошибочная
          return true;
        }
      }
      else {
        pumpOnOff(0);
        return false; // если checkOverflow вернула ошибку
      }
    }

    void pump() {
      if (checkSwitchesCombination()) {
        if ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 0)) {
          Serial.println("1");
          pumpOnOff(1);
        }
        if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 0)) {
          Serial.println("2");
          pumpOnOff(1);
        }
        if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 1)) {
          Serial.println("3");
          pumpOnOff(0);
        }
      }
    }

    void loop() {
      // читаем состояние переключателей
      for (int i = 2; i < 6; i++) {
        switchState[i - 2] = digitalRead(i);
      }
      //в случае изменения состояния переключателей идем в функцию
      for (int i = 0; i < 4; i++) {
        if (switchState[i] != lastSwitchState[i]) {
          led_switching(i);
        }
      }
      blinkS();
      pump();//checkSwitchesCombination();

      // работаем со сдвиговым регистром
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, ledsBurn);
      digitalWrite(latchPin, HIGH);
    }
     
  6. Sparkfire

    Sparkfire Гик

    Поплавковые выключатели, как мне кажется, надежнее ультрасоника. Но их нужно обслуживать? И хорошо, что их несколько. Меньше вероятность поломки всех сразу. Автору пятерка с плюсом.
     
  7. Angbor

    Angbor Нерд

    завышено ;)
    s-l500.jpg
    выключатель вот такой, все запаяно намертво, так что или работает или нет
     
  8. Angbor

    Angbor Нерд

    Тут на другом форуме интересная дискуссия возникла. мне советуют вот этот "ужос"
    Код (C++):
    #define BOTTOM_SWITCH   2
    #define MIDDLE_SWITCH   3
    #define TOP_SWITCH      4
    #define OVERFLOW_SWITCH 5
    ...
    ...
    ...
    void loop() {
      // читаем состояние переключателей
      for (int i = 2; i < 6; i++) {
        switchState[i - 2] = digitalRead(i);
      }
    поменять на вот это
    Код (C++):
    #define NUM 4
    #define BOTTOM_SWITCH   2
    #define MIDDLE_SWITCH   3
    #define TOP_SWITCH   4
    #define OVERFLOW_SWITCH  5
    int arr[NUM] PROGRAM={BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};
    #define GET(b) digitalRead(arr[b])

    void loop()
    {
      for (byte i=0; i<NUM; i++)
      {
        button[i]=GET(i);
      }
    }
    я предложил убрать
    Код (C++):
    #define NUM 4
    и сделать так
    Код (C++):
    int arr[] PROGRAM={BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};
    но сути предложения это не меняет. добавить еще массив, но цикл выглядит "красиво"
    какие мысли?
     
    Последнее редактирование: 5 янв 2016
  9. Angbor

    Angbor Нерд

    короче, отказался я от PROGMEM
    нужна помощь, уже просто голову сломал над тем, как уйти от delay
    логика вот. работает но с delay

    Код (C++):
    bool checkPause() {
    time = millis();
    if (TIMER(t4) > PUMP_DELAY * 1000) { // если таймер простоя превышен
    if (!twvIsOpen) {
    // если вентиль не открыт
    digitalWrite(THREE_WAY_VALWE, TWV_ON);
    Serial.println("VALWE OPEN");
    twvIsOpen = true;
    return false;
    }
    if (twvIsOpen) {
    delay(TWV_OPENING);// минуту как бы ждем открытия вентиля
    pumpIsOn = true;
    Serial.println("PUMP OPEN"); // включаем насос
    digitalWrite(PUMP, PUMP_ON);
    delay (TWV_DELAY);// 15 минут как бы прокачиваем
    digitalWrite(THREE_WAY_VALWE, TWV_OFF);
    Serial.println("VALWE CLOSE");
    twvIsOpen = false;
    CLR_TIMER(t4);
    return false;
    }
    }
    }
    void pumpOnOff (int onOrOff) {
    if ((onOrOff == 1) && !pumpIsOn && checkPause()) {
    digitalWrite(PUMP, PUMP_ON);
    pumpIsOn = true;
    }
    if ((onOrOff == 0) && pumpIsOn) {
    digitalWrite(PUMP, PUMP_OFF);
    CLR_TIMER(t4);
    pumpIsOn = false;
    }
    }
    когда начинаю "в лоб" заменять delay и писать if b т.д. просто дебри получаются и постоянно сбиваюсь
    может просто сам подход неверный?

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

    ИгорьК Гуру

    Включили насос, считали время включения и прибавили к нему 15 минут, установили флаг что насос включён.
    Сделали if в котором проверяете не больше ли текущее время времени, когда надо выключить насос и включён ли он (установлен ли флаг).
    Если установлен флаг работы и время превышено - выключатете насос и сбрасываете флаг.
     
    Последнее редактирование: 6 янв 2016
  11. Angbor

    Angbor Нерд

    Не, Игорь, вы я не смог вам донести логику функционирования :)
    Впрочем не важно :)
    Смешно конечно, и банально. Но сложность кода превысила порог просто "с наскока". Так что погрузился в чтение некоторой литературы :)
    В частности меня интересует вопрос прерываний и таймеров.
     
  12. ИгорьК

    ИгорьК Гуру

    Я, к сожалению, заглядываю сюда с сотового телефона. Отпуск, понимаете ли, страна чужая...
    Так что понять код не в состоянии. Я его практически не вижу.
    Но что-то подсказывает мне, что Вам лучше разобраться со структурой написания программ, а не с таймером и прерываниями. Таймеры нужны для работы с точными отрезками времени, прерывания - для точной отработки событий.
    Что-то я не представляю их необходимости при работе с насосами. Уверен - Вы не туда идёте.
     
  13. Angbor

    Angbor Нерд

    Желаю вам хорошенько отдохнуть.
    Сложность для меня в том, что я впервые сталкиваюсь с логикой программирования микроконтроллеров, я избалован ООП и Delphi. Ну и PHP в виде десерта :)
    Так что в любом случае, мне сейчас надо в теорию вопроса погрузиться. Уверен, что дружеская обратная связь коллег на форумах не даст мне родить монстра вместо элегантной програмульки :)
     
  14. ИгорьК

    ИгорьК Гуру

  15. ИгорьК

    ИгорьК Гуру

    ООП на С++ - классика. Не думаю что для программиста будет сложно создать пару классов, экземпляры и апдейтить их нипадецки.
     
  16. Angbor

    Angbor Нерд

    да я вот "вспоминаю все", как-то у меня из-за новой среды С++ вдруг все забылось, а сейчас понимаю, что ООП вполне себе можно и нужно задействовать.
    про внешние прерывания... учитывая, что на леонардо 4 пина их обрабатывают, у меня 4 "ключевых" кнопки... и "дозвидания loop()" :)
    ссылку, которую вы прислали - там давно присматриваюсь, но требует сесть и почитать, а не бегом просмотреть.
    Но вот сейчас как раз настало время.
    Уезжаю на днях в столицу из деревни на пару месяцев, так что еще больше времени для вдумчивого чтения - кур кормить не надо, дом строить - не надо :)
     
  17. ИгорьК

    ИгорьК Гуру

    И кнопки, даже четыре, совсем не обязательно вешать на прерывания. Там же есть программный антидребезг - вполне себе медленное действо.
     
  18. Angbor

    Angbor Нерд

    да я похоже погорячился, почитал про ограничения использования прерываний - в моем случае осложнений возникнет больше, чем преимуществ. да и с таймерами не все так просто :)
     
  19. Angbor

    Angbor Нерд

    а потом прочел фразу "если 10 событий из 10 важные, то ни одно из 10 не важно"... и понял, что прерывания мне точно не нужны, а вот с таймером поигрался немного. в данном случае проверка раз в милисекунду
    Код (C++):
    class WaterTimer
    {
      private:
        long timerLength; // длительность таймера
        bool timerState;  // Текущее состояние On или Off
        unsigned long previousMillis;

      public:
        WaterTimer(long tLength)
        {
          timerLength = tLength;
          timerState = false;
          previousMillis = 0;
        }
        void setTimerState(bool stateChang) {
          timerState = stateChang;
        }

        bool getTimerState(unsigned long currentMillis)
        {
          if ((timerState) && (currentMillis - previousMillis >= timerLength))
          {
            previousMillis = currentMillis;
            return true;
          }
          else {
            return false;
          }
        }
    };

    WaterTimer timer1(1000);
    WaterTimer timer2(500);
    WaterTimer timer3(800);

    void setup()
    {
      Serial.begin(9600);
      OCR0A = 0xAF;
      TIMSK0 |= _BV(OCIE0A);
    }

    SIGNAL(TIMER0_COMPA_vect)
    {
      unsigned long currentMillis = millis();
      if (timer1.getTimerState(currentMillis)) {
        Serial.println("TIMER 3H");
      }
      if (timer2.getTimerState(currentMillis)) {
        Serial.println("TIMER 15M");
      }
      if (timer3.getTimerState(currentMillis)) {
        Serial.println("TIMER 1M");
      }
    }

    void loop()
    {
      delay(1000);
      Serial.println("ALL is ON");
      timer1.setTimerState(true);
      timer2.setTimerState(true);
      timer3.setTimerState(true);
      delay(10000);
      timer1.setTimerState(false);
      timer2.setTimerState(false);
      timer3.setTimerState(false);
      Serial.println("ALL is OFF");
      delay(3000);
    }
    любопытно, учитывая у меня три таймера в проекте, вполне вариант. пока не вижу подвожных камней
     
    ИгорьК нравится это.
  20. ИгорьК

    ИгорьК Гуру

    WaterTimer(long tLength)
    {
    timerLength=tLength

    unsigned long tLength