Benvenuto! In questa introduzione imparerai a programmare e controllare un PrintBot con un terminale e utilizzando Protocoder. Protocoder è uno strumento di sviluppo per applicazioni su smartphone, che ti permette di creare velocemente programmi e interfacce utente. Protocoder è stato sviluppato da Victor Díaz. Puoi trovare ulteriori informazioni e scaricare Protocoder qui. Useremo Protocoder come un interfaccia per comunicare con la scheda ZUMT BT-328 via Bluetooth, e faremo muovere a piacimento il PrintBot Evolution o lo useremo in modalità automatica: inseguitore di linea, inseguitore di luce e evita ostacoli. Useremo due programmi per controllare il PrintBot Evolution:
- Codice Protocoder: memorizza l’interfaccia grafica e le funzioni da inviare alla scheda ZUM attraverso il Bluetooth del terminale.
- Codice ZUM BT-328: memorizzerà il programma del PrintBot, come le modalità operative o la comunicazione Bluetooth.
Puoi scaricare i codici alla fine di questo post. Prima di utilizzare il codice per Protocoder dovrai installarlo sul tuo terminale (puoi scaricarlo da qui).
Codice Protocoder
Per modificare il codice Protocoder, per prima cosa accedi alla applicazione dal tuo terminale e inserisci l’indirizzo IP che appare nella parte bassa della videata del tuo browser nel tuo computer.
Scarica il codice che trovi alla fine di questo post e carica il file printbot_evolution_protocoder.proto sul tuo terminale. Entra nella cartella in cui si trova il file (solitamente la cartella downloads), clicca sul codice Protocoder e apparirà una finestra di dialogo. Seleziona “apri con Protocoder”. Seleziona le opzioni di installazione nella finestra che si apre. Ora puoi usare e modificare il codice contenuto nel programma.
Una volta che hai importato il file con suffisso .proto, vedrai apparire il progetto nella linguetta MIEI PROGETTI del browser. Clicca sull’icona e vedrai il codice di programma. Varie parti possono essere distinte nel codice:
- Creare i pulsanti: crea un pulsante con del testo, utile per indicare a cosa serve. Per crearli utilizzeremo un codice come questo:
123var btnSiguelineas = ui.addButton("Sigue lineas",240, 350, w, h).onClick(function(){ // We create the button. Parameters: ("texto a mostrar",posición X, posicion Y, ancho, alto)send("siguelineas"); // We pass the world siguelíneas (line follower) to the send function as a parameter, which is fully received on Arduino.});
All’interno del codice, dichiariamo il pulsante con var btnSiguelinas, e ad ogni pulsante dobbiamo dare un nome differente per evitare conflitti di interpretazione. Poi con ui.addButton, affermiamo che la variabile btnSiguelineas sarà un pulsante. In ordine tra parentesi, scriveremo il testo che apparirà nel pulsante, la posizione X e Y, la larghezza ed altezza del pulsante. Dopodichè dovremo indicare la funzione eseguita quando si preme il pulsante, in questo caso, inviare la parola siguelinas (insegui linea) via Bluetooth. - Creare pulsanti con immagini
123var btn180 = ui.addImageButton(x+2*w, y+3*w, w, w, "180.png",false).onClick(function(){ // We create a button with an image. Parameters: (positionX, positionY, width, height,"image file path",true/false)send("180");});
Dichiariamo il pulsante con var btn180, ma questa volta è differente in quanto stiamo usando un’immagine al posto di un testo. Poi con ui.addImageButton, possiamo impostare la variabile btn180 come una immagine con proprietà di pulsante. All’interno delle parentesi e in ordine, scriveremo la posizione X e Y, la larghezza e l’altezza del pulsante ed il nome dell’immagine che vogliamo appaia nel pulsante. Dobbiamo anche indicare la funzione che verrà eseguita quando il pulsante viene premuto, che in questo caso è inviare la parola 180 con il Bluetooth. Per aggiungere un immagine o qualsiasi file, trascinalo semplicemente dal tuo archivio sul computer nel contenitore in basso a destraT. Tieni a mente che più semplice è il nome, e più facile sarà il codice di programma.
- Creare i pulsanti per il pannello utente (dashboard): Per includerli, possiamo farlo nello stesso modo che per i pulsanti per i dispositivi, ma invece di usare il prefisso ui, useremo il prefisso dashboard. In automatico non appare la dashboard, dovrai cambiare dashboard.show(false); con dashboard.show(true);.
- Funzioni che collegano o scollegano il Bluetooth: Questi sono inclusi all’interno delle variabili btnConectar e btnDesconectar. Creiamo anche la variabile btClient, che verrà utilizzata per “interagire” con il Bluetooth.
- Collegamento: Per collegarsi, per prima cosa devi creare un pulsante con il testo “Connect Bluetooth” e all’interno della funzione che viene attivata quando premi il pulsante, scriveremo il codice che connette il Bluetooth.
12345678910111213var btnConectar = ui.addButton("Conectar bluetooth", 240, 150,300,100).onClick(function() { // We create the connect Bluetooth buttonbtClient = network.bluetooth.connectSerial(function(status) { // We connect btClient -- It will display a list of any linked devicesconsole.log("connected " + status); // Debugif (status === true){ // If it is connectedconsole.log(status); // We save the statusbluetoothOn = 1; // We change the status variable to connected (1)ui.jump(btnConectar); // We make the jump buttonui.alpha(btnConectar, 0); // We make the button totally transparentmedia.playSound("Drago"); // We play the stored sounddevice.vibrate(750); // We make the device vibrate}});});
All’interno della funzione, come quando viene connesso il Bluetooth, dichiariamo la variabile bluetoothOn – che ha la funzione di “segnalare” la connessione/disconnessione – e a cui assegnamo il valore 1 e la trasparenza del pulsante viene portata al valore 0, che lo fa diventare interamente trasparente. Aggiungiamo anche qualche animazione come saltare e faremo vibrare il dispositivo quando viene connesso. - Disconnessione: per disconnetere, dobbiamo creare un pulsante con la scritta “Disconnetti” ed all’interno della funzione attivata quando viene premuto il pulsante, inseriremo la disconnessione del bluetooth.
123456var btnDesconectar = ui.addButton("Desconectar", 540, 150,200,100).onClick(function() { // We create the connect buttonbtClient.disconnect(); // We disconnect if pressedbluetoothOn=0; // We change the status variable to disconnected (0)ui.alpha(btnConectar, 255); // We change the transparency of the button to opaqueui.jump(btnDesconectar); // We make the disconnect button jump});
All’interno di questa funzione, oltre a disconnettere il Bluetooth, dichiareremo una variabile BluetoothOn – la quale “segnala” la connessione/disconnessione del bluetooth – viene assegnato il valore 0 e la trasparenza del pulsante viene portata a 255, che diventa opaco.
- Collegamento: Per collegarsi, per prima cosa devi creare un pulsante con il testo “Connect Bluetooth” e all’interno della funzione che viene attivata quando premi il pulsante, scriveremo il codice che connette il Bluetooth.
- Funzione per inviare e ricevere dati dalla funzione Bluetooth: La funzione per la trasmissione dei dati con Bluetooth è :
123456function send(str) { // The function we use to send a stringif (bluetoothOn == 1) { // If the Bluetooth is connected, the code is executed for the following linesbtClient.send("="+str+"+"); // We send the string between the = and + symbolsconsole.log("="+str+"+"); // We display the string being sent on the console}}
Questa funzione non ha un pulsante, dato che verrà chiamata ogni volta che verrà premuto il pulsante, ad eccezzione di quando viene connesso/disonnesso via Bluetooth. Per inviare dati via Bluetooth, useremo il comando btClient.send(“=”+str+”+”); . Per esempio questa funzione invia il comando di avanzamento in avanti della macchina: =avanzar+. Questo è un modo di programmare, ma tu puoi trovare altri modi per ottenere lo stesso risultato. I caratteri = y + sono usati per segnalare l’inizio e la fine di una trasmissione.- Inviare dati via Bluetooth: Per inviare dati attraverso il Bluetooth , useremo la funzione con il testo racchiuso tra virgolette, per esempio, per inviare la parola retroceder (torna indietro) useremo il comando: send(“retroceder”);
Codice per Arduino
Per vedere e modificare il codice da installare sulla scheda Arduino, scarica il pacchetto che trovi alla fine di questo post, e clicca sul file printbot_evolution_arduino.ino. Se non hai installato sul tuo computer la IDE di Arduino o altra IDE per leggere questo file, vai su Programmare con Arduino e ProtoCoder per makers, e scarica la IDE che preferisci. Trovi anche molte informazioni su Arduino e ProtoCoder. Una volta che hai aperto il file, puoi distinguere varie parti del codice di programma:
- Dichiarare variabili e includere librerie: dichiareremo tutte le variabili che useremo nel programma:
1234567891011121314151617181920212223242526272829303132333435// We use the Servo.h library to control the servos#include "Servo.h"// We declare the servosServo servoR, servoL; // Servos of the left and right motorsServo servoBat; // Servo that controls the bat (ultrasound) sensor movement// We declare the variables relating the bat sensor// TP corresponds to the "trigger pin" (the pin that sends the ultrasound signal)// and EP corresponds to the "Echo pin" (the pin that receives the ultrasound signal)// By measuring the time that passes in sending and receiving the ultrasound signal// we can calculate the distanceint TP = 4, EP = 5;// We declare the pins corresponding to the LDR (light-dependent resistor) sensorsint ldrIzq = A2, ldrDrch = A3; // LDR pins// We declare the pins sensores infrared sensorsint irIzq = 2, irDrch = 3; // infrared pins// Default delay times.// A delay is a given period of time during which execution// of the program stops, until this period ends// For example, it is used here to make the PrintBot rotate for a certain duration.int defaultDelay = 200, lineasDelay = 0;// Delays to make the PrintBot rotate at different angles// You might need to adjust these times to your PrintBotint giro_180 = 1028, giro_90 = 514, giro_45 = 257, giro_30 = 200;// We declare the buzzer pinsint pinBuz = 12;// We will receive a string via Protocoder which we will use to// determine the movement or the functioning mode of the PrintBotString inString = "";
Useremo alcune variabili per dire ad Arduino a quali pins abbiamo connesso le “vitamine”, o altro come delays, per cambiare l’esecuzione del programma. Se i giri non sono corretti o vuoi cambiare gli angoli, puoi cambiare le variabili giro_180, giro_90, giro_45 and giro_30. - Setup: Nella funzione setup, una delle funzioni obbligatorie in Arduino (in aggiunta a loop), inizializzeremo il programma ed i componenti utilizzati. Il codice nella funzione setup è:
1234567891011121314151617181920212223242526272829void setup() {// We start the serial communication. The Bluetooth of the ZUM BT 328 board// runs at 19200, so if we change this value it will no longer workSerial.begin(19200);Serial.flush(); // We clear the serial portservoL.attach(6); // We tell Arduino that the Left servo is on pin 6servoR.attach(9); // We tell Arduino that the Right servo is on pin 9servoBat.attach(11); // We tell Arduino that the servo that controls the bat (ultrasound) sensor is on pin 11servoR.write(90); // We write the value 90 degrees on the right servo (90 is equal to stopped)servoL.write(90); // We write the value 90 degrees on the left servo (90 is equal to stopped)servoBat.write(90); // We place the bat sensor servo in the centre// We tell Arduino that the pins will be used as intput and output// bat sensor pinspinMode(EP, INPUT);pinMode(TP, OUTPUT);// LDR pinspinMode(ldrDrch, INPUT);pinMode(ldrIzq, INPUT);// Infrared pinspinMode(irDrch, INPUT);pinMode(irIzq, INPUT);}
In questa funzione, avviamo la porta seriale alla velocità di 19200 bauds/s, che corrisponde alla velocità del modulo Bluetooth di cui è dotata la scheda ZUM BT-328.
Poi dobbiamo indicare dove sono collegati i servo, fermare i servo in movimento e ruotare il servo che muove il sensore ad ultrasuoni bat in posizione centrale.
Per finire, useremo il comando pinMode(pin,mode) per impostare i pins come INPUT o OUTPUT a seconda dell’esigenza. - Loop: All’interno della funzione loop, un’altra funzione obbligatoria, interpreteremo ed utilizzeremo i dati ricevuti:
1234567void loop() {if (Serial.available() > 0) { // If there is data on the serial port (0 indicates that there is)readFromAndroid(); // we call the readFromAndroid function to read the data received via Bluetooth}writeData(); // Once we have read the data received via Bluetooth, we call the// writeData function which will "decide" which mode to enable according to the data receieved}
All’interno di questa funzione, possiamo vedere il comando if (Serial.available() >0) che corrisponde, in un linguaggio “umano”, a: se è arrivato un dato alla porta seriale, allora esegui i seguenti comandi. In questo caso, i comandi chiamano la funzione readFromAndroid();. Questa funzione legge i dati inviati dalla applicazione sviluppata con Protocoder. L’altro comando all’interno del loop è writeData();. Questa funzione è utilizzata per abilitare differenti modalità di controllo (sia manuale che esterno), una volta che il dato ricevuto dalla funzione precedente è stato letto. - readFromAndroid();:
12345678910111213void readFromAndroid() {char inChar; // We create a character variablewhile (Serial.available() > 0) {// Execute the commands in brackets until there is no data on the serial port (Bluetooth)// with the While commandinChar = (char) Serial.read(); // Store the data read via the serial port in the inChar variableSerial.flush(); // clears the serial portif (inChar == '=') // = the character at the end of sendinginString = ""; // If the character at the end of sending has arrived, it deletes the data stored in the inString variableelse if (inChar != '+') // If the character is neither = nor + then, concatenate the characters read on the inString variableinString += inChar;}}
Al posto di questa funzione, avremmo potuto utilizzare la funzione readString(); ma sarebbe stata letta più lentamente, causando un ritardo nella ricezione del comando inviato dal programma Protocoder al PrintBot Evolution. - writeData();: Questa funzione controlla il flusso del programma, legge i dati ricevuti dalla funzione readFromAndroid() ed esegue un comando o un altro in risposta a quel dato.
- Funzioni di movimento: Queste sono le funzioni che permettono al PrintBot Evolution di muoversi. Queste sono: avanzar (andare avanti), retroceder (indietro), parar (stop), izquierda (sinistra), derecha (destra), derecha, giro_180_grados (gira di 180°), izquierdaLineas (dritto a sinistra) and derechaLineas (dritto a destra). Possiamo utilizzare queste funzioni in qualsiasi momento, semplicemente chiamandole. Per esempio, per girare di 180 gradi useremo questo comando:
1giro_180_grados(); - Funzioni modalità: Queste sono funzioni che impostano il PrintBot Evolution con modalità automatiche.
- Inseguitore di linea: Questo automatismo usa i sensori infrarossi per individuare quando il sensore è sopra una linea. Le funzioni speciali izquierdaLineas e derechaLineas sono usate in questo modalità per muovere una sola ruota, al posto di due con direzioni diverse, per eseguire il movimento.
- Evita ostacoli: Questo automatismo usa i sensori ad ultrasuoni per individuare oggetti per evitare di andargli addosso. Il codice di questa funzione è:
123456789101112131415161718192021222324252627282930313233343536373839void obstaculos() {// The following values are used to determine the positions of// the bat sensor bat (using the servoBat)int izq = 140, drch = 50, cent = 90;// Checks if there are obstacles in the 3 positions. If so, it will stopboolean obsIzq = buscaObstaculo(izq); // We call the function which returns true if there is an obstacleif (obsIzq) {parar();}boolean obsCent = buscaObstaculo(cent);if (obsCent) {parar();}boolean obsDrch = buscaObstaculo(drch);if (obsDrch) {parar();}// It carries out a series of checks and moves in the obstacle-free directionif (!obsCent) { // If there is no obstacle in the centreavanzar();} else if (!obsDrch) { // If there is no obstacle on the right and there is in the centrederecha();delay(giro_45);parar();} else if (!obsIzq) { // If there is no obstacle on the left and there is in the centre and on the rightizquierda();delay(giro_45);parar();} else { // If there is an obstacle in the centre, on the right and on the left. In which case it will rotate 180ºretroceder(); // We make the PrintBot go backwars for a certain timedelay(giro_30); // We use the same time delay as for rotating 30ºgiro_180_grados(); // Finally we make the PrintBot turn 180 degrees}}
In questa funzione, dobbiamo individuare le variabili izq, drch and cent che definisco la posizione del mini servo che muove il sensore ad ultrasuoni. Queste posizioni sono rispettivamente sinistra, destra e centro, ma tu puoi modificarle o creare nuove posizioni. Poi dobbiamo verificare se ci sono ostacoli in direzione del sensore usando la funzione buscaObstaculos() usata per tutte le posizioni, trasmettendo l’angolo a cui è orientato il servo come dato. Il codice per questa funzione è:
1234567891011121314151617181920212223boolean buscaObstaculo(int angulo) {// This function is used to detect obstacles.// To use it, you will need to pass the following dataint distancia_max = 25; // Maximum distance to determine if the signal detects any obstaclesint distancia; // We store the distance read hereservoBat.write(angulo); // We move the servo to the reading position which we have passed as an argumentdelay(200); // We pause the execution to give the servo time to arrivedistancia = Distance(); // We save the distance returned by the Distance() function in the distance variableif (distancia != 0 && distancia < distancia_max) {// If the distance is not 0 and is less than the maximum distance// we make the buzzer sound and return true// which indicates that there is an obstacletone(pinBuz, 261, 100);return true;} else {// If the distance is 0 or is greater than the maximum distance// the functions returns false, indicating that there are no obstaclesreturn false;}}
All’interno di questa funzione, puoi cambiare la massima distanza utilizzata per individuare gli ostacoli, o in altre parole, la distanza che determina se un oggetto è un ostacolo oppure no. Per afre questo, vengono usate le funzioni ausiliarie Distance e TP_init, che attivano il sensore bat, inviano un seganle ad ultrasuoni, calcolano il tempo intercorso tra il segnale di andata e l’eco di ritorno, e in base a questo tempo calcolano la distanza. Una volta che sappiamo di fronte a quale posizione si trova l’ìostacolo, possiamo far muovere il PrintBot nella zona libera. - Inseguitore di luce: Questo automatismo muove il PrintBot direttamente verso la luce più intensa. Per fare questo, dobbiamo leggere il valore di ogni pin collegato ai moduli LDR (resistori dipendenti dalla luce), comparare i valori, girare dalla parte di maggior luminosità e far procedere il PrintBot dritto per 1 secondo.
Puoi vedere questo nel seguente video:
Ricorda che puoi cambiare ogni parametro usato nelle funzioni. Questi sono gli esercizi che raccomandiamo:
- Cambiare le icone dell’applicazione ProtoCoder includendo immagini nei pulsanti che hanno solo testo.
- Usare i sensori ad infrarossi per individuare gli angoli. Puoi vedere un esempio qui.
- You can create a button so that your PrintBot moves its head and makes a sound.
Se vuoi imparare di più su Arduino, ProtoCoder e robotica, puoi seguire questo corso: Programmare con Arduino e ProtoCoder per makers