Задержка выводы показаний на индикатор
- Войдите на сайт для отправки комментариев
Здравствуйте, корифеи
Снова обращаюсь к Вашей помощи по следующей проблеме: есть измеренная 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); //выводим текущую влажность на дисплей
Если в 26 строке показываете HumTargetNew, , то ВСЕГДА уже в 32 идет вывод HumCurrent. Где обработка вывода в зависимости от LedInterval?
Опять ошибка в реализации временной задержки. То действие, которое Вы хотите выполнять через LedInterval, должно располагаться сразу перед или после 29-й строки. Т.е. должно быть так "Если прошло времени больше чем LedInterval (стр.28), тогда { запоминаем текущее состояние счетчика как предыдущее (стр.29), выполняем нужное нам действие }".
А сейчас 28-30 строки совсем ничего полезного не делают.
Туплю, но не могу понять, прошу прощения. Если делаю так, то при нажатии кнопки показания 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 после истечения таймера :(
Возвращаемся к алгоритму. Примерно так:
1. Кнопа нажата? Да : устанавливаем: новое значение HumTarget, период показа.
2. Находимся в периоде показа? : Да: показываем новую HumTarget; Нет: показываем текущую HunCurrent.
Внести правки по своему вкусу, приправить тонкостями, исправить баги и использвать :)
Можете на кошках показать? А то с утра воюю, мозг кипит, подозреваю что решение элементарно, но... прячется от меня где то :(
Можете на кошках показать? А то с утра воюю, мозг кипит, подозреваю что решение элементарно, но... прячется от меня где то :(
К сожалению, с текущего места не могу нормально вставить код. По Вашему коду из #0:
п.1: строки 3-19. Там не хватает установки периода показа (примерно как в 27 делаете)
п.2: эээ куда уж "кошатнее"? проверяем период показа (а-ля 28 строка) и строим правильный If(типа 28)-then(26)-else(32). 24-25 - выкинуть в лоток.
Эх, все равно "буратино" моё второе имя (или даже первое - совсем деревянный). Собрал все в кучу, что имеет отношение к этому куску кода:
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 нет.
3. А какой-такой странный интервал Вы вычисляете и проверяете в 32 строке?
4. Какой смысл несет 33 строка?
Да, вот именно в это место я сейчас пристально смотрю и вновь курю описание millis (). Понимаю что косяк именно тут, но вот какой косяк пока не понял. :(
Да, вот именно в это место я сейчас пристально смотрю и вновь курю описание millis (). Понимаю что косяк именно тут, но вот какой косяк пока не понял. :(
CatCurrentMillis - время начала показа (из 15 строки #7)
соответственно 32- должна быть проверка: текущегоMillis - времяНачалаПоказа.
Ну конечно же! Огромное человеческое спасибо!!!
// отображение влажности на LED дисплее if (millis() - CatCurrentMillis < CatInterval) { // Если с момента нажатия кнопки HUM не прошло заданное время... tm1637.display(HumTargetNew); // ...выводим на дисплей заданную влажность. } else { // иначе - tm1637.display(HumCurrent); //выводим на дисплей текущую влажностьНу конечно же! Огромное человеческое спасибо!!!
// отображение влажности на LED дисплее if (millis() - CatCurrentMillis < CatInterval) { // Если с момента нажатия кнопки HUM не прошло заданное время... ***** }Ну в принципе да, но :
5. millis() в условии - плохой тон. Ничего такого страшного (почти).
6. Если ничего не трогать, то где-то через 49 дней у Вас опять сработает данное условие :)) Но это уже совсем другая история.. хотя нет, это тоже об Алгоритме. Начинайте с него.
Ммм... Нужно создать вместо millis() еще одну переменную, присвоить ей значение millis() и уже эту переменную использовать в условии? (зачем?) или Вы соверенно другое имели ввиду?
Это как раз тот случай, когда раньше сдохнет или султан, или ишак (увлажнитель раз в неделю отключается от сети и волокется на помывку, иначе в нем "лягушки" заводятся). В принципе даже если (вдруг) раз в 49 дней на 3 секунды на экране внезапно отобразятся показания заданной влажности и я смогу это увидеть - мое чувство прекрасного это переживет ))) Но хотелось бы узнать - а как это можно реализовать без данного бага?
Так вроде алгоритм и говорит - покажи и засеки время, как пройдет интервал - не показывай. Однако как и в жизни - "но есть нюанс..." да еще и не один.
(зачем?)
"но есть нюанс..." да еще и не один.
"реализовать без бага" - ну, к примеру, правильно переприсваивать CatCurrentMillis. Но тут уже грань - лучшего и хорошего и т.к работает и чувство прекрастного не страдает - НИЧЕГО НЕ ТРОГАЙ! :))
// опрос кнопки 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); //выводим текущую влажность на дисплей }Вот так - кошерно? Этот тоже вариант работает корректно. Или "мои мысли - мои скакуны, да опять не в ту степь"? Если все же угадал - не сочтите за труд объяснить чем это лучше первоначального варианта - по мне так появилась лишняя операция назначения переменной и присвоения ей значения...
Трогать ничего не буду (по крайней мере сделаю бэкап проекта), но как нужно корректно присваивать? Заинтриговали...
...по мне так появилась лишняя операция назначения переменной и присвоения ей значения...
Ага. Где-то так. Совсем правильно было бы 25 строку в первую и тогда в 9 и 26 используется уже закркпленное значение. Но выбор между доп. вызовом millis() или "лишней" переменной - баланс конкретного решения. Тут именно Вам флаг в руки - выбирайте что сочтетё нужным.
...как присваивать...
Комментируя предложенный пример: добавить в 29: LedChangeMillis=LedcurrentMillis+1; и тогда при ненажатой кнопе, индикаторное время всегда будет перед текущим значением millis :)) Но это лишь пример.
Прошу прощения за назойливость (и тугодумность), но не ухвачу Вашу мысль: в 25 строке я присвариваю значение переменной LedCurrentMillis, а в строке 9 я присваиваю значение переменной LedChangeMillis, каким образом перенос 25 строки в первую изменит ситуацию (ну кроме как снизит точность выполнения условия в 26 строке, ведь пока мк дойдет от 1 строки, где мы запомнили текущее время до 26 строки (где мы сравниваем время нажатия кнопки и текущее время, пройдет несколько микросекунд)? Понятно что эту разницу заметить будет невозмжно, но бытовая логика подсказывает мне что логичнее запоминать время аккурат перед тем, как мы его будем сравнивать, а не в произвольном месте кода... Где бытовая логика дала сбой?
Понял. Надо сразу делать хорошо - плохо само получится :) Буду стараться придерживаться традиционного подхода.
Уф, сейчас попытаюсь с помощью бытовой логики осознать...
Погуглил я про переполнение 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 будут отличаться ненамного, и соответственно условие будет всегда ошибочно истиным. Где я ошибаюсь в рассуждениях?
Ой.... хм...
кстати, 100 - 4.294.967.295 это ведь ноль получится, верно
Точно не ноль :) Если инетересно - выведите в сериал :) А потом вспомните про типы и неявное преобразование и поиграйте еще :)
Меж прочим на форуме есть тема: "Великое переполенение millis" от ЕвгенийП (хвала ему, уважуха и всё такое)
И вот на этом месте , я Вас оставлю на некоторое время - ибо путь получения знаний настолько увлекателен, что не буду Вам мешать :)
но бытовая логика подсказывает мне что логичнее запоминать время аккурат перед тем, как мы его будем сравнивать, а не в произвольном месте кода... Где бытовая логика дала сбой?
Ни где. все правильно. нет смысла запоминать , если все равно два вызова. Запоминать имеет смысл ради избавления от лишних вызовов millis ( с целью уменьшения времени выполнения) и то, если это не нарушает логику выполнения.
как то много букв получается.... :(
Не, не дает шило в заднице покоя, накидал скетч для проверки что будет при переполнении 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 через ноль
Протестил с разными значениями LedPreviousMillis, "проблемы 2000" не выявил - во всех вроде бы результат для меня положительный (ну то бишь показания индикатора выводятся корректные), только не могу понять арифметику: 0 - 100 у меня никак не хочет получаться 4294967196. Можете прокомментировать?
Большое спасибо за потраченное на меня время, действительно я уже обнаглел со своими запросами :) Пошел изучать мат.часть.
То есть идея в том, что имеет смысл в цикле считать состояние millis() один раз и запомнить его (если оно нужно более чем в одном месте) и затем обращаться к этой переменной по мере необходимости, а неточностью можно пренебречь ввиду исчезающе малых значений?
То есть идея в том, что имеет смысл в цикле считать состояние millis() один раз и запомнить его (если оно нужно более чем в одном месте) и затем обращаться к этой переменной по мере необходимости, а неточностью можно пренебречь ввиду исчезающе малых значений?
ЕСЛИ неточностью можно пренебречь. А так, да, именно так (если за "в цикле" подразумевается loop).
Да, конечно если можно пренебречь, и да я имел ввиду loop. Спасибо - сейчас подчищу код, а то у меня несколько таймеров в нем сидит.
Протестил с разными значениями LedPreviousMillis, "проблемы 2000" не выявил - во всех вроде бы результат для меня положительный (ну то бишь показания индикатора выводятся корректные), только не могу понять арифметику: 0 - 100 у меня никак не хочет получаться 4294967196. Можете прокомментировать?
Примерно оттуда, откуда в byte 0-1= 255 или FF или 11111111.