Tutorial · Principiante · 1 hora

Controla un robot por Bluetooth con un módulo HC-05

Cablea un HC-05 a un Arduino, emparéjalo con tu teléfono y diseña un protocolo serie de comandos que mantenga el robot receptivo y seguro si cae el enlace.

Introducción

El HC-05 es la forma más barata de hacer un robot inalámbrico. Es un puente serie Bluetooth: lo que tu teléfono envía llega al puerto serie del Arduino como bytes ordinarios, y lo que el Arduino imprime vuelve al teléfono. Nada en tu programa tiene que saber que Bluetooth existe.

Esa simplicidad es el atractivo, y también es la trampa. Un enlace de radio no es un cable —se cae, tiene retardo y se desconecta mientras tu robot sigue conduciendo. La mayor parte de este tutorial trata de esa diferencia.

Cableado, y el error que casi todo el mundo comete

El módulo HC-05 maneja su lógica a 3,3 V, aunque la placa de adaptación acepta 5 V en VCC. El pin RX no tolera 5 V.

HC-05 Arduino Uno / Nano Notas
VCC 5V El regulador de la placa se encarga de esto
GND GND Tierra común con todo lo demás
TXD D10 (RX por software) La salida de 3,3 V se lee bien como HIGH lógico en una entrada de 5 V
RXD D11 (TX por software) a través de un divisor 5 V directos lo dañarán con el tiempo
EN / KEY D9 (opcional) Pon en HIGH al encender para entrar en modo de comandos AT

Baja la línea de transmisión de 5 V del Arduino a 3,3 V con un divisor de dos resistencias —2 kΩ del pin del Arduino al RXD del HC-05, y 3,3 kΩ de esa unión a tierra. Cuesta dos componentes y evita un fallo genuinamente enloquecedor de diagnosticar, porque un módulo dañado así a menudo sigue funcionando a medias: se empareja, transmite e ignora o corrompe en silencio lo que le envías.

Un diagrama de cableado. Un Arduino Uno o Nano conecta su línea TXD directamente al pin D10 de un módulo HC-05, etiquetado como seguro porque la salida de 3,3 voltios se lee como HIGH en una entrada de 5 voltios. La ruta de vuelta del pin D11 al pin RXD del módulo pasa por un divisor de tensión de 2 kiloohmios y 3,3 kiloohmios, con una advertencia de que 5 voltios directos al RXD dañan el módulo parcialmente por lo que sigue emparejando pero corrompe lo que le envías.
Tres de los cuatro hilos son triviales. El cuarto necesita un divisor, porque 5 V en el RXD del módulo lo dañan lentamente en lugar de obviamente. Descargar SVG

Usa SoftwareSerial, no los pines 0 y 1. El puerto serie hardware se comparte con el USB, así que un módulo cableado ahí lucha con el monitor serie y bloquea las subidas.

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

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

Emparejamiento

Alimenta el robot, y el LED del módulo parpadea rápido —eso es «esperando emparejar». En tu teléfono, empareja con HC-05 usando el código 1234 o 0000.

Emparejar no es conectar. El enlace solo se abre cuando una app del teléfono abre realmente el puerto serie. Cualquier terminal serie Bluetooth sirve para empezar; el LED del módulo pasa a un doble parpadeo una vez que la conexión está activa.

Diseñar el protocolo de comandos

El primer protocolo obvio es un carácter por comando, y funciona unos diez minutos:

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

Dos cosas salen mal con él.

No tiene velocidad. Cada comando es a todo gas, así que el robot o está parado o embiste. Envía un valor con el comando en su lugar —una letra y un número, terminados en nueva línea, que es trivial de analizar y trivial de escribir a mano mientras depuras:

// "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);
  ...
}

Supone que el mensaje llega entero. Que bt.available() pase a no cero significa que algunos bytes están aquí, no todos. Leer un carácter por vuelta y actuar de inmediato actuará sobre medio comando. Leer hasta la nueva línea es lo que hace fiable el análisis.

El fallo que importa: perder el enlace

Un robot con cable se detiene cuando sueltas. Un robot inalámbrico sigue haciendo lo último que le dijiste —lo que, si lo último fue «adelante a toda velocidad» y acabas de salir del alcance, es un robot yendo bajo el sofá a toda velocidad.

Un diagrama de tiempos. Una fila de pulsos de comando llega cada 100 milisegundos mientras se mantiene un botón, y luego se detiene en una línea discontinua marcada señal perdida. Debajo, una barra sin timeout permanece roja y conduciendo fuera de control indefinidamente tras perderse la señal. Una segunda barra con timeout de hombre muerto de 400 milisegundos pasa a detenido poco después del último comando.
El flujo de comandos se detiene cuando el enlace se cae, no cuando el usuario suelta —por eso el robot tiene que expirar por sí solo. Descargar SVG

Todo robot inalámbrico necesita un timeout de hombre muerto. La regla es simple: si no ha llegado ningún comando recientemente, detente.

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 que esto ayude, el controlador tiene que seguir hablando. Haz que el teléfono envíe el comando actual repetidamente —cada 100 ms o así mientras se mantiene un botón— en lugar de una vez al pulsar y una vez al soltar. Entonces «botón soltado» y «teléfono fuera de alcance» producen el mismo resultado seguro, y nunca dependes de un mensaje de soltado que quizá nunca llegue.

Usa millis() para esto, nunca delay(). Un delay(200) en el bucle son 200 ms durante los que no se lee ningún comando, no se comprueba el timeout y el robot está sordo.

Modo de comandos AT

Para renombrar el módulo o cambiar su velocidad en baudios, mantén EN/KEY en HIGH al encender. El LED parpadea lento —aproximadamente una vez cada dos segundos— que es como sabes que estás en modo de comandos en lugar de modo de datos.

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

Los comandos deben enviarse a 38400 baudios en modo AT sin importar la velocidad en modo de datos, y la mayoría del firmware requiere retorno de carro y nueva línea. Si AT no devuelve nada, esa discordancia es casi siempre la causa.

Subir la velocidad de datos merece la pena si estás retransmitiendo telemetría, pero déjala en 9600 mientras afinas el protocolo —un enlace más rápido sobre todo hace los errores de encuadre más difíciles de ver.

Verifica antes de conducir

Tres comprobaciones, en este orden:

  1. Prueba de eco. Haz que el Arduino devuelva lo que reciba. Si una línea escrita vuelve intacta, el cableado y la velocidad están bien.
  2. Prueba de análisis. Imprime la letra de comando analizada y el valor. Confirma que F180 da F y 180, no F y 1.
  3. Prueba de timeout. Envía un comando de avance, luego cierra la app o aléjate. El robot debe detenerse dentro de tu timeout. Si no lo hace, arréglalo antes de poner ruedas en el suelo.

Una vez que el enlace es fiable, el mismo protocolo de comandos encaja directo en un robot con autonomía: construye la capa inalámbrica en el proyecto de robot controlado por teléfono, o sube al ESP32, que sirve una página de control por Wi-Fi y no necesita ninguna app.

Explora el grafo

Forma parte de estos montajes

Proyectos y rutas de aprendizaje que incluyen este tutorial.

Lecturas adicionales

Referencias