A02 - Como Controlar Arduino com Python, usando Interface Gráfica e Protocolo Serial Estruturado

Avançado - Projeto 02

Arduino + Python: Controle de LEDs com Interface Gráfica 

Objetivo

O objetivo desse projeto é desenvolver um sistema de comunicação bidirecional entre o microcontrolador Arduino Uno e uma aplicação em Python, utilizando comunicação serial USB e um protocolo de aplicação estruturado com delimitadores.

Para isso, vamos criar um sistema completo de controle e monitoramento entre o Arduino Uno e Python, utilizando comunicação serial USB com um protocolo de aplicação estruturado.

Neste projeto, o usuário poderá:

  • Controlar dois LEDs (verde e vermelho) diretamente pelo computador;
  • Acionar comandos através de uma interface gráfica (GUI) com botões;
  • Executar ações como:
    • Ligar e desligar LEDs individualmente
    • Controlar ambos simultaneamente
    • Ativar um modo automático de alternância (pisca alternado a cada 1 segundo)
  • Receber respostas do Arduino em tempo real, indicando sucesso ou erro nos comandos enviados
  • Encerrar a aplicação com segurança, finalizando a comunicação serial corretamente

A interface gráfica será desenvolvida em Python utilizando a biblioteca Tkinter, permitindo interação via mouse de forma intuitiva, sem necessidade de uso do Monitor Serial da IDE.

O sistema permitirá:

  • Controlar dois LEDs (verde e vermelho) via interface gráfica
  • Executar comandos individuais e combinados
  • Ativar um modo automático de alternância entre LEDs
  • Receber respostas do Arduino em tempo real (OK ou ERRO)
  • Encerrar a aplicação de forma segura

Além disso, o projeto introduz conceitos fundamentais de sistemas embarcados modernos, como:

  • Comunicação não bloqueante
  • Parser de comandos com char[]
  • Protocolo textual estruturado
  • Integração entre firmware e software desktop

Definições

1. Comunicação Serial

A comunicação serial é um método de transmissão de dados onde os bits são enviados sequencialmente, um após o outro, através de um canal físico (USB, UART, etc.).

No Arduino Uno e Arduino Mega, essa comunicação é feita via interface UART, encapsulada pela USB.

Neste projeto:

  • Velocidade: 9600 bps
  • Meio físico: USB
  • Interface: Serial

2. Comunicação Bidirecional

É a capacidade de dois sistemas trocarem dados em ambos os sentidos:

Python → Arduino (comandos)
Arduino → Python (respostas/status)

Diferente do Projeto 01, aqui o sistema:

  • envia comandos
  • recebe respostas
  • atua com feedback em tempo real

4. Protocolo de Aplicação

É o conjunto de regras que define:

  • Como os dados são estruturados
  • Como os comandos são interpretados
  • Como as respostas são formatadas

Neste projeto, usamos:

<LED:GREEN:ON>
[MODE:ALT]

Características:

  • Baseado em texto
  • Leve e fácil de expandir
  • Independente do meio físico

5. Delimitadores de Mensagem

São caracteres que indicam o início e o fim de um comando.

Utilizados:

Tipo Início Fim Exemplo
LED < > <LED:RED:ON>
MODE [ ] [MODE:ALT]

Função:

  • Evitar leitura de dados corrompidos
  • Permitir parsing seguro
  • Separar comandos no fluxo serial

6. Parser de Comandos

É o mecanismo responsável por:

  • Receber a mensagem
  • Separar seus elementos
  • Interpretar ação e parâmetros de cada elemento

No Arduino:

  • Implementado com char[]
  • Utiliza strtok() para separar a string em partes (tokenização)
  • Não utiliza String (evita fragmentação de memória)

7. Comunicação Não Bloqueante

É um modelo onde o sistema continua executando outras tarefas enquanto aguarda eventos (como dados serial ou temporização).

✔ No Arduino:

  • Uso de Serial.available()
  • Uso de millis() (em vez de delay())

✔ No Python:

  • Uso de in_waiting
  • Loop com tkinter.after()

Resultado:

  • Interface não trava
  • Sistema responde em tempo real

8. Firmware

Firmware é o software embarcado que roda no microcontrolador.

Neste projeto, o firmware:

  • Interpreta comandos recebidos
  • Controla os LEDs
  • Executa lógica de modo alternado
  • Retorna respostas ao sistema

9. Interface Gráfica (GUI)

A interface gráfica é responsável pela interação com o usuário.

Implementada em Python com a biblioteca padrão:

  • Tkinter

Permite:

  • Acionar comandos via botões
  • Visualizar respostas do Arduino
  • Encerrar o sistema com segurança

10. Tratamento de Erros

Mecanismo que garante robustez do sistema diante de falhas.

✔ No Arduino:

  • ERRO:FORMATO
  • ERRO:PARAM
  • ERRO:COMANDO

✔ No Python:

  • Exceções na serial
  • Validação de resposta
  • Feedback visual na interface em tempo real

11. Sistema Orientado a Eventos

Modelo onde o sistema reage a eventos, como:

  • Clique de botão (GUI)
  • Chegada de dados (serial)
  • Temporização (millis())

Esse padrão é essencial para:

  • Sistemas embarcados
  • Interfaces gráficas
  • Aplicações em tempo real

Fundamentos para:

  • IoT
  • Automação
  • Sistemas embarcados profissionais

Referências

Comunicação Serial no Arduino com Monitor Serial – Fundamentos e Uso com String
Comunicação Serial Profissional no Arduino: char[], Buffer e Controle de Memória
Implementando um Protocolo de Aplicação para Comunicação Serial no Arduino
A01 - Conectando o Arduino ao PC com Python: Sem Usar o Monitor Serial

Aplicações

Controle de Dispositivos via Computador, Automação Residencial (Home Automation), Interfaces Supervisórias (Estilo SCADA), Prototipagem de Sistemas IoT, Desenvolvimento de Protocolos de Comunicação, Integração Hardware + Software (Desktop), Sistemas Educacionais e Laboratórios.

Componentes necessários

Referência

Componente

Quantidade

Imagem

Observação

Protoboard Protoboard 400 pontos 1

No mínimo utilizar protoboard com 400 pontos

Jumpers Kit cabos ligação macho / macho 1  
Led Difuso 5mm LEDs 5mm 2

1 LED Vermelho e 1 LED verde

Você poderá utilizar também LEDs de 3 mm.

Resistor

Resistor

 2

2 Resistores de 150Ω ou maiores

Se precisar usar outros valores, calcule o resistor apropriado para o led utilizado

Arduino UNO Arduino UNO R3 com cabo USB 1

Você poderá utilizar uma placa Arduino MEGA 2560

Montagem do Circuito

Conecte os componentes no Protoboard como mostra a figura abaixo. Verifique cuidadosamente os cabos de ligação antes de ligar seu Arduino. Lembre-se que o Arduino deve estar totalmente desconectado da força enquanto você monta o circuito.

Atenção

1. Lembre-se que o LED tem polaridade: O terminal maior tem polaridade positiva e o lado do chanfro tem polaridade negativa.

1.1. Portanto, faça a conexão do Arduino no terminal positivo do led (anodo) e o GND no terminal negativo (catodo).

1.2. Para evitar danos ao led é necessário a inclusão de um resistor de no mínimo 150Ω circuito. Como o resistor é um limitador da corrente elétrica, ele poderá estar conectado no anodo (terminal maior) ou no catodo (terminal menor) do led, tanto faz.

2.  Identifique a Porta Serial: Abra o IDE do Arduino e acesse Ferramentas → Porta e você verá a porta identificada.

Observe que na minha conexão a Porta identificada foi COM 4. Veja qual a porta identificada no seu Arduino, anote esse número, pois ele será utilizado no Python.

Instalação do Python

1. Antes de instalar o Python, você poderá verificar se o seu PC já possui o programa instalado. Para isso, abra o terminal (prompt de comando do Windows, por exemplo).

1.1. Digite no terminal: python --version

1.2. Se você não tiver uma resposta e aparecer Python não encontrado, precisará instalar o Python no seu PC.

2. Instalação do Python no PC

2.1. Acesso o site oficial do Python: https://www.python.org/ e clique no botão Downloads

2.2. Clique no link da última versão: Neste exemplo é a versão Python 3.14.3

2.3. Aguarde finalizar o download e clique no arquivo de instalação que foi baixado: 

2.4.Clique no botão Install Now depois de ter marcado a opção Add python.exe to PATH.

2.4.1. IMPORTANTE: Marque a opção Add python.exe to PATH

2.4.2. Aguarde a finalização da instalação. Se pedir alguma confirmação no Terminal, digite sempre Y (yes).

2.5. No terminal (prompt de comando) verifique novamente se foi instalado digitando: python --version

Instalação da biblioteca pyserial do Python

1. Abra o terminal (prompt de comando do Windows, por exemplo), e digite: pip install pyserial

Obs: O pip (Python Package Index) é o gerenciador de pacotes padrão do Python, utilizado para instalar, atualizar e remover bibliotecas e dependências de terceiros que não fazem parte da biblioteca padrão. 

2. Digite Enter e aguarde a instalação. Esse processo pode demorar um pouco.

Código do Projeto (Sketch)

Agora vamos preparar o Arduino para: Receber comandos, Interpretar texto, Executar ação e Responder.

1. Copie e cole o código abaixo no IDE do Arduino:

/*******************************************************************************
*
*    Projeto A02: Comunicação Bidirecional Arduino + Python com GUI (interface gráfica)
* ------------------------------------------------------
*   Controle de LEDs via protocolo serial estruturado
*
*    Autor: Angelo Luis Ferreira
*    Data: 16/02/2026
*    
*             http://squids.com.br/arduino             
*
*******************************************************************************/
/*
 * PROTOCOLO:
 * <LED:GREEN:ON>
 * <LED:RED:OFF>
 * <LED:ALL:ON>
 *
 * [MODE:ALT]
 * [MODE:STOP]
 *
 * [SYS:EXIT]
 *
 * STATUS:
 * LED VERDE LIGADO / DESLIGADO
 * LED VERMELHO LIGADO / DESLIGADO
 * TODOS LEDs LIGADOS / DESLIGADOS
 * MODO ALTERNADO ATIVO / PARADO
 * ENCERRANDO SISTEMA
 *
 * ERROS:
 * ERRO:FORMATO / ERRO:PARAM / ERRO:COMANDO
 */

// -----------------------------
// DEFINIÇÃO PINOS
// -----------------------------
#define LED_VERDE 8
#define LED_VERMELHO 9

// -----------------------------
// BUFFER SERIAL
// -----------------------------
#define BUFFER_SIZE 50

char buffer[BUFFER_SIZE];
byte indexBuffer = 0;
bool recebendo = false;

// -----------------------------
// MODO ALTERNADO
// -----------------------------
bool modoAlternado = false;
unsigned long tempoAnterior = 0;
bool estado = false;

// -----------------------------
// MENSAGENS DE STATUS
// -----------------------------
const char* statusMsg[] = {
  "LED VERDE LIGADO",
  "LED VERDE DESLIGADO",
  "LED VERMELHO LIGADO",
  "LED VERMELHO DESLIGADO",
  "TODOS LEDs LIGADOS",
  "TODOS LEDs DESLIGADOS",
  "MODO ALTERNADO ATIVO",
  "MODO ALTERNADO PARADO",
  "ENCERRANDO SISTEMA"
};

// -----------------------------
// PROTÓTIPOS DAS FUNÇÕES
// -----------------------------
void enviarStatus(int index);
void lerSerial();
void processarComando(char *cmd);
void processarLED(char *cmd);
void processarMODE(char *cmd);
void processarSYS(char *cmd);
void setLED(char *cor, bool estadoLed);
void pararModo();
void executarModo();

// --------------------------------------------------
void setup() {
  pinMode(LED_VERDE, OUTPUT);
  pinMode(LED_VERMELHO, OUTPUT);
  Serial.begin(9600);
}

// --------------------------------------------------
void loop() {
  lerSerial();
  executarModo();
}

// --------------------------------------------------
void enviarStatus(int index) {
  Serial.println(statusMsg[index]);
}

// --------------------------------------------------
void lerSerial() {
  while (Serial.available()) {
    char c = Serial.read();

    // início do comando
    if (c == '<' || c == '[') {
      indexBuffer = 0;
      recebendo = true;
      buffer[indexBuffer++] = c;
      continue;
    }

    if (recebendo) {
      if (indexBuffer < BUFFER_SIZE - 1) {
        buffer[indexBuffer++] = c;
      }

      // fim do comando
      if (c == '>' || c == ']') {
        buffer[indexBuffer] = '\0';
        recebendo = false;
        processarComando(buffer);
      }
    }
  }
}

// --------------------------------------------------
void processarComando(char *cmd) {

  if (cmd[0] == '<') {
    processarLED(cmd);
  }
  else if (cmd[0] == '[') {

    if (strstr(cmd, "MODE")) processarMODE(cmd);
    else if (strstr(cmd, "SYS")) processarSYS(cmd);
    else Serial.println("ERRO:COMANDO");

  }
  else {
    Serial.println("ERRO:COMANDO");
  }
}

// --------------------------------------------------
void processarLED(char *cmd) {

  char *token = strtok(cmd, "<:>");
  if (!token || strcmp(token, "LED") != 0) {
    Serial.println("ERRO:FORMATO");
    return;
  }

  char *cor = strtok(NULL, "<:>");
  char *acao = strtok(NULL, "<:>");

  if (!cor || !acao) {
    Serial.println("ERRO:PARAM");
    return;
  }

  bool estadoLed;

  if (strcmp(acao, "ON") == 0) estadoLed = true;
  else if (strcmp(acao, "OFF") == 0) estadoLed = false;
  else {
    Serial.println("ERRO:PARAM");
    return;
  }

  setLED(cor, estadoLed);
}

// --------------------------------------------------
void setLED(char *cor, bool estadoLed) {

  if (strcmp(cor, "GREEN") == 0) {
    digitalWrite(LED_VERDE, estadoLed);
    enviarStatus(estadoLed ? 0 : 1);
  }
  else if (strcmp(cor, "RED") == 0) {
    digitalWrite(LED_VERMELHO, estadoLed);
    enviarStatus(estadoLed ? 2 : 3);
  }
  else if (strcmp(cor, "ALL") == 0) {
    digitalWrite(LED_VERDE, estadoLed);
    digitalWrite(LED_VERMELHO, estadoLed);
    enviarStatus(estadoLed ? 4 : 5);
  }
  else {
    Serial.println("ERRO:PARAM");
  }
}

// --------------------------------------------------
void processarMODE(char *cmd) {

  char *token = strtok(cmd, "[:]");
  if (!token || strcmp(token, "MODE") != 0) {
    Serial.println("ERRO:FORMATO");
    return;
  }

  char *modo = strtok(NULL, "[:]");
  if (!modo) {
    Serial.println("ERRO:PARAM");
    return;
  }

  if (strcmp(modo, "ALT") == 0) {
    modoAlternado = true;
    enviarStatus(6);
  }
  else if (strcmp(modo, "STOP") == 0) {
    pararModo();
  }
  else {
    Serial.println("ERRO:PARAM");
  }
}

// --------------------------------------------------
void processarSYS(char *cmd) {

  char *token = strtok(cmd, "[:]");
  if (!token || strcmp(token, "SYS") != 0) {
    Serial.println("ERRO:FORMATO");
    return;
  }

  char *acao = strtok(NULL, "[:]");
  if (!acao) {
    Serial.println("ERRO:PARAM");
    return;
  }

  if (strcmp(acao, "EXIT") == 0) {
    enviarStatus(8);
  }
  else {
    Serial.println("ERRO:PARAM");
  }
}

// --------------------------------------------------
void pararModo() {
  modoAlternado = false;
  digitalWrite(LED_VERDE, LOW);
  digitalWrite(LED_VERMELHO, LOW);
  enviarStatus(7);
}

// --------------------------------------------------
void executarModo() {
  if (modoAlternado && millis() - tempoAnterior >= 1000) {
    tempoAnterior = millis();
    estado = !estado;

    digitalWrite(LED_VERDE, estado);
    digitalWrite(LED_VERMELHO, !estado);
  }
}

2. Verifique se o código está funcionando corretamente usando o Monitor Serial (opcional).

2.1. Faça o upload do código para a placa do Arduino. 

2.2. Abra o Monitor Serial. Verifique se a porta de comunicação está correta com o que você verificou antes.

2.3. Digite os comandos:

<LED:GREEN:ON>  → Liga o LED verde
<LED:GREEN:OFF> → Desliga o LED verde
<LED:RED:ON>    → Liga o LED vermelho
<LED:RED:OFF>   → Desliga o LED vermelho
<LED:ALL:ON>    → Liga os dois LEDs
<LED:ALL:OFF>   → Desliga os dois LEDs
[MODE:ALT]      → Ativa o modo alternado (pisca)
[MODE:STOP]     → Para o modo alternado
[SYS:EXIT]      → Encerra a aplicação Python

2.4. Você deverá receber as mensagens de status também:

Código Python (no computador)

1. Crie uma pasta com o nome C:\Arduino\Python\

2. Utilize um editor de código como VS Code, NetBeans, PyCharm entre outros, ou mesmo o bloco de notas do Windows. Copie o código Python abaixo e salve como gui_arduino.py dentro da pasta que você criou.

2.1. Se você utilizar um editor de texto, tipo bloco de notas, renomeie o arquivo de .txt para .py

2.2. O código Python foi cuidadosamente documentado com comentários técnicos que descrevem sua arquitetura, fluxo de execução e a função de cada instrução, facilitando a compreensão e a manutenção do projeto.

import serial
import tkinter as tk

# -----------------------------
# CONFIGURAÇÃO
# -----------------------------
PORTA = 'COM4'
BAUDRATE = 9600

# -----------------------------
# Inicializa comunicação serial
# -----------------------------
try:
    arduino = serial.Serial(PORTA, BAUDRATE)
except Exception as e:
    print("Erro ao abrir porta:", e)
    exit()

# -----------------------------
# ENVIO DE COMANDOS
# -----------------------------
def enviar(cmd):
    try:
        arduino.write((cmd + '\n').encode())
    except Exception as e:
        status_label.config(text=f"ERRO ENVIO: {e}", fg="red")

# -----------------------------
# LEITURA SERIAL (não bloqueante)
# -----------------------------
def ler_serial():
    try:
        while arduino.in_waiting > 0:
            resposta = arduino.readline().decode(errors='ignore').strip()

            if not resposta:
                continue

            # Tratamento de erro
            if "ERRO" in resposta:
                status_label.config(text=resposta, fg="red")

            # Encerramento remoto
            elif "ENCERRANDO SISTEMA" in resposta:
                status_label.config(text=resposta, fg="blue")
                janela.after(500, fechar_app)

            # Status normal
            else:
                status_label.config(text=resposta, fg="green")

    except Exception as e:
        status_label.config(text=f"ERRO SERIAL: {e}", fg="red")

    janela.after(100, ler_serial)

# -----------------------------
# COMANDOS (LED)
# -----------------------------
def verde_on(): enviar("<LED:GREEN:ON>")
def verde_off(): enviar("<LED:GREEN:OFF>")

def red_on(): enviar("<LED:RED:ON>")
def red_off(): enviar("<LED:RED:OFF>")

def all_on(): enviar("<LED:ALL:ON>")
def all_off(): enviar("<LED:ALL:OFF>")

# -----------------------------
# COMANDOS (MODE)
# -----------------------------
def alt(): enviar("[MODE:ALT]")
def stop(): enviar("[MODE:STOP]")

# -----------------------------
# ENCERRAMENTO
# -----------------------------
def sair():
    enviar("[SYS:EXIT]")

def fechar_app():
    try:
        arduino.close()
    except:
        pass
    janela.destroy()

# -----------------------------
# INTERFACE GRÁFICA
# -----------------------------
janela = tk.Tk()
janela.title("Controle Arduino")
janela.geometry("250x350")

# LEDs
tk.Button(janela, text="Verde ON", command=verde_on).pack(pady=2)
tk.Button(janela, text="Verde OFF", command=verde_off).pack(pady=2)

tk.Button(janela, text="Vermelho ON", command=red_on).pack(pady=2)
tk.Button(janela, text="Vermelho OFF", command=red_off).pack(pady=2)

tk.Button(janela, text="Ambos ON", command=all_on).pack(pady=2)
tk.Button(janela, text="Ambos OFF", command=all_off).pack(pady=2)

# Modos
tk.Button(janela, text="Modo Alternado", command=alt).pack(pady=5)
tk.Button(janela, text="Parar", command=stop).pack(pady=2)

# Sair
tk.Button(janela, text="SAIR", command=sair, fg="white", bg="red").pack(pady=10)

# Status
status_label = tk.Label(janela, text="Status: ---", wraplength=200)
status_label.pack(pady=10)

# Inicia leitura contínua
ler_serial()

janela.mainloop()

⚠  ATENÇÃO: Se necessário, altere 'COM4' para a porta que você identificou no início do projeto.

3. Reset automático ao abrir a porta serial

Ao abrir a porta serial no Python, o Arduino normalmente é reiniciado automaticamente. Isso ocorre devido ao sinal DTR (Data Terminal Ready), utilizado pela interface USB-Serial.

Por esse motivo, é recomendado adicionar uma pequena pausa após abrir a conexão:

time.sleep(2)

Esse tempo permite que o Arduino finalize sua inicialização antes de receber comandos, evitando falhas na comunicação inicial.

4. Tratamento de erros na conexão serial

O código utiliza try/except para tratar possíveis erros ao abrir a porta serial.

Isso evita que o programa seja encerrado inesperadamente em situações como:

  • Porta incorreta
  • Arduino desconectado
  • Porta já em uso

Com isso, o usuário recebe uma mensagem clara para corrigir o problema.

5. Encerramento controlado do programa

Foi implementado o comando:

[SYS:EXIT]

Esse comando permite que o Python e o Arduino encerrem a comunicação de forma organizada.

Ao acioná-lo, o Arduino responde com: ENCERRANDO SISTEMA

E o Python finaliza a aplicação automaticamente.

Esse recurso evita encerramentos bruscos e torna o sistema mais robusto.

Executando o Projeto de Comunicação Bidirecional com Interface Gráfica

Após montar o circuito, carregar o código no Arduino e configurar o ambiente no Python, é hora de executar o projeto e observar a comunicação funcionando na prática.

1. Antes de executar o projeto, verifique se:

  • O Arduino está conectado no computador via cabo USB
  • O Código (sketch) do IDE do Arduino foi carregado usando o Upload.
  • O Monitor Serial está fechado
  • Se a Porta identificada (ex: COM3, COM4, etc.) está correta no script do Python
  • Se Baud rate está com 9600 (no monitor serial)

2. Abra o terminal (prompt do Windows) e execute diretamente o arquivo.

3. Vá até a pasta que você criou o arquivo, digite: cd C:\Arduino\Python

4. Digite python gui_arduino.py e dê um enter para executar o script Python.

4.1. Se tudo estiver correto, aparecerá a interface gráfica com os botões dos comandos que definimos.

4.1.1. Ao abrir a comunicação serial, o Arduino pode reiniciar automaticamente. Por isso, o sistema aguarda alguns instantes antes de iniciar a comunicação, garantindo que o microcontrolador esteja pronto para receber comandos.

5. Utilize os botões da interface:

A interface permite enviar comandos diretamente ao Arduino:

  • Botões de controle dos LEDs
  • Ativação do modo alternado
  • Parada do modo
  • Encerramento do sistema

Cada clique envia um comando estruturado para o Arduino, como:

<LED:GREEN:ON>

6. Observe o retorno do Arduino

Após receber o comando, o Arduino processa a ação e envia uma resposta pela serial.

Essa resposta é exibida na interface gráfica, como: LED VERDE LIGADO

Isso caracteriza uma comunicação bidirecional, onde:

  • Python envia comandos
  • Arduino executa
  • Arduino responde
  • Python exibe o resultado

7. Teste o modo alternado

Ao clicar no botão Modo Alternado será enviado o código estruturado para o Arduino:

[MODE:ALT]

Os LEDs passam a alternar automaticamente a cada 1 segundo.

Para interromper clique no botão Parar que enviará o código:

[MODE:STOP]

8. Encerramento do sistema

Ao clicar no botão SAIR, o seguinte comando é enviado:

[SYS:EXIT]

O Arduino responde: ENCERRANDO SISTEMA e o Python finaliza a aplicação de forma segura.

Conclusão

Com esse fluxo, você tem um sistema completo funcionando:

Interface gráfica → envia comando → Arduino executa → retorna status → interface atualiza

Esse modelo é a base para sistemas reais de automação, IoT e interfaces supervisórias.

O anúncio abaixo ajuda a manter o Squids Arduino funcionando

Comentários

×

Infomações do site / SEO








×

Adicionar Marcadores