Как кнопкой прервать бесконечный цикл While?

Всем привет!
Столкнулся с такой проблемой. В схеме 2 кнопки. С помощью библиотеки “GyverButton.h” хочу этими кнопками включать выполнение функций. Функции цикличные с циклом While(1) - это обязательно. В примере 4 функции, каждая для мигания отдельного светодиода. Сначала мигание делал через delay, потом переделал на millis - не помогло. Включать функцию могу только через RESET. Так все работает. Но хотелось, чтобы при работающей одной функции, по нажатию на другую кнопку, включалась другая. Короче вопрос: куда воткнуть “break” или в While(1) вместо “1” нужно, что-то другое… По всякому извращался, не получается, только через сброс можно другую функцию включить… Схема вот


код вот

#define BTN1 4		// кнопка подключена сюда (PIN --- КНОПКА --- GND)
#define BTN2 3      // кнопка подключена сюда (PIN --- КНОПКА --- GND)
#include <Arduino.h>
#include "GyverButton.h"
GButton butt1(BTN1);
GButton butt2(BTN2);

unsigned long currentTime; // переменная времени
boolean ledState = 0; // переменная состояния светодиода

void setup() {
 pinMode(6, OUTPUT);
 pinMode(7, OUTPUT);
 pinMode(8, OUTPUT);
 pinMode(9, OUTPUT); // объявляем пин 9 как выход
}



void loop() {
  butt1.tick();  // обязательная функция отработки. Должна постоянно опрашиваться
  butt2.tick();  

  if (butt1.isClick()) {    //при клике по 1 кнопке - вкл. функция  void Leds1()
    Leds1();
  }

  if (butt2.isClick()) {    //при клике по 2 кнопке - вкл. функция  void Leds2()   
   Leds2();
  }

  if (butt1.isStep()) {     //при удержании 1 кнопки - вкл. функция  void Leds3()
   Leds3();
  }

  if (butt2.isStep()) {    //при удержании 2 кнопки - вкл. функция  void Leds4()
    Leds4();
}


}


void Leds1() {
 while(1) {
  digitalWrite(6, HIGH); // Включаем светодиод
  delay(100); // Ждём 1 секунду
  digitalWrite(6, LOW); // Выключаем светодиод
  delay(100); // Ждём ещё 1 секунду
 }
}

void Leds2() {
 while(1) {
  if (millis() - currentTime > 100) { // проверяем, сколько прошло миллисекунд
 currentTime = millis();
 ledState = !ledState; // меняем состояние светодиода на противоположное
 digitalWrite(7, ledState); // включаем или выключаем светодиод
 }
 }
}

void Leds3() {
 while(1) {
  if (millis() - currentTime > 100) { // проверяем, сколько прошло миллисекунд
 currentTime = millis();
 ledState = !ledState; // меняем состояние светодиода на противоположное
 digitalWrite(8, ledState); // включаем или выключаем светодиод
 }
 }
}

void Leds4() {
 while(1) {
  if (millis() - currentTime > 100) { // проверяем, сколько прошло миллисекунд
 currentTime = millis();
 ledState = !ledState; // меняем состояние светодиода на противоположное
 digitalWrite(9, ledState); // включаем или выключаем светодиод
 }
 }
}

Вариант 1, правильный:
Никаких внутренних бесконечных циклов. Каждая функция является задачей и вызывается периодически (каждые 100 мс или сколько вам нужно), выполняет один цикл и завершается. В этом случае запуском/остановкой задачи можно управлять кнопками, опрашиваемыми в loop(); Управление задачами описано здесь

Вариант 2, костыльный:
Отслеживание кнопок выносится в отдельную функцию, которая вызывается не только в loop(), но и в каждой из функций в ее бесконечном цикле и, соответственно, позволяет не терять управление

1 лайк

Вариант 3, для ленивых - использование готового диспетчера задач, например https://github.com/VAleSh-Soft/shTaskManager. Но тут думать тоже придется :wink:

1 лайк

Вариант 4:
«Повесить» кнопку на прерывание. В прерывании выставлять флаг. В цикле(ах) этот флаг отслеживать и вызывать break.

ЗЫ: всё, кроме 3го - можно считать костылями. Но 4й - наиболее «правильный костыль» (если кнопка одна или по количеству пинов с внешними прерываниями).

1 лайк

Это не схема :rofl:

В таком цикле никогда никакая кнопка не сработает

это бред.

горе луковое

Тебе нужно не условие в While(1) менять, а сам цикл While() выкинуть.

Давайте всё же, как просил наш участник @WladDrakula проявим терпимость и НЕ будем предполагать то, чего не знаем. Ну вот хочется ТС так делать - ну пусть делает, в чем дело?

Спросил он «как решить проблему его» - отвечаем. Если знаем.

ЗЫ: На этом «этапе»: на самом деле, отсеиваются лентяи, неучи, халявщики и просто тупые. Зачем нарушать процесс?))

void Leds4() {
  if (millis() - currentTime < 100) { return; }

 currentTime = millis();
 ledState = !ledState; // меняем состояние светодиода на противоположное
 digitalWrite(9, ledState); // включаем или выключаем светодиод

}

В лупе же нужно постоянно вызывать эту функцию, пока не будет нажата кнопка, отличная от запускающей этот спецэффект.

1 лайк

Ее нужно вызывать только если была удержана кнопка 2. А значит кроме собственно миллиса нужен еще и флаг активности задачи. В общем ссылку на тему по управлению задачами я ему еще в #2 дал ))

Бесспорно. Пусть подумает над этим.

Да ладно, пятница! Жарим мясо :smiley:

1 лайк

Снупдог уже с факелом. Весь Париж будет в дыму.

1 лайк

Тогда коптим… :rofl:

Это ошибка проектирования.
Как только избавитесь от нее, проблема будет решена.

Ок
Если хочется делать наперекосяк - пусть справляется сам.

Я отсеялся

1 лайк

Тема вроде бы не ваша )))

Да он еще не знает «как прямо», а как «криво». Пусть учится. Ошибки исправленные не им ему никак не запомнятся….

1 лайк

Завтра попробую. По ходу самое простое решение.

Kогда много задач проще FreeRTOS подтянуть. Написал каждую задачу, в сетапе активировал и даже луп не понадбиться.