Datum mit Hex Eingabe sortieren

Hallo,

Ich habe einen Arduino Nano, LCD Display, RTC und eine Hex Tastatur. Auf dem EEPROM vom RTC-Baustein speichere ich Werte ab, die ich eingebe. Diese Werte sollen hinterher als Datum dargestellt werden. Das funktioniert auch schon soweit mit hex->int-Konvertierung. Ich bin noch relativ neu bei der Programmierung und hoffe hier auf Hilfe :)

Mein Problem ist nun folgendes:

Die Termine, die eingegeben werden, müssen sortiert werden. Dies darf jedoch nicht auf dem EEPROM passieren, da dieser nicht jedes Mal überschrieben werden soll. In dem seriellen Monitor sieht man, wie die Werte in Hex abgespeichert. Ich benutze dazu immer 12 Bytes. Wenn eine Reihe „ff“ folgt, ist dort nichts gespeichert und die Steuerung sucht sich diesen Speicherplatz beim nächsten abspeichern eines neuen Datum als erstes. Damit habe ich alle Datumeingaben immer an erster Stelle von dem EEPROM.

Ich habe 12 Bytes, die ich immer abspeicher. Die Anordnung ist wie folgt. Immer 2 Bytes für: Tag, Monat, Jahr, Std, Min, Grund

Der Grund ist unabhängig vom Datum. Nun kann ich natürlich einen struct anlegen. Jedoch darf ich die zusammenhängenden Zeilen natürlich nicht auseinanderreisen. Am liebsten würde ich die eingegeben Werte mit der aktuellen Zeit vergleichen, so dass ich einen Referenzwert habe. Dazu habe ich überlegt die Werte mit struct oder bubblesort zu vergleichen. Das Problem an der Sache ist, dass ich für die Werte keinen Datentyp „datum“ habe.

Ich habe schon sehr viel Zeit damit verbracht und komme auf keine gescheite Lösung. Ich hoffe ich konnte mein Problem verständlich darstellen. Ansonsten gerne näher nachhaken.

Zum Vergleichen würde ich die Zeiten in Unixtime (Sekunden seit 1.1.1970 0:0:0 Uhr) wandeln.
Die Formeln dazu findest Du hier im Forum.

Was ist mit Sommer/Winterzeit? Besser für Vergleiche ist es, intern alles in UTC zu betrachten und nur für Eingabe / Ausgabe auf die aktuelle lokale Zeit umzusetzen.

Evtl. könntest Du im EEPROM auch nur die Unixtime ablegen. Das macht das Vergleichen einfacher.

Falls Du mit der Schreibanzahl ein Problem hast, man kann den EEPROM des DS3231 auch Problemlos gegen einen FRAM FM24C64B austauschen (64 KBit = 8 KByte) oder einen FM24C265 (256 KBit = 32 KByte), den teste ich aber erst diese Woche.
Infos dazu unter diesem Link ab Beitrag #22.

Gruß Tommy

Vielen Dank für die schnelle Antwort. Der Vergleich mit Unixtime ist auch vollkommen in Ordnung. Die Uhr wird so eingestellt sein, dass diese sich jede Nacht einmal automatisch stellt.

Ich habe halt ein Problem damit die Daten die ich eingebe weiterhin als ganzes Paket zu betrachten. Ich gebe zwar ein Datum aus, es sind jedoch nur Integer Werte, die in der richtigen Reihenfolge dargestellt werden.

Evtl war mein Problemstellung auch undeutlich formuliert. Ich kann zwar mit der Unixtime als Referenzwert arbeiten, jedoch ist mein Datum noch kein richtiges Datum in diesem Sinne und da weiß ich auch nicht, wie ich dieses Problem lösen kann.

Ich glaube, Du solltest erst nochmal genau beschreiben, was das im Endeffekt werden soll. Dann kann man das sich besser vorstellen. Im Moment stoße ich an die Grenzen Deiner Beschreibung.

Gruß Tommy

Ich habe halt ein Problem damit die Daten die ich eingebe weiterhin als ganzes Paket zu betrachten. Ich gebe zwar ein Datum aus, es sind jedoch nur Integer Werte, die in der richtigen Reihenfolge dargestellt werden.

Auch wenn du Tag, Monat und Jahr in getrennten Variablen hast kannst du die immer noch vergleichen

Bubblesort geht zwar bei kleinen Datenmengen, aber Insertion Sort ist besser und genauso leicht zu implementieren.

Die eigentliche Funktion:

Das Projekt ist eine Kirchenglockensteuerung. Man soll Termine über die Tastatur eingeben und dann wenn der Termin mit der aktuellen Zeit verglichen wird, werden unterschiedliche Kirchenglocken angesteuert, Wenn man Termine abgespeichert hat, sollen diese in der richtigen Reinfolge dargestellt werden. Man kann auch einzelne Termine löschen. Wenn Termine gelöscht werden und ein neuer Termin eingeben wird, steht dieser neue Termin zwischen 2 vorhandenen Terminen. Damit ich den EEPROM nicht jedes Mal überschreibe, will ich die sortieren Werte im RAM speichern. So weit erst einmal zur Funktion.

Meine Denkweise: Alle 12 Bytes wird im Monitor die Variable Tag angezeigt. Wenn ich jetzt zuerst anfange nur nach dem Tag zu sortieren, ist die Reihenfolge der Tage zwar richtig, aber die Monate und Jahre werden dabei nicht berücksichtigt. Wenn ich nun weiter machen würde und die Funktion weiter verschachtel, reiße ich die Datumswerte auseinander.

Angenommen ich arbeite mit bubblesort und verschatel es immer weiter. Also zuerst Tag, Monat, Jahr, Std, Min,

dann habe ich die folgende Funktion als bubblesort:

int swapHolder = 0;
int day_array{];
  for (int i = 0; i <= 31, i++) {
  if (day_array[i] > day_array[i + 1] {

    swapHolder = day_array[i + 1];
      day_array[i + 1] = day_array[i];
      day_array[i] = swapHolder;
    }
  }

Nun wird dort mit einem Array gearbeitet. Ich weiß bloß nicht wie ich meine Tage in das Array schreibe, da diese ja immer an einer bestimmten Position im EEPROM gespeichert sind. Beim Insertion Sort hätte ich das gleiche Problem. Also wie ich es sortieren kann, ist mir bewusst. Jedoch bin ich nicht in der Lage, die gespeicherten Werte aus dem EEPROM in einem Array zu verarbeiten.

Hallo Joe, nach meinem Eindruck machst Du Dir das Leben unnötig schwer. Du schreibst "Am liebsten würde ich die eingegeben Werte mit der aktuellen Zeit vergleichen, so dass ich einen Referenzwert habe." Die Antwort steht schon in #1 "Zum Vergleichen würde ich die Zeiten in Unixtime wandeln."

Im EEPROM stehen die Zeiten nach Stunden, Minuten usw. getrennt, zum Vergleichen machst Du einen Wert daraus. Dazu bietet sich die Unix-Zeit an. Da das Läuten in der Zukunft liegt, kannst Du auch die Sekunden ab einem anderen Jahr als 1970 nehmen.

Als fauler Mensch habe ich mal die Bibliothek Sodaq_DS3231 bemüht, weil dort eine Funktion (Methode) existiert, die die Sekunden ab dem Jahr 2000 berechnet. Die getrennten Variablen Jahr, Monat, Tag, Stunde, Minute werden in einen Wert umgerechnet. Die ganzzahligen Variablen ts1 und ts2 lassen sich leicht vergleichen. So könnte ts1 die aktuelle und ts2 die Läutzeit beinhalten.

#include "Sodaq_DS3231.h"
//year, month, date, hour, min, sec and week-day(starts from 0 and goes to 6)

void setup() {
  Serial.begin(9600);
  Serial.println("Anfang");
  uint16_t Jahr = 2016;
  byte Monat = 12, Tag = 14, Stunde = 15, Minute = 18;
  DateTime dt1(Jahr, Monat, Tag, Stunde, Minute, 0, 0);
  Stunde = 16;
  DateTime dt2(Jahr, Monat, Tag, Stunde, Minute, 0, 0);

  Serial.print("Datum/Zeit 1: ");
  Serial.print(dt1.year(), DEC);
  Serial.print('/');
  Serial.print(dt1.month(), DEC);
  Serial.print('/');
  Serial.print(dt1.date(), DEC);
  Serial.print(' ');
  Serial.print(dt1.hour(), DEC);
  Serial.print(':');
  Serial.print(dt1.minute(), DEC);
  Serial.println();
  uint32_t ts1 = dt1.get();
  Serial.print("Sekunden seit 2000: ");
  Serial.print(ts1, DEC);
  Serial.println();

  Serial.print("Datum/Zeit 2: ");
  Serial.print(dt2.year(), DEC);
  Serial.print('/');
  Serial.print(dt2.month(), DEC);
  Serial.print('/');
  Serial.print(dt2.date(), DEC);
  Serial.print(' ');
  Serial.print(dt2.hour(), DEC);
  Serial.print(':');
  Serial.print(dt2.minute(), DEC);
  Serial.println();
  uint32_t ts2 = dt2.get();
  Serial.print("Sekunden seit 2000: ");
  Serial.print(ts2, DEC);
  Serial.println();
  if (ts1 == ts2) {
    Serial.println("Die Zeiten sind gleich!");
  } else {
    Serial.println("Die Zeiten sind nicht gleich!");
  }
}

void loop() {}

Selbstverständlich kannst Du die Berechnung auch auf Minuten seit 1.1.2000 oder 1.1.2016 umstellen.

Als ich gestern hier meinen Beitrag verfasst habe, sind mir einige Sachen plötzlich klar geworden. Ich habe hier noch mit bubble sort gearbeitet, um erst einmal zu sehen, ob es funktioniert. Das Programm an sich funktioniert zwar, jedoch entspricht die Ausgabe nicht meinen Erwartungen.

      sort[j] = {I2CEEPROM_Read(j)};
      for (int j = j + 6; j < 4096; j++) {
        if (sort[j] > sort[j + 1]) {

          swapHolder = sort[j + 1];
          sort[j + 1] = sort[j];
          sort[j] = swapHolder;
        }
        lcd.setCursor(0, 1);
        for ( j = n; j < 6 + n; j++) {
          lcd.print(sort[j]);
        }
        lcd.setCursor(0, 2);
        for (  j = n + 6; j < 12 + n; j++) {
          lcd.print(sort[j]);
        }
        lcd.setCursor(0, 3);
        for ( j = n + 12; j < 18 + n; j++) {
          lcd.print(sort[j]);
        }
      }

Vielen Dank agmue,

deinen Post habe ich gerade erst nach meinem verfassten gelesen. Ich schaue es mir mal an.

Als fauler Mensch habe ich mal die Bibliothek Sodaq_DS3231 bemüht, weil dort eine Funktion (Methode) existiert, die die Sekunden ab dem Jahr 2000 berechnet

Als kreativer Mensch widerstrebt es mir, auf winzigen Controllern dicke Libraries zu meinen Sketchen hinzuzufügen, nur weil ich evtl. zur Not eine der Funktionen verwenden kann.

Aus den Einzelwerten jahr/monat/tag/stunde/minute eine einzelne leicht sortierbare Zahl zu machen, ist allerdings in Ordnung. Da ich diese Zahl jedoch nur zum Sortieren brauche, muss ich mich um unterschiedliche Monatslängen und Schaltjahre nicht kümmern.

(((((long)jahr*16+monat)*32+tag)*32+stunde)*64)+minute

hat sogar den Vorteil, dass nicht mal multipliziert wird, weil der Compiler das optimieren kann.

Wenn es nur für die Jahre 2000..2063 gehen muss, könnte man noch etwas mehr aufrunden (Und in HEX-Darstellung könnte man den Wert dann sogar fast erkennen).

michael_x: Als kreativer Mensch widerstrebt es mir, auf winzigen Controllern dicke Libraries zu meinen Sketchen hinzuzufügen, nur weil ich evtl. zur Not eine der Funktionen verwenden kann.

Das bleibt Dir unbenommen! Als krativer weil fauler Mensch erfinde ich das Rad lieber nicht ein zweites Mal. Daher nutze ich Bibliotheken gerne als Quelle der Inspiration oder auch direkt. Ob 10% oder 30% des Speichers belegt sind, ist dem µC doch egal.

Für den TO relevant ist, auf der richtigen Schiene in die richtige Richtung zu dampfen. Wenn das gelungen ist, geht es um Optimierung. Und selbstverständlich ist dann eine kleine Formel besser zu verstehen, als eine dicke Bibliothek.

Joe, nimm die in #10 vorgeschlagene Zahl zum Sortieren, viel einfacher als die Bibliothek :)

Bei mir hapert es gerade am Verständnis und an der Umsetzung. Ich habe nun die Formel aus #10 implementiert. Als Rückgabewert erhalte ich die Zahl "17584768". Ist das der Wert in Sekunden bezogen auf welchen Referenzwert? Im EEPROM wurde das Datum "10.12.16 10:00" ausgelesen oder in hex wie es im EEPROM geschrieben steht "0a 0c 10 0a 00".

Oder ist die Zahl quasi nur einen Wert, damit ich mein Datum besser sortieren kann?

      jahr = I2CEEPROM_Read(0x02);
      monat = I2CEEPROM_Read(0x01);
      tag = I2CEEPROM_Read(0x00);
      stunde = I2CEEPROM_Read(0x03);
      tminute = I2CEEPROM_Read(0x04);
      ausgabe_bildschirm = (((((long)jahr * 16 + monat) * 32 + tag) * 32 + stunde) * 64) + tminute;

      lcd.setCursor(0, 1);
      lcd.print(ausgabe_bildschirm);

Okay, die Frage konnte ich mir wohl selbst beantworten. Ich habe nun 3 Datumswerte miteinander verglichen. Das Datum was am 01.12.16 liegt hat eine niedrige Zahl, als das Datum vom 10.12.16 und das Datum vom 31.12.16 ist höherwertiger.

So etwas hilft mir auf jeden Fall deutlich weiter. Vielen Dank für die nette Hilfe hier schon mal an dieser Stelle! :)

Hier habe ich die Ausgabe mal kurz realisiert, aber wie automatisiere ich es, dass ich nun nicht immer die einzelnen Speicheradressen aus dem EEPROM angeben muss und dass mir nach dem sortieren das Datum und nicht der lange Zahlencode angezeigt wird? Jedes 5. Byte wurde ausgelassen, da hier der Grund drin steht, welche Kirchenglocken angesteuert werden.

      jahr = I2CEEPROM_Read(0x02);
      monat = I2CEEPROM_Read(0x01);
      tag = I2CEEPROM_Read(0x00);
      stunde = I2CEEPROM_Read(0x03);
      tminute = I2CEEPROM_Read(0x04);
      ausgabe_bildschirm = (((((long)jahr * 16 + monat) * 32 + tag) * 32 + stunde) * 64) + tminute;
      lcd.setCursor(0, 1);
      lcd.print(ausgabe_bildschirm);

      jahr = I2CEEPROM_Read(0x08);
      monat = I2CEEPROM_Read(0x07);
      tag = I2CEEPROM_Read(0x06);
      stunde = I2CEEPROM_Read(0x09);
      tminute = I2CEEPROM_Read(0x0a);
      ausgabe_bildschirm = (((((long)jahr * 16 + monat) * 32 + tag) * 32 + stunde) * 64) + tminute;
      lcd.setCursor(0, 2);
      lcd.print(ausgabe_bildschirm);


      jahr = I2CEEPROM_Read(0x0e);
      monat = I2CEEPROM_Read(0x0d);
      tag = I2CEEPROM_Read(0x0c);
      stunde = I2CEEPROM_Read(0x0f);
      tminute = I2CEEPROM_Read(0x10);
      ausgabe_bildschirm = (((((long)jahr * 16 + monat) * 32 + tag) * 32 + stunde) * 64) + tminute;
      lcd.setCursor(0, 3);
      lcd.print(ausgabe_bildschirm);

Tommy56: Falls Du mit der Schreibanzahl ein Problem hast, man kann den EEPROM des DS3231 auch Problemlos gegen einen FRAM FM24C64B austauschen (64 KBit = 8 KByte) oder einen FM24C265 (256 KBit = 32 KByte), den teste ich aber erst diese Woche. Infos dazu unter diesem Link ab Beitrag #22.

Gruß Tommy

Du meinst das FM24C256 oder das FM24W256-G FRAM? Außerdem meinst Du wohl eine Platine (Breakout) mit dem D3231 und einem EEPROM wie es zu kaufen gibt; das D3231 hat kein EEPROM integriert. Grüße Uwe

Joe194:
Ist das der Wert in Sekunden bezogen auf welchen Referenzwert?

Bei der Unixzeit wären es Sekunden seit 1970 oder wie in #7 seit 1.1.2000. Für die Formel in #10 gilt das nicht, sie stellt nur eine verläßliche Relation sicher.

Wäre überhaupt mal zu überlegen, ob Du nicht die Unixzeit (4 Bytes) anstelle der Einzelwerte speicherst und nur zur Anzeige eine Rückrechnung auf Einzelwerte vornimmst. Dann holst Du die Unixzeiten aus dem EEPROM, sortierst und zeigst die sortierten Werte als Einzelwerte an. Nur so als Idee.

Die n-te Läutzeit (n ab 0) erhälst Du bei 12 Bytes je Datensatz: tag = I2CEEPROM_Read(0x00+(n*12));
Also for(n=0; 0<Laeutzeiten; n++){}

Die Unixzeit wird übrigens im Jahr 2038 ein Riesenproblem verursachen, kein Vergleich zum Jahr 2000 Hype damals, falls sich noch jemand der Erwachsenen daran erinnern kann ;)

Besser man verwendet sie für neue Arduino Projekte schon jetzt nicht mehr.

Bei der Unixzeit wären es Sekunden seit 1970. Für die Formel in #10 gilt das nicht, sie stellt nur eine verläßliche Relation sicher

Lässt sich auch zurückrechnen. Es gibt halt nur einige Werte, die kein gültiges Datum darstellen, und sie deckt einen etwas kleineren möglichen Bereich ab (knapp 64 statt über 68 Jahre), zugunsten einfacherer Berechnung/Rückrechnung. Und ist nicht so weit verbreitet, da ich sie vorhin erst erfunden habe.

In der hier verwendeten Minutenvariante geht die Rückrechnung z.B. so

jahr   = combinedLongNumber >> 20; 
monat  = combinedLongNumber >> 16 & 0x0F;
tag    = combinedLongNumber >> 11 & 0x1F;
stunde = combinedLongNumber >> 6 & 0x1F;
minute = combinedLongNumber & 0x3F;

Die " combinedLongNumber " kann natürlich genauso wie die Unixzeit im EEPROM gespeichert werden.

Damit beschäftigen wir uns ab 2030. Evtl. gibt es dann keine <64Bit Repräsentation davon mehr und mit 64 Bit Unixtime hält das dann wieder ne Weile.

Gruß Tommy

Das ist super. Die Werte so miteinander zu vergleichen gefällt mir sehr.
Nun wollte ich einmal testen, ob mein bubble sort funktioniert. Dabei habe ich jedoch immer eine Fehlermeldung.

Das ist bestimmt nur ein kleiner Formfehler oder so, hab schon vieles probiert, aber nichts hat mich bisher weiter gebracht. Ich denke, dass ich beim array erstellen einen Fehler habe. Selbst wenn ich eine Variable oder einen Wert in die leere Klammer bei ausgabe schreibe, meckert das Programm rum.

      jahr = I2CEEPROM_Read(0x02);
      monat = I2CEEPROM_Read(0x01);
      tag = I2CEEPROM_Read(0x00);
      stunde = I2CEEPROM_Read(0x03);
      tminute = I2CEEPROM_Read(0x04);
      ausgabe_bildschirm1 = (((((long)jahr * 16 + monat) * 32 + tag) * 32 + stunde) * 64) + tminute;
      lcd.setCursor(0, 1);
      lcd.print(ausgabe_bildschirm1);

      jahr = I2CEEPROM_Read(0x08);
      monat = I2CEEPROM_Read(0x07);
      tag = I2CEEPROM_Read(0x06);
      stunde = I2CEEPROM_Read(0x09);
      tminute = I2CEEPROM_Read(0x0a);
      ausgabe_bildschirm2 = (((((long)jahr * 16 + monat) * 32 + tag) * 32 + stunde) * 64) + tminute;
      lcd.setCursor(0, 2);
      lcd.print(ausgabe_bildschirm2);


      jahr = I2CEEPROM_Read(0x0e);
      monat = I2CEEPROM_Read(0x0d);
      tag = I2CEEPROM_Read(0x0c);
      stunde = I2CEEPROM_Read(0x0f);
      tminute = I2CEEPROM_Read(0x10);
      ausgabe_bildschirm3 = (((((long)jahr * 16 + monat) * 32 + tag) * 32 + stunde) * 64) + tminute;
      lcd.setCursor(0, 3);
      lcd.print(ausgabe_bildschirm3);
 
      int swapHolder=0;
      int ausgabe[] = {ausgabe_bildschirm1, ausgabe_bildschirm2, ausgabe_bildschirm3};
      for ( j = 0; j < 3; j++) {
        if (ausgabe[j] > ausgabe[j + 1]) {
          swapHolder = ausgabe[j + 1];
          ausgabe[j + 1] = ausgabe[j];
          ausgabe[j] = swapHolder;
        }
      }

Joe194: Die Werte so miteinander zu vergleichen gefällt mir sehr.

So wollte ich das :)

Wenn Du das Prinzip verstanden hast, kannst Du frei zwischen Minuten seit 2000 oder combinedLongNumber wählen.

In int ausgabe[] passen keine long-Variablen.

michael_x: Die Unixzeit wird übrigens im Jahr 2038 ein Riesenproblem verursachen, kein Vergleich zum Jahr 2000 Hype damals, falls sich noch jemand der Erwachsenen daran erinnern kann ;)

Wasser in die Badewanne lassen für die Toilettenspülung, Vorräte einkaufen, Autoakku aufladen ... und leugnen, jemals sowas wie die Unixzeit auch nur gekannt zu haben :grin: