Как запустить 4 светодиода последовательно на основе кнопочного входа?

Я пытаюсь создать программу, которая последовательно запускает 4 светодиода, а также может делать другие вещи с разными входами и выходами. Из-за этого я использую функцию millis, а не задержку. У меня есть код, работающий, но не совсем так, как я хочу. Он проходит через светодиоды и выключает их, а затем снова проходит, как и предполагалось, но есть две проблемы:

  1. Он пройдет через первый оператор if, затем снова через первый, затем ко второму, затем через первый, второй, затем третий и т. Д. Я хочу, чтобы он каскадно проходил через операторы if, сохраняя светодиод от предыдущих шагов включенным без повторения. Это приводит к тому, что светодиоды мигают очень быстро.
  2. Код по-прежнему выполняется, даже если входные данные считываются так НИЗКО, как это зависит от миллиса. Мне как-то нужно, чтобы он начинался с самого начала каждый раз, когда кнопка нажата, и сбрасывался, когда кнопка не нажата.

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

ниже приведен код

//Code to build 4 sequentially run LEDs that reset everytime there is not input
int RTS_IN  = A0; //input button

//Output LEDs, assigning them to pins
int LED_5 = 5; 
int LED_6 = 6;
int LED_7 = 7; 
int LED_8 = 8; 

//1 second for each time interval
unsigned long Time1 = 1000;
unsigned long Time2 = 2000;
unsigned long Time3 = 3000;
unsigned long Time4 = 4000; 
unsigned long Time5 = 5000;

//variables for holding millis values
unsigned long currentMillis;
unsigned long newMillis = 0;

void setup() 
{
  Serial.begin(9600);
  
  pinMode(RTS_IN, INPUT);
  pinMode(LED_5, OUTPUT);
  pinMode(LED_6, OUTPUT);
  pinMode(LED_7, OUTPUT);
  pinMode(LED_8, OUTPUT);
  
}

void loop()
{
  currentMillis = millis();

  //using sequential method if the input button pressed and held
  if(digitalRead(RTS_IN) == HIGH)
  {
    sequential();
  }
  //keeping LEDs OFF if button has been released 
  else
  digitalWrite(LED_5, LOW);
  digitalWrite(LED_6, LOW);
  digitalWrite(LED_7, LOW);
  digitalWrite(LED_8, LOW);
}

//method to run LEDs sequentially
void sequential()
{
  
  //if statement checking to see if a second has passed to allow the first led to turn on
  if(currentMillis - newMillis >= Time1)
  {
    Serial.println("5 is on");
    
    digitalWrite(LED_5, HIGH);
    delay(1);
    
    //if statement checking to see if 2 seconds has passed to allow the second led to turn on
    if(currentMillis - newMillis >= Time2)
    {
      Serial.println("6 is on");
      
      digitalWrite(LED_6, HIGH);
      delay(1);

      //if statement checking to see if 3 seconds has passed to allow the third led to turn on
      if(currentMillis - newMillis >= Time3)
      {
        Serial.println("7 is on");
        
        digitalWrite(LED_7, HIGH);
        delay(1);
        
        //if statement checking to see if 4 seconds has passed to allow the fourth led to turn on
        if(currentMillis - newMillis >= Time4)
        {
          Serial.println("8 is on");
          
          digitalWrite(LED_8, HIGH);
          delay(1);
          
          //if statement to turn off LEDs and wait a second before turning the first back on
          if(currentMillis - newMillis >= Time5)
          {
            Serial.println("All off");
            
            digitalWrite(LED_5, LOW);
            digitalWrite(LED_6, LOW);
            digitalWrite(LED_7, LOW);
            digitalWrite(LED_8, LOW);

            //this is in order to add a period of this process to each of the 
            //interval times to keep up with millis without this, they would all stay on
            Time1 = Time1 + 5000;
            Time2 = Time2 + 5000;
            Time3 = Time3 + 5000;
            Time4 = Time4 + 5000;
            Time5 = Time5 + 5000;
            delay(1);

          } 
        }
      }
    }
  }
}
  

обновленный код ниже с операторами if else и другим программированием в сочетании


const int RTS_IN  = A0;
const int LTS_IN = A1;

//break signal
const int BS_IN  = A2;
const int BS_OUT = 11;

//reverse signal
const int RS_IN  = A3;
const int RS_OUT = 10;

const int NR_OF_LEDS = 4;
const int RIGHTLEDS[NR_OF_LEDS] = { 6, 7, 8, 9 };
const int LEFTLEDS[NR_OF_LEDS] = { 5, 4, 3, 2 };
const int interval = 100;

int nrOfRightLedsOn = 0;
int nrOfLeftLedsOn = 0;

unsigned long t = 0;

void StateRight();
void StateRightProcess();

void StateLeft();
void StateLeftProcess();

boolean oldSwitchStateRS = LOW;
boolean newSwitchStateRS = LOW;
boolean oldSwitchStateBS = LOW;
boolean newSwitchStateBS = LOW;


void setup() 
{
  Serial.begin(9600);
  pinMode(RTS_IN, INPUT);
  pinMode(LTS_IN, INPUT);

  for (int led = 0; led < NR_OF_LEDS; led++)
  {
    pinMode(RIGHTLEDS[led], OUTPUT);
  }
  
  for (int led = 0; led < NR_OF_LEDS; led++)
  {
    pinMode(LEFTLEDS[led], OUTPUT);
  }

}

void loop() 
{
  StateRight();
  StateLeft();

  newSwitchStateBS = digitalRead(BS_IN);
   if(newSwitchStateBS != oldSwitchStateBS)
  {
    if(newSwitchStateBS == HIGH)
    {
      digitalWrite(BS_OUT, HIGH);
    }
    else
    digitalWrite(BS_OUT, LOW);
    oldSwitchStateBS = newSwitchStateBS;
  }


 newSwitchStateRS = digitalRead(RS_IN);
  if(newSwitchStateRS != oldSwitchStateRS)
  {
    if(newSwitchStateRS == HIGH)
    {
      digitalWrite(RS_OUT, HIGH);
    }
    else
    digitalWrite(RS_OUT, LOW);
    oldSwitchStateRS = newSwitchStateRS;
  }
  

}

void StateRight()

{
  if(digitalRead(RTS_IN) == LOW)
  {
    SwitchRightLedsOff();
    nrOfRightLedsOn = 0;
    t = millis() + interval;
  }
  else if (nrOfRightLedsOn < NR_OF_LEDS)
  {
    StateRightProcess();
  }
  else
  {
    if(millis() >= t + interval)
    {
      t = millis();
      SwitchRightLedsOff();
      nrOfRightLedsOn = 0;
    }
  }
}

void StateRightProcess()
{
  if(millis() >= t + interval)
  {
    t = millis();
    digitalWrite(RIGHTLEDS[nrOfRightLedsOn], HIGH);
    nrOfRightLedsOn++;
  }
}

void SwitchRightLedsOff()
{
  for (int led = 0; led < NR_OF_LEDS; led++)
  {
    digitalWrite(RIGHTLEDS[led], LOW);
  }
}




void StateLeft()
{
  if(digitalRead(LTS_IN) == LOW)
  {
    SwitchLeftLedsOff();
    nrOfLeftLedsOn = 0;
    t = millis() + interval;
  }
  else if (nrOfLeftLedsOn < NR_OF_LEDS)
  {
    StateLeftProcess();
  }
  else 
  {
    if (millis() >= t + interval)
    {
      t = millis();
      SwitchLeftLedsOff;
      nrOfLeftLedsOn = 0;
    }
  }
}

void StateLeftProcess()
{
  if (millis() >= t + interval)
  {
    t = millis();
    digitalWrite(LEFTLEDS[nrOfLeftLedsOn], HIGH);
    nrOfLeftLedsOn++;
  }
}

void SwitchLeftLedsOff()
{
  for (int led = 0; led < NR_OF_LEDS; led++)
  {
    digitalWrite(LEFTLEDS[led], LOW);
  }
}

, 👍3

Обсуждение

Спасибо за замечательный вопрос. вот ссылка на проект Arduino, которую вы можете использовать для дальнейшего обсуждения. Я уверен, что вам будет очень легко поделиться своим проектом таким образом https://wokwi.com/arduino/projects/321913849735283283, @ArduinoFan


1 ответ


Лучший ответ:

5

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

То, что вам нужно, - это так называемый "конечный автомат".

Я думаю (в соответствии с вашей проблемой), что у вас есть 6 состояний:

0 Idle (no sequence)
1 One LED on
2 Two LEDs on
3 Three LEDs on
4 Four LEDs on
5 No LEDs on 

Для этого лучше всего использовать тип перечисления и глобальную переменную с аналогичными значениями:

enum EState
{
    Idle,
    OneLedOn,
    TwoLedsOn,
    ThreeLedsOn,
    FourLedsOn,
    NoLedsOn
}

EState _state;

Вам нужна функция для проверки нового состояния в зависимости от текущего состояния. В словах что-то вроде:

current     When                          Action           New 
state                                                      state
--------    ----------------------------  ---------------- -----
Idle        digitalRead(RTS_IN) == HIGH   Start counter    OneLedOn
                                          Switch on LED 0
OneLedOn    digitalRead(RTS_IN) == LOW    Switch LEDs off  Idle
            5 seconds have passed         Switch on LED 1
                                          Start counter    TwoLedsOn
TwoLedsOn   digitalRead(RTS_IN) == LOW    Switch LEDs off  Idle
            5 seconds have passed         Switch on LED 2  
                                          Start counter    ThreeLedsOn
ThreeLedsOn digitalRead(RTS_IN) == LOW    Switch LEDs off  Idle
            5 seconds have passed         Switch on LED 3 
                                          Start counter    ThreeLedsOn
FourLedsOn  digitalRead(RTS_IN) == LOW    Switch LEDs off  Idle
            5 seconds have passed         Switch LEDs off
                                          Start counter    NoLedsOn
NoLedsOn    digitalRead(RTS_IN) == LOW    Switch LEDs off  Idle
            5 seconds have passed         Switch on LED 0  
                                          Start counter    OneLedn

(если вы нарисуете один круг для каждого состояния и стрелки между ними, так называемую диаграмму состояний UML, вы увидите гораздо более четкий способ изображения состояний и потоков).

У меня нет компилятора, поэтому я делаю это из головы.

void Process()
{
    switch (_state)
    {
    case EState::Idle:
        if ((digitalRead(RTS_IN) == HIGH)
        {
            _time = millis();
            digitalWrite(LED_5, HIGH);
            _state = EState::OneLedOn;
        }
        break;
    case EState::OneLedOn:
        if ((digitalRead(RTS_IN) == LOW)
        {
            SwitchLedsOff();
            _state = Idle;
        }
        else if (millis() > time + 5000)
        {
            _time = millis();
            digitalWrite(LED_6, HIGH);
            _state = EState::TwoLedsOn;
        }
        break;
    case EState::TwoLedsOn:
       ...
}

Функция SwitchLEDsoff устанавливает все светодиоды на низкий уровень, и вам нужна только одна глобальная переменная long _time без знака, которую вы обновляете при запуске нового состояния и проверяете в большинстве состояний.

Обратите внимание, что, скорее всего, вы можете оптимизировать это (например, сделать каждое состояние своей собственной функцией и удалить почти дублированный код), но главная цель-дать вам понять, как использовать конечный автомат и использовать его в этом скетче и будущих скетчах.

Обновить

Следующий скетч такой же, как у вас, но:

  • Оптимизирован для уменьшения дублирования кода (processState)
  • Введены константы
  • Введенный массив светодиодов для итерации
  • Удалил состояние, вместо него использовал _nrOfLedsOn
  • Удалено состояние ожидания, теперь обрабатывается отдельно
  • Глобальные переменные с префиксом подчеркивания (личные предпочтения)
  • Лучшие имена для переменных/функций
  • Используемые прототипы для приведения функций в логический порядок (от высокого уровня к низкому)

Код:

const int RTS_IN  = A0;
const int NR_OF_LEDS = 4;
const int LEDS[NR_OF_LEDS] = { 5, 6, 7, 8 };
const int DURATION = 300;

int _nrOfLedsOn = 0;
unsigned long _time = 0;

void ProcessState();
void ProcessLedsState();
void SwitchLedsOff();

void setup() 
{
    Serial.begin(9600);
    pinMode(RTS_IN, INPUT);
    for (int led = 0; led < NR_OF_LEDS; led++)
    {
        pinMode(LEDS[led], OUTPUT);
    }
}

void loop()
{
    ProcessState();
}

void ProcessState()
{
    if (digitalRead(RTS_IN) == LOW)
    {
        SwitchLedsOff();
        _nrOfLedsOn = 0;
        _time = millis() + DURATION + 1;
    }
    else if (_nrOfLedsOn < NR_OF_LEDS)
    {
        ProcessLedsState();
    }
    else
    {      
        if (millis() > _time + DURATION)
        {
            _time = millis();
            SwitchLedsOff();
            _nrOfLedsOn = 0;
        }
    }
}

void ProcessLedsState()
{
    if (millis() > _time + DURATION)
    {
        _time = millis();
        digitalWrite(LEDS[_nrOfLedsOn], HIGH);
        _nrOfLedsOn++;
    }
}

void SwitchLedsOff()
{ 
    for (int led = 0; led < NR_OF_LEDS; led++)
    {
        digitalWrite(LEDS[led], LOW);
    }
}
,

Я думаю, ты все сделал правильно! Я считаю, что это гораздо более ясный способ кодирования. Я постараюсь внедрить это в свой код и свяжусь с вами. Я ценю ваш быстрый ответ., @Myles

Не за что., @Michel Keijzers

Я реализовал новый код с вашей помощью, и он отлично работает, за исключением одного: он не входит в состояние 5 и не выключает светодиоды с высоким RTS_IN. Я обновил свой первоначальный пост, @Myles

Измените SwitchLedsOff; на SwitchLedsOff(); в случае 5., @Michel Keijzers

Проверьте обновление, я оптимизировал скетч., @Michel Keijzers

Потрясающе, я проверю этот код и попытаюсь реализовать те части, которые смогу понять. Спасибо, что вы мне очень помогли., @Myles

какой смысл иметь эти операторы case, если код просто проваливается? Я также не понимаю, почему вы добавляете 1 к t в первом операторе if в void processState(). Не могли бы вы объяснить, пожалуйста?, @Myles

Вы правы насчет оператора switch, я преобразовал его/объединил с предыдущим оператором if. Я также исправил "ошибку", когда у меня было два метода с именем equal. И причина, по которой нужно добавить +1: если вы видите функцию ProcessLedsState, вы видите if (millis() > _time + DURATION) ... когда он равен, он не сработает сразу, поэтому я добавил 1. Возможно, вместо этого было бы лучше использовать>=, так как вы, вероятно, хотите менять светодиоды каждые 300 мс, а не когда они (обязательно) превышают 300 мс., @Michel Keijzers

так нужны ли мне тогда только 2 case-оператора?, @Myles

Давайте [продолжим это обсуждение в чате](https://chat.stackexchange.com/rooms/133542/discussion-between-michel-keijzers-and-myles)., @Michel Keijzers