Исправление схемы последовательности пешеходных светофоров в цикле с использованием millis и без задержки

Я эмулировал циклический шаблон пешеходного светофора (красный, зеленый, зеленый мигает) в следующем коде Arduino. Но когда я запускаю этот код на своем мини-контроллере ESP8266 Wemos D1, появляется шаблон (красный, красный и (мигает зеленым) одновременно зеленый). Я не знаю, почему появилась эта странная закономерность и я пытался ее исправить, но у меня ничего не получилось. Как исправить этот код, чтобы воспроизвести шаблон последовательности (красный, зеленый, зеленый мигает)?

// Определить назначения контактов
const int redLED = D1;
const int greenLED = D2;
const int pedestrianButton = D3;
const int potentiometer = A0;

// Определение переменных
unsigned long lastStateChangeTime = 0;
unsigned long greenFlashingDuration = 3000; // 3 секунды
unsigned long redGreenDuration = 5000; // Начальная продолжительность
bool pedestrianRequest = false;

void setup() {
  // Инициализируем контакты
  pinMode(greenLED, OUTPUT);
  pinMode(redLED, OUTPUT);
  pinMode(pedestrianButton, INPUT_PULLUP);
  attachInterrupt(digitalPinToInterrupt(pedestrianButton), pedestrianInterrupt, FALLING);
}

void loop() {
  updateTiming(); // Чтение потенциометра/LDR и обновление таймингов
  manageTrafficLights(); // Управление состояниями светофора
}

void manageTrafficLights() {
  unsigned long currentTime = millis();

  if (pedestrianRequest==true) {
    // Запрос пешехода имеет приоритет
    digitalWrite(greenLED, HIGH);
    digitalWrite(redLED, LOW);
    
    pedestrianRequest = false;
  }
  
  // Определяем состояние по времени
  
  if ( (currentTime-lastStateChangeTime) >= (2 * redGreenDuration) ) {
    lastStateChangeTime = currentTime; // Сброс цикла
  }
  else {
    if ( (currentTime-lastStateChangeTime) < redGreenDuration ) {
      digitalWrite(greenLED, LOW);
      digitalWrite(redLED, HIGH); // Красное состояние
    } else if ( (currentTime-lastStateChangeTime) < (2*redGreenDuration-greenFlashingDuration) ) {
      digitalWrite(redLED, LOW);
      digitalWrite(greenLED, HIGH); // Зелёное состояние
    } else {
      digitalWrite(redLED, LOW);
      
      if ( ( (currentTime-lastStateChangeTime) >= (2*redGreenDuration-greenFlashingDuration) ) 
      && ( (currentTime-lastStateChangeTime) % (greenFlashingDuration/3) < (greenFlashingDuration/6) ) ){
        digitalWrite(greenLED, LOW);
      } else {
        digitalWrite(greenLED, HIGH);
      }
      
    }
  }
  
}

void updateTiming() {
  // Считываем значения потенциометра/LDR и сопоставляем их с таймингами
  // Обновляем redGreenDuration и greenFlashingDuration соответственно
  redGreenDuration = map(analogRead(potentiometer), 0, 1023, 5000, 10000);
}

void pedestrianInterrupt() {
  pedestrianRequest = true;
  // Быстрое изменение цвета на зеленый, если он находится в красном состоянии
  if (digitalRead(redLED) == HIGH) {
    digitalWrite(greenLED, HIGH);
    digitalWrite(redLED, LOW);
    lastStateChangeTime = millis(); // Сбрасываем цикл
  }
  
}


, 👍1


1 ответ


2

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

Он разделяет логику и управление светодиодами с помощью флагов.

Моделирование доступно по адресу https://wokwi.com/projects/389132610627050497


// https://arduino.stackexchange.com/questions/95476/fixing-a-pedestrian-traffic-light-sequence-pattern-in-a-cycle-using-millis-and-w

// моделирование см. https://wokwi.com/projects/389127817478781953.

const int redLED   = 2;
const int greenLED = 3;

unsigned int timeInterval  [4] = {  5,  3,  4,  0};             // таймеры перезагружаются с этими значениями
unsigned int timeCounter   [4] = {  0,  0,  0,  0};             // это настоящие таймеры

//логическое перезагружаемое [4] = {истина, истина, ложь, ложь}; // это определяет, перезапустится ли таймер по достижении конца
                                                                // помещаем значение в <timeCounter> для одноразового мероприятия
                                                                // 3-й и 4-й таймеры не используются в этом примере эскиза

boolean       tick [4]  = {false, false, false, false};         // флаги таймеров обратного отсчета
boolean       mainTick  = false;
boolean       subTick   = false;

boolean       red       = false;
boolean       green     = false;
boolean       blink     = false;


unsigned long previousMillis = 0;

void setup() {

  Serial.begin(115200);

//memcpy(timeCounter, timeInterval, 4 * sizeof(long)); // предварительная загрузка таймеров

  green = true;                                                  // начинаем с зеленого
  timeCounter[0] = 1;                                            // загружаем первый счетчик

}

void loop() {

  unsigned long currentMillis = millis();

// ---------------------------------------------------------------- -------------------

  if (currentMillis - previousMillis >= 500) {                    // тик 0,5 с
    previousMillis = currentMillis;
    subTick = !subTick;                                           // чередуем значение

    if (subTick) {
      mainTick = true;                                            // тик 1 с

  // Serial.println("галочка"); // отладочный код

      for (int i = 0; i < 4; i++) {                               // отслеживаем 4 временных интервала

        timeCounter[i]-- ;                                        // уменьшаем счетчик

        if (timeCounter[i] == 0) {
  // if (reloadable[i]) timeCounter[i] = timeInt erval[i]; // достиг нуля, перезагрузите счетчик, если он доступен для перезагрузки
          tick[i] = true;                                         // выдаем "тик"
        }
      }
    }
  }

// ---------------------------------------------------------------- -------------------

  if (mainTick) {                                         // тик 1 с
  }

// ---------------------------------------------------------------- -------------------

  if (tick[0]) {                                          //
    tick[0] = false;
    timeCounter[1] = timeInterval[1];                     // устанавливаем следующий тик

    red   = false;                                        // устанавливаем флаги
    green = true;
    blink = false;
  }

// ---------------------------------------------------------------- -------------------

  if (tick[1]) {                                          //
    tick[1] = false;                                      // снимаем флаг, чтобы код выполнялся только один раз за тик
    timeCounter[2] = timeInterval[2];                     // устанавливаем следующий тик

    red   = true;
    green = false;
    blink = false;
  }

// ---------------------------------------------------------------- -------------------

  if (tick[2]) {                                          //
    tick[2] = false;                                      // снимаем флаг, чтобы код выполнялся только один раз за тик
    timeCounter[0] = timeInterval[0];                     // устанавливаем следующий тик

    red   = false;
    green = false;
    blink = true;
  }

// ---------------------------------------------------------------- -------------------

  if (mainTick) {                                         // их может быть кратно
    mainTick = false;                                     // очищаем флаг в последнем
  }

// ---------------------------------------------------------------- -------------------

  digitalWrite(redLED  , red             ? HIGH:LOW);     // Тернарный оператор C++
  digitalWrite(greenLED, green           ? HIGH:LOW);
  digitalWrite(redLED  , blink && subTick? HIGH:LOW);     // субтик меняется каждые 500 мс
}
,

Ваша ссылка на Wokwi не работает., @Russell McMahon

@RussellMcMahon Я проверил ссылку на нескольких устройствах, ни на одном из которых не был выполнен вход в Wokwi... все они работали... можете ли вы зайти на https://wokwi.com/?, @jsotola

Теперь это работает для меня. Мне удалось получить доступ к Вокви. Я попытался получить к нему доступ всего через несколько минут после того, как вы разместили ссылку. Возможно, было какое-то время на настройку. || Им нужно периферийное устройство NRF24L01 или, еще лучше, их совокупность :-). (Это было бы полезно для меня сейчас)., @Russell McMahon

@RussellMcMahon, у них есть «индивидуальный чип»… его можно запрограммировать с помощью C++, Rust и Verilog… возможно, его можно будет эмулировать NRF24L01, @jsotola