0

Comunicação Serial Arduino

A Comunicação Serial é amplamente utilizada para comunicar o Arduino com outros dispositivos como módulos ZigBee, Bluetooth entre outros. A comunicação com o computador é possível através do conversor serial USB presente nas placas. A biblioteca padrão do Arduino possui uma classe que possui algumas funcionalidades para a comunicação serial de modo a facilitar a utilização desta função. Neste post veremos algumas dessas implementações e exemplos de aplicações

Primeiros passos

Para utilizarmos a comunicação serial devemos habita-lá através da função begin.

Esta função recebe dois parâmetros e não retorna nada.

void Serial.begin(taxa_de_comunicação, parâmetros = SERIAL_8N1);

  • O primeiro parâmetro configura a taxa de comunicação desejada em bauds. As taxas 100, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600 e 115200 devem ser utilizados no caso da comunicação com um computador.
  • O segundo parâmetro é opcional e configura a paridade.

Por se tratarem de funções membros de uma classe, as funções disponíveis para a comunicação serial devem ser acedida através do objeto principal  “Serial” e do operador “.”(ponto). ex: Serial begin(9600)

No Arduino Leonardo é necessário adicionar “While (!Serial);” após a função begin.

 

Serial Monitor

O Serial Monitor disponibiliza uma interface gráfica que facilita a comunicação entre o Arduino e um computador. Após seleccionarmos a porta serial adequada, o serial monitor pode ser aberto pelo ícone mostrado na figura a baixo.

Arduino IDE, Serial Monitor

Enviando Dados

O envio de dados pela porta serial pode ser feito através da funções print, println e write. A função print imprime os dados na serial utilizando o código ASCIL. Essa função recebe dois parâmetros e retorna a quantia de bytes que foram escritos.

long print(valor, formatação = DEC ou 2);

  • O primeiro parâmetro recebe o valor a ser impresso, A função é sobrecarregada de modo a ser capaz de receber qualquer tipo de padrão (char, int, long, float, etc.)
  • O segundo parâmetro é opcional e define a formatação do texto a ser impresso. Para os números inteiros esse parâmetro especifica a base na qual o número deve ser impresso. Os valores possiveis são BIN. OCT, DEC e HEX. Para floats este parâmetro especifica o número de casas decimais que serão impressas.

A função println imprime o valor desejado semelhante a função print, porém imprime na sequência os carateres ‘\r’ e ‘\n’ de modo a criar uma nova linha.

A função write imprime o valor especifico diretamente em bytes, Esta função possui dois formatos e retorna a quantidade de bytes que foram escritos.

byte write(valor);
byte write (buffer, tamanho);

  • O primeiro formato recebe um parâmetro, o valor a ser impresso. A função é sobrecarregada de modo a suportar apenas valores inteiros. Floats e doubles não são suportados.
  • O segundo formato recebe dois parâmetros, um buffer a ser impresso e o tamanho do mesmo.

Exemplo 1: Comparando os dois modos de impressão, print e write

void setup() {

  // Inicializa a comunicação serial com uma taxa de 9600 bauds.
  Serial.begin(9600); 
 
  Serial.println("print / write");
  for (int i = 33; i < 126; ++i) {
    Serial.print(i); // imprime i usando a tabela ASCII;
    Serial.print("    -    ");
    Serial.write(i); // imprime i como byte;
    Serial.println(); // pula uma linha;
  }
}
 
void loop() {
}

O operador de stream pode ser sobrecarregado para uma escrita mais conveniente do código.

Exemplo 2: Sobrecarregando o operador de stream “<<“.

template 
inline Print& operator << (Print& streamer, const T& x) {
  streamer.print(x);
  return streamer;
}
 
int x = 5;
int y = 10;
 
void setup() {
  // Inicializa a comunicação serial com uma taxa de 9600 bauds.
  Serial.begin(9600); 
 
  Serial << "x: " << x << " y: " << y << "\r\n"; 
 
  /* Equivalente a:
  Serial.print("x: ");
  Serial.print(x);
  Serial.print(" y: ");
  Serial.print(y);
  Serial.print("\r\n"); // ou Serial.println();
  */
}
 
void loop() {
}

Note que essa implementação não consumirá nenhum recurso extra do Arduino (ROM/RAM).

Lendo Dados

Assim que um byte de dado chega na porta do serial do Arduino ele será armazenado em um buffer de tamanho padrão de 64 bytes. A leitura desses dados pode ser feita utilizando a função read. Esta função não recebe nenhum parâmetro e retorna o primeiro byte disponível retirando-o do buffer. Para ler o byte sem retira-lo do buffer devemos utilizar a função peek. Caso nenhum byte esteja disponível ambas as funções retornarão “-1

byte Serial.read();
byte Serial.peek();

Podemos saber quantos bytes estão disponíveis para leitura utilizando a função available que também não recebe nenhum parâmetro.

byte Serial.available();

Exemplo 3: Serial Eco.

void setup() {
  // Inicializa a comunicação serial com uma taxa de 9600 bauds.
  Serial.begin(9600); 
 
}
 
void loop() {
   // Enquanto houverem bytes disponíveis;
  while (Serial.available()) {
    char c = Serial.read(); // Lê byte do buffer serial;
    Serial.print(c); // Faz o eco do byte recebido;
  }
}

Antes de cada repetição da função loop a função serialEvent é executada se algum byte tiver a espera para ser lido no buffer do serial. Podemos redefinir esta função de modo a especificar um tratamento para os bytes recebidos pela porta serial.

 

Exemplo 4: Redefinindo o serialEvent.

void setup() {
  // Inicializa a comunicação serial com uma taxa de 9600 bauds.
  Serial.begin(9600); 
 
}
 
void loop() {
}
 
void serialEvent() {
   // Enquanto houverem bytes disponíveis;
  while (Serial.available()) {
    char c = Serial.read(); // Lê byte do buffer serial;
    Serial.print(c); // Faz o eco do byte recebido;
  }
}

O código do exemplo 4 tem o mesmo efeito do código do exemplo 3.

 

Exemplo 5: Transformando o Arduino em um interpretador de comandos simplificado

const uint8_t BUFFER_SIZE = 20;
uint8_t size = 0;
char buffer[BUFFER_SIZE]; 
 
void serialEvent()
{
  while (Serial.available()) // Enquanto houverem bytes disponíveis;
  {
    char c = Serial.read(); // Lê byte do buffer serial;
    switch(c) {
    case '\r': // Marca o fim de um comando.
    case '\n':
      if (size == 0) return;
      buffer[size] = 0;
      size = 0;
      if (!strcmp(buffer,"led_on")) led_on();
      else if (!strcmp(buffer,"led_off")) led_off();
      else {
        Serial.println("Comando invalido!");
        return;
      }
      Serial.println("ok!");
      break;
 
    // Adiciona carácter ao buffer se não estiver cheio.
    default:
      if (size < BUFFER_SIZE-1) {
        buffer[size] = c;
        ++size;
      }
    }
  }
}
 
void led_on() { digitalWrite(13, 1); }
void led_off() { digitalWrite(13, 0); }
 
void setup() {
  // Inicializa a comunicação serial com uma taxa de 9600 bauds.
  Serial.begin(9600);
  pinMode(13, OUTPUT);
}
 
void loop() {
}

A cada execução da função serialEvent o Arduino irá ler todos os bytes disponíveis naquele momento e armazena-los no buffer de comandos. Caso um marcador de fim de comando seja (‘\r’ ou ‘\n’) o Arduino irá comparar o buffer de comandos com “led_on” e “led_off” caso o buffer contenha essas strings ele executará a função respetiva e retornará “ok!” ao computador. Caso contrário receberá a mensagem “Comando inválido” . Para que o código funcione o Serial Monitor deve estar configurado para enviar “Newline“(´\n´) ou “Carriage Return” (´\r´) ou “Both” (“\r\n”).

 

Artigo gentilmente cedido por Vida de Silicio

 

Todos os produtos utilizados neste artigo podem ser encontrados na Loja de Eletrónica e Robótica – ElectroFun.

Gostaram deste artigo? Deixem o vosso comentário no formulário a baixo e partilhem com os vossos amigos.

Não se esqueçam de fazer like na nossa Página no Facebook.

Podem ainda colocar as vossas dúvidas no nosso Forum da Comunidade Arduino em Portugal ou no nosso Grupo no Facebook Arduino Portugal – Qual o teu projeto?

Comments

Comentários

Mariana Guedes

Deixar uma resposta