Светофор v0.2

Познакомьтесь с обновлённой версией светофора. Светофор имеет 2 режима работы: полностью автоматический и ручной.

Мы определили три режима работы светофора: AUTO – режим движения автомобилей, FULLAUTO – режим работы светофора поочередно разрешающий движение автомобилей и пешеходов, PED – включение разрешающего сигнала для пешеходов, NOPED – аналог AUTO, отличие в запрете на обработку нажатия на кнопку.

В ручном режиме светофор разрешает движение автомобилям и включает зелёный свет пешеходам после нажатия на кнопку. Нажатие на кнопку вызывает аппаратное прерывание, которое устанавливает флаг butOn в значение true. Состояние флага butOn проверяется в основном цикле loop, только в состоянии AUTO.

Также режим работы светофора зависит от освещённости. К схеме подключен аналоговый датчик освещённости. В зависимости от освещённости программа в автоматически выбирает режим AUTO или FULLAUTO.

Изменение режимов происходит только после окончания выполнения текущего цикла.

В обновленной версии мы изменили структуру хранения сценариев. Текущая система позволяет иметь до 8 независимых каналов. Состояние канала описывается одним байтом. Сценарий представляет собой массив структур, включающих время выполнения и состояние каждого “кадра” сценария.

#include <Arduino.h>
#define Z1 0
#define Y1 1
#define R1 2
#define Z2 3
#define Y2 5
#define R2 4
#define SIZE_AR 5
#define SWITCH 3
#define LEVEL_LIGHT 150
int i1 = 0;
volatile boolean butOn = false;
int lightVal = 0;
//********************************************-- сценарии --**************************************************
//словарь состояний по разрядам:
//автомобильный: красный, желтый, зелёный; пешеходный: красный, зеленый
//один байт - одно состояние
uint8_t g1r = 0b00110; //машинам зелёный, пешеходма красный]
uint8_t g0r = 0b00010; //
uint8_t r11r = 0b10010;//красный и машинам и пешеходам
uint8_t r1g1 = 0b10001;//машинам красный, пешеходам зелёный
uint8_t r1g0 = 0b10000;
uint8_t ryr = 0b11010;
uint8_t y1 = 01000;
uint8_t y0 = 0;
//********************************************-- класс состояний --********************************************
enum class Stat {
  AUTO, //0
  PED, //1
  NOPED, //2
  FULLAUTO, //3
  MANUALOFF, //4
};
//*********************************************
volatile Stat stat = Stat::AUTO;
uint32_t oldTime = 0;
int i = 0;
uint8_t arPin[] = {6, 4, 2, 10, 9, 11};
//*********************************************
void butt() {
  detachInterrupt(digitalPinToInterrupt(SWITCH));
  butOn = true;
}
//*********************************************
void testLed() {
  for (uint16_t n = 0; n < sizeof(arPin); ++n) {
    for (uint16_t m = 0; m < sizeof(arPin); ++m) {
      digitalWrite(arPin[m], m == n);
      delay(10);
    }
    delay(500);
  }
}
//************************************************
void setup() {
  for (uint16_t n = 0; n < sizeof(arPin); ++n)
    pinMode(arPin[n], OUTPUT);
  pinMode(SWITCH, INPUT);

  Serial.begin(9600);
  testLed();
  attachInterrupt(digitalPinToInterrupt(SWITCH), butt, FALLING);
  for (uint16_t m = 0; m < sizeof(arPin); ++m)
    digitalWrite(arPin[m], LOW);
}
//***********************************************
struct Step {   //шаг сценария
  uint32_t dT;  //время задержки
  uint8_t scenario; //состояние
};
//сценарий - последовательность шагов состояний
Step scene1[] = {
  {4000, g1r},
};
Step scene2[] = {
  {250, g0r},
  {250, g1r},
  {250, g0r},
  {250, g1r},
  {250, g0r},
  {250, g1r},
  {3000, r1g1},
  {250, r1g0},
  {250, r1g1},
  {250, r1g0},
  {250, r1g1},
  {250, r1g0},
  {250, r1g1},
  {2000, ryr},
};
Step sceneAUTO[] = {
  {4000, g1r},
  {250, g0r},
  {250, g1r},
  {250, g0r},
  {250, g1r},
  {250, g0r},
  {250, g1r},
  {3000, r1g1},
  {250, r1g0},
  {250, r1g1},
  {250, r1g0},
  {250, r1g1},
  {250, r1g0},
  {250, r1g1},
  {1000, ryr},
};
Step sceneMANOFF[] = {
  {250, y1},
  {250, y0},
};
//************************************************
int func(Step * scene, int sizeAr, int& i) {
  boolean flag = false;
  for (int l = 0, k = 16; l < SIZE_AR; ++l, k/=2) {
    digitalWrite(arPin[l], scene[i].scenario&k);
  }
  if (millis() - oldTime > scene[i].dT) {
    oldTime = millis();
    if (++i >= sizeAr) {
      i = 0;
      flag = true;
    }
  }
  return flag;
}
//************************************************
void loop() {
  lightVal = analogRead(A0);
  switch (stat) {
    case Stat::AUTO:
      if (func(scene1, sizeof(scene1) / sizeof(Step), i1) && (butOn || lightVal < LEVEL_LIGHT)) {
        if (butOn) {
          stat = Stat::PED;
        } else {
          stat = Stat::FULLAUTO;
        }
      }
      break;
    case Stat::PED:
      if (func(scene2, sizeof(scene2) / sizeof(Step), i1)) {
        stat = Stat::NOPED;
      }
      break;
    case Stat::NOPED:
      if (func(scene1, sizeof(scene1) / sizeof(Step), i1)) {
        stat = Stat::AUTO;
        attachInterrupt(digitalPinToInterrupt(SWITCH), butt, FALLING);
        butOn = false;
      }
      break;
    case Stat::FULLAUTO:
      if (func(sceneAUTO, sizeof(sceneAUTO) / sizeof(Step), i1)) {
        if (lightVal > LEVEL_LIGHT) {
          stat = Stat::AUTO;
        }
      }
      break;
  }
}