sistema de gerenciamento de consumo de energia elÉtrica ... · o acompanhamento de consumo de...
TRANSCRIPT
SISTEMA DE GERENCIAMENTO DE CONSUMO DE ENERGIA
ELÉTRICA EM TOMADAS
Thiago Costa Lobo
Projeto de Graduação apresentado ao Curso
de Engenharia Eletrônica e de Computação
da Escola Politécnica, Universidade Federal
do Rio de Janeiro, como parte dos requisitos
necessários à obtenção do título de Engenheiro.
Orientador: Carlos Fernando Teodósio Soares
Rio de Janeiro
Agosto de 2017
UNIVERSIDADE FEDERAL DO RIO DE JANEIRO
Escola Politécnica - Departamento de Eletrônica e de Computação
Centro de Tecnologia, bloco H, sala H-217, Cidade Universitária
Rio de Janeiro - RJ CEP 21949-900
Este exemplar é de propriedade da Universidade Federal do Rio de Janeiro, que
poderá incluí-lo em base de dados, armazenar em computador, microfilmar ou adotar
qualquer forma de arquivamento.
É permitida a menção, reprodução parcial ou integral e a transmissão entre bibli-
otecas deste trabalho, sem modificação de seu texto, em qualquer meio que esteja
ou venha a ser fixado, para pesquisa acadêmica, comentários e citações, desde que
sem finalidade comercial e que seja feita a referência bibliográfica completa.
Os conceitos expressos neste trabalho são de responsabilidade do(s) autor(es).
iv
DEDICATÓRIA
Esse trabalho é dedicado ao meu avô, Antonio Lobo. Dele veio grande parte da
influência que me fez escolher o caminho da Engenharia. Sinto saudade das nossas
conversas pós-almoço sobre momento linear, eletricidade e temas filosóficos.
Sei que ele estaria orgulhoso.
v
AGRADECIMENTO
Agradeço, acima de tudo, aos meus pais: Antonio e Sônia. Foi me espelhando na
honestidade e no trabalho duro que eles sempre desempenharam que encontrei, por
diversas vezes, motivação para continuar no difícil caminho do curso de Engenharia
Eletrônica e de Computação da UFRJ. Hoje, olhando para trás, percebo que jamais
teria chegado até aqui sem seu apoio. Do fundo do meu coração, obrigado.
Agradeço, também, à minha irmã Daniela, minha melhor confidente e conselheira.
Agradeço aos meus avós, com os quais adoraria poder compartilhar esse momento.
Agradeço às minhas avós, com as quais compartilho esse momento e por isso sou
muito grato.
Agradeço à minha namorada, Leila, por ter me ajudado a enxergar além do lado
racional das coisas.
Agradeço a cada um dos mestres que compartilharam de seu conhecimento comigo,
especialmente ao meu orientador, Carlos Fernando, cujo magistério é exercido de
maneira ímpar.
Agradeço, finalmente, a todos os amigos com quem compartilhei um pouco desse
caminho. Sem eles, a distância teria sido maior.
vi
RESUMO
O acompanhamento de consumo de energia elétrica é feito, atualmente, apenas
pela observação do medidor de consumo ou do valor que consta nas contas de luz.
Esses métodos não oferecem um nível aceitável de granularidade na apresentação dos
dados, já que condensam toda a quantia de energia utilizada em um único número.
Este trabalho trata do projeto, desenvolvimento, implementação e prototipação
de um sistema de acompanhamento e controle de consumo de energia elétrica base-
ado em três tomadas inteligentes que se comunicam com uma central. Seu objetivo
é viabilizar o acompanhamento de consumo individual de dispositivos elétricos e
eletrônicos residenciais, bem como permitir que sejam desligados e ligados remota-
mente.
As tomadas inteligentes são compostas por um microcontrolador ATmega328, um
relé de 10 A, um sensor de corrente ACS712, um transceptor nRF24L01+, uma
fonte chaveada de 12 V e um sensor de tensão AC personalizado, cujo projeto é aqui
detalhado.
A central é um Raspberry Pi 3, ao qual um transceptor nRF24L01+ é conectado.
Nela, um serviço de comunicação com as tomadas é executado, o qual armazena
dados de consumo num banco de dados MongoDB. Esses dados são expostos por
uma API REST. Uma interface web utiliza essa API para exibir ao usuário detalhes
sobre o consumo de cada tomada, permitindo, também, que o usuário controle-as à
distância.
Palavras-Chave: internet das coisas, energia, elétrica, consumo, tomadas, senso-
res, rede, comunicação, monitoramento
vii
ABSTRACT
Electricity monitoring is a task currently achieved by observing the value shown
by the consumption meter or at the electrical bill. The aforementioned methods
of data presentation offer unoptimal granularity, as they condense all the power
consumption in a single number.
This work deals with the project, development, implementation and prototyping
of an electricity consumption monitoring and control system based on three smart
sockets which communicate with a central. It’s goal is to enable individual con-
sumption monitoring for household electric and electronic devices, as well as remote
activation and deactivation.
Each smart socket is composed by an ATmega328 microcontroler, a 10 A relay, an
ACS712 current sensor, a nRF24L01+ transceiver, a switching 12 V power supply
and a custom AC voltage sensor, whose project is hereby detailed.
The central consists of a Raspberry Pi 3 to which a nRF24L01+ transceiver is
connected. It executes a communication service which exchanges messages with
each of the sockets and stores received data in a MongoDB database. This data is
exposed by a RESTful API. A web application uses the API to present consumption
details and allow users to remotely turn the sockets on and off.
Key-words: internet of things, electricity, consumption, sockets, sensors, network,
communication, monitoring
viii
SIGLAS
ADC - Analog-to-digital Converter
IDE - Integrated Development Environment
HTTP - Hypertext Transfer Protocol
GPIO - General Purpose Input/Output
SSH - Secure Shell
TX - Transmitter
RX - Receiver
CI - Circuito Integrado
USB - Universal Serial Bus
AC - Alternated Current
DC - Direct Current
JSON - JavaScript Object Notation
API - Application Programming Interface
HTML - Hypertext Markup Language
REST - Representational State Transfer
URL - Uniform Resource Locator
URI - Uniform Resource Identifier
CSS - Cascading Style Sheets
DOM - Document Object Model
ix
Sumário
1 Introdução 1
1.1 Tema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.2 Delimitação . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.3 Justificativa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.4 Objetivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.5 Metodologia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
1.6 Materiais . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
1.7 Descrição . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2 Conceitos Teóricos 6
2.1 A Série de Fourier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2 A Rede Elétrica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
2.3 Potência . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
2.4 Amostragem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
3 Módulos de Hardware 12
3.1 Raspberry Pi 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
3.2 Arduino Nano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.3 Transceptor nRF24L01+ . . . . . . . . . . . . . . . . . . . . . . . . . 16
3.4 Sensor de Corrente ACS712 . . . . . . . . . . . . . . . . . . . . . . . 17
3.5 Relé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.6 Fonte Chaveada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.7 Sensor de Tensão . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
3.7.1 Atenuador de Entrada . . . . . . . . . . . . . . . . . . . . . . 21
3.7.2 Amplificador de Instrumentação . . . . . . . . . . . . . . . . . 23
x
3.7.3 Estágio de mudança de offset . . . . . . . . . . . . . . . . . . 25
3.7.4 Geração do sinal offset . . . . . . . . . . . . . . . . . . . . . . 26
3.7.5 Visão geral do Circuito Medidor de Tensão . . . . . . . . . . . 26
4 Módulos de Software 29
4.1 Biblioteca RF24 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
4.2 MongoDB . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
4.3 Node.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
4.3.1 Express . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.3.2 API REST . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
4.4 Materialize CSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.5 jQuery . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
4.6 Chart.js . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
5 Panorama de Funcionamento 37
5.1 Protocolo de Comunicação . . . . . . . . . . . . . . . . . . . . . . . . 37
5.2 Tomadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
5.2.1 Inicialização . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
5.2.2 Calibração e Leitura do Sensor de Corrente . . . . . . . . . . . 40
5.2.3 Calibração e Leitura do Sensor de Tensão . . . . . . . . . . . . 41
5.2.4 Leitura do Medidor de Consumo . . . . . . . . . . . . . . . . . 42
5.2.5 Comunicação . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.2.6 Loop Principal . . . . . . . . . . . . . . . . . . . . . . . . . . 45
5.3 Central . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.3.1 Inicialização do Comunicador . . . . . . . . . . . . . . . . . . 46
5.3.2 Loop Principal do Comunicador . . . . . . . . . . . . . . . . . 48
6 Resultados 52
6.1 Montagem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.2 Análises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
7 Conclusão 59
Bibliografia 61
xi
A Demonstração 1 66
B Códigos-Fonte: Central 67
B.1 communicator.py . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
B.2 my_config_reader.py . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
B.3 config.ini . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
C Códigos-Fonte: Tomadas 73
C.1 arduino_wrapper.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
C.2 current_sensor.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
C.3 current_sensor.ino . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
C.4 voltage_sensor.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
C.5 voltage_sensor.ino . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
C.6 power_meter.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
C.7 power_meter.ino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
C.8 relay.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
C.9 relay.ino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
C.10 transceiver.h . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
C.11 transceiver.ino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
C.12 socket.ino . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
xii
Lista de Figuras
3.1 Diagrama de blocos - central . . . . . . . . . . . . . . . . . . . . . . . . 12
3.2 Diagrama de blocos - tomada . . . . . . . . . . . . . . . . . . . . . . . . 13
3.3 Raspberry Pi 3 e uma moeda para comparação de tamanho . . . . . . . . 14
3.4 Arduino Nano e uma moeda para comparação de tamanho . . . . . . . . 16
3.5 nRF24L01+ e uma moeda para comparação de tamanho . . . . . . . . . . 17
3.6 ACS712 e uma moeda para comparação de tamanho . . . . . . . . . . . . 18
3.7 Relé e uma moeda para comparação de tamanho . . . . . . . . . . . . . . 19
3.8 Fonte chaveada e uma moeda para comparação de tamanho . . . . . . . . 20
3.9 Atenuador de entrada do sensor de tensão AC . . . . . . . . . . . . . . . 21
3.10 Circuito do amplificador de instrumentação . . . . . . . . . . . . . . . . 23
3.11 Estágio de que adapta o nível de offset da tensão produzida pelo medidor
de tensão para o range do ADC do microcontrolador . . . . . . . . . . . . 26
3.12 Esquemático completo do sensor de tensão AC . . . . . . . . . . . . . . . 27
3.13 Simulação das saídas do amplificador de instrumentação e do último estágio
do medidor de tensão, VOUT e VADC , respectivamente, para uma entrada
de 179.6 Vp e 60 Hz . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
4.1 JSON representando o estado de uma tomada . . . . . . . . . . . . . . . 30
4.2 JSON representando uma leitura de consumo de uma tomada . . . . . . . 30
4.3 Visualização das informações referentes à tomada 2 através da interface web 34
6.1 Montagem da central . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
6.2 Montagem de uma tomada inteligente . . . . . . . . . . . . . . . . . . . 53
6.3 Cinco segundos de leituras de potência ativa provenientes de uma lâmpada
incandescente de 42 W . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
xiii
6.4 Cinco segundos de leituras de potência ativa provenientes de uma lâmpada
incandescente de 70 W . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
6.5 Quatro segundos de leituras de potência ativa sem carga ou qualquer tipo
de filtragem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
6.6 Leituras de potência ativa demonstrando o cancelamento de ruído até o
momento em que a carga de 42 W é ativada . . . . . . . . . . . . . . . . 56
6.7 Leituras de potência ativa demonstrando o cancelamento de ruído até o
momento em que a carga de 70 W é ativada . . . . . . . . . . . . . . . . 57
6.8 Interface web exibindo dados de consumo coletados de uma geladeira, de
00:15 às 09:15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
6.9 Interface web exibindo dados de consumo coletados de um ventilador, de
00:15 às 09:15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
xiv
Lista de Tabelas
1.1 Materiais utilizados para realizar o projeto . . . . . . . . . . . . . . . . . 4
3.1 Detalhes dos traços da Figura 3.13 . . . . . . . . . . . . . . . . . . . . . 27
4.1 Exemplos de operações numa API REST sendo acessada na rede local pela
porta 3000 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
4.2 Operações REST implementadas na central . . . . . . . . . . . . . . . . 33
5.1 Formato das mensagens do protocolo de comunicação . . . . . . . . . . . 37
5.2 Especificação das mensagens implementadas no protocolo de comunicação . 39
6.1 Resultados dos testes de discrepância entre o medidor de potência e
um multímetro AC . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
xv
Lista de Algoritmos
5.1 Estrutura de dados que representa uma mensagem . . . . . . . . . . . 38
5.2 Função que transforma uma mensagem num array de bytes . . . . . . 38
5.3 Função que transforma um array de bytes em uma mensagem . . . . 39
5.5 Função responsável pela integração da taxa de consumo . . . . . . . . 43
5.4 Função responsável pela leitura do consumo de energia em W . . . . 44
5.6 Função responsável por responder as mensagens destinadas às tomadas 45
5.7 Inicialização do banco de dados no Comunicador . . . . . . . . . . . . 47
5.8 Loop principal do Comunicador . . . . . . . . . . . . . . . . . . . . . 48
5.9 Função do Comunicador responsável por gerenciar os Relés . . . . . . 49
5.10 Função do Comunicador responsável por gerenciar as leituras de con-
sumo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
xvi
Capítulo 1
Introdução
1.1 Tema
O tema do trabalho é o desenvolvimento da prova de conceito de um sistema de
monitoramento de consumo de energia elétrica para ambientes domiciliares baseado
em tomadas inteligentes, oferecendo a possibilidade de acesso e controle à distância.
Nesse sentido, apresenta soluções para os problemas da medição do consumo pro-
priamente dita, da comunicação entre as tomadas e uma central e da entrega desses
dados ao usuário por meio de uma interface web.
A grande área do projeto é a Internet das Coisas, cujo enfoque é a integração e
automação do funcionamento de equipamentos do dia a dia através da internet [1][2].
Assim sendo, princípios de eletrônica bem como de computação foram abordados.
Dentre eles: instrumentação, arquitetura de backend e frontend, princípios de projeto
de circuitos eletrônicos, sistemas embarcados, protocolos de comunicação etc.
1.2 Delimitação
Por ser uma prova de conceito, o trabalho utilizou extensões no lugar de tomadas,
já que não tratou do desenvolvimento de um "produto final". Além disso, permite
apenas que o usuário acompanhe o consumo em kWh e controle (ligue ou desligue)
cada uma de três tomadas que foram confeccionadas. O acesso à distância está
restrito à rede local e a interface foi desenvolvida para ser acessada pelo navegador
1
Google Chrome sendo executado em Desktops.
A escolha dos circuitos de instrumentação aqui empregados se deu sob o respaldo
de que o sistema será empregado apenas em ambientes domiciliares, onde a rede
elétrica é de 127 VAC, com frequência de 50 ou 60 Hz. Além disso, o sistema não
suporta equipamentos cujo consumo de corrente supere 10 A.
1.3 Justificativa
O Brasil tem testemunhado aumentos consistentes no preço da eletricidade, os
quais devem continuar a acontecer ao longo dos próximos anos [3]. Para o assinante,
a depender de sua posição financeira, esse fato pode implicar em necessidade de
redução de gastos.
Todavia, a tarefa de redução do consumo de energia elétrica pode ser bastante
difícil devido à falta de conhecimento sobre o impacto energético de cada um dos
equipamentos que o assinante utiliza. Ademais, as únicas formas de acompanha-
mento desse tipo de grandeza amplamente empregadas são a observação do medidor
de consumo ou a avaliação da conta de luz mês a mês. As informações providas
por esses métodos são pouco granulares e métodos de redução de consumo que se
baseiem nelas terão efetividade reduzida.
Nesse sentido, a hipótese que o projeto se propõe a verificar é a viabilidade e
a efetividade de um sistema de acompanhamento de consumo de energia elétrica
baseado em tomadas inteligentes. Caso o sistema seja viável e efetivo, soluções mais
complexas poderão ser desenvolvidas, aliando automação de equipamentos às metas
de consumo.
1.4 Objetivos
O objetivo geral é, então, desenvolver um sistema de acompanhamento de consumo
de energia elétrica baseado em tomadas inteligentes e uma central.
2
Os objetivos específicos são:
1. Medir o consumo de energia elétrica com um microcontrolador e sensores
2. Estabelecer uma rede de comunicação entre uma central e as tomadas
3. Registrar os dados na central
4. Implementar uma aplicação web que permita o acompanhamento dos dados e
o controle das tomadas
1.5 Metodologia
Inicialmente, foram desenvolvidas as tomadas inteligentes, empregando integração
de hardware e software embarcado. Um microcontrolador acessa sensores de tensão
e corrente alternada, de forma a obter amostras de potência instantânea, cuja média
permite obter a potência ativa consumida pelo equipamento conectado. A integral
discreta desse valor permite obter o consumo em kWh.
Esse microcontrolador também atua sobre um relé, permitindo que a tomada seja
ligada ou desligada à distância. Cada tomada inteligente tem, também, um trans-
ceptor, permitindo que mensagens sejam enviadas e recebidas por radiofrequência.
Dessa forma, uma rede foi estabelecida entre as três tomadas e a central. Todo o
código que é executado nessas unidades foi escrito em linguagem C, exigida pela
Arduino IDE.
Em seguida, o desenvolvimento do projeto continuou no âmbito da central, a qual
desempenha um papel autoritário na rede de comunicação e envia mensagens es-
pecíficas, exigindo atualizações sobre o estado de consumo de cada tomada. Esses
valores são armazenados em um banco de dados, junto ao horário em que foram
obtidos. O módulo que realiza essas tarefas foi escrito em linguagem Python, de-
vido à sua alta flexibilidade e à extensa disponibilidade de bibliotecas, como a do
transceptor responsável pela comunicação remota.
3
Também na central, um servidor permite que os dados de consumo sejam expostos
por meio de uma API REST [4], a qual, finalmente, é acessada por uma aplicação
web, dando ao usuário a capacidade de visualizar os dados de consumo e de ligar ou
desligar cada uma das tomadas que constituem o sistema. Esses elementos foram
desenvolvidos em linguagem Javascript pois essa, sendo executada no navegador do
usuário, permite a construção dos elementos dinâmicos da interface com o sistema.
1.6 Materiais
Tabela 1.1: Materiais utilizados para realizar o projeto
Elemento Quantidade AplicaçãoRaspberry Pi 3 com Fonte USB 1 CentralTransceptor nRF24L01+ 4 Central / TomadaFonte de 12 V e 450 mA 3 TomadaArduino Nano (ATmega328) 3 TomadaSensor de corrente ACS 712 3 TomadaRelé de 127/220 V e 10 A 3 TomadaExtensão de tomada com 70 cm 3 TomadaRegulador de tensão 7806 3 TomadaAmplificador Operacional TL074 3 TomadaCapacitor Eletrolítico 1µF 63 V 3 TomadaResistor 10 kΩ 18 TomadaResistor 47 kΩ 3 TomadaResistor 68 kΩ 6 TomadaResistor 330 kΩ 6 TomadaResistor 470 kΩ 6 Tomada
1.7 Descrição
O Capítulo 2 expõe brevemente alguns conceitos teóricos importantes para a re-
alização e entendimento do projeto.
O Capítulo 3 trata dos módulos de hardware que foram utilizados, além de suas
peculiaridades.
Os módulos de software utilizados são apresentados no Capítulo 4.
4
No Capítulo 5, um panorama geral de funcionamento do sistema é apresentado.
O Capítulo 6 foi dedicado aos resultados alcançados pelo projeto
Finalmente, no Capítulo 7, uma conclusão é apresentada. Junto a ela, futuras
melhorias são propostas.
5
Capítulo 2
Conceitos Teóricos
2.1 A Série de Fourier
A Série de Fourier é uma derivação matemática que viabiliza a representação de
funções periódicas por meio de uma soma infinita de senoides, a partir da seguinte
expressão [5][6]:
F (t) =a02
+∞∑n=1
[ancos(
2nπt
T) + bnsen(
2nπt
T)]
(2.1)
F (t) será igual à função f(t) caso esta atenda aos seguintes requisitos:
• Tenha periodicidade com período T , ou seja: f(t) = f(t+ T ) ∀t
• Seja contínua exceto, possivelmente, por um número finito de descontinuidades
no período T
• Tenha um número finito de máximos e mínimos no período T
• Seja absolutamente integrável, ou seja:∫ T0|f(t)|dt converge.
Nesse caso, os coeficientes a0, an e bn podem ser calculados conforme as seguintes
relações:
6
a0 =1
T
∫ T
0
f(t)dt
an =1
T
∫ T
0
f(t)cos(2nπt
T)dt, n ≥ 1
bn =1
T
∫ T
0
f(t)sen(2nπt
T)dt, n ≥ 1
(2.2)
Uma representação equivalente da Série de Fourier que se mostrará mais conveni-
ente para as necessidades do trabalho aqui desenvolvido é a Forma Harmônica [7],
que, para a mesma função f(t), é dada pelo que segue:
f(t) = A0 +∞∑n=1
Ancos(ωnt− θn) (2.3)
Com:
A0 =a02
An =√a2n + b2n ∀n ≥ 1
θn = atan(anbn
) ∀n ≥ 1
ωn =2nπ
T∀n ≥ 1
(2.4)
Dessa forma, dispõe-se de uma maneira compacta de representar um sinal periódio
em termos de sua frequência fundamental e de seus harmônicos.
2.2 A Rede Elétrica
Atualmente, na maior parte dos domicílios no mundo onde há fornecimento de
energia elétrica, o assinante terá acesso a um sinal de tensão senoidal, de 50 ou 60 Hz
com valores eficazes de 127 ou 220 V. Isso se deve, principalmente, à facilidade da
geração de eletricidade em AC e à possibilidade de elevação da tensão transmitida,
para reduzir as perdas nos sistemas de transmissão e distribuição [8].
7
Dados esses fatos, é razoável assumir que a tensão proveniente de uma tomada é
majoritariamente composta por sua componente fundamental (de 60 Hz, em terri-
tório brasileiro), com poucas distorções harmônicas.
O mesmo, por outro lado, não pode ser afirmado sobre o sinal de corrente de
um dispositivo conectado à tomada. Isso se deve à ampla adoção de circuitos de
alimentação não lineares em equipamentos eletrônicos [9]. Esses possuem eficiência
elevada, tamanho reduzido e comportamento intrinsecamente não linear. A con-
sequência é que sua corrente, apesar de periódica com frequência fundamental igual
à da rede elétrica, apresenta componentes harmônicas significativas, cujas frequên-
cias superam o valor fundamental em até 7 vezes. Nesse trabalho, portanto, será
considerado que as componentes harmônicas significativas apresentam frequências
de até 420 Hz em uma rede elétrica operando em 60 Hz.
Finalmente, é importante destacar que as concessionárias de energia elétrica co-
bram pela quantidade total de energia utilizada pelo assinante, medida em kWh.
Ou seja, resumidamente, a quantidade de dinheiro gasta com energia elétrica é di-
retamente proporcional à quantidade de energia elétrica utilizada no mês.
2.3 Potência
Sistemas elétricos são descritos, principalmente, por sinais de tensão (V) e cor-
rente (I) como funções do tempo. Sem perda de generalidade, define-se a potência
instantânea de um sistema dessa natureza da seguinte forma:
Pinst(t) = V (t)I(t) (2.5)
Caso os sinais de tensão e corrente sejam senoidais, de mesma frequência angular
(ω) e defasados de um ângulo θ, a potência instantânea do sistema será diretamente
influenciada pelo fator de defasagem:
8
V (t) = Vmcos(ωt)
I(t) = Imcos(ωt+ θ)(2.6)
Pinst(t) = Vmcos(ωt)Imcos(ωt+ θ)
Pinst(t) =VmIm
2[cos(θ) + cos(2ωt+ θ)]
Pinst(t) =VmIm
2[cos(θ) + cos(2ωt)cos(θ)− sen(2ωt)sen(θ)]
(2.7)
Finalmente:
Pinst(t) =VmIm
2cos(θ)︸ ︷︷ ︸P
[1 + cos(2ωt)]
︸ ︷︷ ︸Sempre positiva
− VmIm2
sen(θ)︸ ︷︷ ︸Q
sen(2ωt)
︸ ︷︷ ︸Alternada
(2.8)
O ângulo de defasagem, θ, advém da natureza do sistema em questão. Caso o
circuito seja puramente resistivo, esse valor será nulo e, consequentemente, a potên-
cia instantânea será sempre positiva, já que os sinais de tensão e corrente terão suas
polaridades mudadas simultaneamente (a parcela alternada da Expressão 2.8 será
nula). Isso implica que, sendo a potência uma medida de fluxo de energia, esse só
acontece em um sentido: da fonte para o circuito.
Em contrapartida, é comum encontrar sistemas elétricos que empregam compo-
nentes reativos. Esses resistem às variações dos sinais de corrente ou tensão arma-
zenando energia em campo elétrico ou magnético e devolvendo-a à fonte periodica-
mente. Nesse caso, o ângulo θ será diferente de zero e, em determinados instantes
de tempo, a potência instantânea terá sinal negativo, denotando o fluxo de energia
no sentido contrário: do circuito para a fonte. No caso extremo, quando o sistema é
puramente reativo, a defasagem entre tensão e corrente é de 90 e, a cada ciclo, toda
a energia entregue pela fonte é integralmente devolvida pelo circuito [10]. Nessa
situação, apenas a parcela alternada da Expressão 2.8 estará presente.
A partir dessa análise, entende-se que a Expressão 2.8 particiona o fluxo de energia
do sistema em uma parcela ativa (P) e noutra reativa (Q). Potência ativa, na física, é
9
a grandeza que quantifica a taxa de realização de trabalho de um sistema. Em outras
palavras, é dada pela razão entre a quantidade de energia efetivamente consumida
e o tempo ao longo do qual essa transferência ocorreu. A unidade de medida de
potência ativa no sistema internacional é o Watt (W), que equivale à razão entre
energia e tempo: W = Js [11].
Tendo isso em vista, é interessante encontrar uma forma sistemática de se obter
a parcela ativa da potência instantânea de qualquer sistema elétrico, independente-
mente de sua natureza, apenas a partir dos sinais de tensão e corrente medidos. Esse
objetivo é atingido quando realiza-se a média temporal da potência instantânea ao
longo de um número inteiro de períodos. O raciocínio segue:
Dados um sinal de tensão V (t) = Vmcos(ωt) e um sinal de corrente periódico com
frequência angular fundamental ω, não senoidal e sem nível DC representado por
uma Série de Fourier I(t) =∑∞
n=1 Incos(ωnt + θn), a potência instantânea é dada
por:
Pinst(t) = Vmcos(ωt)∞∑n=1
Incos(ωnt+ θn) (2.9)
Pinst(t) = VmI1cos(ωt)cos(ω1t+ θ1) +
VmI2cos(ωt)cos(ω2t+ θ2) +
VmI3cos(ωt)cos(ω3t+ θ3) . . .
(2.10)
Com ωn = nω e com a demonstração apresentada no Apêndice A, pode-se rees-
crever essa expressão como:
Pinst(t) =VmI1
2[cos(θ1) + cos(2ωt+ θ1)] +
VmI22
[cos(ωt+ θ2) + cos(3ωt+ θ2)] +
VmI32
[cos(2ωt+ θ3) + cos(4ωt+ θ3)] . . .
(2.11)
Dessa forma, lembrando que ω = 2πT
e sendo T o período fundamental, a média
de Pinst(t) pode ser tomada ao longo de um período, eliminando as componentes
10
oscilatórias [12]:
1
T
∫ T
0
Pinst(t)dt =VmI1
2cos(θ1) = P (2.12)
Finalmente, convém salientar que a integral no tempo da potência ativa, P , de um
sistema resulta na quantidade total de energia consumida. Isto posto, uma maneira
razoável de realizar o acompanhamento de consumo de energia é a utilização de um
dispositivo que meça e integre a potência ativa consumida pela carga em questão.
2.4 Amostragem
Amostragem é o nome dado ao processo de conversão de um sinal contínuo no
tempo em uma versão equivalente discreta no tempo. Basicamente, amostras "ins-
tantâneas" de um sinal contínuo no tempo são capturadas por meio de um Conversor
Analógico-Digital (ADC), o qual converte o valor das amostras em palavras de um
certo número de bits.
O processo de amostragem deve ser feito com cautela, já que pode gerar um efeito
negativo denominado aliasing, onde um sinal de frequências muito altas é represen-
tado, no domínio digital, como um sinal de frequências menores. De forma sucinta,
a solução analítica para esse problema foi enunciada no Teorema da Amostragem
de Nyquist-Shannon [13].
O teorema enuncia que não haverá aliasing desde que a frequência de amostragem
(fs) escolhida seja superior ou igual ao dobro da maior frequência (fm) presente no
espectro do sinal analógico a ser amostrado:
fs ≥ 2fm (2.13)
11
Capítulo 3
Módulos de Hardware
Esse Capítulo apresenta uma explicação detalhada sobre cada um dos módulos
de hardware utilizados no projeto, além de explicitar os motivos pelos quais foram
escolhidos.
A central é composta pelos seguintes módulos, interconectados como na Figura
3.1:
• Raspberry Pi 3
• Transceptor nRF24L01+
Figura 3.1: Diagrama de blocos - central
Cada tomada é composta pelos seguintes módulos, interconectados como na Fi-
gura 3.2:
• Fonte Chaveada de 12 V
• Arduino Nano
• Transceptor nRF24L01+
12
• Sensor de Corrente ACS712• Relé• Sensor de Tensão
Figura 3.2: Diagrama de blocos - tomada
3.1 Raspberry Pi 3
Como a especificação do projeto exige uma interface web para servir os dados de
acompanhamento de consumo e a capacidade de controle sobre as tomadas ao usuá-
rio, faz-se necessário implementar um servidor HTTP. Esse responderá a requisições
e permitirá interações à distância.
Um servidor nada mais é do que um computador executando um tipo específico de
código que observa certas portas da interface de rede esperando receber pacotes que
sigam um determinado padrão, especificado pelo protocolo HTTP [14]. A forma
como as respostas às requisições supracitadas são geradas depende da lógica de
negócio implementada e da presença e natureza do banco de dados utilizado.
13
As tarefas a serem executadas pela central que se comunica com as tomadas não
são altamente complexas (não exigem processamento excessivo) e, portanto, não
justificam a utilização de um computador do tipo desktop. Felizmente, diversas
alternativas estão disponíveis no mercado, com custo e tamanho substancialmente
menores. Essas alternativas costumam se encaixar na categoria de Single Board
Computers.
Tendo isso em mente, um Raspberry Pi 3 foi utilizado como central. Esse pequeno
computador pode executar distribuições de Linux e oferece diversos recursos como
Wi-Fi, Bluetooth e um conjunto de pinos GPIO. Dessa forma, foi possível estabelecer
conexões por SSH com o Raspberry Pi 3, permitindo a escrita e a execução de código.
No escopo do projeto, as linguagens Python, devido à sua simplicidade, e Ja-
vascript (Node.js), devido à facilidade de se implementar um servidor HTTP nesse
framework, foram utilizadas para programar o computador. O Capítulo 4 aprofun-
dará esses assuntos.
Figura 3.3: Raspberry Pi 3 e uma moeda para comparação de tamanho
14
3.2 Arduino Nano
Um microcontrolador é um pequeno dispositivo, implementado num circuito inte-
grado, que possui entradas e saídas de diversas naturezas e a capacidade de executar
instruções previamente carregadas. Essas características tornam esse componente
uma interessante forma de abstrair funcionalidades fixas implementadas por ele-
mentos monolíticos de hardware (transistores, resistores, portas lógicas etc) devido
à capacidade de reprogramação que é disponibilizada ao usuário.
Dado o objetivo do projeto e as considerações sobre Potência Ativa explicitadas
no Capítulo 2, faz-se interessante o uso de um microcontrolador para executar as
operações matemáticas necessárias, o armazenamento do valor de consumo e sua
transferência para a central, por meio do módulo transceptor.
Assim sendo, o microcontrolador utilizado para executar o software que controla
cada uma das tomadas inteligentes foi o ATmega328, da Atmel [15], exposto pela
placa de prototipagem Arduino Nano [16].
A justificativa para essa decisão foi a alta disponibilidade do módulo em mercado
de varejo, seu tamanho diminuto, seu baixo preço e suas capacidades tecnológicas.
Dentre essas, as seguintes merecem ser ressaltadas:
• Possibilidade de programação nas linguagens C e C++
• Extensa biblioteca de funcionalidades
• Extensa comunidade online
• Abstração do protocolo de comunicação SPI [17]
• ADC de 10 bits com entrada no intervalo [0, 5] V e máxima taxa de amostragem
de 10 kHz
• Alimentação no intervalo [7, 12] V
• Saída de 5 V exposta
• Saídas digitais facilmente manipuláveis
• 30 KB de memória flash disponível para carregar código
• 2 KB de memória SRAM
• Clock de 16 MHz
15
O processo de programação do Arduino Nano é facilitado pela Arduino IDE, a
qual abstrai todas as etapas de compilação e carregamento de binário (via USB) em
um simples botão na interface gráfica.
Figura 3.4: Arduino Nano e uma moeda para comparação de tamanho
3.3 Transceptor nRF24L01+
A capacidade de comunicação sem fio é essencial para o funcionamento do sistema
de acompanhamento de consumo. Dessa maneira, a central e as tomadas podem
estar distantes umas das outras sem que a troca de mensagens seja prejudicada.
Existem diversos módulos de comunicação por radiofrequência de baixa potência
disponíveis no mercado de varejo, porém, muitos possuem a desvantagem de estarem
fisicamente divididos em submódulos de recepção e transmissão de dados.
O nRF24L01+ é um módulo projetado pela Nordic Semiconductor [18], que opera
a 2.4 GHz e se sobressai por possuir as seguintes características:
• Transceptor: TX e RX no mesmo circuito
• Comunicação SPI [17]
• Diferentes modos de operação: 250 Kbps, 1 Mbps e 2 Mbps
• Biblioteca de interface em C para Arduino
16
• Biblioteca de interface em Python para Raspberry Pi
• Baixo consumo de energia: pico de 50 mW
• Custo acessível
Outra vantagem que esse tranceptor oferece é a comunicação por pipes. Esses
nada mais são que canais de endereçamento, nos quais os módulos podem escrever
ou ler mensagens. Um nRF24L01+ pode, ao mesmo tempo, escrever em 1 pipe e
ler de 6 pipes. Fica claro que, dessa maneira, o sistema de acompanhamento de
consumo pode operar sobre dois pipes:
1. Escrita da central e leitura das tomadas
2. Escrita das tomadas e leitura da central
Figura 3.5: nRF24L01+ e uma moeda para comparação de tamanho
3.4 Sensor de Corrente ACS712
Conforme a discussão sobre Potência apresentada no Capítulo 2, o acompanha-
mento de consumo de energia elétrica é possível por meio da integração no tempo
de valores de potência ativa. Para obtê-los, calcula-se a média temporal das amos-
tras de potência instantânea: Pinst(t) = I(t)V (t). Assim sendo, é necessário que o
microcontrolador obtenha amostras instantâneas de corrente e tensão.
17
A solução para a obtenção de amostras de corrente foi o sensor ACS712 [19] que
se utiliza do Efeito Hall [20] e, apesar de invasivo (exige interrupção da via cuja
corrente será medida), oferece as seguintes vantagens:
• Largura de banda selecionada por componentes externos
• Erro máximo de 1.5% a 25C
• Capacidade de medição de corrente AC e DC
• Sensibilidades de 66, 100 e 185 mV/A
• Custo acessível
O modelo utilizado foi o de sensibilidade igual a 100 mV/A. Seu range de medidas
é de ±20 A. Além disso, é importante mencionar que foi aplicado um breakout, ou
seja, uma placa de prototipagem que inclui o CI do sensor. Dessa forma, não houve
necessidade de levar em conta os componentes que determinam a largura de banda,
já que estes estão incluídos na placa e configuram um valor de 34 kHz, satisfatório
para as necessidades do projeto.
Essa placa expõe as portas do sensor por onde o sinal a ser medido deve passar,
os pinos de alimentação (Vcc = +5 V) e uma saída analógica no intervalo [0, Vcc] que
se mantém a Vcc2
quando não há fluxo de corrente.
Figura 3.6: ACS712 e uma moeda para comparação de tamanho
18
3.5 Relé
Para permitir que as tomadas sejam controladas remotamente, ou seja: ligadas e
desligadas à distância, foi necessário escolher uma forma de realizar o corte de fluxo
de corrente, assim, um Relé foi empregado.
Esse elemento é essencialmente um interruptor eletromecânico, já que o contato
entre seus terminais de entrada e saída é movimentado magneticamente para fechar
ou abrir o circuito. Para realizar esse deslocamento, um sinal de controle é aplicado
sobre uma bobina, criando força magnética.
É importante especificar o Relé apropriadamente, já que esse componente apre-
senta limites de tensão e corrente. No projeto, foram utilizados relés de 127/220 VAC
com corrente máxima de 10 A em breakout, expondo pinos de alimentação (Vcc = +5
V) e uma entrada digital que determina seu estado.
Figura 3.7: Relé e uma moeda para comparação de tamanho
3.6 Fonte Chaveada
Cada tomada (conjunto de microcontrolador, relé, sensores e transceptor) é ali-
mentada por uma fonte chaveada de 12 V com corrente máxima de 450 mA. O
regulador de tensão de 5 V exposto no Arduino Nano foi utilizado para alimentar os
19
módulos que trabalham sob essa tensão. Essa fonte foi escolhida devido ao tamanho
diminuto e ao seu baixo preço.
Figura 3.8: Fonte chaveada e uma moeda para comparação de tamanho
3.7 Sensor de Tensão
Como supracitado, a obtenção de potência ativa requer amostras instantâneas
de tensão e corrente. Isso sugere a necessidade de um módulo medidor de tensão
instantânea. Contudo, alguns cuidados devem ser tomados quando sistemas são
acoplados à rede elétrica, no sentido de prevenir acidentes potencialmente fatais [21]
e proteger os circuitos eletrônicos de baixa tensão, evitando que queimem. Existem
diversas maneiras de evitar que haja fluxo livre de corrente da rede elétrica para
os terminais do circuito, uma das quais é a Isolação Galvânica [22], que consiste
em utilizar alternativas a meios condutores metálicos para transferir energia ou
informação entre módulos distintos.
Dessa forma, diversas opções de módulos medidores de tensão foram consideradas,
a maior parte das quais tem alto custo, baixíssima disponibilidade no varejo brasi-
leiro ou mesmo tamanho e peso incompatíveis (devido à presença de transformadores
operantes a 60 Hz) com os que seriam aceitáveis para uma tomada inteligente.
20
A partir dessas considerações, foi necessário projetar um sensor de tensão que
atenda aos seguintes requerimentos:
• Isolamento de fluxo de corrente da rede elétrica
• Operação em redes de 50 ou 60 Hz e 127 Vrms (179.6 Vp)
• Saída restrita ao intervalo [0, 5] V
• Saída a 2.5 V quando a entrada for nula
• Baixo custo a varejo
• Possibilidade de implementação com apenas uma fonte assimétrica ([GND, Vcc])
Com base nesses requisitos, o circuito foi projetado em 3 estágios, os quais serão
detalhados a seguir.
3.7.1 Atenuador de Entrada
Esse estágio tem contato direto com a rede elétrica, representada pela fonte VIN
e foi projetado para, inicialmente, atenuar o sinal de tensão da rede, de modo a
adequá-lo para o circuito eletrônico do medidor. Seu esquemático apresenta-se na
Figura 3.9.
Figura 3.9: Atenuador de entrada do sensor de tensão AC
21
Nesse caso, por superposição, as tensões VAB e VCB podem ser determinadas.
Caso a tensão da rede elétrica, VIN , seja anulada, teremos uma malha sem retorno
para a terra formada pelos resistores R1 e R2. Com IM = 0, as tensões VA e VC
em relação à terra serão iguais a VDC . A corrente IG é sempre nula, pois o terra do
circuito eletrônico (GND) está isolado da rede elétrica. Dessa maneira, a corrente
da malha, IM , pode ser obtida a partir da resistência equivalente:
Req = 2R1 + 2R2 (3.1)
IM =VINReq
(3.2)
Com isso, podemos obter as tensões VAB e VCB da seguinte forma:
VAB = VA = R2IM =
[R2
2R1 + 2R2
]VIN (3.3)
VCB = −VAB (3.4)
Utilizando os valores de resistores conforme a Figura 3.9 e tomando VAC :
VAC = VAB − VCB = 2VAB
VAC = A1VIN
A1 = 0.02941
(3.5)
Destaca-se que, por superposição, as tensões nos nós A e C serão constituídas por
uma versão atenuada da tensão na rede, somada a um offset de VDC . Esse offset
é necessário para polarizar corretamente os amplificadores operacionais da entrada
do próximo estágio. O valor escolhido para VDC foi de 6 V, pois a alimentação
dos mesmos foi feita no intervalo [0, 12] V, com uma fonte chaveada simples. Os
resistores foram escolhidos de forma a atenuar a amplitude do sinal de tensão da
rede elétrica para um valor compatível com os limites do circuito eletrônico. Com
o ganho A1, uma rede de 127 Vrms originará um sinal de amplitude de 2.64 V na
entrada dos amplificadores operacionais do estágio seguinte, um valor distante dos
22
limites de entrada impostos pela alimentação.
3.7.2 Amplificador de Instrumentação
O próximo estágio consiste em um amplificador de instrumentação, o qual dispõe
de altíssima impedância de entrada. Seu esquemático segue na Figura 3.10, onde os
nós A e C são os mesmos do circuito da Figura 3.9.
Figura 3.10: Circuito do amplificador de instrumentação
Considerando que a tensão nos terminais inversores dos amplificadores operacio-
nais é igual à tensão nos respectivos terminais não-inversores e que a impedância de
entrada destes é infinita, pode-se calcular a tensão VA′ por superposição.
Aplicando-se apenas o offset, VDC , do estágio anterior, temos:
VA = VDC
VC = VDC
(3.6)
IA′C′ =VDC − VDC
R4
= 0 (3.7)
VA′ |VDC 6=0 = VDC (3.8)
23
Agora, considerando que há apenas a entrada VA e que VDC e VC são nulos:
VAR4
=VA′ − VA
R3
(3.9)
VA′ |VA 6=0 = VA
[R3 +R4
R4
](3.10)
Finalmente, com VA = VDC = 0 e VC aplicado:
−VCR4
=VA′
R3
(3.11)
VA′ |VC 6=0 = −VC
[R3
R4
](3.12)
Somando as saídas correspondentes a cada um dos três casos, obtém-se o seguinte
valor para VA′ :
VA′ = (VA − VC)
[R3
R4
]+ VA + VDC (3.13)
VC′ pode ser obtido de maneira análoga, permitindo calcular a tensão diferencial
VA′C′ = VA′ − VC′ :
VC′ = (VC − VA)
[R3
R4
]+ VC + VDC (3.14)
VA′C′ = (VA − VC)
[2R3 +R4
R4
](3.15)
Os nós A′ e C ′ delimitam a entrada de uma estrutura bastante comum: um
amplificador diferencial com um offset de VDC onde tradicionalmente estaria o nó de
terra. O efeito desse offset na saída, VOUT , pode ser dimensionado anulando VIN no
estágio inicial. Isso faz com que a corrente IA′C′ seja nula. Por consequência VA′ e VC′
serão iguais a VDC . Dessa forma, a corrente IXA′ será nula e fará com que as tensões
VX e VY assumam o valor VDC , o que polariza o amplificador operacional pela mesma
24
razão descrita anteriormente. Isso, finalmente, fará com que VOUT |VIN=0 = VDC .
Sabendo-se disso, a saída do amplificador de instrumentação pode ser calculada
a partir da expressão para o ganho do amplificador diferencial:
VOUT |VDC=0 = VA′C′
[R6
R5
](3.16)
VOUT = (VA − VC)
[2R3 +R4
R4
][R6
R5
]+ VDC (3.17)
Com os valores dos componentes como na Figura 3.10:
VOUT = A2VAC + VDC
A2 = 0.20963(3.18)
Na análise acima fica clara a razão pela qual o offset de VDC é útil. Essencialmente,
toda a operação do circuito passou a ser referenciada a essa tensão e isso se reflete na
saída. Essa mudança de referência permite que os amplificadores operacionais sejam
alimentados de maneira assimétrica, no intervalo [0, 12] V, o que reduz o custo do
projeto pois requer apenas uma fonte alimentação. A justificativa para a escolha dos
valores dos resistores R3 e R4 foi gerar um ganho que mantenha a amplitude do sinal
VA′C′ compatível com os limites de excursão de sinal dos amplificadores operacionais
de entrada. Os resistores R5 e R6 foram escolhidos de forma que a amplitude da
saída do medidor de tensão seja menor que 2.5 V (quando o valor eficaz de VIN é
de 127 V), valor correspondente à metade da máxima tensão aceita pelo ADC do
microcontrolador.
3.7.3 Estágio de mudança de offset
Como é necessário que a saída do sensor de tensão esteja no range do ADC do
ATmega328, em [0, 5] V, é importante um último estágio que coloque o nível médio
da saída do amplificador de instrumentação em 2.5 V, correspondendo ao nível médio
(nulo) da entrada do medidor de tensão. Isso foi feito com o esquemático da Figura
25
3.11.
A análise DC desse circuito consiste num simples divisor resistivo com ganho de
0.5. Isso coloca a saída VADC a 2.5 V. Em AC, obtém-se um filtro passa-altas, com
R = 235 kΩ e C = 1 µF. Para esse filtro:
fc =1
2πRC= 0.68Hz
∆φ|f=50Hz = arctan
(1
2πfRC
)= 0.38
(3.19)
Esse estágio, portanto, permite que o sinal de 50 ou 60 Hz prossiga sem atenuação
ou desvio de fase significativos e fique com o nível médio desejado. A alimentação
de 5 V provém da placa do Arduino Nano.
Figura 3.11: Estágio de que adapta o nível de offset da tensão produzida pelo medidorde tensão para o range do ADC do microcontrolador
3.7.4 Geração do sinal offset
Como explicado anteriormente, foi necessário gerar um offset de VDC nas entradas
de todos os amplificadores operacionais para permitir operação com uma única fonte
assimétrica. Para atingir esse objetivo, um regulador LM7806, o qual gera uma saída
de 6 V, foi utilizado.
3.7.5 Visão geral do Circuito Medidor de Tensão
A Figura 3.12 exibe o esquemático completo do medidor de tensão. Notar que
VDC é a tensão de offset que viabiliza a operação com alimentação assimétrica. A
26
saída do circuito, ADC, é conectada diretamente a uma das entradas analógicas
(ADC) do Arduino Nano.
O sensor foi simulado com o software LTSpice e, na Figura 3.13 pode-se ver, para
uma entrada de 179.6 Vp - que equivale a 127 Vrms - e 60 Hz, a saída do amplificador
de instrumentação, VOUT , e a saída do último estágio, VADC . Na Tabela 3.1 são
apresentados os valores máximo, mínimo, médio e pico a pico de cada um dos traços
da simulação.
Tabela 3.1: Detalhes dos traços da Figura 3.13
Traço Valor Máximo Valor Mínimo Valor Médio Pico a PicoVOUT 7.11 V 4.89 V 6.00 V 2.22 VVADC 3.59 V 1.41 V 2.50 V 2.18 V
Figura 3.12: Esquemático completo do sensor de tensão AC
27
Figura 3.13: Simulação das saídas do amplificador de instrumentação e do último estágiodo medidor de tensão, VOUT e VADC , respectivamente, para uma entrada de 179.6 Vp e60 Hz
28
Capítulo 4
Módulos de Software
4.1 Biblioteca RF24
Para realizar a interface do Arduino Nano e do Raspberry Pi 3 com o módulo
transceptor nRF24L01+, apresentado no Capítulo 3, as bibliotecas RF24 [23] e
nrf24 [24] foram utilizadas, respectivamente.
São bibliotecas de código aberto, desenvolvidas pela comunidade que utiliza o
módulo e oferecem uma camada de abstração sobre a interface SPI [17], permitindo
que o programador se concentre em sua aplicação final.
Das funcionalidades oferecidas pelas bibliotecas e utilizadas no projeto, destacam-
se as seguintes:
• setDataRate(): Escolhe a taxa de transmissão de dados a ser utilizada pelo
transceptor: RF24_250KBPS, RF24_1MBPS ou RF24_2MBPS.
• setPALevel(): Escolhe a potência de transmissão a ser utilizada pelo trans-
ceptor: RF24_PA_MIN, RF24_PA_LOW, RF24_PA_HIGH ou RF24_PA_MAX.
• openWritingPipe(): Escolhe o endereço do pipe de escrita a ser utilizado
(unsigned int).
• openReadingPipe(): Dado um valor de 0 a 5, escolhe o endereço de leitura
do respectivo pipe (o módulo pode receber dados em 6 pipes simultâneos).
• startListening(): Coloca o transceptor em modo de leitura.
• stopListening(): Tira o transceptor do modo de leitura.
29
• available(): Retorna ‘true’ caso existam dados disponíveis para leitura em
algum pipe.
• read(): Dado um buffer de bytes, retorna nele os dados recebidos.
• write(): Dado um buffer de bytes, envia seus dados no pipe de escrita.
4.2 MongoDB
Para armazenar os dados de consumo e o estado de cada uma das tomadas na
central, faz-se necessário utilizar algum tipo de banco de dados. Tendo isso em
mente, MongoDB foi a solução escolhida devido ao fato de ser bastante flexível e
open-source. Esse é um banco de dados não-relacional [25] na medida que funciona
de maneira orientada a documentos e não a tabelas.
Os documentos armazenados em uma MongoDB devem seguir o padrão JSON
[26], comumente utilizado em transações web. Esse padrão define objetos como
conjuntos de pares chave-valor, onde uma chave é sempre do tipo String e um valor
pode consistir em um ou mais elementos do tipo String ou Number. Seguem dois
exemplos de objetos JSON utilizados no projeto:
Figura 4.1: JSON representando o estado de uma tomada
Figura 4.2: JSON representando uma leitura de consumo de uma tomada
Cada documento armazenado em MongoDB deve estar dentro de uma coleção,
que nada mais é do que um conjunto de documentos que, preferencialmente, porém
não obrigatoriamente, compartilham finalidade.
30
As APIs oferecidas pelo banco de dados expõem operações de inserção, busca,
remoção e atualização de documentos. Para as três últimas, filtros baseados nas
chaves presentes nos objetos podem ser implementados, de forma a aumentar a
especificidade das operações.
Há diversas implementações das APIs de MongoDB para diferentes linguagens.
No projeto, foram utilizadas pymongo [27] e mongodb [28]: em Python e Node.js,
respectivamente.
4.3 Node.js
A linguagem JavaScript foi criada e tradicionalmente utilizada na web, sendo
interpretada e executada pelos navegadores, que acessam scripts anexados a docu-
mentos HTML. A natureza multiplataforma e aberta da web acabou gerando um
ambiente propício para o surgimento de aplicações e frameworks criados por usuários
comuns.
Uma das consequências mais recentes desse ecossistema foi o Node.js [29], que
nada mais é do que um ambiente de interpretação e execução de código JavaScript
desatrelado de navegadores. Com isso, a linguagem pode ser utilizada para escrever
programas da maneira tradicional, executando-os pela linha de comando.
A grande vantagem da utilização de Node.js advém da quantidade de ferramentas
robustas e complexas herdadas da web. Além disso, a execução segue o paradigma
assíncrono, de forma que uma leitura de arquivos, por exemplo, não bloqueia a
execução do código. Isso permite que funções callback sejam passadas para tarefas
de execução longa, de modo que seus resultados tornem-se disponíveis ao resto do
código, sem que outras tarefas sejam bloqueadas.
Um dos usos mais tradicionais do ambiente é a implementação de servidores
HTTP, explorando a execução assíncrona e permitindo que requisições grandes não
obstaculizem a resposta a requisições pequenas. Isso proporciona um enorme grau
de escalabilidade ao serviço.
31
4.3.1 Express
Express [30] é um framework que abstrai grande parte das tarefas que envolvem a
implementação de um servidor em Node.js a partir do zero. A filosofia da ferramenta
é expandir a funcionalidade do servidor utilizando módulos de middleware [31].
Nesse contexto, esses módulos são associados ao serviço principal e chamados em
série ao longo do processo de resposta a uma requisição HTTP.
É possível, com essa ferramenta, implementar um servidor que responde com texto
a requisições do tipo GET em apenas 10 linhas de código [32].
Dentre os middlewares utilizados no projeto, destacam-se:
• Morgan [33]: Uma ferramenta que gera logs em tempo real das requisições que
chegam ao servidor, auxiliando o processo de depuração.
• Body-Parser [34]: Um middleware que acessa o corpo das requisições e con-
verte os dados para o formato JSON, permitindo acesso fácil por parte de
outros middlewares.
• express.Router [35]: Um método de tratamento de requisições que permite
roteamento por URL.
4.3.2 API REST
Uma API REST compõe um conjunto de URIs que dão acesso a representações
textuais (em formato JSON, por exemplo) de recursos web de qualquer tipo, junto
a quatro verbos que permitem modificar a forma de acesso aos mesmos. Os quatro
verbos seguem:
• GET: Obter a representação do recurso.• POST: Criar um recurso.• PUT: Modificar um recurso.• DELETE: Remover um recurso.
As requisições feitas a uma API REST, portanto, devem conter um dos quatro
verbos, a informação adicional necessária anexada ao corpo e um endereço URL
indicando a rota a ser seguida.
32
Em particular, o roteamento de requisições funciona de maneira bastante harmo-
niosa quando em conjunto com um banco de dados como MongoDB. Isso se deve
ao fato de que os documentos armazenados nesse banco de dados são separados por
coleções e recebem identificadores automaticamente. Exemplos de operações de uma
API REST podem ser encontrados na Tabela 4.1.
Tabela 4.1: Exemplos de operações numa API REST sendo acessada na rede local pelaporta 3000
URL GET POST PUT DELETE
http://localhost:3000/recursos
Retorna informaçãosobre todos osdocumentos dacoleção ‘recursos’.
Cria um novodocumento dentroda coleção‘recursos’.
Substitui todosos documentosda coleção‘recursos’.
Remove todosos documentosda coleção‘recursos’.
http://localhost:3000/recursos/3
Retorna informaçãosobre o documentode identificador ‘3’pertencente àcoleção ‘recursos’.
Normalmentenão é utilizado.
Modifica um ou maiscampos do documento deidentificador ‘3’ da coleção‘recursos’.
Remove odocumento deidentificador ‘3’da coleção‘recursos’.
No projeto, os dados expostos pela API REST são: o estado de cada tomada e
os pacotes periódicos contendo o consumo de cada tomada. Dessa maneira, as rotas
criadas são as apresentadas pela Tabela 4.2.
Tabela 4.2: Operações REST implementadas na central
URL GET POST
http://localhost:3000/socketsRetorna um objeto contendo identificação(id) e estado (ligado ou desligado) paracada uma das três tomadas.
Não utilizado.
http://localhost:3000/sockets/#id
Retorna um objeto contendo identificação(id) e estado (ligado ou desligado) e todosos objetos contendo o valor periódicode consumo da tomada com identificador‘#id’.
Cria um novo objeto contendovalor periódico de consumorelacionado à tomada comidentificador ‘#id’.
4.4 Materialize CSS
Uma página web pode ser estilizada por um arquivo CSS. Esse tipo de arquivo
permite que o programador altere características como cores, tamanhos e posiciona-
mento de elementos HTML [36].
Grande parte da experiência de um usuário advém da interface através da qual
acontece a interação com o serviço oferecido. Isso implica que uma das características
33
mais importantes de um serviço web é o seu aspecto visual.
Existem diversos padrões de design abertos a uso público, alguns dos quais ofe-
recem até mesmo frameworks CSS implementando seus componentes. Um desses
é o Material Design [37], de propriedade da Google. Essa especificação determina
diversos aspectos do design de interfaces como: paleta de cores, containers, uso de
sombras, animações etc. Uma implementação em CSS dessa especificação é a Mate-
rialize CSS [38]. Além dos elementos de design, o framework oferece funcionalidades
de responsividade, permitindo que a interface se adapte a tamanhos de tela variados,
compreendendo tablets, smartphones e desktops.
A interface web que permite visualização de dados de consumo e controle à distân-
cia das tomadas inteligentes foi desenvolvida utilizando esse framework e é exibida
na Figura 4.3.
Figura 4.3: Visualização das informações referentes à tomada 2 através da interface web
34
4.5 jQuery
jQuery é uma biblioteca leve e multiplataforma para JavaScript que abstrai tarefas
envolvendo interação com elementos HTML [39]. A linguagem JavaScript oferece
funções nativas de interface com o DOM como a document.getElementById("id"),
que retorna uma referência a um objeto representando um elemento HTML por meio
de seu identificador. Porém o funcionamento dessas funções pode não ser isomorfo
entre navegadores diferentes.
Uma das vantagens de utilizar-se jQuery é, portanto, a garantia de funcionamento
multiplataforma. Ademais, o acesso a elementos é extremamente flexível, podendo
ser feito por identificador, classe, tipo ou hierarquia. Com referências aos objetos, é
possível alterar suas características sem que a página tenha de ser recarregada.
Outra grande vantagem da biblioteca é a possibilidade de realizar requisições
HTTP assíncronas. Isso, junto à flexibilidade no que diz respeito à alteração da
aparência do HTML, permite que as chamadas Single Page Applications [40] sejam
implementadas. Essas são aplicações web que aparentam ser programas tradicionais
para desktop, já que as interações são instantâneas na medida que a página nunca é
recarregada. Com isso, foi possível implementar a interface do sistema de controle
de consumo facilmente, permitindo que as visualizações sejam atualizadas em tempo
real.
4.6 Chart.js
Para viabilizar a exibição dos dados de consumo de forma significativa, um gráfico
de barras com o valor total de kWh para cada hora do dia foi implementado, como na
Figura 4.3. Dessa maneira, o usuário pode, para cada uma das tomadas inteligentes,
escolher um dia base e obter suas informações.
A implementação dos gráficos na aplicação web se deu utilizando o framework
Chart.js [41], o qual oferece uma API com diversas opções de gráficos e ferramentas
relacionadas. Colocando-se os dados referentes aos eixos x e y em um objeto JSON e
realizando uma chamada ao método update(), pode-se atualizar o gráfico em tempo
35
real. Dessa maneira, a cada 5 segundos, o gráfico exibido na interface é atualizado
com novos dados de consumo.
36
Capítulo 5
Panorama de Funcionamento
5.1 Protocolo de Comunicação
Sobre a biblioteca RF24, mencionada no Capítulo 4, foi especificado um protocolo
de comunicação que define o formato das mensagens trocadas entre as tomadas
e a central. Como as nuances de recuperação da mensagem são abstraídas pela
biblioteca, o protocolo só lida com o problema de endereçamento e formatação de
dados e comandos.
Cada mensagem trocada no ecossistema do projeto é composta por 32 bytes e
segue o formato ilustrado na Tabela 5.1:
Tabela 5.1: Formato das mensagens do protocolo de comunicação
B0 B1 B2 B3 B4 B5 B6 ... B30 B31R_ID S_ID M_ID P0 P1 P2 P3 ... P27 P28
Na Tabela 5.1, B0 indica o byte mais significativo e B31 o byte menos significativo.
Os componentes de uma mensagem são, então, os seguintes:
• R_ID: Receiver ID - Número de identificação do destinatário
• S_ID: Sender ID - Número de identificação do remetente
• M_ID: Message ID - Número de identificação da mensagem
• P0 - P28: Payload Bytes - 29 bytes dedicados ao conteúdo da mensagem.
37
Com esse formato, é possível endereçar até 255 tomadas, já que o ID 0 é reservado
à central. Ademais, 256 mensagens diferentes podem ser implementadas. Os bytes
de payload podem ser utilizados de maneira genérica, a depender da finalidade da
mensagem em questão.
Nas duas plataformas onde o protocolo foi implementado (Raspberry Pi e Arduino
Nano), a construção de mensagens foi abstraída por meio de uma representação
genérica, como no Algoritmo 5.1 e um par de funções que codifica ou decodifica
uma mensagem, como nos Algoritmos 5.2 e 5.3, respectivamente.
Algoritmo 5.1 Estrutura de dados que representa uma mensagemstructure Message
byte receiver_idbyte sender_idbyte message_idbyte[] payload
end structure
Algoritmo 5.2 Função que transforma uma mensagem num array de bytesprocedure message_to_bytes(message)
bytes← ∅bytes[0]← message.receiver_idbytes[1]← message.sender_idbytes[2]← message.message_idi← 0while i < 29 do
bytes[i+ 3]← message.payload[i]i← i+ 1
end whilereturn bytes
end procedure
38
Algoritmo 5.3 Função que transforma um array de bytes em uma mensagemprocedure bytes_to_message(bytes)
Message messagemessage.receiver_id← bytes[0]message.sender_id← bytes[1]message.message_id← bytes[2]i← 3while i < 32 do
message.payload[i− 3]← bytes[i]i← i+ 1
end whilereturn message
end procedure
No âmbito do projeto, foram implementadas as quatro mensagens que seguem na
Tabela 5.2.
Tabela 5.2: Especificação das mensagens implementadas no protocolo de comunicação
Nome ID Descrição Payload
MESSAGE_STATUS_REQUEST 0 Solicita uma atualização do estadode consumo de uma tomada. -
MESSAGE_STATUS_RESPONSE 1Responde à mensagemMESSAGE_STATUS_REQUESTcom o valor de consumo atual.
4 bytes que representamo float do valor de consumoatual em kWh.
MESSAGE_RELAY_REQUEST 2 Solicita que o relé tenha seu estadoalterado.
1 byte com valor 0 ou 1para desligado ou ligado,respectivamente.
MESSAGE_RELAY_RESPONSE 3Responde à mensagemMESSAGE_RELAY_REQUESTconfirmando o novo estado do relé.
1 byte com valor 0 ou 1para desligado ou ligado,respectivamente.
5.2 Tomadas
O firmware executado em cada uma das tomadas é dividido em um componente
que implementa o loop principal, socket.ino, e subcomponentes que atuam como
drivers [42], provendo interfaces de software a seus respectivos elementos de hard-
ware. As interfaces dos drivers encontram-se definidas nos seguintes arquivos, que
podem ser encontrados no Apêndice C:
• current_sensor.h: Interface com o sensor de corrente, ACS712.
• voltage_sensor.h: Interface com o sensor de tensão cujo projeto foi apresen-
tado no Capítulo 3.
• relay.h: Interface com o módulo de relé.
39
• transceiver.h: Interface com o transceptor, nRF24L01+. Baseado na bibli-
oteca RF24.
A Biblioteca Padrão do Arduino Nano oferece todos os recursos necessários para
realizar comunicações com elementos externos, como, por exemplo, leituras e escri-
tas nos pinos analógicos ou digitais. Por razões de Engenharia de Software, todas
as funções que realizam tarefas especificas à Biblioteca Padrão do Arduino foram
abstraídas no arquivo arduino_wrapper.h. Dessa maneira, caso o sistema venha
a ser implementado sobre outro microcontrolador, será necessário que apenas esse
arquivo seja alterado, já que todo o resto do código opera sobre as funções nele
definidas.
Além dos componentes supracitados, o firmware incorpora um subcomponente
que trata do cálculo do valor de consumo em kWh propriamente dito, implementado
em power_meter.h.
5.2.1 Inicialização
No arquivo socket.ino, são definidas as funções setup(), que é executada uma
única vez assim que o microcontrolador é ligado, e loop(), a qual é executada
repetidas vezes, infinitamente, após o término da primeira. Esse é um padrão de
arquitetura adotado na programação de microcontroladores Arduino.
Tendo isso em vista, a função setup() foi utilizada como meio de inicialização
dos drivers. Em particular, é durante a execução dessa função que os sensores de
corrente e tensão são calibrados. Ademais, funções necessárias à inicialização da
biblioteca RF24 são chamadas, também, a esse ponto.
5.2.2 Calibração e Leitura do Sensor de Corrente
Como delineado no Capítulo 3, a saída do ACS712 de 20 A é um valor de tensão no
intervalo de 0 a 5 V, centrado, idealmente, em 2.5 V. Também restritas ao intervalo
de 0 a 5 V encontram-se as entradas do ADC do Arduino Nano.
40
Devido a efeitos como variações de temperatura, a saída do ACS712 pode não
estar perfeitamente centrada a 2.5 V. Para lidar com esse empecilho, uma etapa de
calibração é realizada. Essa etapa consiste no processo de obtenção de um valor de
offset a ser aplicado a cada posterior leitura do ADC, corrigindo eventuais variações.
Seguem os passos:
1. Introduzir um valor de offset (ADCoff = 0) a ser acrescentado a todas as
leituras do ADC, cujos valores estão restritos ao intervalo [0, 1023].
2. Abrir o relé para impedir qualquer fluxo de corrente.
3. Realizar 2000 leituras do ADC, acumulando os valores numa variável auxiliar:
S =∑2000
i=1 ADCi.
4. Atualizar o valor de ADCoff com a diferença entre a média das leituras rea-
lizadas e o valor ideal, que corresponde à metade do intervalo de leituras do
ADC (210−12
): ADCoff = 10232− S
2000.
Dessa maneira, para realizar uma leitura de corrente, basta efetuar os seguin-
tes passos:
1. Obter a saída (corrigida por ADCoff ) do ADC: ADCo, no intervalo [0, 1023].
2. Converter ADCo em um valor de tensão correspondente: Vo = 5ADCo
1023, em
volts.
3. Utilizar o valor de sensibilidade indicado no datasheet [19] (0.1VA) para con-
verter Vo na leitura de corrente instantânea correspondente: Io = Vo0.1
, em
ampères.
5.2.3 Calibração e Leitura do Sensor de Tensão
Por motivos semelhantes aos do sensor de corrente, o valor correspondente à tensão
nula na entrada do sensor de tensão pode não ser exatamente de 2.5 V como desejado.
Isso exige uma etapa de calibração análoga à apresentada anteriormente.
Dessa maneira, uma leitura do sensor de tensão é efetuada da seguinte maneira:
1. Obter a saída (corrigida por ADCoff ) do ADC: ADCo, no intervalo [0, 1023].
2. Converter ADCo em um valor de tensão correspondente: Vo = 5ADCo
1023, em
volts.
41
3. Lembrando, como exposto no Capítulo 3, que os ganhos dos dois primeiros
estágios do sensor de tensão são A1 = 0.02941 e A2 = 0.20963, resultando
num ganho total A = 0.006165. Assim, obtém-se um ganho inverso Ainv =
162.1841. Esse valor deve ser utilizado para corrigir Vo e obter a leitura de
tensão instantânea correspondente: V ′o = AinvVo, em volts.
5.2.4 Leitura do Medidor de Consumo
O leitor de consumo de energia utiliza os sensores de tensão e corrente. Seu
funcionamento é baseado no conteúdo teórico exposto no Capítulo 2. Para tanto,
utiliza-se a média do produto de leituras instantâneas de corrente e tensão para
obter-se a potência ativa, a qual é discretamente integrada no tempo.
Em primeiro lugar, é importante considerar que, como mencionado no Capítulo
2, os harmônicos importantes do sinal de corrente chegam 420 Hz. Isso requer
uma frequência de amostragem de, ao menos, 840 Hz. No projeto, foi utilizada uma
frequência de 1 kHz. Por limitações computacionais, o Arduino Nano não é capaz de
atingir essa taxa de amostragem caso seja necessário executar muitas instruções de
cálculo entre duas leituras consecutivas do ADC. Tendo isso em vista, e lembrando
da necessidade de execução de instruções relacionadas ao protocolo de comunicação,
um mecanismo de leitura que comporta as duas tarefas foi desenvolvido.
A ideia central é concentrar todo o processamento do microcontrolador na realiza-
ção de um determinado número de pares de amostragens dos sensores de corrente e
tensão, armazenando esses valores em dois buffers e, posteriormente, utilizá-los para
obter a potência ativa e sua integral. É importante mencionar que o tamanho dos
buffers é limitado pela memória disponível no microcontrolador, o qual possui um
total de 2 KB de SRAM. Neste trabalho, foram utilizadas 100 amostras por buffer
pelas seguintes razões:
• Cada float ocupa 4 bytes, totalizando 800 bytes para os dois buffers, o que
deixa mais da metade da memória disponível ao restante do firmware.
• 100 amostras a 1 kHz equivale a 100 ms de leituras, contemplando um número
inteiro de ciclos em redes de 50 e 60 Hz: 5 e 6 ciclos, respectivamente.
42
De posse das amostras do ADC para ambos os sensores ao longo dos últimos 100
ms, converte-se cada valor em seu correspondente de corrente ou tensão, obtém-se
seu produto e acumula-se numa variável auxiliar, a qual é posteriormente dividida
por 100 e representa a potência ativa lida. O Algoritmo 5.4 apresenta esses passos.
É importante enfatizar que um piso de ruído foi determinado. Qualquer leitura
de potência ativa que esteja no intervalo [−∞, 4] W é considerada nula. Esse valor
foi obtido empiricamente, observando-se as leituras de potência ativa quando não
há nenhum dispositivo conectado à tomada em questão. Leituras negativas, apesar
de conceitualmente incorretas (no contexto estudado), podem acontecer devido às
eventuais flutuações ao redor de 0 presentes nos sensores de tensão e corrente.
O valor de potência ativa obtido é assumido constante desde o início das últimas
100 leituras até o início das próximas 100 leituras, antes das quais é multiplicado
pelo tempo (em horas) decorrido e acumulado no contador de kWh, como mostra o
Algoritmo 5.5.
As leituras do ADC foram feitas, por conveniência, sem utilizar interrupções, o que
exigiria lidar com variáveis internas do microcontrolador. Portanto, um sistema de
temporizadores baseado nas abstrações para obtenção de tempo em microssegundos
e para leitura do ADC foi utilizado no projeto.
Algoritmo 5.5 Função responsável pela integração da taxa de consumolast_reading_time← 0last_reading ← 0total_power ← 0
procedure power_meter_readif last_reading_time 6= 0 then
hours← (get_time_micros()− last_reading_time) ∗ 13600000000000
total_power ← total_power + hours ∗ last_readingend iflast_reading_time← get_time_micros()last_reading ← power_meter_sample()
end procedure
43
Algoritmo 5.4 Função responsável pela leitura do consumo de energia em Wvoltage_readings← ∅current_readings← ∅
procedure power_meter_samplesample_counter ← 0past← get_time_micros()while sample_counter < 100 do
now ← get_time_micros()if now − past ≥ 1000 then
past← nowvoltage_readings[sample_counter]← voltage_sensor_sample()current_readings[sample_counter]← current_sensor_sample()sample_counter ← sample_counter + 1
end ifend whilepower ← 0for i in [1, 100] do
power ← power + voltage_sensor_convert(voltage_readings[i]) ∗current_sensor_convert(current_readings[i])
end forpower ← power
100
if power < 4 thenpower ← 0
end ifreturn power
end procedure
44
5.2.5 Comunicação
Periodicamente, as tomadas utilizam as funções oferecidas pela biblioteca RF24
para checar se há mensagens recebidas. Caso estas existam e sejam endereçadas à
tomada em questão, uma rotina de resposta é executada, lidando devidamente com
cada tipo de mensagem, como mostra o Algoritmo 5.6.
Algoritmo 5.6 Função responsável por responder as mensagens destinadas às to-madasprocedure transceiver_update
Message messageif check_receive(message) then
Message responseif message.message_id = MESSAGE_STATUS_REQUEST then
response.payload← total_powerresponse.sender_id← MY_IDresponse.receiver_id← message.sender_idresponse.message_id← MESSAGE_STATUS_RESPONSEsend_message(response)
end ifif message.message_id = MESSAGE_RELAY_REQUEST then
relay_toggle(message.payload)response.payload← relay_status()response.sender_id← MY_IDresponse.receiver_id← message.sender_idresponse.message_id← MESSAGE_RELAY_RESPONSEsend_message(response)
end ifend if
end procedure
5.2.6 Loop Principal
O loop principal do firmware executado nas tomadas consiste em apenas duas
chamadas de funções: uma lida com o transceptor e checa se há mensagens recebidas
a serem respondidas, como mostra o Algoritmo 5.6 e a outra é a mesma que o
Algoritmo 5.5 apresenta. Como essa função guarda a última leitura de consumo e o
momento em que ela aconteceu, é seguro executar funções diferentes antes de realizar
uma nova leitura, já que sempre será possível calcular o tempo total decorrido,
necessário ao processo de integração.
45
5.3 Central
A central, como apresentado no Capítulo 3, é implementada em um Raspberry
Pi: um computador com sistema operacional UNIX. Isso permite níveis mais altos
de abstração, como, por exemplo, a execução de múltiplas tarefas simultaneamente.
Para o funcionamento do sistema de monitoramento de consumo, é necessário que
três tarefas sejam executadas:
1. communicator.py: Realiza a comunicação com as tomadas, enviando coman-
dos periódicos e armazenando as leituras recebidas no banco de dados.
2. mongod: Executa o banco de dados MongoDB utilizado.
3. Node.js: Ativa o servidor Node.js, permitindo que a interface web seja aces-
sada na rede local.
Como as Tarefas 2 e 3 seguem uma estrutura padronizada e amplamente disponível
na web, esta seção se dedica a detalhar apenas o funcionamento da Tarefa 1, o
Comunicador. Este funciona, também, com uma etapa de inicialização seguida de
um loop principal.
5.3.1 Inicialização do Comunicador
Nessa etapa, a implementação da biblioteca RF24 para Python é utilizada para
executar as funções que inicializam o transceptor nRF24L01+, seguindo a estrutura
de pipes descrita no Capítulo 3.
Em seguida, uma função de inicialização do banco de dados é executada, a qual
disponibiliza um cursor [43] ao Comunicador. Além disso, um objeto que representa
o estado das três tomadas é inicializado, com todas em modo ativo. Esse objeto é,
então, armazenado no banco de dados e copiado em uma variável local ao Comu-
nicador, servindo, futuramente, como um sincronizador de estados. Ademais, um
buffer contendo o último valor de consumo recebido de cada tomada é iniciado com
zeros. O Algoritmo 5.7 contém essas etapas.
46
Algoritmo 5.7 Inicialização do banco de dados no Comunicadordb← ∅sockets← ∅sockets_last_value← ∅procedure db_initialize
sockets_last_value← [0.0, 0.0, 0.0]db← db_connect(localhost:27017)db.clear()sockets← [
′id′ ← 1,′status′ ← 1
,
′id′ ← 2,′status′ ← 1
,
′id′ ← 3,′status′ ← 1
,]db.insert(sockets)
end procedure
47
5.3.2 Loop Principal do Comunicador
Após a inicialização, o Comunicador executa um loop infinito, no qual dois tem-
porizadores são utilizados para periodizar a gestão dos relés e da leitura de dados
de consumo de cada tomada. A estrutura geral dessa função é apresentada no Al-
goritmo 5.8.
Algoritmo 5.8 Loop principal do Comunicadorprocedure main_loop
while 1 = 1 donow ← current_time()poll_relay(now)now ← current_time()poll_status(now)
end whileend procedure
Caso o usuário escolha desligar uma das tomadas, o clique no botão da inter-
face web fará com que o servidor altere o estado dessa tomada no banco de dados.
Sendo assim, a cada segundo, o Comunicador percorre o estado das tomadas no
banco de dados e compara com o estado local em busca de diferenças. Sempre
que uma diferença é encontrada, uma mensagem do tipo MESSAGE_RELAY_REQUEST
é construída e enviada, de forma a alterar o estado do relé para o desejado. O
Comunicador repete essa etapa até receber uma confirmação da tomada em ques-
tão, MESSAGE_RELAY_RESPONSE, com o estado correto no payload, como mostra o
Algoritmo 5.9.
A cada vinte segundos o Comunicador constroi e envia uma mensagem do tipo
MESSAGE_STATUS_REQUEST para uma das tomadas. Caso não haja resposta, esperam-
se dois segundos antes de enviar um novo pedido. Se, ainda assim, após cinco pe-
didos, a tomada em questão não vier a responder, o Comunicador simplesmente
tentará a próxima tomada. Sempre que o Comunicador recebe uma mensagem do
tipo MESSAGE_STATUS_RESPONSE, a leitura de consumo é extraída do payload e utili-
zada, junto à última leitura da tomada atual, para calcular e armazenar a diferença
de kWh detectada. Essas etapas são apresentadas no Algoritmo 5.10.
48
Algoritmo 5.9 Função do Comunicador responsável por gerenciar os Reléspast_relay ← 0procedure poll_relay(now)
if now − past_relay ≥ 1 thenpast_relay ← current_time()db_sockets← db.find_sockets()for db_socket in db_sockets do
local_socket← sockets[db_socket.id]if db_socket.status 6= local_socket.status then
Message requestrequest.payload← db_socket.statusrequest.sender_id← 0request.receiver_id← db_socket.idrequest.message_id← MESSAGE_RELAY_REQUESTsend_message(request)Message responsereceive_message(response)if response.message_id = MESSAGE_RELAY_RESPONSE and
response.sender_id = db_socket.id thenif response.payload = db_socket.status then
sockets[db_socket.id]← db_socket.statusend if
end ifend if
end forend if
end procedure
49
Algoritmo 5.10 Função do Comunicador responsável por gerenciar as leituras deconsumopast_status← 0
target_socket← 0
timeouts← 0
procedure poll_status(now)if now − past_status ≥ 1 then
past_status← current_time()target_id← sockets[target_socket % 3].id
Message requestrequest.sender_id← 0request.receiver_id← target_idrequest.message_id← MESSAGE_STATUS_REQUESTsend_message(request)timeout← falseMessage responsewhile not receive_message(response) do
if current_time()− past_status ≥ 2 thentimeout← truetimeouts← timeouts+ 1
if timeouts = 5 thentimeouts← 0
target_socket← target_socket+ 1
end ifbreak
end ifend whileif not timeout then
if response.message_id = MESSAGE_STATUS_RESPONSE andresponse.sender_id = target_id then
total_power ← response.payload
delta_power ← total_power−sockets_last_value[target_socket]db.insert_socket_data(target_id, delta_power)sockets_last_value[target_socket]← total_powertimeouts← 0
target_socket← target_socket+ 1
end ifend if
end ifend procedure
50
Dessa maneira, o Comunicador permite que as tomadas sejam controladas à dis-
tância e que o banco de dados seja preenchido com valores incrementais de leituras
em kWh para cada uma das três tomadas.
51
Capítulo 6
Resultados
6.1 Montagem
A montagem da central foi bastante simples, já que esta envolve poucos módulos
de hardware, como mostra a Figura 6.1. Já as tomadas inteligentes exigiram uma
quantia maior de elementos eletrônicos além de uma protoboard sobre a qual o
circuito medidor de tensão foi implementado. Sua montagem pode ser vista na
Figura 6.2.
Figura 6.1: Montagem da central
52
Figura 6.2: Montagem de uma tomada inteligente
6.2 Análises
É de suma importância garantir que o módulo de medição de potência funciona
de forma precisa, já que ele está no coração da operação do sistema como um todo.
Não faz sentido uma interface web polida e fluida se as medições realizadas pelo
sistema estiverem incorretas.
Tendo isso em mente, o medidor de potência foi testado contra um multímetro de
bancada em duas etapas. Primeiro, com uma lâmpada incandescente com potência
nominal de 42 W e em seguida com outra de 70 W. Os valores nominais são segundo
o fabricante e constam impressos nos bulbos das respectivas lâmpadas. A razão da
escolha dessas cargas de teste foi o fato de serem puramente resistivas. Isso permite
que sua potência ativa seja calculada como o produto dos valores eficazes de tensão
e corrente medidos pelo multímetro AC, já que sua potência aparente não possui
parcela reativa.
53
Nos testes, a medida final do sistema foi tomada como a média das 100 últimas
amostras, o que equivale a cerca de dez segundos de medição. Os resultados constam
na Tabela 6.1.
Tabela 6.1: Resultados dos testes de discrepância entre o medidor de potência e ummultímetro AC
Valor Nominal Multímetro Módulo do Projeto Discrepância42 W 38.7 W 39.7 W 2.6%70 W 67 W 68.5 W 2.2%
Para fins de visualização, as Figuras 6.3 e 6.4 apresentam cinco segundos de
medidas de potência das lâmpadas de 42 e 70 W, respectivamente.
Figura 6.3: Cinco segundos de leituras de potência ativa provenientes de uma lâmpadaincandescente de 42 W
54
Figura 6.4: Cinco segundos de leituras de potência ativa provenientes de uma lâmpadaincandescente de 70 W
Como justificativa para o piso de ruído escolhido, a Figura 6.5 apresenta quatro
segundos de leituras de potência ativa sem carga ligada à tomada e sem nenhum tipo
de filtragem aplicada. É possível observar eventuais valores negativos e poucos que
superam 4 W. Dessa maneira, considerando todas as leituras no intervalo [−∞, 4]
W como nulas, qualquer "falso positivo" representará um valor irrisório no total de
energia consumida. As Figuras 6.6 e 6.7 ilustram o piso de ruído sendo filtrado até
o momento em que as lâmpadas de 42 e 70 W são ativadas, respectivamente.
As Figuras 6.8 e 6.9 ratificam o cumprimento dos outros objetivos do projeto: im-
plementar uma rede de comunicação entre as tomadas e a central, registrar os dados
de consumo num banco de dados e exibí-los de forma significativa via interface web.
Com dados coletados das 00:15 às 09:15, pode-se observar a característica oscila-
tória do consumo de uma geladeira, totalizando 0.287 kWh e a natureza constante
de consumo de um ventilador, o qual totalizou 0.537 kWh. No contexto do pro-
jeto, apenas o gráfico de consumo horário foi implementado e isso viabiliza, além de
acompanhamento de consumo, comparações em termos quantitativos dos aspectos
de gasto energético de diferentes dispositivos.
55
Figura 6.5: Quatro segundos de leituras de potência ativa sem carga ou qualquer tipo defiltragem
Figura 6.6: Leituras de potência ativa demonstrando o cancelamento de ruído até omomento em que a carga de 42 W é ativada
56
Figura 6.7: Leituras de potência ativa demonstrando o cancelamento de ruído até omomento em que a carga de 70 W é ativada
Figura 6.8: Interface web exibindo dados de consumo coletados de uma geladeira, de 00:15às 09:15
57
Figura 6.9: Interface web exibindo dados de consumo coletados de um ventilador, de 00:15às 09:15
58
Capítulo 7
Conclusão
O foco desse trabalho foi projetar, desenvolver, prototipar e avaliar um sistema
de acompanhamento e controle de consumo de energia elétrica baseado em tomadas
inteligentes. As tomadas foram construídas em torno do microcontrolador Arduino
Nano, integrando diversos outros módulos de hardware. A central foi implementada
em um Raspberry Pi 3, integrando um módulo de hardware e diversos de software.
Os principais objetivos foram atingidos com êxito: as medidas de taxa de consumo
foram comparáveis às de um dispositivo dedicado a realizar medidas elétricas; a rede
de comunicação foi estabelecida entre as tomadas e a central, construída sobre um
protocolo escalável, no sentido de permitir payloads genéricas e diversas mensagens;
os dados foram registrados na central de forma efetiva e utilizando MongoDB, um
banco de dados amplamente utilizado, grátis e open-source. Também foi desenvol-
vida uma aplicação web responsiva, no estilo Single Page Application, que permite
o acompanhamento de consumo e o controle sobre as tomadas.
De maneira mais ampla, a viabilidade e a efetividade do sistema foram verifica-
dos com sucesso. Com apenas uma visualização dos dados de consumo o usuário
já consegue realizar comparações e o acompanhamento detalhado dos dispositivos
conectados às tomadas. Além disso, o sistema foi construído com módulos e com-
ponentes amplamente disponíveis a preços relativamente baixos no varejo brasileiro.
Um produto final pode ser desenvolvido de maneira ainda mais barata, já que todos
os circuitos integrados podem ser comprados em grandes quantidades e montados
na mesma placa de circuito impresso.
59
Dentro do contexto da Internet das Coisas [1] e da cultura open-source, espero
que o conhecimento aqui apresentado seja útil e possa servir como alicerce para
sistemas de complexidade ainda maior. As oportunidades são inúmeras: os dados
de consumo podem ser processados por algoritmos de aprendizagem computacional;
um sistema de controle automático sobre as tomadas pode ser implementado com
foco na diminuição do consumo; o protocolo de comunicação pode ser melhorado de
forma a incluir dispositivos de naturezas diferentes, fazendo da central um integrador
de dispositivos inteligentes no âmbito domiciliar.
Como Trabalho de Conclusão de Curso, o projeto unifica diversas áreas do conhe-
cimento abordadas pelo curso de Engenharia Eletrônica e de Computação: projeto
de hardware, sinais e sistemas, arquitetura de software e, principalmente, o desen-
volvimento de soluções sob consideração de restrições mútuas de diversas naturezas
- atividade que compõe o cerne da atuação do Engenheiro.
60
Referências Bibliográficas
[1] ZAMBARDA, P., “Internet das Coisas: entenda o conceito e o que muda
com a tecnologia”, http://www.techtudo.com.br/noticias/noticia/
2014/08/internet-das-coisas-entenda-o-conceito-e-o-que-muda-com-
tecnologia.html, 2014, [Online; acessado em 22 de Maio de 2017].
[2] SACHS, G., “The Internet of Things Explained: Making sense of
the next mega-trend”, https://qz.com/269840/the-internet-of-things-
explained-making-sense-of-the-next-mega-trend/, 2014, [Online; aces-
sado em 22 de Maio de 2017].
[3] GLOBO, G., “Para cobrir rombo, contas de luz devem ficar 7% mais caras em
2017”, http://g1.globo.com/jornal-nacional/noticia/2017/02/para-
cobrir-rombo-contas-de-luz-devem-ficar-7-mais-caras-em-
2017.html, 2017, [Online; acessado em 22 de Maio de 2017].
[4] TARGET, T., “RESTful API ”, http://searchcloudstorage.techtarget.
com/definition/RESTful-API, 2016, [Online; acessado em 22 de Maio de
2017].
[5] SODRé, U., “Séries de Fourier”, http://pessoal.sercomtel.com.br/
matematica/superior/fourier/sfourier.pdf, 2003, [Online; acessado em 23
de Maio de 2017].
[6] SANTOS, F., “Introdução às Séries de Fourier”, http://www.matematica.
pucminas.br/profs/web_fabiano/calculo4/sf.pdf, 2004, [Online; acessado
em 23 de Maio de 2017].
[7] WIKIPEDIA, “Série de Fourier”, https://pt.wikipedia.org/wiki/Série_
de_Fourier#cite_note-:1-5, 2017, [Online; acessado em 23 de Maio de 2017].
61
[8] FAULKNER, R., “AC vs. DC Powerlines and the Electrical Grid”,
http://www.theenergycollective.com/roger_rethinker/204396/ac-
versus-dc-powerlines, 2013, [Online; acessado em 23 de Maio de 2017].
[9] MEETTECHNIEK, “Measuring the PC power consumption”, http://
meettechniek.info/measurement-examples/pc-power-consumption.html,
2014, [Online; acessado em 23 de Maio de 2017].
[10] WIKIPEDIA, “AC power”, https://en.wikipedia.org/wiki/AC_power,
2017, [Online; acessado em 23 de Maio de 2017].
[11] WIKIPEDIA, “Power (physics)”, https://en.wikipedia.org/wiki/Power_
(physics), 2017, [Online; acessado em 23 de Maio de 2017].
[12] SVENSSON, S., “Power measurement techniques for non-sinusoidal con-
ditions”, https://www.sp.se/en/index/services/DSWM/Documents/
SvenssonStefanPhD.pdf, 1999, [Online; acessado em 23 de Maio de 2017].
[13] VIEIRA, M., “Teoria da Amostragem”, http://iris.sel.eesc.usp.br/
sel414m/Aula%2020%20-%20Teoria%20da%20Amostragem.pdf, [Online; aces-
sado em 23 de Maio de 2017].
[14] WIKIPEDIA, “Hypertext Transfer Protocol”, https://en.wikipedia.org/
wiki/Hypertext_Transfer_Protocol, [Online; acessado em 24 de Maio de
2017].
[15] ATMEL, “ATmega48A/PA/88A/PA/168A/PA/328/P”, http://www.atmel.
com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-
88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf, 2015, [Online;
acessado em 24 de Maio de 2017].
[16] ARDUINO, “Arduino Nano”, https://www.arduino.cc/en/Main/
arduinoBoardNano, 2017, [Online; acessado em 24 de Maio de 2017].
[17] SPARKFUN, “Serial Peripheral Interface (SPI)”, https://learn.sparkfun.
com/tutorials/serial-peripheral-interface-spi, [Online; acessado em
24 de Maio de 2017].
62
[18] SEMICONDUCTOR, N., “nRF24L01+ Single Chip 2.4GHz Transceiver”,
https://www.sparkfun.com/datasheets/Components/SMD/nRF24L01Pluss_
Preliminary_Product_Specification_v1_0.pdf, [Online; acessado em 25
de Maio de 2017].
[19] ALLEGRO, “Fully Integrated, Hall Effect-Based Linear Current Sensor IC
with 2.1 kVRMS Isolation and a Low-Resistance Current Conductor”, http:
//www.allegromicro.com/~/media/Files/Datasheets/ACS712-Datasheet.
ashx?la=en&hash=36988234DAD64352493E4A4686E6C3A927F4D7AC, [Online;
acessado em 25 de Maio de 2017].
[20] HYPERPHYSICS, “Hall Effect”, http://hyperphysics.phy-astr.gsu.edu/
hbase/magnetic/Hall.html, [Online; acessado em 16 de Julho de 2017].
[21] BRAGA, N. C., “Choque Elétrico”, http://www.newtoncbraga.com.br/
index.php/almanaque/1069-alm182.html, [Online; acessado em 26 de Maio
de 2017].
[22] WIKIPEDIA, “Isolação Galvânica”, https://pt.wikipedia.org/wiki/
Isolaç~ao_galvânica, [Online; acessado em 26 de Maio de 2017].
[23] MANIACBUG, “RF24 v1”, https://maniacbug.github.io/RF24/index.
html, 2015, [Online; acessado em 6 de Junho de 2017].
[24] LAVERY, B., “lib nrf24”, https://github.com/BLavery/lib_nrf24, 2014,
[Online; acessado em 6 de Junho de 2017].
[25] MONGODB, “Relational Vs Non Relational Database”, https:
//www.mongodb.com/scale/relational-vs-non-relational-database,
[Online; acessado em 6 de Junho de 2017].
[26] JSON.ORG, “Introdução ao JSON”, http://www.json.org/json-pt.html,
[Online; acessado em 6 de Junho de 2017].
[27] MONGODB, “PyMongo 3.4.0 Documentation”, https://api.mongodb.com/
python/current/#, [Online; acessado em 6 de Junho de 2017].
63
[28] KVALHEIM, C., “MongoDB Node.JS Driver”, https://www.npmjs.com/
package/mongodb, [Online; acessado em 6 de Junho de 2017].
[29] FOUNDATION, N., “About Node.js”, https://nodejs.org/en/about/, [On-
line; acessado em 8 de Junho de 2017].
[30] STRONGLOOP, “Express: Fast, unopinionated, minimalist web framework for
Node.js”, http://expressjs.com, [Online; acessado em 8 de Junho de 2017].
[31] STRONGLOOP, “Usando middlewares”, http://expressjs.com/pt-br/
guide/using-middleware.html, [Online; acessado em 8 de Junho de 2017].
[32] STRONGLOOP, “Hello world example”, http://expressjs.com/en/
starter/hello-world.html, [Online; acessado em 8 de Junho de 2017].
[33] NPM, “morgan”, https://www.npmjs.com/package/morgan, [Online; acessado
em 8 de Junho de 2017].
[34] NPM, “body-parser-json”, https://www.npmjs.com/package/body-parser-
json, [Online; acessado em 8 de Junho de 2017].
[35] STRONGLOOP, “Roteamento”, http://expressjs.com/pt-br/guide/
routing.html, [Online; acessado em 8 de Junho de 2017].
[36] TABLELESS, “O que é CSS?”, http://tableless.github.io/iniciantes/
manual/css/, [Online; acessado em 8 de Junho de 2017].
[37] GOOGLE, “Material Design”, https://material.io, [Online; acessado em 8
de Junho de 2017].
[38] MATERIALIZE, “Materialize CSS”, http://materializecss.com, [Online;
acessado em 8 de Junho de 2017].
[39] FOUNDATION, T. J., “jQuery API Documentation”, http://api.jquery.
com, [Online; acessado em 13 de Junho de 2017].
[40] WIKIPEDIA, “Single Page Application”, https://en.wikipedia.org/wiki/
Single-page_application, [Online; acessado em 13 de Junho de 2017].
64
[41] CHART.JS, “Chart.js: Simple yet flexible JavaScript charting for designers and
developers”, http://www.chartjs.org, [Online; acessado em 13 de Junho de
2017].
[42] WIKIPEDIA, “Device Driver”, https://en.wikipedia.org/wiki/Device_
driver, [Online; acessado em 21 de Junho de 2017].
[43] WIKIPEDIA, “Cursor”, https://en.wikipedia.org/wiki/Cursor_
(databases), [Online; acessado em 24 de Junho de 2017].
65
Apêndice A
Demonstração 1
Demonstração da seguinte relação:
cos(x)cos(nx+ θ) =cos((n− 1)x+ θ) + cos((n+ 1)x+ θ)
2(A.1)
Das relações de Euler:
cos(x) =ejx + e−jx
2(A.2)
Substituindo A.2 no lado esquerdo de A.1:
(ejx + e−jx)
2
(ejnx+jθ + e−jnx−jθ)
2=
ej(n+1)x+jθ + e−j(n−1)x−jθ + ej(n−1)x+jθ + e−j(n+1)x−jθ
4=
1
2
[(ej(n+1)x+jθ + e−j(n+1)x−jθ)
2+
(ej(n−1)x+jθ + e−j(n−1)x−jθ)
2
]=
cos((n− 1)x+ θ) + cos((n+ 1)x+ θ)
2
(A.3)
66
Apêndice B
Códigos-Fonte: Central
B.1 communicator.py
1 # −∗− coding : utf−8 −∗−2 from pymongo import MongoClient3 from ppr int import ppr int4 from datet ime import datet ime5 from my_config_reader import MyConfigReader6 import a s t7 import s t r u c t8 import b i n a s c i i9 import copy
10
11 import RPi .GPIO as GPIO12 from l ib_nr f24 import NRF2413 import time14 import sp idev15
16 # Message IDs17 MESSAGE_STATUS_REQUEST = 0x0018 MESSAGE_STATUS_RESPONSE = 0x0119 MESSAGE_RELAY_REQUEST = 0x0220 MESSAGE_RELAY_RESPONSE = 0x0321
22 # −−−−− Config Pars ing23 s e c t i on_xce iv e r = ’ x c e i v e r ’24 section_db = ’db ’25 s ec t ion_id = ’ id ’26 s e c t i on_po l l i n g = ’ p o l l i n g ’27
28 c on f i g = MyConfigReader ( )29
30 xceiver_pipe_read = ast . l i t e r a l_ e v a l ( c on f i g . get ( s ec t i on_xce ive r , ’ xceiver_pipe_read’ ) )
31 xce iver_pipe_write = ast . l i t e r a l_ e v a l ( c on f i g . get ( s ec t i on_xce ive r , ’xce iver_pipe_write ’ ) )
32 xceiver_message_size = ast . l i t e r a l_ e v a l ( c on f i g . get ( s ec t i on_xce ive r , ’xce iver_message_size ’ ) )
33 xce iver_channel = as t . l i t e r a l_ e v a l ( c on f i g . get ( s ec t i on_xce ive r , ’ xce iver_channel ’ ) )34
35 db_address = con f i g . get ( section_db , ’ db_address ’ )36 db_port = in t ( c on f i g . get ( section_db , ’ db_port ’ ) )37 db_name = con f i g . get ( section_db , ’db_name ’ )38 db_socket_collection_name = con f i g . get ( section_db , ’ db_socket_collection_name ’ )39 db_data_collection_name = con f i g . get ( section_db , ’ db_data_collection_name ’ )
67
40
41 id_master = ast . l i t e r a l_ e v a l ( c on f i g . get ( sect ion_id , ’ id_master ’ ) )42 id_socket_1 = ast . l i t e r a l_ e v a l ( c on f i g . get ( sect ion_id , ’ id_socket_1 ’ ) )43 id_socket_2 = ast . l i t e r a l_ e v a l ( c on f i g . get ( sect ion_id , ’ id_socket_2 ’ ) )44 id_socket_3 = ast . l i t e r a l_ e v a l ( c on f i g . get ( sect ion_id , ’ id_socket_3 ’ ) )45 s o cke t s = [ ]46 sockets_last_value = [ 0 . 0 , 0 . 0 , 0 . 0 ] ;47
48 po l l i ng_per i od = f l o a t ( c on f i g . get ( s e c t i on_po l l i ng , ’ po l l i ng_per i od ’ ) )49 po l l ing_re lay_per iod = f l o a t ( c on f i g . get ( s e c t i on_po l l i ng , ’ po l l ing_re lay_per iod ’ ) )50 pol l ing_max_tr ia l s = in t ( c on f i g . get ( s e c t i on_po l l i ng , ’ po l l ing_max_tr ia l s ’ ) )51
52 # −−−−− XCeiver Globals53 xc e i v e r = None54
55 # −−−−− DB Globals56 c l i e n t = None57 db = None58 c o l l e c t i on_so ck e t = None59 co l l e c t i on_data = None60
61 c l a s s Message :62 de f __init__( s e l f , r ece ive r_id , message_id = MESSAGE_STATUS_REQUEST, sender_id =
id_master ) :63 s e l f . message_id = message_id64 s e l f . sender_id = sender_id65 s e l f . r e c e i v e r_ id = rece i v e r_ id66 s e l f . payload = [0 f o r i in range ( xce iver_message_size − 3) ]67
68 de f to_buf fer ( s e l f ) :69 bu f f e r = [ 0 f o r i in range ( xce iver_message_size ) ]70
71 bu f f e r [ 0 ] = s e l f . r e c e i v e r_ id72 bu f f e r [ 1 ] = s e l f . sender_id73 bu f f e r [ 2 ] = s e l f . message_id74
75 i = 376
77 f o r i in range ( xce iver_message_size − 3) :78 bu f f e r [ i + 3 ] = s e l f . payload [ i ]79
80 r e turn bu f f e r81
82 @classmethod83 de f from_buffer ( c l s , bu f f e r ) :84 # pr in t ( ’ ’ . format ( bu f f e r ) )85 r e s u l t = c l s ( r e c e i v e r_ id = bu f f e r [ 0 ] , sender_id = bu f f e r [ 1 ] , message_id =
bu f f e r [ 2 ] )86
87 f o r i in range (3 , xce iver_message_size ) :88 r e s u l t . payload [ i − 3 ] = bu f f e r [ i ]89
90 r e turn r e s u l t91
92 de f set_payload ( s e l f , b u f f e r ) :93 f o r i in range (min ( l en ( bu f f e r ) , xce iver_message_size ) ) :94 s e l f . payload [ i ] = bu f f e r [ i ]95
96 de f l og ( s e l f ) :97 pr in t ( ’ Message Log : \ nRece iver ID : \ nSender ID : \nMessage ID : \nPayload :
’ . format ( s e l f . r ece ive r_id , s e l f . sender_id , s e l f . message_id , s e l f . payload ) )98
99 de f x c e i v e r _ i n i t i a l i z e ( ) :100 g l oba l x c e i v e r101 pr in t ">> I n i t i a l i z i n g t r a n s c e i v e r . . . "102
103 GPIO. setmode (GPIO.BCM)
68
104 GPIO. setwarn ings ( Fa l se )105
106 xc e i v e r = NRF24(GPIO, sp idev . SpiDev ( ) )107 xc e i v e r . begin (0 , 17)108
109 xc e i v e r . s e tPay loadS ize ( xce iver_message_size )110 xc e i v e r . setChannel ( xce iver_channel )111 xc e i v e r . setDataRate (NRF24 .BR_250KBPS)112 xc e i v e r . setPALevel (NRF24 .PA_MAX)113
114 xc e i v e r . setAutoAck (True )115 xc e i v e r . enableDynamicPayloads ( )116 xc e i v e r . enableAckPayload ( )117
118 xc e i v e r . openWritingPipe ( xce iver_pipe_write )119 xc e i v e r . openReadingPipe (1 , xceiver_pipe_read )120
121 pr in t ">> Transce ive r s t a tu s dump : "122 xc e i v e r . p r i n tDe t a i l s ( )123
124 de f xceiver_debug ( ) :125 pr in t ">> Debug t r a n s c e i v e r . . . "126
127 whi le l en ( message ) < 32 :128 message . append (0 )129
130 whi le (1 ) :131 s t a r t = time . time ( )132 xc e i v e r . wr i t e ( message )133 pr in t ( "Sent the message : " . format ( message ) )134 xc e i v e r . s t a r t L i s t e n i n g ( )135
136 whi le not x c e i v e r . a v a i l a b l e (0 ) :137 time . s l e e p (1 / 100)138 i f time . time ( ) − s t a r t > 2 :139 pr in t ( "Timed out . " )140 break141
142 rece ivedMessage = [ ]143 xc e i v e r . read ( rece ivedMessage , x c e i v e r . getDynamicPayloadSize ( ) )144 pr in t ( "Received : " . format ( rece ivedMessage ) )145
146 pr in t ( " Trans la t ing the rece ivedMessage in to unicode cha ra c t e r s " )147 s t r i n g = ""148 f o r n in rece ivedMessage :149 # Decode in to standard unicode s e t150 i f (n >= 32 and n <= 126) :151 s t r i n g += chr (n)152 pr in t ( "Out r e c e i v ed message decodes to : " . format ( s t r i n g ) )153
154 xc e i v e r . s t opL i s t en ing ( )155 time . s l e e p (1 )156
157 de f d b_ i n i t i a l i z e ( ) :158 g l oba l c l i e n t159 g l oba l db160 g l oba l c o l l e c t i on_so ck e t161 g l oba l co l l e c t i on_data162 g l oba l s o cke t s163 pr in t " I n i t i a l i z i n g MongoDB . . . "164
165 c l i e n t = MongoClient ( db_address , db_port )166
167 db = c l i e n t [ db_name ]168 c o l l e c t i on_so ck e t = db [ db_socket_collection_name ]169 co l l e c t i on_data = db [ db_data_collection_name ]170
69
171 c o l l e c t i on_so ck e t . drop ( )172
173 sockets_db = [174 175 ’ socket_id ’ : 1 ,176 ’ s t a tu s ’ : 1177 ,178 179 ’ socket_id ’ : 2 ,180 ’ s t a tu s ’ : 1181 ,182 183 ’ socket_id ’ : 3 ,184 ’ s t a tu s ’ : 1185 186 ]187
188 s o cke t s = copy . deepcopy ( sockets_db )189 c o l l e c t i on_so ck e t . insert_many ( sockets_db )190
191 de f f loat_from_bytes ( bytes ) :192 r e turn s t r u c t . unpack ( ’<f ’ , b i n a s c i i . h e x l i f y ( bytearray ( bytes ) ) . decode ( ’ hex ’ ) ) [ 0 ]193
194 de f main_loop ( ) :195 g l oba l x c e i v e r196 g l oba l s o cke t s197 pr in t ">> Sta r t i ng main loop . . . "198
199 then_relay = time . time ( )200 then_pol l ing = time . time ( )201 id_pointer = 0202 t imeouts = 0203
204 whi le (1 ) :205 now = time . time ( )206
207 i f (now − then_relay >= pol l ing_re lay_per iod ) :208 then_relay = time . time ( )209
210 db_sockets = co l l e c t i on_so ck e t . f i nd ()211
212 f o r db_socket in db_sockets :213 i f not db_socket [ ’ s t a tu s ’ ] == socke t s [ db_socket [ ’ socket_id ’ ] − 1 ] [ ’ s t a tu s ’
] :214 outgoingMessage = Message ( db_socket [ ’ socket_id ’ ] , MESSAGE_RELAY_REQUEST)215 outgoingMessage . set_payload ( [ db_socket [ ’ s t a tu s ’ ] ] )216
217 xc e i v e r . wr i t e ( outgoingMessage . to_buf fer ( ) )218
219 xc e i v e r . s t a r t L i s t e n i n g ( )220
221 t imeout = False222
223 whi le not x c e i v e r . a v a i l a b l e (0 ) :224 time . s l e e p (1 / 100)225 i f time . time ( ) − then_relay > 2 . 0 :226 pr in t ( "Timed out r e l ay . " )227 t imeout = True228 break229
230 i f not timeout :231 incomingMessageBuffer = [ 0 f o r i in range ( xce iver_message_size ) ]232 xc e i v e r . read ( incomingMessageBuffer , xce iver_message_size )233 incomingMessage = Message . from_buffer ( incomingMessageBuffer )234
235 i f ( incomingMessage . r e c e i v e r_ id == id_master ) :236 i f ( incomingMessage . message_id == MESSAGE_RELAY_RESPONSE) :
70
237 i f ( incomingMessage . sender_id == db_socket [ ’ socket_id ’ ] ) :238 i f ( incomingMessage . payload [ 0 ] == db_socket [ ’ s t a tu s ’ ] ) :239 s o cke t s [ db_socket [ ’ socket_id ’ ] − 1 ] [ ’ s t a tu s ’ ] = db_socket [ ’
s t a tu s ’ ]240
241 xc e i v e r . s t opL i s t en ing ( )242
243 now = time . time ( )244
245 i f (now − then_pol l ing >= po l l ing_per i od ) :246 then_pol l ing = time . time ( )247
248 target_id = socke t s [ id_pointer % 3 ] [ ’ socket_id ’ ]249 outgoingMessage = Message ( target_id )250
251 xc e i v e r . wr i t e ( outgoingMessage . to_buf fer ( ) )252 pr in t ( ">> Sent message to socke t #" . format ( target_id ) )253
254 xc e i v e r . s t a r t L i s t e n i n g ( )255
256 t imeout = False257
258 whi le not x c e i v e r . a v a i l a b l e (0 ) :259 time . s l e e p (1 / 100)260 i f time . time ( ) − then_pol l ing > 2 . 0 :261 pr in t ( "Timed out data . " )262 t imeout = True263 t imeouts += 1264 i f ( t imeouts == pol l ing_max_tr ia l s ) :265 t imeouts = 0266 id_pointer += 1267 break268
269 i f not timeout :270 incomingMessageBuffer = [ 0 f o r i in range ( xce iver_message_size ) ]271 xc e i v e r . read ( incomingMessageBuffer , xce iver_message_size )272 incomingMessage = Message . from_buffer ( incomingMessageBuffer )273
274 i f ( incomingMessage . r e c e i v e r_ id == id_master ) :275 i f ( incomingMessage . message_id == MESSAGE_STATUS_RESPONSE) :276 i f ( incomingMessage . sender_id == socke t s [ id_pointer % 3 ] [ ’ socket_id ’ ] ) :277 pr in t ( ’ S to r ing socket # s t a tu s ’ . format ( s o cke t s [ id_pointer % 3 ] [ ’
socket_id ’ ] ) )278 f l oa t_byte s = [ incomingMessage . payload [ i ] f o r i in range (4 ) ]279 rece ived_value = float_from_bytes ( f l oa t_byte s )280 data = 281 ’ socket_id ’ : s o cke t s [ id_pointer % 3 ] [ ’ socket_id ’ ] ,282 ’ va lue ’ : rece ived_value − sockets_last_value [ id_pointer % 3 ] ,283 ’ dt ’ : datet ime . now( ) . i s o f o rmat ( )284 285 co l l e c t i on_data . insert_one ( data )286 sockets_last_value [ id_pointer % 3 ] = rece ived_value287 t imeouts = 0288 id_pointer += 1289 pr in t ( data )290
291 xc e i v e r . s t opL i s t en ing ( )292
293 x c e i v e r _ i n i t i a l i z e ( )294 db_ i n i t i a l i z e ( )295 main_loop ( )
B.2 my_config_reader.py
1 # −∗− coding : utf−8 −∗−
71
2 import Conf igParser3
4 c l a s s MyConfigReader ( ) :5
6 de f __init__( s e l f , conf ig_path=" . / c on f i g . i n i " ) :7 s e l f . conf ig_path = config_path8 s e l f . c on f i g = Conf igParser . Sa feConf igParse r ( )9 s e l f . data =
10
11 t ry :12 s e l f . c on f i g . read ( s e l f . conf ig_path )13 s e c t i o n s = s e l f . c on f i g . s e c t i o n s ( )14 f o r s e c t i o n in s e c t i o n s :15 s ec t i on_opt ions = 16 opt ions = s e l f . c on f i g . opt i ons ( s e c t i o n )17 f o r opt ion in opt ions :18 s ec t i on_opt ions [ opt ion ] = s e l f . c on f i g . get ( s e c t i on , opt ion )19 s e l f . data [ s e c t i o n ] = sec t i on_opt ions20 except Exception as e :21 pr in t "Couldn ’ t read con f i g f i l e at : " . format ( s e l f . config_path , e .
message )22
23 de f get ( s e l f , s e c t i on , opt ion ) :24 i f s e c t i o n in s e l f . data and opt ion in s e l f . data [ s e c t i o n ] :25 r e turn s e l f . data [ s e c t i o n ] [ opt ion ]26 e l s e :27 r e turn None
B.3 config.ini
1 [ p o l l i n g ]2 po l l ing_re lay_per iod : 1 . 03 po l l i ng_per i od : 204 pol l ing_max_tr ia l s : 55
6 [ id ]7 id_master : 0x008 id_socket_1 : 0x019 id_socket_2 : 0x02
10 id_socket_3 : 0x0311
12 [ x c e i v e r ]13 xceiver_pipe_read : [ 0 xE8 , 0xE8 , 0xE8 , 0xE8 , 0xE1 ]14 xce iver_pipe_write : [ 0 xF0 , 0xF0 , 0xF0 , 0xF0 , 0xE1 ]15 xceiver_message_size : 3216 xce iver_channel : 0x7617
18 [ db ]19 db_address : l o c a l h o s t20 db_port : 2701721 db_name : economeasy22 db_socket_collection_name : socke t_ent r i e s23 db_data_collection_name : data_entr i e s24 time_storage_format : %%H:%%M
72
Apêndice C
Códigos-Fonte: Tomadas
C.1 arduino_wrapper.h
1 #pragma once2
3 #de f i n e DEFAULT_SERIAL S e r i a l4
5 #de f i n e ez_print DEFAULT_SERIAL. p r i n t6 #de f i n e ez_pr int ln DEFAULT_SERIAL. p r i n t l n7
8 #de f i n e ez_pwm(x , y ) ( analogWrite ( ( x ) , ( y ) ) )9 #de f i n e ez_time_micros ( ) ( micros ( ) )
10 #de f i n e ez_time_mil l i s ( ) ( m i l l i s ( ) )11 #de f i n e ez_time ( ) ( ez_time_micros ( ) / 1000000.0 f )12 #de f i n e ez_delay (x ) ( de lay ( ( x ) ) )13
14 #de f i n e ADC_RESOLUTION 1015 #de f i n e ADC_MAX_VOLTAGE 5.0 f16 #de f i n e ADC_TO_VOLT(x ) ( ( f l o a t ) ( x ) / ( ( f l o a t ) (1 << ADC_RESOLUTION) ) ∗
ADC_MAX_VOLTAGE)17 #de f i n e ez_adc (x ) ( analogRead ( ( x ) ) )18 #de f i n e ez_adc_volts ( x ) (ADC_TO_VOLT( ez_adc ( ( x ) ) ) )19
20 #de f i n e ON HIGH21 #de f i n e OFF LOW22 #de f i n e ez_make_output (x ) ( pinMode ( ( x ) , OUTPUT) )23 #de f i n e ez_make_input (x ) ( pinMode ( ( x ) , INPUT) )24 #de f i n e ez_output_mode (x , y ) ( d i g i t a lWr i t e ( ( x ) , ( y ) ) )25 #de f i n e ez_dig i ta l_read (x ) ( d ig i t a lRead (x ) )26
27 // Math s t u f f28
29 void ez_float_to_bytes ( f l o a t value , byte bytes_array [ ] ) 30 union 31 f l o a t f l o a t_va r i ab l e ;32 byte temp_array [ 4 ] ;33 u ;34
35 u . f l o a t_va r i ab l e = value ;36
37 memcpy( bytes_array , u . temp_array , 4) ;38 39
40 #de f i n e EZ_PI 3.14159265358979323 f41 #de f i n e EZ_TAU (2 . 0 f ∗ EZ_PI)
73
42 #de f i n e EZ_EULER 2.71828182845904523 f43
44 #de f i n e EZ_DEG_FACTOR (180 .0 f / EZ_PI)45 #de f i n e EZ_RAD_FACTOR (EZ_PI / 180 .0 f )46 #de f i n e EZ_DEG(x ) ( ( x ) ∗ EZ_DEG_FACTOR)47 #de f i n e EZ_RAD(x ) ( ( x ) ∗ EZ_RAD_FACTOR)48
49 #de f i n e EZ_SQR(x ) ( ( x ) ∗ ( x ) )50 #de f i n e EZ_POW(a , b) ( powf ( a , b ) )51 #de f i n e EZ_MIN(a , b) ( ( a ) < (b) ? ( a ) : (b) )52 #de f i n e EZ_MAX(a , b) ( ( a ) > (b) ? ( a ) : (b ) )53 #de f i n e EZ_ABS(x ) ( ( x ) >= 0.0 f ? ( x ) : −(x ) )54 #de f i n e EZ_SQRT(x ) ( sq r t ( x ) )
C.2 current_sensor.h
1 #pragma once2
3 #de f i n e EZ_CURRENT_SENSOR_PORT A04 #de f i n e EZ_CURRENT_SENSOR_SENSITIVITY 10 .0 f // (A / V)5 #de f i n e EZ_CURRENT_SENSOR_CALIBRATION_SIZE 10006 #de f i n e EZ_CURRENT_SENSOR_SAMPLE_TIME 100000 // ( us )7 #de f i n e EZ_CURRENT_SENSOR_ENABLE_CALIBRATION8
9 f l o a t cur r ent_sensor_o f f s e t = 0 .0 f ;10
11 void e z_cur r en t_sen so r_ in i t i a l i z e ( ) ;12 void ez_current_sensor_ca l ibrate ( ) ;13 f l o a t ez_current_sensor_sample ( ) ;14 f l o a t ez_current_sensor_convert ( f l o a t adc_output ) ;
C.3 current_sensor.ino
1 #inc lude " current_sensor . h"2
3 void e z_cur r en t_sen so r_ in i t i a l i z e ( )4 5 ez_pr int ln ( ">> I n i t i a l i z i n g cur rent s enso r . " ) ;6 #i f d e f EZ_CURRENT_SENSOR_ENABLE_CALIBRATION7 ez_current_sensor_ca l ibrate ( ) ;8 #end i f9
10
11 void ez_current_sensor_ca l ibrate ( )12 13 ez_pr int ln ( ">> Sta r t i ng cur rent s enso r c a l i b r a t i o n . " ) ;14
15 f l o a t t o t a l = 0 .0 f ;16
17 f o r ( unsigned i = 0 ; i < EZ_CURRENT_SENSOR_CALIBRATION_SIZE; i++)18 19 t o t a l += ez_current_sensor_sample ( ) ;20 21
22 cur r ent_sensor_o f f s e t = 511 .5 f − t o t a l / EZ_CURRENT_SENSOR_CALIBRATION_SIZE;23
24 ez_pr int ln ( ">> Current s enso r c a l i b r a t e d : " ) ;25 ez_print ( " O f f s e t : " ) ; ez_pr int ln ( cur r ent_senso r_o f f s e t ) ;26 27
28 f l o a t ez_current_sensor_sample ( )29 30 r e turn ( f l o a t ) ez_adc (EZ_CURRENT_SENSOR_PORT) + cur rent_sensor_o f f s e t ;
74
31 32
33 f l o a t ez_current_sensor_convert ( f l o a t adc_output )34 35 r e turn (ADC_TO_VOLT( adc_output ) − ADC_MAX_VOLTAGE / 2 .0 f ) ∗
EZ_CURRENT_SENSOR_SENSITIVITY;36
C.4 voltage_sensor.h
1 #pragma once2
3 #de f i n e EZ_VOLTAGE_SENSOR_PORT A54 #de f i n e EZ_VOLTAGE_SENSOR_ATTENUATION 162.18416661 f5 #de f i n e EZ_VOLTAGE_SENSOR_ENABLE_CALIBRATION6 #de f i n e EZ_VOLTAGE_SENSOR_CALIBRATION_TIME 2000000 // us7
8 f l o a t vo l tage_senso r_o f f s e t = 0 .0 f ;9
10 void e z_vo l t ag e_sen so r_ in i t i a l i z e ( ) ;11 void ez_vol tage_sensor_ca l ibrate ( ) ;12 f l o a t ez_voltage_sensor_sample ( ) ;13 f l o a t ez_voltage_sensor_convert ( f l o a t adc_output ) ;
C.5 voltage_sensor.ino
1 #inc lude " vo l tage_sensor . h"2
3 void e z_vo l t ag e_sen so r_ in i t i a l i z e ( )4 5 ez_pr int ln ( ">> I n i t i a l i z i n g vo l tage s enso r . " ) ;6
7 #i f d e f EZ_VOLTAGE_SENSOR_ENABLE_CALIBRATION8 ez_vol tage_sensor_ca l ibrate ( ) ;9 #end i f
10 11
12 void ez_vol tage_sensor_ca l ibrate ( )13 14 ez_pr int ln ( ">> Sta r t i ng vo l tage s enso r c a l i b r a t i o n . " ) ;15 ez_delay (2000) ;16
17 unsigned long then = ez_time_micros ( ) ;18
19 f l o a t max = 0 .0 f ;20 f l o a t min = 1024.0 f ;21
22 whi le ( ez_time_micros ( ) − then < EZ_VOLTAGE_SENSOR_CALIBRATION_TIME)23 24 f l o a t read ing = ez_voltage_sensor_sample ( ) ;25
26 i f ( r ead ing < min )27 28 min = read ing ;29 30
31 i f ( r ead ing > max)32 33 max = read ing ;34 35 36
37 vo l tage_senso r_o f f s e t = 511 .5 f − ( f l o a t ) (max + min) / 2 .0 f ;
75
38
39 ez_pr int ln ( ">> Voltage s enso r c a l i b r a t e d : " ) ;40 ez_print ( "Max: " ) ; ez_pr int ln ( ez_voltage_sensor_convert (max) ) ;41 ez_print ( "Min : " ) ; ez_pr int ln ( ez_voltage_sensor_convert (min ) ) ;42 ez_print ( "Middle : " ) ; ez_pr int ln ( ( f l o a t ) (max + min) / 2 .0 f ) ;43 ez_print ( " O f f s e t : " ) ; ez_pr int ln ( vo l tage_senso r_o f f s e t ) ;44 45
46 f l o a t ez_voltage_sensor_sample ( )47 48 r e turn ( f l o a t ) ez_adc (EZ_VOLTAGE_SENSOR_PORT) + vo l tage_senso r_o f f s e t ;49 50
51 f l o a t ez_voltage_sensor_convert ( f l o a t adc_output )52 53 r e turn (ADC_TO_VOLT( adc_output ) − ADC_MAX_VOLTAGE / 2 .0 f ) ∗
EZ_VOLTAGE_SENSOR_ATTENUATION;54
C.6 power_meter.h
1 #inc lude " current_sensor . h"2 #inc lude " vo l tage_sensor . h"3
4 #pragma once5
6 #de f i n e EZ_POW_METER_SAMPLES 1007 #de f i n e EZ_POW_METER_SAMPLING_FREQUENCY 1000.0 f8 #de f i n e EZ_POW_METER_SAMPLING_PERIOD 1.0 f / EZ_POW_METER_SAMPLING_FREQUENCY ∗
1000000.0 f // us9
10 #de f i n e EZ_POW_METER_ENABLE_CALIBRATION11 #de f i n e EZ_POW_METER_CALIBRATION_TIME 3000000 // us12
13 #de f i n e EZ_POW_METER_NOISE_THRESHOLD_ENABLE14 #de f i n e EZ_POW_METER_NOISE_FLOOR 4.0 f15
16 f l o a t pow_meter_total_power = 0 .0 f ;17 f l o a t pow_meter_last_reading = 0 .0 f ;18 unsigned long pow_meter_last_reading_time = 0 ;19
20 f l o a t pow_meter_offset = 0 .0 f ;21 bool pow_meter_calibrated = f a l s e ;22
23 f l o a t vo l tage_read ings [EZ_POW_METER_SAMPLES] ;24 f l o a t current_readings [EZ_POW_METER_SAMPLES] ;25
26 void ez_power_meter_init ia l ize ( ) ;27 void ez_power_meter_calibrate ( ) ;28 f l o a t ez_power_meter_sample ( ) ;29 void ez_power_meter_read ( ) ;
C.7 power_meter.ino
1 #inc lude "power_meter . h"2
3 void ez_power_meter_init ia l ize ( )4 5 ez_pr int ln ( ">> I n i t i a l i z i n g power meter . " ) ;6 #i f d e f EZ_POW_METER_ENABLE_CALIBRATION7 ez_power_meter_calibrate ( ) ;8 #end i f9
76
10
11 void ez_power_meter_calibrate ( )12 13 ez_pr int ln ( ">> Sta r t i ng power meter c a l i b r a t i o n . " ) ;14 unsigned long then = ez_time_micros ( ) ;15
16 f l o a t sum = 0.0 f ;17 unsigned long samples = 0 ;18
19 unsigned long now = ez_time_micros ( ) ;20 whi le (now − then < EZ_POW_METER_CALIBRATION_TIME)21 22 now = ez_time_micros ( ) ;23 sum += ez_power_meter_sample ( ) ;24 samples++;25 26
27 pow_meter_offset = − sum / samples ;28
29 ez_pr int ln ( ">> Power meter c a l i b r a t e d : " ) ;30 ez_print ( " O f f s e t : " ) ; ez_pr int ln ( pow_meter_offset ) ;31
32 pow_meter_calibrated = true ;33 34
35 f l o a t ez_power_meter_sample ( )36 37 unsigned sample_counter = 0 ;38 unsigned long then = ez_time_micros ( ) ;39
40 whi le ( sample_counter < EZ_POW_METER_SAMPLES)41 42 unsigned long now = ez_time_micros ( ) ;43
44 i f (now − then >= EZ_POW_METER_SAMPLING_PERIOD)45 46 then = now ;47
48 vo l tage_read ings [ sample_counter ] = ez_voltage_sensor_sample ( ) ;49 current_readings [ sample_counter++] = ez_current_sensor_sample ( ) ;50 51 52
53 f l o a t sum = 0.0 f ;54
55 f o r ( unsigned i = 0 ; i < EZ_POW_METER_SAMPLES; i++)56 57 sum += ez_voltage_sensor_convert ( vo l tage_read ings [ i ] ) ∗
ez_current_sensor_convert ( current_readings [ i ] ) ;58 59
60 f l o a t power = sum / EZ_POW_METER_SAMPLES + pow_meter_offset ;61
62 i f ( pow_meter_calibrated )63 64 power = EZ_MAX(0 . 0 f , power ) ;65
66 #i f d e f EZ_POW_METER_NOISE_THRESHOLD_ENABLE67 i f ( power < EZ_POW_METER_NOISE_FLOOR)68 69 power = 0 .0 f ;70 71 #end i f72 73
74 r e turn power ;75
77
76
77 void ez_power_meter_read ( )78 79 // Avoid erroneous i n i t i a l i n t e g r a t i o n step80 i f ( pow_meter_last_reading_time != 0)81 82 // us / 10^6 = s −> s / 3600 = h −> w / 10^3 = kw83 f l o a t hours = ( ez_time_micros ( ) − pow_meter_last_reading_time ) /
3600000000000.0 f ;84 pow_meter_total_power += hours ∗ pow_meter_last_reading ;85 86
87 pow_meter_last_reading_time = ez_time_micros ( ) ;88 pow_meter_last_reading = ez_power_meter_sample ( ) ;89
C.8 relay.h
1 #inc lude "arduino_wrapper . h"2
3 #pragma once4
5 #de f i n e EZ_RELAY_ON OFF6 #de f i n e EZ_RELAY_OFF ON7 #de f i n e EZ_RELAY_PORT 38
9 void e z_ r e l a y_ i n i t i a l i z e ( ) ;10 void ez_re lay_toggle ( bool on ) ;
C.9 relay.ino
1 #inc lude "power_meter . h"2
3 void e z_ r e l a y_ i n i t i a l i z e ( ) 4 ez_pr int ln ( " I n i t i a l i z i n g r e l a y . " ) ;5
6 ez_make_output (EZ_RELAY_PORT) ;7 8
9 void ez_re lay_toggle ( bool on ) 10 i f ( on ) 11 ez_output_mode (EZ_RELAY_PORT, EZ_RELAY_ON) ;12 e l s e 13 ez_output_mode (EZ_RELAY_PORT, EZ_RELAY_OFF) ;14 15
C.10 transceiver.h
1 #pragma once2
3 #inc lude <SPI . h>4 #inc lude <RF24 . h>5 #inc lude " r e l ay . h"6 #inc lude "power_meter . h"7
8 #de f i n e EZ_XCEIVER_CE_PIN 99 #de f i n e EZ_XCEIVER_CS_PIN 10
10 #de f i n e EZ_XCEIVER_CHANNEL 0x7611 #de f i n e EZ_XCEIVER_DATA_RATE RF24_250KBPS12 #de f i n e EZ_XCEIVER_POWER_LEVEL RF24_PA_MAX
78
13 #de f i n e EZ_XCEIVER_WRITE_PIPE 0xE8E8E8E8E114 #de f i n e EZ_XCEIVER_READ_PIPE 0xF0F0F0F0E115 #de f i n e EZ_XCEIVER_MESSAGE_SIZE 3216
17 #de f i n e EZ_XCEIVER_MY_ID 118 #de f i n e EZ_XCEIVER_MASTER_ID 019
20 #de f i n e EZ_XCEIVER_MESSAGE_STATUS_REQUEST 021 #de f i n e EZ_XCEIVER_MESSAGE_STATUS_RESPONSE 122 #de f i n e EZ_XCEIVER_MESSAGE_RELAY_REQUEST 223 #de f i n e EZ_XCEIVER_MESSAGE_RELAY_RESPONSE 324
25 typede f s t r u c t ez_xceiver_message_def26 27 byte message_id ;28 byte sender_id ;29 byte r e c e i v e r_ id ;30 byte payload [EZ_XCEIVER_MESSAGE_SIZE − 3 ] ;31 ez_xceiver_message ;32
33 RF24 xc e i v e r (EZ_XCEIVER_CE_PIN, EZ_XCEIVER_CS_PIN) ;34 byte xce iver_id = 0 ;35 byte xce i ve r_bu f f e r [EZ_XCEIVER_MESSAGE_SIZE ] ;36
37 void e z_ t r a n s c e i v e r_ i n i t i a l i z e ( ) ;38 bool ez_transceiver_decode_message ( ez_xceiver_message∗ message ) ;39 bool ez_transce iver_check_rece ive ( ez_xceiver_message∗ message ) ;40 void ez_transce iver_send ( ez_xceiver_message∗ message ) ;41 void ez_transce iver_update ( ) ;42 void ez_transceiver_print_message ( ez_xceiver_message∗ message ) ;
C.11 transceiver.ino
1 #inc lude " t r a n s c e i v e r . h"2
3 void e z_ t r a n s c e i v e r_ i n i t i a l i z e ( ) 4 ez_pr int ln ( " I n i t i a l i z i n g t r a n s c e i v e r . " ) ;5 xc e i v e r . begin ( ) ;6 xc e i v e r . setDataRate (EZ_XCEIVER_DATA_RATE) ;7 xc e i v e r . setPALevel (EZ_XCEIVER_POWER_LEVEL) ;8 xc e i v e r . setChannel (EZ_XCEIVER_CHANNEL) ;9 xc e i v e r . openWritingPipe (EZ_XCEIVER_WRITE_PIPE) ;
10 xc e i v e r . openReadingPipe (1 , EZ_XCEIVER_READ_PIPE) ;11 xc e i v e r . enableDynamicPayloads ( ) ;12 xc e i v e r . powerUp ( ) ;13 14
15 bool ez_transceiver_decode_message ( ez_xceiver_message∗ message ) 16 message−>rece i v e r_ id = xce ive r_bu f f e r [ 0 ] ;17
18 i f ( message−>rece i v e r_ id != EZ_XCEIVER_MY_ID) 19 r e turn f a l s e ;20 21
22 message−>sender_id = xce ive r_bu f f e r [ 1 ] ;23 message−>message_id = xce ive r_bu f f e r [ 2 ] ;24
25 f o r ( unsigned i = 3 ; i < EZ_XCEIVER_MESSAGE_SIZE; i++) 26 message−>payload [ i − 3 ] = xce iv e r_bu f f e r [ i ] ;27 28
29 r e turn true ;30 31
32 bool ez_transce iver_check_rece ive ( ez_xceiver_message∗ message )
79
33 xc e i v e r . s t a r t L i s t e n i n g ( ) ;34
35 i f ( x c e i v e r . a v a i l a b l e ( ) ) 36 xc e i v e r . read ( xce ive r_buf f e r , EZ_XCEIVER_MESSAGE_SIZE) ;37 xc e i v e r . s t opL i s t en ing ( ) ;38
39 i f ( ez_transceiver_decode_message ( message ) ) 40 r e turn true ;41 42 43
44 r e turn f a l s e ;45 46
47 void ez_transce iver_send ( ez_xceiver_message∗ message ) 48 byte message_content [EZ_XCEIVER_MESSAGE_SIZE ] ;49
50 f o r ( unsigned i = 0 ; i < EZ_XCEIVER_MESSAGE_SIZE; i++) 51 message_content [ i ] = 0 ;52 53
54 message_content [ 0 ] = message−>rece i v e r_ id ;55 message_content [ 1 ] = message−>sender_id ;56 message_content [ 2 ] = message−>message_id ;57
58 f o r ( unsigned i = 0 ; i < EZ_XCEIVER_MESSAGE_SIZE − 3 ; i++) 59 message_content [ i + 3 ] = message−>payload [ i ] ;60 61
62 xc e i v e r . wr i t e ( message_content , EZ_XCEIVER_MESSAGE_SIZE) ;63 64
65 void ez_transceiver_print_message ( ez_xceiver_message∗ message ) 66 ez_print ( "Message_id : " ) ; ez_pr int ln ( message−>message_id ) ;67 ez_print ( "Sender_id : " ) ; ez_pr int ln ( message−>sender_id ) ;68 ez_print ( "Receiver_id : " ) ; ez_pr int ln ( message−>rece i v e r_ id ) ;69
70 ez_print ( "Payload : " ) ;71 f o r ( unsigned i = 0 ; i < EZ_XCEIVER_MESSAGE_SIZE − 3 ; i++) 72 ez_print ( message−>payload [ i ] ) ;73 ez_print ( " " ) ;74 75 ez_pr int ln ( ) ;76 77
78 void ez_transce iver_update ( ) 79 ez_xceiver_message message ;80
81 i f ( ez_transce iver_check_rece ive (&message ) ) 82 ez_xceiver_message response ;83
84 i f ( message . message_id == EZ_XCEIVER_MESSAGE_STATUS_REQUEST) 85 f o r ( unsigned i = 0 ; i < EZ_XCEIVER_MESSAGE_SIZE − 3 ; i++) 86 re sponse . payload [ i ] = 0 ;87 88
89 re sponse . sender_id = EZ_XCEIVER_MY_ID;90 re sponse . r e c e i v e r_ id = message . sender_id ;91 re sponse . message_id = EZ_XCEIVER_MESSAGE_STATUS_RESPONSE;92
93 // f l o a t va lue = ( f l o a t ) random(10000) / 10 .0 f ;94
95 byte value_bytes [ 4 ] ;96 ez_float_to_bytes ( pow_meter_total_power , value_bytes ) ;97
98 f o r ( unsigned i = 0 ; i < 4 ; i++) 99 re sponse . payload [ i ] = value_bytes [ i ] ;
80
100 101
102 ez_transce iver_send(&response ) ;103 104
105 i f ( message . message_id == EZ_XCEIVER_MESSAGE_RELAY_REQUEST) 106 ez_re lay_toggle ( message . payload [ 0 ] ) ;107
108 f o r ( unsigned i = 0 ; i < EZ_XCEIVER_MESSAGE_SIZE − 3 ; i++) 109 re sponse . payload [ i ] = 0 ;110 111
112 re sponse . payload [ 0 ] = message . payload [ 0 ] ;113
114 re sponse . sender_id = EZ_XCEIVER_MY_ID;115 re sponse . r e c e i v e r_ id = message . sender_id ;116 re sponse . message_id = EZ_XCEIVER_MESSAGE_RELAY_RESPONSE;117
118 ez_transce iver_send(&response ) ;119 120 121
C.12 socket.ino
1 #inc lude "Arduino . h"2
3 #inc lude "arduino_wrapper . h"4
5 #inc lude " current_sensor . h"6 #inc lude " vo l tage_sensor . h"7 #inc lude "power_meter . h"8 #inc lude " t r a n s c e i v e r . h"9 #inc lude " r e l ay . h"
10
11 void setup ( )12 13 S e r i a l . begin (9600) ;14
15 e z_cur r en t_sen so r_ in i t i a l i z e ( ) ;16 e z_vo l t ag e_sen so r_ in i t i a l i z e ( ) ;17 ez_power_meter_init ia l ize ( ) ;18 e z_ t r a n s c e i v e r_ i n i t i a l i z e ( ) ;19 e z_ r e l a y_ i n i t i a l i z e ( ) ;20 21
22 void loop ( )23 24 ez_power_meter_read ( ) ;25 ez_transce iver_update ( ) ;26
81