Programa no PC falando com o Arduino pela serial: porta, velocidade e o reset escondido
Faça um programa no computador conversar com o Arduino pela USB: mesma velocidade dos dois lados, porta certa, espera pelo reset da placa e timeout na leitura.
Você escreveu um programa no computador com um botão "Ligar LED", o Arduino tem um sketch que lê a serial, e nada acontece. Pelo monitor serial da IDE, a letra funciona. Pelo seu programa, não. Na maioria das vezes, o programa manda o comando cedo demais: abrir a porta reinicia o Uno, e os primeiros bytes chegam enquanto a placa ainda está acordando. Quando não é isso, é velocidade diferente nos dois lados, a porta errada ou o monitor serial da IDE ainda segurando a porta.
A resposta curta: use a mesma velocidade (9600, por exemplo) no Serial.begin e no programa, abra a porta que apareceu quando a placa foi conectada, feche o monitor serial da IDE, espere a placa avisar que está pronta antes de mandar o primeiro comando e use timeout em toda leitura.
Por que a placa não responde ao primeiro comando
A serial é um fio de bytes com um ritmo combinado. Não há negociação: se um lado fala a 9600 bits por segundo e o outro escuta a 115200, os bits são amostrados no tempo errado e chega lixo ou nada.
O reset é menos óbvio. No Uno e em placas parecidas, abrir a porta serial no computador aciona um sinal que reinicia o microcontrolador. É assim que a IDE consegue gravar o programa sem você apertar o botão de reset. O efeito colateral é que, ao abrir a porta no seu programa, a placa reinicia, passa pelo bootloader e só depois de algo como um ou dois segundos chega ao setup(). O comando enviado nesse intervalo se perde.
Placas com USB nativo, como o Leonardo, não reiniciam ao abrir a porta. Nelas, a mensagem de "pronto" do setup() pode ter saído antes do seu programa conectar. O protocolo abaixo trata os dois casos.
O protocolo mínimo: pronto, comando, resposta
O exemplo que vai atravessar o texto: um script em Python, no notebook, que liga e desliga o LED do pino 13. O protocolo tem três regras: a placa anuncia PRONTO ao iniciar e sempre que receber ?; cada comando é uma letra; toda letra recebe uma linha de resposta.
O sketch do Arduino
const int PINO_LED = 13;
void setup() {
pinMode(PINO_LED, OUTPUT);
Serial.begin(9600);
Serial.println("PRONTO");
}
void loop() {
if (Serial.available() > 0) {
char c = Serial.read();
if (c == 'A') {
digitalWrite(PINO_LED, HIGH);
Serial.println("OK A");
} else if (c == 'a') {
digitalWrite(PINO_LED, LOW);
Serial.println("OK a");
} else if (c == '?') {
Serial.println("PRONTO");
} else if (c >= ' ') {
// Ignora quebras de linha; avisa sobre qualquer outro caractere.
Serial.print("ERRO ");
Serial.println(c);
}
}
}
O programa no computador, em Python
Ele usa a biblioteca pyserial (pip install pyserial). A porta vem como argumento: no Windows, algo como COM3; no Linux, /dev/ttyACM0 ou /dev/ttyUSB0; no macOS, um nome começando com /dev/cu..
import sys
import time
import serial
from serial.tools import list_ports
BAUD = 9600
def ler_linha(ser):
return ser.readline().decode('utf-8', errors='replace').strip()
def esperar_pronto(ser, limite_s=6.0):
fim = time.monotonic() + limite_s
while time.monotonic() < fim:
linha = ler_linha(ser)
if linha == 'PRONTO':
return True
if linha == '':
# Nada em 1 s: talvez a placa já tenha iniciado antes de conectarmos.
ser.write(b'?')
return False
def main():
if len(sys.argv) < 2:
print('uso: python led.py PORTA')
print('portas encontradas:')
for p in list_ports.comports():
print(' ', p.device, '-', p.description)
return 1
porta = sys.argv[1]
try:
with serial.Serial(porta, BAUD, timeout=1) as ser:
if not esperar_pronto(ser):
print('A placa não disse PRONTO. Confira velocidade, porta e sketch.')
return 2
for comando in (b'A', b'a'):
ser.write(comando)
resposta = ler_linha(ser)
print('enviei', comando.decode(), '-> recebi', resposta or '(nada em 1 s)')
time.sleep(1)
except serial.SerialException as erro:
print('Não consegui usar a porta', porta, '-', erro)
return 3
return 0
if __name__ == '__main__':
sys.exit(main())
Rodando com a placa certa, a saída esperada é:
enviei A -> recebi OK A
enviei a -> recebi OK a
O timeout=1 no construtor faz o readline() desistir depois de um segundo. Sem ele, uma placa travada ou desconectada deixa o seu programa esperando para sempre. É a mesma lição de colocar timeout antes que a espera vire infinita, só que num cabo USB.
Como achar a porta e o cabo certos
- Liste as portas sem a placa, rodando o script sem argumentos.
- Conecte a placa e liste de novo. A porta nova é a sua. Não escolha a primeira da lista: no Windows, COM1 costuma existir sem ter nada a ver com o Arduino.
- Se nenhuma porta nova aparece, troque o cabo. Muitos cabos USB vendidos com carregadores só têm os fios de energia. A placa acende e o computador não vê nada.
- Clones com conversor USB-serial diferente podem precisar de driver em alguns sistemas. A descrição da porta na listagem ajuda a identificar o chip.
- No Linux, permissão negada costuma significar que o seu usuário não está no grupo que pode usar portas seriais (em Debian e Ubuntu,
dialout). Adicione e faça login de novo.
Uma porta, um programa
Uma porta serial só aceita um programa aberto por vez. Se o monitor serial da IDE está aberto, o seu script recebe erro de acesso negado ou porta ocupada. O contrário também vale: com o script rodando, a IDE não consegue gravar o sketch. Feche um antes de abrir o outro.
Quando o comando precisa levar um número
Uma letra por comando acaba rápido. Para mandar um valor, como o brilho de um LED, o caminho mais fácil de depurar é texto em linha: o computador envia B128 seguido de quebra de linha, e o Arduino junta os caracteres até a quebra antes de interpretar. Não processe byte a byte achando que a linha inteira já chegou; a serial entrega aos pedaços, no ritmo dela. Texto também tem a vantagem de poder ser testado à mão no monitor serial, sem o seu programa no meio.
A mesma ideia de canal serial vale sem fio. Um módulo Bluetooth clássico é uma serial com o cabo trocado por rádio, como em Bluetooth HC-05 no Arduino.
Erros comuns
- Velocidades diferentes nos dois lados: chega lixo ou nada, e o código parece certo nos dois programas.
- Mandar comando logo após abrir a porta: a placa está reiniciando e o primeiro comando some.
- Ler sem timeout: se a placa trava ou o cabo sai, o programa congela junto.
- Monitor serial da IDE aberto ao mesmo tempo: o seu programa não consegue abrir a porta.
- Cabo só de carga: a placa liga, mas nenhuma porta aparece no computador.
- Protocolo sem resposta: o computador nunca sabe se o comando chegou, e você depura no escuro.
Checklist
- Mesma velocidade no
Serial.begine no programa do computador. - A porta usada é a que apareceu quando a placa foi conectada.
- O cabo é de dados, testado com a IDE.
- Monitor serial da IDE fechado enquanto o programa roda.
- O programa espera
PRONTOantes do primeiro comando. - Toda leitura tem timeout.
- Cada comando recebe uma resposta, inclusive para caractere desconhecido.
Rode o script com a sua porta agora. A placa disse PRONTO, e a letra A voltou como OK A? Se a primeira linha nunca chega, o problema está na porta ou na velocidade, não no comando.
Referência: http://waihung.net/arduino-controlled-by-visual-basic-program/