Herzlich willkommen!
In diesem Beitrag lernen wir, wie man einen PrintBot von einem Terminal mit Hilfe von Protocoder programmieren und steuern kann.
Protocoder ist ein Prototyping-Werkzeug für mobile Anwendungen, das es ermöglicht, Schnittstellen und Programme auf schnelle Art und Weise zu erstellen. Entwickelt wurde Protocoder von Victor Díaz.
Hier findest du weitere Informationen und kannst das Programm downloaden. Wir verwenden Protocoder als Schnittstelle und setzen diese dazu ein, uns über Bluetooth mit der Platine ZUM BT-328 zu verbinden. Wir können den PrintBot Evolution frei bewegen oder einen der automatischen Modi wählen: Er kann Linien oder dem Licht folgen oder Objekten ausweichen.
Für die Steuerung des PrintBot Evolution werden wir zwei Programme verwenden:
- Protocoder Code: speichert die Grafik-Schnittstelle sowie die Funktionen, die dazu dienen, über das Bluetooth des Terminals Daten an die Platine zu senden.
- ZUM BT-328 Code: speichert die Programmierung des PrintBot Evolution, wie zum Beispiel die Funktionsweise oder die Bluetooth-Kommunikation.
Am Ende dieses Beitrags kannst du beide Codes herunterladen. Denk daran, dass du für das Ausführen des Protocoder-Codes, Protocoder bereits in deinem Terminal installiert haben musst (du kannst es hier herunterladen).
Protocoder-Code
Um den Protocoder-Code sehen und modifizieren zu können, musst du zunächst von deinem Terminal aus auf die Anwendung zugreifen und die IP-Adresse eingeben, die im unteren Teil auf dem Bildschirm deines Webbrowsers erscheint. Lade den Code am Ende dieses Beitrags herunter und verschiebe die Datei printbot_evolution_protocoder.proto auf dein Terminal. Dort öffnest du im Datei-Explorer den Ordner, in dem du die Datei gespeichert hast (für gewöhnlich Downloads). Klicke auf den Protocoder-Code, damit sich ein Dialogfenster öffnet. Wähle „mit Protocoder öffnen“ aus. Wähle anschließend im Pop-up-Fenster die Option „installieren“ aus. Jetzt kannst du den Code verwenden und modifizieren.
Wenn du die Datei .proto, importiert hast, kannst du das Projekt in dem Reiter des Browsers MY PROJECTS sehen. Klicke auf das Bild, um den Code zu visualisieren. Innerhalb des Codes unterscheiden wir verschiedene Teile:
- Textfeld erstellen: Eine Schaltfläche mit einem Text zu erstellen ist hilfreich, um mehr Informationen über die Funktion dieser Schaltfläche zu geben. Der Code dafür sieht folgendermaßen aus:
123var btnSiguelineas = ui.addButton("Sigue lineas",240, 350, w, h).onClick(function(){ // Creamos el botón. Parametros: ("texto a mostrar",posición X, posicion Y, ancho, alto)send("siguelineas"); // Pasamos como parámetro a la función send (enviar en inglés) la palabra siguelineas, que se recibirá completamente en Arduino.});
Im Code erstellen wir die Schaltfläche mit var btnSiguelineas. Damit es kein Durcheinander gibt müssen wir jede Schaltfläche anders benennen. Anschließend bestimmen wir mit ui.addButton, dass die Variable btnSiguelineas eine Schaltfläche sein soll. In Klammern fügen wir der Reihe nach den Text ein, der in der Schaltfläche erscheinen soll: die X- und Y-Position sowie die Höhe und die Breite der Schaltfläche. Anschließend geben wir die Funktion an, die mit Drücken der Schaltfläche ausgeführt werden soll, in diesem Fall per Bluetooth das Wort siguelineas (Linienfolger) zu senden. - Eine Schaltfläche mit Bild erstellen
123var btn180 = ui.addImageButton(x+2*w, y+3*w, w, w, "180.png",false).onClick(function(){ // Creamos un botón con imagen. Parámetros: (posiciónX,posiciónY,ancho,alto,"ruta de la imagen",true/false)send("180");});
Wir erstellen die Schaltfläche mit var btn180, aber diese ist anders als die Vorige. In diesem Fall verwenden wir ein Bild anstelle eines Textes. Anschließend bestimmen wir mit ui.addImageButton, dass die Variable btn180 eine Bildfläche sein soll. In Klammern fügen wir der Reihe nach die X- und Y-Position, die Höhe und die Breite der Fläche sowie den Namen des Bildes, das auf der Fläche erscheinen soll, hinzu. Anschließend geben wir an, welche Funktion mit dem Drücken der Schaltfläche ausgeführt werden soll. In diesem Fall lautet sie, per Bluetooth 180 zu senden. Bilder oder andere Dateien können leicht hinzugefügt werden, in dem wir sie vom Datei-Explorer des Computers in die rechte untere Ecke ziehen. Achte darauf, dass sich der Name, je einfacher er ist, auch umso leichter fehlerfrei in den Code einsetzen lässt.
- Schaltflächen für das Dashboard erstellen: Diese fügen wir genauso wie die Schaltflächen des Gerätes hinzu. Allerdings verwenden wir dieses Mal statt des Präfixes ui, das Präfix dashboard. Standardmäßig erscheint das Dashboard nicht. Wenn du dies ändern willst, ersetze die Linie dashboard.show(false) durch dashboard.show(true).
- Funktionen für das Verbinden und Trennen von Bluetooth: Es geht um jene, die in den Variablen btnConectar und btnDesconectar enthalten sind. Außerdem erstellen wir eine Variable mit dem Namen btClient, die wir für die „Interaktion“ mit Bluetooth verwenden.
- Verbinden: Bevor wir eine Verbindung erstellen können, müssen wir zunächst die Schaltfläche mit dem Text „Bluetooth verbinden“ bilden. In die Funktion, die mit dem Drücken der Taste aktiviert wird, fügen wir die Bluetooth-Verbindung ein.
12345678910111213var btnConectar = ui.addButton("Conectar bluetooth", 240, 150,300,100).onClick(function() { // Creamos el botón del bluetoothbtClient = network.bluetooth.connectSerial(function(status) { // Conectamos btClient -- Mostrará una lista de los dispositivos enlazados previamenteconsole.log("connected " + status); // Debugif (status === true){ // Si esta conectadoconsole.log(status); // guardamos el estadobluetoothOn = 1; // Cambiamos la variable de estado a conectado (1)ui.jump(btnConectar); // Hacemos que el boon "salte"ui.alpha(btnConectar, 0); // Hacemos completamente transparente el botónmedia.playSound("Drago"); // Reproducimos el sonido almacenado previamente.device.vibrate(750); // Hacemos que vibre el dispositivo}});});
Außerdem bestimmen wir, abgesehen davon, dass wir uns mit Bluetooth verbinden, dass der Variable bluetoothOn, die wir als „Signal“ für das Verbinden/Trennen von Bluetooth verwenden, der Wert 1 zugewiesen wird. Die Transparenz der Fläche ändern wir auf 0, das heißt, auf komplett transparent. Darüber hinaus fügen wir ein paar Animationen wie jump hinzu und lassen das Gerät beim Verbinden vibrieren. - Trennen: Zum Trennen müssen wir zunächst eine Schaltfläche mit dem Text „Trennen“ erstellen. In die Funktion, die mit dem Drücken der Taste aktiviert wird, fügen wir das Trennen von Bluetooth hinzu.
123456var btnDesconectar = ui.addButton("Desconectar", 540, 150,200,100).onClick(function() { // Creamos el botón de conectarbtClient.disconnect(); // Desconectamos una vez se ha pulsadobluetoothOn=0; // Cambiamos la variable de estado a desconectado (0)ui.alpha(btnConectar, 255); // Cambiamos la transparencia del botón a opacoui.jump(btnDesconectar); // Hacemos que "salte" el botón de desconectar});
Außerdem bestimmen wir, abgesehen davon, dass wir die Bluetooth-Verbindung trennen, dass der Variable bluetoothOn, die wir als „Signal“ für das Verbinden/Trennen von Bluetooth verwenden, der Wert 0 zugewiesen wird. Die Transparenz der Fläche ändern wir auf 255, das heißt, auf opak.
- Verbinden: Bevor wir eine Verbindung erstellen können, müssen wir zunächst die Schaltfläche mit dem Text „Bluetooth verbinden“ bilden. In die Funktion, die mit dem Drücken der Taste aktiviert wird, fügen wir die Bluetooth-Verbindung ein.
- Funktion für das Senden und Empfangen von Daten über Bluetooth: Die Funktion für die Datenübertragung per Bluetooth lautet:
123456function send(str) { // Función que utilizamos para enviar un string (cadena de caracteres)if (bluetoothOn == 1) { // Si el bluetooth está conectado, se ejecuta el código de las siguientes lineasbtClient.send("="+str+"+"); // Enviamos la cadena de caracteres entre los simbolos = y +console.log("="+str+"+"); // Mostramos en la consola la cadena de caracteres que se esta enviando.}}
Diese Funktion besitzt keine Schaltfläche, denn sie wird, mit Ausnahme des Verbindens/Trennens von Bluetooth, beim Anklicken einer Schaltfläche mit aufgerufen. Um Daten über Bluetooth zu senden verwenden wir den Befehl btClient.send(„=“+str+“+“). Für die Vorwärtsbewegung eines Autos sendet diese Funktion beispielsweise:=avanzar+. In diesem Fall haben wir den Code so festgelegt, aber du kannst auch eine andere Kodifizierung verwenden. Es werden die Zeichen =y+ als Begrenzungen für den Anfang und das Ende der Übertragung verwendet.- Daten über Bluetooth senden: Um Daten über Bluetooth zu senden verwenden wir die Funktion send() mit einem Text, der in Anführungszeichen gesetzt ist. Wenn wir beispielsweise das Wort retroceder (zurücksetzen) senden wollen verwenden wir: senden („retroceder“).
Arduino Code
Um den Code von Arduino einsehen oder modifizieren zu können, musst du am Ende dieses Beitrags das Paket herunterladen und auf die Datei printbot_evolution_arduino.ino klicken. Wenn du keine Arduino-IDE oder eine andere IDE (mit der du die Datei lesen kannst) installiert hast, kannst du sie im Programmierkurs für Maker zu Arduino und Protocoder herunterladen. Dort kannst du auch verschiedene IDEs ansehen und deinen Favoriten herunterladen. So lernst du auch gleich mehr über Arduino und Protocoder.
- Deklaration von Variablen und Einfügen von Bibliotheken: Wir legen die einzelnen Variablen fest, die wir während des Programms verwenden:
1234567891011121314151617181920212223242526272829303132333435//Para controlar los servos utilizamos la librería Servo.h#include "Servo.h"// Declaramos los servosServo servoR, servoL; // Servos de los motores derecho e izquierdoServo servoBat; // Servo que controla el movimiento del sensor Bat (ultrasonido)// Declaramos las variables relativas al sensor Bat// TP corresponde al "trigger pin" (pin que envía la señal de ultrasonido)// y EP corresponde al "Echo pin" (pin que recibe la señal de ultrasonido)// Midiendo el tiempo que pasa desde que se envía hasta que se recibe la señal de ultrasonido// podemos calcular la distancia.int TP = 4, EP = 5;//Declaramos los pines correspondientes a los sensores LDR (Resistencias Dependientes de la Luz)int ldrIzq = A2, ldrDrch = A3; //pines ldr//Declaramos los pines correspondientes a los sensores Infrarrojosint irIzq = 2, irDrch = 3; // pines infrarrojos//Tiempos por defecto para los Delays.// Un delay es un retardo, es decir, un periodo de tiempo dado en el que se para la ejecución// del programa hasta que se termina ese tiempo.// Por ejemplo, se utiliza aquí para conseguir que el PrintBot gire durante cierto tiempo.int defaultDelay = 200, lineasDelay = 0;//Retardos para conseguir que el PrintBot gire los diferentes ángulos.//Es posible que tengas que modificar estos tiempos para adecuarlo a tu PrintBot.int giro_180 = 1028, giro_90 = 514, giro_45 = 257, giro_30 = 200;//Declaramos los pines del zumbador (buzzer en Inglés)int pinBuz = 12;// Desde Protocoder recibiremos una cadena de caracteres (string)que utilizaremos para// recibir determinar cual es el movimiento o modo de funcionamiento del PrintBotString inString = "";
Einige von ihnen verwenden wir, um Arduino zu sagen, an welche Pins die nicht gedruckten Teile („vitamins“) angeschlossen werden müssen und andere, wie die delays, um die Ausführung des Programmes zu modifizieren. Wenn die Kreise nicht korrekt ausgeführt werden oder du ihren Drehwinkel ändern möchtest, kannst du die Variablen giro_180, giro_90, giro_45 y giro_30 verändern. - Setup: In der Funktion Setup, zusammen mit Loop eine der obligatorischen Funktionen bei Arduino, initialisieren wir das Programm und die Komponenten, die es bilden. Der Code der Setup-Funktion lautet folgendermaßen:
1234567891011121314151617181920212223242526272829void setup() {//Iniciamos la comunicación serie. El Bluetooth de la placa ZUM BT 328// trabaja a 19200, por lo que si modificamos este valor no funcionara.Serial.begin(19200);Serial.flush(); // Vaciamos el puerto serieservoL.attach(6); // Decimos a Arduino que el servo Izquierda (Left) en el pin número 6servoR.attach(9); // Decimos a Arduino que el servo Derecha (Right) en el pin número 9servoBat.attach(11); // Decimos a Arduino que el servo que controla el sensor Bat (Ultrasonido) en el pin número 11servoR.write(90); // Escribimos el valor 90 grados en el servo derecha (90 equivale a parado)servoL.write(90); // Escribimos el valor 90 grados en el servo izquierda (90 equivale a parado)servoBat.write(90); // Colocamos el servo del sensor Bat en el centro// Decimos a Arduino que pines se utilizarán como entrada (INPUT) y como salida (OUTPUT)// Pines del sensor BatpinMode(EP, INPUT);pinMode(TP, OUTPUT);//pines ldrpinMode(ldrDrch, INPUT);pinMode(ldrIzq, INPUT);//pines infrarrojospinMode(irDrch, INPUT);pinMode(irIzq, INPUT);}
Innerhalb dieser Funktion initialisieren wir die serielle Schnittstelle mit einer Geschwindigkeit von 19200 Bauds/s. Dies ist die Geschwindigkeit, mit der das Bluetooth Modul der Platine ZUM BT-328 funktioniert. Dann müssen wir angeben, wo jeder einzelne Servo angeschlossen wurde, die sich bewegenden Servos anhalten und den Servo mit dem Bat Ultraschallsensor zentrieren. Als Letztes legen wir mit der Anweisung pinMode(pin,mode) die verschiedenen Eingangspins (INPUT) oder Ausgangspins (OUTPUT) je nach Bedarf fest. - Loop: In der Funktion Loop, der zweiten obligatorischen Funktion bei Arduino, geht es um den Datenerhalt:
1234567void loop() {if (Serial.available() > 0) { // Si hay datos en el puerto serie (0 indica que hay)readFromAndroid(); // Llamamos a la función readFromAndroid para leer los datos recibidos por Bluetooth.}writeData(); // Una vez que hemos leído los datos del Bluetooth llamamos a la función// writeData que "decidirá" en función de los datos recibidos que modo hay que activar.}
Wir sehen in ihr die Anweisung if (Serial.available() >0). Dies bedeutet in “menschliche” Sprache übersetzt so viel wie: Sind an der seriellen Schnittstelle Daten vorhanden, führe die folgenden Anweisungen aus. In diesem Fall lautet die Anweisung, die Funktion readFromAndroid() aufzurufen. Diese Funktion liest die Daten, die von der Protocoder-Anwendung gesendet wurden. Die andere in Loop erscheinende Anweisung lautet writeData();. Diese Funktion sorgt für den Übergang in die verschiedenen Steuermodi (sowohl manuell und extern), sobald die Daten aus der vorigen Funktion gelesen wurden. - readFromAndroid();:
12345678910111213void readFromAndroid() {char inChar; // Creamos una variable que sea un carácterwhile (Serial.available() > 0) {//Realiza las ordenes entre corchetes hasta que no haya datos en el puerto serie (bluetooth)// con la orden WhileinChar = (char) Serial.read(); // Almacena en la variable inChar los datos leídos desde el puerto serieSerial.flush(); // vacía el puerto serieif (inChar == '=') // = es el carácter de fin de envíoinString = ""; // Si se ha llegado al carácter de fin de envío vacía los datos almacenados en la variable inStringelse if (inChar != '+') // Si el carácter no es ni = ni + entonces, concatena los caracteres leídos en la variable inStringinString += inChar;}}
Statt dieser Funktion hätten wir auch die Funktion readString(); verwenden können, aber sie wird langsamer ausgelesen. Dies würde dazu führen, dass die Anweisungen, die wir vom Protocoder Programm auf unserem Gerät aus senden, verspätet zu unserem PrintBot Evolution gelangen. - writeData();: Diese Funktion kontrolliert den Programmfluss, liest die aus der Funktion readFromAndroid() erhaltenen Daten und führt entsprechend dieser Daten die eine oder die andere Anweisung aus.
- Bewegungs-Funktionen: Diese Funktionen dienen dazu, den PrintBot Evolution zu bewegen. Es sind konkret: avanzar (vorwärts bewegen), retroceder (rückwärts bewegen), parar (anhalten), izquierda (links), derecha (rechts), giro_180_grados (180_Grad_Drehung), izquierdaLineas (linksLinien) und derechaLineas (rechtsLinien). Durch Aufrufen können wir diese Funktionen jederzeit verwenden. Für eine 180-Grad-Drehung verwenden wir beispielsweise die folgende Anweisung:
1giro_180_grados(); - Modi-Funktionen: Diese Funktionen werden dazu verwendet, den PrintBot in den automatischen Modus zu versetzen.
- Linienfolger: Dieser Automodus verwendet die Infrarotsensoren, um festzustellen, ob sich der Sensor auf einer Linie befindet. In diesem Modus werden die besonderen Bewegungs-Funktionen verwendet, linksLinien und rechtsLinien, die nur ein Rad statt zwei Rädern in entgegengesetzter Richtung bewegen, um eine Bewegung durchzuführen.
- Objekten ausweichen: In diesem Automodus wird der Ultraschallsensor zum Erfassen von Objekten verwendet, um ein Zusammenstoßen mit diesen zu umgehen. Der Code dieser Funktion lautet:
123456789101112131415161718192021222324252627282930313233343536373839void obstaculos() {// Los siguientes valores se utilizan para determinar las posiciones a las que// se pondrá el sensor bat (a través del servoBat).int izq = 140, drch = 50, cent = 90;// Comprueba si hay obstáculos en las tres posiciones. Si hay obstáculo se para.boolean obsIzq = buscaObstaculo(izq); //Llamamos a la función, que devuelve verdadero (true) si hay obstáculoif (obsIzq) {parar();}boolean obsCent = buscaObstaculo(cent);if (obsCent) {parar();}boolean obsDrch = buscaObstaculo(drch);if (obsDrch) {parar();}// Realiza una serie de comprobaciones y se desplaza hacia donde no hay obstáculos.if (!obsCent) { // No hay obstáculo en el centroavanzar();} else if (!obsDrch) { // No hay obstáculo en la derecha y si en el centroderecha();delay(giro_45);parar();} else if (!obsIzq) { // No hay obstáculo en la izquierda y si en el centro y derechaizquierda();delay(giro_45);parar();} else { // Hay obstáculo en el centro, derecha e izquierda. Por lo que se gira en 180ºretroceder(); // Hacemos que el printbot retroceda durante un cierto tiempodelay(giro_30); // Utilizamos el mismo tiempo que se utilizaría para girar 30ºgiro_180_grados(); // Por último, giramos el printbot 180 grados.}}
In dieser Funktion finden wir zunächst die Variablen izq,drcg und cent, welche die Position des Mini-Servos festlegen, der den Ultraschallsensor bewegt. Zu diesen Positionen zählen entsprechend links, rechts und zentriert, aber die Positionen können an andere angepasst oder auch neu erstellt werden. Anschließend müssen wir mit Hilfe der Funktion buscaObstaculos() prüfen, dass sich keine Objekte in der Richtung befinden, in die der Servo steuert. Diese Funktion verwenden wir für alle Positionen und sie übermittelt den Winkel, den der Servo anstreben soll. Der Code dieser Funktion lautet:
1234567891011121314151617181920212223boolean buscaObstaculo(int angulo) {// Se utiliza esta función para buscar obstáculos.// Para utilizarla hay que pasar como datosint distancia_max = 25; // Distancia máxima para determinar si la señal se convierte en un obstáculo o noint distancia; // Almacenaremos la distancia leída aquíservoBat.write(angulo); //Desplazamos el servo a la posición de lectura que hemos pasado como argumentodelay(200); // Paramos la ejecución para dar tiempo al servo a llegar.distancia = Distance(); // Guardamos la distancia que devuelve la función Distance() en la variable distanciaif (distancia != 0 && distancia < distancia_max) {// Si la distancia no es 0 y la distancia es menor de la distancia máxima// hacemos que suene un tono en el zumbador y devuelve true (verdadero)// indicando que hay un obstáculotone(pinBuz, 261, 100);return true;} else {// Si la distancia es 0 o la distancia es mayor de la distancia máxima// la función devuelve false (falso) indicando que no hay obstáculo.return false;}}
In dieser Funktion könnt ihr die maximale Entfernung verändern, die wir verwenden, um Objekte zu erkennen. Mit anderen Worten, du kannst die Distanz verändern, ab der wir ein Objekt auf unserem Weg als Hindernis ansehen oder nicht. Um die Entfernung zu kalkulieren werden die Hilfsfunktionen Distance und TP_init verwendet. Sie sorgen für die Aktivierung des Bat Sensors, senden ein Ultraschallsignal, kalkulieren die Zeit, die für das Auslesen des Signals benötigt wird sobald es zurückgesendet wird und berechnen entsprechend die bestehende Entfernung. Sobald wir wissen, ob wir in einer der drei Positionen ein Hindernis vor uns haben, bewegen wir den PrintBot in den freien Bereich. - Lichtfolger: Dieser Automodus wird dafür verwendet, den Printbot in den Bereich zu bewegen, in dem die Lichtintensität am größten ist. Dafür lesen wir den Wert jedes einzelnen Pins aus, an dem die LDR Module (LDR=lichtabhängiger Widerstand) verbunden sind. Wir vergleichen die Werte, drehen in Richtung der größten Lichtintensität und bewegen den PrintBot 1 Sekunde lang geradeaus.
In diesem Video kannst du dir die Umsetzung dieses Beitrags ansehen:
- Verändere die Symbole der Protocoder Anwendung, um anstelle des Textes Bilder in die Schaltflächen zu integrieren.
- Verwende die Infrarotsensoren, um Abgründe zu erkennen. Ein Beispiel kannst du dir hier ansehen.
- Du kannst eine Schaltfläche erstellen, damit dein PrintBot den Kopf bewegt und einen Ton abgibt.
Wenn du mehr über Arduino, Protocoder und Robotik lernen möchtest, kannst du dem Programmierkursus für Maker in Arduino und Protocoder folgen.