#include <Arduino.h>  // Импорт стандартной библиотеки arduino
#include "Blinker.h" // Импорт класса мигающего светодиода

#define TEST_MODE 0

#define FillScale 0
#define HALFPERIOD 500

/**
 * Объявление и инициализация переменных
 */

// Экземпляры объектов мигающих светодиодов
Blinker Led13;
Blinker Led4, Led11; 

bool I_UP = 0,      // Флаг режима движения вверх
      I_DN = 0,    // Флаг режима движения вниз
      I_ZAP = 0,   // Флаг аварии
      TAKT =0;     // Флаг наличия переднего фронта тактового сигнала

uint8_t I_RD = 0;   // Режим работы контроллера

uint8_t SCENE = 0;  // Номер сцены




bool ALARM = false; // Флаг аварии
int a0,       // Сырые значения с аналоговых входов
    a1,
    a2,
    a3,
    a7;


/**
 * Объявление функций (прототипов)
 * Тут просто перечислены все функции, которые в принципе существуют.
 * Если этого не сделать, то в некоторых версиях компилятора будет ошибка,
 * так как функции будут вызываться раньше их объявления и определения
 */
void setup();         // Инициализирующая функции при старте МК (или сбросе)
void loop();          // Итерация бесконечного цикла работы ПО

void InitAnalog();    // Инициализация аналоговых пинов
void InitDigital();   // Инициализация цифровых пинов
void ReadMode();      // Чтение режима работы

void InitIndication();// Индикация инициализации прошивки 

void AnalogRead();    // Чтение аналоговых пинов
void TaktDetect();    // Чтение тактового импульса
void AlarmDetect();   // Определение условий перехода в режим "авария" и выхода из него
void SceneControl();  

void LedFillScale(uint8_t);
void LedFillScale0();
void LedFillScale1();
void LedFillScale2();
void LedFillScaleAlarm(); 


/**
 * Определение функций (логика)
 */


//===============================================================================================================================================
//=================  Нормальный режим работы  ===================================================================================================
//===============================================================================================================================================
#if TEST_MODE == 0

// Функция инициализации
  void setup()
  {
    Serial.begin(9600);   // Инициализация последовательного порта (для терминала)
    InitAnalog();         // Инициализация аналоговых входов
    InitDigital();        // Инициализация цифровых выходов
    ReadMode();           // Чтение режима работы
    InitIndication();     // Инициализирующая индикация
  }

  // Итерация бесконечного цикла
  void loop()
  {
    AnalogRead ();      // Читаем входы и тактовые импульсы
    AlarmDetect ();     // Проверка режима аварии переключает переменную ALARM

    // В нормальном режиме работы
    if ( ALARM == false )
    {
      TaktDetect ();    // Чтение тактового импульса
      if ( TAKT )
      {
          SceneControl ();          // Управление номером сцены
                                    // А также управление 4 и 11 светодиодами
          LedFillScale(FillScale);  //Управление всеми остальными светодиодами
      }
    }
    // В режиме аварии
    else
      LedFillScaleAlarm();

    // Фоновые методы, обеспечивающие моргание
    Led4.tick();
    Led11.tick();
    Led13.tick();
  }

#else
//===============================================================================================================================================
//===================  Тестовый режим работы  ===================================================================================================
//===============================================================================================================================================
  // Функция инициализации
  void setup()
  {
    Serial.begin(9600);
    InitAnalog();
    InitDigital();

  }

  // Итерация бесконечного цикла
  void loop()
  {

    AnalogRead ();    // Читаем входы и тактовые импульсы

    // D9 повторяет A0; D6=A1; D8=A3; D7=A7; A2
    digitalWrite(9, a0);
    digitalWrite(6, a1);
    digitalWrite(8, a3);
    digitalWrite(7, a7);
  }

#endif

//===============================================================================================================================================
//=================  Инициализирующие функции  ==================================================================================================
//===============================================================================================================================================
// Инициализация аналоговых пинов
void InitAnalog()// конфигурирование аналоговых пинов
{
  pinMode(A0, INPUT); //    I.UP
  pinMode(A1, INPUT); //    I.DN
  pinMode(A2, INPUT); //    I.Rd
  pinMode(A3, INPUT); //    I.TAKT, тактовый генератор
  // Не используются
  pinMode(A4, OUTPUT); digitalWrite(A4, LOW);//   OR4
  pinMode(A5, OUTPUT); digitalWrite(A5, LOW);//   OR5
  pinMode(A6, OUTPUT); digitalWrite(A6, LOW);//   OR6

  pinMode(A7, INPUT); //    I.ZAP
}

// Инициализация цифровых пинов
void InitDigital()
{
  pinMode(2, OUTPUT);         // O.MOTOR реле светодиод Bl (голубой)
  pinMode(3, OUTPUT);         // O.DN реле концевое светодиод Bl (голубой)
  Led4.init(4, HALFPERIOD);   // VD1-R светодиод Red Flash
  pinMode(5, OUTPUT);         // VD2-L светодиод Green
  pinMode(6, OUTPUT);         // VD3-Y светодиод
  pinMode(7, OUTPUT);         // VD4-Y светодиод
  pinMode(8, OUTPUT);         // VD5-Y светодиод
  pinMode(9, OUTPUT);         // VD6-Y светодиод
  pinMode(10, OUTPUT);        // VD7-L светодиод Green
  Led11.init(11, HALFPERIOD); // VD8-R светодиод Red Flash
  pinMode(12, OUTPUT);        // O.UP реле концевое светодиод Bl (голубой)
  Led13.init(13);             // Светодиод на плате
}

// Чтение режима работы контроллера при его старте
void ReadMode()
{
  int a2 = analogRead (A2); // Читаем уровень с А2

  if( a2 <= 300)            // Двухпозиционный
    I_RD = 0;
  else if ( a2 >= 700 )     // Трехпозиционный
    I_RD = 2;
  else                      // Пускатель
    I_RD = 1;
  
  Serial.print("Режим ");
  Serial.println(I_RD);
}

// Индикация успешной инициализации прошивки
void InitIndication()
{
  static int Period = 500;

  Led13.on();
  delay( Period * 2 );  // Тире
  Led13.off();
  delay( Period );      // выкл
  Led13.on();
  delay( Period );      // Точка
  Led13.off();
  delay( Period );      // выкл
  Led13.on();
  delay( Period * 2 );  // Тире
  Led13.off();
  delay( Period );      // выкл
  Led13.on();
  delay( Period * 2 );  // Тире
  Led13.off();
  delay( Period );      // выкл

  for (int i = 4; i != 12; ++i)
    digitalWrite(i, HIGH);
  
  delay ( 1000 );

  for (int i = 4; i != 12; ++i)
    digitalWrite(i, LOW);
}



//===============================================================================================================================================
//=====================  Функции - тикеры  ======================================================================================================
//===============================================================================================================================================
// Чтение аналоговых входов с гистерезисом
void AnalogRead ()  
{
  // Чтение входов-ключей с гистерезисом
  a0 = analogRead (A0);
  a1 = analogRead (A1);
  a2 = analogRead (A2);
  
  a3 = analogRead (A3);
  a7 = analogRead (A7);

  // Определение значений I_UP I_DN в зависимости от режима работы контроллера
  switch (I_RD)
  {
    case 0:     // Двухпозиционный
      I_UP = a0 > 512;
      I_DN = !I_UP;
      break;

    case 1:     // Пускатель
      if ( a0 < 512 )
        I_UP = true;
      else if ( a1 < 512 )
        I_UP = false;
      
      if ( a1 < 512 )
        I_DN = true;
      else if ( a0 < 512 )
        I_DN = false;
      break;

    case 2:     // Трехпозиционный
      I_UP = a0 < 512;
      I_DN = a1 < 512;
      break;

    default:
      break;
  }

  Serial.print("UP ");
  Serial.print(I_UP);
  Serial.print("; ");
  Serial.print("DN ");
  Serial.println(I_DN);
}

// Определение условий перехода в режим "авария"
void AlarmDetect()
{
  static bool a7_prev = false;

  // Проверка сигналов в режиме НЕ КЗ
  if ( I_RD > 0 )
    if ( I_DN && I_UP )
      ALARM = true;
  
  // Проверка аварийного сигнала
  if ( a7 > 512 )
    ALARM = true;
  
  if (ALARM)
  {
    Led4.start();
    Led11.start();
    Serial.println("ALARM!");

    if ( a7 < 512 && a7_prev == true )
    {
      ALARM = false;
      Serial.println("ALARM END!");
    }
  }

  a7_prev = a7 > 512;
}

// Определенеи тактового импульса
void TaktDetect()
{
  static const int GIST = 200;
  static bool TAKT_PRED = 0;  // Только при инициализации присваивается значение
                              // static сохраняет значение переменной между вызовами функции
                              // Такой аналог глобальной переменной, но с ограниченим области видимости
  bool TAKT_TEK = TAKT_PRED;  // Текущее значение пина
  
  // Смотрим текущее значение с учетом мертвой зоны
  if ( a3 > 512 + GIST )
    TAKT_TEK = true;
  else if ( a3 < 512 - GIST )
    TAKT_TEK = false;
  
  // Ловим фронт
  if ( TAKT_PRED == false && TAKT_TEK == true )
  {
    TAKT = true;
    delay( 50 );              // Заморозка при фронте
  }
  else if ( TAKT_PRED == true && TAKT_TEK == false )
    TAKT = false;
  
  TAKT_PRED = TAKT_TEK;           // Запоминаем значение
}

// Управление номером сцены
// Управление сценой заключается в запуске соответствующего светодиода
void SceneControl()
{
  if ( I_UP == false && I_DN == false )
  {
    Led4.stop();
    Led11.stop();
    Led11.on();
    Led4.on();
  }
  else if ( I_UP && !I_DN )
  {
    SCENE = constrain(SCENE + 1, 0, 5);
    Led11.start();
    Led4.stop();
  }
  else if ( !I_UP && I_DN )
  {
    SCENE = constrain(SCENE - 1, 0, 5);
    Led11.stop();
    Led4.start();
  }
  else
  {
    ALARM = true;
    Led4.start();
    Led11.start();
  }

  if ( SCENE == 0 || SCENE == 5 )
  {
    Led4.stop();
    Led11.stop();
  }
    
  TAKT = 0; // Поскольку фронт отработан, сбрасываем его флаг
  Serial.print("Scene ");
  Serial.println(SCENE);
}

void LedFillScale (uint8_t mode)
{
  // Состояние не зависит от режима работы
  digitalWrite(2, SCENE >= 3);
  digitalWrite(3, SCENE == 0);
  // 4 управляется в SceneControl()
  digitalWrite(5, SCENE == 0);
  // 6-9 управляется в зависимости от сцены
  digitalWrite(10, SCENE == 5);
  // 11 управляется в SceneControl()
  digitalWrite(12, SCENE == 5);

  switch (mode)
  {
    case 0:
      LedFillScale0();
      break;
    case 1:
      LedFillScale1();
      break;
    case 2:
      LedFillScale2();
      break;

    default:
      break;
  }

  
}

void LedFillScale0()
{
  digitalWrite(6, SCENE == 1);
  digitalWrite(7, SCENE == 2);
  digitalWrite(8, SCENE == 3);
  digitalWrite(9, SCENE == 4);
}
void LedFillScale1()
{
  digitalWrite(6, SCENE >= 1);
  digitalWrite(7, SCENE >= 2);
  digitalWrite(8, SCENE >= 3);
  digitalWrite(9, SCENE >= 4);
}
void LedFillScale2()
{
  digitalWrite(6, SCENE == 1 || SCENE == 2);
  digitalWrite(7, SCENE == 2 || SCENE == 3);
  digitalWrite(8, SCENE == 3 || SCENE == 4);
  digitalWrite(9, SCENE == 4 || SCENE == 5);
}
void LedFillScaleAlarm()
{
  // Код индикации 4 и 11 уже запущены в AlarmDetect
} 
0.3