Page 1

Προγραμματισμός Arduino

Δομή (Structure)

Ο κώδικας που φορτώνεται στη μνήμη του Arduino μετά την μετάφρασή του στον υπολογιστή μας, εκτελείται σε δύο φάσεις. Πρώτα εκτελείται η λειτουργία setup() για μία φορά και στη συνέχεια η λειτουργία loop() που επαναλαμβάνεται συνεχώς μέχρι να επανεκκινηθεί το Arduino ή να διακοπεί η λειτουργία του. Ας δούμε τις δυο φάσεις αναλυτικά.

Η λειτουργία setup()

Η setup() εκτελείται αυτόματα από τον μικροελεγκτή του Arduino μία φορά μετά από:

• τη μετάφραση του προγράμματος στον υπολογιστή μας και τη φόρτωσή του στη μνήμη του Arduino, • την επανεκκίνηση του Arduino όταν πατήσουμε το πλήκτρο reset, και

• όταν τροφοδοτούμε το Arduino με ρεύμα για πρώτη φορά.

Οσες εντολές είναι γραμμένες μέσα στη λειτουργία setup εκτελούνται σε αυτή τη φάση και συνήθως τοποθετούμε εκεί όλες τις εντολές που είναι απαραίτητες για να αρχικοποιήσουμε τον μικροελεγκτή και τις εισόδους και εξόδους των συσκευών που είναι συνδεδεμένες στη διάταξή μας, να δώσουμε αρχικές τιμές στις μεταβλητές που χρησιμοποιεί το πρόγραμμά μας, καθώς και για τη φόρτωση των βιβλιοθηκών που θα χρησιμοποιήσουμε. Παράδειγμα void setup() { pinMode(13, OUTPUT); pinMode(10, INPUT); }

Αρχικοποιεί το pin 13 σαν εξόδου στο οποίο μπορεί να στείλει τιμές LOW και HIGH η συνάρτηση digitalWrite() και το pin 10 σαν εισόδου από το οποίο μπορεί να εισάγει τιμές η συνάρτηση digitalRead().

7


Η λειτουργία loop()

Η loop(), και κατά συνέπεια οι εντολές που εμπεριέχει, εκτελείται από τον μικροελεγκτή του Arduino για πρώτη φορά μετά την εκτέλεση της setup() κάθε φορά που συμβαίνει ένα από τα γεγονότα για τα οποία εκτελείται η setup() και στη συνέχεια επαναλαμβανόμενα κάθε φορά που ολοκληρώνεται η εκτέλεσή της. Η loop() επομένως πρέπει να περιέχει εκείνες τις εντολές με τις οποίες θα κατευθύνουμε το Arduino να διαβάζει τις ψηφιακές και αναλογικές εισόδους και να ελέγχει τις εξόδους ψηφιακές και ευροπαλμικές (PWM) που είναι συνδεδεμένες στη διάταξή μας. Παράδειγμα void loop() { digitalWrite(13, HIGH); delay(500); digitalWrite(13, LOW); delay(500); }

Η λειτουργία loop() στέλνει ένα HIGH στο pin 13 και καθυστερεί για 500msec (μισό δευτερόλεπτο) και στη συνέχεια στέλνει ένα LOW και καθυστερεί άλλα 500msec. Η παραπάνω λειτουργία επαναλαμβάνεται συνεχώς και ατέρμονα όσο λειτουργεί το Arduino. Αν στο pin 13 έχουμε συνδέσει ένα LED, με την εκτέλεση του παραπάνω προγράμματος το LED θα αναβοσβήνει περιοδικά κάθε δευτερόλεπτο.

8


Δομές ελέγχου Για την αποτελεσματική λειτουργία του Arduino απαιτείται σε πολλές περιπτώσεις να χρησιμοποιήσουμε δομές ελέγχου με τις οποίες εκτελείται μια ομάδα εντολών εφόσον ισχύει μια δεδομένη συνθήκη ή επαναλαμβάνονται κάποιες εντολές όσο ισχύει ή μέχρι να ισχύσει κάποια συνθήκη. Σε όλες σχεδόν τις συνθήκες είναι απαραίτητη η χρήση ενός συγκριτικού τελεστή μεταξύ των έξι που υπάρχουν. x==y

(x ίσο με το y)

x<y

(x μικρότερο από το y)

x!=y

(x διάφορο του y)

x>y

(x μεγαλύτερο από το y)

x>=y

(x μεγαλύτερο από ή ίσο με το y)

x<=y

(x μικρότερο από ή ίσο με το y)

Προσοχή!

Στις εντολές που απαιτούν συνθήκη και χρήση του ==, αν χρησιμοποιηθεί κατά λάθος το μονό ίσο (=) η συνθήκη θα θεωρηθεί αληθής και ταυτόχρονα θα γίνει και αλλαγή της τιμής της μεταβλητής με την τιμή την οποία έχουμε δεξιά του ίσον. Επομένως η εντολή if(x=10){...} θα θέσει το x ίσο με 10 και θα εκτελέσει τις εντολές μέσα στις αγκύλες.

Πολύ συχνά, χρησιμοποιούμε πιο πολύπλοκες συνθήκες με τη χρήση των λογικών τελεστών:

(x<0 && y>=20) αληθής εφόσον ισχύουν ταυτόχρονα και τα δυο

(x<0 || y>=20)

(!logExp)

αληθής εφόσον ισχύει τουλάχιστον ένα

αληθές αν η τιμή της logExp είναι ψευδής

9


if <συνθήκη>

Η if χρησιμοποιείται μαζί με κάποια συνθήκη, το αποτέλεσμα της οποίας καθορίζει αν η εντολή ή οι εντολές που περιέχονται στην if θα εκτελεστούν ή όχι. Παράδειγμα if (SchoolStudents < 100) { // θα συμβεί αν η συνθήκη ισχύει }

Στο παραπάνω παράδειγμα οι εντολές που βρίσκονται ανάμεσα στις αγκύλες θα εκτελεστούν εφόσον η μεταβλητή SchoolStudents έχει τιμή μικρότερη από 100. Οι αγκύλες ομαδοποιούν μία ή περισσότερες εντολές που θα εκτελεστούν αν η συνθήκη είναι αληθής, ενώ αν απαιτείται μόνο μία εντολή δεν είναι απαραίτητες. Παράδειγμα if (SchoolStudents < 100) digitalWrite(LEDpin, HIGH); if (SchoolStudents < 100) digitalWrite(LEDpin, HIGH);

if (SchoolStudents < 100) { digitalWrite(LEDpin, HIGH);}

if (SchoolStudents < 100) { digitalWrite(LEDpin, HIGH); delay(1000); }

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

10


if <συνθήκη> … else

Η συγκεκριμένη μορφή είναι ισχυρότερη της προηγούμενης if, αφού έχει τη δυνατότητα να εκτελέσει μια εντολή ή μια ομάδα εντολών (με τη χρήση αγκυλών) τόσο εφόσον ισχύει η συνθήκη, όσο και όταν δεν ισχύει. Παράδειγμα if (Degree < 5) { // θα συμβεί αν η συνθήκη ισχύει } else { // θα συμβεί αν η συνθήκη δεν ισχύει }

Στο παράδειγμα αν η τιμή Degree είναι πράγματι μικρότερη του 5 θα εκτελεστούν μόνο οι εντολές που βρίσκονται ανάμεσα στο πρώτο ζευγάρι αγκυλών. Αν όμως η μεταβλητή Degree είναι μεγαλύτερη από ή ίση με 5μ θα εκτελεστούν μόνο οι εντολές που περιλαμβάνονται στο ζευγάρι αγκυλών μετά την else. Για τις αγκύλες ισχύει ότι και στην if.

Εκτός της else μπορούμε να χρησιμοποιήσουμε πολλαπλές συνθήκες στην ίδια δομή με τη χρήση του ζεύγους else if. Αυτές οι εντολές μαζί χρησιμοποιούνται για να περιγράψουν πολλές διαφορετικές τιμές ή διαστήματα τιμών για τα οποία θέλουμε να ελεγχθεί κάποια μεταβλητή ή έκφραση. Μόνο μια ομάδα εντολών θα εκτελεστεί από το σύνολο των if … else if … else if … else. Παράδειγμα if (Vl < 0) { // θα συμβεί αν η Vl είναι μικρότερη από 0 } else if (Vl==0) { // θα συμβεί αν η Vl είναι μηδέν } else { // θα συμβεί αν η Vl είναι μεγαλύτερη από 0 }

Στο παραπάνω παράδειγμα ελέγχεται η τιμή της μεταβλητής Vl και θα εκτελεστεί μόνο μία από τις τρεις ομάδες εντολών, ανάλογα με την τιμή της Vl.

11


for (αρχικοποίηση ; συνθήκη ; μεταβολή) { … }

Με τη for μπορούμε να επαναλάβουμε μια ομάδα εντολών πολλές φορές. Ο αριθμός των επαναλήψεων εξαρτάται από τη συνθήκη που περιγράφεται μέσα στα ορίσματα. Συνήθως αρχικοποιούμε μια μεταβλητή που στη συνέχεια ελέγχει την δομή της επανάληψης μέχρι να πάψει να ικανοποιείται η συνθήκη. Η μεταβλητή αυτή αυξάνεται ή μειώνεται στην μεταβολή, ενώ εκτελούνται όλες οι εντολές που είναι ανάμεσα στο ζευγάρι των αγκυλών. Παράδειγμα for(x=0;x<10;x++) { // εντολές που επαναλαμβάνεται η εκτέλεσή τους }

Στο παραπάνω παράδειγμα η μεταβλητή x αρχικοποιείται παίρνοντας την τιμή 0 μόνο την πρώτη φορά που εκτελείται και στη συνέχεια ελέγχεται η συνθήκη x<10. Οσο η συνθήκη αυτή είναι αληθής η εκτέλεση του προγράμματος συνεχίζεται στο εσωτερικό της for που καθορίζεται από τις αγκύλες. Μετά το τέλος της εκτέλεσης αυτών των εντολών εκτελείται και πάλι η for πρώτα αυξάνοντας το x κατά 1 (x++) και μετά ελέγχεται και πάλι η συνθήκη. Αν δεν ισχύει, ο έλεγχος του προγράμματος μεταφέρεται στην πρώτη εντολή μετά τις αγκύλες, ενώ αν ισχύει επαναλαμβάνεται η διαδικασία. Φυσικά, το μέγεθος της αύξησης ή μείωσης της τιμής της μεταβλητής που ελέγχει την επανάληψη μπορεί να είναι διαφορετική από το ένα. Θα πρέπει τότε να δείξουμε ακριβώς πόσο κάνοντας χρήση της x+=2 αν θέλουμε να αυξήσουμε κατά 2 ή άλλη τιμή και αντίστοιχα x-=2 αν θέλουμε να μειώσουμε κατά 2. Παράδειγμα void setup() { pinMode(13,OUTPUT); }

void loop() { for(int x=0;x<6;x+=2) { digitalWrite(13,HIGH); delay(500); digitalWrite(13,LOW); delay(500); } delay(5000); }

Σε αυτό το παράδειγμα αρχικοποιούμε το pin 13 ως εξόδου (εκεί υπάρχει ενσωματωμένο led που μπορούμε να χρησιμοποιήσουμε) και μέσα στη loop χρησιμοποιούμε τη for που επαναλαμβάνει τις εντολές των αγκυλών τρεις φορές. Η πρώτη επανάληψη εκτελείται για x=0, η δεύτερη για x=2, η τρίτη για x=4 και στη συνέχεια η τιμή του x καθώς αυξάνει κατά δυο γίνεται 6 και δεν ικανοποιείται η συνθήκη με αποτέλεσμα να εξέλθουμε της επαναληπτικής δομής. Πετυχαίνουμε έτσι να αναβοσβήνει το led τρεις φορές με εναλλαγή κάθε μισό δευτερόλεπτο και στη συνέχεια με αναμονή πέντε δευτερολέπτων αρχίζει και πάλι η διαδι-

12


κασία από την αρχή. Στην αρχικοποίηση της μεταβλητής x πρέπει να ορίσουμε τον τύπο της (εκτός αν αυτό έχει γίνει προηγουμένως) και έτσι τη δηλώνουμε ακέραια (int).

Η for χρησιμοποιείται συχνά όταν θέλουμε να χρησιμοποιήσουμε τα στοιχεία ενός πίνακα (που έχει αριθμήσιμες θέσεις), αλλά και κάθε φορά που πρέπει να επαναλάβουμε ένα σύνολο εντολών για γνωστό αριθμό επαναλήψεων.

while (συνθήκη) { … }

Η while επαναλαμβάνει ένα σύνολο εντολών που βρίσκονται μέσα σε αγκύλες όσο η συνθήκη είναι αληθής. Οι μεταβλητή ή οι μεταβλητές που χρησιμοποιούνται στη συνθήκη θα πρέπει να αρχικοποιηθούν πριν πρωτοχρησιμοποιηθούν και να αλλάξουν στο εσωτερικό της while ώστε να γίνει η συνθήκη ψευδής και να τερματιστεί η while. Αν κάτι τέτοιο δε συμβεί οι εντολές της while θα επαναλαμβάνονται ατέρμονα. Αν την πρώτη φορά που επιχειρείται να εκτελεστεί η while, η συνθήκη είναι ψευδής, δε θα εκτελεστούν καθόλου οι εντολές της επανάληψης. Η while χρησιμοποιείται συνήθως για να παίρνουμε την τιμή ενός αισθητήρα μέχρι να συμβεί κάποια αλλαγή που θα μας επιτρέψει να τερματιστεί. Παράδειγμα int vl=1; while (vl<100) { // εντολές που επαναλαμβάνεται η εκτέλεσή τους vl++; }

Στο παράδειγμα αρχικοποιούμε την ακέραια μεταβλητή vl με τιμή 1 και ξεκινά η εκτέλεση των εντολών που βρίσκονται στο εσωτερικό της while εφόσον η συνθήκη είναι αληθής. Μετά το τέλος τους, ελέγχεται και πάλι η συνθήκη και αν ισχύει ξαναεκτελούνται, ενώ αν δεν ισχύει ολοκληρώνεται η εκτέλεση της while και των εντολών που περιλαμβάνει και η εκτέλεση του προγράμματος συνεχίζεται με τις εντολές μετά τις αγκύλες της while. Στο εσωτερικό της while θα πρέπει να μεταβληθεί η τιμή της vl (vl++) ώστε να ολοκληρωθεί μετά από ένα πεπερασμένο αριθμό επαναλήψεων.

13


do { … } while (συνθήκη)

Η do…while επαναλαμβάνει ένα σύνολο εντολών με τον ίδιο τρόπο που το κάνει και η while, όμως ο έλεγχος γίνεται στο τέλος της δομής, γι’ αυτό και οι εντολές επανάληψης εκτελούνται οπωσδήποτε μια φορά. Παράδειγμα do { // εντολές που επαναλαμβάνεται η εκτέλεσή τους } while (vl<100)

Στο παραπάνω παράδειγμα οι εντολές που βρίσκονται ανάμεσα στο do και το while εκτελούνται οπωσδήποτε μια φορά πριν ελεγχθεί η τιμή της μεταβλητής vl. Θα πρέπει στο εσωτερικό της επανάληψης η τιμή της vl να μεταβάλλεται με κάποιο τρόπο αν θέλουμε να εξέλθουμε από τη δομή.

break

Η break χρησιμοποιείται για να βγούμε από μια επανάληψη for, while ή do…while παρακάμπτοντας τη συνθήκη ελέγχου της δομής. Παράδειγμα for(x=0;x<100;x++) { digitalWrite(PWMpin, x); sens=analogRead(sensorPin); if (sens>threshold){ // Έξοδος αν ο αισθητήρας ανιχνεύσει συμβάν x=0; break; } delay(50); }

Στο παραπάνω παράδειγμα θα γίνει έλεγχος της τιμής ενός αισθητήρα 100 φορές ανά 0,05 δευτερόλεπτα. Αν όμως η τιμή του αισθητήρα είναι πάνω από ένα όριο, θα γίνει διακοπή αυτής της διαδικασίας και θα εκτελεστεί το πρόγραμμα μετά τις αγκύλες. Σε αυτή την περίπτωση η τιμή του x γίνεται 0 ώστε να ξέρουμε αν ανιχνεύτηκε τέτοιο γεγονός ή απλά ολοκληρώθηκε ο χρόνος ανάγνωσης του αισθητήρα.

14


continue

Η continue παρακάμπτει το υπόλοιπο της τρέχουσας επανάληψης for, while ή do…while και συνεχίζει με τον έλεγχο της συνθήκης του βρόγχου. Αν η συνθήκη το επιτρέπει επαναλαμβάνονται οι εντολές της επανάληψης. Παράδειγμα for(x=0;x<255;x++) { if(x>40 && x<120){ // απορρίπτονται οι τιμές αυτές continue; } analogWrite(PWMpin,x); delay(50); }

Στο παράδειγμα η for με τη μεταβλητή x θα μετρήσει τις τιμές από 0 μέχρι 255, αλλά για τις ενδιάμεσες τιμές πάνω από 40 και κάτω από 120 δεν θα έχει κάποιο αποτέλεσμα.

return

Τερματίζει μια λειτουργία και επιστρέφει μια τιμή από μια συνάρτηση που κλήθηκε αν κάτι τέτοιο είναι επιθυμητό. Παράδειγμα int checkSensor(){ if (analogRead(0)>400) { return 1; else{ return 0; } }

Στο παραπάνω παράδειγμα η checkSensor θα επιστρέψει την τιμή 1 αν ο αισθητήρας έχει τιμή μεγαλύτερη από 400, ή την τιμή 0 σε κάθε άλλη περίπτωση.

Η τιμή που επιστρέφει η return μπορεί να είναι οποιαδήποτε σταθερή τιμή ή η τιμή μιας μεταβλητής.

15


Μεταβλητές

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

Δήλωση Μεταβλητών

Πριν χρησιμοποιηθούν, όλες οι μεταβλητές, πρέπει να δηλωθούν. Δήλωση μεταβλητής σημαίνει να οριστεί ο τύπος της και προαιρετικά να ορίσουμε μια αρχική τιμή (αρχικοποίηση της μεταβλητής). Δεν είναι απαραίτητο να ορίσουμε αρχική τιμή, αλλά συχνά είναι πιο χρήσιμο. Παράδειγμα int inputVariable1; int inputVariable2 = 0;

// both are correct

Οι προγραμματιστές θα πρέπει να αποφασίσουν για το μέγεθος των αριθμών που θέλουν να αποθηκεύσουν στον επιλεγμένο τύπο μεταβλητής. Οι μεταβλητές κυλίουν κυκλικά το περιεχόμενό τους όταν οι αποθηκευμένες ποσότητες υπερβαίνουν τον καθορισμένο χώρο που διατίθεται από τον τύπο. Παράδειγμα int x x = -32,768; x = x - 1; x = 32,767; x = x + 1;

// η x περιέχει τώρα την τιμή 32.767

// η x περιέχει τώρα την τιμή -32.768

Εμβέλεια Μεταβλητών

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

Η δήλωση των μεταβλητών στη C, την γλώσσα προγραμματισμού του Arduino, είναι διαφορετική από αυτή της BASIC όπου κάθε μεταβλητή είναι γενική και μπορεί να χρησιμοποιηθεί από οποιοδήποτε τμήμα του προγράμματος.

Μια γενική μεταβλητή, είναι μια μεταβλητή που τη «βλέπουν» όλοι μέσα στο πρόγραμμα. Οι τοπικές μεταβλητές είναι ορατές μόνο στις συναρτήσεις που αυτέ δηλώθηκαν. Στο περιβάλλον ανάπτυξης του Arduino, κάθε μεταβλητή που δηλώνεται έξω από μια συνάρτηση (π.χ. setup(), loop(), κλπ. ), είναι μια γενική μεταβλητή, ορατή από παντού.

Οταν τα προγράμματα γίνονται μεγαλύτερα και πιο πολύπλοκα, οι τοπικές μεταβλητές είναι

16


ένας χρήσιμος τρόπος να βεβαιωθούμε ότι μόνο η τρέχουσα συνάρτηση έχει πρόσβαση στις μεταβλητές της. Αυτό αποτρέπει προγραμματιστικά σφάλματα όταν μια συνάρτηση μεταβάλλει τις τιμές των μεταβλητών που χρησιμοποιούνται και από άλλες συναρτήσεις.

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

Παράδειγμα int gPWMval;

void setup() { // ... }

// Μεταβλητή ορατή από παντού

void loop() { int i; // το "i" είναι ορατό μέσα στη λειτουργία "loop" float f; // το "f" είναι ορατό μέσα στη λειτουργία "loop" // ...

}

for (int j = 0; j <100; j++){ // η μεταβλητή j είναι ορατή μόνο στο εσωτερικό // του βρόγχου for }

Αρχικοποίηση Μεταβλητών

Μια μεταβλητή μπορεί να αρχικοποιηθεί (αποδίδεται μια αρχική τιμή) όταν δηλώνεται ή όχι. Είναι πάντως καλή προγραμματιστική τακτική να γίνεται επανέλεγχος στα δεδομένα μιας μεταβλητής όταν πρόκειται να χρησιμοποιηθούν για διαφορετικό σκοπό από τον αρχικό. Παράδειγμα //δηλώνει την calibrVal και ορίζει την αρχική τιμή int calibrVal = 17;

Αναδίπλωση Μεταβλητών

Οταν η τιμή της μεταβλητής υπερβαίνει το ανώτατο όριο, αυτή αναδιπλώνει την τιμή της στην μικρότερη δυνατή και το αντίστροφο.

Χρησιμοποιώντας τις Μεταβλητές

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

17


inputVar1 = 7; // η μεταβλητή inputVar1 παίρνει τιμή 7 inputVar2 = analogRead(2); // η inputVar2 παίρνει τιμή από την // αναλογική θύρα στο pin #2 Παραδείγματα int lightSensVal; char currentLetter; unsigned long speedOfLight = 186000UL; char errorMessage = {"choose another option"}; Σημείωση

Θα πρέπει να δίνετε στις μεταβλητές περιγραφικά ονόματα, ώστε να κάνετε τον κώδικά σας ευανάγνωστο. Ονόματα μεταβλητών όπως tiltSensor ή pushButton σας βοηθούν (αλλά και όσους άλλους διαβάσουν τον κώδικά σας) να καταλάβουν τί απεικονίζει τη μεταβλητή σας. Ονόματα μεταβλητών όπως var ή value, από την άλλη, κάνουν τον κώδικά σας λιγότερο ευανάγνωστο. Δεν μπορείτε να χρησιμοποιήσετε δεσμευμένες λέξεις της C σαν ονόματα μεταβλητών.

Αποφύγετε να χρησιμοποιήσετε ονόματα που ξεκινούν με αριθμητικούς χαρακτήρες.

18


Τύποι Δεδομένων (Μεταβλητών-Συναρτήσεων)

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

void

Η δήλωση void χρησιμοποιείται μόνο για τον τύπο των συναρτήσεων-λειτουργιών. Δηλώνει ότι δεν απαιτείται καμία τιμή επιστροφής από την συνάρτηση την οποία καλέσαμε.

Παράδειγμα void setup() { // ... } void loop() { // ... }

19


boolean

Η δήλωση boolean αποθηκεύει μία λογική τιμή TRUE ή FALSE. Στη μνήμη καταλαμβάνει ένα byte. Παράδειγμα boolean flash=TRUE; for(i=0;i<10;i++) flash=!flash;

Στο παράδειγμα η τιμή της μεταβλητής flash μεταβάλλεται δέκα φορές και εξέρχεται με την τιμή FALSE.

char

Η δήλωση char χρησιμοποιεί ένα byte και αποθηκεύει αριθμητικές τιμές στο διάστημα -128 έως 127. Συνήθως χρησιμοποιείται για να αποθηκεύει χαρακτήρες. Τους χαρακτήρες μπορούμε να τους αποδώσουμε σε μια μεταβλητή τύπου char βάζοντάς τους σε μονά εισαγωγικά (π.χ. ‘L’), αλλά εσωτερικά αποθηκεύεται σαν αριθμός. Τα διπλά εισαγωγικά χρησιμοποιούνται για μεγαλύτερες του ενός χαρακτήρα φράσεις (π.χ. “ABC”). Αφού οι τιμές είναι αριθμητικές μπορούμε να κάνουμε και αριθμητικές πράξεις, όπως ‘Α’+1 αποδίδει την τιμή 66, δεδομένου ότι το ‘Α’ έχει τιμή ASCII 65. Παράδειγμα char myChar = 'A'; char myChar = 65;

Στο παράδειγμα οι δυο εκφράσεις είναι ισοδύναμες.

unsigned char

Η δήλωση unsigned char χρησιμοποιεί και αυτή ένα byte αλλά χωρίς πρόσημο στο διάστημα 0 έως 255. Εχει τις ίδιες ιδιότητες με τη δήλωση byte, αλλά για λόγους συνέπειας στον προγραμματισμό του Arduino συνιστάται η χρήση της byte. Παράδειγμα unsigned char myChar = 240;

20


byte

Η δήλωση byte χρησιμοποιεί και πάλι ένα byte αλλά αποθηκεύει αριθμητικές τιμές στο διάστημα 0 έως 255. Παράδειγμα byte b = B10010;

Στο παράδειγμα η μεταβλητή b αποθηκεύει τη δυαδική (Binary) τιμή 10010, δηλαδή 18.

int

Η δήλωση int είναι αυτή που χρησιμοποιείται περισσότερο στο Arduino, δεδομένου ότι αποθηκεύει τιμές σε δυο bytes και στο διάστημα -32.768 έως 32.767, όπου υπάρχουν και οι περισσότερες χρήσεις και εφαρμογές. Παράδειγμα int ledPin = 13;

Ο τύπος int έχει ιδιαιτερότητες ως προς τον τρόπο που αναπαριστά τα περιεχόμενά του. Από τα 16 bits του int, το 1ο χρησιμοποιείται σαν πρόσημο και τα υπόλοιπα για την αποθήκευση του αριθμού. Αν τον μικρότερο δυνατό αριθμό του τύπου int τον -32.768 τον μειώσουμε κατά 1, προκύπτει ο μεγαλύτερος, δηλαδή ο 32.767 και το αντίστροφο. Παράδειγμα int x; x = -32768; x = x - 1; x = 32767; x = x + 1;

short

// το x τώρα περιέχει την τιμή 32.767 // το x τώρα περιέχει το -32.768

Ο τύπος short είναι εύρους 16 bit και αποθηκεύει τιμές στο διάστημα -32.768 και 32.767. Παράδειγμα short x=100;

21


unsigned int

Η δήλωση unsigned int μοιάζει με την int αλλά δεν χρησιμοποιεί το 1ο bit για το πρόσημο. Ετσι οι τιμές που αποδίδονται είναι στο διάστημα 0 και 65.535. Παράδειγμα unsigned int x x = 0; x = x - 1; // το x τώρα περιέχει την τιμή 65.535 x = x + 1; // το x τώρα περιέχει το 0 Στην αριθμητική αυτού του τύπου ισχύει ότι και στον int ως προς την κυκλική αναδίπλωση. Ο μεγαλύτερος δυνατός αριθμός, το 65.535, αν αυξηθεί κατά 1 η νέα τιμή θα είναι 0 και το αντίθετο, αν μειωθεί το 0 γίνεται 65.535.

word

Η δήλωση word είναι ίδια με την unsigned int, δηλαδή αποθηκεύει τιμές στα 16 bits στο διάστημα 0 και 65535. Παράδειγμα word x=100;

long

Η δήλωση long είναι ακέραιου τύπου αλλά χρησιμοποιεί 32 bits με πρόσημο στο διάστημα -2.147.483.648 μέχρι 2.147.483.647. Παράδειγμα long speedOfLight = 186000L;

Στο παραπάνω παράδειγμα το L δίπλα στην αριθμητική τιμή σημαίνει ότι η τιμή αυτή χαρακτηρίζεται ως long. Αν χρειαστεί να γίνουν πράξεις και με ακέραιους θα πρέπει τουλάχιστον μία από τις τιμές να έχει το χαρακτηρισμό L.

unsigned long

Η δήλωση unsigned long είναι ακέραιου τύπου αλλά χρησιμοποιεί και τα 32 bits για την αποτύπωση μη αρνητικών αριθμών στο διάστημα 0 μέχρι 4.294.967.295.

Παράδειγμα unsigned long time; time = millis();

Στο παράδειγμα η unsigned long μεταβλητή time παίρνει την τιμή των χιλιοστών δευτερολέπτου που πέρασαν από τη στιγμή που ξεκίνησε Arduino. Η τιμή θα μηδενιστεί μετά από 50 περίπου ημέρες.

22


float

Η δήλωση float χρησιμοποιείται για την αναπαράσταση αριθμών που απαιτούν δεκαδικό τμήμα. Εχει μεγαλύτερη δυνατότητα απεικόνισης από τους ακέραιους γιατί χρησιμοποιεί 4 bytes και μπορεί να αποθηκεύσει αριθμούς στο διάστημα -3.4028235E+38 έως 3.4028235E+38.

Οι αριθμοί τύπου float αποτελούνται συνολικά από 6 ή 7 ψηφία. Οι μικρότεροι αριθμοί (θετικοί και αρνητικοί) έχουν περισσότερα δεκαδικά, άρα μεγαλύτερη ακρίβεια. Με μεγάλοι αριθμοί, έχουν λιγότερα δεκαδικά ψηφία και μικρότερη ακρίβεια. Για μεγαλύτερη ακρίβεια σε άλλες πλατφόρμες ανάπτυξης χρησιμοποιούνται οι αριθμοί τύπου double (μέχρι και 15 ψηφία), αλλά στο Arduino ο τύπος double είναι ίδιος με τον τύπο float.

Ο τύπος float είναι δίνει μη ακριβείς αριθμούς. Για παράδειγμα η έκφραση 6.0 / 3.0, δε δίνει αποτέλεσμα ακριβώς 2,0. Επίσης οι πράξεις με αριθμούς αυτού του τύπου γίνονται πιο αργά από τις αντίστοιχες πράξεις με ακέραιους. Ετσι προτείνεται να γίνονται πράξεις με ακέραιους όσο περισσότερο είναι δυνατό και να περιορίζεται η χρήση των αριθμών τύπου float. Παράδειγμα int x; int y; float z;

x = 1; y = x / 2;

// το y τώρα περιέχει την τιμή 0, // δεν υπάρχουν δεκαδικά z = (float)x / 2.0; // το z περιέχει τώρα το .5

Το (float) πριν την τιμή x χρησιμοποιείται για να μετατρέψει το ακέραιο περιεχόμενο της x σε float. Πρέπει να χρησιμοποιηθεί το 2.0 και όχι το 2.

double

Η δήλωση double στα περισσότερα Arduino (εκτός του Arduino Due) είναι ακριβώς το ίδιο με τη δήλωση float. Χρησιμοποιεί 4 bytes όπως και η float.

Στο Arduino Due χρησιμοποιεί 8 bytes και μεγαλύτερη ακρίβεια.

23


string

Η δήλωση string χρησιμοποιείται για να περιγράψουμε μια σειρά χαρακτήρων που τελειώνουν σε null. Παραδείγματα char Str1[15]; char Str2[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o'}; char Str3[8] = {'a', 'r', 'd', 'u', 'i', 'n', 'o', '\0'}; char Str4[ ] = "arduino"; char Str5[8] = "arduino"; char Str6[15] = "arduino";

Δυνατότητες για τη δήλωση συμβολοσειρών (strings)

Δεσμεύουν μια σειρά από χαρακτήρες χωρίς αρχικοποίηση όπως η Str1.

Δεσμεύουν μια σειρά από χαρακτήρες (με ένα επιπλέον χαρακτήρα) και ο μεταγλωττιστής θα προσθέσει ένα χαρακτήρα null στο τέλος που απαιτείται για τον τερματισμό της αλυσίδας των χαρακτήρων, όπως η Str2. Ρητή προσθήκη του χαρακτήρα null στην Str3.

Γίνεται προετοιμασία με μια σταθερή συμβολοσειρά σε εισαγωγικά. Ο μεταγλωττιστής θα υπολογίσει το μέγεθος του πίνακα για να χωρέσει η σταθερή συμβολοσειρά και ένας επιπλέον τερματικός χαρακτήρας null στο Str4.

Γίνεται προετοιμασία του πίνακα που περιέχει σαφές μέγεθος και σταθερή συμβολοσειρά στο Str5.

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

Γενικά, οι συμβολοσειρές τελειώνουν με έναν χαρακτήρα null (με κωδικό ASCII το 0). Αυτό επιτρέπει στις συναρτήσεις που χρησιμοποιούν συμβολοσειρές να πουν πού αυτό τελειώνει. Αν δεν υπάρχει τέτοιος χαρακτήρας θα συνεχίσουν να διαβάζουν σαν χαρακτήρες το συνεχόμενο τμήμα μνήμης που ακολουθεί και ας μην είναι μέρος της συμβολοσειράς.

Αυτό σημαίνει ότι η συμβολοσειρά χρειάζεται χώρο για ένα επιπλέον χαρακτήρα απ’ ότι το κείμενο που περιέχει. Για αυτό το Str2 και Str5 χρειάζεται να είναι 8 χαρακτήρων, ακόμα και αν η λέξη “Arduino” έχει 7, η τελευταία θέση αυτόματα γεμίζει με τον χαρακτήρα null. Το μήκος του Str4 θα υπολογιστεί αυτόματα στους 8 χαρακτήρες, ένας επιπλέον για τον null. Στο Str3, συμπεριλάβαμε ένα κενό χαρακτήρα μόνοι μας (γράφεται \0).

Να σημειώσουμε ότι είναι πιθανό να έχουμε μια συμβολοσειρά χωρίς των τελικό null χαρακτήρα (π.χ. αν έχουμε καθορισμένο το μήκος του Str2 ως 7 αντί για 8). Αυτό θα δημιουργήσει προβλήματα στις περισσότερες λειτουργίες που χρησιμοποιούν strings, γι’ αυτό δεν πρέπει να γίνει σκόπιμα.

24


Απλά ή διπλά εισαγωγικά;

Οι συμβολοσειρές ορίζονται πάντα μέσα σε διπλά εισαγωγικά (“Abc”) και οι χαρακτήρες μέσα σε μονά εισαγωγικά (‘A’). Αναδίπλωση μεγάλου μήκους συμβολοσειρών

Μπορείτε να αναδιπλώσετε μεγάλου μήκους συμβολοσειρές με τον ακόλουθο τρόπο: char myString[] = "Αυτή είναι η πρώτη γραμμή" " αυτή η δεύτερη γραμμή" " κ.λ.π.";

Πίνακες συμβολοσειρών

Συχνά είναι βολικό, όταν εργάζεστε με μεγάλους όγκους κειμένων, όπως μια εφαρμογή με μια οθόνη LCD, να δομήσετε έναν πίνακα με συμβολοσειρές. Επειδή αυτές είναι από μόνες τους πίνακες, αυτό είναι ένα παράδειγμα πίνακα δύο διαστάσεων. Παράδειγμα char* myStrings[]={"Αυτή "Αυτή "Αυτή "Αυτή "Αυτή "Αυτή

void setup(){ Serial.begin(9600); }

είναι είναι είναι είναι είναι είναι

η η η η η η

1η 2η 3η 4η 5η 6η

συμβολοσειρά", συμβολοσειρά", συμβολοσειρά", συμβολοσειρά", συμβολοσειρά", συμβολοσειρά"};

void loop(){ for (int i = 0; i < 6; i++){ Serial.println(myStrings[i]); delay(500); } }

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

Σε αυτόν τον κώδικα, ο αστερίσκος μετά τον τύπο δεδομένων char «char *» δηλώνει ότι πρόκειται για μια σειρά από "δείκτες". Ολα τα ονόματα του πίνακα είναι στην πράξη δείκτες, έτσι απαιτείται να γίνει ένας πίνακες από πίνακες. Οι δείκτες (Pointers) είναι ένα από τα πιο απόκρυφα μέρη της C και δύσκολο να το κατανοήσουν οι αρχάριοι, αλλά δεν είναι απαραίτητο να γίνει πλήρως κατανοητό πώς δουλεύουν για να τους χρησιμοποιήσουμε αποτελεσματικά εδώ.

25


Πίνακες

Ενας πίνακας είναι μια συλλογή μεταβλητών που προσπελαύνονται με έναν δείκτη. Οι πίνακες στην γλώσσα προγραμματισμού C στην οποία βασίζεται το Arduino μπορούν να γίνουν πολύπλοκοι αλλά η χρήση απλών πινάκων είναι σχετικά απλή. Δημιουργώντας (Δηλώνοντας) ένα Πίνακα

Ολες οι μέθοδοι που ακολουθούν είναι αποδεκτοί τρόποι για να δημιουργήσουμε (δηλώσουμε) έναν πίνακα. int myInts[6]; int myPins[] = {2, 4, 8, 3, 6}; int mySensVals[6] = {2, 4, -8, 3, 2}; char message[6] = "hello"; Μπορείτε να δηλώσετε έναν πίνακα χωρίς να τον αρχικοποιήσετε όπως στο myInts.

Στον myPins δηλώσαμε ένα πίνακα χωρίς να ορίσουμε το ακριβές μέγεθός του. Ο compiler μετράει τα στοιχεία και φτιάχνει τον πίνακα με το απαραίτητο μέγεθος.

Τέλος, μπορείτε ταυτόχρονα και να αρχικοποιήσετε και καθορίσετε το ακριβές μέγεθος όπως στο mySensVals. Να σημειωθεί ότι όταν δηλώνεται ένας πίνακας χαρακτήρων, ένα ή περισσότερα στοιχεία απαιτούνται για τους χαρακτήρες null. Προσπελαύνοντας ένα Πίνακα

Οι πίνακες έχουν μηδενικό αρχικό δείκτη. Δηλαδή, στον πίνακα mySensVals το πρώτο στοιχείο έχει δείκτη 0, το δεύτερο έχει δείκτη 1 κ.ο.κ. mySensVals[0] == 2, mySensVals[1] == 4, κ.ο.κ.

Αυτό επίσης σημαίνει ότι ένας πίνακας με δέκα στοιχεία, έχει το τελευταίο του στοιχείο με δείκτη 9. Ετσι, int myArray[10]={9,3,2,4,3,2,7,8,9,11}; // myArray[9] περιέχει την τιμή 11 // myArray[10] δεν έχει έγκυρη τιμή και περιέχει // τυχαίες πληροφορίες

Για το λόγο αυτό θα πρέπει να είστε προσεκτικοί όταν προσπελαύνετε πίνακες. Η προσπέλαση στοιχείων μετά το τέλος του πίνακα (χρησιμοποιώντας έναν δείκτη μεγαλύτερο του επιτρεπτού -1) διαβάζει από τη μνήμη που χρησιμοποιείται για άλλες χρήσεις. Ανάγνωση από τέτοιες θέσεις πιθανότατα θα οδηγήσουν σε απρόβλεπτα μη έγκυρα δεδομένα. Εγγραφή σε τυχαίες θέσεις μνήμης είναι απολύτως κακή ιδέα και συχνά μπορεί να έχει δυσάρεστα αποτελέσματα όπως κολλήματα και δυσλειτουργίες του προγράμματος. Αυτό επίσης είναι μπορεί να είναι ένα πρόβλημα που δεν εντοπίζεται εύκολα.

Αντίθετα από την BASIC ή τη JAVA, ο C compiler δεν ελέγχει αν η προσπέλαση ενός πίνακα είναι μέσα στα σωστά όρια του μεγέθους που έχει δηλωθεί.

26


Για να αντιστοιχίσετε μια τιμή σε ένα πίνακα mySensVals[0] = 10; Για να ανακτήσετε μια τιμή από ένα πίνακα x = mySensVals[4];

Μετατροπές

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

char(x)

Μετατρέπει την τιμή μιας οποιουδήποτε τύπου μεταβλητής σε χαρακτήρα.

byte(x)

Μετατρέπει την τιμή μιας οποιουδήποτε τύπου μεταβλητής σε byte, δηλαδή τιμή στο διάστημα 0 έως 255.

int(x)

Μετατρέπει την τιμή μιας οποιουδήποτε τύπου μεταβλητής σε int, δηλαδή τιμή στο διάστημα -32.768 έως 32.767.

word(x) ή word(h,l)

Μετατρέπει την τιμή μιας οποιουδήποτε τύπου μεταβλητής σε word, δηλαδή τιμή στο διάστημα 0 έως 65.535, ή συνενώνει δυο bytes, το υψηλής αξίας (h) και το χαμηλής αξίας (l) σε τιμή τύπου word.

long(x)

Μετατρέπει την τιμή μιας οποιουδήποτε τύπου μεταβλητής σε long, δηλαδή τιμή στο διάστημα -2.147.483.648 έως 2.147.483.647.

float(x)

Μετατρέπει την τιμή μιας οποιουδήποτε τύπου μεταβλητής σε float. Για περισσότερες λεπτομέρειες ως προς το εύρος των τιμών δείτε στη Δήλωση μεταβλητών τύπου float.

27


Λειτουργίες-Συναρτήσεις

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

28


Ψηφιακή Είσοδος/Εξοδος

Για τον χειρισμό των ψηφιακών ακίδων, υπάρχουν λειτουργίες που μπορούν να αρχικοποιήσουν, να διαβάσουν την κατάσταση κάποιας εισόδου ή να αποδώσουν μια δυαδική τιμή σε κάποια έξοδο. Οι λειτουργίες pinMode(), digitalRead() και digitalWrite(), χρησιμοποιούνται ακριβώς για τους σκοπούς αυτούς.

pinMode()

Διαμορφώνει το καθορισμένο pin ώστε να συμπεριφέρεται τόσο σαν είσοδος όσο και σαν έξοδος. Η σύνταξη της εντολής είναι: pinMode(pin, mode)

όπου, pin είναι ο αριθμός του ακροδέκτη του οποίου τη λειτουργία θέλουμε να ρυθμίσουμε και mode μπορεί να είναι μία από τις τιμές INPUT, OUTPUT, ή INPUT_PULLUP.

Είναι δυνατό να επιτρέψουμε τη χρήση των εσωτερικών αντιστάσεων με τη λειτουργία INPUT_PULLUP. Επιπλέον , η λειτουργία INPUT απενεργοποιεί ρητά τις εσωτερικές αντιστάσεις. Η λειτουργία αυτή δεν επιστρέφει κάποια τιμή. Παράδειγμα int ledPin = 13; // ένα LED είναι συνδεδεμένο στο pin 13 void setup() { pinMode(ledPin, OUTPUT); // ορίζω τον αντίστοιχο // ακροδέκτη ως εξόδου } void loop() { digitalWrite(ledPin, HIGH); // ανάβω το LED delay(1000); // περιμένω ένα δευτερόλεπτο digitalWrite(ledPin, LOW); // σβήνω το LED delay(1000); // περιμένω ένα δευτερόλεπτο } Σημείωση

Οι αναλογικοί ακροδέκτες εισόδου μπορούν να χρησιμοποιηθούν σαν ψηφιακές αρκεί να αναφερθούν με το όνομα Α0, Α1, κλπ.

29


digitalWrite()

Γράφει μια τιμή στον ψηφιακό ακροδέκτη που μπορεί να είναι HIGH ή LOW. Αν το pin διαμορφώθηκε σαν εξόδου (OUTPUT) με τη λειτουργία pinMode(), η τάση του θα οριστεί στα 5V (ή στα 3,3V στις αντίστοιχες εκδόσεις υλικού) για την τιμή HIGH, 0V (γείωση) για την τιμή LOW. Αν το pin έχει ρυθμιστεί ως INPUT , γράφοντας μια τιμή HIGH με τη λειτουργία digitalWrite() θα επιτρέψει μια εσωτερική αντίσταση pullup στα 20KΩ. Γράφοντας LOW θα απενεργοποιηθεί το pullup. Η αντίσταση pullup είναι αρκετή για να ανάψει ένα LED αμυδρά, επομένως αν τα LEDs φαίνονται να λειτουργούν, αλλά αμυδρά, μπορεί να οφείλεται σε αυτό. Η λύση είναι να ρυθμίσετε το ψηφιακό pin σαν μι έξοδο με τη λειτουργία pinMode(). Σημείωση

Το ψηφιακό pin 13 είναι δυσκολότερο να χρησιμοποιηθεί ως ψηφιακή είσοδος όπως τα υπόλοιπα ψηφιακά pins γιατί συνδέεται με ένα LED και αντίσταση σε σειρά ενσωματωμένα στην πλακέτα, στις περισσότερες πλακέτες Arduino. Εάν ενεργοποιήσετε την εσωτερική 20k pull- up αντίσταση, θα δώσει γύρω στο 1,7 V αντί της αναμενόμενης τάσης των 5V, γιατί η ενσωματωμένη αντίσταση και το LED σε σειρά θα ρίξει το επίπεδο τάσης προς τα κάτω, σε αυτή την τιμή και αυτό σημαίνει ότι θα επιστρέφει πάντα τιμή LOW. Εάν πρέπει να χρησιμοποιήσετε το pin 13 ως ψηφιακή είσοδο, χρησιμοποιήστε μια εξωτερική αντίσταση. Η σύνταξη της εντολής είναι:

digitalWrite(pin, value)

όπου, pin είναι ο αριθμός του ακροδέκτη και value η τιμή που θα του στείλουμε HIGH ή LOW. Η digitalWrite() δεν επιστρέφει καμία τιμή. Παράδειγμα int ledPin = 13;

// LED connected to digital pin 13

void setup() { pinMode(ledPin, OUTPUT); // ορίζει το ψηφιακό pin σαν εξόδου } void loop() { digitalWrite(ledPin, HIGH); // sets the LED on delay(1000); // waits for a second digitalWrite(ledPin, LOW); // sets the LED off delay(1000); // waits for a second } Αναβοσβήνει το ενσωματωμένο LED της πλακέτας του Arduino ανά 2 δευτερόλεπτα. Σημείωση

Οι αναλογικοί ακροδέκτες εισόδου μπορούν να χρησιμοποιηθούν σαν ψηφιακές αρκεί να αναφερθούν με το όνομα Α0, Α1, κλπ.

30


digitalRead()

Διαβάζει την τιμή από ένα συγκεκριμένο ψηφιακό pin, η οποία μπορεί να είναι είτε HIGH ή LOW. Η σύνταξη της εντολής είναι: digitalRead(pin)

όπου, pin είναι ο αριθμός του ψηφιακού pin που θέλουμε να διαβάσουμε (η τιμή πρέπει να είναι τύπου int). Η επιστρεφόμενη τιμή είναι HIGH ή LOW. Παράδειγμα

Ορίζει τον ακροδέκτη 13 με την ίδια τιμή όπως ο ακροδέκτης 7 που δηλώνεται ως εισόδου. int ledPin = 13; // Το LED συνδέεται στην ψηφιακή ακίδα 13 int inPin = 7; // Ένας διακόπτης πίεσης συνδέεται στο pin 7 int val = 0; // μεταβλητή για να αποθηκεύσουμε την // τιμή ανάγνωσης

void setup() { pinMode(ledPin, OUTPUT); // ορίζει το ψηφιακό pin 13 // σαν εξόδου pinMode(inPin, INPUT); // ορίζει το ψηφιακό pin 7 // σαν εισόδου } void loop() { val = digitalRead(inPin); // διαβάζει το pin εισόδου digitalWrite(ledPin, val); // ορίζει το LED ίδιο με την // τιμή του πλήκτρου }

Αν στον ακροδέκτη 7 δεν έχουμε συνδέσει έναν διακόπτη πίεσης, η digitalRead() θα επιστρέφει είτε HIGH ή LOW (και αυτό μπορεί να αλλάζει τυχαία).

Σημείωση

Οι αναλογικοί ακροδέκτες εισόδου μπορούν να χρησιμοποιηθούν σαν ψηφιακές αρκεί να αναφερθούν με το όνομα Α0, Α1, κλπ.

31


Αναλογική Είσοδος/Εξοδος

Τους αναλογικούς ακροδέκτες μπορούμε να τους χειριστούμε με λειτουργίες οι οποίες καθορίζουν τον τρόπο που θα διαβάσουμε τις τιμές τους, έχοντας υπόψη ότι απαιτείται η μετατροπή ενός αναλογικού σήματος σε ψηφιακό (γι’ αυτό χρησιμοποιείται ένας 10-bit A/D converter), αλλά και το γεγονός ότι οι πηγές του σήματος που θα διαβάσουμε θα πρέπει να λειτουργούν στις ίδιες τάσεις τις οποίες πρέπει να αναφέρουμε στην ανάγνωση του σήματος. Επιπλέον, δεδομένου ότι δεν υπάρχουν αναλογικές έξοδοι, χρησιμοποιείται η ψευδοαναλογική-ευροπαλμική διαμόρφωση (PWM) των ψηφιακών ακίδων που πρέπει να διαμορφώσουμε κατάλληλα. Οι λειτουργίες analogReference(), analogRead() και analogWrite() χρησιμοποιούνται για τους σκοπούς αυτούς.

32


analogReference()

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

analogReference(type)

όπου, type είναι ο τύπος αναφοράς που θέλετε να χρησιμοποιήσετε (DEFAULT, INTERNAL, INTERNAL1V1, INTERNAL2V56, ή EXTERNAL). Οι επιλογές που μπορούμε να κάνουμε είναι:

• DEFAULT: η προεπιλεγμένη τιμή αναφοράς είναι τα 5 volt (για όσες πλακέτες Arduino λειτουργούν σε αυτή την τάση) ή 3,3 volt (σε πλακέτες Arduino των 3,3V)

• INTERNAL: μια ενσωματωμένη τιμή αναφοράς, ίση με 1,1 volt στο ATmega168 ή ATmega328 και 2,56 volt στο ATmega8 (δεν διατίθεται για το Arduino Mega )

• INTERNAL1V1: μια ενσωματωμένη τιμή αναφοράς στα1.1V (μόνο για το Arduino Mega) • INTERNAL2V56: μια ενσωματωμένη τιμή αναφοράς 2.56V (μόνο για το Arduino Mega)

• EXTERNAL: η τάση που εφαρμόζεται στον ακροδέκτη AREF (0 έως 5V μόνο) χρησιμοποιείται σαν αναφορά.

Η analogReference() δεν επιστρέφει καμία τιμή. Σημείωση

Μετά την αλλαγή της αναλογικής αναφοράς, οι πρώτες ενδείξεις που διαβάζονται από την analogRead() μπορεί να μην είναι ακριβείς. Προειδοποίηση

Μην χρησιμοποιείτε τίποτα λιγότερο από 0V ή περισσότερο από 5V για εξωτερική τάση αναφοράς για το pin AREF! Εάν χρησιμοποιείτε μια εξωτερική τιμή αναφοράς στο pin AREF, πρέπει να ρυθμίσετε την αναλογική αναφορά EXTERNAL πριν από την κλήση analogRead(). Διαφορετικά, θα βραχυκυκλώσει η ενεργός τάση αναφοράς (που δημιουργείται εσωτερικά) και το pin AREF, και πιθανόν να καταστρέψουν τον μικροελεγκτή Arduino στην πλακέτα σας.

Εναλλακτικά , μπορείτε να συνδέσετε την εξωτερική τάση αναφοράς στο pin AREF μέσω μιας αντίστασης 5K, που σας επιτρέπει να πραγματοποιήσετε εναλλαγή μεταξύ εξωτερικών και εσωτερικών τάσεων αναφοράς. Σημειώστε ότι η αντίσταση θα μεταβάλλει την τάση που χρησιμοποιείται ως αναφορά επειδή υπάρχει μια εσωτερική αντίσταση 32Κ επί του ακροδέκτη AREF. Τα δυο pin λειτουργούν ως διαιρέτης τάσεως, έτσι, για παράδειγμα, εφαρμόζονται 2.5V διαμέσου της αντίστασης και θα αποδώσει 2,5*32 / (32+5)=~2.2V στο pin AREF.

33


analogRead()

Διαβάζει την τιμή από τον καθορισμένο αναλογική ακροδέκτη. Η πλακέτα Arduino περιέχει 6 κανάλια (8 κανάλια για το Mini και Nano, 16 για το Mega), και μετατροπέα αναλογικό σε ψηφιακό στα 10 bit. Αυτό σημαίνει ότι θα χαρτογραφήσει τάσεις εισόδου μεταξύ 0 και 5 βολτ σε ακέραιες τιμές μεταξύ 0 και 1023. Αυτό δίνει μια ανάλυση μεταξύ των τιμών που εισάγονται: 5 βολτ / 1024 μονάδες ή 0,0049 βολτ ( 4.9 mV ) ανά μονάδα. Το εύρος εισόδου και η ανάλυση μπορεί να αλλάξει με τη χρήση της εντολής analogReference(). Κάθε ανάγνωση από τις αναλογικές εισόδους χρειάζεται περίπου 100 μικροδευτερόλεπτα (0.0001sec) για να πραγματοποιηθεί, οπότε ο μέγιστος ρυθμός ανάγνωσης είναι περίπου 10.000 φορές το δευτερόλεπτο. Η σύνταξη της εντολής είναι: analogRead(pin)

όπου pin είναι ο αριθμός του αναλογικού ακροδέκτη εισόδου που θέλουμε να διαβάσουμε (τιμή 0 έως 5 στις περισσότερες πλακέτες, 0 έως 7 στο Mini και Nano, 0 έως 15 για το Mega).

Η τιμή που επιστρέφεται είναι ακέραιου τύπου int στο διάστημα 0 έως 1023. Σημείωση

Αν το αναλογικό pin εισόδου δεν συνδέεται με τίποτα, η τιμή που επιστρέφεται από την εντολή analogRead() θα εξαρτηθεί από διάφορους άλλους (τυχαίους) παράγοντες, όπως οι τιμές των άλλων αναλογικών εισόδων, πόσο κοντά είναι στο χέρι σας από την πλακέτα του Arduino, κλπ. Παράδειγμα int analogPin = 3; int val = 0;

// ποτενσιόμετρο συνδεδεμένο στον μεσαίο // ακροδέκτη και στο αναλογικό pin 3. // εξωτερικά συνδέεται στη γείωση και // στα +5V // μεταβλητή για να αποθηκεύσουμε //την τιμή που διαβάζεται

void setup() { Serial.begin(9600); }

//

αρχικοποίηση της σειριακής θύρας

void loop() { val = analogRead(analogPin); }

Serial.println(val);

34

// διάβασμα της τιμής του // ακροδέκτη εισόδου // εμφάνιση τιμής στη σειριακή θύρα


analogWrite() – PWM

Γράφει μια αναλογική τιμή (ψευδοαναλογικό-ευροπαλμικό PWM κύμα) σε ένα ακροδέκτη. Μπορεί να χρησιμοποιηθεί για να ανάψει ένα LED σε διάφορες φωτεινότητες ή να οδηγήσουμε ένα μοτέρ σε διάφορες ταχύτητες. Μετά την κλήση της analogWrite(), στο pin θα δημιουργηθεί μια σταθερή τετραγωνική κυματομορφή με συγκεκριμένο κύκλο μέχρι την επόμενη κλήση analogWrite() ( ή μια κλήση digitalRead() ή digitalWrite() που αφορά τον ίδιο ακροδέκτη). Η συχνότητα του σήματος PWM για τα περισσότερα pins είναι περίπου 490 Hz. Στο Uno και παρόμοιες πλακέτες, τα pins 5 και 6 έχουν συχνότητα περίπου 980 Hz. Τα pins 3 και 11 για το Leonardo επίσης τρέχει στα 980 Hz.

Στις περισσότερες πλακέτες Arduino (αυτές με το ATmega168 ή ATmega328), η λειτουργία αυτή λειτουργεί στις ακίδες 3, 5, 6, 9, 10, και 11. Στο Arduino Mega, λειτουργεί στις ακίδες 2 έως 13 και 44 έως 46. Σε παλαιότερες πλακέτες Arduino με ATmega8 η analogWrite() υποστηρίζεται μόνο στους ακροδέκτες 9, 10, και 11.

Το Arduino Due υποστηρίζει analogWrite() στις ακίδες 2 έως 13, καθώς και στις ακίδες DAC0 και DAC1. Σε αντίθεση με τις ακίδες PWM, τα DAC0 και DAC1 είναι μετατροπείς Ψηφιακό σε Αναλογικό, και να λειτουργούν σαν πραγματικές αναλογικές έξοδοι. Δεν χρειάζεται να καλέσετε την pinMode() για να ρυθμίσετε την ακίδα ως έξοδο πριν από την κλήση της analogWrite().

Η λειτουργία analogWrite δεν έχει καμία σχέση με τις αναλογικές ακίδες ή τη λειτουργία analogRead. Η σύνταξη της εντολής είναι:

analogWrite(pin, value)

όπου, pin είναι η ακίδα στην οποία θέλουμε να γράψουμε και value η περίοδος λειτουργίας από 0 (πάντα κλειστό) μέχρι και 255 (πάντα ανοικτό). Η analogWrite δεν επιστρέφει τίποτα. Σημειώσεις και γνωστά θέματα

Οι ευροπαλμικές έξοδοι PWM που παράγονται στις ακίδες 5 και 6 θα έχουν υψηλότερη του αναμενόμενου περίοδο λειτουργίας. Αυτό συμβαίνει λόγω των αλληλεπιδράσεων με τις λειτουργίες millis() και delay(), οι οποίες μοιράζονται το ίδιο εσωτερικό χρονόμετρο που χρησιμοποιείται για την παραγωγή των εν λόγω εξόδων PWM. Αυτό μπορεί να παρατηρηθεί κυρίως στις χαμηλές ρυθμίσεις περιόδου λειτουργίας (π.χ. 0-10) και μπορεί να οδηγήσει σε μια τιμή 0 που δεν είναι πλήρως απενεργοποιημένη στην έξοδο των ακίδων 5 και 6.

35


Παράδειγμα

Ρυθμίζει την έξοδο προς το LED ανάλογη της τιμής που διαβάζεται από το ποτενσιόμετρο. int ledPin = 9; // LED συνδεμένο στην ψηφιακή ακίδα 9 int analogPin = 3; // ποτενσιόμετρο συνδεμένο στην // αναλογική ακίδα 3 int val = 0; // μεταβλητή για να αποθηκεύσουμε την // τιμή ανάγνωσης void setup() { pinMode(ledPin, OUTPUT); }

void loop() { val = analogRead(analogPin);

}

// ορίζει το pin εξόδου // διαβάζει το pin εισόδου

// μετατροπή των τιμών της στο διάστημα 0 έως 255 // για την analogWrite analogWrite(ledPin, val / 4);

36


Προηγμένη Είσοδος/Εξοδος

Οι λειτουργίες αυτές θεωρούνται προηγμένες δεδομένου ότι δεν είναι από τις πιο συνηθισμένες στη χρήση σε σχέση με τις προαναφερόμενες. Ομως είναι από αυτές που μπορούν να μας γλιτώσουν από την παραγωγή εκτεταμένου κώδικα ώστε να τις αντικαταστήσουμε με άλλους τρόπους. Οι βασικότερες που μπορούμε να χρησιμοποιήσουμε είναι η tone() και η noTone() που έχουν να κάνουν με τον ήχο που μπορούμε να παράξουμε σε κάποια ακίδα. Αλλες λειτουργίες, που όμως ξεφεύγουν από τα πλαίσια της εργασίας μας είναι η shiftOut() και η shiftIn() που στέλνουν ακολουθίες δυαδικών τιμών με σειριακό τρόπο σε εξωτερικές συσκευές (οι οποίες συνήθως διαθέτουν ενός είδους μνήμη και τις ανασυνθέτουν) και η pulseIn() για την εισαγωγή αντίστοιχων δυαδικών ακολουθιών που προέρχονται από εξωτερικές συσκευές ή αισθητήρες.

tone()

Παράγει ένα τετραγωνικό κύμα μιας καθορισμένης συχνότητας σε έναν ακροδέκτη. Η διάρκεια μπορεί να καθοριστεί, διαφορετικά το κύμα συνεχίζεται μέχρι να γίνει κλήση της noTone(). Η ακίδα μπορεί να συνδεθεί με ένα πιεζοηλεκτρικό βομβητή ή άλλο ηχείο για να παίξει τυος τόνους.

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

Η χρήση της λειτουργίας tone() χρησιμοποιεί την ευροπαλμική διαμόρφωση - PWM στις ακίδες 3 και 11 (για τις πλακέτες Arduino, εκτός από το Mega).

Δεν είναι δυνατόν να δημιουργηθούν τόνοι με συχνότητα χαμηλότερη από 31Hz. Σημείωση

Αν θέλετε να παίξετε διαφορετικούς τόνους σε πολλές ακίδες, θα πρέπει να καλέσετε την λειτουργία noTone() σε ένα pin πριν από την κλήση tone() για την επόμενη ακίδα. Η σύνταξη της εντολής είναι:

tone(pin, frequency)

tone(pin, frequency, duration)

όπου, pin είναι η ακίδα στην οποία θα παραχθεί ο ήχος, frequency είναι η συχνότητα του τόνου σε hertz (τύπος μεταβλητής unsigned int) και duration είναι η διάρκεια του ήχου σε χιλιοστά του δευτερολέπτου (προαιρετική τιμή - unsigned long) Η tone δεν επιστρέφει τίποτα.

37


noTone()

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

Αν θέλετε να παίξετε διαφορετικούς τόνους σε πολλαπλές ακίδες, θα πρέπει να καλέσετε πρώτα την noTone () σε ένα pin πριν από την κλήση της tone() για την επόμενη ακίδα. Η σύνταξη της εντολής είναι: noTone(pin)

όπου, pin είναι η ακίδα στην οποία θα σταματήσει να παράγεται ο ήχος.

Η noTone δεν επιστρέφει κάποια τιμή.

38


Χρόνος

Ο χειρισμός του χρόνου στο Arduino έχει να κάνει με τον χρόνο που διανύθηκε από τη στιγμή που ξεκίνησε να εκτελείται το τρέχον πρόγραμμα, όσο και πόσο χρόνο θέλουμε να ανακόψουμε την εκτέλεση του προγράμματος καθυστερώντας σκόπιμα. Οι χρόνοι εκφράζονται σε χιλιοστά και σε εκατομμυριοστά του δευτερολέπτου και για την πρώτη περίπτωση χρησιμοποιούνται οι συναρτήσεις millis() και micros(), ενώ για τις καθυστερήσεις οι λειτουργίες delay() και delayMicroseconds().

millis()

Επιστρέφει τον αριθμό των χιλιοστών των δευτερολέπτων που πέρασαν από την εκκίνηση του Arduino και του τρέχοντος προγράμματος. Αυτός ο αριθμός μπορεί να υπερχειλίσει, δηλαδή να αρχίσει να μετράει και πάλι από το μηδέν μετά την πάροδο 50 περίπου ημερών.

Η συνάρτηση millis() δεν έχει παραμέτρους και επιστρέφει τα χιλιοστά του δευτερολέπτου που πέρασαν από την εκκίνηση του Arduino και του προγράμματος σε μια μεταβλητή τύπου unsigned long. Παράδειγμα unsigned long time;

void setup(){ Serial.begin(9600); } void loop(){ Serial.print("Time: "); time = millis(); // τυπώνει τον διανυθέντα χρόνο από την εκκίνηση // του προγράμματος Serial.println(time); // περιμένει ένα δευτερόλεπτο ώστε να μην αποστέλλονται // πολλά δεδομένα delay(1000); } Σημείωση

Η τιμή που επιστρέφει η millis είναι τύπου unsigned long, και επομένως μπορεί να δημιουργηθούν προβλήματα αν ο προγραμματιστής δοκιμάσει να τη χρησιμοποιήσει σε αριθμητικές πράξεις σε άλλο τύπο δεδομένων όπως π.χ. σαν int.

39


micros()

Επιστρεφει τον αριθμό των εκατομμυριοστών του δευτερολέπτου από τη στιγμή που άρχισε η εκτέλεση του τρέχοντος προγράμματος στο Arduino. Αυτός ο αριθμός θα υπερχειλίσει, δηλαδή θα αρχίσει να μετράει και πάλι από το μηδέν με από 70 περίπου λεπτά. Σε πλακέτες Arduino χρονισμένες στα 16 MHz (π.χ. Duemilanove και Nano), αυτή η συνάρτηση έχει ανάλυση 4 εκατομμυριοστών του δευτερολέπτου (δηλαδή η τιμή που επιστρέφεται είναι πάντα πολλαπλάσιο του τέσσερα). Σε πλακέτες χρονισμένες στα 8MHz (όπως το LilyPad), η συνάρτηση έχει ανάλυση στα 8 εκατομμυριοστά του δευτερολέπτου. Σημείωση

Υπάρχουν 1.000 εκατομμυριοστά του δευτερολέπτου σε ένα χιλιοστό του δευτερολέπτου και 1.000.000 σε ένα δευτερόλεπτο. Η micros() δεν έχει παραμέτρους και επιστρέφει τον αριθμό των διανυθέντων εκατομμυριοστών του δευτερολέπτου από τη στιγμή που ξεκίνησε η εκτέλεση του τρέχοντος προγράμματος σαν τιμή τύπου unsigned long. Παράδειγμα unsigned long time;

void setup() { Serial.begin(9600); }

void loop() { Serial.print("Time: "); time = micros(); // τυπώνει τον διανυθέντα χρόνο από την εκκίνηση // του προγράμματος Serial.println(time); // περιμένει ένα δευτερόλεπτο ώστε να μην // αποστέλλονται πολλά δεδομένα delay(1000); }

40


delay()

Σταματά την εκτέλεση του προγράμματος για όσο χρόνο (σε χιλιοστά του δευτερολέπτου) ορίζει η παράμετρός της. (Υπάρχουν 1.000 χιλιοστά σε κάθε δευτερόλεπτο.) Η σύνταξη της εντολής είναι: delay(ms)

όπου, ms είναι ο αριθμός των χιλιοστών του δευτερολέπτου που θα διακοπεί η εκτέλεση του προγράμματος (unsigned long).

Η delay() δεν επιστρέφει καμία τιμή.

Παράδειγμα int ledPin = 13; // ένα LED έχει συνδεθεί στην ψηφιακή // ακίδα 13

void setup() { pinMode(ledPin, OUTPUT); // ορίζει την ψηφιακή ακίδα // σαν εξόδου } void loop() { digitalWrite(ledPin, HIGH); // ανάβει το delay(1000); // περιμένει digitalWrite(ledPin, LOW); // σβήνει το delay(1000); // περιμένει }

LED ένα δευτερόλεπτο LED ένα δευτερόλεπτο

Ανακοπή

Οσο εύκολο είναι να αναβοσβήσουμε ένα LED με τη λειτουργία delay() - και πολλά προγράμματα τη χρησιμοποιούν για μικρές καθυστερήσεις, - η χρήση της delay() σε ένα πρόγραμμα έχει σημαντικά μειονεκτήματα. Καμία ανάγνωση από τους αισθητήρες, κανένας μαθηματικός υπολογισμός ή χειρισμός ακίδας δε μπορεί να γίνει όσο διαρκεί η ανακοπή εκτέλεσης που δημιουργεί η delay(). Για εναλλακτικές προσεγγίσεις του ελέγχου του προγράμματός μας και των χρονικών καθυστερήσεων μπορεί να χρησιμοποιηθεί η συνάρτηση millis(). Πολλοί προγραμματιστές συνήθως αποφεύγουν τη χρήση της delay() για τον χρονισμό γεγονότων για διάρκειες μεγαλύτερες των 10 χιλιοστών του δευτερολέπτου εκτός και αν ο κώδικας είναι πολύ απλός.

Πάντως, όσο η λειτουργία delay() έχει ανακόψει την εκτέλεση του προγράμματος, κάποια πράγματα συνεχίζουν να εξελίσσονται επειδή αυτή η λειτουργία δεν απενεργοποιεί τις διακοπές (interrupts). Η σειριακή επικοινωνία που εμφανίζεται στο pin RX καταγράφεται, η ευροπαλμική διαμόρφωση (PWM) της analogWrite() διατηρείται, και οι διακοπές (interrupts) θα δουλέψουν όπως θα έπρεπε.

41


delayMicroseconds()

Σταματά την εκτέλεση του προγράμματος για όσο χρόνο (σε εκατομμυριοστά του δευτερολέπτου) ορίζει η παράμετρός της. Υπάρχουν 1.000 εκατομμυριοστά του δευτερολέπτου σε ένα χιλιοστό του δευτερολέπτου και 1.000.000 σε ένα δευτερόλεπτο.

Προς το παρόν, που θα θέσει μια ακριβή καθυστέρηση είναι 16383 εκατομμυριοστά του δευτερολέπτου. Για καθυστέρηση μεγαλύτερη από μερικές χιλιάδες μικροδευτερόλεπτα, θα πρέπει να χρησιμοποιήσετε την delay(). Οπως και στην delay() οι διακοπές (interrupts) συνεχίζουν να εκτελούνται κανονικά. Η σύνταξη της εντολής είναι:

delayMicroseconds(us)

όπου, us είναι ο αριθμός των μικροδευτερολέπτων για διακοπή εκτέλεση του προγράμματος (unsigned int). Η delayMicroseconds() δεν επιστρέφει καμία τιμή.

Παράδειγμα int outPin = 8;

// ψηφιακή ακίδα 8

void setup() { pinMode(outPin, OUTPUT); // ορίζει την ακίδα ως εξόδου } void loop() { digitalWrite(outPin, HIGH); delayMicroseconds(50); digitalWrite(outPin, LOW); delayMicroseconds(50); }

// // // //

ανάβει την ακίδα περιμένει για 50 μseconds σβήνει την ακίδα περιμένει για 50 μseconds

Ρυθμίζει την ακίδα 8 να δουλέψει ως εξόδου. Στέλνει μια ακολουθία παλμών με περίοδο 100 μικροδευτερόλεπτα. Προειδοποιήσεις

Αυτή η λειτουργία είναι πολύ ακριβής για 3 μικροδευτερόλεπτα και πάνω. Δεν μπορούμε να είμαστε σίγουροι ότι η delayMicroseconds() θα εκτελεστεί επακριβώς για μικρότερη τιμή καθυστέρησης.

42


Μαθηματικά

Οι μαθηματικές συναρτήσεις που διατίθενται στο προγραμματιστικό περιβάλλον του Arduino έχουν σχέση με την μικρότερη και μεγαλύτερη τιμή μεταξύ δύο τιμών με χρήση των συναρτήσεων min() και max(), την απόλυτη τιμή ενός αριθμού με την abs(), της τετραγωνικής ρίζας με την sqrt() και την ύψωση σε δύναμη ενός αριθμού βάσης pow(). Επιπλέον διατίθενται δύο συναρτήσεις που χρησιμοποιούνται αρκετά συχνά στον έλεγχο των τιμών των αισθητήρων, ώστε να περιορίσουμε την τιμή που διαβάζεται από έναν αισθητήρα σε κάποιο αποδεκτό διάστημα τιμών με τη constrain() και να υπολογίσουμε μια τιμή σε διαφορετική κλίμακα απ’ ότι χρησιμοποιούμε με την map().

min(x,y)

Υπολογίζει τον μικρότερο από δυο αριθμούς. Η σύνταξη της συνάρτησης είναι: min(x,y)

όπου, x είναι ο πρώτος αριθμός, οποιουδήποτε τύπου δεδομένων και y είναι ο δεύτερος αριθμός, οποιουδήποτε τύπου και αυτός.

Η min(x,y) επιστρέφει τον μικρότερο από τους δυο αριθμούς. Παράδειγμα sensVal = min(sensVal, 100); // // // // //

εκχωρεί στη sensVal τη μικρότερη τιμή από τις sensVal και 100 διασφαλίζοντας ότι ποτέ δε θα πάρει τιμή μεγαλύτερη από 100

Σημείωση

Η min() χρησιμοποιείται για να περιορίσει το ανώτερο άκρο του φάσματος τιμών που επιτρέπεται σε μια εφαρμογή. Προειδοποίηση

Λόγω του τρόπου που υλοποιείται η συνάρτηση min(), αποφύγετε τη χρήση άλλων λειτουργιών μέσα στην παρένθεση, γιατί μπορεί να οδηγήσει σε λανθασμένα αποτελέσματα. min(a++, 100); // αποφύγετέ το γιατί μπορεί να οδηγήσει // σε λανθασμένα αποτελέσματα a++; min(a, 100);

// χρησιμοποιήσετε αυτό, κρατώντας τα // μαθηματικά έξω από τη συνάρτηση

43


max(x,y)

Υπολογίζει τον μεγαλύτερο από δυο αριθμούς. Η σύνταξη της συνάρτησης είναι: max(x,y)

όπου, x είναι ο πρώτος αριθμός, οποιουδήποτε τύπου δεδομένων και y είναι ο δεύτερος αριθμός, οποιουδήποτε τύπου και αυτός.

Η max(x,y) επιστρέφει τον μεγαλύτερο από τους δυο αριθμούς. Παράδειγμα sensVal = max(sensVal, 20); // // // // //

εκχωρεί στη sensVal τη μεγαλύτερη τιμή από τις sensVal και 20 διασφαλίζοντας ότι ποτέ δε θα πάρει τιμή μικρότερη από 20

Σημείωση

Η max() χρησιμοποιείται για να θέσει το κατώτερο άκρο του φάσματος τιμών που επιτρέπεται σε μια εφαρμογή. Προειδοποίηση

Λόγω του τρόπου που υλοποιείται η συνάρτηση max(), αποφύγετε τη χρήση άλλων λειτουργιών μέσα στην παρένθεση, γιατί μπορεί να οδηγήσει σε λανθασμένα αποτελέσματα. max(a--, 20); // αποφύγετέ το γιατί μπορεί να οδηγήσει // σε λανθασμένα αποτελέσματα a--; max(a, 20);

44

// χρησιμοποιήσετε αυτό, κρατώντας τα // μαθηματικά έξω από τη συνάρτηση


abs(x)

Υπολογίζει την απόλυτη τιμή ενός αριθμού. Η σύνταξη της συνάρτησης είναι: abs(x)

όπου, x είναι ένας αριθμός.

Η abs(x) επιστρέφει την τιμή x εφόσον ο αριθμός είναι μεγαλύτερος ή ίσος του μηδενός, την τιμή –x αν ο αριθμός είναι αρνητικός. Προειδοποίηση

Λόγω του τρόπου που υλοποιείται η συνάρτηση abs(), αποφύγετε τη χρήση άλλων λειτουργιών μέσα στην παρένθεση, γιατί μπορεί να οδηγήσει σε λανθασμένα αποτελέσματα. abs(a++); // αποφύγετέ το γιατί μπορεί να οδηγήσει // σε λανθασμένα αποτελέσματα a++; abs(a);

// χρησιμοποιήσετε αυτό, κρατώντας τα μαθηματικά // έξω από τη συνάρτηση

constrain(x, a, b)

περιορίζει έναν αριθμό ανάμεσα σε άλλους δύο. Η σύνταξη της συνάρτησης είναι: constrain(x, a, b)

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

Παράδειγμα sensVal = constrain(sensVal, 10, 150); // περιορίζει το εύρος των τιμών ενός αισθητήρα // στην περιοχή τιμών μεταξύ 10 και 150

45


map(value, fromLow, fromHigh, toLow, toHigh)

Ανάγει μια τιμή από μια περιοχή τιμών σε ανάλογη τιμή άλλης περιοχής τιμών. Η σύνταξη της συνάρτησης είναι:

map(value, fromLow, fromHigh, toLow, toHigh)

όπου value είναι η τιμή της οποίας θέλουμε να πάρουμε την αντίστοιχή της σε άλλη κλίμακα τιμών, fromLow η κατώτερη τιμή της αρχικής κλίμακας, fromHigh η ανώτερη τιμή της αρχικής κλίμακας, toLow είναι η κατώτερη τιμή της νέας κλίμακας και toHigh η ανώτερη τιμή της νέας κλίμακας.

Επιστρέφει την τιμή που αναλογεί στην τιμή της value καθώς αυτή ανάγεται από μια περιοχή τιμών σε μια νέα περιοχή τιμών.

Δεν περιορίζει τις τιμές μέσα στην περιοχή που χρησιμοποιούμε, αλλά η συνάρτηση constrain() μπορεί να χρησιμοποιηθεί είτε πριν είτε μετά από αυτή τη λειτουργία, εάν είναι επιθυμητό να περιορίσουμε τα όρια των τιμών. Σημειώστε ότι τα κατώτερα όρια και των δυο περιοχών μπορεί να είναι μεγαλύτερα ή μικρότερα από τα ανώτερα όρια ώστε η αλλαγή κλίμακας να χρησιμοποιηθεί για την αντιστροφή ενός αριθμού στα όρια της περιοχής. Παράδειγμα y = map(x, 1, 50, 50, 1);

Η συνάρτηση χειρίζεται επίσης και αρνητικούς αριθμούς το ίδιο καλά. Παράδειγμα y = map(x, 1, 50, 50, -100);

είναι αποδεκτή και επίσης δουλεύει σωστά. Η συνάρτηση map() χρησιμοποιεί ακέραια μαθηματικά και έτσι δεν θα δημιουργήσει δεκαδικά όταν οι πράξεις είναι τέτοιες που προοιωνίζουν ότι θα δημιουργηθούν. Τα δεκαδικά υπόλοιπα περικόπτονται, και δεν στρογγυλεύονται σε γειτονικές τιμές. Παράδειγμα /* Αλλάζει κλίμακα μια αναλογικής τιμής σε κλίμακα 8 bits (0 έως 255) */ void setup() {}

void loop() { int val = analogRead(0); val = map(val, 0, 1023, 0, 255); analogWrite(9, val); }

46


Παράρτημα

Από μαθηματική σκοπιά, εδώ περιγράφεται η όλη συνάρτηση long map(long x, long in_min, long in_max, long out_min, long out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; }

pow(base, exponent)

Υπολογίζει την τιμή ενός αριθμού υψωμένο σε κάποια δύναμη.

Η pow() μπορεί να χρησιμοποιηθεί για να υψώσουμε ένα αριθμό σε κλασματική δύναμη. Αυτό είναι χρήσιμο για την παραγωγή πίνακα εκθετικών τιμών ή καμπύλες. Η σύνταξη της συνάρτησης είναι: pow(base, exponent)

όπου, base είναι ο αρθμός βάσης (float), exponent είναι η δύναμη στην οποία υψώνεται η βάση (float) Η pow() επιστρέφει το αποτέλεσμα της ύψωσης σε δύναμη (double).

sqrt(x)

Υπολογίζει την τετραγωνική ρίζα ενός αριθμού. Η σύνταξη της συνάρτησης είναι: sqrt(x)

όπου x είναι ο αριθμός του οποίου θέλουμε να υπολογίσουμε την τετραγωνική ρίζα.

Επιστρέφει την τετραγωνική ρίζα ενός αριθμού (double).

47


Τριγωνομετρία

Για τον υπολογισμό των τριγωνομετρικών τιμών του ημιτόνου, συνημιτόνου και εφαπτομένης, στο Arduino χρησιμοποιούνται οι συναρτήσεις sin(), cos() και tan().

sin(rad)

Υπολογίζει το ημίτονο μιας γωνίας εκφρασμένης σε ακτίνια. Το αποτέλεσμα θα είναι μεταξύ -1 και 1. Η σύνταξη της συνάρτησης είναι: sin(rad)

όπου, rad είναι η γωνία σε ακτίνια (float).

Επιστρέφει το ημίτονο της γωνίας (double).

cos(rad)

Υπολογίζει το συνημίτονο μιας γωνίας εκφρασμένης σε ακτίνια. Το αποτέλεσμα θα είναι μεταξύ -1 και 1. Η σύνταξη της συνάρτησης είναι: cos(rad)

όπου, rad είναι η γωνία σε ακτίνια (float).

Επιστρέφει το συνημίτονο της γωνίας (double).

tan(rad)

Υπολογίζει την εφαπτομένη μιας γωνίας εκφρασμένης σε ακτίνια. Το αποτέλεσμα θα είναι μεταξύ –άπειρο (-∞) και +άπειρο (+∞). Η σύνταξη της συνάρτησης είναι:

tan(rad)

όπου, rad είναι η γωνία σε ακτίνια (float).

Επιστρέφει την εφαπτομένη της γωνίας (double).

48


Τυχαίοι Αριθμοί

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

randomSeed(seed)

Αρχικοποιεί την ψευδο-τυχαία γεννήτρια αριθμών, με αποτέλεσμα να ξεκινήσει η παραγωγή τυχαίων αριθμών από ένα αυθαίρετο σημείο με τυχαία αλληλουχία τιμών. Αυτή η αλληλουχία, ενώ είναι τυχαία για μακρύ σύνολο τιμών, ωστόσο είναι πάντα η ίδια.

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

Αντιστρόφως, μπορεί περιστασιακά να είναι χρήσιμο να χρησιμοποιηθούν ψευδο-τυχαίες ακολουθίες αριθμών που επαναλαμβάνονται με ακρίβεια. Αυτό μπορεί να επιτευχθεί με την κλήση της randomSeed(), με παράμετρο ένα σταθερό αριθμό, πριν από την έναρξη της λήψης τυχαίας αλληλουχίας αριθμών. Η σύνταξη της συνάρτησης είναι: randomSeed(seed)

όπου, seed είναι ένας αριθμός (long ή int) που αρχικοποιεί τη γεννήτρια τυχαίων αριθμών. Η randomSeed() δεν επιστρέφει καμία τιμή. Παράδειγμα long randNumber;

void setup(){ Serial.begin(9600); randomSeed(analogRead(0)); } void loop(){ randNumber = random(300); Serial.println(randNumber); delay(50); }

49


random()

Η συνάρτηση random() παράγει ψευδο-τυχαίους αριθμούς. Η σύνταξη της συνάρτησης είναι: random(max)

random(min, max)

όπου, max είναι το ανώτερο όριο της τυχαίας τιμής που παράγεται (υποχρεωτική τιμή) και min η μικρότερη τιμή της παραγόμενης τυχαίας τιμής (προαιρετική). Επιστρέφει έναν τυχαίο αριθμό μεταξύ min και max-1 (τύπου δεδομένων long).

Σημείωση

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

Αντιστρόφως, μπορεί περιστασιακά να είναι χρήσιμο να χρησιμοποιηθούν ψευδο-τυχαίες ακολουθίες αριθμών που επαναλαμβάνονται με ακρίβεια. Αυτό μπορεί να επιτευχθεί με την κλήση της randomSeed(), με παράμετρο ένα σταθερό αριθμό, πριν από την έναρξη της λήψης τυχαίας αλληλουχίας αριθμών. Παράδειγμα long randNumber;

void setup(){ Serial.begin(9600); // αν η αναλογική ακίδα 0 δεν είναι συνδεμένη, ο τυχαίος // θόρυβος της αναλογικής εισόδου είναι ο λόγος που η // κλήση της randomSeed() θα αναπαράγει διαφορετικές // ακολουθίες τιμών κάθε φορά που εκτελείται εκ νέου το // πρόγραμμα. randomSeed(analogRead(0)); } void loop() { // εκτύπωση ενός τυχαίου αριθμού στο διάστημα 0 έως 299 randNumber = random(300); Serial.println(randNumber);

}

// εκτύπωση ενός τυχαίου αριθμού στο διάστημα 10 έως 19 randNumber = random(10, 20); Serial.println(randNumber); delay(50);

50

Σημειώσεις arduino kales  
Advertisement