Сигнальные огни дорожной полиции

Ещё один пример использования разработанных нами классов. В данном примере мы рассмотрим программу выполняющую управление светодиодами в режиме огней на машине дорожной полиции: два красных, два синих и небольшая пауза перед началом следующего цикла.

Вариантов решения этой задачи много, но мы расширим возможности класса softLed. Класс softLed умеет плавно включать и плавно выключать один светодиод. Порт к которому подключен светодиод, длительность изменения состояния и начальное состояние класса softLed указывается при инициализации.

Давайте определим производный класс Sl:

class Sl : public softLed {
  private:
    int Stat;
    int nFlash;
  public:
    Sl(int pin, int nFlash = 2):softLed(pin, 5, false, 20){Stat = 0;this->nFlash = nFlash;}
    bool action() override;
    void SlOn();
};
//************************
void Sl::SlOn(){
  Stat = 0;
  ledOn();
}
//**************************
bool Sl::action(){
  Stat++;
  if(Stat < nFlash*2)ledOn();
}

При инициализации класса Sl, в качестве параметров от получает номер порта (пин) и число вспышек в одном цикле. Число вспышек сохраняется в переменной члене класса nFlash. Если, при инициализации класса, значение nFlash не указано, по умолчанию, в конструктор передается число 2. Номер порта передается в базовый класс. Так же в базовый класс передаются параметры определяющие число шагов и время каждого шага при плавном включении.

Кроме конструктора, класс содержит две функции: SlOn и action(). Обратите внимание, после объявления функции action(), написано ключевое слово override, указывающее, что эта функция переопределяет виртуальную функцию базового класса. (класс softLed содержит виртуальную функцию action(), которую мы переопределяем в производном классе).

Функция SlOn запускает однократный цикл работы. Светодиод, управляемый нашим классом, должен мигнуть nFlash раз. Для этого мы будем использовать счетчик числа включений светодиода Stat. При вызове функции SlOn, счетчик Stat сбрасывается в 0 (т.е. его значение становится менее числа полупериодов, необходимых для выполнения данной задачи).

Пока значение Stat меньше, чем nFlash * 2, при вызове виртуальной функции action(), мы будем вызывать функцию ledOn() класса softLed для изменения состояния светодиода (плавного включения или выключения).

Для работы программы мы определим динамический массив элементов, нашего нового, класса Sl. Число подключенных светодиодов вычисляется исходя из числа значений массива arPins. Таким образом, если мы хотим сделать не 2, а другое число светодиодов, нам нужно дописать в массив портов нужные значения: static const int arPins[] = {9, 11}; – 2 светодиода, static const int arPins[] = {9, 10, 11}; -три светодиода.

Число светодиодов сохраняем в глобальной переменной nLed = sizeof(arPins) / sizeof(int);

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

#include "softLed.h"
#include "timer.h"

#define TIME_RB 700

static const int arPins[] = {9, 11}; 
int nLed = 0;
//***********************************
class Sl : public softLed {
  private:
    int Stat;
    int nFlash;
  public:
    Sl(int pin, int nFlash = 2):softLed(pin, 5, false, 20){Stat = 0;this->nFlash = nFlash;}
    bool action() override;
    void SlOn();
};
//************************
void Sl::SlOn(){
  Stat = 0;
  ledOn();
}
//**************************
bool Sl::action(){
  Stat++;
  if(Stat < nFlash*2)ledOn();
}
//******************************
int Status = 0;
timer *Timer;
Sl **sL;
//----------------------------------
void setup() {
  nLed = sizeof(arPins) / sizeof(int);
  sL = new Sl*[nLed];
  for( int n = 0; n < nLed; ++n){
    sL[n] = new Sl(arPins[n], 3);
  }
  Timer = new timer();
}
//**************************************
void loop() {
  if(!Timer -> getTimer()){
    Timer->setTimer(TIME_RB);
    sL[Status]->SlOn();
    if(++Status >= nLed)Status = 0;
  }
    sL[Status]->cycle();
}

softLed.h

#ifndef SOFTLED_H
#define SOFTLED_H
#include "timer.h"
/*
класс softLed обеспечивает плавное включение и выключение светодиода,
выполняя, при этом, функции триггера состояния.
Состояние изменяется на противоположное при каждом вызове функции ledOn().
*/
class softLed{
  private:
    int n;        //текущая итерация
    int pin;
  protected:
    int steps;    //число шагов
    int stat;     //состояние светодиода
    unsigned long dt; //длительность задержки
    timer *Timer;
    virtual bool action();
  public:
    softLed(int pin, int steps = 30, bool stat = true, unsigned long dt = 50);    //конструктор
    ~softLed();   //деструктор
    bool ledOn(); //включение
    bool cycle();   //цикл в цепи
    int getPin();
};
#endif

softLed.cpp

#include "Arduino.h"
#include "softLed.h"
#include "timer.h"

//***********************
softLed::softLed(int pin, int steps, bool stat, unsigned long dt){
  this->pin = pin;
  this->n = 0;
  this->steps = steps;
  this->stat = stat;
  this -> dt = dt;
  Timer = new timer();
  pinMode(pin, OUTPUT);
  digitalWrite(pin, stat);
}
//***********************
softLed::~softLed(){
  delete Timer;
}
//***********************
int softLed::getPin(){
  return pin;
}
//***********************
bool softLed::ledOn(){
  n = steps;
  stat = !stat;
  return n > 0;
}
//***********************
bool softLed::action(){
  return false;
}
//***********************
bool softLed::cycle(){
  if(n && !Timer->getTimer()){
      Timer->setTimer(dt); //запускаем таймер на следующий период
      int levelLight = map(n-1, 0, steps-1, 0, 255); 
      levelLight = stat? 255 - levelLight: levelLight;
      analogWrite(pin, levelLight);
      n--;
      if(!n)action(); 
  } else 
  return n > 0;
}

timer.h

#ifndef MYLIBRARY_H
#define MYLIBRARY_H

//class timer;
class timer {
  private:
     unsigned long ulTimer;
  public:
     timer(unsigned long dT);
     timer();
     //*****************************       установка времени задержки таймера
     bool setTimer(unsigned long dT);
     //***************************** запрос состояния таймера. Возвращает true, если включен.
     bool getTimer();

};
#endif

timer.cpp

#include  "Arduino.h"
#include "timer.h"

timer::timer(unsigned long dT){ ulTimer = dT;}
timer::timer(){ulTimer = 0;}
//***********************************************************************
bool timer::setTimer(unsigned long dT){
bool bActive = false;
  if(ulTimer == 0){           //если таймер не включен
      bActive = true;
      ulTimer = millis() + dT;  //задаем значение - сумма текущего времени
  }                               //и заданного времни ожидания
  return bActive;
}
//***************************** запрос состояния таймера. Возвращает true, если включен.
bool timer::getTimer(){
  if(ulTimer <= millis()){   //проверяем состояние таймера
      ulTimer = 0;            //если время истекло, сбрасываем заданное значение, выключаем таймер.
  }
  return ulTimer != 0;       //возвращаем логическое состояние таймера, если включен - true
}