Κατασκευή Τηλεκατευθυνόμενου Ρομπότ Arduino Leonardo με σύνδεση κινητού τηλεφώνου μέσω Bluetooth

Η βασική ιδέα του project που έπρεπε να υλοποιήσει η ομάδα ρομποτικής του 3ου Γυμνασίου Αμαλιάδας ήταν η κατασκευή ενός τηλεκατευθυνόμενου ρομπότ που θα μπορούσε να παίξει ποδόσφαιρο σε ένα γήπεδο- πίστα που θα κατασκευάζαμε και το οποίο θα είχε διαστάσεις παρόμοιες με αυτές που έχουν οι πίστες στους διαγωνισμούς της WRO.

Αφού πρώτα φανταστήκαμε και στην συνέχεια κατασκευάσαμε το ρομποτάκι μας, σκεφτήκαμε να το υλοποιήσουμε με ενσύρματη σύνδεση και το καταφέραμε χωρίς μεγάλες δυσκολίες. Το συγκεκριμένο Arduino φορώντας του το firmware που παρέχει το Scratch for Arduino (s4a.cat) και το οποίο μπορείτε να κατεβάσετε από εδώ, έχει την δυνατότητα να προγραμματιστεί με Scratch. Για να γίνει αυτό βέβαια πρέπει να κατεβάσετε και να εγκαταστήσετε μια ειδική έκδοση του Scratch που θα βρείτε στο ίδιο site (s4a.cat).

Για του λόγου το αληθές έχουμε και το σχετικό video:

Με events (γεγονότα) κουμπιών και τις εντολές κίνησης των κινητήρων που μας παρέχει το S4A καταφέραμε να μπορούμε να χειριζόμαστε το ρομπότ μας από το πληκτρολόγιο του υπολογιστή μας.

Επειδή όμως το καλύτερο είναι ο εχθρός του καλού αποφασίσαμε ότι θα προσπαθήσουμε να συνδέσουμε ασύρματα το ρομπότ μας με υπολογιστή αρχικά (αν τα καταφέρναμε) και στην συνέχεια να ερευνήσουμε αν αυτό μπορούσε να γίνει με κινητό τηλέφωνο.

Για αυτό τον λόγο αγοράσαμε το HC-05 Bluetooth Module for Arduino και το συνδέσαμε με το Arduino Leonardo που περιέχει το πακέτο μας.

Η βασική δυσκολία στην κατασκευή του συγκεκριμένου project ήταν η σύνδεση της πλακέτας Arduino Leonardo που περιέχει το GIGO S4A μέσω Bluetooth με οποιαδήποτε συσκευή. Η σύνδεση μέσω Bluetooth με τον υπολογιστή ή οποιαδήποτε άλλη συσκευή πραγματοποιόταν με επιτυχία, αλλά η εφαρμογή του s4a scratch δεν μπορούσε να εντοπίσει την πλακέτα. Δεν καταφέραμε να επιλύσουμε αυτό το πρόβλημα. Εξάλλου όταν άνοιγε το s4a η σύνδεση μέσω Bluetooth δεν ήταν σταθερή (υπήρχαν συνεχείς αποσυνδέσεις). Επομένως εγκαταλείφθηκε η ιδέα να συνδέσουμε τον υπολογιστή με ασύρματη σύνδεση με την πλακέτα και με αυτό τον τρόπο να χειριζόμαστε το ρομπότ, έτσι όπως ακριβώς κάναμε με το καλώδιο.

Τότε στο τραπέζι έπεσε η ιδέα να φτιάξουμε μια εφαρμογή σε app inventor, με την οποία θα χειριζόμαστε το ρομπότ. Διαπιστώσαμε ότι το HC-05 Module συνδεόταν με οποιοδήποτε android κινητό, οπότε γιατί όχι να φτιάχναμε μια παρόμοια εφαρμογή με αυτή που είχαμε ήδη φτιάξει για το ρομπότ της LEGO. Η πρόκληση αφορούσε το γεγονός ότι το app inventor διαθέτει έτοιμα blocks για τον χειρισμό των κινητήρων και των αισθητήρων του LEGO EV3, αλλά στην δική μας περίπτωση θα έπρεπε να βρούμε άλλους τρόπους επικοινωνίας της εφαρμογής με το ρομπότ μας.

Για αυτό τον λόγο θα έπρεπε να προγραμματίσουμε από την αρχή το Arduino. Πρώτη κίνηση που έπρεπε να κάνουμε ήταν να εδραιώσουμε μια Bluetooth σύνδεση, την οποία θα χρησιμοποιούσαμε για να επικοινωνεί το κινητό τηλέφωνο με το ρομπότ μας. Γι’ αυτό τον λόγο χρησιμοποιήσαμε την βιβλιοθήκη SoftwareSerial.h, μέσω της οποίας μπορούσαμε να υποκαταστήσουμε τα προεπιλεγμένα pins σειριακής επικοινωνίας του arduino  με άλλα (η λειτουργία των οποίων προσομοιώθηκε με software) έτσι ώστε να μπορέσουμε να εκμεταλλευτούμε το Bluetooth Module.

Ο κώδικάς μας έμοιαζε κάπως έτσι:

#include <SoftwareSerial.h&gt; //Software Serial Port
#define RxD 8
#define TxD 7
SoftwareSerial blueToothSerial(RxD,TxD);

void setup() {
pinMode(RxD, INPUT);
pinMode(TxD, OUTPUT);
setupBlueToothConnection();
Serial1.begin(9600);
}
void loop() {

}

void setupBlueToothConnection()
{
blueToothSerial.begin(9600); //Set BluetoothBee BaudRate to (9600) the default baud rate is 38400
blueToothSerial.print("\r\n+STWMOD=0\r\n"); //set the bluetooth work in slave mode
blueToothSerial.print("\r\n+STNA=SeedBT\r\n"); //set the bluetooth name as "SeedBT"
blueToothSerial.print("\r\n+STOAUT=1\r\n"); // Permit Paired device to connect me
blueToothSerial.print("\r\n+STAUTO=0\r\n"); // Auto-connection should be forbidden here
delay(2000); // This delay is required.
blueToothSerial.print("\r\n+INQ=1\r\n"); //make the slave bluetooth inquirable
delay(2000); // This delay is required.
blueToothSerial.flush();
}

Στην συνέχεια και αφού είχαμε δοκιμάσει την Bluetooth σύνδεση μιμηθήκαμε τον τρόπο λειτουργίας του firmware του Scratch For Arduino και χρησιμοποιήσαμε κάποιες από τις συναρτήσεις που χρησιμοποιούσε προκειμένου να ελέγξει τους κινητήρες και τους αισθητήρες. Έτσι προσθέσαμε τις συναρτήσεις αρχικοποίησης των pins configurePins() και resetPins(), καθώς και τις συναρτήσεις ελέγχου των κινητήρων sendUpdateServomotors(), servo() και pulse().

Ο κώδικάς μας, σε αυτό το σημείο, έμοιαζε κάπως έτσι:

#include <SoftwareSerial.h&gt; //Software Serial Port
#define RxD 8
#define TxD 7
SoftwareSerial blueToothSerial(RxD,TxD);
String voice;
typedef enum {
input, servomotor, pwm, digital }
pinType;
typedef struct pin {
pinType type; //Type of pin
int state; //State of an output
//byte value; //Value of an input. Not used by now. TODO
};
pin arduinoPins[14]; //Array of struct holding 0-13 pins information
unsigned long lastDataReceivedTime = millis();
void setup() {
pinMode(RxD, INPUT);
pinMode(TxD, OUTPUT);
setupBlueToothConnection();
Serial1.begin(9600);
configurePins();
resetPins();
}
void loop() {

while (Serial1.available())
{
delay(10);
char c = Serial1.read();
voice += c;
}

sendUpdateServomotors();
}

void configurePins()
{
arduinoPins[0].type=input;
arduinoPins[1].type=input;
arduinoPins[2].type=input;
arduinoPins[3].type=input;
arduinoPins[4].type=servomotor;
arduinoPins[5].type=pwm;
arduinoPins[6].type=pwm;
arduinoPins[7].type=servomotor;
arduinoPins[8].type=servomotor;
arduinoPins[9].type=pwm;
arduinoPins[10].type=digital;
arduinoPins[11].type=digital;
arduinoPins[12].type=digital;
arduinoPins[13].type=digital;
}
void resetPins() {
for (byte index=0; index <=13; index++)
{
if (arduinoPins[index].type!=input)
{
pinMode(index, OUTPUT);
if (arduinoPins[index].type==servomotor)
{
arduinoPins[index].state = 255;
servo (index, 255);
}
else
{
arduinoPins[index].state=0;
digitalWrite(index,LOW);
}
}
}
}

void sendUpdateServomotors()
{
for (byte p = 0; p < 10; p++)
if (arduinoPins[p].type == servomotor) servo(p, arduinoPins[p].state);
}
void servo (byte pinNumber, byte angle)
{
if (angle != 255)
pulse(pinNumber, (angle * 10) + 100);
}

void pulse (byte pinNumber, unsigned int pulseWidth)
{
digitalWrite(pinNumber, HIGH);
delayMicroseconds(pulseWidth);
digitalWrite(pinNumber, LOW);
}

void setupBlueToothConnection()
{
blueToothSerial.begin(9600); //Set BluetoothBee BaudRate to (9600) the default baud rate is 38400
blueToothSerial.print("\r\n+STWMOD=0\r\n"); //set the bluetooth work in slave mode
blueToothSerial.print("\r\n+STNA=SeedBT\r\n"); //set the bluetooth name as "SeedBT"
blueToothSerial.print("\r\n+STOAUT=1\r\n"); // Permit Paired device to connect me
blueToothSerial.print("\r\n+STAUTO=0\r\n"); // Auto-connection should be forbidden here
delay(2000); // This delay is required.
blueToothSerial.print("\r\n+INQ=1\r\n"); //make the slave bluetooth inquirable
delay(2000); // This delay is required.
blueToothSerial.flush();
}

Το μόνο που έμενε από εδώ και πέρα ήταν να φτιάξουμε την γλώσσα επικοινωνίας μεταξύ της εφαρμογής και του ρομπότ. Η σύλληψη είναι πολύ απλή, καθώς η εφαρμογή μέσω της σύνδεσης Bluetooth στέλνει συγκεκριμένα μηνύματα προς το ρομπότ και αυτό θα πρέπει να εκτελεί διαφορετικές λειτουργίες ανάλογα με την είσοδο που έλαβε.

Έτσι εκπαιδεύσαμε το ρομπότ μας όταν λαμβάνει το μήνυμα F1 τότε οι δύο κινητήρες να κινούνται προς τα εμπρός με ίδια δύναμη, όταν λαμβάνει το μήνυμα Β1  οι δύο κινητήρες να κινούνται προς τα πίσω με ίδια δύναμη, ενώ όταν δέχεται το μήνυμα L1 και R1 οι δύο κινητήρες να κινούνται προς αντίθετες φορές, έτσι ώστε το ρομπότ να στρίβει αριστερά και δεξιά αντίστοιχα. Επιπλέον όταν δέχεται το μήνυμα Κ1 να απλώνει  τον μπροστινό βραχίονα και όταν δέχεται το μήνυμα Κ2 να μαζεύει τον μπροστινό βραχίονα. Τέλος όταν το ρομπότ δέχεται το μήνυμα 0 τότε θα πρέπει να σταματάει. Η λογική με την οποία λειτουργεί ο προγραμματισμός του Arduino είναι ότι φορτώνουμε σε κάθε Σύνδεση (pin) την τιμή που θέλουμε και μέσω μια συνάρτησης που εκτελείται συνεχώς ελέγχει τις τιμές και ανάλογα κινεί τους κινητήρες. Για παράδειγμα αν φορτώσουμε στο pin4 την τιμή 100 τότε ο κινητήρας που είναι συνδεδεμένος στο pin4 θα περιστραφεί  κατά 100 μοίρες στην μονάδα του χρόνου ενώ αν του θέσουμε τιμή -100 τότς θα περιστραφεί αντίστροφα 100 μοιρες. Τέλος αν θέσουμε την τιμή 255 τότε οι κινητήρες σταματούν.

if (voice.length() &gt; 0)
{
Serial1.println(voice);
if(voice == "F1") //μπροστά
{

arduinoPins[4].state=100;
arduinoPins[7].state=100;
}
if(voice == "0") //σταμάτημα
{
arduinoPins[4].state=255;
arduinoPins[7].state=255;
}
if(voice == "L1") //αριστερά
{
arduinoPins[4].state=-100;
arduinoPins[7].state=100;

}
if(voice == "R1") //δεξιά
{
arduinoPins[4].state=100;
arduinoPins[7].state=-100;

}
if(voice == "B1") //πίσω
{
arduinoPins[4].state=-100;
arduinoPins[7].state=-100;

}
if(voice == "K1") //κλωτσιά άνοιγμα
{
arduinoPins[8].state=-100;
}
if(voice == "K2") //κλωτσιά μάζεμα
{
arduinoPins[8].state=100;
}
if(voice == "K0") //κλωτσιά σταμάτημα
{
arduinoPins[8].state=255;
}
voice="";
}

Ο τελικός κώδικας που έχουμε φορτώσει είναι στο Arduino είναι:

#include <SoftwareSerial.h&gt; //Software Serial Port
#define RxD 8
#define TxD 7
SoftwareSerial blueToothSerial(RxD,TxD);
String voice;
typedef enum {
input, servomotor, pwm, digital }
pinType;
typedef struct pin {
pinType type; //Type of pin
int state; //State of an output
//byte value; //Value of an input. Not used by now. TODO
};
pin arduinoPins[14]; //Array of struct holding 0-13 pins information
unsigned long lastDataReceivedTime = millis();
void setup() {
pinMode(RxD, INPUT);
pinMode(TxD, OUTPUT);
setupBlueToothConnection();
Serial1.begin(9600);
configurePins();
resetPins();
}
void loop() {

while (Serial1.available())
{
delay(10);
char c = Serial1.read();
voice += c;
}
if (voice.length() &gt; 0)
{
Serial1.println(voice);
if(voice == "F1")
{

arduinoPins[4].state=100;
arduinoPins[7].state=100;
}
if(voice == "0")
{
arduinoPins[4].state=255;
arduinoPins[7].state=255;
}
if(voice == "L1")
{
arduinoPins[4].state=-100;
arduinoPins[7].state=100;

}
if(voice == "R1")
{
arduinoPins[4].state=100;
arduinoPins[7].state=-100;

}
if(voice == "B1")
{
arduinoPins[4].state=-100;
arduinoPins[7].state=-100;

}
if(voice == "K1")
{
arduinoPins[8].state=-100;
}
if(voice == "K2")
{
arduinoPins[8].state=100;
}
if(voice == "K0")
{
arduinoPins[8].state=255;
}
voice="";
}
sendUpdateServomotors();
}

void configurePins()
{
arduinoPins[0].type=input;
arduinoPins[1].type=input;
arduinoPins[2].type=input;
arduinoPins[3].type=input;
arduinoPins[4].type=servomotor;
arduinoPins[5].type=pwm;
arduinoPins[6].type=pwm;
arduinoPins[7].type=servomotor;
arduinoPins[8].type=servomotor;
arduinoPins[9].type=pwm;
arduinoPins[10].type=digital;
arduinoPins[11].type=digital;
arduinoPins[12].type=digital;
arduinoPins[13].type=digital;
}
void resetPins() {
for (byte index=0; index <=13; index++)
{
if (arduinoPins[index].type!=input)
{
pinMode(index, OUTPUT);
if (arduinoPins[index].type==servomotor)
{
arduinoPins[index].state = 255;
servo (index, 255);
}
else
{
arduinoPins[index].state=0;
digitalWrite(index,LOW);
}
}
}
}

void sendUpdateServomotors()
{
for (byte p = 0; p < 10; p++)
if (arduinoPins[p].type == servomotor) servo(p, arduinoPins[p].state);
}
void servo (byte pinNumber, byte angle)
{
if (angle != 255)
pulse(pinNumber, (angle * 10) + 100);
}

void pulse (byte pinNumber, unsigned int pulseWidth)
{
digitalWrite(pinNumber, HIGH);
delayMicroseconds(pulseWidth);
digitalWrite(pinNumber, LOW);
}

void setupBlueToothConnection()
{
blueToothSerial.begin(9600); //Set BluetoothBee BaudRate to (9600) the default baud rate is 38400
blueToothSerial.print("\r\n+STWMOD=0\r\n"); //set the bluetooth work in slave mode
blueToothSerial.print("\r\n+STNA=SeedBT\r\n"); //set the bluetooth name as "SeedBT"
blueToothSerial.print("\r\n+STOAUT=1\r\n"); // Permit Paired device to connect me
blueToothSerial.print("\r\n+STAUTO=0\r\n"); // Auto-connection should be forbidden here
delay(2000); // This delay is required.
blueToothSerial.print("\r\n+INQ=1\r\n"); //make the slave bluetooth inquirable
delay(2000); // This delay is required.
blueToothSerial.flush();
}

Και ερχόμαστε τώρα στην εφαρμογή για κινητά (εδώ τα πράγματα είναι πιο απλά), όπου χρησιμοποιήσαμε την εκπαιδευτική πλατφόρμα του App Inventor.

Αρχικά δημιουργήσαμε το γραφικό περιβάλλον της εφαρμογής, όπου προσθέσαμε τα κουμπιά των τεσσάρων κατευθύνσεων (εμπρός, πίσω, αριστερά, δεξιά), ένα κουμπί για να κλοτσάει, μία λίστα επιλογής συνδεδεμένων Bluetooth συσκευών και τέλος ένα κουμπί αποσύνδεσης. Επίσης μας χρειάστηκε ένας Bluetoothclient και ένα clock (χρονομετρητής).

Αφού διαμορφώσαμε κατάλληλα την εμφάνιση των αντικειμένων που θα χρησιμοποιούσαμε στην συνέχεια ασχοληθήκαμε με τον προγραμματισμό τους. Έτσι μέσω του BlutoothClient ανιχνεύουμε αρχικά όλες τις συσκευές Bluetooth που βρίσκονται κοντά μας και επιλέγοντας κάποια από τις συσκευές εδραιώνεται σύνδεση μεταξύ τους. Στην συνέχεια ορίσαμε για κάθε κουμπί το μήνυμα (σε μορφή κειμένου) που θα στέλνει στο ρομπότ μας έτσι ώστε, σύμφωνα με όσα αναφέρονται παραπάνω, το ρομπότ να αντιδρά κατάλληλα.

Το μόνο αξιοσημείωτο σημείο εδώ είναι ο τρόπος με τον οποίο γίνεται η κλοτσιά, αφού το πάτημα του αντίστοιχου κουμπιού εκτός από το μήνυμα απλώματος του βραχίονα, ενεργοποιεί ένας χρονομετρητής, ο οποίος μετά από ένα δευτερόλεπτο στέλνει μήνυμα επαναφοράς του βραχίονα και τέλος στέλνει και μήνυμα σταματήματος του.

Παρακάτω φαίνεται το πρόγραμμα που χρησιμοποιούμε για την εφαρμογή.

Στο παρακάτω βίντεο φαίνεται επιτυχημένη προσπάθεια εκτέλεσης πέναλτυ!

Αφήστε μια απάντηση

Η ηλ. διεύθυνση σας δεν δημοσιεύεται. Τα υποχρεωτικά πεδία σημειώνονται με *