Intern EEPROM

Bijna elke Arduino heeft EEPROM aan board. EEPROM (Electronically Erasable Programmable Read-Only Memory) is geheugen dat wel te veranderen is, maar behouden blijft als de spanning weg valt. Ideaal dus voor het opslaan van gegevens die je later wilt verwerken. De hoeveelheid EEPROM geheugen is niet erg groot. Hieronder zie je een lijstje met gegevens hierover: Je kunt er heel gemakkelijk achter komen hoeveel EEPROM geheugen op jouw Arduino aanwezig is met de uitstekende bibliotheek die hiervoor beschikbaar is: Serial.println(EEPROM.length());.

Een nadeel van het interne EEPROM kan zijn dat als je de Arduino voor allerlei projecten gebruikt, het ene programma de data van het andere programma wist. Een ander nadeel kan zijn dat je te weinig intern geheugen hebt. In zo'n geval kun je in plaats van intern EEPROM een EEPROM geheugenchip aansluiten op je Arduino. Die kun je vervolgens weer op andere Arduino's aansluiten zonder verlies aan gegevens. Een voordeel van het intern geheugen is, is dat het erg snel is omdat er geen protocol als I²C of SPI nodig is. Een ander voordeel is dat er een standaard bibliotheek aanwezig is, met bijzonder handige functies: EEPROM.h. Deze bibliotheek is vooral zo handig omdat hij elk type data, zelf door jouw verzonnen type, in een keer kan wegschrijven en teruglezen. Hiervoor gebruik je de functies put() en get().
Ik volsta met het geven van een voorbeeld: het bewaren van een scorelijst. Hiervoor heb ik een simpel programma gemaakt, waarbij je niets anders nodig hebt dan de Arduino, aangesloten op je computer. Voor een beschrijving van dit spel en uitleg over het gebruik van de Serial.read() functie verwijs ik naar een andere pagina, maar let op: de sketch die daar staat werkt met een externe geheugenchip. Ik heb dit getest met de Arduino Pro Micro.


Het programma in actie
// Demonstratie scorelijst met intern EEPROM. Voor EEPROM met 512 bytes of meer.
#include <EEPROM.h>
#define MaxLetters 32 // Maximale lengte van de opgegeven namen (kies 45 of lager)
#define Afstand 50 // Zo passen 10 namen plus scores in EEPROM van 512 bytes. 
char Naam[MaxLetters + 1]; // Hierin worden de namen tijdelijk opgeslagen
byte aantalScores; // Houdt bij hoeveel scores er in EEPROM zijn opgeslagen

void bewaarScore(long score) { // Bij gelijke scores komt de laatste onderaan te staan
  int invoeg = 0;
  if (aantalScores > 0) {
    for (int i = 0; i < aantalScores; i++) {
      long inscore; EEPROM.get(1 + i * Afstand, inscore);
      if ( score >= inscore ) invoeg = i + 1; else break; // invoeg is laatste waarvan de score kleiner is dan de huidige score
    }
    int laatste;
    if (aantalScores < 10) laatste = aantalScores - 1; else laatste = 9;
    for (int i = laatste; i >= invoeg; i--) { // Laatste wordt overschreven
      int start = 1 + i * Afstand; long inscore;
      EEPROM.get(start, inscore); EEPROM.get(start + 4, Naam);
      EEPROM.put(start + Afstand, inscore); EEPROM.put(start + Afstand + 4, Naam);
    }
  }
  int adres = 1 + invoeg * Afstand;
  EEPROM.put(adres, score);
  adres += 4;
  Serial.println("Zend nu je naam via de seriële monitor.");
  while (!Serial.available()); // Wacht tot er iets in ingetypt
  byte teller = 0;
  while (Serial.available()) {
    delay(5);
    char r = Serial.read();
    if ( (r == '\n') || (r == '\r') ) continue; // verwijder eventuele CR of LF
    if ( teller < MaxLetters ) {
      EEPROM.put(adres, r); // Te lange namen worden afgekapt
      adres++; teller++;
    }
  }
  EEPROM.put(adres, 0); // Eindig string met een 0 karakter
  if (aantalScores < 10) {
    aantalScores++;
    EEPROM.put(0, aantalScores);
  }
}

void toonScores() {
  Serial.println("Nr  Score      Naam");
  for (int i = 0; i < aantalScores; i++) {
    Serial.print(i + 1); if (i < 9) Serial.print(" "); Serial.print("  ");
    int adres = i * Afstand + 1;
    long score; EEPROM.get(adres, score);
    Serial.print(score);
    for (int k = floor(log10(score)); k < 10; k++) Serial.print(" "); // Simuleer een tab, werkt beter dan /t
    adres += 4;
    while (true) {
      char c;
      EEPROM.get(adres, c); adres++;
      if (c == 0) break; // De namen zijn immers met een 0-byte afgesloten
      Serial.print(c);
    }
    Serial.println();
  }
}

boolean besteScore(unsigned long score) { // Ga na of de score hoger lager is dan de laagste score
  if (aantalScores > 10) return false;
  if (aantalScores < 10) return true;
  long inscore; EEPROM.get(1 + Afstand * 9, inscore);
  return score < inscore;
}

char lees1() { // Lost problemen op als de Seriële monitor niet is ingesteld op "geen regeleinde"
  while ( !Serial.available() ); // Wacht tot er iets gebeurt
  int r; char c = 0;
  while (Serial.available()) {
    r = Serial.read();
    switch (r) {
      case '\n': break; // nieuwe regel
      case '\r': break; // regeleinde
      default: if (c == 0) c = r & 0xFF; // ik kan geen 0-code verzenden, dus dit gaat goed
    } delay(5); // Zeer belangrijk. Deze waarde kun je eventueel vergroten
  }
  return c;
}

void setup() {
  Serial.begin(9600); while (!Serial); // alleen nodig bij de Leonarde en Pro Micro
  Serial.println("Spelregels");
  Serial.println("De Arduino kiest een letter. Jij moet zo snel mogelijk dezelfde letter verzenden.");
  Serial.println("Na drie keer wordt je score bepaald. De laagste score wint.");
  EEPROM.get(0, aantalScores); if (aantalScores == 255) aantalScores = 0; // EEPROM waarschijnlijk nog niet gebruikt
  if (aantalScores > 10)  {
    Serial.println("WAARSCHUWING: Er staan al andere gegevens in EEPROM.");
    Serial.println("Als die gegevens weg mogen kies dan voor 'nieuwe scorelijst'.");
  }
  char Keuze = ' ';
  if (aantalScores > 0) {
    if (aantalScores <= 10) {
      Serial.println("Huidige scorelijst:");
      toonScores();
    }
    Serial.println("Wis scorelijst? Zend een w. Niet wissen? Zend iets anders.\nHet spel start daarna meteen.");
    Keuze = lees1();
    if (tolower(Keuze) == 'w') {
      EEPROM.put(0, 0);
      aantalScores = 0;
    }
  }
  if (aantalScores == 0) Serial.println("Er zijn geen scores...");
  randomSeed(millis()); // kies willekeurige startwaarde voor de random number generator
}

String Nde[] = {"Eerste", "Tweede", "Derde "};
void loop() {
  unsigned long Score = 0, Start;
  for (int i = 0; i < 3; i++) {
    Serial.print(Nde[i]); Serial.print(" letter komt zo...    "); delay(1500 + random(0, 1000));
    char c = random(byte('a'), byte('z') + 1);
    while (Serial.available()) Serial.read(); // voor als een speler te vroeg heeft geantwoord
    Start = millis();
    Serial.println(c);
    char t = lees1();
    Score += millis() - Start;
    if (t != c) {
      Serial.println("Foute letter teruggezonden: 1 seconde straf");
      Score += 1000;
    }
    Serial.print("Je tijd is "); Serial.println(Score);
  }
  if ( besteScore(Score) ) {
    Serial.println("Je score is in de top tien.\nZend n of N als je dit NIET wilt bewaren en iets anders als je dat wel wil.");
    if (tolower( lees1() ) != 'n') {
      bewaarScore(Score);
      toonScores();
    }
  }
  Serial.println("Spel over. Opnieuw spelen ? [j/n]");
  if (tolower(lees1()) == 'n') while (true); // Blijf hier tot de Arduino gereset wordt
}