Выполнить действие однократно в loop

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
Парни, подскажите - если вставить выдачу сериал сообщения в условие кнопки, например if (ringbut.isPress()) Serial.println("RING"); , то сообщение выдается 1 раз , но если кнопкой менять режим if (ringbut.isPress()) MODE = 1; а выдачу сообщения привязать к режиму if (MODE == 1) Serial.println("RING"); , то сообщение идет не однократно, а строчит сплошным потоком, соответственно, вопрос - как сделать чтобы при режиме MODE == 1 сообщение выдало однократно, а не строчило?

C++:
#include "GyverButton.h"

#define RING_BUT 2
GButton ringbut(RING_BUT);

uint8_t MODE = 0;

void setup() {
  Serial.begin (9600);
}
void loop() {
  ringbut.tick();

if (ringbut.isPress()) MODE = 1;

if (ringbut.isRelease())   MODE = 0;

if (MODE == 1) Serial.println("RING");

}
Пробовал уже и добавлять boolean true false , выносил за пределы лупа - тоже строчит.
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
переместить вывод в условие проверки кнопки
А если мне это не удобно и надо привязать именно к режиму? Код будет другой, этот максимально упрощен для примера, все лишнее выкинул, но у меня там будут условия с задержками millis, поэтому просто необходимо привязать выдачу сообщения к режиму, вот допустим мне надо будет сделать такое условие

if (MODE == 1 && (millis() - timer) > 2000){
Serial.println("RING");
}

так оно через 2 секунды начнет строчить, а нужно однократно.
 

poty

★★★★★★✩
19 Фев 2020
3,228
939
@martinways, Вы получаете ровно то, как формулируете. "режим" - это длящийся процесс, пока он есть - будет осуществляться вывод. Вы же подразумеваете совсем другое: вывести сообщение при смене режима. Поэтому и совет Вам дан правильный. Неважно как изменяется режим: кнопкой или ещё как - в момент смены - печатаете сообщение.
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
@martinways, Вы получаете ровно то, как формулируете. "режим" - это длящийся процесс, пока он есть - будет осуществляться вывод. Вы же подразумеваете совсем другое: вывести сообщение при смене режима. Поэтому и совет Вам дан правильный. Неважно как изменяется режим: кнопкой или ещё как - в момент смены - печатаете сообщение.
Ну хорошо, а как же тогда сделать условие - если прошло 2 секунды с момента активации MODE=1, то выдать однократно сообщение? Если в лупе создать такое условие, то все равно начинает строчить через 2 сек
if ((millis() - timer) > 2000){
Serial.println("RING");
}

А если вставить это условие прямо в кнопку, то оно не может выполнится на момент нажатия кнопки

if (ringbut.isPress()) {
MODE = 1;
timer = millis();
if ((millis() - timer) > 2000){
Serial.println("RING");
}
}
 
Изменено:

IamNikolay

★★★✩✩✩✩
15 Янв 2020
820
175
Ну хорошо, а как же тогда сделать условие - если прошло 2 секунды с момента активации MODE=1, то выдать однократно сообщение? Если в лупе создать такое условие, то все равно начинает строчить через 2 сек
так это уже дополнительные условия, естественно по другому решается.
без полной формулировки нужной задачи и/или кода угадать что нужно навряд ли получится.
к примеру, этот код выведет однократно через 2 секунды после смены режима, но остановит выполнение всего кода на эти самые 2 секунды.
C++:
if (ringbut.isPress()) {
    MODE = 1;
    delay(2000);
    Serial.println("RING");
}
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
так это уже дополнительные условия, естественно по другому решается.
без полной формулировки нужной задачи и/или кода угадать что нужно навряд ли получится.
к примеру, этот код выведет однократно через 2 секунды после смены режима, но остановит выполнение всего кода на эти самые 2 секунды.
C++:
if (ringbut.isPress()) {
    MODE = 1;
    delay(2000);
    Serial.println("RING");
}
Да, я знаю, что так можно, но мне надо без delay, так как во время этих двух секунд надо чтобы была возможность выполнения другого кода, например той же смены режима на MODE=0. Да и вообще мне надо привязать выдачу сообщения именно к режиму MODE=1, то есть если сработал режим MODE=1 и прошло 2 секунды, то выдать сообщение, а если во время этих 2 секунд сработал другой режим, то не выдавать.
 

poty

★★★★★★✩
19 Фев 2020
3,228
939
Постановка задачи снова неполная. В общем случае это делается с помощью "флага" - бинарной переменной. Вот Вы запоминаете момент, когда произошёл переход на режим 1:
timer = millis();
в этом же месте нужно присвоить флагу (допустим, toMode1) значение true. И условие
if ((millis() - timer) > 2000){
Serial.println("RING");
}
превратится в
if ( ((millis() - timer) > 2000) && toMode1){
Serial.println("RING");
}

Если происходит что-то, что должно отменять вывод, в этом "что-то" сбрасываете флаг в false.
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
Если происходит что-то, что должно отменять вывод, в этом "что-то" сбрасываете флаг в false.
Я выше писал, что это делал через boolean, но все равно строчит, вот посмотрите может я как-то неправильно переменную задал

C++:
#include "GyverButton.h"

#define RING_BUT 2
GButton ringbut(RING_BUT);

uint8_t MODE = 0;
boolean toMode1 = false;
unsigned long timer;

void setup() {
  Serial.begin (9600);
}
void loop() {
  ringbut.tick();

if (ringbut.isPress()) {
  MODE = 1;
  timer = millis();
  toMode1=true;
}
if ( ((millis() - timer) > 2000) && toMode1){
Serial.println("RING");
}

if (ringbut.isRelease()) {
   MODE = 0;
   toMode1=false;
}
}
 

poty

★★★★★★✩
19 Фев 2020
3,228
939
После вывода сообщения нужно сбросить флаг в false.
 

Старик Похабыч

★★★★★★★
14 Авг 2019
4,262
1,300
Москва
Несложную задачу усложнили до миллисов и кучи флагов. А poty верно все написал.
Однако есть НО
Просто так выводить в монитор смысла нет. Общий смысл затеи ускользает...
 

IamNikolay

★★★✩✩✩✩
15 Янв 2020
820
175
так должно быть
C++:
if ( ((millis() - timer) > 2000) && toMode1){
Serial.println("RING");
toMode1=false;
}
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
так должно быть
C++:
if ( ((millis() - timer) > 2000) && toMode1){
Serial.println("RING");
toMode1=false;
}
Во, а теперь сработало! Вот в чем был фокус, спасибо, дружище!

Несложную задачу усложнили до миллисов и кучи флагов. А poty верно все написал.
Однако есть НО
Просто так выводить в монитор смысла нет. Общий смысл затеи ускользает...
Приветствую, друг! Я понимаю, что выдача в сериал это так скучно для вас тут))) но я такую задачу специально поставил, чтобы сконцентрироваться именно на моменте, который у меня не получается, но благо уже общими усилиями я нашел решение. В общих чертах - это будет устройство, которое реагирует на некоторые события и управляет реле и выдает звуковые сообщения через DFPlayer. Из-за того, что я не мог добиться однократной выдачи команды запуска трека DFplayer не "играл" при смене режима, так как на него поступал поток команд mp3_Play (1);
Кстати, до сих пор очень благодарен Вам, что я понял как работают флаги, режимы, а то чет раньше никак не мог вкурить когда мне на форумах писали - "примени флаг", я думал че за флаг и че с ним делать. Времени на изучение основ C++ нет, но при помощи форума уже узнал много чего, что помогло мне создать несколько очень полезных для меня устройств, для 3д принтера, сигнализации и др.
 

martinways

✩✩✩✩✩✩✩
23 Дек 2020
25
0
А почему вариант типа
C++:
if (ringbut.isPress()) {
  MODE = 1;
Serial.println("RING");
}
не устраивает ?
Потому что, мне надо выдать сообщение только если поменялся режим на MODE=1 и прошло 2 секунды с момента активации этого режима, а если во время этих 2 секунд режим поменялся на MODE=0 то сообщение не выдавать. В моем устройстве вместо сериал сообщения будет команда mp3_Play (1); (включить трек 1) , а если сработала другая кнопка в течении этих 2 секунд и режим поменялся на MODE=0, то трек не включать. Но это только один момент из всей прошивки, остальные свои задумки вроде знаю как сделать. Это будет что-то вроде сигнализации в несколько извращенной форме, но больше деталей не могу сообщить.