Модернизация индикации

Давайте применим наши знания о контейнерах переписав систему индикации в программе «КОНТРОЛЬ ИЗМЕНЕНИЯ ТЕМПЕРАТУРЫ». Индикация построена на базе класса Vled. Каждый экземпляр класса, при инициализации получает номер пина, к которому подключен светодиод. Число миганий каждого индикатора задается либо при инициализации, либо при вызове функции включения индикатора.

Как это было:

Vled vledBlue(LED_PIN_BLUE);
Vled vledGreen(LED_PIN_GREEN);
Vled vledRed(LED_PIN_RED);
//Объявление и инициализация трёх индикаторов. Число миганий, по умолчанию 2. 
//Другое число миганий может быть задано вторым параметром

Включение любого индикатора осуществляется вызовом функции члена класса Vled turnOnVLed().

vledRed.turnOnVLed();
vledGreen.turnOnVLed(8);
vledBlue.turnOnVLed(4);
//в функцию может быть передано число миганий, или оставить значение установленное при инициализации.

Для работы индикаторов необходимо циклическое обновление внутреннего состояния. Класс Vled использует встроенный таймер для управления состояния светодиодами. Т.е. нам нужно передавать управление индикатору в каждом цикле основной программы контроллера.

void loop(){
...............
vledBlue.cycleVLed();
vledGreen.cycleVLed();
vledRed.cycleVLed();
}

Этих строк кода достаточно для управления тремя индикаторами.

Для добавления дополнительного индикатора нужно выполнить два действия: объявить его и добавить в цикл loop. После этого индикатор можно включать, по мере необходимости, в процессе выполнения программы. Однако если забыть добавить вызов функции cycleVLed() в цикл loop(), можно потратить много времени на поиск причины, почему не работает индикатор.

Решение

Мы с вами начали проходить тему «Контейнеры». Посмотрите как изящно можно решить эту задачу применяя новые знания.

Создадим новый класс ArVled представляющий собой “массив” индикаторов. Массив в кавычках потому, что мы будем использовать контейнер из стандартной библиотеки std::map.

std::map – отсортированный ассоциативный контейнер, содержащий пары ключ-значение с уникальными ключами. Ключи отсортированы с помощью функции сравнения Compare

#ifndef ARVLED_H 
#define ARVLED_H
#include <ArduinoSTL.h>
#include "Vled.h"
#include <map>
#endif

using mapVled = std::map<const char*, Vled*>;  //исключительно для красоты

class ArVled : public mapVled {
  public:
    void cycle();
};

Новый класс добавляет к функционалу контейнера функцию cycle();

void ArVled::cycle(){
  for(auto it: *this)it.second->cycleVLed();
}
//эта функция последовательно вызывает функцию cycleVLed() у каждого элемента массива.
ArVled arVled; //Глобальная переменная 
void setup(){
...
  arVled.insert(std::make_pair(static_cast<const char*>("BLUE"), new Vled(LED_PIN_BLUE) ) );
  arVled.insert(std::make_pair(static_cast<const char*>("RED"), new Vled(LED_PIN_RED) ) );
  arVled.insert(std::make_pair(static_cast<const char*>("GREEN"), new Vled(LED_PIN_GREEN) ) );
}

Выше показано, как можно добавить в контейнер три индикатора.

Для включения индикатора достаточно обратиться к нему по “Имени”:

switch(dsb20->getEr().second){
    case Errors::CONNECT:arVled["RED"]->turnOnVLed(20);arVled["GREEN"]->turnOnVLed(20);arVled["BLUE"]->turnOnVLed(20);break;
    case Errors::DT_UP:arVled["RED"]->turnOnVLed(8);break;
    case Errors::MAXVAL:arVled["RED"]->turnOnVLed();break;
    case Errors::DT_DOWN:arVled["BLUE"]->turnOnVLed(8);break;
    case Errors::MINVAL:arVled["BLUE"]->turnOnVLed();break;
    default:break;
}

Работу всех индикаторов обеспечивает вызов функции cycle() класса ArVled.

void loop(){
.....
 arVled.cycle(); //передача управления каждому индикатору.
}

Всё. Жду вопросов.

//****************************************************************************** Vled

#ifndef VLED_H
#define VLED_H
#include "timer.h"

class Vled {           //аппаратно - виртуальный индикатор
  private:
    int pin;              //номер пина, к которому подключен светодиод
    int nFlash;           //число миганий светодиода этого индикатора
    unsigned long time_x; //длительность включения светодиода
  protected:
    int statTimer;        //состояние индикатора, текущий полупериод
    Timer vTimer;         //внутренний таймер виртуального индикатора
  public:
    bool turnOnVLed();       //включение индикатора
    bool turnOnVLed(int);       //включение индикатора
    bool cycleVLed();     //обеспечение работы индикатора
    Vled(int, int nFlash = 2, unsigned long time_x = 180);
};

#endif
//---------------------------------------------------------- vLed.cpp
#include "Arduino.h"
#include "vLed.h"
#include "timer.h"
//++++++++++++++++++++++++++++++++++++++++++++++++++++
Vled::Vled(int pin, int nFlash= 2, unsigned long time_x = 180){
    pinMode(pin, OUTPUT);
    this->pin = pin;
    this->nFlash = nFlash;    //фиксированное число миганий этого индикатора
    this->time_x = time_x;    //длительность свечения
    this->statTimer = 0;      
}
//****************************************************
bool Vled::turnOnVLed(){   //включение индикатора c фиксированным числом миганий
  return turnOnVLed(this -> nFlash);
}
//****************************************************
bool Vled::turnOnVLed(int nF){//включение индикатора с заданным числом миганий
  if(this->statTimer == 0){   //если индикатор свободен
    this->statTimer = nF * 2; //переводим в полупериоды
    this->vTimer.setTimer(this->time_x);
    return true;              //индикатор включен
  } else return false;        //индикатор занят
}
//************************************
bool Vled::cycleVLed(){               //обеспечение работы индикатора
  if(this->statTimer > 0 && !vTimer.getTimer()){ //определяем состояние таймера и если выключился,
      this->vTimer.setTimer(this->time_x);           //запускаем заново
      int st = (this->statTimer & 1) == 0;            //состояние светодиода определяется младшим разрядом счетчика индикатора
      digitalWrite(this->pin, st);
      this->statTimer--;             //уменьшаем счетчик индикатора
  }
  return this->statTimer > 0;
}