Задержка выводы показаний на индикатор

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

Здравствуйте, корифеи

Снова обращаюсь к Вашей помощи по следующей проблеме: есть измеренная HumCurrent и целевая HumTargetNew влажность. При нажатии на кнопку, значения переменной HumTargetNew изменяются с шагом 5% в диапазоне 40-80%. По нажатию кнопки значение HumTargetNew должно выводиться на дисплей допустим на 5 секунд (LedInterval), после чего на него вновь должно выводиться значение HumCurrent. В случае неоднократного нажатия кнопки, значение HumTargetNew должно выводиться не дожидаясь окончания LedInterval и таймер должен перезапуститься. Вот то что я породил, и оно закономерно не работает (HumCurrent отображается, но при нажатии на кнопку отображения HumTargetNew не происходит, вернее вероятнее всего происходит, но на очень короткий промежуток времени, поскольку иногда получается уловить мгновенную засветку сегментов):

  // опрос кнопки Humidity
  if ((SpeedNumber) == 0) {                                   // опрашиваем только в режиме AUTO
    if (debouncer3.update()) {                                // если произошло событие
      if ((debouncer3.read() == 0) && StatusHum == 0) {       // если кнопка Hum нажата и перемення StatusHum равна 0 , то ...
        HumTargetNew = HumTargetNew + 5;                      // увеличиваем значение целевой влажности на 5%
        if (HumTargetNew > 80) HumTargetNew = 40;             // диапазон допустимых значений 40-80%
        if (DEBUG == 1) {
          Serial.begin(9600);
          Serial.print("Humidity changed to: ");
          Serial.print(HumTargetNew);
          Serial.println("%.");
        }
        StatusHum = 1;
        tone(Buzzer, 3000, 100);
      }
      if (digitalRead(ButtonHum) == HIGH && StatusHum == 1) { //если кнопка Hum не нажата и переменная StatusHum равна - 1 ,то ...
        StatusHum = 0; //обнуляем переменную StatusHum
      }
    }
  }
  // конец опроса кнопки Humidity

  // отображение влажности на LED дисплее
  if (HumTargetNew != HumTargetOld) {
    HumTargetOld = HumTargetNew;
    tm1637.display(HumTargetNew);
    unsigned long LedCurrentMillis = millis(); // вот где то тут собака и порылась вероятно
    if (LedCurrentMillis - LedPreviousMillis > LedInterval) { // вот где то тут собака и порылась вероятно
      LedPreviousMillis = LedCurrentMillis;  // вот где то тут собака и порылась вероятно
    }
  }
  tm1637.display(HumCurrent); //выводим текущую влажность на дисплей
T.Rook
Offline
Зарегистрирован: 05.03.2016

Если в 26 строке показываете HumTargetNew, , то ВСЕГДА уже в  32  идет вывод HumCurrent. Где обработка вывода в зависимости от LedInterval?

kalapanga
Offline
Зарегистрирован: 23.10.2016

Опять ошибка в реализации временной задержки. То действие, которое Вы хотите выполнять через LedInterval, должно располагаться сразу перед или после 29-й строки. Т.е. должно быть так "Если прошло времени больше чем LedInterval (стр.28), тогда { запоминаем текущее состояние счетчика как предыдущее (стр.29), выполняем нужное нам действие }".

​А сейчас 28-30 строки совсем ничего полезного не делают.

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

Туплю, но не могу понять, прошу прощения. Если делаю так, то при нажатии кнопки показания HumTargetNew на дисплее меняются при нажатии кнопки, но естественно не переходят через LedInterval в режим отображения показаний HumCurrent

// отображение влажности на LED дисплее

if (HumTargetNew != HumTargetOld) {
    HumTargetOld = HumTargetNew;
    tm1637.display(HumTargetNew);
//    unsigned long LedCurrentMillis = millis();
//    if (LedCurrentMillis - LedPreviousMillis > LedInterval) {
//    LedPreviousMillis = LedCurrentMillis;  // сохраняем время последнего переключения
//  }
}
//tm1637.display(HumCurrent); //выводим текущую влажность на дисплей

 Если делаю так (я так понимаю что это имел ввиду kalapanga), то на время действия LedInterval нажатия кнопки не приводят к изменению показаний на дисплее (а надо чтобы при каждом нажатии на кнопку показания обновлялись, и таймер перезапускался). Естественно также не переходят через LedInterval в режим отображения показаний HumCurrent

// отображение влажности на LED дисплее

if (HumTargetNew != HumTargetOld) {
    HumTargetOld = HumTargetNew;
    unsigned long LedCurrentMillis = millis();
    if (LedCurrentMillis - LedPreviousMillis > LedInterval) {
    LedPreviousMillis = LedCurrentMillis;  // сохраняем время последнего переключения
    tm1637.display(HumTargetNew);
    }
}
//tm1637.display(HumCurrent); //выводим текущую влажность на дисплей

И хоть убей, не могу сообразить как организовать обработку LedInterval для возрата к показаниям HumCurrent после истечения таймера :(

 

T.Rook
Offline
Зарегистрирован: 05.03.2016

Возвращаемся к алгоритму. Примерно так:

1. Кнопа нажата? Да : устанавливаем: новое значение HumTarget, период показа.

2. Находимся в периоде показа? : Да: показываем новую HumTarget; Нет: показываем текущую HunCurrent.

 

Внести правки по своему вкусу, приправить тонкостями, исправить баги и  использвать :)

 

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

Можете на кошках показать? А то с утра воюю, мозг кипит, подозреваю что решение элементарно, но... прячется от меня где то :(

T.Rook
Offline
Зарегистрирован: 05.03.2016

Dinosaur пишет:

Можете на кошках показать? А то с утра воюю, мозг кипит, подозреваю что решение элементарно, но... прячется от меня где то :(

К сожалению, с текущего места не могу нормально вставить код. По Вашему коду из #0:

п.1: строки 3-19. Там не хватает установки периода показа (примерно как в 27 делаете)

п.2: эээ куда уж "кошатнее"?  проверяем период показа (а-ля 28 строка) и строим правильный If(типа 28)-then(26)-else(32).  24-25 - выкинуть в лоток.

 

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

Эх, все равно "буратино" моё второе имя (или даже первое - совсем деревянный). Собрал все в кучу, что имеет отношение к этому куску кода:

long CatPreviousMillis = 0;    // время, когда целевая влажность выводилась в последний раз - тренировка на кошках
unsigned long CatCurrentMillis = 0;     // тренировка на кошках
const long CatInterval = 3000; // время отображения целевой влажности на LED дисплее - тренировка на кошках
void setup() {
}
void loop() {
// опрос кнопки Humidity
  if ((SpeedNumber) == 0) {                                   // опрашиваем только в режиме AUTO
    if (debouncer3.update()) {                                // если произошло событие
      if ((debouncer3.read() == 0) && StatusHum == 0) {       // если кнопка Hum нажата и перемення StatusHum равна 0 , то ...
        HumTargetNew = HumTargetNew + 5;                      // увеличиваем значение целевой влажности на 5%
        if (HumTargetNew > 80) HumTargetNew = 40;             // диапазон допустимых значений 40-80%
        StatusHum = 1;
        tone(Buzzer, 3000, 100);
        CatCurrentMillis = millis();                          // тренировка на кошках
        if (DEBUG == 1) {
          Serial.begin(9600);
          Serial.print("Target humidity changed to: ");
          Serial.print(HumTargetNew);
          Serial.println("%.");
        }
      }
      if (digitalRead(ButtonHum) == HIGH && StatusHum == 1) { //если кнопка Hum не нажата и переменная StatusHum равна - 1 ,то ...
        StatusHum = 0; //обнуляем переменную StatusHum
      }
    }
  }
  // конец опроса кнопки Humidity

  // отображение влажности на LED дисплее

  if (CatCurrentMillis - CatPreviousMillis > LedInterval) { // тренировка на кошках
    CatPreviousMillis = CatCurrentMillis;             // тренировка на кошках
    tm1637.display(HumTargetNew);                     // выводим на дисплей заданную влажность
  } else {                                            // тренировка на кошках
    tm1637.display(HumCurrent);                           //выводим текущую влажность на дисплей
  }
}

Сейчас при старте выводит измеренную влажность HumCurrent, при нажатии кнопки все как и было - пытается отобразить целевой влажности HumTargetNew, но их тут же затирает снова HumCurrent и в течение интервала при нажатии кнопки овых попыток выводить HumTargetNew нет. 

T.Rook
Offline
Зарегистрирован: 05.03.2016

3. А какой-такой странный интервал Вы вычисляете и проверяете в 32 строке?

4. Какой смысл несет 33 строка?

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

Да, вот именно в это место я сейчас пристально смотрю и вновь курю описание millis (). Понимаю что косяк именно тут, но вот какой косяк пока не понял. :(

T.Rook
Offline
Зарегистрирован: 05.03.2016

Dinosaur пишет:

Да, вот именно в это место я сейчас пристально смотрю и вновь курю описание millis (). Понимаю что косяк именно тут, но вот какой косяк пока не понял. :(

CatCurrentMillis - время начала показа (из 15 строки #7)

соответственно 32-  должна быть проверка: текущегоMillis - времяНачалаПоказа.

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

Ну конечно же! Огромное человеческое спасибо!!!

 // отображение влажности на LED дисплее

  if (millis() - CatCurrentMillis < CatInterval) {          // Если с момента нажатия кнопки HUM не прошло заданное время...
    tm1637.display(HumTargetNew);                           // ...выводим на дисплей заданную влажность.
    } else {                                                // иначе - 
    tm1637.display(HumCurrent);                             //выводим на дисплей текущую влажность
  }
}

 

T.Rook
Offline
Зарегистрирован: 05.03.2016

Dinosaur пишет:

Ну конечно же! Огромное человеческое спасибо!!!

 // отображение влажности на LED дисплее

  if (millis() - CatCurrentMillis < CatInterval) {          // Если с момента нажатия кнопки HUM не прошло заданное время...
***** 
}

Ну в принципе да, но :

5. millis()  в условии - плохой тон. Ничего такого страшного (почти).

6. Если ничего не трогать, то где-то через 49 дней у Вас опять сработает данное условие :)) Но это уже совсем другая история.. хотя нет, это тоже об Алгоритме. Начинайте с него.

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

T.Rook пишет:
5. millis()  в условии - плохой тон. Ничего такого страшного (почти).

Ммм... Нужно создать вместо millis() еще одну переменную, присвоить ей значение millis() и уже эту переменную использовать в условии? (зачем?) или Вы соверенно другое имели ввиду?

T.Rook пишет:
6. Если ничего не трогать, то где-то через 49 дней у Вас опять сработает данное условие :)

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

T.Rook пишет:
Но это уже совсем другая история.. хотя нет, это тоже об Алгоритме. Начинайте с него.

Так вроде алгоритм и говорит - покажи и засеки время, как пройдет интервал - не показывай. Однако как и в жизни - "но есть нюанс..." да еще и не один.

T.Rook
Offline
Зарегистрирован: 05.03.2016

Dinosaur пишет:

T.Rook пишет:
5. millis()  в условии - плохой тон. Ничего такого страшного (почти).

(зачем?)

Хотя бы  затем, что результат возвращаемый  millis() по мере выполнения кода разный. Да и время выполнения millis() "лишняя" прибавка...

Dinosaur пишет:

 "но есть нюанс..." да еще и не один.

Именно :)

"реализовать без бага" - ну, к примеру, правильно переприсваивать CatCurrentMillis. Но тут уже грань - лучшего и хорошего и т.к работает и чувство прекрастного не страдает - НИЧЕГО НЕ ТРОГАЙ! :))  

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

T.Rook пишет:
Хотя бы  затем, что результат возвращаемый  millis() по мере выполнения кода разный. Да и время выполнения millis() "лишняя" прибавка...

// опрос кнопки Humidity
  if ((SpeedNumber) == 0) {                                   // опрашиваем только в режиме AUTO
    if (debouncer3.update()) {                                // если произошло событие
      if ((debouncer3.read() == 0) && StatusHum == 0) {       // если кнопка Hum нажата и перемення StatusHum равна 0 , то ...
        HumTarget = HumTarget + 5;                            // увеличиваем значение целевой влажности на 5%
        if (HumTarget > 80) HumTarget = 40;                   // диапазон допустимых значений 40-80%
        StatusHum = 1;
        tone(Buzzer, 3000, 100);
        LedChangeMillis = millis();                          // Запоминаем время время нажатия кнопки
        if (DEBUG == 1) {
          Serial.begin(9600);
          Serial.print("Target humidity changed to: ");
          Serial.print(HumTarget);
          Serial.println("%.");
        }
      }
      if (digitalRead(ButtonHum) == HIGH && StatusHum == 1) { //если кнопка Hum не нажата и переменная StatusHum равна - 1 ,то ...
        StatusHum = 0; //обнуляем переменную StatusHum
      }
    }
  }
  // конец опроса кнопки Humidity

  // отображение влажности на LED дисплее
  unsigned long LEDcurrentMillis = millis();                // запоминаем текущее время
  if (LEDcurrentMillis - LedChangeMillis < LedInterval) {   // если тренировка на кошках
    tm1637.display(HumTarget);                              // выводим на дисплей заданную влажность
    } else {                                                // тренировка на кошках
    tm1637.display(HumCurrent);                             //выводим текущую влажность на дисплей
  }

Вот так - кошерно? Этот тоже вариант работает корректно. Или "мои мысли - мои скакуны, да опять не в ту степь"? Если все же угадал - не сочтите за труд объяснить чем это лучше первоначального варианта - по мне так появилась лишняя операция назначения переменной и присвоения ей значения...

T.Rook пишет:
"реализовать без бага" - ну, к примеру, правильно переприсваивать CatCurrentMillis. Но тут уже грань - лучшего и хорошего и т.к работает и чувство прекрастного не страдает - НИЧЕГО НЕ ТРОГАЙ! :))

Трогать ничего не буду (по крайней мере сделаю бэкап проекта), но как нужно корректно присваивать? Заинтриговали...

T.Rook
Offline
Зарегистрирован: 05.03.2016

Dinosaur пишет:

...по мне так появилась лишняя операция назначения переменной и присвоения ей значения...

Ага. Где-то так. Совсем правильно было бы 25 строку в первую и тогда  в 9 и 26 используется уже закркпленное значение. Но выбор между доп. вызовом millis() или "лишней" переменной - баланс  конкретного решения. Тут именно Вам флаг в руки - выбирайте что сочтетё нужным.

Dinosaur пишет:

...как присваивать...

Комментируя предложенный пример: добавить в 29: LedChangeMillis=LedcurrentMillis+1; и тогда при ненажатой кнопе, индикаторное время всегда будет перед текущим значением millis :)) Но это лишь пример.

 

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

T.Rook пишет:
Ага. Где-то так. Совсем правильно было бы 25 строку в первую и тогда  в 9 и 26 используется уже закркпленное значение.

Прошу прощения за назойливость (и тугодумность), но не ухвачу Вашу мысль: в 25 строке я присвариваю значение переменной LedCurrentMillis, а в строке 9 я присваиваю значение переменной LedChangeMillis, каким образом перенос 25 строки в первую изменит ситуацию (ну кроме как снизит точность выполнения условия в 26 строке, ведь пока мк дойдет от 1 строки, где мы запомнили текущее время до 26 строки (где мы сравниваем время нажатия кнопки и текущее время, пройдет несколько микросекунд)? Понятно что эту разницу заметить будет невозмжно, но бытовая логика подсказывает мне что логичнее запоминать время аккурат перед тем, как мы его будем сравнивать, а не в произвольном месте кода... Где бытовая логика дала сбой?

T.Rook пишет:
Но выбор между доп. вызовом millis() или "лишней" переменной - баланс  конкретного решения. Тут именно Вам флаг в руки - выбирайте что сочтетё нужным.

Понял. Надо сразу делать хорошо - плохо само получится :) Буду стараться придерживаться традиционного подхода.

T.Rook пишет:
Комментируя предложенный пример: добавить в 29: LedChangeMillis=LedcurrentMillis+1; и тогда при ненажатой кнопе, индикаторное время всегда будет перед текущим значением millis :)) Но это лишь пример.

Уф, сейчас попытаюсь с помощью бытовой логики осознать...

Погуглил я про переполнение millis() понял то проблема не ограничится переключением индикатора на 5 секунд. Понятно что вероятность возникновения данной ситуации в моем проекте сродни вероятности встречи с динозавром на улице, но в любом деле лучше перебздеть, чем недобздеть, поэтому я попытаюсь сложить пазл в своей голове, попрошу Вас, если не затруднит, поправить если опять не туда меня занесет.

Для переменных LedChangeMillis и LedcurrentMillis задан тип unsigned long, LedInterval = 3000, допустим в 9 строке мы когда то поймали LedChangeMillis = 4.294.967.295, после перехода millis через ноль в какой то момент в строке 25 мы ловим LedcurrentMillis=100, тогда условие в 26 строке (100 - 4.294.967.295 < 3000) становится ошибочно истиным, (кстати, 100 - 4.294.967.295 это ведь ноль получится, верно?) и будет истиным капец как долго (т.е. на индикаторе перестанет отображаться текущая влажность, а будет отображаться только заданная). Тут я пока прав?

Если в 29 строку добавляем предложенные Вами LedChangeMillis=LedcurrentMillis+1 при тех же исходных данных, то положительного эффекта не вижу, т.к. по ходу выполнения цикла мы сперва споткнемся об обнуление millis, после чего на следующем цикле получим  условие (105 (допустим 105, хотя не уверен, но ведь какое то время прошло и полученное значение LedcurrentMillis будет уже другим) - 101 < 3000) - получаем ту же самую ошибочную истину, более того, мы будем получать ошибочную истину всегда, поскольку значения LedChangeMillis и LedcurrentMillis будут отличаться ненамного, и соответственно условие будет всегда ошибочно истиным. Где я ошибаюсь в рассуждениях?

 

 

T.Rook
Offline
Зарегистрирован: 05.03.2016

Ой.... хм...

Dinosaur пишет:

кстати, 100 - 4.294.967.295 это ведь ноль получится, верно

Точно не ноль :) Если инетересно - выведите в сериал  :) А потом вспомните про типы и неявное преобразование и поиграйте еще :)

 Меж прочим на форуме есть тема: "Великое переполенение millis" от ЕвгенийП (хвала  ему, уважуха и всё такое)

И вот на этом месте , я Вас оставлю на некоторое время - ибо путь получения знаний настолько увлекателен, что не буду Вам мешать :)

 
T.Rook
Offline
Зарегистрирован: 05.03.2016

Dinosaur пишет:

 но бытовая логика подсказывает мне что логичнее запоминать время аккурат перед тем, как мы его будем сравнивать, а не в произвольном месте кода... Где бытовая логика дала сбой?

Ни где. все правильно. нет смысла запоминать , если все равно два вызова. Запоминать имеет смысл ради избавления от лишних вызовов millis ( с целью уменьшения времени выполнения) и то, если это не нарушает логику выполнения.

как то много букв получается.... :(

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

Не, не дает шило в заднице покоя, накидал скетч для проверки что будет при переполнении unsigned long:

unsigned long LedPreviousMillis = 100;
unsigned long LedCurrentMillis;
const int LedInterval = 3000;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.println(" Проверка перехода millis через ноль");
}

void loop() {
  for (LedCurrentMillis = 4294967285; ; ++LedCurrentMillis) {
      Serial.print("calculation: ");
      Serial.print(LedCurrentMillis);
      Serial.print(" - ");
      Serial.print(LedPreviousMillis);
      Serial.print(" = ");
      Serial.print(LedCurrentMillis - LedPreviousMillis);
      Serial.print(" < ");
      Serial.println(LedInterval);
    if (LedCurrentMillis - LedPreviousMillis < LedInterval) {
      Serial.println(" true - target humidity");
    } else {
      Serial.println(" false - current humidity");
//     LedPreviousMillis = LedCurrentMillis + 1;
    }
    delay(2000);
  }
  
}

А вот что он выдал в порт:

---

Проверка перехода millis через ноль

calculation: 4294967294 - 100 = 4294967194 < 3000
 false - current humidity
calculation: 4294967295 - 100 = 4294967195 < 3000
 false - current humidity
calculation: 0 - 100 = 4294967196 < 3000
 false - current humidity
calculation: 1 - 100 = 4294967197 < 3000
---

Протестил с разными значениями LedPreviousMillis, "проблемы 2000" не выявил - во всех вроде бы результат для меня положительный (ну то бишь показания индикатора выводятся корректные), только не могу понять арифметику: 0 - 100 у меня никак не хочет получаться 4294967196. Можете прокомментировать?

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

T.Rook пишет:
Меж прочим на форуме есть тема: "Великое переполенение millis" от ЕвгенийП (хвала  ему, уважуха и всё такое). И вот на этом месте , я Вас оставлю на некоторое время - ибо путь получения знаний настолько увлекателен, что не буду Вам мешать :)

Большое спасибо за потраченное на меня время, действительно я уже обнаглел со своими запросами :) Пошел изучать мат.часть.

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

T.Rook пишет:
Ни где. все правильно. нет смысла запоминать , если все равно два вызова. Запоминать имеет смысл ради избавления от лишних вызовов millis ( с целью уменьшения времени выполнения) и то, если это не нарушает логику выполнения.

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

T.Rook
Offline
Зарегистрирован: 05.03.2016

Dinosaur пишет:

T.Rook пишет:
Ни где. все правильно. нет смысла запоминать , если все равно два вызова. Запоминать имеет смысл ради избавления от лишних вызовов millis ( с целью уменьшения времени выполнения) и то, если это не нарушает логику выполнения.

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

ЕСЛИ неточностью можно пренебречь. А так, да, именно так (если за "в цикле" подразумевается loop).

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

T.Rook пишет:
ЕСЛИ неточностью можно пренебречь. А так, да, именно так (если за "в цикле" подразумевается loop).

Да, конечно если можно пренебречь, и да я имел ввиду loop. Спасибо - сейчас подчищу код, а то у меня несколько таймеров в нем сидит.

bwn
Offline
Зарегистрирован: 25.08.2014

Dinosaur пишет:

Протестил с разными значениями LedPreviousMillis, "проблемы 2000" не выявил - во всех вроде бы результат для меня положительный (ну то бишь показания индикатора выводятся корректные), только не могу понять арифметику: 0 - 100 у меня никак не хочет получаться 4294967196. Можете прокомментировать?

Примерно оттуда, откуда в byte 0-1= 255 или FF или 11111111.