4.1 Diagrama de componentes do alarme
Antes de iniciar a conversão, vamos revisar o diagrama de componentes:
4.2 Criando a classe Timer
Vamos nos concentrar no componente Timer, que possui duas portas: iniciar e timeout.
Estes são os nossos dois métodos públicos, que podem ser acessados em qualquer escopo.
Assim, podemos declarar a classe Timer e seus métodos no arquivo timer.h
#ifndef TIMER_H_INCLUDED
#define TIMER_H_INCLUDED
class Timer {
public:
Timer();
void iniciar(int controle);
int timeout();
private:
int situacao;
unsigned long horaInicio;
};
#endif // TIMER_H_INCLUDED
onde adicionamos dois membros privados,situação
e horaInicio
, e o construtor.
Os dois membros estão encapsulados, uma vez que só podem ser acessados dentro dos métodos da instância.
Note que é recomendado utilizar o padrão de iniciar o nome da classe com letra maiúscula.
As definições podem ser adicionadas no arquivo timer.cpp
#include <Arduino.h>
#include "definicoes_sistema.h"
#include "timer.h"
#define TEMPO 10
Timer::Timer()
{
situacao = false;
}
void Timer::iniciar(int controle)
{
situacao = controle;
if (controle)
{
horaInicio = millis();
}
}
int Timer::timeout()
{
if (situacao == false)
{
return false;
}
if(millis() - horaInicio > TEMPO*1000)
{
return true;
}
return false;
}
onde Arduino.h
foi incluído para poder utilizar as função millis
.
4.3 Lista de inicialização de membros (opcional)
Observe que a variável situacao
foi inicializada dentro do corpo do construtor.
Neste caso, a variável é primeiramente inicializada sem valor definido e depois o seu valor é modificado com o operador de atribuição.
Ou seja, é algo similar a
int situacao;
int main() {
situacao = false;
...
O que é ineficiente, pois estamos atribuindo valores duas vezes para a variável. O ideal seria escrever
int situacao = false;
Para membros de classes, um comportamento parecido com o código acima pode ser obtido utilizando a lista de inicialização de membros. No caso do componente Timer, isso significa que a definição do construtor deve ser mudada para:
Timer::Timer() : situacao(false)
{
}
Note que, para membros com modificador const
ou para usar a sintaxe inicialização de array, é obrigatório o uso da lista de inicialização de membros.
4.4 Utilizando o componente Timer
Para utilizar a nova implementação do componente, vamos criar uma instância da classe no arquivo principal (p. ex. alarme.ino
)
#include "definicoes_sistema.h"
#include "comunicacao.h"
#include "ihm.h"
#include "senhas.h"
#include "sirene.h"
#include "timer.h"
Timer tmr;
...
Depois, é necessário modificar a função executarAcao()
int executarAcao(int codigoAcao)
{
int retval;
retval = NENHUM_EVENTO;
if (codigoAcao == NENHUMA_ACAO)
return retval;
switch(codigoAcao)
{
case A01:
tmr.iniciar(true);
break;
case A02:
sne_bip();
com_notificar("Alarme em alerta");
tmr.iniciar(false);
break;
case A03:
com_notificar("Alarme desacionado");
tmr.iniciar(false);
break;
case A04:
com_notificar("Alarme desacionado");
break;
case A05:
tmr.iniciar(true);
break;
case A06:
sne_acionar(true);
com_notificar("Invasao");
tmr.iniciar(false);
break;
case A07:
com_notificar("Alarme desacionado");
tmr.iniciar(false);
sne_acionar(false);
break;
} // switch
return retval;
} // executarAcao
Para finalizar o aprimoramento do código, idealmente todos os componentes devem ser convertidos para classes. Isto fica como exercício.
No caso de dúvidas, o código final da aplicação pode ser consultado em
https://gitlab.uspdigital.usp.br/andre.kubagawa/alarme_arduino/-/tree/master/alarme_classes
4.5 Adaptando para o padrão de biblioteca do Arduino (opcional)
As bibliotecas do Arduino, como a Serial, são definidas utilizando classes. No entanto, para utilizá-las, às vezes é necessário instanciar objetos, principalmente se não existem argumentos no construtor. Isso porque, ao incluir o header, já é instanciado um único objeto, que é utilizado pelo usuário para acessar os métodos da biblioteca.
Vamos transformar o componente Timer em uma biblioteca do Arduino.
Para isso, precisamos primeiro declarar a instância no escopo global no arquivo timer.h
#ifndef TIMER_H_INCLUDED
#define TIMER_H_INCLUDED
class Timer {
public:
Timer();
void iniciar(int controle);
int timeout();
private:
int situacao;
unsigned long horaInicio;
};
extern Timer tmr;
#endif // TIMER_H_INCLUDED
Já no arquivo timer.cpp
é necessário adicionar a linha
Timer tmr;
para definição da variável global tmr
.
Por fim, no arquivo principal do projeto (alarme.ino
), remova a linha
Timer tmr;
Note que, para simular uma biblioteca, o nome da instância deve iniciar com letra maiúscula.
No nosso caso, o mais apropriado seria utilizar o nome Timer
para a instância.
Para tanto, deveríamos modificar o nome da classe para não coincidir o do objeto.
Isto fica como exercício, assim como a adaptação dos outros componentes para bibliotecas Arduino.
Bibliografia para este capítulo
- Código fonte do alarme no Arduino (com classes): https://gitlab.uspdigital.usp.br/andre.kubagawa/alarme_arduino/-/tree/master/alarme_classes
- Capítulo 12 da "Apostila de Introdução à linguagem C" - Orientação a objetos no C++: disponível na página do e-disciplinas de PMR3402-2021.