ЛЕД бегущая волна, кнопка, как победить delay с помощю millis

Здравствуйте форумчане, вот с внучком решил поиграться с ардуино и упёрлись в пока что неразрешимую загадку, delay тормозит работу с кнопкой но красиво зажигает ЛЕДы,
пробовал разные способы с millis тогда Леды работают как попало, есть один вариант с millis который как то заработал но тогда надо писать свой таймеры задержки для каждого ЛЕДа.

#define bt 2
byte p = 6;
byte p1 = 13;                        // даём пинам имена

byte flag1 = 0;                      // переменная для работы
byte flag2 = 0;                      // переменная для работы

uint32_t time = 0;                   // отсчёт, время для millis()
int settime = 50;                    //время для сравнения



int led[] = {6, 7, 8, 9, 10, 11, 12, 13};    //массив пины с которыми будем работать

//////////////////////////////////////////////////////////////////////////////
void setup() {
  for (int l = 0; l < 8; l++) {
    pinMode(led[l], OUTPUT);         //пины из масива, задаём режим работы на выход
  }
  pinMode(bt, INPUT);                // пин, режим работы на вход для кнопки
  Serial.begin(9600);                // включаем монитор порта
}
//////////////////////////////////////////////////////////////////////////////
void loop() {
  if (millis() - time > settime) {
    time = millis();
    if (flag2 == 1) {
      up(500);
    }                       // функция бегущая волна ЛЕДов 13->6 если положение флага2=1
    if (flag2 == 3) {
      down(1000);
    }                       // функция бегущая волна ЛЕДов 6->13 если положение флага2=3

  }
  button();                 // функия кнопки
}
//////////////////////////////////////////////////////////////////////////////
void up(int settimeON) {
  /*p1 = p1 - 1;                    //  вариант без цикла for
    if (p1 < 6) {
    p1 = 13;
    }
    digitalWrite(p1, HIGH);         // включаем ЛЕД
    delay(settimeON);               // ждём
    digitalWrite(p1, LOW);          // выключаем ЛЕД
    Serial.println(p1);             // пишем в монитор какой пин
  */
  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  for (p1 = 13; p1 > 5; p1--) {     // вариант с циклом for
    digitalWrite(p1, HIGH);         // включаем ЛЕД
    delay(settimeON);               // ждём
    digitalWrite(p1, LOW);          // выключаем ЛЕД
    Serial.println(p1);             // пишем в монитор какой пин
  }

}
/////////////////////////////////////////////////////////////////////////////////
void down(int settimeON) {
  /*p = p + 1;
    if (p > 13) {                  //  вариант без цикла for
    p = 6;
    }
    digitalWrite(p, HIGH);
    delay(settimeON);
    digitalWrite(p, LOW);
    Serial.println(p);
  */
  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
  for (p = 6; p < 14; p++) {       // вариант с циклом for
    digitalWrite(p, HIGH);
    delay(settimeON);
    digitalWrite(p, LOW);
    Serial.println(p);
  }
}
/////////////////////////////////////////////////////////////////////////////////
void button() {

  if (digitalRead(bt) == LOW && flag1 == 0) { //читаем состояние кнопки и флага1
    flag1 = 1;                               // меняем состояние флаг1
    flag2 = flag2 + 1;                       // меняем состояние флаг2 на один шаг
    if (flag2 > 4) {
      flag2 = 0; // сбрасывае флаг2 в исходное положение при достижении условия
    }
  }
  if (digitalRead(bt) == HIGH && flag1 == 1) { //опять читаем состояние кнопки и флага1
    flag1 = 0;                               //сбрасывае флаг в исходное положение
  }
  Serial.print(flag1);                     //пишем в монитор состояние флага1
  Serial.println(flag2);                   //пишем в монитор состояние флага2
}


именно так

Если вы смешиваете миллис и delay - ничего путного не выйдет

после своих “непосильных трудов” вроде бы понял что “писанины” кода будет много,
но возникает вопрос может всё таки есть способ организовать бегущую волну ЛЕДов другим способом или есть всё таки способ побороть delay

миллис
А чтобы не писать отдельный таймер на каждый лед - используйте циклы.
У вас же все леды одинаковые.

да пытался таймеры засунуть в цикл но пока что не получилось, что то недопонимаю а с этим кодом как говорится “нашла коса на камень” за то хорошая школа написанием кода и исправления ошибок перебирая варианты.

Добавлю - и еще используйте массивы.
Вот у вас есть массив led[] - так чего ж вы леды по номеру пина зажигаете? А если пины будут не подряд - все сломается?
А если использовать массив, пины могут быть какие угодно

данный код тренировочный пока что для работа что бы номер ПИНа имел тоже имя(номер)
или так не правильно делать

Так надо не “пробовать разные”, а придумать осмысленный алгоритм, и заставить МК работать по этому алгоритму.

Тут все очень просто: нужно представить себя на месте МК.
Вот представьте, что Вы стоите с часами, и Вам нужно включать и выключать светодиоды тумблерами. Опишите, как именно Вы будете это делать. По-русски. А потом переведите на Си.

на сколько я понимаю работу кода, цикл FOR переключает по очереди ПИНы(леды),после каждого переключения delay останавливает выполнение всего кода, если на millis, мне нужно включить ПИН(лед) и удерживать его включенным несколько циклов то есть остановить сам цикл FOR переключения ПИНов для этого нужно условие выполнения, вот тут пока загвоздка

уже поздно лень писать комментарии в коде, пока что получилось вот так но есть проблема ЛЕДы как будто загораются в пол накалаПрограммный код

#define bt 2
byte p = 6;
byte p1 = 13;                        // даём пинам имена

byte flag1 = 0;                      // переменная для работы
byte flag2 = 0;                      // переменная для работы

uint32_t time = 0;                   // отсчёт, время для millis()
int settime = 1000;                    //время для сравнения

byte pinON = 6;
byte pinON2 = 13;



int led[] = {6, 7, 8, 9, 10, 11, 12, 13};    //массив пины с которыми будем работать

//////////////////////////////////////////////////////////////////////////////
void setup() {
  for (int l = 0; l < 8; l++) {
    pinMode(led[l], OUTPUT);         //пины из масива, задаём режим работы на выход
  }
  pinMode(bt, INPUT);                // пин, режим работы на вход для кнопки
  Serial.begin(9600);                // включаем монитор порта
}
//////////////////////////////////////////////////////////////////////////////
void loop() {

  if (flag2 == 1) {
    up(); // функция бегущая волна ЛЕДов 13->6 если положение флага2=1
  }
  if (flag2 == 3) {
    down(); // функция бегущая волна ЛЕДов 6->13 если положение флага2=3
  }

  button();                 // функия кнопки
}
//////////////////////////////////////////////////////////////////////////////
void up() {
  if (millis() - time > 500) {
    if (pinON > 13) {
      pinON = 5;
    }
    else {
      pinON++;
      digitalWrite(pinON, HIGH);
    }
    time = millis();
  }
  digitalWrite(pinON, LOW);
  Serial.println("pinON:");
  Serial.println(pinON);
}

/////////////////////////////////////////////////////////////////////////////////
void down() {
  if (millis() - time > 500) {
    if (pinON2 < 6) {
      pinON2 = 14;
    }
    else {
      pinON2--;
      digitalWrite(pinON2, HIGH);
    }
    time = millis();
  }
  digitalWrite(pinON2, LOW);
  Serial.println("pinON2:");
  Serial.println(pinON2);
}

/////////////////////////////////////////////////////////////////////////////////
void button() {

  if (digitalRead(bt) == LOW && flag1 == 0) { //читаем состояние кнопки и флага1
    flag1 = 1;                               // меняем состояние флаг1
    flag2 = flag2 + 1;                       // меняем состояние флаг2 на один шаг
    if (flag2 > 4) {
      flag2 = 0; // сбрасывае флаг2 в исходное положение при достижении условия
    }
  }
  if (digitalRead(bt) == HIGH && flag1 == 1) { //опять читаем состояние кнопки и флага1
    flag1 = 0;                               //сбрасывае флаг в исходное положение
  }

  Serial.println(flag1);                     //пишем в монитор состояние флага1
  Serial.print(flag2);                   //пишем в монитор состояние флага2
}


Нет.
Вам нужно гнать цикл непрерывно, и в этом цикле в нужное время включать или выключать светодиоды. Т.е. выключение светодиода - такое же событие, как и включение, и его точно так же надо отслеживать по времени.

Есть “правильный” подход с “конечными автоматами” (что бы это ни значило), с millis вместо делай и т.д.
Есть “костыльный” (или в случае внуков - “ходунковый”), когда пытаемся приспособить имеющийся простой код под возросшие потребности.
Мне больше симпатичен подход, когда переход к усложнению происходит осознанно. Поэтому приведу костыльный вариант, (исключительно в демонстрационных целях). В нем используется своя версия delay и yield для прерывания ПРОСТЫХ линейных алгоритмов.

Спасибо, буду изучать, быстренько посмотрел работу скетча в wokwi заметил что ЛЕДы зажигаются и каждый со своей задержкой более длинной гаснут.
добавлю обновлённое своё “кривое с утра творение” на суд

#define bt 2
byte p = 6;
byte p1 = 13;  // даём пинам имена

byte flag1 = 0;  // переменная для работы
byte flag2 = 0;  // переменная для работы

uint32_t time = 0;   // отсчёт, время для millis()
int settime = 1000;  //время для сравнения

byte pinON = 6;
byte pinON2 = 13;   // даём пинам имена вариант2



int led[] = { 6, 7, 8, 9, 10, 11, 12, 13 };  //массив пины с которыми будем работать

//////////////////////////////////////////////////////////////////////////////
void setup() {
  for (int l = 0; l < 8; l++) {
    pinMode(led[l], OUTPUT);  //пины из масива, задаём режим работы на выход
  }
  pinMode(bt, INPUT);  // пин, режим работы на вход для кнопки
  Serial.begin(9600);  // включаем монитор порта
}
//////////////////////////////////////////////////////////////////////////////
void loop() {

  if (flag2 == 1) {
    up(300, 400, 500, 600, 700);  // функция бегущая волна ЛЕДов 13->6 если положение флага2=1
    Serial.println("pinON:");
    Serial.println(pinON);
  }
  if (flag2 == 3) {
    down(300, 400, 500, 600, 700);  // функция бегущая волна ЛЕДов 6->13 если положение флага2=3
    Serial.println("pinON2:");
    Serial.println(pinON2);
  }

  button();  // функия кнопки
  Serial.println(flag1);  //пишем в монитор состояние флага1
  Serial.print(flag2);    //пишем в монитор состояние флага2



}
//////////////////////////////////////////////////////////////////////////////
void button() {

  if (digitalRead(bt) == LOW && flag1 == 0) {  //читаем состояние кнопки и флага1
    flag1 = 1;                                 // меняем состояние флаг1
    flag2 = flag2 + 1;                         // меняем состояние флаг2 на один шаг
    if (flag2 > 4) {
      flag2 = 0;  // сбрасывае флаг2 в исходное положение при достижении условия
    }
  }
  if (digitalRead(bt) == HIGH && flag1 == 1) {  //опять читаем состояние кнопки и флага1
    flag1 = 0;                                  //сбрасывае флаг в исходное положение
  }


}

/////////////////////////////////////////////////////////////////////////////////

  void up(int settime, int settime1, int settime2, int settime3, int settime4) {


  if (millis() - time > settime) {
    digitalWrite(6, HIGH);
  }

  if (millis() - time > settime1) {
    digitalWrite(7, HIGH);
  }

  if (millis() - time > settime2) {
    digitalWrite(8, HIGH);
  }

  if (millis() - time > settime3) {
    digitalWrite(9, HIGH);
  }

  if (millis() - time > settime4) {
    digitalWrite(10, HIGH);
    //time = millis();
  }

  if (millis() - time > settime) {
    digitalWrite(6, LOW);
  }
  if (millis() - time > settime1) {
    digitalWrite(7, LOW);
  }
  if (millis() - time > settime2) {
    digitalWrite(8, LOW);
  }
  if (millis() - time > settime3) {
    digitalWrite(9, LOW);
  }
  if (millis() - time > settime4) {
    digitalWrite(10, LOW);
    time = millis();
  }

}


/////////////////////////////////////////////////////////////////////////////////
void down(int settime, int settime1, int settime2, int settime3, int settime4) {


  if (millis() - time > settime) {
    digitalWrite(13, HIGH);
  }

  if (millis() - time > settime1) {
    digitalWrite(12, HIGH);
  }

  if (millis() - time > settime2) {
    digitalWrite(11, HIGH);
  }

  if (millis() - time > settime3) {
    digitalWrite(10, HIGH);
  }

  if (millis() - time > settime4) {
    digitalWrite(9, HIGH);

  }

  if (millis() - time > settime) {
    digitalWrite(13, LOW);
  }
  if (millis() - time > settime1) {
    digitalWrite(12, LOW);
  }
  if (millis() - time > settime2) {
    digitalWrite(11, LOW);
  }
  if (millis() - time > settime3) {
    digitalWrite(10, LOW);
  }
  if (millis() - time > settime4) {
    digitalWrite(9, LOW);
    time = millis();
  }

}

фигня какая-то. Для каждого времени по два условия - одно включает. другое выключает…
И как вы думаете это одновременно будет работать? Вы чего пытались добиться-то?

и да, вы продолжаете использовать номера пинов, хотя у вас есть массив.

Попробуйте адаптировать мой пример под свои реальные светодиоды, может поймёте тогда.

//имитатор 20 светодиодов на цветном дисплее st7735 128*160
//групповой блинк
#include "svetodiod_.h"
long Y[20];//моменты переключения
int t[20]={127,220,380,500,550,600,660,180,240,400,490,560,620,700,745,800,333,444,777,999};//длительности
bool flag[20];//флаги состояний
uint16_t color[]={green,yellow,orange,blue,
 green,yellow,orange,blue,green,yellow,orange,blue,
 green,yellow,orange,blue,green,yellow,orange,blue};//цвета

void setup() {
 fun_setup();
 }
void loop() {
 for(int i=0;i<20;i++){
 if(millis()-Y[i]>t[i]){
 Y[i]=millis();flag[i]=!flag[i];
 if(flag[i]){led_(i,color[i]);}else{led_(i,off);}//если флаг истина включить итый светодиод, иначе выключить
 }
 }
 }


Спойлер
//имитатор 20 светодиодов на цветном дисплее st7735 128*160
//
const unsigned char ris_1[]PROGMEM = {//РИСУНОК СВЕТОДИОДА
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X07,0XE0,0X00,0X00,0X1F,0XF8,0X00,
0X00,0X30,0X0C,0X00,0X00,0X60,0X36,0X00,0X00,0XC0,0X1B,0X00,0X00,0XC0,0X0D,0X00,
0X01,0X80,0X05,0X80,0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,
0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,0X01,0X80,0X06,0X80,
0X01,0X80,0X02,0X80,0X01,0X80,0X00,0X80,0X07,0XFF,0XFF,0XE0,0X04,0X00,0X00,0X20,
0X04,0X00,0X00,0X20,0X04,0X00,0X00,0X20,0X07,0XFF,0XFF,0XE0,0X07,0XFF,0XFF,0XE0,
0X00,0X00,0X00,0X00,0X00,0X30,0X0C,0X00,0X00,0X30,0X0C,0X00,0X00,0X30,0X0C,0X00,
0X00,0X30,0X0C,0X00,0X00,0X30,0X0C,0X00,0X00,0X00,0X0C,0X00,0X00,0X00,0X00,0X00  
};
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <SPI.h>

#define TFT_CS     10
#define TFT_RST    9
#define TFT_DC     8
// TFT_SCL 13   
// TFT_SDA 11 
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS,  TFT_DC, TFT_RST);
#define off  tft.Color565(100,100,100)//выключен - серый цвет
#define red  tft.Color565(255,0,0)//красный цвет
#define green  tft.Color565(0,255,0)//зелёный цвет
#define yellow  tft.Color565(255,255,0)//жёлтый цвет
#define blue  tft.Color565(0,255,255)//голубой цвет
#define orange  tft.Color565(255,127,0)//оранжевый цвет
#define white  tft.Color565(255,255,255)//белый цвет



///////////функция отрисовки картинки по заданным координатам,размерам и цветам
void drawBitmapFast(int16_t x, int16_t y, const uint8_t bitmap[],
                    int16_t w, int16_t h, uint16_t color, uint16_t bg) {
  uint8_t b(0);
  uint8_t bc(0);
  uint16_t tc(0);
  int16_t c =  w * h;
 // tft.startWrite();
  tft.setAddrWindow(x, y, x+w-1, y+h-1);
  SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));
  digitalWrite(TFT_DC, HIGH);
  digitalWrite(TFT_CS, LOW); 
 asm volatile(
    "mloop_%=: \n"
    "   LPM %[b],%a[bm]+ \n"
    "   LDI %[bc],1 \n"
    "loop_%=: \n"
    "   SBRC %[b],7 \n"
    "   MOVW %[tc],%[color] \n"
    "   SBRS %[b],7 \n"
    "   MOVW %[tc],%[bg] \n"
    "   IN __tmp_reg__,%[spdr]-1 \n"
    "   OUT %[spdr],%B[tc] \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   RJMP .+0 \n"
    "   IN __tmp_reg__,%[spdr]-1 \n"
    "   OUT %[spdr],%A[tc] \n"
    "   SBIW %[c],1 \n"
    "   BREQ exit_%= \n"
    "   NOP \n"
    "   LSL %[b] \n"
    "   LSL %[bc] \n"
    "   BRCS mloop_%= \n"
    "   RJMP .+0 \n"
    "   NOP \n"
    "   RJMP loop_%= \n"
    "exit_%=: \n"
  : [tc] "=&r" (tc)
  : [b] "l" (b), [bm] "z" (bitmap), [bc] "d" (bc), [color] "w" (color), [bg] "w" (bg),
    [spdr] "M" (_SFR_IO_ADDR(SPDR)), [c] "w" (c)
  : );
 // tft.endWrite();
  digitalWrite(TFT_CS, HIGH);
  SPI.endTransaction();
}
////////////////////////////////////////////////////
void leds_off(){//отрисовка всех светодиодов выключенными
 for(int i=0;i<160;i=i+32){
 for(int j=0;j<128;j=j+32){ 
 drawBitmapFast(i, j, ris_1, 32, 32,off,tft.Color565(0,0,0) );  
 }
 }
}
////////////////////////////////////////////////////
void led_(byte N,uint16_t color_){//отрисовка светодиода по номеру включенным заданным цветом или выключенным
 if(N>=0&&N<20){
drawBitmapFast(32*(N%5), 32*(N/5), ris_1, 32, 32,color_,tft.Color565(0,0,0) );
  }  
}
////////////////////////////////////////////////////
void fun_setup(){//перемещённый "сетап"
  tft.initR(INITR_BLACKTAB);
  tft.setRotation(3);
  tft.fillScreen(ST7735_BLACK);
 leds_off();//рисуем все светодиоды выключенными 
}
////////////////////////////////////////////////////

VID_20230207_112140

 for (p = 6; p < 14; p++) {       // вариант с циклом for
    digitalWrite(p, HIGH);
    delay(settimeON);
    digitalWrite(p, LOW);
    Serial.println(p);
  }

К примеру как этот цикл переписать без делей (которых так иногда не хватает)
Ну вот так, наверное, по сути:

 if (millis() - time >settimeON ) {
   time = millis();
p++;
if(p>13){digitalWrite(p-1, LOW);p=6;digitalWrite(p, HIGH);}
  else  {digitalWrite(p-1, LOW);digitalWrite(p, HIGH); }
    }

Если ошибаюсь, поправьте.

static uint32_t time = millis();
static uint8_t p = 6;

if (millis() - time > settimeON)
{
  time = millis();
  // гасим все светодиоды
  for (uint8_t i = 6; i < 14; i++)
  {
    digitalWrite(i, LOW);
  }
  // зажигаем текущий светодиод
  digitalWrite(p, HIGH);
  // определяем номер следующего светодиода
  p = (p < 13) ? p + 1 : 6;
}

а вам что нужно, вау эффект для внука ? или научить программировать ?
это пойдет ? для вау))

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

Спасибо всем, буду переваривать полученную информацию