EEPROM schreiben & lesen

Hello again,

nun habe ich versucht einige Messwerte im EEPROM des Arduino zu speichern und auszulesen ... erfolglos.

#include "DHT.h"
#include "EEPROM.h"

#define DHTPIN 50
#define DHTTYPE DHT22
int addr = 0;
DHT dht(DHTPIN, DHTTYPE);

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

void loop() 
{
  start:
  int h = dht.readHumidity();
  int t = dht.readTemperature();

  if (isnan(t) || isnan(h)) 
  {
    goto start;
  }
  else 
  {
    EEPROM.write(addr, h);
    addr = addr + 1;
    if (addr == 512)
      addr = 0;
    EEPROM.write(addr, t);
    addr = addr + 1;
    if (addr == 512)
      addr = 0;
  }
  
  delay(1000);
}

Leider weiß ich aber nicht warum.
Nachdem ich den obrigen Sketch etwa eine Minute hab laufen lassen, habe mit Folgendem versucht auszulesen.

#include "EEPROM.h"

int addr = 0;
byte val;

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

void loop()
{
  Serial.print(addr);
  Serial.print("\t");
  Serial.print(val, DEC);
  Serial.println();
  
  addr = addr + 1;
  if (addr == 512)
   addr = 0;
   
   delay(100);
}

Der Serial Monitor zeigte mir

1 0
2 0
3 0
...

Da ich als Wert nur zweistellige Zahlen erwarte ( % und Temp, wobei ein- oder dreistellige Zahlen sehr unwahrscheinlich sind), habe ich

// need to divide by 4 because analog inputs range from
// 0 to 1023 and each byte of the EEPROM can only hold a
// value from 0 to 255.
int val = analogRead(0) / 4;

vernachlässigt.
Hat schon jemand meinen Fehler gefunden? :slight_smile:
Kann der Mega2560 4096 Byte speichern?
Kann ich das Auslesen beim letzen Byte stoppen? Das Programm anhalten?

Danke.
Lg Jim

Du definierst
int h = dht.readHumidity();
als integer-Zahl das heißt 2 Byte.
Das EEprom kann aber nur 1 Byte pro Speicherzelle aufnehmen.

Du mußt den Int wert in 2 Byte trennen.
entweder mit
lowbyte = h%256;
highbyte = h/256;

oder die Funktionen http://arduino.cc/en/Reference/LowByte und http://arduino.cc/en/Reference/HighByte

Grüße Uwe

Oder das High-Byte einfach um 8 nach rechts schieben

Und gewöhne dir goto ab. Es gibt zwar zwei legitimierte Verwendungen dafür, aber wenn man loop() wieder von vorne anfangen lassen will, kann man einfach ein "return" machen.

In den Sketch braucht es weder goto noch return.

 if (isnan(t) || isnan(h)) 
  {
    goto start;
  }
  else 
  {
    EEPROM.write(addr, h);
    addr = addr + 1;
    if (addr == 512)
      addr = 0;
    EEPROM.write(addr, t);
    addr = addr + 1;
    if (addr == 512)
      addr = 0;
  }
  
  delay(1000);
}

ist einfacher und macht das gleiche:

 if (!isnan(t) && !isnan(h)) 
  {
    EEPROM.write(addr, h);
    addr = addr + 1;
    if (addr == 512)
      addr = 0;
    EEPROM.write(addr, t);
    addr = addr + 1;
    if (addr == 512)
      addr = 0;  
    delay(1000);
  }
}

Grüße UWe

uwefed:
int h = dht.readHumidity();
als integer-Zahl das heißt 2 Byte

Ich würd auch gern eine Stelle hinterm Komma speichern.
Das ist aber aufwendiger als nur eine zweistellige Zahl, bzw zwei Byte, vermute ich ...

Serenifly:
Oder das High-Byte einfach um 8 nach rechts schieben

ööhhh ... :?

Serenifly:
Und gewöhne dir goto ab.

Dann gewöhn ichs mir gar nicht erst an ..

Ich les erstmal was zu den High und Low Byte.
Für zwei zweistelligen Zahlen brauche ich vier Byte. Bei zwei mal schreiben pro Stunde sind das acht Byte.
Somit könnte ich mit nem 2560 knapp 512 Stunden schreiben, bis der Speicher voll ist?

Danke.
Lg Jim

Dann gewöhn ichs mir gar nicht erst an ..

Das ist eine gute Einstellung.

Ich würd auch gern eine Stelle hinterm Komma speichern.

Dann mußt Du mit 10 multipliieren bevor Du den Wert in eine INT-Variable abspeicherst. und vor der Anzeige durch 10 dividieren unde die Kommastelle gesondert anzeigen.

Grüße Uwe

ööhhh ... :?

int test = 1236;
byte low = test;    //schneidet das High-Byte ab
byte high = test >> 8;    //schiebt um 8 Bit nach rechts

Für zwei zweistelligen Zahlen brauche ich vier Byte

Ein Byte geht von 0-255. Eine zwei-stellige Ganzzahl geht also in ein Byte.

Wegen den Nachkommastellen brauchst du allerdings int, da z.B: 25.9 * 10 = 259

EDIT:
Um aus zwei Bytes wieder einen int zu machen gibt es word():

int test = word(high, low);

Oder per Hand:

int test = (high << 8) | low;

Immer wieder 0. Im Serial Monitor wird mir die Adresse angezeigt, der Rest ist immer 0 ...

#include "DHT.h"
#include "EEPROM.h"

#define DHTPIN 50
#define DHTTYPE DHT22
int addr = 0;
int hx;
int tx;
DHT dht(DHTPIN, DHTTYPE);

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

void loop() 
{
  int h = dht.readHumidity();
  int t = dht.readTemperature();

  if (isnan(t) && isnan(h)) 
  {
    hx = h * 10;
    EEPROM.write(addr, highByte(hx));
    addr = addr + 1;
    if (addr == 2048)
      addr = 0;
    EEPROM.write(addr, lowByte(hx));
    addr = addr + 1;
    if (addr == 2048)
      addr = 0;
    tx = t * 10;
    EEPROM.write(addr, highByte(tx));
    addr = addr + 1;
    if (addr == 2048)
      addr = 0; 
    EEPROM.write(addr, lowByte(tx));
    addr = addr + 1;
    if (addr == 2048)
      addr = 0;
  }
  
  delay(1000);
}

Damit versuche ich zu schreiben. Byte für Byte.
Mit dem Folgendem, versuche ich in der richtigen Reihenfolge auszulesen.

#include "EEPROM.h"

int addr = 0;
byte val;

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

void loop()
{
  Serial.print(addr);
  Serial.print("\t");
  Serial.print(val, DEC);
  addr = addr + 1;
  if (addr == 512)
   addr = 0;
  Serial.print(val, DEC);
  Serial.print("\t");
  addr = addr + 1;
  if (addr == 512)
   addr = 0;
  Serial.print(val, DEC);
  addr = addr + 1;
  if (addr == 512)
   addr = 0;
  Serial.print(val, DEC);
  Serial.println();
  addr = addr + 1;
  if (addr == 512)
   addr = 0;
  Serial.println();
   delay(100);
}

Ich weiß aber auch nicht, ob ich falsch schreib oder falsch lese ... oder beides falsch ist ... :~

Solltest du nicht irgendwo mal EEPROM.read() machen?

int h = dht.readHumidity();
hx = h * 10;

So wird das nichts.
mit h hast Du bereits die Nachkommastellen verloren.
Was bekommst Du wenn Du die gelesenen Werte sofort (vor abspeichern im EEprom) auf die Serielle Schnittstelle ausgibst?

Grüße Uwe

Serenifly:
Solltest du nicht irgendwo mal EEPROM.read() machen?
http://arduino.cc/en/Reference/EEPROMRead

Ja, das bringt bestimmte Vorteile.
Die Tomaten von den Augen entfernen hilft auch, daß ich den Fehler hätte finden können. :wink: :wink:
Grüße Uwe

Du hast aber auch recht. Damit man überhaupt Nachkommastellen hat, muss man die Werte erst mal als float abspeichern. Sonst werden die Nachkommastellen einfach abgeschnitten.

Serenifly:
Solltest du nicht irgendwo mal EEPROM.read() machen?

Doch ... da bin ich mir sicher ... XD

uwefed:
Was bekommst Du wenn Du die gelesenen Werte sofort (vor abspeichern im EEprom) auf die Serielle Schnittstelle ausgibst?

Als

int h = dht.readHumidity();
int t = dht.readTemperature();

deklariert, eine zweistellige Ganzzahl:

Luftfeuchtigkeit: 40 % Temperatur: 26 *C

Zum lesen schöner und informativer, da ich gern eine Nachkommastelle hätte, finde ich

float h = dht.readHumidity();
  float t = dht.readTemperature();
...
    Serial.print("Luftfeuchtigkeit: "); 
    Serial.print(h,1);
    Serial.print(" %\t");
    Serial.print("Temperatur: "); 
    Serial.print(t,1);
    Serial.println(" *C");
...

Luftfeuchtigkeit: 41.7 % Temperatur: 26.3 *C

Den int habe ich nur zum speichern gewählt, da dieser mir eine zweistellige Ganzzahl ausgibt.
Ich dachte diese könne ich in einem Byte ( 0 - 255 ) speichern, da ich nicht wusste, dass ein int immer aus zwei Byte besteht.
Das bedeutet, um erstmal nur zu speichern kann ich ein Byte verwenden, da ich nur Zahlen zwischen 0 - 100 (%) erwarte?

Du nimmst jetzt den Float wert und multiplizierst ihn mit 10. Dann wird z.B. aus 41.7 -> 417

int h = dht.readHumidity() * 10;

Das kannst du dann in zwei Bytes teilen und abspeichern:

Beim Auslesen dividierst du durch 10.0 (wichtig! Nicht durch 10). Dann hast du wieder einen float Wert.

Normalerweise werden bei einer Zuweisung von float auf int durch den implizierten Cast die Nachkommastellen abgeschnitten. Aber durch das *10 hast du keine mehr.

#include "DHT.h"
#include "EEPROM.h"

#define DHTPIN 50
#define DHTTYPE DHT22
int addr = 0;
DHT dht(DHTPIN, DHTTYPE);

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

void loop() 
{
  int h = dht.readHumidity() * 10;
  int t = dht.readTemperature() * 10;

  if (isnan(t) && isnan(h)) 
  {
    EEPROM.write(addr, highByte(h));
    addr = addr + 1;
    if (addr == 2048)
      Serial.end();
    EEPROM.write(addr, lowByte(h));;
    addr = addr + 1;
    if (addr == 2048)
      Serial.end();
    EEPROM.write(addr, highByte(t));
    addr = addr + 1;
    if (addr == 2048)
      Serial.end();
    EEPROM.write(addr, lowByte(t));
    addr = addr + 1;
    if (addr == 2048)
      Serial.end();
  }
  
  delay(5000);
}

Wenn h als float 41,7 wäre, dann ist der int in diesem Fall 417, wobei 4 das highByte und 17 das lowByte ist?
Dann wird bei addr = 0 eine 4 gespeichert, bei addr = 1 eine 17?
Beim Auslesen muss ich zwei Byte nacheinander lesen und daraus mit word() einen int machen, den ich im
Serial Monitor ausgeben kann?

Schau dir mal an wie das Binärsystem funktioniert. Dir fehlen ein paar elementare Grundlagen. Der Windows Taschenrechner hilft da auch:
417 = 0000 0001 1010 0001

Also ist das High-Byte 1 und das Low-Byte ist 161 oder in Hex 0xA1. 1 * 256 + 161 = 417

Beim Auslesen muss ich zwei Byte nacheinander lesen und daraus mit word() einen int machen, den ich im
Serial Monitor ausgeben kann?

Ja

HcBenjy:
...
Wenn h als float 41,7 wäre, dann ist der int in diesem Fall 417, wobei 4 das highByte und 17 das lowByte ist?
Dann wird bei addr = 0 eine 4 gespeichert, bei addr = 1 eine 17?
Beim Auslesen muss ich zwei Byte nacheinander lesen und daraus mit word() einen int machen, den ich im
Serial Monitor ausgeben kann?

Nicht ganz. Arduino rechent nicht dezimal sondern binär und speichert jedesmal eine 8-Bit lange Zahl
417 ist binär 1 1010 0001 darum binär 1 und 10100001 das dann dezimal 1 und 161 ist. Also dezimal H-byte = 1 und L-Byte ist 161
Grüße Uwe

Serenifly:
Schau dir mal an wie das Binärsystem funktioniert. Dir fehlen ein paar elementare Grundlagen.

Ja, erwähnte ich am Anfang. Ich hab nur mal übern MC gelesen und nun hab ich n bissl Zeit um rauszufinden, ob das was Interessantes ist oder nicht. Den Arduino hab ich gewählt, weil das einfacher schien, als nur n IC vor sich liegen zu haben.
Versteh mich bitte nicht falsch, aber mein PC rechnet auch im Binärsystem .. glaub ich ... ^^ Den kann ich auch bedienen ohne zu wissen, wie er rechnet oder so. Sicher wird es Anwendungen geben, wo das Wissen über das Binärsystem unerlässlich ist, für meinen jetzigen Stand finde ich aber

uwefed:
Nicht ganz. Arduino rechent nicht dezimal sondern binär und speichert jedesmal eine 8-Bit lange Zahl.

ausreichend. Somit habe ich für den int high und low, vorn und hinten, 4 und 17 oder 1 und 161 ... das Prinzip bleibt gleich, auch wenn sich die Namen ändern. Aus zwei in bestimmter Reihenfolge eins machen ... Das hab ich zwar alles in der Reference gelesen, aber jetzt ergeben sich langsam Zusammenhänge ... :slight_smile:
Allerdings ergeben sich dadraus auch wieder neue Fragen, was die Sache ansich interessant macht. Ich hab meinen Blick mal über das Binärsystem fliegen lassen und das sieht mehr nach intensiven Lernen aus, als nach Freizeitbeschäftigung... für mein Empfinden von Spass, Freude, etc. .. :wink:

Mein Speicherproblem hab ich jedenfalls lösen können und das bringt mich nun dazu rauszufinden was "not a number" bedeutet.
Warum die Anzeige im Serial Monitor funktioniert (mit dieser Vorraussetzung), das Speichern aber nicht. Denn ohne die Zeile, in der nach Nummer oder keine Nummer :astonished: gefragt wird, funktioniert das ganze und ich kenne nun den Temperaturverlauf von gestern Nacht in meinem Schlafzimmer .... Warum? Weil ich jetzt sagen kann:

Weil ichs kann!

Ich habe kein konkretes Ziel. Ich einfach n "paar Teile" gegooglet und bestellt, die was mit "Arduino" zutun hatten.
Damit beschäftige ich mich nun, weil ichs interessant finde neue Dinge rauszufinden... Die Fragen ergeben sich sponten und manchmal ohne Zusammenhang.
So hab ich hier einen DHT11 und einen DHT22. Warum auch immer, aber kann man beide an einem Arduino betreiben?
Oder demnächst in nen neuen Topic, hoffe euch trotzdem noch lesen zu dürfen ... :slight_smile:

Fazit: Juhu, ich kann Zahlen speichern ...
So long.. Jim

HcBenjy:
Somit habe ich für den int high und low, vorn und hinten, 4 und 17 oder 1 und 161 ... das Prinzip bleibt gleich, auch wenn sich die Namen ändern.

Es ist eben nicht 4 und 17 :slight_smile: Du kannst nicht einfach die Dezimalzahlen auseinanderschneiden.

Binär ist eigentlich ganz einfach. Jede Ziffer hat die doppelte Wertigkeit wie die davor:
128 64 32 16 8 4 2 1

Wenn du also z.B. 1001 hast ist das 8 + 1 = 9. Da bietet sich auch das Hexadezimal-System an, was man da oft sieht Statt 10011001 schreibt man dann 0x99. Jede Hex-Ziffern entspricht 4 Bit. Also geht der Bereich einer Ziffer von 0-15 und man nimmt noch die Buchstaben A-F für 10-15.
Dezimal 417 ist 0x01A1. Daraus sieht man schon dass das obere Byte 0x01 ist und das untere 0xA1. Anders als Dezimal hat man da sofort die Aufteilung in 8 oder auch 16 oder 32 Bit breite Blöcke.

Das geht auch auf dem Arduino wenn du Serial.println(var, HEX) machst.

Den kann ich auch bedienen ohne zu wissen, wie er rechnet oder so.

Bedienen ja. Aber programmieren nicht unbedingt :slight_smile: