Expansão de portas de um Raspberry Pi usando serial e Arduino – Parte 2

Este post faz parte da série Expansão de portas de um Raspberry Pi usando serial e Arduino. Leia também os outros posts da série:

Na primeira parte dessa série criamos uma interface gráfica semelhante a um terminal, testamos as formas de comunicação possíveis e enviamos e recebemos informações para as portas do Arduino.

Nesta continuação, iremos desenvolver a aplicação usando padrões de projeto e o protótipo da GUI previamente desenvolvido, o qual vai nos proporcionar maior extensibilidade da aplicação.

Abordaremos também o uso de:

  • Threads, para execução de tarefas em paralelo;
  • Callback, para acessar informação vindas de processos em threads;
  • Extends class, para adicionar funções a alguma classe;
  • UpdateListener, para que tarefas só sejam executadas depois de um determinado período de tempo;
  • Criação de novas classes, para organização do projeto;
  • Conceito de threads produtora e consumidora, para sincronização de processos.

Montando a estrutura do projeto

O projeto terá 3 pastas principais para as partes do projeto, respectivamente: serialexpansion, ui e utils.

A lista de arquivos que devem ser criados em cada pasta são mostrados na imagem abaixo.

Estes são todos os arquivos de código que vamos precisar para desenvolver o projeto, a estrutura final de pastas do projeto deve ficar como a seguir.

RPIExpansionPorts

└───src

 └───main

 ├───java

 │ └───com

 │ └───totalcross

 │ └───sample

 │ └───rpiexpansionports

 │ ├───serialexpansion

 │ ├───ui

 │ └───utils

 └───resources

 ├───Fonts

 └───Images

Hardware

  • 2 Resistores de 1K ohms;
  • 3 LEDs;
  • 1 push button;
  • 1 joystick ou 2 potenciômetros;
  • 1 sensor de temperatura Termistor NTC de 10K ohms e um resistor de 10K ohms ou um modulo NTC de 10K ou um potenciômetro de 10K ohms e um resistor de 10K ohms;
  • 1 protoboard;
  • 1 Arduino;
  • jumpers.

Abaixo veja o diagrama esquemático. Na figura o potenciômetro mais a direita representa o sensor de temperatura NTC e os outros dois o joystick.

O sensor Termistor NTC a seguir é um sensor de temperatura com as seguintes características.

  •  Tensão de operação: 3,3 ou 5VDC
  •  Faixa de medição: -55°C a 125° celsius
  •  Precisão: ±1%

Para a montagem, ele deve ser colocado no lugar do potenciômetro mais a direita na imagem, com uma das pernas no GND e a outra no ponto que liga o resistor a porta A1.

O módulo joystick à esquerda já possui a indicação de como deve ser feita as ligações, sendo que VRx deve ser ligado no pino A1 e VRy no A2 do arduino.

Por fim, temos a imagem a seguir com o esquema real do hardware que será usado.

Utils

A imagem abaixo foi construída por um designer de UX da TotalCross para ser a última etapa do desafio, como comentei no artigo anterior, criar uma GUI que seja atrativa para os usuários. O protótipo com as informações necessárias está no projeto dashboard sensors no site da adobe XD, lá podemos ver as imagens cores e fontes utilizadas para o design e adicioná-las as utils do projeto.

As imagens e fontes usadas no projeto se encontram no repositório do github, basta apenas baixar os arquivos e adicioná-los a pasta resources do projeto.

Fonts

A fonte escolhida para o projeto foi a Roboto bold e será usado apenas 4 das formas possíveis que estão no arquivo para download indicado no item anterior.

Para usar as fontes, criaremos uma classe nova a qual poderá carregar as fontes e definir os elementos necessários com elas.

Nessa nova classe, que nomeei de Fonts (linha 4) são criadas 4 variáveis que serão usadas de acordo com suas nomeações, e definidas usando “getFont(“path”,false,int size)” função da classe Font que tratará o arquivo ttf, permitindo o uso das fontes como visto da linha 11 a 14 do código abaixo.

Colors

As cores escolhidas para o projeto serão adicionadas a uma classe Colors como o código a seguir mostra, para facilitar sua utilização e manutenção do código pois dessa forma, caso seja necessário fazer alguma alteração na paleta de cores do projeto, é necessário apenas alterá-las nessa classe.

Para esse caso as variáveis das cores são criadas e têm seus valores definidos simultaneamente e já podem ser utilizadas normalmente no projeto.

Images

A classe Images tem como objetivo facilitar a utilização das imagens contidas no projeto. Dessa forma, centralizando o carregamento das imagens, caso sejam necessárias alterações no futuro, apenas um local deve ser alterado, refletindo por todo o código do projeto que utiliza as imagens, sendo mais fácil manter o código fonte.

MaterialConstants

Essa classe contém as constantes e duas variáveis que serão usadas para facilitar a utilização dos dados recebidos e o envio de dados para o serial.

  • command receberá todos os comandos que devem ser enviados em sequência.
  • feedback receberá os valores para cada porta requisitada em suas respectivas posições.

SerialExpansion

Esta classe é onde a comunicação ocorrerá, através dela será inicializado a conexão com o Arduino é feito o envio e recebimento de informação.

O processo consiste de duas partes: leitura e escrita no serial, onde a leitura deve ser iniciada antes da primeira escrita pois é através dela que é iniciada a conexão com o Arduino.

O processo para fazer a leitura do serial através do PortConnector, explicado na parte 1 do artigo, consiste de ler as informações recebidas, gravá-las em uma variável em ordem para que seja retornada através do callback, linha 75, que é uma interface que deve ser implementada na classe main para que dessa forma sejam usados os dados disponibilizados pela thread consumidora o que será feito mais a frente neste artigo.

Para a escrita é bem mais simples, com a comunicação iniciada na leitura o serial fica aberto para que possa ser enviado informação, dessa forma basta apenas enviar o comando pelo método apropriado o qual pode ser visto na função PortConnectorSendCommand, linha 79 a 87 do código a seguir.

Para que haja uma comunicação harmônica com o serial e que os dados recebidos sejam usados com o mínimo de perdas possíveis serão usadas Threads: uma geradora que receberá os valores e os espalhará em uma variável, e outra consumidora que desempilhar os valores e permitirá que sejam lidos.

A função start inicia a comunicação definindo o PortConnector para comunicação usando USB com uma velocidade de 115200, como é visto na linha 25. Na linha seguinte é criado um LineReader e passado como parâmetro o pc, com isso será possível verificar e ler a informação recebida.

Partindo para a thread geradora, linha 28 a 51, enquanto a capacidade da variável data não for alcançada é verificado em loop se algo foi recebido no LineReader, se houver, será armazenado em data, quando for alcançado o limite de data, a thread deve dormir para que esses dados sejam consumidos, pela consumidora.

Já a thread consumidora, linha 53 a 73, colocará a disposição do callback as informações adquiridas pela geradora sempre que ela estiver dormindo, enquanto houver dados, para então dormir e permitir novamente o funcionamento da geradora.

Ui

Agora é o momento de criar as partes da Ui, começando pela expansão da classe label, para que a mesma se adeque a necessidade do projeto e em seguida a criação de cada bloco da UI.

ShowContainer

Nessa parte do artigo construiremos a base da interface para que possa ser terminada na parte 3. Nos arquivos AnalogInput, DigitalInput devem ser feito como foi feito DigitalOutput a seguir, alterando apenas a linha 7 para o nome do arquivo.

A partir disso podemos montar a base da interface, bem como no código anterior fazemos uma extensão da classe Container, “como é visto na linha 12”, que possibilita chamar este e os anteriores como se fosse um elemento nativo da classe.

Os tamanhos de cada um dos elementos foi feito com base no design apresentado no item 3. Cada elemento possui um tamanho fixo em pixel definido no protótipo mencionado, porém para que a aplicação seja responsiva os valores foram convertidos para porcentagem do tamanho da tela.

O primeiro a ser feito é exibir os textos, título do projeto, autor e company, para isso estou colocando minhas informações, mas vocês que está desenvolvendo a aplicação junto comigo pode colocar seus dados da forma que quiser.

Das linhas 20 a 26 é definido o texto e adicionado ao container, os labels que estão sendo usados, os RPIExpansionPortsLabel, serão criados e explicados nos próximos passos. 

Das linhas 28 a 32 é criado, definido e exibido a imagem do mascote sendo que, primeiro a imagem já definida no item 3.3 é adicionada a um imageControl para que possa ser modificada de acordo com a necessidade, que nesse caso é definindo o background como transparente, linha 29, o que pode ser feito pois a imagem é um png sem fundo, e tornar a escala da imagem proporcional ao espaço onde está contida, o que é feito na linha 30 e faz com que se ajuste automaticamente as proporções definidas quando adicionada na tela, linhas 31 e 32.

Por fim é adicionado ao container dessa classe os elementos das classes de input e output para que toda a interface possa ser chamada na classe principal apenas com a declaração e exibição dessa única classe.

Para que seja possível obter uma breve visualização de como está ficando a interface podemos executar a classe principal, inicializando as classes de imagens e fontes, segue o código a seguir:

RPIExpansionPortsLabel

A principal utilidade dessa classe vai ser ler as informações adquiridas e armazenadas na variável feedback e exibi-las da melhor forma, mas também será usada para os outros casos que necessitem de labels, para facilitar algumas adaptações e futuras atualizações caso necessite.

Essa classe será criada como uma extensão da classe Label como é possível observar na linha 12 do código a seguir, para aproveitar todos os métodos e comportamentos da classe mãe.

Algumas variáveis definidas das linhas 13 a 19, serão necessárias para o funcionamento dessa classe, e tem como suas respectivas funções:

  • type: variável que indicará como deve ser feita a leitura dos dados
  • port: variável que indicará qual porta deve ser lida.
  • refreshTime: variável que indicará tempo para uma nova leitura e envio de comando para o serial.
  • elapsedRefreshTime: variável que contará quanto tempo passou desde a última atualização.
  • idle: variável que servirá para medir tempo sem enviar comandos.
  • im: variável que definirá uma imagem no caso de ser uma entrada digital.
  • termoMode: variável que acionará método de conversão de entrada para temperatura.

Os métodos construtores dessa classe além de chamar o construtor da classe mãe definem algumas características extras que serão necessárias para termos o funcionamento que necessitamos, como é visto na linha 76, além da variável string nativa da classe Label, a variável type que definirá o tipo de fonte que será usada para label e a variável opcional im que definirá o estado de uma imagem caso a label seja responsável por um input digital, é opcional pois caso esse não seja o caso é possível criar a label normalmente com a segunda instância do construtor na linha 102, que não necessita dessa imagem a definindo como null.

Agora a parte mais importante dessa classe, ler e exibir as informações. Com a utilização do updateListener criado na linha 21, é possível saber a quanto tempo ele foi executado e armazenar esse valor em uma variável, elapsedRefreshTime. Isso vai permitir com que possamos definir um tempo entre as leituras para que não haja uma interrupções na aplicação durante a utilização pelo usuário, o que é feito na linha 27, onde a cada 200 ms uma nova leitura e um novo comando é enviado para o Arduino.

Há ainda a possibilidade de que esse comando seja perdido ou tenha conflito com algum comando enviado pelo usuário, o que faria o label não atualizar por isso é usado a variável idle que na linha 64 é verificada e caso seu valor for maior que 2000ms|2s, um novo comando é enviado e então reinicia a contagem.

Esse tempo ocioso ocorre devido que a atualização da label e o envio de um novo pedido de resposta só é feito caso exista algum valor na sua posição respectiva na variável feedback, linha 30, existindo valor, é exibido a informação de acordo com o tipo da variável que está sendo lida, o que é definido pela função setSendCommand que recebe a porta e seu tipo para a leitura e inicializa o updateListener, como vista na linha 106.

Existem 4 configurações possíveis sendo elas:

  • Quando o tipo é “>” e não existe uma imagem, nesse caso sera exibido ON ou OFF no componente, linha 42.
  • Quando o tipo é “>” e existe uma imagem, nesse caso sera alterado a imagem de acordo com o estado do pino,linha 34.
  • Quando o tipo é diferente de “>”, para essa configuração sera exibido um valor entre 0 e 1024 seguindo o estado do pino analógico,linha 55.
  • Quando o tipo é diferente de “>” e a função setTermoMode() foi chamada, para este, o valor de 0 a 1024 é convertido para temperatura em C°, linha 52.

A conversão de temperatura é feita baseada no sensor de temperatura resistivo NTC de 10 Kohms, e um resistor de 10 Kohms associado a ele, configuração comum mais recomendada para sua forma de trabalho, e para esse projetos vamos nos prender a ela já que o objetivo direto não é trabalhar com o ajuste desse periférico.

RPIExpansionPorts

Agora podemos dar continuidade a classe principal do projeto apresentada no ponto 5.1 onde agora será definida os tipos das portas, alocado os valores que chegam da serial de forma correta e feito o envio dos comandos.

Essa classe, além de ser uma extensão da MainWindow, implementa obrigatoriamente o Callback mencionado no tópico 4. Essa implementação é tornada obrigatória na definição da classe, linha 12, onde, como implements, temos o Callback.

A implementação é feita na linha 49 e consiste de verificar se o separador “:” é contido no dado e se ao dividi-lo possui duas partes, linha 54, isso é feito pois o Arduino retorna os valores com o número da porta, o separador e o valor como por exemplo “3:200” que indica que a porta 3 está com o valor de 200, estando dentro dessa estrutura, o dado é dividido, onde a primeira parte indica o index onde deve ser adicionada a segunda na variável feedback, o que pode ser visto nas linhas 55 e 56.

O envio de dados é semelhante ao processo feito pela classe RPIExpansionPortsLabel, com a utilização de um UpdateListener, linha 21, nesse caso é feito a leitura do primeiro elemento da variável command onde é armazenado os comandos que as Labels tentaram enviar, nesse ponto cada um dos comando é enviado em sequência com um intervalo de 150 ms, indicado pela variável refreshTime na linha 15, até que não haja mais o que enviar.

Para a definição do tipo de cada porta, a função defineMode, linha 87, recebe como entrada um conjunto de portas e se deve ser um input ou output, sendo 1 para output e 0 para input, então é feito o envio com a função PortConnectorSendCommand.

Por fim, a chamada dessas funções começando pela inicialização da comunicação na linha 75, após ela é necessário esperar um período de tempo de 3 segundos para que a comunicação seja efetuada com sucesso, feito isso é possível definir os tipos das variáveis e iniciar o UpdateListener,linha 91, dessa forma finalizando os processos para a comunicação constante.

Conclusão

Nessa parte do artigo avançamos muito no projeto, preparamos totalmente a comunicação, utilizando as classes apresentadas na primeira parte, fizemos sincronização de threads para que não haja conflito na utilização dos dados e iniciamos a criação da interface já exibindo a estrutura base do projeto.

Para o próxima parte do projeto terminaremos a montagem das classes que faltam e teremos o projeto completo para utilizarmos no Raspberry Pi, com isso poderemos testar tudo que foi desenvolvido e visualizar uma interface completa desenvolvida a partir de um designer profissional.

Outros artigos da série

<< Expansão de portas de um Raspberry Pi usando serial e Arduino – Parte 1
Licença Creative Commons Esta obra está licenciada com uma Licença Creative Commons Atribuição-CompartilhaIgual 4.0 Internacional.

Receba os melhores conteúdos sobre sistemas eletrônicos embarcados, dicas, tutoriais e promoções.

Raspberry Pi » Expansão de portas de um Raspberry Pi usando serial e Arduino - Parte 2
Comentários:
Notificações
Notificar
guest
1 Comentário
recentes
antigos mais votados
Inline Feedbacks
View all comments
Vaneska Sousa
Vaneska
21/08/2020 10:39

Muito bom! Cadê a proxima parte?

Talvez você goste:

Séries



Outros da Série

Menu

WEBINAR

Projeto de Hadware: ASIC e FPGA

DATA: 24/02 às 15h