Προγραμματισμός στο Arduino για αρχάριους, μέρος 1ο

Arduino Programming

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

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

Μιας και είναι το 1o μέρος, Θα προσπαθήσω να περιγράψω κάποιες πολύ πολύ βασικές τεχνικές στον προγραμματισμό στο Arduino (π.χ. sampling, averaging χρησιμοποιώντας conditions/loops), καθώς και στα μικροηλεκτρονικά.

 

Περί Arduino

Μία πολύ σύντομη εισαγωγή στο Arduino, από το οποίο θέλω να κρατήσουμε 3 βασικά πράγματα. Τα αναλογικά Pins, τα ψηφιακά Pins, καθώς και την τροφοδοσία που μας παρέχει (5V, 3,3V, GND).

Τα αναλογικά Pins, χάρη στο γεγονός ότι υποστηρίζουν ADC (μετατροπή από αναλογικό σε ψηφιακό), τα χρησιμοποιούμε συνήθως ως εισόδους στο Arduino. Μπορούν να διαβάσουν τάση εισόδου από 0-5V, και να την μετατρέψουν σε "μέτρηση" από 0 μέχρι 1023, χρησιμοποιώντας το analogRead().

Τα ψηφιακά Pins από την άλλη, αν χρησιμοποιηθούν ως είσοδοι, καταλαβαίνουν μόνο 2 καταστάσεις: HIGH και LOW.

Τέλος τα +5V, τα +3,3V και το GND τα χρησιμοποιούμε συχνά για τροφοδοσία εξωτερικών κυκλωμάτων που θα συνδέσουμε στο Arduino.

Arduino Pins

 

Η λογική του συγκεκριμένου Arduino Project

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

 

Το κύκλωμα για το Arduino Project

Το +5V και το GND του Arduino τα οδηγούμε στα οριζόντια power rails της breadboard, και από εκεί τροφοδοτούμε ένα photoresistor σε σειρά με μία αντίσταση. Το σημείο που συνδέονται η δίοδος με το photoresistor αποτελεί το Vout μας, το οποίο οδηγούμε στο A0 του Arduino με το άσπρο καλώδιο.

Επίσης από τα power rails οδηγούμε τα +5V και το GND (κόκκινο και μαύρο καλώδιο) σε ένα Relay το οποίο έχουμε ενσωματώσει μέσα σε ένα πολύμπριζο, ενώ το trigger (πράσινο καλώδιο) το συνδέουμε στο ψηφιακό PIN 2.

Arduino Circuit

Για να εστιάσουμε όμως λίγο στην αντίσταση που θα χρησιμοποιήσουμε, και στην λογική του κυκλώματος που φτιάξαμε:

Arduino Voltage Divider

Σας θυμίζει τίποτα το παραπάνω κύκλωμα? Όχι? Χμ, οκ, αν αντικαταστήσουμε το photoresistor με μία άλλη αντίσταση, και προσέξουμε τα Vin, Vout και GND μήπως τώρα σας θυμίζει κάτι?

Arduino Voltage Divider

Ναι, διαιρέτης τάσης!

Arduino Voltage Divider

 

Ο κώδικας στο Arduino

Κάθε sketch στο arduino περιέχει την void setup(), η οποία τρέχει μία φορά στο boot, και την void loop(), η οποία τρέχει σε επανάληψη όσο παίρνει ρεύμα το Arduino.

Πριν την void setup() θα δηλώσουμε τα PINs που θα χρειαστούμε από το Arduino (στην περίπτωσή μας το Α0, το οποίο θα ονομάσουμε sensorPin, και το 2, το οποίο θα ονομάσουμε lightPin).

const int sensorPin = A0;
const int lightPin = 2;

Μέσα στην void setup θα ορίσουμε αν τα παραπάνω Pins τα χρειαζόμαστε ως εισόδους ή εξόδους, αλλά και ότι θα χρειαστούμε Serial Monitor για Debugging.

void setup() {
  pinMode(sensorPin, INPUT);
  pinMode(lightPin, OUTPUT);
  Serial.begin(9600);
}

Προφανώς το βασικό κομμάτι του κώδικά μας, πάει μέσα στην void loop(). Για να διαβάσουμε από την έξοδο του αισθητήρα, την τιμή τάσης, αρκεί το παρακάτω κομμάτι κώδικα.

void loop() {
  int lightReading;

  lightReading = analogRead(sensorPin);
  Serial.println(lightReading);
  delay (1);
}

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

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

Μέσα στην "for loop" το i μεταβάλλεται από 1 μέχρι 10, καθορίζοντας τον αριθμό της επανάληψης, σε κάθε μία από τις οποίες η μέτρηση lightReading που διαβάζουμε από το sensorPin, προστίθεται στην μεταβλητή lightSum.

Στο τέλος κάθε επανάληψης εισάγαμε ένα μικρό χρονικό delay (1ms) για σταθερότητα, το οποίο ταυτόχρονα είναι ο ρυθμός δειγματοληψίας μας. Αν θέλουμε να κάνουμε δειγματοληψία κάθε 1 λεπτό θα βάζαμε delay(60000).

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

Αφού τελειώσουν οι 10 επαναλήψεις (δηλαδή όταν σταματήσει να ισχύει η συνθήκη i<=10), στη μεταβλητή lightAverage θα βάλουμε το άθροισμα των μετρήσεων διαιρεμένο με τον αριθμό των επαναλήψεων, ώστε να πάρουμε τον μέσο όρο.

void loop() {
  int lightReading;
  int lightAverage;
  int lightSum = 0;
  
  for (int i = 1; i <= 10; i++) {
    lightReading = analogRead(sensorPin);
    lightSum+= lightReading;
    delay(1); //o rithmos deigmatolipsias twn metrisewn. Se monimes efarmoges oriste to 60000
  }

  lightAverage = lightSum / 10;
  delay(1);
}  

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

Παρακολουθώντας το Serial Monitor (Ctrl+Shift+M) έχουμε δει περίπου τι τιμές παίρνει το lightAverage, οπότε ορίζουμε αν αυτή η μεταβλητή πέσω κάτω από μία συγκεκριμένη τιμή, το lighPin να δώσει +5V (το οποίο θα ενεργοποιήσει το Relay που έχουμε συνδέσει στο N-O με το φως μας).

Αντίστοιχα, όταν το φως στο δωμάτιο "ανέβει" πάνω από κάποια τιμή, το lightPin σταματάει να δίνει Voltage, το οποίο σημαίνει ότι το Relay ανοίγει το κύκλωμα και "σβήνει" το φωτιστικό μας.

Και μέσω των "if" μπορούμε να φροντίσουμε να μην "αναβοσβήνει" άσκοπα το φως στις οριακές καταστάσεις, στην συγκεκριμένη περίπτωση το κάναμε αφήνοντας περιθώριο μεταξύ των οριακών τιμών φωτεινότητας: αν το lightAverage πάρει τέτοιες τιμές (400 μέχρι 420) το Arduino διατηρεί την προηγούμενη κατάσταση στο lightPin.

  if (lightAverage < 400) {
    digitalWrite(lightPin, HIGH); //na dwsw 5V sto lightPin
  }
  else if (lightAverage > 420) {
    digitalWrite(lightPin, LOW); //na dwsw 0V sto lightPin
  }

 

Βίντεο του Project μας στο Arduino

 

Τελικός κώδικας για το Arduino

Και ορίστε το τελικό Arduino sketch μας:

const int sensorPin = A0;
const int lightPin = 2;

void setup() {
  pinMode(sensorPin, INPUT);
  pinMode(lightPin, OUTPUT);
  Serial.begin(9600);
}

void loop() {
  int lightReading;
  int lightAverage;
  int lightSum = 0;

  for (int i = 1; i <= 10; i++) {
    lightReading = analogRead(sensorPin);
    lightSum += lightReading;
    delay(1); //o rithmos deigmatolipsias twn metrisewn. Se monimes efarmoges oriste to 60000
  }

  lightAverage = lightSum / 10;
  Serial.println(lightAverage);

  if (lightAverage < 400) {
    digitalWrite(lightPin, HIGH); //na dwsw 5V sto lightPin
  }
  else if (lightAverage > 420) {
    digitalWrite(lightPin, LOW); //na dwsw 0V sto lightPin
  }
  delay (1);
}

aByte