Η μνήμη EEPROM του Arduino – Απομνημόνευση της τελευταίας κατάστασης LED

Σ αυτόν τον οδηγό θα εξηγήσουμε τι είναι η EEPROM του Arduino και σε τι χρησιμεύει. Θα σας δουμε επίσης πώς να γράφετε και να διαβάζετε από την EEPROM και θα γράψουμε  ένα παράδειγμα του κώδικα για να εφαρμόσουμε τις έννοιες αυτές που μάθατε στην πράξη.
arduino uno + eeprom memory

Εισαγωγή

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

Τι είναι η EEPROM;

Ο μικροελεγκτής στην πλακέτα Arduino (ATmega328 στην περίπτωση του Arduino UNO) στην παρακάτω εικόνα διαθέτει EEPROM (Electrically Erasable Programmable Read-Only Memory, Ηλεκτρικά Διαγράψιμη Προγραμματιζόμενη Μνήμη Μόνο για ανάγνωση). Πρόκειται για έναν μικρό αποθηκευτικό χώρο που μπορεί να αποθηκεύσει μεταβλητές τύπου byte. Οι μεταβλητές που αποθηκεύονται στην EEPROM παραμένουν εκεί ακόμα και όταν κάνεις επαναφορά (reset) ή διακόψει την τροφοδοσία του Arduino. Με απλά λόγια, η EEPROM είναι μια μόνιμη αποθήκευση, παρόμοια με τον σκληρό δίσκο στους υπολογιστές.

atmega328P-PU
Η EEPROM μπορεί να διαβαστεί, να διαγραφεί και να επανεγγραφεί ηλεκτρονικά. Στο Arduino, μπορείς εύκολα να διαβάζεις και να γράφεις δεδομένα στην EEPROM χρησιμοποιώντας τη βιβλιοθήκη EEPROM.

Πόσα bytes μπορείς να αποθηκεύσεις;

Κάθε θέση της EEPROM μπορεί να αποθηκεύσει ένα byte, πράγμα που σημαίνει ότι μπορείς να αποθηκεύσεις μόνο αριθμούς 8-bit, δηλαδή ακέραιες τιμές από 0 έως 255.

Ο αριθμός των bytes που μπορείς να αποθηκεύσεις στην EEPROM εξαρτάται από τον μικροελεγκτή που διαθέτει η κάθε πλακέτα Arduino. Ρίξε μια ματιά στον παρακάτω πίνακα:

MicrocontrollerEEPROM
ATmega328 (Arduino Uno, Nano, Mini)1024 bytes
ATmega168 (Arduino Nano)512 bytes
ATmega2560 (Arduino Mega)4096 bytes

Ωστόσο, εάν χρειάζεται να αποθηκεύσετε περισσότερα δεδομένα, μπορείτε να προμηθευτείτε μια εξωτερική EEPROM.

Η διάρκεια ζωής της EEPROM είναι περιορισμένη

Η EEPROM έχει περιορισμένη διάρκεια ζωής.Στο Arduino, η EEPROM έχει 100.000 κύκλους εγγραφής/διαγραφής για κάθε θέση.Ωστόσο, η ανάγνωση των δεδομένων είναι απεριόριστη.Αυτό σημαίνει ότι μπορείτε να διαβάσετε από την EEPROM όσες φορές θέλετε χωρίς να επηρεάζεται η διάρκεια ζωής της.

Η εφαρμογή στο Arduino – Απομνημόνευση τελευταίας κατάστασης

Η EEPROM είναι χρήσιμη σε εφαρμογές με Arduino που πρέπει να διατηρούν δεδομένα ακόμα και όταν το Arduino επανεκκινείται ή όταν διακοπεί η τροφοδοσία. Είναι ιδιαίτερα χρήσιμο να θυμόμαστε την τελευταία κατάσταση μιας μεταβλητής ή να θυμόμαστε πόσες φορές ενεργοποιήθηκε μια συσκευή. Για παράδειγμα, φανταστείτε το ακόλουθο σενάριο:

  • Ελέγχετε ένα LED με το Arduino σας και το LED  είναι αναμμένο.
  • Το Arduino χάνει ξαφνικά την τροφοδοσία.
  • Όταν επανέλθει η τροφοδοσία, το LED θα παραμείνει σβηστό – δεν διατηρεί την τελευταία του αλλαγή.

Δεν θέλουμε να συμβεί αυτό. Θέλουμε το Arduino να θυμάται τι συνέβαινε πριν χάσει την τροφοδοσία και να ξεκινήσει με την τελευταία αποθηκευμένη κατάσταση.
Το πρόβλημα της κατάστασης του LED μετά από την επανεκκίνηση του arduino

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

Ανάγνωση και Εγγραφή

Μπορείτε εύκολα να διαβάσετε και να γράψετε στην EEPROM χρησιμοποιώντας τη βιβλιοθήκη EEPROM.

Για να προσθέσετε τη βιβλιοθήκη EEPROM:

#include < EEPROM.h > ;

Εγγραφή (Write)

Για να γράψετε δεδομένα στην EEPROM, χρησιμοποιείτε τη συνάρτηση EEPROM.write(), η οποία δέχεται δύο ορίσματα. Το πρώτο είναι η θέση ή διεύθυνση της EEPROM όπου θέλετε να αποθηκεύσετε τα δεδομένα και το δεύτερο είναι η τιμή που θέλετε να αποθηκεύσετε:

EEPROM.write(address, value);

Για παράδειγμα, για να γράψετε την τιμή 9 στη διεύθυνση 0:

EEPROM.write(0, 9);

Ανάγνωση (Read)

Για να διαβάσετε ένα byte από την EEPROM, χρησιμοποιείτε τη συνάρτηση EEPROM.read(). Αυτή η συνάρτηση δέχεται ως όρισμα τη διεύθυνση του byte.

EEPROM.read(address);

Για παράδειγμα, για να διαβάσετε το byte που αποθηκεύτηκε προηγουμένως στη διεύθυνση 0:

EEPROM.read(0);

Αυτό θα επιστρέψει 9, που είναι η τιμή που είναι αποθηκευμένη σε εκείνη τη θέση.

Ενημέρωση τιμής (Update a value)

Η συνάρτηση EEPROM.update() είναι ιδιαίτερα χρήσιμη. Γράφει στην EEPROM μόνο αν η νέα τιμή είναι διαφορετική από αυτή που είναι ήδη αποθηκευμένη.

Καθώς η EEPROM έχει περιορισμένη διάρκεια ζωής λόγω περιορισμένου αριθμού κύκλων εγγραφής/διαγραφής, η χρήση της EEPROM.update() αντί της EEPROM.write() εξοικονομεί κύκλους εγγραφής.

Η χρήση της EEPROM.update() γίνεται ως εξής:

EEPROM.update(address, value);

Αυτή τη στιγμή, έχουμε την τιμή 9 αποθηκευμένη στη διεύθυνση 0. Επομένως, αν καλέσουμε:

EEPROM.update(0, 9);

Δεν θα γίνει νέα εγγραφή στην EEPROM, επειδή η αποθηκευμένη τιμή είναι ίδια με αυτή που θέλουμε να γράψουμε.

Παράδειγμα: Arduino που «θυμάται» την αποθηκευμένη κατάσταση LED

Σε αυτό το παράδειγμα, θα δουμε πώς μπορούμε να κάνουμε το Arduino να θυμάται την αποθηκευμένη κατάσταση ενός LED, ακόμα και όταν κάνουμε επανεκκίνηση (reset) στο Arduino ή όταν διακοπεί η τροφοδοσία.

Η παρακάτω εικόνα δείχνει τι πρόκειται να υλοποιήσουμε:
led state eepropm

Εξαρτήματα που θα χρειαστούμε για το παράδειγμα

  • Arduino UNO
  • 1x LED
  • 1x αντίσταση 220Ω
  • 1x κουμπί
  • 1x αντίσταση 1kΩ
  • 1x πλακέτα breadboard
  • Καλώδια jumper

Το σχηματικό διάγραμμα

Ακολουθεί τα σχηματικά κυκλώματος για αυτό το project. Εδώ είναι απλώς ένα κουμπί που ενεργοποιεί και απενεργοποιεί μια λυχνία LED.
arduino uno, button, led, resistors, cables

Γράφουμε τον κώδικα

#include < EEPROM.h > ;   // Βιβλιοθήκη για αποθήκευση δεδομένων στη μνήμη EEPROM
// Ορισμός pin
const int buttonPin = 8;    // Pin στο οποίο είναι συνδεδεμένο το κουμπί
const int ledPin = 4;       // Pin στο οποίο είναι συνδεδεμένο το LED

// Μεταβλητές κατάστασης
int ledState;   // Μεταβλητή που αποθηκεύει την κατάσταση του LED (HIGH ή LOW)
int buttonState;  // Η τρέχουσα κατάσταση του κουμπιού
int lastButtonState = LOW;   // Η προηγούμενη κατάσταση του κουμπιού
// Οι μεταβλητές τύπου long χρησιμοποιούνται γιατί ο χρόνος σε ms
// μπορεί να ξεπεράσει το όριο του int.
long lastDebounceTime = 0;  // Η τελευταία στιγμή που άλλαξε η κατάσταση
long debounceDelay = 50;    // Χρόνος αποθορυβοποίησης (debounce) σε ms

void setup() {
  // Ορισμός κατεύθυνσης pin
  pinMode(buttonPin, INPUT);    // Το κουμπί ως είσοδος
  pinMode(ledPin, OUTPUT);      // Το LED ως έξοδος

  // Θέτουμε την αρχική κατάσταση του LED
 digitalWrite(ledPin, ledState);
 // Εκκίνηση σειριακής επικοινωνίας
  Serial.begin (9600);
  // Έλεγχος της αποθηκευμένης κατάστασης LED από την EEPROM  checkLedState(); 
}
void loop() {
  // Διαβάζουμε την τρέχουσα κατάσταση του κουμπιού
 int reading = digitalRead(buttonPin);  

// Αν η ένδειξη άλλαξε από την προηγούμενη φορά
 if(reading != lastButtonState) {
    // Μηδενίζουμε τον χρονομετρητή debounce
    lastDebounceTime = millis();
  }

  // Αν έχει περάσει ο χρόνος debounce
  if((millis() - lastDebounceTime) > debounceDelay) {
// Αν η κατάσταση του κουμπιού έχει πραγματικά αλλάξει
    if(reading != buttonState) {
buttonState = reading;
     // Αν το κουμπί πατήθηκε (HIGH)
 if(buttonState == HIGH) {

 // Αντιστρέφουμε την κατάσταση του LED
ledState = !ledState;
      }
    }
  }
// Ενημερώνουμε το LED με τη νέα κατάσταση 
digitalWrite(ledPin, ledState);
// Αποθηκεύουμε την κατάσταση του LED στη θέση 0 της EEPROM
// Η EEPROM.update γράφει μόνο αν η τιμή αλλάξει (προστασία φθοράς μνήμης)
  EEPROM.update(0, ledState); // Αποθηκεύουμε την τρέχουσα ένδειξη ως προηγούμενη 
lastButtonState = reading;
}
// Συνάρτηση που εκτελείται στην εκκίνηση
// Διαβάζει την αποθηκευμένη κατάσταση LED από την EEPROM
void checkLedState() {
   Serial.println("LED status after restart: ");

   // Διαβάζουμε από τη θέση 0 της EEPROM
   ledState = EEPROM.read(0);
   if(ledState == 1) {
    Serial.println ("ON");
    digitalWrite(ledPin, HIGH);
   } 
if(ledState == 0) {
    Serial.println ("OFF");
    digitalWrite(ledPin, LOW);
   }
}

Ο κώδικας αυτός αλλάζει την κατάσταση του LED κάθε φορά που πατάτε το κουμπί. Θυμάται την αποθηκευμένη κατάσταση του LED, ακόμα και μετά την επαναφορά ή την ενεργοποίηση του Arduino. Βασικά, αποθηκεύουμε την τρέχουσα κατάσταση του LED στη μεταβλητή ledState και την αποθηκεύουμε στην EEPROM με την ακόλουθη γραμμή:

EEPROM.update(0,ledState);

Στην αρχή του κώδικα στην setup(), ελέγχουμε το ledState που είναι αποθηκευμένο στην EEPROM και ρυθμίζουμε το led να ενεργοποιείται ή να απενεργοποιείται ανάλογα με αυτήν την κατάσταση όταν επανεκκινούμε το πρόγραμμα. Αυτό το κάνουμε με μια συνάρτηση που έχουμε δημιουργήσει στο τέλος του κώδικα, την checkLedState().

// Συνάρτηση που ελέγχει και επαναφέρει την κατάσταση του LED
// μετά από επανεκκίνηση ή τροφοδοσία του Arduino

void checkLedState() {
// Εκτυπώνει μήνυμα στο Serial Monitor
Serial.println("LED status after restart: ");
// Διαβάζει από τη θέση 0 της EEPROM
// Εκεί έχει αποθηκευτεί η τελευταία κατάσταση του LED (0 ή 1)
ledState = EEPROM.read(0);

// Αν η αποθηκευμένη τιμή είναι 1 (HIGH)
if (ledState == 1) {
// Εμφανίζει στο Serial Monitor ότι το LED είναι αναμμένο
Serial.println("ON");

// Θέτει το LED σε κατάσταση HIGH (αναμμένο)
digitalWrite(ledPin, HIGH);
}

// Αν η αποθηκευμένη τιμή είναι 0 (LOW)
if (ledState == 0) {

// Εμφανίζει στο Serial Monitor ότι το LED είναι σβηστό
Serial.println("OFF");

// Θέτει το LED σε κατάσταση LOW (σβηστό)
digitalWrite(ledPin, LOW);
}
}

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