Daten im EEPROM speichern, auslesen und Variablen zuweisen

In diesem Beitrag möchte ich euch zeigen wie man Daten auf dem EEPROM des Arduino speichern und diese auch wieder auslesen kann. Beim dieser Art von Speicher handelt es sich um einen Speicherbereich im Microcontroller, der wie eine Festplatte, seine Daten auch nach dem ausschalten nicht verliert. Je nach Art des Microcontrollers stehen unterschiedliche Speichergrößen zwischen 512 Byte und 4 kByte zur Verfügung. Der AtMega328 den ich auf meinem Arduino-Uno-Board nutze verfügt z.B. über eine Größe von 1024 Bytes. 1024 Bytes hören sich nicht viel an, sind aber absolut ausreichend für die meisten Anwendungsfälle. Für größere Projekte gibt es die Möglichkeit externe Speicherchips zu nutzen.

ArduinoEEPROMPreview

Ich benutze in einem meiner Projekte diesen nicht flüchtigen Speicherbereich dazu, sämtliche Netzwerkeinstellungen, die ich zum betreiben des Ethernet-Shields und den Zugriff auf eine Website darüber benötige. Außerdem speichere ich die DeviceAdressen sämtlicher am Arduino angeschlossenen Temperatur-Sensoren. Diese Daten in diesem Speicherbereich des Micro-Controllers zu speichern bietet den Vorteil, diese Werte z.B. über das Terminal oder wahlweise über ein Webinterface, ändern zu können ohne den Arduino neu programmieren zu müssen.

Bei meiner Recherche zu diesem Thema stieß ich bei playground.arduino.cc auf folgenden Code, den ich für meine Anwendungen nutze. Um mein Projekt übersichtlich zu halten, habe ich den kompletten unten angegebenen Code auf eine eigene Seite ausgelagert. Dies geht in der Arduino IDE über das kleine Dreieck auf der rechten Seite unter der Lupe und einen Klick auf “Neuer Tab”. Diesen Tab habe ich unter EEPROM abgespeichert.

Arduino - Neues Tab

 

 

Arduino - TabName

 

 

Arduino-Sketch

EEPROM_TUTORIAL

In diesem Sketch habe ich zur Veranschaulichung die Ethernet und die DallasTemperature Libraries eingebunden um zu demonstrieren wie ich Netzwerkeinstellungen, sowie auch die DeviceAdressen meiner Temperatursensoren im nichtflüchtigen Speicher abspeichern, diese daraus laden und anschließend Variablen zuweisen kann. Die meisten Zeilen habe ich kommentiert so sollte eigentlich alles klar sein.

 

#include <EEPROM.h>
#include <SPI.h>
#include <Ethernet.h> 
#include <DallasTemperature.h>
#include <OneWire.h>

/***************************************************************************************************************************************************
  AB HIER BITTE DIE DATEN ANPASSEN                                                                                                                 *
***************************************************************************************************************************************************/
#define WRITE_EEPROM false // true = Standardwerte im nichtflüchtigen Speicherbereich schreiben, false nichts tun
OneWire  ds(2);            // Digitaler Pin an denen die Temperatursensoren angeschlosen sind
long interval = 600;       // Interval in Sekunden (Dient als Ersatz für delay damit in der Zeit Befehle ausgeführt werden können) 
const int numSensors=4;    // Anzahl der angeschlossenen Sensoren

/***************************************************************************************************************************************************
  AB HIER BITTE NICHTS MEHR AENDERN                                                                                                                *
***************************************************************************************************************************************************/
byte mac[6];               // MAC-Adresse des Arduino
byte localip[4];           // IP-Adresse des Arduino
byte netmask[4];           // Netzmaske
byte gateway[4];           // Gateway
byte DNS[4];               // DNS
byte serverip[4];          // IP-Adresse des Web-Servers zu dem Daten gesendet werden
char serverhost[30];       // Domain
char serverurl[50];        // Serverurl
char serverkey[10];        // ServerKey
char useragent[10];        // Useragent

EthernetClient client;     // Fur Verbindung zum Server

const int BUFSIZE=50; 
char buffer[BUFSIZE]; 

const int EEPROM_MIN_ADDR = 0;
const int EEPROM_MAX_ADDR = 1024;

DallasTemperature sensors(&ds);   // Dallas Temperatursensoren  
uint8_t Adressen[numSensors][8];  // Array für die DeviceAdressen der Sensoren
float temp = 0.0;                 // Speichert die Temperatur eines Sensors

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

  /*************************************************
    EEPROM INITIALISIEREN                          *
    Wenn EEPROM_WRITE = true dann komplett löschen *
    und Standardwerte darin schreiben              *
    ***********************************************/
  initializeEEPROM();

  /***********************************************
    Werte aus nichtflüchtigem Speicher lesen     *
    Diese Werte den richtigen Variablen zuweisen *
    *********************************************/
  initializeVariables();

  /***********************************************
    Variable "interval" liegt in Sekunden vor.   *
    Diesen Wert in Millisekunden umrechnen.      *
    *********************************************/
  interval = interval * 1000;

  /***********************************************
    Sensoren initialisieren                      *
    *********************************************/
  sensors.begin();
  sensors.requestTemperatures();

  for(int i=0; i<numSensors; i++)
  {
    temp = sensors.getTempC(Adressen[i]);
    Serial.print("Sensor"+String(i)+" = "); 
    Serial.println(temp,1);
  } Serial.println();

  /***********************************************
    Ethernet initialisieren.                     *
    *********************************************/
  delay(1000); 
  Ethernet.begin(mac, localip, dns, gateway, netmask);
}

void loop()
{

}

/****************************************
  Werte auslesen und Variablen zuweisen *
  **************************************/
void initializeVariables()
{
  eeprom_read_string(2, buffer, BUFSIZE);   // MAC-Adresse
  sscanf(buffer,"%2x,%2x,%2x,%2x,%2x,%2x",&mac[0],&mac[1],&mac[2],&mac[3],&mac[4],&mac[5]);
  Serial.println(buffer);

  eeprom_read_string(20, buffer, BUFSIZE);  // Lokale IP
  sscanf(buffer,"%u,%u,%u,%u",&localip[0],&localip[1],&localip[2],&localip[3]); 
  Serial.println(buffer); 

  eeprom_read_string(36, buffer, BUFSIZE);  // SUBNET
  sscanf(buffer,"%u.%u.%u.%u",&netmask[0],&netmask[1],&netmask[2],&netmask[3]);  
  Serial.println(buffer);

  eeprom_read_string(52, buffer, BUFSIZE);  // Gateway
  sscanf(buffer,"%u,%u,%u,%u",&gateway[0],&gateway[1],&gateway[2],&gateway[3]);  
  Serial.println(buffer);

  eeprom_read_string(68, buffer, BUFSIZE);  // DNS
  sscanf(buffer,"%u.%u.%u.%u",&DNS[0],&DNS[1],&DNS[2],&DNS[3]);
  Serial.println(buffer);

  eeprom_read_string(84, buffer, BUFSIZE);  // ServerIP
  sscanf(buffer,"%u,%u,%u,%u",&serverip[0],&serverip[1],&serverip[2],&serverip[3]);
  Serial.println(buffer);

  eeprom_read_string(100, buffer, BUFSIZE); // ServerHost
  sscanf(buffer,"%s",&serverhost);
  Serial.println(buffer);

  eeprom_read_string(130, buffer, BUFSIZE); // ServerURL
  sscanf(buffer,"%s",&serverurl);
  Serial.println(buffer);

  eeprom_read_string(180, buffer, BUFSIZE); // ServerKey
  sscanf(buffer,"%s",&serverkey);
  Serial.println(buffer);

  eeprom_read_string(190, buffer, BUFSIZE); // UserAgent
  sscanf(buffer,"%s",&useragent);
  Serial.println(buffer);

  eeprom_read_string(200, buffer, BUFSIZE); // Interval
  sscanf(buffer,"%d",&interval);
  Serial.println(buffer);

  int point = 205; //Startbereich des ersten Sensors
  for(int i=0; i<numSensors; i++)
  {
    eeprom_read_string(point, buffer, BUFSIZE); //Sensoradressen
    sscanf(buffer,"%2x,%2x,%2x,%2x,%2x,%2x,%2x,%2x",&Adressen[i][0],&Adressen[i][1],&Adressen[i][2],&Adressen[i][3],&Adressen[i][4],&Adressen[i][5],&Adressen[i][6],&Adressen[i][7]);  
    point = point + 25; //nächsten Startbereich berechnen 
  }
}

 

 

Arduino-Sketch

EEPROM

in diesem Sketch habe ich Teile von einem Sketch genutzt das ich auf http://playground.arduino.cc/Code/EepromUtil gefunden habe. Mit diesem Code ist es nämlich möglich Integerwerte und Strings in hoher Geschwindigkeit im EEPROM abzulegen was mit der Standard Library so nicht geht.

Wichtig “initializeEEPROM” ganz unten können Sie die Standardwerte für Ihre Variablen deklarieren die im nichtflüchtigen Speicherbereich des Microcontrollers gespeichert werden sollen wenn das StatusFlag entweder ungleich 49 (0) ist oder  wenn die Variable “WRITE_EEPROM” true ist. Bitte beachten Sie, wenn Sie z.B. für “ServerHost” den Startwert 100 angeben und “ServerURL” den Startwert 130 hat, dass der Text in “ServerHost” dann auf keinen Fall länger als 30 Zeichen sein sollte. Andernfalls wird der Wert in “ServerURL” überschrieben wird.

 

EEPROM-DUMP

Wenn Sie sich die Sketches genau angeschaut haben und die Ausgabe im Bild oben sehen, werden Sie sich sicher fragen was das zu bedeuten hat. Hier eine kurze Erklärung.
An Adresse 0 haben wir ja die Zahl 1 gespeichert, hier steht aber die Zahl 31. Das ist so ganz richtig, denn die Zahl 31 wurde in Hexadezimaler Form gespeichert. Der Hex-Wert 31 steht also für den Ascii-Wert 1. Danach folgen zwei Nullen, diese zwei Nullen erscheinen weil die Mac-Adresse, die als nächstes gespeichert wurde, als Startadresse die 2 bekommen hat und somit Adresse 1 leer ist. Danach haben wir die MAC-Adresse “5A:A2:DA:0D:56:7A” gespeichert. Es gibt einige Rechner mit denen man HEX in ASCII umwandeln kann. Sie können aber auch eine Seite wie “https://de.wikipedia.org/wiki/ASCII” benutzen um zu schauen was welcher Wert bedeutet. Also die 35 steht für die Zahl 5, die Zahl 41 steht für den Buchstaben A usw.

 

/************************************************************
  Funktionen zum lesen und schreiben des EEPROMS            *
  **********************************************************/

// Speicherbereich löschen
void eeprom_erase_all(byte b = 0xFF) {
  int i;
  for (i = EEPROM_MIN_ADDR; i <= EEPROM_MAX_ADDR; i++) {
    EEPROM.write(i, b);
  }
}

// Speicherbereich auslesen
// gibt einen Dump des Speicherinhalts in tabellarischer Form über den seriellen Port aus.
void eeprom_serial_dump_table(int bytesPerRow = 16) {
  int i;
  int j;
  byte b;
  char buf[10];
  j = 0;

  for (i = EEPROM_MIN_ADDR; i <= EEPROM_MAX_ADDR; i++) {
    if (j == 0) {
      sprintf(buf, "%03X: ", i);
      Serial.print(buf);
    }
    b = EEPROM.read(i);
    sprintf(buf, "%02X ", b);
    j++;
    if (j == bytesPerRow) {
      j = 0;
      Serial.println(buf);
    } else Serial.print(buf);
  }
}

//Schreibt eine Folge von Bytes, beginnend an der angegebenen Adresse.
//Gibt True zurück wenn das gesamte Array geschrieben wurde, 
//Gibt False zurück wenn Start- oder Endadresse nicht zwischen dem minimal und maximal zulässigen Bereich liegt.
//Wenn False zurückgegeben wurde, wurde nichts geschrieben 
boolean eeprom_write_bytes(int startAddr, const byte* array, int numBytes) {
  int i;

  if (!eeprom_is_addr_ok(startAddr) || !eeprom_is_addr_ok(startAddr + numBytes)) return false;

  for (i = 0; i < numBytes; i++) {
    EEPROM.write(startAddr + i, array[i]);
  }  return true;
}

//Schreibt einen int-Wert an der angegebenen Adresse. 
boolean eeprom_write_int(int addr, int value) {
  byte *ptr;

  ptr = (byte*)&value;
  return eeprom_write_bytes(addr, ptr, sizeof(value));
}

//Liest einen Integer Wert an der angegebenen Adresse
boolean eeprom_read_int(int addr, int* value) {
  return eeprom_read_bytes(addr, (byte*)value, sizeof(int));
}

//Liest die angegebene Anzahl von Bytes an der angegebenen Adresse
boolean eeprom_read_bytes(int startAddr, byte array[], int numBytes) {
  int i;

  if (!eeprom_is_addr_ok(startAddr) || !eeprom_is_addr_ok(startAddr + numBytes)) return false;

  for (i = 0; i < numBytes; i++) {
    array[i] = EEPROM.read(startAddr + i);
  } return true;
}

//Liefert True wenn die angegebene Adresse zwischen dem minimal 
//und dem maximal zulässigen Bereich liegt.
//Wird durch andere übergeordnete Funktionen aufgerufen um Fehler
//zu vermeiden.
boolean eeprom_is_addr_ok(int addr) {
  return ((addr >= EEPROM_MIN_ADDR) && (addr <= EEPROM_MAX_ADDR));
}

//Schreibt einen String, beginnend bei der angegebenen Adresse
boolean eeprom_write_string(int addr, const char* string) {
  int numBytes;
  numBytes = strlen(string) + 1;

  return eeprom_write_bytes(addr, (const byte*)string, numBytes);
}

//Liest einen String ab der angegebenen Adresse
boolean eeprom_read_string(int addr, char* buffer, int bufSize) {
  byte ch;
  int bytesRead;

  if (!eeprom_is_addr_ok(addr)) return false;
  if (bufSize == 0) return false;

  if (bufSize == 1) {
    buffer[0] = 0;
    return true;
  }

  bytesRead = 0;
  ch = EEPROM.read(addr + bytesRead);
  buffer[bytesRead] = ch;
  bytesRead++;

  while ((ch != 0x00) && (bytesRead < bufSize) && ((addr + bytesRead) <= EEPROM_MAX_ADDR)) {
    ch = EEPROM.read(addr + bytesRead);
    buffer[bytesRead] = ch;
    bytesRead++;
  }

  if ((ch != 0x00) && (bytesRead >= 1)) buffer[bytesRead - 1] = 0;

  return true;
}

/**************************************************
  Wenn WRITE_EEPROM = true oder statusFlag != 49  *
  dann EEPROM löschen und Standardwerte schreiben *
  ************************************************/
void initializeEEPROM()
{
  int i;
  int statusFlag = EEPROM.read(0);

  Serial.print("StatusFlag ist: ");
  Serial.println(statusFlag);

  if(WRITE_EEPROM || statusFlag != 49)
  {
    Serial.println("Loesche Speicherbereich...");
    eeprom_erase_all();
    delay(1000);

    Serial.println("Lese EEPROM...");
    eeprom_serial_dump_table();

    delay(1000);

    Serial.println("Schreibe Speicherbereich...");

    strcpy(buffer, "1"); //Status-FLAG (0-1) 1 Zeichen
    eeprom_write_string(0, buffer);

    // Netzwerkeinstellungen
    strcpy(buffer, "5A:A2:DA:0D:56:7A");    // MAC Adresse (2-19) 18 Zeichen
    eeprom_write_string(2, buffer);    

    strcpy(buffer, "192,168,0,111");        // Lokale IP (20-35) 16 Zeichen
    eeprom_write_string(20, buffer);

    strcpy(buffer, "255,255,255,0");        // SUBNET MASK (36-51) 16 Zeichen
    eeprom_write_string(36, buffer);  

    strcpy(buffer, "192,168,0,1");          // Gateway (52-67) 16 Zeichen
    eeprom_write_string(52, buffer);

    strcpy(buffer, "192,168,0,1");          // DNS (68-83) 16 Zeichen
    eeprom_write_string(68, buffer);

    strcpy(buffer, "95,143,172,135");       // ServerIP (84-99) 16 Zeichen
    eeprom_write_string(84, buffer);

    strcpy(buffer, "fluuux.de");            // ServerHost (100-129) 30 Zeichen
    eeprom_write_string(100, buffer);

    strcpy(buffer, "/ARDUINO/Update.php");  // ServerURL (130-179) 50 Zeichen
    eeprom_write_string(130, buffer);

    strcpy(buffer, "12345");                // ServerKey (180-189) 10 Zeichen
    eeprom_write_string(180, buffer);

    strcpy(buffer, "Uno");                  // useragent (190-199) 10 Zeichen
    eeprom_write_string(190, buffer);

    //UploadInterval
    strcpy(buffer, "600");                  // Interval (200-204) 5 Zeichen
    eeprom_write_string(200, buffer);

    //DeviceAdressen
    strcpy(buffer, "10,16,2E,57,02,08,00,2F"); // (205-229) 25 Zeichen
    eeprom_write_string(205, buffer);
    strcpy(buffer, "10,25,48,C0,01,08,00,95"); // (230-254) 25 Zeichen
    eeprom_write_string(230, buffer);
    strcpy(buffer, "28,C9,C6,BE,03,00,00,C9"); // (255-279) 25 Zeichen
    eeprom_write_string(255, buffer);
    strcpy(buffer, "28,E3,AE,BE,03,00,00,9F"); // (280-304) 25 Zeichen
    eeprom_write_string(280, buffer);

    //Strings für Terminalausgabe    
    strcpy(buffer, "Initialisiere Sensoren..."); // (670-699) 30 Zeichen
    eeprom_write_string(670, buffer);

    strcpy(buffer, "Initialisiere Ethernet..."); // (700-729) 30 Zeichen
    eeprom_write_string(700, buffer);

    strcpy(buffer, "Sende Temperaturdaten.");   // (730-759) 30 Zeichen
    eeprom_write_string(730, buffer);

    strcpy(buffer, " [Fehler]");                // (760-789) 30 Zeichen
    eeprom_write_string(760, buffer);

    strcpy(buffer, " [Erfolgreich]");           // (790-819) 30 Zeichen
    eeprom_write_string(790, buffer);

    strcpy(buffer, "Verbindung hergestellt");   // (820-849) 30 Zeichen
    eeprom_write_string(820, buffer);

    strcpy(buffer, "Verbindung beendet");       // (850-879) 30 Zeichen
    eeprom_write_string(850, buffer);

    delay(1000);

    Serial.println("Lese Speicherbereich...");
    eeprom_serial_dump_table();

    // erase buffer
    for (i = 0; i < BUFSIZE; i++) 
    {
      buffer[i] = 0;
    }
  }
}

 

Um die Werte auszulesen und den deklarierten Variablen zuweisen zu können, nutzen Sie die Funktion “InitializeVariables”. Folgender Code ließt z.B. den ServerHost aus dem Adressbereich 100 aus, weißt diesen Wert der Variablen serverhost zu und gibt ihn über den seriellen Port aus.

eeprom_read_string(100, buffer, BUFSIZE); // ServerHost
sscanf(buffer,"%s",&serverhost);
Serial.println(buffer);

 

Bei Fragen können Sie gern die Kommentarfunktion hier unten benutzen oder direkt bei Arduino Playground vorbeischauen.

Über profwebapps

Programmierer, Webdesigner, Grafiker, Mac-User, Blogger, Screencaster. profwebapps ist auch bei Google+ vertreten

Kommentar verfassen