Início
/
Software
/
Dicas de Software
/
Implementando um Protocolo de Aplicação para Comunicação Serial no Arduino
Implementando um Protocolo de Aplicação para Comunicação Serial no Arduino
Angelo Luis Ferreira | 27/03/2026
Acessos: 43
Comunicação Serial Profissional no Arduino: Protocolo de Aplicação
Objetivo

O objetivo deste artigo é apresentar os fundamentos da criação de um protocolo de aplicação simples para comunicação serial no Arduino, estruturando mensagens de forma padronizada, segura e previsível.
Nos artigos anteriores desta série, exploramos os conceitos fundamentais da comunicação serial, o funcionamento do objeto Serial e diferentes técnicas para leitura e interpretação de dados enviados pelo Monitor Serial da IDE. Agora, avançaremos um passo além: organizar essas mensagens dentro de um protocolo próprio de comunicação.
Em sistemas embarcados reais, enviar apenas textos soltos como LEDON ou NUMERO 1500 pode ser suficiente para testes simples, mas não oferece estrutura adequada para aplicações mais complexas. Para garantir que dois dispositivos consigam se comunicar corretamente, é necessário definir regras claras sobre o formato das mensagens, criando assim um protocolo de comunicação.
Ao final desta leitura, você será capaz de:
- Entender o que é um protocolo de aplicação no contexto da comunicação serial
- Compreender por que sistemas embarcados utilizam mensagens estruturadas
- Definir um formato padronizado de mensagens para comunicação entre dispositivos
- Implementar um protocolo simples utilizando delimitadores e campos de dados
- Criar um interpretador de comandos baseado em protocolo no Arduino
- Validar e processar mensagens recebidas pela porta serial
- Preparar a base para integração com outras linguagens e sistemas externos, como Python, ESP ou outros microcontroladores
Ao final do artigo, você terá implementado um protocolo de comunicação simples, mas estruturado, capaz de tornar a troca de dados entre dispositivos mais robusta, organizada e adequada a aplicações reais em sistemas embarcados.
Referências
Definições
Comunicação Serial
Comunicação serial é o processo de transmissão de dados bit a bit, de forma sequencial, entre dois dispositivos através de um canal de comunicação.
No Arduino Uno, Arduino Mega e em muitos outros microcontroladores, essa comunicação é realizada pela interface UART (Universal Asynchronous Receiver Transmitter).
Ela utiliza dois pinos principais:
- TX (Transmit) → responsável por enviar dados (Pino 0 no Arduino Uno)
- RX (Receive) → responsável por receber dados (Pino 1 no Arduino Uno)
Quando conectamos o Arduino ao computador via USB, um conversor USB-Serial presente na placa transforma os sinais elétricos TX/RX em dados que podem ser interpretados pelo computador.
Esses dados podem ser visualizados ou enviados através do Monitor Serial da Arduino IDE, permitindo que o computador e o microcontrolador troquem informações em tempo real.
O que é um Protocolo de Comunicação?
Um protocolo de comunicação é um conjunto de regras que define como os dados são estruturados, transmitidos e interpretados entre dois dispositivos.
Essas regras estabelecem, por exemplo:
- Como uma mensagem começa e termina
- Como os dados são organizados dentro da mensagem
- Como comandos e valores devem ser interpretados
Sem um protocolo definido, os dados transmitidos podem se tornar ambíguos, inconsistentes ou difíceis de processar.
Por exemplo, considere a seguinte mensagem enviada ao Arduino: LEDON
Embora seja simples, não existe nenhuma estrutura que indique claramente as seguintes dúvidas:
- Onde a mensagem começa e termina?
- Existe algum parâmetro associado?
- A mensagem foi recebida completamente?
- Como diferenciar esse comando de outros semelhantes?
Em aplicações simples isso pode funcionar, mas em sistemas mais complexos pode gerar erros de interpretação.
Protocolo de Aplicação no Contexto do Arduino
Dentro da comunicação serial no Arduino, é importante entender que existem diferentes níveis (ou camadas) de comunicação.
De forma simplificada:
- A UART é responsável por transmitir bits (nível físico)
- O objeto
Serial permite enviar e receber dados (nível de interface)
- O protocolo de aplicação define o significado dos dados transmitidos
Ou seja:
A UART transporta os dados, mas é o protocolo de aplicação que dá sentido a eles.
O protocolo de aplicação portanto, é a camada responsável por definir como as mensagens devem ser organizadas e interpretadas logicamente dentro do sistema.
Ele estabelece um padrão que permite que diferentes dispositivos compreendam exatamente o que está sendo transmitido.
Para este artigo, adotaremos o seguinte formato:
<COMANDO|PARAMETRO>
Regras do Protocolo no formato acima
Nesse modelo, definimos as seguintes regras:
< → indica o início da mensagem
> → indica o fim da mensagem
| → separa os campos da mensagem
- O primeiro campo representa o comando
- O segundo campo representa o parâmetro
Por que isso é importante?
Com essa padronização:
- O Arduino consegue identificar mensagens completas
- O processamento se torna mais simples e previsível
- Reduzimos ambiguidades na interpretação dos dados
- Facilitamos a comunicação com outros sistemas (Python, ESP, etc.)
Dessa forma, deixamos de trabalhar com texto livre e passamos a utilizar uma estrutura de comunicação definida, característica essencial em sistemas embarcados mais robustos.
Exemplos de mensagens estruturadas:
<LED|1>
<LED|0>
<TEMP|?>
<MOTOR|1500>
Interpretação das mensagens:
<LED|1> → ligar o LED
<LED|0> → desligar o LED
<TEMP|?> → solicitar leitura de temperatura
<MOTOR|1500> → definir velocidade ou posição do motor
Observe que, independentemente do comando, todas as mensagens seguem exatamente o mesmo padrão estrutural.
Vantagens da Estrutura Definida
Ao utilizar delimitadores e campos bem definidos, o sistema passa a ter várias vantagens:
1️⃣ Identificação clara da mensagem: O Arduino consegue detectar facilmente quando uma mensagem começa (<) e quando termina (>).
2️⃣ Separação organizada dos dados: O caractere | permite separar o comando do parâmetro, facilitando o processamento.
3️⃣ Maior robustez na comunicação: Se uma mensagem chegar incompleta ou com formato incorreto, o sistema pode simplesmente ignorá-la.
4️⃣ Facilidade de expansão: Novos comandos podem ser adicionados sem alterar a estrutura do protocolo.
Limitações do Protocolo
Nosso protocolo é intencionalmente simples, pois o objetivo deste artigo é demonstrar os conceitos fundamentais.
Protocolos mais avançados podem incluir recursos adicionais, como:
- checksum ou CRC para verificação de erros
- tamanho da mensagem
- identificação de dispositivo
- confirmação de recebimento (ACK/NACK)
Esses recursos são comuns em sistemas profissionais, mas para este tutorial utilizaremos uma estrutura simples, suficiente para demonstrar como implementar um protocolo de aplicação no Arduino.
Implementando o Mecanismo de Recepção do Protocolo
Depois de definir a estrutura do protocolo, o próximo passo é implementar no Arduino um mecanismo capaz de receber, montar e interpretar as mensagens enviadas pela porta serial.
Como vimos anteriormente, nosso protocolo utiliza delimitadores para indicar o início e o fim de cada mensagem:
Para que o Arduino consiga interpretar corretamente essas mensagens, precisamos implementar um processo capaz de:
- Detectar o início da mensagem
- Armazenar os caracteres recebidos
- Detectar o fim da mensagem
- Finalizar a string recebida
- Enviar a mensagem para processamento
Esse mecanismo é normalmente implementado utilizando leitura serial não bloqueante, combinada com um buffer de recepção.
Estratégia de Implementação
O funcionamento do sistema será baseado em três etapas principais.
1️⃣ Detectar o início da mensagem
Quando o Arduino receber o caractere <, significa que uma nova mensagem está começando.
Nesse momento:
- O índice do buffer é reiniciado
- O sistema começa a armazenar os caracteres recebidos
2️⃣ Armazenar os caracteres da mensagem
Enquanto os caracteres chegam pela porta serial, eles são armazenados em um buffer de memória.
Por exemplo, ao receber:
<LED|1>
o buffer irá armazenar gradualmente:
3️⃣ Detectar o fim da mensagem
Quando o Arduino recebe o caractere >, significa que a mensagem foi completamente recebida.
Nesse momento:
- O buffer é finalizado com
'\0'
- A mensagem se torna uma string válida em C
- A função responsável pelo processamento da mensagem é chamada
Fluxo Simplificado do Processo
O funcionamento geral do sistema pode ser representado da seguinte forma:
Esse modelo garante que:
- O Arduino nunca fica aguardando dados
- Outras tarefas do sistema continuam funcionando
- A mensagem só é processada quando estiver completa
Implementação do Código
A seguir está um exemplo de implementação do mecanismo de recepção do protocolo.
#define TAM_BUFFER 40
char buffer[TAM_BUFFER];
byte indice = 0;
bool recebendo = false;
void setup() {
Serial.begin(9600);
pinMode(13, OUTPUT);
Serial.println("Sistema pronto.");
}
void loop() {
while (Serial.available() > 0) {
char c = Serial.read();
// Detecta início da mensagem
if (c == '<') {
indice = 0;
recebendo = true;
}
// Detecta fim da mensagem
else if (c == '>') {
buffer[indice] = '\0';
recebendo = false;
processaMensagem();
}
// Armazena caracteres intermediários
else if (recebendo) {
if (indice < TAM_BUFFER - 1) {
buffer[indice++] = c;
}
}
}
}
Nesse código:
< inicia a captura da mensagem
> finaliza a captura
- os caracteres intermediários são armazenados no buffer
Ao final da recepção, a função processaMensagem() será responsável por interpretar o conteúdo da mensagem recebida.
Conteúdo do Buffer
Se o usuário enviar a mensagem:
<LED|1>
o buffer armazenará apenas o conteúdo interno:
Isso facilita a separação dos campos da mensagem, pois já removemos automaticamente os delimitadores < e >.
Interpretando a Mensagem: Separando Comando e Parâmetro
Após implementar o mecanismo de recepção, o Arduino já é capaz de capturar corretamente uma mensagem estruturada enviada pela comunicação serial.
Por exemplo, ao receber o conteúdo armazenado no buffer:
Precisamos agora interpretar essa mensagem, separando:
- COMANDO →
LED
- PARÂMETRO →
1
Para isso, utilizaremos funções da biblioteca padrão da linguagem C: string.h.
Biblioteca <string.h>
A biblioteca <string.h> fornece funções eficientes para manipulação de strings no formato char[].
Neste artigo, utilizaremos principalmente:
strtok() → separar a string em partes (tokens)
strcmp() → comparar strings
Separando os Dados com strtok()
A função strtok() permite dividir uma string em partes menores com base em um delimitador.
Sintaxe
char* token = strtok(buffer, "|");
buffer → string original
"|" → delimitador
Observação Importante sobre strtok()
-
A função strtok() modifica o conteúdo original da string, substituindo o delimitador (|) por '\0'.
-
A função strtok() continua a leitura da string a partir da posição onde parou anteriormente, permitindo acessar as próximas partes da mensagem ao usar NULL nas chamadas seguintes.
Por que usar char* ?
No código acima (sintaxe) o * indica que a variável token é um ponteiro para char.
Isso significa que:
token não armazena diretamente os caracteres,

ele armazena o endereço na memória onde a string está localizada.
Na prática,
- Você pode usar
token como se fosse uma string normal
- Funções como
strcmp() funcionam normalmente com ele
- Ele apenas “aponta” para uma parte do buffer original
Por exemplo:
char* comando = strtok(buffer, "|");
comando passa a apontar para o início da palavra "LED" dentro do buffer.
Vantagem disso no Arduino
- Não duplica dados na memória
- É muito rápido
- Ideal para sistemas com pouca RAM (como o Arduino Uno)
Exemplo prático
Dado o conteúdo:
Podemos fazer:
Resultado:
comando → "LED"
parametro → "1"
O que cada linha faz:
char* comando = strtok(buffer, "|");
- Ação: A função
strtok() analisa o buffer, procura o primeiro caractere | e “divide” a string nesse ponto.
- Resultado: A variável
comando passa a apontar para o início da primeira parte da mensagem, por exemplo: "LED"
- O “pulo do gato”: O
strtok() substitui o caractere | por '\0', encerrando a primeira string diretamente na memória.
char* parametro = strtok(NULL, "|");
- Ação: O uso de
NULL indica para a função → “Continue a partir de onde você parou na chamada anterior.”
- Resultado: A função ignora a parte já processada e retorna o próximo trecho da string. A variável
parametro passa a apontar para "1" (ou "ON", dependendo da mensagem enviada).
Comparando Comandos com strcmp()
Após separar os dados, utilizamos strcmp() para verificar qual comando foi recebido.
Sintaxe
- Retorna
0 se as strings forem iguais
Exemplo
Implementação Completa da Interpretação
Agora vamos integrar tudo na função processaMensagem():
#include <string.h>
void processaMensagem() {
// Separa comando e parâmetro
char* comando = strtok(buffer, "|");
char* parametro = strtok(NULL, "|");
// Validação básica
if (comando == NULL || parametro == NULL) {
Serial.println("Erro: mensagem inválida");
return;
}
// ===== COMANDO LED =====
if (strcmp(comando, "LED") == 0) {
if (strcmp(parametro, "1") == 0) {
digitalWrite(13, HIGH);
Serial.println("LED ligado");
}
else if (strcmp(parametro, "0") == 0) {
digitalWrite(13, LOW);
Serial.println("LED desligado");
}
else {
Serial.println("Erro: parâmetro inválido");
}
}
// ===== COMANDO MOTOR =====
else if (strcmp(comando, "MOTOR") == 0) {
int valor = atoi(parametro);
Serial.print("Motor ajustado para: ");
Serial.println(valor);
}
// ===== COMANDO TEMP =====
else if (strcmp(comando, "TEMP") == 0) {
Serial.println("Temperatura: 25 C (simulada)");
}
// ===== COMANDO DESCONHECIDO =====
else {
Serial.println("Erro: comando desconhecido");
}
}
O que esse código faz?
✔ Divide a mensagem em partes
✔ Identifica o comando
✔ Interpreta o parâmetro
✔ Executa ações diferentes conforme o comando
✔ Trata erros básicos
Código Completo — Protocolo de Aplicação no Arduino
Este código implementa:
✔ Leitura serial não bloqueante
✔ Protocolo estruturado <COMANDO|PARAMETRO>
✔ Interpretação com strtok() e strcmp()
✔ Tratamento básico de erros
#include <string.h>
// =======================================================
// CONFIGURAÇÃO DO SISTEMA
// =======================================================
#define TAM_BUFFER 40
char buffer[TAM_BUFFER];
byte indice = 0;
bool recebendo = false; // controla se estamos dentro de < >
// =======================================================
// SETUP
// =======================================================
void setup() {
Serial.begin(9600);
pinMode(13, OUTPUT);
Serial.println("Sistema pronto.");
Serial.println("Envie comandos no formato:");
Serial.println("<LED|1> -> Liga LED");
Serial.println("<LED|0> -> Desliga LED");
Serial.println("<MOTOR|1500>");
Serial.println("<TEMP|?>");
Serial.println("-------------------------");
}
// =======================================================
// LOOP PRINCIPAL
// =======================================================
void loop() {
while (Serial.available() > 0) {
char c = Serial.read();
// INÍCIO DA MENSAGEM
if (c == '<') {
indice = 0;
recebendo = true;
}
// FIM DA MENSAGEM
else if (c == '>') {
buffer[indice] = '\0'; // finaliza string
recebendo = false;
processaMensagem();
}
// ARMAZENA DADOS
else if (recebendo) {
buffer[indice++] = c;
// PROTEÇÃO CONTRA OVERFLOW
if (indice >= TAM_BUFFER - 1) {
indice = TAM_BUFFER - 1;
}
}
}
// Aqui você pode adicionar outras tarefas sem bloqueio
}
// =======================================================
// PROCESSAMENTO DA MENSAGEM
// =======================================================
void processaMensagem() {
Serial.print("Recebido: ");
Serial.println(buffer);
// SEPARA COMANDO E PARÂMETRO
char* comando = strtok(buffer, "|");
char* parametro = strtok(NULL, "|");
// VALIDAÇÃO
if (comando == NULL || parametro == NULL) {
Serial.println("Erro: mensagem inválida");
return;
}
// ===================================================
// COMANDO LED
// ===================================================
if (strcmp(comando, "LED") == 0) {
if (strcmp(parametro, "1") == 0) {
digitalWrite(13, HIGH);
Serial.println("OK: LED ligado");
}
else if (strcmp(parametro, "0") == 0) {
digitalWrite(13, LOW);
Serial.println("OK: LED desligado");
}
else {
Serial.println("Erro: parâmetro LED inválido");
}
}
// ===================================================
// COMANDO MOTOR (SIMULADO)
// ===================================================
else if (strcmp(comando, "MOTOR") == 0) {
int valor = atoi(parametro);
Serial.print("OK: Motor ajustado para ");
Serial.println(valor);
}
// ===================================================
// COMANDO TEMPERATURA (SIMULADO)
// ===================================================
else if (strcmp(comando, "TEMP") == 0) {
Serial.println("OK: Temperatura = 25 C");
}
// ===================================================
// COMANDO DESCONHECIDO
// ===================================================
else {
Serial.println("Erro: comando desconhecido");
}
}
Como testar no Monitor Serial
Configure:
- Baud rate: 9600
- Final de linha: "Nova linha" (newline)
Exemplos de comandos
Digite no Monitor Serial:
<LED|1>
<LED|0>
<MOTOR|1500>
<TEMP|?>
Resultado

Conclusão
Com este artigo, você deu um passo importante além do uso básico da comunicação serial no Arduino.
Deixamos de trabalhar com comandos simples e passamos a implementar um protocolo de aplicação estruturado, capaz de organizar, validar e interpretar mensagens de forma clara e previsível.
Ao longo desta série, evoluímos progressivamente:
- No primeiro artigo, entendemos os fundamentos da comunicação serial e o uso da classe
String
- No segundo, exploramos uma abordagem mais robusta com
char[], buffers e leitura não bloqueante
- Agora, no terceiro, aplicamos esses conceitos para construir um sistema de comunicação mais próximo da prática profissional
Mais do que enviar dados ao Monitor Serial, você agora compreende:
- Como as mensagens são estruturadas
- Como são armazenadas na memória
- Como são interpretadas pelo microcontrolador
- E como projetar regras de comunicação entre sistemas
Esse conhecimento é essencial para aplicações como:
- Integração com Python
- Comunicação entre microcontroladores
- Sistemas IoT
- Interfaces homem-máquina
- Protocolos personalizados
A partir daqui, você não está apenas “programando Arduino”.
Você está projetando sistemas de comunicação embarcados.
O anúncio abaixo ajuda a manter o Squids Arduino funcionando
Comentários