Processing


Introdução
Baixar o programa

A IDE processing está disponivel para Linux, Mac e Windows. Para baixar, entre no link: https://processing.org/download .

Ao entrar, clique na opção de seu sistema operacional (atenção à quantidade de bits de seu sistema operacional). O programa será baixado em um pacote compactado do tipo “.zip”, sendo necessária sua descompactação.

Quando descompactado, o aplicativo já estará disponível dentro da pasta de descompactação, não sendo necessário instalá-lo.

Após a descompactação, clique neste ícone para abrir a IDE
interface
Interface inicial do Processing.

Está é a interface principal da IDE Processing. Temos em branco, na parte central, a área para o código e dois botões principais logo acima. O primeiro, em formato triangular, ao ser clicado começa a execução do código; já o segundo a interrompe.

comandos básicos

Para usar a linguagem de programação Processing, temos uma estrutura e elementos básicos.

Primeiramente, temos a estrutura do código que depende de dois ciclos principais.

O “void setup()” é uma função que opera apenas uma vez, logo quando acionamos o programa, nela normalmente é onde definimos parâmetros e configurações do sketch.

O “void draw()” é uma função que opera em loop infinitamente depois do “void setup()” sendo onde colocamos todas as funções e comandos do sketch para nosso programa.

A seguir são apresentados os principais comandos utilizados no Processing:

size(altura, largura);

O comando “size” com dois parâmetros, define a altura e largura da janela do seu programa, onde serão mostrados os desenhos e imagens geradas na IDE. Seu primeiro parâmetro é o de altura da janela e o segundo o de largura, os dois medidos em pixels. Caso este comando não seja usado na programação, a janela terá por padrão 100×100 pixels.

rect(coordenada X, coordenada Y, altura, largura);

Comando para desenho de um retângulo, com 4 parâmetros: coordenada no eixo X, coordenada eixo Y, altura e largura. Pode ter 5 parâmetros, a quinta sendo o raio para arredondamento dos cantos do retângulo.

triangle(x1,y1,x2,y2,x3,y3);

Comando para criar um triangulo sendo seus parâmetros as respectivas posições nos eixos X e Y de cada ponto.

 fill(r,g,b);

Preenche as formas geométricas com uma cor, a qual é definida pelos parâmetros red, green e blue. Também pode ser passado um único valor, correspondendo a um tom de cinza (onde 0 equivale a preto e 255 equivale a branco).

text("texto",posx,posy);

O comando “text“, como o próprio nome diz, adiciona texto ao nosso programa, sendo seu primeiro parâmetro o texto a ser adicionado, podendo ser uma variável ou um texto fixo (neste caso, precisa estar entre aspas), seu segundo parâmetro é sua posição em relação ao eixo X e logo depois sua posição no eixo Y.

textSize(tamanho);

“textSize” também é um comando super intuitivo, define o tamanho de todos os textos a partir dele. Seu parâmetro é o tamanho desejado dos textos.

minha_imagem = loadImage("nome do arquivo da imagem");

Carrega imagens para o programa. Necessário adicionar a imagem no sketch via IDE. Também, necessário criar uma variável especial para armazenamento de imagens dentro do Processing, no caso chamada de ‘minha_imagem’.

image(minha_imagem, 60, 80, 130, 540);

Utiliza imagem na programação. Seu primeiro parâmetro é a imagem carregada previamente com o comando “loadImage”, depois temos sua posição no eixo X, logo depois sua posição no eixo Y. Seus últimos dois parâmetros são opcinais, e são referentes a altura e largura em pixels da imagem.

exemplo de uso com arduino

Podemos utilizar o Processing com uma placa Arduino uno e fazer interfaces gráficas tanto passivas ( apenas recebendo informação do Arduino) quanto ativas (enviando informação para o Arduino). A seguir apresentamos alguns exemplos de programas de comunicação com o Arduino.

Programa no Arduino

Para fazer a conexão do Arduino e Processing, primeiramente precisamos gravar um programa no Arduino.

Esse programa de exemplo mede um sinal analógico na porta A0 do Arduino e envia o valor medido pela porta serial. A aquisição e controle da frequência de aquisição são feitas do mesmo modo como já vimos no post “Fazendo medições com o Arduino“. Você pode acessar esse código pelo GitHub aqui: [ver código], [baixar código].

// Exemplo de comunicação entre o Arduino e o Processing.
// O Arduino mede dados do ADC usando a função analogRead(), realizando
// as medições em uma taxa de amostragem conhecida e enviando
// continuamente os valores medidos pela porta serial. O Processing
// realiza a leitura dos dados da porta serial e apresenta os mesmos
// em um gráfico.
//
// A frequência de aquisição do Arduino pode ser alterada através de
// comandos enviados pelo Processing através da porta serial.
//
// Criado por: Erick Dario León Bueno de Camargo, 2023

// Frequencia de amostragem desejada, em Hz. Usar valores entre 1 e 2000:
long frequencia = 10;

unsigned long atraso_us;

int medida;
unsigned long tempo_atual, tempo_anterior;
byte comando;

bool coleta = false;

////////////////////////////////////////////////////////////////////////////////
// Esta função só roda quando chamada
void verifica_comandos() {
  if(Serial.available()){
    comando = Serial.read();

    switch (comando) {
      case 'a':            // inicia coleta
        coleta = true;
        break;

      case 'b':            // interrompe coleta
        coleta = false;
        break;

      case '1':            // altera frequencia
        frequencia = 10;
        break;

      case '2':            // altera frequencia
        frequencia = 20;
        break;

      case '3':            // altera frequencia
        frequencia = 30;
        break;

      case '4':            // altera frequencia
        frequencia = 40;
        break;

      case '5':            // altera frequencia
        frequencia = 50;
        break;

      case '6':            // altera frequencia
        frequencia = 60;
        break;

      case '7':            // altera frequencia
        frequencia = 70;
        break;

      case '8':            // altera frequencia
        frequencia = 80;
        break;

      case '9':            // altera frequencia
        frequencia = 100;
        break;

      default: // comando não reconhecido
        break;
    }
    atraso_us = (1000000.0/frequencia);
  }
}

////////////////////////////////////////////////////////////////////////////////
// Esta função só roda uma vez, no início
void setup() {
  Serial.begin(115200);
  atraso_us = (1000000.0/frequencia);
  tempo_anterior = micros();
}

////////////////////////////////////////////////////////////////////////////////
// Esta função se repete indefinidamente
void loop() {

  if (coleta) {
    tempo_atual = micros();
    
    // o abs() evita problemas com overflow da função micros()
    if (abs(tempo_atual - tempo_anterior) > atraso_us) {
      tempo_anterior = tempo_atual;
      medida = analogRead(A0);
      Serial.println(medida);
    }
  }

  verifica_comandos();
}

Além de enviar os dados lidos, nesse programa também é possível receber comandos via porta serial, como implementado na função verifica_comandos(); (linhas 26 a 80). Os comandos implementados aqui são baseados em apenas um caractere (letra ou número), conforme apresentado a seguir:

  • ‘a’: inicia a coleta, modificando o valor da variável coleta para true;
  • ‘b’: interrompe a coleta, modificando o valor da variável coleta para false;
  • ‘1’ a ‘9’: alteram a frequência de aquisição para valores pré-definidos (entre 10 e 100 amostras por segundo.

Caso queira, você pode alterar esse exemplo para acrescentar outros comandos, usando outras letras, por exemplo, para acender ou apagar um LED, ou alterar o canal de medida.

Grave esse programa em seu Arduino, e, com o Arduino conectado em seu computador, você já pode rodar o programa do Processing.

Interface gráfica simples no Processing

Este primeiro exemplo de interface gráfica no Processing é bastante simples. Ele simplesmente recebe um valor pela porta Serial, e, em seguida, acrescenta esse valor medido em um gráfico, que ocupa toda a janela. Acesse esse código pelo GitHub aqui: [ver código], [baixar código].

// Exemplo simples de comunicação entre o Arduino e o Processing.
// O Arduino mede dados do ADC usando a função analogRead(), realizando
// as medições em uma taxa de amostragem conhecida e enviando
// continuamente os valores medidos pela porta serial. O Processing
// realiza a leitura dos dados da porta serial e apresenta os mesmos
// em um gráfico.
//
// A frequência de aquisição do Arduino pode ser alterada através de
// comandos enviados pelo Processing através da porta serial.
//
// Criado por: Erick Dario León Bueno de Camargo, 2023

import processing.serial.*;   // importa a biblioteca serial.
Serial MinhaSerial;           // cria um objeto da classe serial
String Buffer;                // variável onde ficarão os dados lidos
int posicao = 0;              // guarda posição atual do gráfico
int medida;                   // guarda medida atual
int medida_anterior = 0;      // guarda medida anterior

////////////////////////////////////////////////////////////////////////////////
// Envia uma tecla pressionada pela serial.
// Esta função roda automaticamente quando uma tecla é pressionada
void keyPressed() {
    MinhaSerial.write(key);
}

////////////////////////////////////////////////////////////////////////////////
// Esta função só roda uma vez, no início
void setup() {
    size(640, 480); // indica o tamanho da janela do programa
    background(255);  // limpa a tela na cor de fundo (255 = branco, 0 = preto)
    
    println("Portas seriais disponiveis:"); // lista portas disponíveis no console
    println(Serial.list());
    MinhaSerial = new Serial(this, "/dev/ttyUSB0", 115200); // alterar para a sua porta
    MinhaSerial.clear(); // limpa qualquer dado anterior na porta
}

////////////////////////////////////////////////////////////////////////////////
// Esta função se repete indefinidamente
void draw(){
    Buffer = null;
    if (MinhaSerial.available() > 0){ // se tem dados na porta serial
        Buffer = MinhaSerial.readStringUntil(10); // lê os dados até o final da linha (código ASCII 10)
        if (Buffer != null) {        // buffer ok
            // limpa dado anterior desenhando um retângulo branco em cima
            fill(255);
            stroke(255);
            rect(posicao, 0, 2, 480);
            
            // converte o dado recebido (texto) em um número que cabe no gráfico
            medida = int(float(Buffer)*480/1024.0);
            
            // desenha dado atual (y=0 fica no topo da tela)
            stroke(255,0,0); // cor da linha (R,G,B: vermelho)
            line(posicao, 480-medida_anterior, posicao+1, 480-medida);
            posicao = posicao + 1;
            if (posicao >= 638) posicao = 0; // se chegou no final da tela
            medida_anterior = medida;
        }
    }
    if (MinhaSerial.available() > 10) println(MinhaSerial.available());
}

Nesse exemplo, para enviar um comando pela porta serial, basta pressionar uma tecla qualquer. Ao fazer isso, o Processing automaticamente chama a função keyPressed() (linha 23), e, no nosso exemplo, envia o caractere da tecla pressionada (‘key‘) pela porta serial (linha 24).

Na função setup(), que sempre roda ao inicializarmos o programa, primeiramente temos a definição do tamanho da janela (linha 30) e da cor de fundo (linha 31). Em seguida, o programa lista as portas seriais disponíveis no console (parte preta da janela do Processing abaixo do seu código) e conecta na porta serial do Arduino (linha 35 e 36). Caso seu Arduino esteja conectado em uma porta serial diferente, basta alterar para a porta correta na linha 35. A porta serial onde está seu Arduino é a mesma que apareceu na IDE do Arduino, quando você gravou seu programa nele.

Na função draw(), que fica rodando em loop até o programa ser finalizado (como a função loop() do Arduino), o programa verifica se existem dados disponíveis na porta serial (linha 43) e, em caso afirmativo, inclui esse novo ponto no gráfico.

Para isso, primeiramente o dado deve ser lido (linha 44). Aqui é importante notar que lemos os dados da porta serial até o final da linha (já que o Arduino manda os dados em formato texto, ou seja, o valor ‘345’ é enviado como 4 caracteres: ‘3’, ‘4’ e ‘5’, terminando com um código indicando a quebra de linha (pois, no programa do Arduino, foi usando o comando println(), que inclui uma quebra de linha ao final do comando). O código enviado pela serial para indicar uma quebra de linha é o código 10, então lemos até receber esse código.

Para incluir o valor no gráfico, precisamos antes apagar o valor anterior, já que, ao chegar no final da tela, o gráfico volta para o início da tela, e vai sobrescrevendo os valores antigos. Assim, apagamos desenhando um retângulo branco que sobre os dados que tinham sido desenhados na última passada do gráfico pela tela (linhas 47 a 49). Repare que a largura desse retângulo é de apenas 2 pixels, ou seja, apagamos apenas a parte do gráfico que será sobreposta pelos novos dados.

Em seguida, o valor lido (que é um texto, guardado na variável Buffer) é convertido em um valor inteiro. Como o valor lido inicialmente varia entre 0 e 1023, e queremos mostrar em um gráfico que possui apenas 480 pixels de altura (que é o tamanho da janela), fazemos também a conversão desse valor, multiplicando por 480 e dividindo por 1023 (linha 52).

Nas linhas 55 a 59, desenhamos uma linha ligando o último ponto medido ao novo ponto medido, incrementamos a posição no gráfico, e voltamos para o início caso o gráfico tenha chegado ao final da tela.

Por último, na linha 62, verificamos se existem dados que estão acumulando na porta serial. Isso pode acontecer caso os dados sejam enviados em uma frequência mais alta do que o Processing consegue ler e atualizar o gráfico. Aqui no meu computador, isso acontece para frequências de aquisição acima de 60 amostras por segundo. Caso aconteça de ter mais de 10 caracteres acumulados na porta serial, o programa mostra o número de caracteres acumulados no console do Processing.

Para rodar esse programa, abra o código no Processing e pressione o botão Play (botão triangular no canto superior esquerdo da tela). Inicialmente, teremos apenas uma tela em branco, já que o programa do Arduino não envia dados enquanto não for solicitado, através do comando ‘a’. Assim, para iniciar as medidas, basta pressionar a tecla ‘a’. Os dados serão lidos e apresentados na tela, como podemos ver a seguir:

Para alterar a frequência de aquisição, você pode simplesmente pressionar as teclas de ‘1’ a ‘9’, e verificar o resultado. Não esqueça de prestar atenção se existem dados acumulando na porta serial, sendo que, nesse caso, você deve diminuir a frequência de aquisição para que os dados sejam lidos em tempo real.

Interface gráfica completa no Processing

O exemplo anterior é bastante simples, mas é possível fazer muito mais no Processing, como apresentar imagens, textos e usar botões. O exemplo a seguir mostra como isso é feito. Para esse exemplo funcionar, você deve baixar os 3 arquivos que estão no GitHub neste link. Neste projeto, temos 2 arquivos de códigos, e um arquivo de imagem. Para baixar a imagem, clique no nome do arquivo e, em seguida, clique no botão “Download”. Para baixar os códigos, clique no nome do arquivo desejado e, em seguida, clique no botão “Raw”. Os 3 arquivos devem estar em um mesmo diretório, chamado “processing_grafico”.

A divisão do código em 2 arquivos foi feita apenas como exemplo de organização, mas caso queira você pode colocar todo o código apenas no arquivo principal do Processing (para o Processing, não faz diferença se o código está todo em um mesmo arquivo .pde ou separado em vários arquivos .pde). Para acrescentar novos arquivos em seu projeto no Processing, basta clicar no triângulo pequeno ao lado do nome do arquivo do seu código, e em seguida, clicar em “Nova Aba”, ou, se preferir, pode usar o teclado, pressionando Ctrl+Shift+N.

O código principal é apresentado a seguir:

// Exemplo de comunicação entre o Arduino e o Processing.
// O Arduino mede dados do ADC usando a função analogRead(), realizando
// as medições em uma taxa de amostragem conhecida e enviando
// continuamente os valores medidos pela porta serial. O Processing
// realiza a leitura dos dados da porta serial e apresenta os mesmos
// em um gráfico.
//
// A frequência de aquisição do Arduino pode ser alterada através de
// comandos enviados pelo Processing através da porta serial.
//
// Criado por: Erick Dario León Bueno de Camargo, 2023

import processing.serial.*;    // importa a biblioteca serial.

// variáveis usadas na interface gráfica:
PFont f;                       // declaração da variável da fonte que será usada
int altura_botao = 20;
color cor_botao = color(200,200,255);    // RGB: azul claro
color cor_borda_botao = color(0,0,100);  // RGB: azul escuro
int BRANCO = 255;
int PRETO = 0;
int altura_grafico = 240;
int largura_grafico = 620;

// variáveis usadas na comunicação serial:
Serial MinhaSerial;            // cria um objeto da classe serial
int indice_porta = 0;          // posição da porta que será utilizada
String Buffer;                 // variável onde ficarão os dados lidos
boolean conectado = false;     // variável que indica se a porta serial está conectada
int lf = 10;                   // Código de nova linha (Linefeed) em ASCII
String mensagem = "desconectado."; // variável que vai guardar o status da porta serial
int mensagem_x = 10;           // posição x da mensagem
int mensagem_y = 145;          // posição y da mensagem

// variáveis usadas na manipulação dos dados
int medida;
int medida_anterior = 0;
int posicao = 0;
long tempo_anterior;

////////////////////////////////////////////////////////////////////////////////
// Esta função só roda uma vez, no início
void setup() {
    size(640, 480);   // indica o tamanho da janela do programa
    background(BRANCO);  // cor de fundo da tela
    f = createFont("Arial", 10); // cria uma fonte para ser usada nos textos
    
    cria_fundo();
    desenha_botoes_serial();
}

////////////////////////////////////////////////////////////////////////////////
// Esta função se repete indefinidamente
void draw(){
    if (conectado) {
        Buffer = null;    // limpa o dado anterior
        le_dado_serial(); // lê um dado da porta serial, se disponível
        atualiza_texto("dados acumulados na serial: " + MinhaSerial.available(), 18, 320, 180, 310, BRANCO);
        
        if (Buffer != null) {     // se teve um novo dado...
            atualiza_taxa_processamento();
            atualiza_grafico();
        }
    }
    else {
        atualiza_texto("", 18, 10, 180, 620, BRANCO); // 'apaga' textos
    }
}

Esse código é bem simples, ele cria todas as variáveis globais usadas pelo código e tem apenas as funções principais, setup() e draw(). A primeira é responsável por criar a janela e o cabeçalho da janela (título, imagem e botões para controle da porta serial), e a segunda por atualizar o gráfico, caso esteja conectado no Arduino.

Todas as demais funções estão no arquivo funcoes.pde, apresentado abaixo:

////////////////////////////////////////////////////////////////////////////////
// Cria os objetos do fundo da janela.
// Esta função só roda quando chamada.
void cria_fundo() {
    // escreve o título grande
    textFont(f, 60);   // fonte utilizada, tamanho da fonte em pixels
    fill(13,98,58);    // cor utilizada: verde logo
    text("EMBARCASAÚDE", 120, 70);  // escreve um texto na posição (120,70) 
    
    // mostra o logo
    PImage img_logo;   // cria um objeto imagem para colocar o logo
    img_logo = loadImage("logo_embarcasaude_100dpi.png"); // carrega arquivo
    img_logo.resize(100, 0); // redimensiona a imagem; usar '0' para manter a proporção dos lados 
    image(img_logo, 10, 10); // coloca a imagem na posição (10,10)
    
    // área do gráfico
    stroke(PRETO);   // cor da borda
    fill(BRANCO);    // cor do interior
    rect(10,200,largura_grafico,altura_grafico);
}

////////////////////////////////////////////////////////////////////////////////
// Desenha um botão com texto dentro.
// Esta função só roda quando chamada.
void desenha_botao(String texto, int x, int y, int lagura, int altura, color cor, color cor_borda){
    fill(cor);         // cor utilizada no interior
    stroke(cor_borda); // cor da borda
    rect(x, y, lagura, altura);
    fill(PRETO);       // cor utilizada
    textFont(f, 12);   // fonte utilizada, tamanho da fonte em pixels
    text(texto, x+5, y+15);
}

////////////////////////////////////////////////////////////////////////////////
// Desenha os botões e portas seriais disponíveis.
// Esta função só roda quando chamada.
void desenha_botoes_serial(){
    if (conectado) {  // se já está conectado...
        desenha_botao("", 410, 105, altura_botao, altura_botao, BRANCO, BRANCO); // 'apaga' botão
        desenha_botao("", 440, 105, altura_botao, altura_botao, BRANCO, BRANCO); // 'apaga' botão
        desenha_botao("Desconectar", 470, 105, 150, altura_botao, cor_botao, cor_borda_botao);
    } else{
        desenha_botao("<", 410, 105, altura_botao, altura_botao, cor_botao, cor_borda_botao);
        desenha_botao(">", 440, 105, altura_botao, altura_botao, cor_botao, cor_borda_botao);
        desenha_botao("Conectar", 470, 105, 150, altura_botao, cor_botao, cor_borda_botao);
    }
    
    atualiza_texto("Portas seriais disponíveis:", 18, 10, 120, 230, BRANCO);
    // caixa onde vai ficar o texto da porta serial
    fill(BRANCO);            // cor utilizada
    stroke(cor_borda_botao); // cor da borda
    rect(250, 105, 150, altura_botao);
    // texto da porta serial
    fill(PRETO);       // cor utilizada
    textFont(f, 12);   // fonte utilizada, tamanho da fonte em pixels
    text(Serial.list()[indice_porta], 255, 120);
    
    atualiza_texto("Status: " + mensagem, 18, mensagem_x, mensagem_y, 600, BRANCO);
}

////////////////////////////////////////////////////////////////////////////////
// Escreve um texto em determinada posição apagando o que tinha antes desenhando
// um retângulo branco em cima.
// Esta função só roda quando chamada.
void atualiza_texto(String texto, int tam_fonte, int x, int y, int largura, int borda){
    // apaga o texto anterior desenhando um retânculo branco por cima:
    fill(BRANCO);            // cor utilizada
    stroke(borda);           // cor da borda (usar outra para ver o que está sendo apagado)
    rect(x, y-int(0.8*tam_fonte), largura, int(1.1*tam_fonte));
    
    // escreve texto:
    textFont(f, tam_fonte);  // fonte utilizada, tamanho da fonte em pixels
    fill(PRETO);             // cor utilizada
    text(texto, x, y);
}

////////////////////////////////////////////////////////////////////////////////
// Verifica se o mouse está dentro de um determinado botão
// Esta função só roda quando chamada.
boolean no_botao(int x, int y, int largura, int altura)  {
  if (mouseX >= x && mouseX <= x+largura && mouseY >= y && mouseY <= y+altura) {
    return true;
  } else {
    return false;
  }
}

////////////////////////////////////////////////////////////////////////////////
// Desenha os botões de controle dos dados.
// Esta função só roda quando chamada.
void desenha_botoes_controle(){
    desenha_botao("inicia",  10, 450, 37, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("para",    57, 450, 33, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=10Hz", 100, 450, 50, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=20Hz", 160, 450, 50, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=30Hz", 220, 450, 50, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=40Hz", 280, 450, 50, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=50Hz", 340, 450, 50, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=60Hz", 400, 450, 50, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=70Hz", 460, 450, 50, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=80Hz", 520, 450, 50, altura_botao, cor_botao, cor_borda_botao);
    desenha_botao("f=100Hz",580, 450, 50, altura_botao, cor_botao, cor_borda_botao);
}

////////////////////////////////////////////////////////////////////////////////
// Executa uma ação quando o botão do mouse é clicado, dependendo da posição
// do mouse.
// Esta função roda automaticamente quando o botão do mouse é clicado.
void mousePressed() {
    if (conectado) {  // se já está conectado...
        if ( no_botao(10, 450, 37, altura_botao) ) { // botão 'inicia'
            MinhaSerial.write('a');
        }
        else if ( no_botao(57, 450, 33, altura_botao) ) { // botão 'para'
            MinhaSerial.write('b');
        }
        else if ( no_botao(100, 450, 50, altura_botao) ) { // botão '10Hz'
            MinhaSerial.write('1');
        }
        else if ( no_botao(160, 450, 40, altura_botao) ) { // botão '20Hz'
            MinhaSerial.write('2');
        }
        else if ( no_botao(220, 450, 50, altura_botao) ) { // botão '30Hz'
            MinhaSerial.write('3');
        }
        else if ( no_botao(280, 450, 40, altura_botao) ) { // botão '40Hz'
            MinhaSerial.write('4');
        }
        else if ( no_botao(340, 450, 50, altura_botao) ) { // botão '50Hz'
            MinhaSerial.write('5');
        }
        else if ( no_botao(400, 450, 40, altura_botao) ) { // botão '60Hz'
            MinhaSerial.write('6');
        }
        else if ( no_botao(460, 450, 50, altura_botao) ) { // botão '70Hz'
            MinhaSerial.write('7');
        }
        else if ( no_botao(520, 450, 40, altura_botao) ) { // botão '80Hz'
            MinhaSerial.write('8');
        }
        else if ( no_botao(580, 450, 50, altura_botao) ) { // botão '100Hz'
            MinhaSerial.write('9');
        }
        else if ( no_botao(470, 105, 150, altura_botao) ) { // botão 'Conectar'/'Desconectar'
            MinhaSerial.clear();
            MinhaSerial.stop();
            conectado = false;
            mensagem = "desconectado.";
            // 'apaga' botões de controle:
            desenha_botao("",10, 450, 620, altura_botao, BRANCO, BRANCO);
        }
    } else {          // se ainda não está conectado...
        
        if ( no_botao(410, 105, altura_botao, altura_botao) ) { // botão '<'  
            indice_porta = indice_porta - 1;
        }
        else if ( no_botao(440, 105, altura_botao, altura_botao) ) { // botão '>'
            indice_porta = indice_porta + 1;
        }
        else if ( no_botao(470, 105, 150, altura_botao) ) { // botão 'Conectar'/'Desconectar'
            abre_serial();
        }
        // verifica se indice_porta está nos limites das portas disponíveis 
        if (indice_porta < 0) indice_porta = Serial.list().length -1;
        if (indice_porta > (Serial.list().length -1)) indice_porta = 0;
    }
    
    desenha_botoes_serial(); // atualiza o texto com a nova porta serial
}

////////////////////////////////////////////////////////////////////////////////
// Abre uma porta serial para comunicação.
// Esta função só roda quando chamada.
void abre_serial() {
    String nome_porta = Serial.list()[indice_porta];
    // Inicializa a porta serial
    try{ // tenta um comando, mas não encerra o programa em caso de erro...
        conectado = true;
        MinhaSerial = new Serial(this, nome_porta, 115200);
    }catch(Exception e){ // se deu erro...
        mensagem = "ERRO: Não foi possível conectar na porta " + nome_porta + ".";
        conectado = false;
    }
    
    if(conectado){ // se conectou com sucesso...
        mensagem = "conectado na porta " + nome_porta + ".";
        desenha_botoes_controle();
        MinhaSerial.clear(); // limpa qualquer dado anterior na porta
    }
}

////////////////////////////////////////////////////////////////////////////////
// Lê um dado da porta serial.
// Esta função só roda quando chamada.
void le_dado_serial() {
    if (MinhaSerial == null){ // se perdeu a conexão...
        conectado = false;
        mensagem = "conexão perdida.";
        atualiza_texto("Status: " + mensagem, 18, mensagem_x, mensagem_y, 600, 255);
    }
    else if (MinhaSerial.available() > 0){ // Se tiver algum dado disponível
        Buffer = MinhaSerial.readStringUntil(lf); // lê o dado até uma quebra de linha
    }
}

////////////////////////////////////////////////////////////////////////////////
// Atualiza o gráfico com um novo dado do Buffer.
// Esta função só roda quando chamada.
void atualiza_grafico(){
    // limpa dado anterior
    fill(BRANCO);
    stroke(BRANCO);
    rect(posicao+11, 201, 2, altura_grafico-2);
    
    // converte o dado recebido (texto) em um número que cabe no gráfico
    medida = int(float(Buffer)*altura_grafico/1024.0);
    
    // desenha dado atual
    stroke(0,128,0); // cor da linha (R,G,B: verde escuro)
    line(posicao+11, 199+altura_grafico-medida_anterior, posicao+12, 199+altura_grafico-medida);
    posicao = posicao + 1;
    if (posicao >= largura_grafico-3) posicao = 0;
    medida_anterior = medida;
    
    // escreve valor do dado lido na tela:
    atualiza_texto("Medida: " + Buffer.trim(), 18, 10, 180, 120, BRANCO);
}

////////////////////////////////////////////////////////////////////////////////
// Atualiza a taxa de processamento dos dados apresentada na tela.
// Esta função só roda quando chamada.
void atualiza_taxa_processamento(){
    // verifica taxa de atualização dos dados:
    long tempo_atual = millis();
    long intervalo = tempo_atual-tempo_anterior;
    tempo_anterior = tempo_atual;
    float freq = 1.0/(intervalo/1000.0);
    atualiza_texto("Freq: " + nf(freq,4,2) + "Hz", 18, 135, 180, 150, BRANCO);
}

A janela inicial desse programa pode ser vista a seguir:

Os elementos dessa janela são criados pelas funções cria_fundo() e desenha_botoes_serial(). A função cria_fundo() (linhas 4 a 20) escreve o título “EMBARCASAÚDE”, acrescenta a imagem do logo e desenha o retângulo onde ficará o gráfico. A função desenha_botoes_serial() (linhas 37 a 59) desenha os retângulos dos botões e do texto da porta serial em uso. Como são desenhados vários botões, criamos uma função desenha_botao() (linhas 25 a 32) para ajudar com essa tarefa repetitiva. É possível verificar as portas seriais disponíveis pressionando os botões ‘<‘ e ‘>’ da interface e, após a escolha da porta serial do Arduino, a conexão é feita ao pressionar o botão “Conectar”. Após a conexão, os botões ‘<‘ e ‘>’ são apagados, desenhando-se um retângulo branco por cima deles, e o texto do botão “Conectar” é alterado para “Desconectar”. Para alterar textos na janela, é preciso antes apagar o texto anterior, desenhando um retângulo branco por cima. Como isso é feito diversas vezes, criamos uma função para isso também: atualiza_textos(), definida nas linhas 65 a 75.

Ao contrário do exemplo anterior, onde usávamos o teclado para enviar comandos, a interação com a janela aqui é feita através do mouse. Para isso, o Processing chama, automaticamente, a função mousePressed() (linhas 109 a 169) quando é feito um clique com o mouse. Essa função verifica em que região da tela o mouse estava quando o clique foi feito e roda o respectivo comando. Para verificar a região em que mouse está, criamos uma função no_botao(), que verifica se o mouse está dentro da área de um botão. Os botões disponíveis dependem do estado do programa, se está conectado ou não no Arduino, assim essa verificação é feita antes de processarmos os comandos dos botões (linhas 110 e 152).

A atualização do gráfico (linhas 209 a 227) é feita do mesmo modo do exemplo anterior, mas agora ocupando apenas uma parte da janela, e apresentando um texto com o valor lido.

A janela do programa após a conexão com o Arduino e durante o recebimento de dados pode ser vista a seguir:


Texto por: Lucas Jácome Cabral e Erick León

Revisão e testes: Erick León


, , ,

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *