Skip to content

goodeath/cloud-sensors

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Introdução

O MQTT (Message Queuing Telemetry Transport) é um protocolo para comunicação de mensagens entre dispositivos como sensores e computadores móveis. Neste projeto, integraremos o modelo anterior, que se baseava na comunicação UART entre a placa raspberry e o node MCU, com a comunicação MQTT para uma interface de usuário remota.

Estrutura do projeto

Segue abaixo a estrutura de diretórios do projeto

├── dashboard
│   └── chart.js
│   └── index.html
│   └── mqttws31.min.js
├── nodemcu/uart
│   └── uart.ino
├── README.md
└── rpi
    ├── display.s
    ├── examples
    │   └── countdown.c
    ├── lib
    │   ├── fileio.s
    │   ├── gpio.s
    │   ├── lcd.s
    │   └── utils.s
    ├── LICENSE
    ├── makefile
    ├── system.c
    └── uart
        └── uart.c

cloud-sensors/nodemcu/ - Obtém os valores registrados pelos sensores, realiza a comunicação via UART com a SBC (Raspberry) e MQTT com a interface remota.
cloud-sensors/rpi/examples/ - Possui um programa C utilizando as bibliotecas exportadas
cloud-sensors/rpi/lib/ - Pasta com os módulos utilizados na solução
cloud-sensors/dashboard/ - Interface remota feita em Javascript

Bibliotecas

lib/fileio.s

Possui a macro open_file para abertura de arquivos. Recebe no R0, o descritor do arquivo aberto, no R1, o modo de abertura do arquivo.

lib/utils.s

Possui a macro nanosleep para fazer o programa parar durante o tempo específicado. R0 é um ponteiro para quantidade de segundos e R1 é um ponteiro para quantidade de nanossegundos.

lib/gpio.s

Possui macros para configurar pinos como entrada e saída, alterar o nível lógico no modo de saída e ler o nível lógico em determinado pino. A sessão de pinos tem seu array configurado da seguinte maneira:

lib/lcd.s

Biblioteca principal para o controle do LCD

display.s

Programa principal para execução do contador. O valor do contador fica registrado em R1, e as flags para pausar/continuar e reiniciar contagem, estão nos registradores R6 e R5, respectivamente

Makefile

Para facilitar a construção do programa, existe um makefile dentro da pasta rpi, onde é possível executar: $ make csystem Para construção do executável. Logo em seguida basta utilizar: $ sudo ./system para executar o programa

countdown: counter
example: cexample
uart: cuart
system: csystem

counter: display.o
	gcc -o display display.o

display.o: display.s lib/lcd.o
	as -o display.o display.s

lib/lcd.o: lib/utils.o lib/gpio.o lib/fileio.o
	as -o lib/lcd.o lib/lcd.s

lib/fileio.o: lib/fileio.s
	as -o lib/fileio.o lib/fileio.s

lib/gpio.o: lib/gpio.s
	as -o lib/gpio.o lib/gpio.s

lib/utils.o: lib/utils.s
	as -o lib/utils.o lib/utils.s

uart/uart.o: uart/uart.c
	gcc -c uart/uart.c -lwiringPi

cexample: examples/countdown.c lib/lcd.s
	gcc -o countdown examples/countdown.c lib/lcd.s

cuart: uart/uart.c lib/lcd.s
	gcc -o uartx uart/uart.c lib/lcd.s -lwiringPi

csystem: system.c lib/lcd.s
	gcc -o system system.c lib/lcd.s -lwiringPi

Dispositivos

Abaixo está presente os dispositivos utilizados, suas características e documentação utilizada para desenvolvimento do projeto

NodeMCU

A plataforma NodeMCU é uma placa de desenvolvimento que combina o chip ESP8266, uma interface usb-serial e um regulador de tensão 3.3V. Mais dados sobre sua documentação podem ser encontrados aqui.

Alguns pinos utilizados na NodeMCU estão listados na tabela abaixo:

Pino Descrição
D0 Sensor Digital 1
D1 Sensor Digital 2
A0 Sensor Analógico 1
TX Envio comunicação serial
RX Recebimento comunicação serial

Raspberry Pi Zero

Além de contar com os pinos para comunicação UART com a Node MCU, agora utilizam-se 3 botões para a interface local humana. A função destes botões será explicada posteriormente.

Pino GPIO Descrição
8 14 TX
10 15 RX
- 5 BTN
- 19 BTN
- 26 BTN

Comandos

Para troca de informações entre os dispositivos, foram definidos comandos. Cada informação é enviada com 1 byte, onde os três bits menos significativos indicam um comando:

B2 B1 B0 Descrição
0 0 1 Solicita status da NodeMCU
0 1 0 Solicita status do sensor
0 1 1 Solicita valor do sensor

Os bits mais significativos B7-B3, indicam qual sensor vai ser executado o comando: 0 - 31 (32 sensores).

Arquitetura

Em relação ao projeto anterior, manteve-se a conexão via UART entre a SBC e a node MCU. O que mudou foi a adição de novos dados sendo passados e o protocolo MQTT estabelecido entre a Node e a interface local, intermediada pelo broker do laboratório.

Funcionamento

NodeMCU

Os valores dos sensores e seus status foram armazenados em dois vetores de 32 posições. Uma vez que a informação está presente, é recuperada de maneira genérica pela estrutura da informação, onde é separado informação e sensor associado. Desta forma, caso se queira adicionar um novo sensor, basta garantir que a informação vai estar presente na posição escolhida para o mesmo.

A NodeMCU fica constantemente ouvindo o seu canal RX, e toda vez que recebe um pedido, efetua os procedimentos anteriores para retornar a resposta.

Raspberry PI

Utilizando a implementação para o UART feita no projeto anterior como biblioteca, aqui a nova função para a raspberry é assumir o papel de interface humana local. O usuário, a partir de botões da placa, poderá alterar o sensor, modo de funcionamento e tempo do intervalo. Ao mesmo tempo, todas estas mudanças são registradas no visor LCD.

Inicialmente, define-se 4 constantes. Cada constante representa um comando que será interpretado pela NodeMCU. MODE_SENSOR (0) é o modo em que será exibido o valor atual do sensor escolhido e também possibilitará que o usuário possa alternar entre os possíveis sensores. MODE_FREQUENCY (1) é o modo em que o usuário pode alterar o valor atual do tempo de intervalo da comunicação e esta alteração afetará a interface remota, além de, claro, visualizar no LCD o valor atual vingente. No modo MODE_MCU_STATUS (2), mostra o status de comunicação da NodeMCU. MODE_SENSOR_STATUS (3) tal qual o modo anterior, mostra o status de conexão, porém do sensor específico.

Abaixo, há outras constantes e variáveis úteis para facilitação de entendimento do código. TOTAL_SENSOR representa o número totais de sensores da comunicação, ou seja 1 analógico e 2 digitais. A variável current_screen inicializa o programa em modo de tela de valor de sensor. Em current_frequency a frequência de tempo inicial é definida como 5.

Na imagem acima, ilustra-se 4 funções que definem a lógica por trás da seleção de sensores e alteração da frequência. Numa comunicação UART, só é possível enviar 8 bits de informação. Dito isso, pré-definiu-se que os 5 primeiros bits representarão o número do sensor ou o valor da frequência, e os 3 últimos o a seleção de comandos. Assim, como tem-se 5 bits reservados a sensor e frequência, o usuário pode selecionar do sensor 00000 (0) ao sensor 11111 (31), totalizando 32 sensores. Da mesma maneira, como os mesmos 5 bits são reservados a frequência, o usuário pode alterar a frequência de 0 a 31 segundos. Como dito anteriormente, os 3 últimos bits são responsáveis por decidir o comando. 001 para status do nodemcu, 010 para status do sensor, 011 para o valor do sensor e 100 para frequência.

Iniciando pela primeira função get_mcu_status_code, aqui meramente é retornado o valor 1 ou, em binário de 8 bits, XXXXX001, sendo os 5 primeiros bits em don't care já que o número do sensor ou a frequência são irrelevantes para a tela de status da node. Em sequência, get_sensor_status_code recebe o valor do sensor atual, faz um deslocalamento em 3 bits (multiplica por 8) e soma com 2. Dessa forma, se, por exemplo, o usuário deseja obter o status do sensor 2, a função retornará (28)+2, ou seja, 18, que, em binário de 8 bits é representado por 00010 (Sensor 2) 010 (Comando 2). As funções get_read_sensor_code e get_set_frequency_code seguem exatamente a mesma lógica anterior, a primeira recebe o sensor novamente, multiplica por 8 e soma com 3 (Teria-se o binário 00010 011 pegando o mesmo sensor do exemplo anterior) e a segunda recebe a frequência, também multiplica por 3 e adiciona ao comando 4, dessa forma, caso o usuário deseje estabelecer a velocidade como 22 segundos, por exemplo, teria-se o valor 228 + 4, ou 180, que em binário é representado como 10110 (22) 100 (4).

Já na função principal, a comunicação UART é inicializada e os 3 botões de interface local humana são pinados. O botão de pino 05 é resposável por alterar a tela atual. Os pinos 19 e 26 são responsáveis por, respectivamente, voltar ou avançar na seleção de sensores ou diminuir e aumentar o valor da frequência. Em sequência cria-se algumas variáveis para ajudar a legibilidade do código.

Entrando em loop, o sistema lê o valor atual do pino 05, caso esteja pressionado, a váriavel current_screen é incrementada. Como só existem 4 telas possíveis, o valor da tela atual retorna ao ponto inicial (0) quando o usuário pressionar o botão 05 pela 4° vez. Após isso, gera-se um delay de 500 milissegundos para evitar o fator boucing do botão. Neste ponto, nota-se que o botão 05 é o principal, pois ele decide em que modo a interface local irá operar, podendo alterar a função dos dois botões de avanço e retardo ou, até mesmo, inutilizá-los em telas que não é necessário alterar sensor ou frequência.

Em sequência, verifica-se se o valor da tela atual corresponde ao modo de sensor (0), caso sim, a condição é atendida e é lido os valores dos botões 19 e 26. Caso o botão 19 tenha sido pressionado, o sensor atual (armazenado na variável current_sensor é decrementado, caso seja o botão 26, o sensor atual é incrementado. A constante TOTAL_SENSOR é útil para, na operação de divisão e resto, forçar a contagem variar entre 0, 1 e 2 (3 sensores). Feito isso, independente se foi incrementado ou não, obtém-se o comando (cmd) chamando a função get_read_sensor_code e passando o sensor atual. Finalmente, o comando obtido é passado pela UART chamando a função send_data.

Caso o valor da tela corresponda ao modo de frequência (1), novamente lê-se o valor dos botões 19 e 26. Da mesma forma que 19 volta ao sensor anterior, aqui 19 diminui o valor da frequência atual, enquanto 26 aumenta este valor. As divisões e operações de resto com 0 e 31 servem para manter a contagem alternando de 0 segundos a 31 segundos. Da mesma forma, o valor da frequência é enviado para a função get_set_frequency_code e obtém-se o comando, o qual é enviado para a node mcu.

Uma vez na tela de status da node (2), os botões de avanço e recuo são inúteis, bastando retornar diretamente o valor da função get_mcu_status_code. O status foi definido de forma fixa como 1.

Por fim, na tela de status de sensor (3), repete-se a mesma lógica utilizada no modo de sensor, botão 19 para recuo, 26 para avanço, chama a função específica para esta operação e retorna o comando a node mcu através da UART.

Interface Remota

A interface remota foi feita em linguagem de marcação HTML e programação em javascript.

Inicialmente se define os parâmetros da conexão em MQTT. Os valores para host, porta, usuário e senha foram pré-definidos no broker do laboratório LEDS. Após isso cria-se 2 tópicos, um para atualização dos sensores (lê (subscriber) o valor e os nomes do sensores publicados (publisher) pela Node MCU) e outro para alterar a frequência (publisher).

A constante onMessageArrived é utilizada para obter os dados dos sensores da Node MCU. Inicialmente, obtém-se o nome do tópico e status da conexão. Caso o tópico corresponda ao tópico de atualização e a conexão esteja habilitada, a condição é atendida. Em seguida, obtém o valor da data atual, exclui-se a primeira data registrada na conexão (a mais antiga) e atualiza o histórico para as 10 mais novas (com a atual). A informação da conexão é parseada para o formato json, de modo a utilizar a função map e obter um array de objetos contendo o atributo label (nome do sensor) e data (valor do sensor). Por fim, limpa-se o gráfico em removeCharts e adiciona os novos dados em addData. Tais funções serão explicadas posteriormente.

A constante onConnect é útil para estabelecer a interface remota como subscriber do tópico de atualização. Utiliza-se a biblioteca PAHO para utilizar as funções do protocolo MQTT. Aqui chama-se a função mqtt.subscribe para inscrever-se no tópico de atualização e lança uma mensagem caso haja conexão.

Por fim, estabelece-se a conexão MQTT passando os parâmetros de host e porta definidos anteriormente. Em caso de uma mensagem recebida, chama-se a constante onMessageArrived para obter os nomes e valores de sensores explicados anteriormente. Por fim, a conexão é efetivamente iniciada em mqtt.connect, passando os parâmetros de usuários e senha. Caso haja conexão, a constante onConnect é chamada para inscrever a interface ao tópico.

Neste ponto, cria-se na interface uma caixa de input. Nela, o usuário digita o valor da frequência que será enviado ao node mcu. Em seguida, e logo abaixo na interface, cria-se um canvas, corpo de HTML que permite edição de javascript. Neste canvas é implementado o gráfico do histórico de valor dos sensores.

Na área de script, adiciona um evento de listening para o formulário de frequência. A cada vez que o usuário atualiza o valor vingente, o programa resgata tal valor e o publica no tópico de frequência utilizando a função _mqtt.send.

Em sequência, cria-se o gráfico utilizando a biblioteca Chart na área do canvas. Inicialmente, o gráfico é preenchido com valores aleatórios, apenas para ocupar a tela enquanto os valores da conexão não são recebidos.

Por fim, têm-se as duas funções citadas anteriormente. A função addData substitui os labels do gráfico pelo vetor que armazena os nomes de todos os sensores conectados a node mcu e atualiza os dados pelos respectivos valores temporais de cada sensor. Já a função removeData apenas esvazia os registros do gráfico, esvaziando os vetores.

Como executar

UART - Raspberry Pi

  1. Na pasta rpi/ execute:

$ make csystem

  1. Em seguida execute o programa

$ sudo ./system

MQTT - NodeMCU

  1. Na pasta nodemcu/ abra o arquivo uart.ino na Arduino IDE:
  2. Configure as bibliotecas do NodeMCU
  3. Descarregue o código na pltaforma

Testes

Teste 1 - Código Secreto

Para a Raspberry Pi se comunicar com a NodeMCU é preciso receber uma palavra chave da NodeMCU indicando uma inicialização. Devido ao fato que ao reiniciar a NodeMCU a mesma emite vários dados aleatórios nas saídas da UART.

  1. Inicializar o programa na Raspberry Pi
  2. Descarregar programa na NodeMCU
  3. No terminal, deve ser imprimido a mensagem: "Secret code found", indicando que a comunicação foi estabelecida

Teste 2 - Transferência de Informação

  1. Após o Teste 1 executado, o sistema está pronto pra execução.
  2. Inserir comando: 11, para transferir a informação presente no sensor de índice 0.
  3. Caso queira inserir o valor presente na entrada D0 da NodeMCU, basta realizar: SENSOR_VALUE[0] = digitalRead(D0) na função de loop da NodeMCU
  4. Caso haja um nível lógico ativo, a resposta deve ser 1. Caso contrário, a resposta deve ser 0.

Teste 3 - Frequência

  1. Com a interface local, fora da tela de frequência, acessar a interface remota e configurar a frequência para 5.
  2. As atualizações, em ambas interfaces devem ocorrer a cada 5 segundos.
  3. Mudar o valor de um sensor, esperar a mudança.
  4. Mudar novamente o valor e contar quanto tempo passará até a atualização.
  5. O novo valor deve ser atualizado após 5 segundos.

Teste 4 - Interface Remota

  1. Acessar interface remota.
  2. Alterar valor de algum sensor.
  3. Observar o valor alterado na interface remota.

O protótipo construído é um sistema digital utilizando plataformas de desenvolvimento IoT, em que se pode adicionar até 32 sensores analógicos e/ou digitais e exibir as respectivas informações em um display de LCD.

Limitações

Quantidade de Informação

Devido a limitações da biblioteca, a quantidade de informações transferidas é limitada, pois a mesma está sendo quebrada em diversos pacotes. Isso gera um atraso na transferência, devido ao tempo de publicação das mensagens. como o caso do i2c;

Relógio

A NodeMCU não possui relógio. Então, as informações não são enviadas com a hora em que elas foram coletadas. Diferentemente da Raspberry Pi que conta com este recurso, facilitando assim a organização temporal dos dados.

Memória

A NodeMCU não é capaz de reter grandes quantidades de informação em memória, logo, se houvesse a necessidade de guardar um histórico mais longo dos sensores, poderia se tornar inviável

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •