Заменить delay на millis()

Nikita1019
Offline
Зарегистрирован: 29.10.2019

Хочу в коде заменить delay на millis(). Не понимаю, почему не работает. Код можно разделить на 2 блока. один выполняется при помощи delay, а второй я хочу выполнить при помощи millis. Задача на LED дисплее написать, какая кнопка нажата. Проблема в том, что без задержки запись сразу пропадает. В следствии появляется задержка в 1 секунду. 

Мое понятия написанного мной кода в том, что как только срабатывает положительное значение, то на дисплей выводится надпись. Она там будет написана, пока не сотрется. Стирание происходит при помощи millis. Я создал интервал времени в 1 секунда, далее millis приравнивается к значению к прошлому значению. Если интервал закончился, то выполняется стирание надписи. блок заканчивается. начинается новый цикл.

 

void loop ()
if (val < 20) // если значение val меньше 20, то выполняется задача
  {
    lcd.setCursor (5,1); // отображается на дисплее на второй линии на 6 символе начинается моя надпись
    lcd.print ("Right"); // отображается информация
  if (millis() - last_time > 1000) //интервал в 1 секунду
   last_time = millis(); // следствия условия
    lcd.clear(); //следствия условия
   }
  
if (val > 120 && val < 150) 
  {
  lcd.setCursor (7,1);
  lcd.print("UP"); 
  delay (1000);
  lcd.clear ();
  }

 

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

После какого события нужно начинать отсчитывать миллисекунды?

b707
Offline
Зарегистрирован: 26.05.2017

Приведите скетч целиком

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

sadman41, давайте, наконец требовать выкладывания полного кода! Иначе опять будет как в соседней теме - все бедного новичка обижают.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

Строка 6 и 7 надо:

if (millis() - last_time >= 1000){ //интервал в 1 секунду 
last_time +=1000; } // следствия условия

А то у вас получается - сколько будет дважды два - сорок учитель, нет 7-8 где-то так, но не сорок жеж )))

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

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

sadman41, давайте, наконец требовать выкладывания полного кода! Иначе опять будет как в соседней теме - все бедного новичка обижают.


Слушаю и повинуюсь.

Nikita1019
Offline
Зарегистрирован: 29.10.2019

sadman41 пишет:

После какого события нужно начинать отсчитывать миллисекунды?

отсчет миллисекунд должен начаться после нажатия на кнопку. Соответсвенно после того, как условие  if (val < 20)  стало верным. 11 строчка

Nikita1019
Offline
Зарегистрирован: 29.10.2019

я выложил почти полный код.

#include <LiquidCrystal.h> // include the library code:
LiquidCrystal lcd (12,11,5,4,3,2); // initialize the library with the numbers of the interface pins
int val = 0; 
unsigned long last_time;
void setup ()
{}

void loop ()
if (val < 20) // если значение val меньше 20, то выполняется задача
  {
    lcd.setCursor (5,1); // отображается на дисплее на второй линии на 6 символе начинается моя надпись
    lcd.print ("Right"); // отображается информация
  if (millis() - last_time > 1000) //интервал в 1 секунду
   last_time = millis(); // следствия условия
    lcd.clear(); //следствия условия
   }
  
if (val > 120 && val < 150) 
  {
  lcd.setCursor (7,1);
  lcd.print("UP"); 
  delay (1000);
  lcd.clear ();
  }

 

Nikita1019
Offline
Зарегистрирован: 29.10.2019

ua6em пишет:

Строка 6 и 7 надо:

if (millis() - last_time >= 1000){ //интервал в 1 секунду 
last_time +=1000; } // следствия условия

А то у вас получается - сколько будет дважды два - сорок учитель, нет 7-8 где-то так, но не сорок жеж )))

Я понимаю строку 6 и 7 так. 

 if (millis() - last_time > 1000) //интервал в 1 секунду
   last_time = millis(); // следствия условия

Начинает функция millis, идет отсчет. Изначально last_time = 0. как только millis() - last time стал больше 1000. 

last_time = millis(). Это значит, что last_time стал равен 1000. и дальше millis() - 1000, где millis уже 1001. По сути строка ничем не отличается от Вашей. 

При условии, что я правильно все понимаю.

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

Nikita1019 пишет:

я выложил почти полный код.

Ну, а Вам почти помогли разобраться.

Nikita1019 пишет:

отсчет миллисекунд должен начаться после нажатия на кнопку. Соответсвенно после того, как условие  if (val < 20)  стало верным. 11 строчка

Ваша val, в строке №3 получает значение 0 и больше нигде и никогда не изменяется. Она всегда < 20. Это нормально? Так и задумывалось?

Nikita1019
Offline
Зарегистрирован: 29.10.2019

Извините, пожалуйста, в коде пропущена одна самая важная строчка. Добавил строку 10. значение val считывается при помощи аналогового входа. Исходя из этих данных я могу определить, какая кнопка нажата и какую информацию мне нужно выводить на дисплей. Далее я пишу информацию на дисплее, которая сама по себе не стирается. Остается написанной пока не будет дана команда стереть или будет дана команда написать поверх этой надписи. 

Соотвественно, проблема состоит в том, что бы стереть надпись через интервал времени 1 секунду. При помощи delay у меня нет никаких проблем, но это в корне не правильно и я написал альтернативу при помощи millis().

#include <LiquidCrystal.h> // include the library code:
LiquidCrystal lcd (12,11,5,4,3,2); // initialize the library with the numbers of the interface pins
int val = 0; 
unsigned long last_time;
void setup ()
{}

void loop ()
{
val = analogRead(14);
if (val < 20) // если значение val меньше 20, то выполняется задача
  {
    lcd.setCursor (5,1); // отображается на дисплее на второй линии на 6 символе начинается моя надпись
    lcd.print ("Right"); // отображается информация
  if (millis() - last_time > 1000) //интервал в 1 секунду
   last_time = millis(); // следствия условия
    lcd.clear(); //следствия условия
   }
  
if (val > 120 && val < 150) 
  {
  lcd.setCursor (7,1);
  lcd.print("UP"); 
  delay (1000);
  lcd.clear ();
  }
}

 

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

ардуина мега?

по видимому оно стирается и тут же выводится вновь, так написано по крайней мере

а так?
 

#include <LiquidCrystal.h> // include the library code:
LiquidCrystal lcd (12,11,5,4,3,2); // initialize the library with the numbers of the interface pins
int val = 0; 
unsigned long last_time;
byte myTimer = 0;

void setup ()
{}

void loop ()
{
val = analogRead(14);
if (val < 20) // если значение val меньше 20, то выполняется задача
  { if(myTimer == 0){
    last_time = millis(); 
    myTimer = 1;
                    }
    lcd.setCursor (5,1); // отображается на дисплее на второй линии на 6 символе начинается моя надпись
    lcd.print ("Right"); // отображается информация
  if (millis() - last_time >= 1000) {//интервал в 1 секунду
    last_time +=1000; // следствия условия
    lcd.clear(); //следствия условия
    myTimer = 0;
      }
   }
  
if (val > 120 && val < 150) 
  {
  lcd.setCursor (7,1);
  lcd.print("UP"); 
  delay (1000);
  lcd.clear ();
  }
}

 

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

Nikita1019 пишет:

sadman41 пишет:

После какого события нужно начинать отсчитывать миллисекунды?

отсчет миллисекунд должен начаться после нажатия на кнопку. Соответсвенно после того, как условие  if (val < 20)  стало верным. 11 строчка

Окей, тогда почему после того, как условие стало верным, в переменную last_time не помещается кол-во мс? 

Вам нужно для начала сделать для двух кнопок следующее: определили, что кнопка нажата, запомнили в какой момент была нажата, вывели что-то на экран. Лишнее пока убрать. Пусть временно будут наслоения.

Nikita1019
Offline
Зарегистрирован: 29.10.2019

sadman41 пишет:

Окей, тогда почему после того, как условие стало верным, в переменную last_time не помещается кол-во мс? 

Вам нужно для начала сделать для двух кнопок следующее: определили, что кнопка нажата, запомнили в какой момент была нажата, вывели что-то на экран. Лишнее пока убрать. Пусть временно будут наслоения.

Верно. Я сейчас осознал 1 ошибку. нажатие на кнопку может быть в любой момент и у меня не было точки отсчета. Я исправил.

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

Исправленный код. Я фиксирую момент нажатия кнопки.

// Это весь код
#include <LiquidCrystal.h> // include the library code:
LiquidCrystal lcd (12,11,5,4,3,2); // initialize the library with the numbers of the interface pins
int val = 0; 
unsigned long last_time;
void setup ()
{}

void loop ()
{
val = analogRead(14);
if (val < 20) // если значение val меньше 20, то выполняется задача
  {
  last_time = millis();  
  lcd.setCursor (5,1); // отображается на дисплее на второй линии на 6 символе начинается моя надпись
  lcd.print ("Right"); // отображается информация
  
   }

В таком варианте у меня надпись появляется на дисплее сразу после нажатия.  Надпись все время отображается. Можно только поверх нее что-то написать.

Далее я добавляю условие millis(). По истечению 1 секунды дисплей очищается. 

void loop ()
{
val = analogRead(14);
if (val < 20) // если значение val меньше 20, то выполняется задача
  {
  last_time = millis();  // фиксирую время, в которое была нажата кнопка
  lcd.setCursor (5,1); // отображается на дисплее моя надпись
  lcd.print ("Right"); // отображается информация
  if (millis() - last_time >= 1000) //интервал в 1 секунду
  lcd.clear(); //следствия условия
   }

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

Я для проверки использую симулятор Arduino UNO на сайте Tinkercad.com. Может с этим можем быть проблема. Хотя лампочками моргать при помощи millis() там получилось.

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

Я вам не зря про две кнопки написал. Это ключевой момент для понимания.

MaksVV
Offline
Зарегистрирован: 06.08.2015

вот такой таймер постоянно раз в интервал делает выражение в фигурных скобках: 

if (millis() - last_time > interval) 
      {
        // тут бла бла
        last_time = millis();    
      }

а вот такой делает один раз, спустя интервал после того момента, как  вы его запускаете 

static bool timer = 0;
if (timer && millis() - last_time > interval) 
      {
        // тут бла бла
        timer = 0;    
      }

запускаем таймер так

timer = 1; last_time = millis();

Соответственно вам нужен второй вариант. Запускать таймер после определения, что кнопка нажата. В фигурных скобках таймера сделать очистку экрана.

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

Макс, ты только полностью не вываливай код. Человек же понять должен, а не списать бездумно.

MaksVV
Offline
Зарегистрирован: 06.08.2015

ок, не буду. Пусть мозг тренируется, хотя иногда по комментам лучше понять можно. 

Nikita1019
Offline
Зарегистрирован: 29.10.2019

MaksVV пишет:

вот такой таймер постоянно раз в интервал делает выражение в фигурных скобках: 

if (millis() - last_time > interval) 
      {
        // тут бла бла
        last_time = millis();    
      }

а вот такой делает один раз, спустя интервал после того момента, как  вы его запускаете 

static bool timer = 0;
if (timer && millis() - last_time > interval) 
      {
        // тут бла бла
        timer = 0;    
      }

запускаем таймер так

timer = 1; last_time = millis();

Соответственно вам нужен второй вариант. Запускать таймер после определения, что кнопка нажата. В фигурных скобках таймера сделать очистку экрана.

Может я что-то не правильно понимаю или упускаю что-то, но в моём случае условие millis() выполняется всего 1 раз, потому что условие millis() вызывается оператором IF, что кнопка нажата. Если кнопка не нажата, то условие millis() никогда не будет выполняться. Это следует из строки if (val > 100). Дальше фигурные скобки, которые при TRUE должны выполниться и при FALSE пропустить или выполнить ELSE. 

if (val > 100) 
{
if (millis() - last_time > interval) 
      {
        // тут бла бла
        last_time = millis();    
      }
}

  Это я не к тому, что бы ничего не делать, а просто хочу разобраться. У меня теперь появилось понимание, как сделать цикл с повторением в 1 раз.

ua6em
ua6em аватар
Offline
Зарегистрирован: 17.08.2016

а при не нажатой кнопке какое значение val?

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

Nikita1019 пишет:
У меня теперь появилось понимание, как сделать цикл с повторением в 1 раз.
А не появилось понимание "зачем"? :)

MaksVV
Offline
Зарегистрирован: 06.08.2015

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

Nikita1019
Offline
Зарегистрирован: 29.10.2019

Спасибо за помощь. Осознал, что оператор if millis исполняется только в тот момент пока нажата кнопка. Получился такой рабочий код. Возможно там есть ошибки, которые стоит исправить.

#include <LiquidCrystal.h> // include the library code:
LiquidCrystal lcd (12,11,5,4,3,2); // initialize the library with the numbers of the interface pins
int val = 0; 
unsigned long lastMillis;
boolean flag;
void setup() {
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.print("Button Test"); // Print a message to the LCD.
}

void loop() 
{
val = analogRead(14);
if (val < 20 && flag == 0) 
  {
    lcd.setCursor (5,1);
    lcd.print ("Right");
    lastMillis = millis();
    flag = 1;
    };

    
if ((flag == 1) && (millis() - lastMillis >= 1000))
    {
    lcd.clear();
    flag = 0;
    }
  
if (val > 120 && val < 150) 
  {
  lcd.setCursor (7,1);
  lcd.print("UP"); // print analog intput A0 value
  delay (1000);
  lcd.clear ();
  }

}

 

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

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

Ну, и для скорости, clear() делать не стоит. Лучше запечатать строку пробелами. На физическом LCD разница будет видна невооруженным глазом.

Nikita1019
Offline
Зарегистрирован: 29.10.2019

sadman41 пишет:

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

Ну, и для скорости, clear() делать не стоит. Лучше запечатать строку пробелами. На физическом LCD разница будет видна невооруженным глазом.

Допустим мне надо было бы стирать надпись А через 1 секнкдк, а надпись Б через 2. В таком случае я бы поменял условия вызова таймера. В 1 секунде вызывал бы flag = 1, в 2 секундах timer = 1 , это правильное решение задачи или можно лучше?

MaksVV
Offline
Зарегистрирован: 06.08.2015

вместо цифры 1000 введите переменную. И в ифе нажатия каждой кнопки впишите какой таймаут вам нужен и нафига delay то остался в строке 33 ?

Nikita1019
Offline
Зарегистрирован: 29.10.2019

В данном варианте обе надписи удаляются через 1 секунду. Возможно я допустил ошибку в знаках. Выходит так, как говорил Sadman, поэтому я уточнил. Что бы у надписей были разные времена, мне тогда надо использовать дополнительный запуск другого "таймера". Соответственно, правильное ли это.

#include <LiquidCrystal.h> // include the library code:
LiquidCrystal lcd (12,11,5,4,3,2); // initialize the library with the numbers of the interface pins
int val = 0; 
unsigned long lastMillis;
boolean flag;
void setup() {
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.print("Button Test"); // Print a message to the LCD.
}

void loop() 
{
val = analogRead(14);
if (val < 20 && flag == 0) 
{ 
    lcd.setCursor (5,1);
    lcd.print ("Right");
    lastMillis = millis();
    flag = 1;
    };

    
if ((flag == 1) && (millis() - lastMillis >= 1000))
    {
    lcd.begin(16,2);
  	lcd.print (" ");
    flag = 0;
}
  
if (val > 120 && val < 150) 
  {
  lcd.setCursor (7,1);
  lcd.print("UP"); // print analog intput A0 value
  flag = 1;
  lastMillis = millis();
  }

}

 

MaksVV
Offline
Зарегистрирован: 06.08.2015

А где :

MaksVV пишет:

вместо цифры 1000 введите переменную. И в ифе нажатия каждой кнопки впишите какой таймаут вам нужен


?

Nikita1019
Offline
Зарегистрирован: 29.10.2019

MaksVV пишет:

вместо цифры 1000 введите переменную. И в ифе нажатия каждой кнопки впишите какой таймаут вам нужен

Допустим мне надо было бы стирать надпись А через 1 секунду, а надпись Б через 2. В таком случае я бы поменял условия вызова 2 таймеров. В 1 секунде вызывал бы flag = 1, который отвечал бы за таймер в одну секунду. В 2 секундах timer = 1, который отвечал бы за 2 секунды. Это правильное регулирования таймаута, который мне нужен или можно лучше?

Пример.

#include <LiquidCrystal.h> // include the library code:
LiquidCrystal lcd (12,11,5,4,3,2); // initialize the library with the numbers of the interface pins
int val = 0; 
unsigned long lastMillis;
boolean flag, timer;
void setup() {
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.print("Button Test"); // Print a message to the LCD.
}

void loop() 
{
val = analogRead(14);
if (val < 20 && flag == 0) 
  {
    lcd.setCursor (5,1);
    lcd.print ("Right");
    lastMillis = millis();
    flag = 1;
    };

    
if ((flag == 1) && (millis() - lastMillis >= 4000))
    {
    lcd.clear();
    flag = 0;
    }
  
if (val > 120 && val < 150) 
  {
  lcd.setCursor (7,1);
  lcd.print("UP"); // print analog intput A0 value
  lastMillis = millis();
    timer = 1;
  }
if ((timer == 1) && (millis() - lastMillis >= 1000))
    {
    lcd.clear();
    timer = 0;
    }
}

Второй вариант, который я смог придумать, задумавшись над вашей идеей такой.

#include <LiquidCrystal.h> // include the library code:
LiquidCrystal lcd (12,11,5,4,3,2); // initialize the library with the numbers of the interface pins
int val = 0; 
int a = 0;
unsigned long lastMillis;
boolean flag, timer;
void setup() {
lcd.begin(16, 2); // set up the LCD's number of columns and rows:
lcd.print("Button Test"); // Print a message to the LCD.
}

void loop() 
{
val = analogRead(14);
if (val < 20 && flag == 0) 
  {
    lcd.setCursor (5,1);
    lcd.print ("Right");
    lastMillis = millis();
    flag = 1;
  a = 1000;
    };

    
if ((flag == 1) && (millis() - lastMillis >= a))
    {
    lcd.clear();
    flag = 0;
    }
  
if (val > 120 && val < 150) 
  {
  lcd.setCursor (7,1);
  lcd.print("UP"); // print analog intput A0 value
  lastMillis = millis();
    flag = 1;
  a = 2000;
  }

}

 

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

Естественно - #2.

Только переменная 'а' должна быть того же типа, что и остальные, в которых миллисекунды хранятся.

Nikita1019
Offline
Зарегистрирован: 29.10.2019

Огромное спасибо за оказанную помощь и наставления Sadman41 и MaksVV. Премного благодарен за уделенное теме время. Еще раз спасибо.