Гистерезис - алгоритм управления увлажнителем по показаниям датчика влажности

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

И снова горячо привествую всех пользователей форума. Есть задача: нужно управлять переключением скоростей мотора вентилятора в зависимости от показаний датчика влажности. Алгоритм работы следующий

Если влажность выше заданной на более чем 4% - увлажнитель не работает, если влажность отличается на 3% от заданной в обе стороны, то включаем в режим малой производительности, если влажность ниже заданной на 4-6%, то включаем увлажнитель в режим средней производительности, и если влажность ниже заданной более чем на 7%, включаем максимальную производительность увлажнителя. Тупо в лоб, решил задачу таким кодом:

case 0: //автоматический режим выбора скорости, основанный на показаниях датчика
      {
        digitalWrite(LedAutoSpeed, HIGH);
        digitalWrite(LedLowSpeed, LOW);
        digitalWrite(LedMedSpeed, LOW);
        digitalWrite(LedHighSpeed, LOW);
                                                    // вот тут автоматика началась
        HumCalc = HumTargetNew - HumCurrent;        //вычисляем разницу между измеренной и заданной влажностью
        if (HumCalc <= -4) {                        //если влажность на 4% выше зааданной, то...
          digitalWrite(MotorFan, LOW);              //...отключаем мотор вентилятора...
          digitalWrite(PumpWater, LOW);             //...отключаем помпу...
          digitalWrite(MotorOsc, LOW);              // ...отключаем OSC.
        } else if (HumCalc >= -3 && HumCalc <= 3) { //если влажность отличается на 3% от зааданной в обе стороны, то...
          analogWrite(MotorFan, FanLowSpeed);       //...включаем мотор вентилятора на малую скорость...
          digitalWrite(PumpWater, HIGH);            //...включаем помпу...
          if (StateOsc == true) {                   // проверяем был ли включен OSC, если да, то...
            digitalWrite(MotorOsc, HIGH);           // ...включаем OSC
          }
        } else if (HumCalc >= 4 && HumCalc <= 6) {  //если влажность ниже заданной на 4-6%, то...
          analogWrite(MotorFan, FanMedSpeed);       //...включаем мотор вентилятора на среднюю скорость...
          digitalWrite(PumpWater, HIGH);            //...включаем помпу...
          if (StateOsc == true) {                   // проверяем был ли включен OSC, если да, то...
            digitalWrite(MotorOsc, HIGH);           // ...включаем OSC
          }
        } else if (HumCalc >= 7) {                  //если влажность ниже заданной более чем на 7%, то...
          analogWrite(MotorFan, FanHighSpeed);      //...включаем мотор вентилятора на высокую скорость...
          digitalWrite(PumpWater, HIGH);            //...включаем помпу...
          if (StateOsc == true) {                   // проверяем был ли включен OSC, если да, то...
            digitalWrite(MotorOsc, HIGH);           // ...включаем OSC
          }
        }                                           // вот тут автоматика закончилась
        break;

Но уже на этапе тестирования наступил на грабли - когда влажность колеблется на границе диапазона на 1-2% туда-сюда и соответствено дергаются туда-сюда скорости мотора, что не есть гуд. Гугленье дало несколько варианто решения проблемы:

1 - увеличение интервала опроса датчика - С этим поигрался, в принципе если поставить интервал опроса датчика раз в 5 минут, то все работает, но решение не слишком красивое на мой взгляд.

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

3 - учет гистерезиса - вроде как и самое правильное решение, но алгоритм никак не рождается. Если бы была одна скорость, то решение просто как 5 копеек, создаем переменную HumHysteresis и присваиваем ей значение например 2, и:

// кошки on
    case 0: //автоматический режим выбора скорости, основанный на показаниях датчика - тест гистерезиса
      {
        digitalWrite(LedAutoSpeed, HIGH);
        digitalWrite(LedLowSpeed, LOW);
        digitalWrite(LedMedSpeed, LOW);
        digitalWrite(LedHighSpeed, LOW);
        if (HumCurrent >= HumTarget + HumHysteresis) { // если измеренная влажность больше целевой на велицину гистерезиса
          digitalWrite(MotorFan, LOW); // выключаем вентилятор
          digitalWrite(PumpWater, LOW); //... и помпу
        } else if (HumCurrent <= HumTarget - HumHysteresis) { // если измеренная влажность меньше целевой на велицину гистерезиса
          digitalWrite(MotorFan, HIGH); // включаем вентилятор
            digitalWrite(PumpWater, HIGH); //... и помпу
          }
           
        break;
      }
    // кошки off

А вот с тремя скоростями ну никак схема в эту конструкцию не укладывается. 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Dinosaur пишет:
А вот с тремя скоростями ну никак схема в эту конструкцию не укладывается.
Принято к сведению.

Информируйте нас о новых идеях и продвижени работ.

Когда (и если) у Вас появятся вопросы, не стесняйтесь спрашивать.

Пока же позволю себе побрюзжать на строки 16, 22 и 28. Нафига проверять флаг, если потом не изменять его значения?

sadman41
Offline
Зарегистрирован: 19.10.2016

ЕвгенийП пишет:

Пока же позволю себе побрюзжать на строки 16, 22 и 28. Нафига проверять флаг, если потом не изменять его значения?

Выскажу предположение, что этот флаг должны менять залетевшие извне нейтрино. Т.е. такой датчик из МК получается.

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Можно после смены режима не ранее (например) 2 мин разрешать следующее, что в принципе сравнимо с более редким опросом датчика.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

ЕвгенийП пишет:
Когда (и если) у Вас появятся вопросы, не стесняйтесь спрашивать.

Ох, прошу прощения, мозги совсем вскипели к моменту отправки поста. Вопрос - люди добрые, подскажите как можно уложить гистересис в первый фрагмент кода или направьте в нужную сторону, если полет мыслей завел меня на тупиковый путь.

ЕвгенийП пишет:
Пока же позволю себе побрюзжать на строки 16, 22 и 28. Нафига проверять флаг, если потом не изменять его значения?

Этот флаг переворачивается в другом куске кода:

 // опрос кнопки OSC и управление OSC
  if (debouncer1.update()) { //если произошло событие
    if ((debouncer1.read() == 0) && StatusOsc == 0) { //если кнопка OSC нажата и перемення StatusOsc равна 0 , то ...
      digitalWrite(MotorOsc, !digitalRead(MotorOsc));
      StateOsc = !StateOsc; // запоминаем состояние OSC
      StatusOsc = 1;
      tone(Buzzer, 3000, 100);
    }
    if (digitalRead(ButtonOsc) == HIGH && StatusOsc == 1) { //если кнопка OSC не нажата и переменная StatusOsc равна - 1 ,то ...
      StatusOsc = 0; //обнуляем переменную StatusOsc
    }
  }
  // конец опроса кнопки OSC

Т.е. моя логика такая - когда влажность выше заданной, а увлажнитель в режиме работы АВТО, исполнительные устройства (помпу, мотор вентилятора, мотор OSC) отключаются. Мотор OSC на момент отключения всех устройств может быть как включен, так и выключен. Соответственно в момент нажатия кнопки OSC мы включаем мотор и запоминаем его состояние, а в строках 16, 22 и 28 проверяем его состояние, и если мотор OSC работал до момента откючения исполнительных устройсв, то включаем его, иначе - не включаем. Считаете что ректальная реализация?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Dinosaur пишет:

а в строках 16, 22 и 28 проверяем его состояние, и если мотор OSC работал до момента откючения исполнительных устройсв, то включаем его

Вот, включаете. Состояние мотора поменялось? Он ведь теперь включён! А кто будет это запоминать? То бишь флаг менять?

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

ЕвгенийП пишет:
Вот, включаете. Состояние мотора поменялось? Он ведь теперь включён! А кто будет это запоминать? То бишь флаг менять?

А зачем мне запоминать состояние мотора? Этот флаг говорит что то вроде "включать мотор когда увлажнитель работает", и от того что в автоматическом режиме мы этот мотор переодически отключаем (ну достигли мы заданной влажности и перевели увлажнитель в режим мониторинга влажности до достижении порогового значения, зачем нам этим мотором вхолостую маслать), указание "включить этот мотор" при старте вентилятора не теряет силы, просто мы его на время простоя временно отключаем. т.е. в этих строках мы проверяем "было ли дано задание на включение мотора", если да, то включаем, если нет - то не включаем. Возможно сумбурно изложил, но в этом месте код работает так, как я от него и жду.

UPD: Или имеете ввиду что не есть гуд дергать пин, к которому подключен мотор, при переходе со скорости на скорость? Я так понимаю что дерганья и не происходит, если PIN в состоянии HIGH, то при следующем digitalWrite HIGH для него ничего не поменяется, т.е. переключения фактически не будет. Или я не прав?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Dinosaur пишет:

А зачем мне запоминать состояние мотора? 

Ну, не надо, значит - не надо.

nik182
Offline
Зарегистрирован: 04.05.2015

После включения мотора значение флага можно учитывать в формуле сравнения. Прибавляя или вычитая значение флага из померенной влажности можно автоматически поиметь гестирезис.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

nik182 пишет:
После включения мотора значение флага можно учитывать в формуле сравнения. Прибавляя или вычитая значение флага из померенной влажности можно автоматически поиметь гестирезис.

Не совсем уловил Вашу идею. Измеренная мощность заносится в пересенную HumCurrent, значение флага включения мотора будет 1 (я так понимаю речь идет про мотор вентилятора). Формула сравнения будет (HumTarget - (HumCurrent - 1)) <= -4 - и мы получим только сдвиг включения/выключения на 1%, а никак не гистерезис. 

 

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Dinosaur, гистерезитс - частный случай при наличии единственной границы: при движении вверх подвигаем границу вверх, а при движении вниз, соответственно, - вниз. Если границ несколько, то и подход стандартный - переменная состояния.

Но Вам бы пока с гистерезисом разобраться. Судя по предыдущему посту, Вы так и не поняли, как использовать флаг включения мотора для организации гистерезиса. Вы вот, вроде, привели формулу, но флага в ней как не было, так и нет.

nik182
Offline
Зарегистрирован: 04.05.2015

Флаг может иметь любое отличное от нуля значение. Соответственно и сдвиг уровня может быть не на 1% а на столько,сколько нужно.   

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Добрый вечер, корифеи

В общем вопрос поднятый в первом посте так и не дает мне покоя - на данный момент функция (в упрощенном виде) выглядит так:

void speedAutoControl () {
  const byte hysteresis = 3;
  if (humCurrentAverage <= humTarget - hysteresis * 3)  {           
    if (fanCurrentSpeed != fanHighSpeed) {                          
      fanControl(fanHighSpeed);                                     
      Serial.println ("Humidity is very low - speed set to 3");
    }
  } else if (humCurrentAverage <= humTarget - hysteresis * 2)  {  
    if (fanCurrentSpeed != fanMedSpeed) {                         
      fanControl(fanMedSpeed);                                    
      Serial.println ("Humidity is low - speed set to 2");
    }
  } else if (humCurrentAverage <= humTarget - hysteresis) {     
    if (fanCurrentSpeed != fanLowSpeed) {                       
      fanControl(fanLowSpeed);                                  
      Serial.println ("Humidity is ok - speed set to 1");
    }
  } else if (humCurrentAverage < humTarget + hysteresis) {      
    if (fanCurrentSpeed > fanLowSpeed) {                        
      fanControl(fanLowSpeed);                                  
      Serial.println ("Hysteresis loop - speed set to 1");
    }
  } else if (humCurrentAverage >= humTarget + hysteresis) {     
    if (fanCurrentSpeed != 0) {                                 
      fanControl(0);                                            
      Serial.println ("Humidity is high - fan is off");
    }
  }
}

В части включения LOW скорости/выключения все работает корректно: при превышении порогового значения плюс гистерезис мотор отключается, при уходе ниже порогового значения минус гистерезис, мотор включается на LOW скорость, внутри петли гистерезиса его состояние не меняется (за исключением попадания в петлю путем перескока "через скорость" - в этом случае принудительно ставится скорость LOW).

Но на стыках скоростей LOW-MED и MED-HIGH возникает "болтанка", когда humCurrentAverage колеблется возле порогового значения, соответственно скорости мотора переключаются туда-сюда. В голову приходят только "индийские" варианты решения проблемы: вызывать функцию реже (например раз в минуту), тогда переключения скорости мотора будут происходить не так часто и вообще есть надежда что значение humCurrentAverage успеет "убежать" за это время из пограничного диапазопа; прикрутить таймер и запретить изменение скоростей мотора чаще чем заданное время таймера (собственно разновидность первого варианта). Но на мой взгляд это костыли, и должно быть решение проблемы пограничных состояний, а не борьба со следствием. Подскажите в какую сторону думу думать?

IgorR
Offline
Зарегистрирован: 03.04.2018

Нужно отслеживать направление перехода и значение гистерезиса именно для этого перехода (для этой границы) устанавливать в обратную сторону. То есть, если у тебя есть три границы переходов, то нужен массив из трех гистерезисов, в который при каждом пересечении границы нужно установить плюс/минус гистерезис. Опционально, для остальных границ в этот момент записыввать нулевой гистерезис. 

Как-то так.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Ну здесь работает высказывание -Теория без практики мертва, практика без теории слепа. У Вас второй вариант. Пока не разберетесь с теорией, будете тыкаться как слепой котенок.

IgorR
Offline
Зарегистрирован: 03.04.2018

Это Вы мне? У меня уже года три работает выытяжка на кухне именно по этому принцципу: 4 уровня загрязнения воздуха, переключение с гистерезисом на каждом уровне. Практика с теорией слились в экстазе.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

IgorR, скорее всего не Вам. Теория  это не то что работает, а то что позволяет объяснить другому как это работает. И как только вы сможете объяснить Dinosaur-у  и он поймет, то можно сказать что у Вас работает не только практика, но и теория. А так это все слова.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Мужики, спасибо конечно за крылатые фразы, но хотелось бы по существу - это начало моего пути и я прекрасно понимаю что и теория, и практика хромают у меня на обе ноги, в том числе с помощью этого форума пытаюсь разобраться.

к IgorR - определить направление движения проблем нет, но дальше не вполне уловил Вашу мысль. Например целевое значение стоит 50, границы переходов 53 (выкл), 47 (скор 1), 44 (скор 2), 41 (скор 3). НО!!!! текущее значение например 37 (т.е. включаем третью скорость), а следующее прилетает например.... 52. если взять его за новую границу перехода, и от него построить гистерезис в обе стороны, то сущая ерунда получится и еще ошибка накапливаться будет. Можете свою мысль кодом проиллюстрировать?

 

IgorR
Offline
Зарегистрирован: 03.04.2018

Привет. Я тут подумал на досуге. Можно немного по другому и проще. Направление определить несложно. А для переключчения использовать два массива границ: для движения вверх и для движения вниз.

Например, желаемые уровни 20, 40 и 60. Абсолютное значение гистерезиса пусть будет 5.

Тогда Levels_UP = [25, 45, 65], а Levels_DOWN = [15, 35, 55].

И никаких лишних телодвижений :)

IgorR
Offline
Зарегистрирован: 03.04.2018

Dinosaur пишет:

 

к IgorR - определить направление движения проблем нет, но дальше не вполне уловил Вашу мысль. Например целевое значение стоит 50, границы переходов 53 (выкл), 47 (скор 1), 44 (скор 2), 41 (скор 3). НО!!!! текущее значение например 37 (т.е. включаем третью скорость), а следующее прилетает например.... 52. если взять его за новую границу перехода, и от него построить гистерезис в обе стороны, то сущая ерунда получится и еще ошибка накапливаться будет. Можете свою мысль кодом проиллюстрировать?

 

Имеелось ввиду, что, для Вашего примера, значениее гистерезиса будет применено к уровню 47. Например, для гистерезиса = 2, новое значение станет 45 (сместится вниз). Остальные останутся неизменными. В общем случае - изменяется на значение гистерезиса в обратную движению сторону ближайшая пересеченная граница, остальные приводятся к "стандартному" уровню. Но это немного гиморно. См. предыдущий пост.

 

PS Как-то у Вас уж очень близко значения расположены, если это из реальных данных. Тут как-то с гистерезисом уже сложно становится :(

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Опять не ухвачу Вашу мысль - уже на листочке нарисовал цветными фломастерами. Рассмотрим уроверь 40 (пороги соответсвенно при понижении - 35, при увеличении - 45), значение переменной уменьшается до уровня 36 (не пересекаем уровень 35, работает первая скорость), значение переменной стабилизирутеся  и начинает увеличиваться (опа, вроде как все нормально, производительности хватает, но пес с ним - врубаем вторую скорость) до  37, где опять стабилизируется и идет вниз (твоюмать, опять врубаем первую скорость) к 36, ну и так по кругу. 

 

IgorR
Offline
Зарегистрирован: 03.04.2018

Постойте, а зачем врубать 2-ю передачу при значении 37? При движении вверх граница включения - 45. Вот и движемся, пока 45 не достигнем. Достигли, врубили посильнее - показания стали уменьшаться. Но, пока не достигли границы переключения на ниспадающейй ветви (а это - 35) - мотор у Вас крутится на второй скорости. Наступило 35 - бац, и вентилятор зашуршал еле-еле, пытаясь поддержать достигнутое значение. Но кто-то сильно задышал и показания поползли вверх. Достигли 45 - и все по новой. Так и будет болтаться вокруг границы 40. И это нормально. Три значения скорости - это довольно грубая регулировка. А гистерезис нужен, чтобы при достижении показаний уровня перключения в 40 единиц не происходила "болтанка" из-за того, что датчик будет в течение какого-то времени хаотично выдавать 40 +- 1 единицу, пока воздух стабильно не уйдет в какое-то из состояний.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

!!! Это не вытяжка, а увлажнитель, поэтому логика работы противоположная ))) Сегодня уже голова не варит - завтра с утра еще раз перечитаю Ваше сообщение и постараюсь осмыслить.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

IgorR пишет:
Постойте, а зачем врубать 2-ю передачу при значении 37?

Ведь как только изменится направление изменения переменной (от падения к росту), мы попадаем в левую сторону рисунка и получается должны отработать это событие? или имеете ввиду никак не отрабатывать а ждать выход за пределы верхней границы (45)? Тогда как быть с "перескоком" между диапазонами?

IgorR
Offline
Зарегистрирован: 03.04.2018

Перескок. А какая разница системе, последовательно вы проходите диапазоны или резкое изменение вызовет пропуск какого-то из них?

Ну а граница по любому будет размыта. При желаемом уровне переключения, предположим 50 единиц, в одну сторону переключение произойдет при 50 + Gist, а в другую при 50 - Gist. При этом Gist должен быть больше некого "эпсилон", характеризующего а) нестабильность показания датчика (например, он дает показания плюс-минус 0.5) б) инерционность системы (при изменении оборотов движка система отреагирует очень далеко не сразу, поэтому какое-то время показания датчика так и будут болтаться в районе 50 +/- эпсилон).
Поэтому желательно произвести ряд замеров при стационарном состоянии системы и определить максимальное значение эпсилона и Gist взять равным двум-трем эпсилонам.

IgorR
Offline
Зарегистрирован: 03.04.2018

Dinosaur пишет:

Ведь как только изменится направление изменения переменной (от падения к росту), мы попадаем в левую сторону рисунка и получается должны отработать это событие? или имеете ввиду никак не отрабатывать а ждать выход за пределы верхней границы (45)?

Изменение направления движения фиксировать при переключении скорости движка. 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Возьмем обычный гистерезис. Там переход A->B происходит если Т > T1.  А переход В->A Если т < T1-Delta . Это и есть обычный гистерезис.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Теперь задача усложнилась. Добавились переходы. Переход B->C происходит если Т>T2, а переход   C->B если Т< T2-Delta.  Да что бы перейти из А в С надо проскочить B . Так даже лучше для механизма. Плавнее переход . Вы же идете по леснице, а не прыгаете в окно , потому что так "быстрее". И да теперь в условиях перехода нужно добавлять в таком состоянии. А так скеч пишется обычным " автоматом Пуха". Это не из=за гордости, а что бы хоть название было для отличия.

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Покопайте в сторону PID

yul-i-an
yul-i-an аватар
Offline
Зарегистрирован: 10.12.2012

Еще как варийант засекать время посоледнего перехода. И запретить смену скорости пока не пройдет допустим минута от последнего переключения.

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

qwone пишет:
Возьмем обычный гистерезис. Там переход A->B происходит если Т > T1.  А переход В->A Если т < T1-Delta . Это и есть обычный гистерезис.

Да, этот этап я понимаю как работает, тут ничего сложного:

if (humCurrentAverage <= humTarget - hysteresis) {     
      Serial.println ("Humidity is ok - speed set to 1");
}
if (humCurrentAverage >= humTarget + hysteresis) {     
       Serial.println ("Humidity is high - fan is off");
}
Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

qwone пишет:
Теперь задача усложнилась. Добавились переходы. Переход B->C происходит если Т>T2, а переход   C->B если Т< T2-Delta.  Да что бы перейти из А в С надо проскочить B . Так даже лучше для механизма. Плавнее переход . Вы же идете по леснице, а не прыгаете в окно , потому что так "быстрее". И да теперь в условиях перехода нужно добавлять в таком состоянии.

ммм... как то так?

qwone пишет:
А так скеч пишется обычным " автоматом Пуха". Это не из=за гордости, а что бы хоть название было для отличия.

Не будет ли любезен многоуважаемый Джинн... :) Где можно посмотреть на этот автомат?

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Начинаем с основы 

/**/
const int T1 = 41;
const int T2 = 46;
const int T3 = 51;
const int Delta = 2;
enum state_t {speed0, speed1, speed2, speed3} state;
void stand(state_t s) {
  state = s;
  switch (state) {
    case speed0:
      break;
    case speed1:
      break;
    case speed2:
      break;
    case speed3:
      break;
  }
}
//-------------------------------------------
void setup() {
  stand(speed0);
}
void loop() {
  switch (state) {
    case speed0:
      break;
    case speed1:
      break;
    case speed2:
      break;
    case speed3:
      break;
  }
}

 

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

qwone пишет:
Начинаем с основы 

const int T1 = 41;
const int T2 = 46;
const int T3 = 51;
const int Delta = 2;

Где используются эти переменные в Вашем примере?

 

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

Dinosaur пишет:

В общем спасибо всем за подсказки, этот алгоритм оказался вполне работоспособен, для тех кто столкнется с подобной задачей, выкладываю код (вероятно задумку можно оформить изящнее, но конструкции <if> <else> мне на данном этапе освоения языка понятнее):

void speedAutoControl () {
  if (humCurrentAverage <= humTarget - 12) {   // если влажность ниже заданной на 12%
    if (fanCurrentSpeed != fanHighSpeed) {        // если вентилятор работает на скорости не HIGH
      fanControl(fanHighSpeed);                          // включаем мотор вентилятора на HIGH скорость
    }
  } else if (humCurrentAverage <= humTarget - 9)  {  // если влажность ниже заданной на 9-11%
    if (fanCurrentSpeed != fanMedSpeed && fanCurrentSpeed != fanHighSpeed) {  // если скорость мотора не MED или HIGH
      fanControl(fanHighSpeed);                                               // включаем мотор вентилятора на HIGH скорость
    }
  } else if (humCurrentAverage <= humTarget - 7)  {  // если влажность ниже заданной на 7-8%
    if (fanCurrentSpeed != fanMedSpeed) {  // если скорость мотора не MED
      fanControl(fanMedSpeed);  // включаем мотор вентилятора на MED скорость
    }
  } else if (humCurrentAverage <= humTarget - 4)  {  // если влажность ниже заданной на 4-6%
    if (fanCurrentSpeed != fanMedSpeed && fanCurrentSpeed != fanLowSpeed) {   // если скорость мотора не MED или HIGH
      fanControl(fanMedSpeed);  // включаем мотор вентилятора на MED скорость
    }
  } else if (humCurrentAverage <= humTarget - 2)  {  // если влажность ниже заданной на 2-3%
    if (fanCurrentSpeed != fanLowSpeed) {  // если скорость мотора не LOW
      fanControl(fanLowSpeed);  // включаем мотор вентилятора на LOW скорость
    }
  } else if (humCurrentAverage < humTarget + 2) { // если влажность в пределах заданной (+/- 1%)
    if (fanCurrentSpeed > fanLowSpeed) { // если скорость мотора не LOW или OFF
      fanControl(fanLowSpeed); // включаем мотор вентилятора на LOW скорость...
    }
  } else {  // если влажность выше  заданной больше чем на 3%
    if (fanCurrentSpeed != 0) {  // если вентилятор работает
      fanControl(0);  // выключаем мотор вентилятора
    }
  }
}

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Здесь

/**/
const int T1 = 51;
const int T2 = 46;
const int T3 = 51;
const int Delta = 2;
enum state_t {speed0, speed1, speed2, speed3} state;
void stand(state_t s) {
  state = s;
  switch (state) {
    case speed0:
      break;
    case speed1:
      break;
    case speed2:
      break;
    case speed3:
      break;
  }
}
//-------------------------------------------
void setup() {
  stand(speed0);
}
void loop() {
  int T = 46;/*некая функция получения T*/
  switch (state) {
    case speed0:
      if (T < T1 - Delta)/*если T<49*/
        stand(speed1);
      break;
    case speed1:
      if (T > T1)/*если T>51*/
        stand(speed0);
      if (T < T2 - Delta)/*если T<44*/
        stand(speed2);
      break;
    case speed2:
      if (T > T2)/*если T>46*/
        stand(speed1);
      if (T < T3 - Delta)/*если T<39*/
        stand(speed3);
      break;
    case speed3:
      if (T > T3)/*если T>41*/
        stand(speed2);
      break;
  }
}
/**/

 

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Ну там ясно. Как только "ступил" на чужую белую зону, то переходи к "врагу". А на граничных своих серых зонах можно "гулять сколько хочешь".

Dinosaur
Dinosaur аватар
Offline
Зарегистрирован: 01.01.2018

qwone пишет:
Ну там ясно. Как только "ступил" на чужую белую зону, то переходи к "врагу". А на граничных своих серых зонах можно "гулять сколько хочешь".

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

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

Пух, это что? О_О 

07 void stand(state_t s) {
08   state = s;
09   switch (state) {
10     case speed0:
11       break;
12     case speed1:
13       break;
14     case speed2:
15       break;
16     case speed3:
17       break;
18   }
19 }
 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

DetSimen пишет:

Пух, это что? О_О 

Автомат Пуха.