Исправление схемы последовательности пешеходных светофоров в цикле с использованием 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 ответ
▲ 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 мс
}
,
@jsotola
Смотрите также:
- Как справиться с rollover millis()?
- Использование millis() и micros() внутри процедуры прерывания
- Постоянная частота дискретизации АЦП на ESP8266 или ESP32
- Отключить прерывания при выполнении важных действий?
- Влияет ли `millis()` на длинные ISR?
- ESP8266: ошибка: 'getLocalTime' was not declared in this scope
- Как разобрать 20180810T143000Z в time_t
- Как найти разницу между двумя timestamp
Ваша ссылка на Wokwi не работает., @Russell McMahon
@RussellMcMahon Я проверил ссылку на нескольких устройствах, ни на одном из которых не был выполнен вход в Wokwi... все они работали... можете ли вы зайти на https://wokwi.com/?, @jsotola
Теперь это работает для меня. Мне удалось получить доступ к Вокви. Я попытался получить к нему доступ всего через несколько минут после того, как вы разместили ссылку. Возможно, было какое-то время на настройку. || Им нужно периферийное устройство NRF24L01 или, еще лучше, их совокупность :-). (Это было бы полезно для меня сейчас)., @Russell McMahon
@RussellMcMahon, у них есть «индивидуальный чип»… его можно запрограммировать с помощью C++, Rust и Verilog… возможно, его можно будет эмулировать NRF24L01, @jsotola