Tutorial · Iniciante · 1 hora

Controle um robô por Bluetooth com um módulo HC-05

Ligue um HC-05 a um Arduino, pareie-o com seu celular e projete um protocolo de comandos seriais que mantenha o robô responsivo e seguro quando a conexão cai.

Introdução

O HC-05 é a maneira mais barata de tornar um robô sem fio. É uma ponte serial Bluetooth: o que seu celular envia chega à porta serial do Arduino como bytes comuns, e o que o Arduino imprime volta para o celular. Nada no seu código precisa saber que o Bluetooth existe.

Essa simplicidade é o atrativo, mas também é a armadilha. Uma conexão por rádio não é um fio — ela cai, atrasa e se desconecta enquanto seu robô ainda está andando. A maior parte deste tutorial trata da diferença entre um cabo e uma conexão sem fio.

Ligação, e o erro que quase todo mundo comete

O módulo HC-05 opera sua lógica em 3,3 V, embora a placa adaptadora aceite 5 V em VCC. O pino RX não tolera 5 V.

HC-05 Arduino Uno / Nano Observações
VCC 5V O regulador da placa resolve isso
GND GND Massa comum com todo o restante
TXD D10 (RX por software) A saída de 3,3 V do HC-05 é lida corretamente como nível lógico ALTO por uma entrada de 5 V
RXD D11 (TX por software) por meio de um divisor Aplicar 5 V diretamente ao RXD pode danificá-lo com o tempo
EN / KEY D9 (opcional) Coloque em ALTO na energização para entrar no modo de comandos AT

Reduza a linha de transmissão de 5 V do Arduino para 3,3 V com um divisor de tensão de dois resistores — 2 kΩ do pino do Arduino ao RXD do HC-05 e 3,3 kΩ dessa junção à massa. Custa dois componentes e evita uma falha realmente frustrante de diagnosticar, porque um módulo danificado assim muitas vezes continua parcialmente funcional: ele pareia, transmite e ignora ou corrompe silenciosamente os comandos enviados.

Diagrama de ligação. Um Arduino Uno ou Nano conecta sua linha TXD diretamente ao pino D10 de um módulo HC-05, identificado como seguro porque a saída de 3,3 volts do HC-05 é lida como ALTO em uma entrada de 5 volts. O caminho de retorno do pino D11 ao RXD do módulo passa por um divisor de tensão de 2 quilo-ohms e 3,3 quilo-ohms, com um aviso de que 5 volts diretos no RXD podem danificar parcialmente o módulo, de modo que ele ainda pareia, mas corrompe os comandos enviados.
Três dos quatro fios são triviais. O quarto precisa de um divisor, porque 5 V no RXD do módulo o danificam lentamente, não de forma obviamente imediata. Baixar SVG

Use SoftwareSerial, não os pinos 0 e 1. A porta serial de hardware é compartilhada com o USB, então um módulo ligado ali disputa com o monitor serial e bloqueia o carregamento do código.

#include <SoftwareSerial.h>
SoftwareSerial bt(10, 11);   // RX, TX

void setup() {
  Serial.begin(115200);      // USB, for debugging
  bt.begin(9600);            // HC-05 default baud
}

Pareamento

Alimente o robô, e o LED do módulo piscará rapidamente — isso significa “aguardando pareamento”. No celular, pareie com HC-05 usando o código 1234 ou 0000.

Pareamento não é conexão. A conexão só abre quando um aplicativo no celular realmente abre a porta serial. Qualquer terminal serial Bluetooth serve para começar; o LED do módulo passa a piscar com dois lampejos em intervalos mais lentos quando há uma conexão ativa.

Projetando o protocolo de comandos

O primeiro protocolo óbvio é um caractere por comando, e ele funciona por cerca de dez minutos:

if (bt.available()) {
  char c = bt.read();
  if (c == 'F') forward();
  else if (c == 'B') back();
  else if (c == 'S') stop();
}

Duas coisas dão errado com ele.

Ele não tem velocidade. Cada comando representa potência total, então o robô fica parado ou se desloca na velocidade máxima. Envie um valor junto com o comando — uma letra e um número, encerrados por uma quebra de linha, o que é facilmente analisável e fácil de digitar manualmente durante a depuração:

// "F180\n" — forward at 180 of 255
char buffer[16];
if (bt.available()) {
  int n = bt.readBytesUntil('\n', buffer, sizeof(buffer) - 1);
  buffer[n] = '\0';
  char cmd = buffer[0];
  int value = atoi(buffer + 1);
  ...
}

Ele pressupõe que a mensagem chega inteira. bt.available() ficar diferente de zero significa que alguns bytes chegaram, não todos. Ler um caractere por laço e agir imediatamente pode fazer o robô executar uma ação antes de o comando completo chegar. Ler até a quebra de linha é o que torna a análise confiável.

A falha que importa: perder a conexão

Um robô com fio para quando você solta os comandos. Um robô sem fio continua fazendo a última coisa que você mandou — que, se a última coisa foi “avançar na velocidade máxima” e você acabou de sair do alcance, é um robô embaixo do sofá em velocidade total.

Diagrama de temporização. Uma fileira de pulsos de comando chega a cada 100 milissegundos enquanto um botão é mantido e para em uma linha tracejada marcada como sinal perdido. Abaixo, uma barra sem tempo limite de segurança permanece vermelha, com o robô se movendo sem controle indefinidamente depois que o sinal se perde. Uma segunda barra com tempo limite de segurança de 400 milissegundos muda para parado pouco depois do último comando.
O fluxo de comandos para quando a conexão cai, não quando o usuário solta os comandos — por isso o robô precisa parar por conta própria. Baixar SVG

Todo robô sem fio precisa de um tempo limite de segurança. A regra é simples: se nenhum comando chegou recentemente, pare.

unsigned long lastCommandAt = 0;
const unsigned long TIMEOUT_MS = 500;

void loop() {
  if (readCommand()) lastCommandAt = millis();

  // Nothing heard recently? Assume the link is gone.
  if (millis() - lastCommandAt > TIMEOUT_MS) stop();
}

Para isso ajudar, o controlador precisa continuar falando. Faça o celular enviar o comando atual repetidamente — a cada 100 ms, mais ou menos, enquanto um botão estiver pressionado — em vez de uma vez ao pressionar e uma vez ao soltar. Assim, “botão solto” e “celular fora de alcance” produzem o mesmo resultado seguro, e você nunca depende de uma mensagem de soltura que talvez nunca chegue.

Use millis() para isso, nunca delay(). Um delay(200) no laço significa 200 ms sem ler comandos, sem verificar o tempo limite e sem conseguir receber novos comandos.

Modo de comandos AT

Para renomear o módulo ou alterar sua taxa de baud, mantenha EN/KEY em ALTO enquanto o módulo é energizado. O LED pisca lentamente — cerca de uma vez a cada dois segundos — e isso indica que você está no modo de comando, não no modo de dados.

AT              → OK
AT+NAME=MyRobot → OK
AT+PSWD="4821"  → OK
AT+UART=38400,0,0

Os comandos devem ser enviados a 38400 baud no modo AT, independentemente da taxa de baud do modo de dados, e a maioria das implementações de firmware exige retorno de carro e quebra de linha. Se AT não retornar nada, essa incompatibilidade é quase sempre o motivo.

Aumentar a taxa de baud de dados vale a pena se você estiver enviando telemetria de volta, mas deixe em 9600 enquanto acertar o protocolo — uma taxa mais alta quase sempre dificulta enxergar erros de enquadramento.

Verifique antes de controlar

Três verificações, nesta ordem:

  1. Teste de eco. Faça o Arduino imprimir de volta o que recebe. Se uma linha digitada voltar intacta, a ligação e a taxa de baud estão corretas.
  2. Teste de análise. Imprima a letra e o valor do comando analisado. Confirme que F180 resulta em F e 180, não em F e 1.
  3. Teste do tempo limite. Envie um comando de avanço e feche o aplicativo ou se afaste. O robô precisa parar dentro do prazo de segurança definido. Se não parar, corrija isso antes de colocar rodas no chão.

Quando a conexão estiver confiável, o mesmo protocolo de comandos entra diretamente em um robô com autonomia: construa a camada sem fio no projeto de robô controlado por celular ou avance para o ESP32, que serve uma página de controle pela web via Wi-Fi e não precisa de aplicativo algum.

Explore o grafo

Faz parte destas montagens

Projetos e caminhos de aprendizagem que incluem este tutorial.

Leitura complementar

Referências