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

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

  1. ИгорьК

    ИгорьК Гуру

    Для "разобраться" вполне здорово. Но для работы - не факт что нужна проверка таймеров по прерываниям таймера.
    Общее правило функций в прерывании - они должны быть очень короткими.
    Ваша же функция, ИМХО!, - очень длинная по времени: в прерывании трижды происходят вычисления времени средствами Ардуино.
    Проверки таймеров лучше делать в цикле программы и не грузить процессор постоянными расчётами - это не вызывается необходимостью по сути программы. Строго ИМХО.
     
    Angbor нравится это.
  2. Angbor

    Angbor Нерд

    у меня соображения были как раз из сокращения частоты рассчетов процессором
    если я правильно понимаю, то loop гоняется на максимальной доступной скорости
    Код (C++):
    void loop() {
      time = millis();
      if (TIMER(t1) > READ_PIN_DELAY) {
        CLR_TIMER(t1);
        pinReading();
      }
      if (TIMER(t2) > REGISTR_DELAY) {
        CLR_TIMER(t2);
        regWork();
      }
      if (TIMER(t3) > BLINK_DELAY) {
        CLR_TIMER(t3);
        blinkS();
    /}
    }
    и если у меня событие просто по определению не может произойти чаще, чем раз в миллисекунду, то к чему частить?
    а так через Timer0 я запускаю все это только раз в миллисекунду
    это я рассуждаю вслух, может я и неправ?
     
  3. ИгорьК

    ИгорьК Гуру

    Есть некто DiHalt - easyelectronics.ru. Все мои заблуждения с этого сайта. Посмотрите, там есть курс AVR в том числе и тема о работе на прерывания.
     
  4. Angbor

    Angbor Нерд

    дык грызу потихоньку
     
  5. Angbor

    Angbor Нерд

    Ну, вроде все работает.

    Пойду почитаю про классы (чего в public, а чего нет), попробую-таки функции вынести в библиотеку (пока безуспешно) и потом поприкручиваю дисплейчик :)
    Код (C++):
    /*
      WaterSystem.h
      Created by Angbor, January 1, 2016
      Released into the public domain
    */


    #ifndef WaterSystem_h
    #define WaterSystem_h

    #include "Arduino.h"

    class WaterTimer
    {
        private:
            long timerLength; // длительность таймера

            unsigned long previousMillis;
            String timerName = "nill";
            void (*ptrOnFuniction)();
            byte withFunction = 0;

        public:
            WaterTimer(long tLength, void f(), byte withFunc, String tName, String tTipe);
            WaterTimer(long tLength, byte withFunc, String tName, String tTipe);
            void timerOn(unsigned long currentMillis);
            void timerOff();
            bool getIsTimerOn(unsigned long currentMillis);
            bool timerOverflow = false;
            String timerType = "loop"; // or "line"
            bool timerIsOn = false;  // Текущее состояние On или Off
    };

    #endif
    Код (C++):
    /*
      WaterSystem.cpp
      Created by Angbor, January 1, 2016
      Released into the public domain
    */

    #include <Arduino.h>
    #include <WaterSystem.h>

    WaterTimer::WaterTimer(long tLength, void f(), byte withFunc, String tName, String tTipe){
       timerName = tName;
       timerType = tTipe;
       withFunction = withFunc;
        ptrOnFuniction = f;
        timerLength = tLength;
        timerIsOn = false;
        previousMillis = 0;
    }

    WaterTimer::WaterTimer(long tLength, byte withFunc, String tName, String tTipe){
       timerName = tName;
       timerType = tTipe;
       withFunction = withFunc;
        timerLength = tLength;
        timerIsOn = false;
        previousMillis = 0;
    }
                                                         
    void WaterTimer::timerOn(unsigned long currentMillis) {
       timerOverflow = false;
       previousMillis = currentMillis;
        timerIsOn = true;
        Serial.print(timerName);
       Serial.println(" ON");
    }

    void WaterTimer :: timerOff() {
        timerIsOn = false;
        Serial.print(timerName);
       Serial.println(" OFF");
    }

    bool WaterTimer :: getIsTimerOn(unsigned long currentMillis){
        if ((timerIsOn) && (currentMillis - previousMillis >= timerLength))
            {
                if (timerType == "loop") {
                    if (withFunction == 1) {
                        ptrOnFuniction();
                    }
                    previousMillis = currentMillis;
                    }
                if (timerType == "line"){
                Serial.print(timerName);
                Serial.println(" overflow");
                timerOverflow = true;
             timerOff();
                }
                return true;
            }
            else {
                return false;
            }
    }
     
    ИгорьК нравится это.
  6. ИгорьК

    ИгорьК Гуру

    Круто: Created by Angbor, January 1, 2016 :)
     
  7. Angbor

    Angbor Нерд

    Код (C++):
    #include <WaterSystem.h>

    // определяем пины для переключателей
    #define BOTTOM_SWITCH   2
    #define MIDDLE_SWITCH   3
    #define TOP_SWITCH      4
    #define OVERFLOW_SWITCH 5

    //=== замедления циклов ======
    #define READ_PIN_DELAY 100
    #define REGISTR_DELAY  150
    #define BLINK_DELAY    300

    //=== реле ======
    #define PUMP            9
    #define PUMP_ON         1
    #define PUMP_OFF        0
    #define THREE_WAY_VALWE 10
    #define TWV_ON          1
    #define TWV_OFF         0

    //потом надо перевести это дело как-то в часы и минуты (соответственно таймеру). сейчас пока в милисекундах
    #define DOWNTIME        5000 // 3H время простоя, после которого обязательна прокачка.
    #define TWV_DELAY       3000 // 15M время прокачки
    #define TWV_OPENING     2000 // 1M время на открытие вентиля (потом надо заменить это на чтение концевика 3W

    // прототипы и укзатели функций
    void blinkS();
    void pinReading();
    void regWork();

    void (*ptrBlinkS)() = blinkS;
    void (*ptrPinReading)() = pinReading;
    void (*ptrRegWork)() = regWork;

    // -- таймеры для снижения частоты смены состояний ---------------
    #define WITH_FUNCTION    1
    #define WITHOUT_FUNCTION 0

    WaterTimer timerBlink(BLINK_DELAY, ptrBlinkS, WITH_FUNCTION, "BLINK_DELAY", "loop");
    WaterTimer timerPinRead(READ_PIN_DELAY, ptrPinReading, WITH_FUNCTION, "READ_PIN_DELAY", "loop");
    WaterTimer timerRegistrDelay(REGISTR_DELAY, ptrRegWork, WITH_FUNCTION, "REGISTR_DELAY", "loop");
    WaterTimer timer3H(DOWNTIME, WITHOUT_FUNCTION, "3H", "line");
    WaterTimer timer1M(TWV_OPENING, WITHOUT_FUNCTION, "1M", "line");
    WaterTimer timer15M(TWV_DELAY, WITHOUT_FUNCTION, "15M", "line");

    bool fPumpIsOn  = false;
    bool f3wIsOpen  = false;

    // описание состояний
    #define TANK_IS_EMPTY         0
    #define TANK_LESS_THAN_A_HALF 1
    #define TANK_MORE_THAN_A_HALF 2
    #define TANK_IS_FULL          3
    #define WRONG_SENSOR_COMB     4
    #define TANK_OVERFLOW         5
    #define SENSORS_IS_OK         6 // заглушка для функции проверки аварии, когда в прошлом вызове была авария, а сейчас нет и еще нет сигнала от сенсоров, чтобы переписать состояние
    byte fSystemState = 0;

    String systemStateName[] = {"EMPTY", "LESS THEN A HALF", "MORE THAN A HALF", "FULL", "WRONG SENSORS", "OVERFLOW", "RESET SENSORS"};

    // ====== для цикла чтения пинов
    #define NUM 4
    #define GET(b) digitalRead(arr[b])
    const int arr[] = {BOTTOM_SWITCH, MIDDLE_SWITCH, TOP_SWITCH, OVERFLOW_SWITCH};

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

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

    String ledSName[] = {"Bottom switch", "Middle switch", "Top switch", "OverFlow switch"};
    byte  ledS[] = {B00000001, B00000010, B00000100, B00001000}; //значения, соответствующие включенному LED

     
     
  8. Angbor

    Angbor Нерд

    Код (C++):
    /*================================================
      ================================================
      ================================================
      =============== SETUP ==========================
      ================================================
      ================================================
      ================================================
    */

    void setup() {
      {
        // Timer0 уже используется функцией millis()
        // чтобы не мешать, прерывемся примерно посередине и вызываем ниже функцию "Compare A"
        OCR0A = 0xAF;
        TIMSK0 |= _BV(OCIE0A);
      }

      unsigned long currentMillis = millis();
      timerBlink.timerOn(currentMillis);
      timerPinRead.timerOn(currentMillis);
      timerRegistrDelay.timerOn(currentMillis);
      timer3H.timerOn(currentMillis);

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

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

      // инициализация "кнопок"
      for (byte i = 0; i < NUM; i++) {
        pinMode(GET(i), INPUT);
      }

      Serial.begin(9600);
      while (!Serial) {
      }
    }

     
     
  9. Angbor

    Angbor Нерд

    Код (C++):

    /*================================================
      ================================================
      ================================================
      ===============  FUNCTIONS =====================
      ================================================
      ================================================
      ================================================
    */


    SIGNAL(TIMER0_COMPA_vect)
    {
      unsigned long currentMillis = millis();

      timerBlink.getIsTimerOn(currentMillis);
      timerPinRead.getIsTimerOn(currentMillis);
      timerRegistrDelay.getIsTimerOn(currentMillis);
      timer3H.getIsTimerOn(currentMillis);
      timer1M.getIsTimerOn(currentMillis);
      timer15M.getIsTimerOn(currentMillis);
    }

    void blinkS() {
      for (int i = 0; i < 4 ; i++) {
        if ((blinkF[i] == 1) && (switchState[i]) == 1) {
          ledsBurn = ledsBurn ^ ledS[i];
        }
      }
    }

    void systemStateChanging (byte inInt) {
      static byte st = 0;
      switch (st)
      {
        case 0: if ( ((fSystemState == TANK_IS_EMPTY) || (fSystemState == TANK_LESS_THAN_A_HALF))  && (!timer3H.timerOverflow))
            /*бак пустой полностью || уровень ниже половины) && 3ч не истек*/
          {
            st = 1;
            Serial.println("PUMP");
            timer3H.timerOff();
            pumpOnOff(PUMP_ON);
          }
          if ( ((fSystemState == TANK_IS_EMPTY) || (fSystemState == TANK_LESS_THAN_A_HALF)) && (timer3H.timerOverflow))
            /*(бак пустой полностью || уровень ниже половины) && 3ч истек*/
          {
            st = 2;
            Serial.println("3W opening");
            timer3H.timerOff();
            threeWayOnOff(TWV_ON);
            timer1M.timerOn(millis());
          }
          if (caseOfEmergency ()) {
            st = 4;
          }
          break;
        //==============================================
        case 1: if (fSystemState == TANK_IS_FULL) {
            /*бак полный*/
            st = 0;
            Serial.println("FULL");
            pumpOnOff(PUMP_OFF);
            timer3H.timerOn(millis());
          }
          if (caseOfEmergency ()) {
            st = 4;
          }
          break;
        //==============================================
        case 2: if (timer1M.timerOverflow) {
            /*таймер 1М истек*/
            st = 3;
            Serial.println("3w is open start PUMP OUT");
            timer15M.timerOn(millis());
            pumpOnOff(PUMP_ON);
          }
          if (caseOfEmergency ()) {
            st = 4;
          }
          break;
        //==============================================
        case 3: if (timer15M.timerOverflow) {
            /*таймер 15м истек*/
            st = 1;
            Serial.println("3w close start pump IN");
            threeWayOnOff(TWV_OFF);
          }
          if (caseOfEmergency ()) {
            st = 4;
          }
          break;
        //==============================================
        case 4: if (inInt == 0/*сброс*/) {
            st = 0;
            Serial.println("DROP");
            // просто переход в обчныое состояние
          }
          break;
      }
    }

    bool caseOfEmergency () {
      if ((fSystemState == WRONG_SENSOR_COMB) || (fSystemState == TANK_OVERFLOW)) {
        timer1M.timerOff();
        timer15M.timerOff();
        timer3H.timerOn(millis());
        threeWayOnOff(TWV_OFF);
        pumpOnOff(PUMP_OFF);
        if (fSystemState == WRONG_SENSOR_COMB) {
          /*неверная комбинация датчиков*/
          Serial.println("WRONG COMB");
          return true;
        }
        if (fSystemState == TANK_OVERFLOW) {
          /* перелив*/
          Serial.println("OVERFLOW");
          return true;
        }
      }
      else return false;
    }


    void pumpOnOff (int onOrOff) {
      if ((onOrOff == 1) && !fPumpIsOn) {
        digitalWrite(PUMP, PUMP_ON);
        fPumpIsOn = true;
        Serial.println("PUMP IS ON");
      }
      if ((onOrOff == 0) && fPumpIsOn) {
        digitalWrite(PUMP, PUMP_OFF);
        fPumpIsOn = false;
        Serial.println("PUMP IS OFF");
      }
    }

    void threeWayOnOff (int onOrOff) {
      if ((onOrOff == 1) && !f3wIsOpen) {
        digitalWrite(THREE_WAY_VALWE, TWV_ON);
        f3wIsOpen = true;
        Serial.println("3W IS ON");
      }
      if ((onOrOff == 0) && f3wIsOpen) {
        digitalWrite(THREE_WAY_VALWE, TWV_OFF);
        f3wIsOpen = false;
        Serial.println("3W IS OFF");
      }
    }

    void sensorsRead() {
      if (checkSwitchesCombination()) {
        if ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 0) && (switchState[3] == 0)) {
          fSystemState = TANK_IS_EMPTY;
          //  Serial.println("EMPTY");
        }
        else if ((switchState[0] == 0) && (switchState[1] == 0) && (switchState[2] == 0) && (switchState[3] == 0)) {
          //  Serial.println("LESS THAN A HALF");
          fSystemState = TANK_LESS_THAN_A_HALF;
        }
        else if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 0) && (switchState[3] == 0)) {
          fSystemState = TANK_MORE_THAN_A_HALF;
          //   Serial.println("MORE THAN A HALF");
          // в этом состоянии насос не включаем
        }
        else if ((switchState[0] == 0) && (switchState[1] == 1) && (switchState[2] == 1) && (switchState[3] == 0)) {
          fSystemState = TANK_IS_FULL;
          //    Serial.println("FULL");
        }
      }
    }

    void led_switching (int switchNumber) {
      if (switchState[switchNumber] == HIGH) {
        bitWrite(ledsBurn, switchNumber, 1);
      }
      else {
        if (switchState[switchNumber] == LOW) {
          bitWrite(ledsBurn, switchNumber, 0);
        }
      }
    }

    bool checkSwitchesCombination() {
      if (switchState[3] == 1) { // если переполнение
        if (fSystemState !=  TANK_OVERFLOW) {
          fSystemState = TANK_OVERFLOW;
        }
        return false;
      }
      else if (((switchState[0] == 0) && (switchState[1] == 0) && (switchState[2] == 1) && (switchState[3] == 0)) ||
               ((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 1) && (switchState[3] == 0)) ||
               ((switchState[0] == 1) && (switchState[1] == 0) && (switchState[2] == 1) && (switchState[3] == 0)) ||
               ((switchState[0] == 1) && (switchState[1] == 1) && (switchState[2] == 0) && (switchState[3] == 0))) {
        // Serial.println("WRONG COMBINATION");
        if (fSystemState != WRONG_SENSOR_COMB) {
          fSystemState = WRONG_SENSOR_COMB;
        }
        return false;
      }
      else { // если все ОК
        if ((fSystemState == WRONG_SENSOR_COMB) || (fSystemState == TANK_OVERFLOW)) {// если в прошлом вызове была аварийная ситуация
          fSystemState = SENSORS_IS_OK;
          systemStateChanging(0);// сброс
        }
        return true;
      }
    }

    void regWork() {
      // использует подготовленную другими функциями перменную ledsBurn
      digitalWrite(latchPin, LOW);
      shiftOut(dataPin, clockPin, MSBFIRST, ledsBurn);
      digitalWrite(latchPin, HIGH);
    }

    void pinReading() {
      for (byte i = 0; i < NUM; i++) {
        switchState[i] = GET(i);
        if (switchState[i] != lastSwitchState[i]) {
          led_switching(i);
          sensorsRead();
        }
        lastSwitchState[i] = switchState[i];//обновляем значение последнего состояния переключателя
        systemStateChanging(1);
      }
    }

    void loop() {

      while (Serial.available() > 0) {
        byte sensors = Serial.parseInt();
        if (Serial.read() == '\n') {
          if (sensors == 9) {
            //
          }
        }
      }
    }
     
  10. Angbor

    Angbor Нерд

    круче другое:
    даже не надеюсь на прочтение кем-то кода :)
    просто на случай, если опять что-то и где-то поудаляется :)
     
    Sparkfire и ИгорьК нравится это.
  11. Angbor

    Angbor Нерд

    Это конечно, смешно, но прошло больше, чем год :D;)
    Когда я сел вспоминать, что я там наваял столько времени спустя, даже учитывая мою почти параноидальную склонность комментировать очевидные вещи, я с трудом справился :)
    Поскольку задача передо мной сейчас была сугубо практическая - таки запустить автоматику, я решил оставить в стороне всякие излишества, типа дисплея (через неделю потеряшек в менюшках, отложил), типа часов реального времени (китайский шедевр при отключении упорно выдавал 0,6в с рабочей батарейки с ожидаемым сбросом времени), записи на SD (я уже и не вспомнил, что я таки хотел записывать:) )

    Итак, вчера я все запустил. Работает. Вместо дисплея - 7 led-ов. Но, конечно, есть ряд нареканий и поводов к улучшению.
    1. Логика кода была так жестко завязана на состояние поплавков в жесткой связке с светодиодами, что попытки поменять логику "мигания" подвела меня к необходимости вообще пересмотреть сам принцип сигнализации состояний. Иначе ничего не выходит.
    2. При первом включении системы по умолчанию запускается полный цикл с прокачкой скважины и т.д. Но состояние поплавков считывается уже после того, как проходит цикл анализа состояния - в результате, даже если бак полный, полный цикл стартует с риском перелива (сегодня проверю, так ли это). Но почему и где это происходит - загадка. В своем же коде не могу разобраться :(
    Попробую вообще с чистого листа начать.

    Ну и плюшки на будущее.
    1. Я хотел бы все-таки прикрутить сдвиговый регистр на вход (на выход уже работает). Пока не успешно.
    2. Считывать состояние вентиля (там есть концевики), убрав один таймер.
    3. Есть потребность летом один раз в сутки по любому прокачивать скважину, наполняя технический пруд.
    4. Заказал на Али ультрасоник, очень уж хочется знать точно, сколько воды в баке. А то я его в пенал из ЭППС запаковал (иначе конденсатом заливает пол жестко) - ну и ничего не видно.
    5. Дисплей, часы и SD :)

    На память себе закдываю файлики.
     

    Вложения: