Tutorial · Einsteiger · 1 Stunde

Roboter per Bluetooth mit HC-05-Modul steuern

HC-05 mit Arduino verdrahten, mit dem Handy koppeln und ein Befehlsprotokoll entwerfen, das den Roboter reaktiv und sicher hält, wenn die Strecke abreißt.

Einführung

Das HC-05 ist der billigste Weg, einen Roboter drahtlos zu machen. Es ist eine Bluetooth-Seriell-Brücke: Was dein Handy sendet, kommt am seriellen Port des Arduino als normale Bytes an, und was der Arduino druckt, geht zurück ans Handy. Nichts in deinem Sketch muss wissen, dass Bluetooth existiert.

Diese Einfachheit ist der Reiz – und die Falle. Eine Funkstrecke ist kein Kabel: Sie bricht ab, sie verzögert, und sie trennt, während dein Roboter noch fährt. Der größte Teil dieser Anleitung handelt von diesem Unterschied.

Verdrahtung – und der Fehler, den fast jeder macht

Das HC-05-Modul läuft logisch mit 3,3 V, auch wenn die Breakout-Platine an VCC 5 V akzeptiert. Der RX-Pin ist nicht 5-V-tolerant.

HC-05 Arduino Uno / Nano Hinweise
VCC 5V Der Regler der Platine übernimmt das
GND GND Gemeinsame Masse mit allem anderen
TXD D10 (Soft-RX) 3,3 V heraus werden von einem 5-V-Eingang sauber als High gelesen
RXD D11 (Soft-TX) über einen Teiler 5 V direkt hinein beschädigen es mit der Zeit
EN / KEY D9 (optional) Beim Einschalten auf HIGH für den AT-Befehlsmodus

Senke die 5-V-Sendeleitung des Arduino mit einem Zweier-Teiler auf 3,3 V – 2 kΩ vom Arduino-Pin zum RXD des HC-05 und 3,3 kΩ von diesem Knoten nach Masse. Das kostet zwei Bauteile und verhindert einen Ausfall, der wahrhaftig wahnsinnig schwer zu diagnostizieren ist, weil ein so beschädigtes Modul oft halb weiterarbeitet: Es koppelt, es sendet, und es ignoriert oder verstümmelt still, was du ihm schickst.

Ein Verdrahtungsplan. Ein Arduino Uno oder Nano verbindet seine TXD-Leitung direkt mit Pin D10 eines HC-05-Moduls, beschriftet als sicher, weil 3,3 Volt heraus an einem 5-Volt-Eingang als HIGH gelesen werden. Der Rückweg von Pin D11 zum RXD-Pin des Moduls läuft über einen 2-Kiloohm- und 3,3-Kiloohm-Spannungsteiler, mit Warnhinweis, dass 5 Volt direkt in RXD das Modul teilweise beschädigen, sodass es sich noch koppelt, aber Gesendetes verstümmelt.
Drei der vier Drähte sind trivial. Der vierte braucht einen Teiler, weil 5 V in den RXD des Moduls es langsam statt offensichtlich beschädigen. SVG herunterladen

Nutze SoftwareSerial, nicht Pins 0 und 1. Die Hardware-Serienschnittstelle teilt sich USB, ein dort verdrahtetes Modul bekämpft also den seriellen Monitor und blockiert Uploads.

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

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

Koppeln

Versorge den Roboter, und die LED des Moduls blinkt schnell – das heißt „wartet auf Kopplung“. Kopple auf deinem Handy mit HC-05 per Code 1234 oder 0000.

Koppeln ist nicht Verbinden. Die Strecke öffnet sich erst, wenn eine App auf dem Handy den seriellen Port tatsächlich öffnet. Zum Start tut es jedes Bluetooth-Seriell-Terminal; die LED des Moduls wechselt auf langsames Doppelblinken, sobald eine Verbindung lebt.

Das Befehlsprotokoll entwerfen

Das naheliegende erste Protokoll ist ein Zeichen pro Befehl, und es funktioniert etwa zehn Minuten:

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

Zwei Dinge gehen damit schief.

Es hat keine Drehzahl. Jeder Befehl ist Vollgas, der Roboter steht also oder rast. Sende stattdessen einen Wert mit dem Befehl – ein Buchstabe und eine Zahl, abgeschlossen mit Zeilenumbruch, trivial zu parsen und trivial von Hand zu tippen beim Debuggen:

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

Es nimmt an, die Nachricht komme ganz an. Dass bt.available() ungleich null wird, heißt einige Bytes sind da, nicht alle. Ein Zeichen pro Schleife zu lesen und sofort zu handeln heißt, auf einen halben Befehl zu reagieren. Bis zum Zeilenumbruch zu lesen macht das Parsen zuverlässig.

Der Ausfall, der zählt: Abriss der Strecke

Ein kabelgebundener Roboter stoppt, wenn du loslässt. Ein Funkroboter macht mit dem Letzten weiter, was du ihm gesagt hast – und wenn das Letzte „vorwärts mit Vollgas“ war und du gerade aus der Reichweite gelaufen bist, ist das ein Roboter unterm Sofa mit Vollgas.

Ein Zeitdiagramm. Eine Reihe von Befehlspulsen trifft alle 100 Millisekunden ein, solange ein Knopf gehalten wird, und stoppt an einer gestrichelten Linie mit Markierung Signal verloren. Darunter zeigt ein Balken ohne Timeout rot und fährt nach Signalverlust unbegrenzt unkontrolliert weiter. Ein zweiter Balken mit 400-Millisekunden-Totmann-Timeout wechselt kurz nach dem letzten Befehl auf gestoppt.
Der Befehlsstrom stoppt, wenn die Strecke abreißt, nicht wenn der Nutzer loslässt – darum muss sich der Roboter selbst per Timeout stoppen. SVG herunterladen

Jeder Funkroboter braucht einen Totmann-Timeout. Die Regel ist einfach: Kam kürzlich kein Befehl, stoppe.

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();
}

Damit das hilft, muss die Steuerung weiterreden. Lass das Handy den aktuellen Befehl wiederholt senden – etwa alle 100 ms, solange ein Knopf gehalten wird –, statt einmal beim Drücken und einmal beim Loslassen. Dann erzeugen „Knopf losgelassen“ und „Handy außer Reichweite“ dasselbe sichere Ergebnis, und du hängst nie an einer Loslass-Nachricht, die vielleicht nie kommt.

Nutze dafür millis(), niemals delay(). Ein delay(200) in der Schleife sind 200 ms, in denen kein Befehl gelesen, der Timeout nicht geprüft wird und der Roboter taub ist.

AT-Befehlsmodus

Um das Modul umzubenennen oder seine Baudrate zu ändern, halte EN/KEY beim Einschalten auf HIGH. Die LED blinkt langsam – etwa alle zwei Sekunden –, so weißt du, dass du im Befehls- statt im Datenmodus bist.

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

Befehle müssen im AT-Modus mit 38400 Baud gesendet werden, egal welche Datenmodus-Baudrate gilt, und die meiste Firmware verlangt Wagenrücklauf plus Zeilenumbruch. Gibt AT nichts zurück, liegt es fast immer an dieser Diskrepanz.

Die Daten-Baudrate zu erhöhen lohnt sich, wenn du Telemetrie zurückstreamst, aber lasse sie auf 9600, solange du das Protokoll gerade biegst – eine schnellere Strecke macht Rahmenfehler meist nur schwerer sichtbar.

Prüfen, bevor du fährst

Drei Prüfungen, in dieser Reihenfolge:

  1. Echotest. Lass den Arduino zurückdrucken, was er empfängt. Kommt eine getippte Zeile intakt zurück, stimmen Verdrahtung und Baudrate.
  2. Parsetest. Drucke Befehlsbuchstabe und Wert. Bestätige, dass F180 F und 180 ergibt, nicht F und 1.
  3. Timeouttest. Sende einen Vorwärtsbefehl, schließe dann die App oder gehe weg. Der Roboter muss innerhalb deines Timeouts stoppen. Tut er es nicht, behebe das, bevor Räder auf den Boden kommen.

Sobald die Strecke zuverlässig ist, passt dasselbe Befehlsprotokoll direkt auf einen Roboter mit Autonomie: Baue die Funkschicht im handygesteuerten Roboterprojekt, oder steige auf den ESP32 um, der eine Steuerseite über WLAN liefert und gar keine App braucht.

Den Graphen erkunden

Teil dieser Bauprojekte

Projekte und Lernpfade, die dieses Tutorial enthalten.

Weiterführende Literatur

Quellen