Hi, ich protokolliere bei meiner Heizungssteuerung rund 90 Werte (verschiedene Sensoren, Reglervariablen, Zählerimpulse....). Dazu werden die Daten auf eine SD-Karte geschrieben. Sie können dann seriell abgerufen werden und landen in einer Datenbank. Ich möchte die Daten auf der Karte nun platzsparender speichern, da die SD-Karte (Software-SPI) beim Lesen vieler angesammelter Datensätze sehr langsam ist. Das aktuelle Format sieht so aus: #xxx;x;xx;xxxx;xxxxx@#xxx;x;xx;xxxx;xxxxx@#xxx;x;xx;xxxx;xxxxx@ # ist die Startmarkierung eines Datensatzes, das @ das Ende. Die Felder sind mit ; getrennt und enthalten im Klartext lesbare Werte, etwa 231, oder auch 16-10-31 für das Datum. Meine Idee wäre, alle Variablen mit 16 Bit zu speichern und auf Feldtrenner zu verzichten. Bei 90 Werten hätte ich dann 180 Bytes. Die Sekunden sind nicht relevant, beim Datum müsste ich schauen, wie (ob) ich es mit 16 Bit abbilden kann. Aber wie markiere ich Start und Ende eines Datensatzes? Mit einem Muster? Wie lange sollte diese sein, um einmalig zu sein? Sowohl der Controller muss ja beim Lesen erkennen, ab wo ein neuer Datensatz beginnt (es wird pro Anfrage immer nur einer ausgegeben). Das abrufende System muss Anfang und Ende signalisiert bekommen, damit es weiß, ob der Datensatz vollständig ist (Länge wird natürlich auch geprüft). Danke schön! Gruß, Chris
Fixed Length ist vermutlich nicht viel platzsparender. Ne rudimentäre Komprimierung könnte aber helfen.
eventuell reicht es ja schon, wenn du die Daten als HEX speicherst. Dan würden 16 bit noch 4 Zeichen brauchen, hätten den Vorteil das noch ausreichend Steuerzeichen für Start und Ende frei sind. Was auch noch geht ist Base64.
Chris schrieb: > Ich möchte die Daten auf der Karte nun platzsparender speichern, da die > SD-Karte (Software-SPI) beim Lesen vieler angesammelter Datensätze sehr > langsam ist. Bist du sicher, dass das langsame Lesen an der Datenmenge und nicht an der Zugriffsmethode liegt? Wird der Zugriff nur langsam, wenn du mittendrin etwas lesen möchtest, oder auch wenn du sequentiell eine Datei komplett überträgst?
> Peter II (Gast) > Was auch noch geht ist Base64. Unfug: bläht den Datensatz noch weiter auf. @ To > Aber wie markiere ich Start und Ende eines Datensatzes? Wenn die Sätze immer die gleiche Länge haben: überhaupt nicht.
Sascha_ schrieb: > Fixed Length Und nicht im Textformat, sondern roh. Chris schrieb: > Mit einem > Muster? Brauchst du dann nicht mehr.
Ihr habt alle keine Ahnung, WIE ÜBLICH! Chris, dein Ansatz ist völlig richtig. Du brauchst keinen Trenner. Wenn du Datensatz 100 auslesen willst, überspringst du einfach 99*180 Bytes. Das ist das schöne an der Binärcodierung. Die Datensatzlänge ist immer gleich lang und du kannst jeden Datensatz direkt und ganz ohne Indizierung anspringen. Datensatz #887785? Kein Problem. Alles klar?
Meine Nutzlast hat im Moment 376 Zeichen. Das sind ja deutlich mehr als 90x2=180 Zeichen + Start/Ende-Markierung. Daher dürfte das schon was bringen, oder? Im Moment gehen 90 Byte allein für die Trennzeichen drauf. Ich denke auch, dass die Zugriffsmethode das Problem ist. Es kann ja nicht so lange dauern, 376 + 2 Byte zu lesen.... Die Ansteuerung nutzt Sofware-SPI. Soweit ich den Code verstehe, arbeitet sie nicht mit Timeouts, sondern prüft eine Leitung bzw. ließt ein Register aus, bevor es weiter geht. Für jede Anfrage geschieht auf dem Controller folgendes: - Anfrage vom PC wird empfangen - fileopen - dateilaenge=getfilelength() - ist (dateilaenge >= nextpos+datensatzlänge)? - fileseek zu nextpos //nextpos ist Position des ersten ungelesenen Datensatzes) - solange (gelesenebytes < datensatzlänge) und (zeichen != Ende-Markierung) { zeichen=fileread() //1 Zeichen lesen uartsenden(zeichen) } - fileclose - Auf Bestätigung warten, dann nextpos=aktuellepos Beim Zugriff beobachte ich: - PC sendet Anfrage (RX-LED am Controller blitzt kaum sichtbar) - LED an der SD-Karte geht dauerhaft an, flimmert am Ende ein paar mal schnell, geht aus (dauert insgesammt gut 1 Sekunde!!!). - TX-LED blitzt kurz, PC erhält Datensatz - PC bestätigt Datensatz (RX-KED am Controller blitzt kaum sichtbar). Zwischen Anfrage und Reaktion bzw. Bestätigung des PCs vergeht kaum Zeit. Nach 100 ms werfen Client oder Controller einen Timeout, um ewiges Warten zu verhindern, wenn mal was nicht klappt. Der einzige Zugriff, den ich einsparen kann, ist getfilelength(), eventuell auch fileopen und fileclose. Ich muss die Datei ja nicht jedes mal neu öffnen/schließen. Beim Schreiben gehe ich so vor: - fileopen - fileseek(filelength) - filewrite(logdataalsstring) - fileclose. Ich werde mal beobachten, ob das auch so lange braucht.
@ASM Superprofi: Gute Idee, aber wenn etwas verrutscht, lese ich ab dort nur noch Müll und merke es nicht. Es ist nicht schlimm, wenn mal ein Datensatz verloren geht, aber falsche Daten würden vieles durcheinander bringen. Daher muss ich Anfang und Ende schon erkennen können. Wenn die Länge dazwischen nicht passt, wird der Datensatz verworfen.
Burgfried schrieb: > Unfug: bläht den Datensatz noch weiter auf. nein, man spart die Trennzeichen und bei großen Zahlen auch noch 1 Zeichen. > @ To > >> Aber wie markiere ich Start und Ende eines Datensatzes? > Wenn die Sätze immer die gleiche Länge haben: überhaupt nicht. viel zu unsicher, und unflexibel. Was ist wenn er mal ein Wert mehr speichern will?
Arduino F. schrieb: > Chris schrieb: >> aber wenn etwas verrutscht, > Irre! Wie sagte mein Statistikprof immer: Man darf sich ruhig mal in der Zeile vertun aber lieber nicht in der Spalte :-D
Ich öffne nun die Datei nur noch bei Programmstart. Lesen und Schreiben ist somit deutlich schneller. Ich hoffe, es macht nichts, wenn die Stromzufuhr unterbrochen wird, während die Datei offen ist. Gruß, chris
Chris schrieb: > Gute Idee, aber wenn etwas verrutscht, lese ich ab dort nur noch Müll > und merke es nicht. Der Begriff "Prüfsumme" ist dir bekannt? > Ich öffne nun die Datei nur noch bei Programmstart. Lesen und > Schreiben ist somit deutlich schneller. Unter der Annahme, dass du FAT benutzt, ist das auch logisch, denn dein fopen() muss ja die ganze FAT durchsuchen, um den nächsten freien Cluster zu finden. > Ich hoffe, es macht nichts, wenn die > Stromzufuhr unterbrochen wird, während die Datei offen ist. Doch, tut es. Weder die Datensicherheit noch das Timing ist bei SD-Karten im Allgemeinen in irgendeiner Form garantiert. Über eine bestimmte SD-Karte kannst du hingegen durchaus Aussagen treffen. Es gab dazu letztens eine längliche Diskussion hier: Beitrag "FatFS Dateisystem"
376 vs. 180 macht gerade 50% Einsparung. Dafuer wuerde ich keine Kopfstaende machen, bei einer Binaerdatei weiss nachher keiner mehr, was damit anzufangen ist.
Für meine Messwerterfassung habe ich ein Datenformat mit lauter 16-Bit Werten. Hat sich ganz gut bewährt. Als Datensatzende gibt es ein 0xFFFF. Falls es als Wert vorkommt, wird es in 0xFFFE geändert. Das erste Wort je Datensatz ist die Uhrzeit in Doppelsekunden, die passen ganz gut in 16 Bit. Der Tag kommt in den Dateinamen, im Format JJJJMMTT der Sortierung wegen.
Lass die Feldtrenner weg und begrenze jedes Datenfeld auf die wirklich nötige Größe. Das mit dem Verrutschen ist Schwachsinn, aber auch das kann mit einer einfachen CRC32 hinter jedem Datensatz gelöst werden.
Kein FS. Ein Block = Ein Datensatz Blockanfang = Datensatzanfang Kein öffnen und schließen notwendig. Eine Checksumme über die Daten im Block markiert einen gültigen Block/Datensatz. In Block 0 liegt ein Salt, welches zur Prüfsummenberechnung herangezogen wird. Wird dies verändert, sind alle Blöcke ungültig und damit frei. Bei Neustart oder wenn das System feststellt, dass eine neue Karte eingesteckt wurde, wird die Karte nach dem Baumprinzip nach der ersten freien Stelle durchsucht. Gruß Jobst
Chris schrieb: > Aber wie markiere ich Start und Ende eines Datensatzes? Garnicht. Bei Text ist einfach das '\n' das Ende eines Datensatzes und logischer Weise das erste Zeichen nach einem '\n' ein neuer Datensatz. Das Datum braucht man auch nur bei einem Wechsel speichern. Falls die Datensätze in einem konstanten Zeitinterval erzeugt werden, braucht man auch nicht jedesmal die Zeit speichern.
Je nachdem wie viele Daten sich nur langsam oder gar nicht ändern könnte man darüber nachdenken nur Änderungen zu speichern. Dann muss man aber jedes Datenfeld identifizieren und darf nichts zwischendrin verlieren. Das lohnt also nur wenn der große Teil der Werte sich deutlich langsamer ändert als dein Loggingintervall ist.
S. R. schrieb: > Chris schrieb: >> Gute Idee, aber wenn etwas verrutscht, lese ich ab dort nur noch Müll >> und merke es nicht. > > Der Begriff "Prüfsumme" ist dir bekannt? Dann liest er ab dort nur noch Müll und merkt es. Naja, immerhin ein kleiner Fortschritt. >> Ich hoffe, es macht nichts, wenn die >> Stromzufuhr unterbrochen wird, während die Datei offen ist. > > Doch, tut es. Weder die Datensicherheit noch das Timing ist bei > SD-Karten im Allgemeinen in irgendeiner Form garantiert. Über eine > bestimmte SD-Karte kannst du hingegen durchaus Aussagen treffen. Die SD-Karte weiß doch gar nicht, ob die Datei offen ist. Sie weiß nicht mal, was eine Datei ist. Damit kann es nicht von der SD-Karte abhängig sein, ob es ein Problem ist, wenn man die Datei nicht nach jedem Zugriff schließt und wieder öffnet.
Jobst M. schrieb: > Kein FS. Genau. Warum machst du eigentlich mit FAT rum, wenn a) du nur mit dem Mikrocontroller liest und schreibst und b) nur eine Datei (einen Datensatz) hast? Vorschlag 1: Benutze FAT und lies die Karte mit nem PC aus -> 1000x schneller. Vorschlag 2: Beschreibe die SD-Karte direkt und rück die Daten nur über den µC raus. Zwecks Start/Endsymbolen: Pack doch deine Daten in structs und lass den Compiler bzw. Mikrocontroller sich drum kümmern! Wenn dir das zu unsicher ist: Speicher ist deutlich billiger als die Arbeitszeit, da besonderes Framing reinzubasteln (oder am Ende Daten wieder herzustellen). Darum: speichere doch einfach aligned an der nächsthöheren Zweierpotenz! Ein Beispiel: Dein Datensatz ist 90 Bytes. Nächsthöhere Zweierpotenz ist 128. Speichere Datensatz "n" also an der Speicherstelle "128*n". Dadurch müssen die niedrigsten 7 Bit deiner Adressen immer Null sein. Dadurch kannst du Fehler detektieren (Wenn Adresse & 0x007F != 0 -> Fehler), Fehler vermeiden (Adresse = Adresse & 0xFF80) und hast popeleinfache Zugriffe (for(int i=0;;i++){serial_transmit(start_pointer + i << 7);}
Grund für FAT: ursprünglich habe ich die SD-Karte am PC ausgelesen und die Daten in Excel übernommen. Der Abruf über Seriell (und dsnn weiter über Ethernet/TCP) kam erst später. Ich habe nun folgende Lösung umgesetzt: Die Datei wird beim Abruf geöffnet und 10 Sekunden nach der letzten Anfrage geschlossen. Somit gehen aufeinanderfolgende Anfragen schnell genug. Danke nochmal für die vielen Vorschläge! Gruß Chris
Rolf M. schrieb: >> Der Begriff "Prüfsumme" ist dir bekannt? > > Dann liest er ab dort nur noch Müll und merkt es. Naja, immerhin ein > kleiner Fortschritt. Er schrieb, dass Müll ein echtes Problem ist. Dagegen hilft die Prüfsumme. Willst du etwa einen adaptiven Fensterungsalgorithmus bauen, der den Offset bei Datenverschiebungen automatisch nachführt? Wie soll der reagieren, wenn ein 4 MB großer Datenblock irgendwo einfach fehlt, weil die SD-Karte einen Eraseblock beim Abschalten zerstört hat? > Die SD-Karte weiß doch gar nicht, ob die Datei offen ist. Sie weiß nicht > mal, was eine Datei ist. Damit kann es nicht von der SD-Karte abhängig > sein, ob es ein Problem ist, wenn man die Datei nicht nach jedem Zugriff > schließt und wieder öffnet. Erstens: Eine SD-Karte weiß sehr wohl, ob ein Eraseblock offen ist oder nicht. Sie weiß oft sogar, ob der Eraseblock zur FAT gehört oder nicht. Der Controller der SD-Karte entscheidet, ob offene Eraseblocks bei Abschaltung gerettet werden oder nicht. Zweitens: Eine offene Datei kann Daten enthalten, die noch nichtmal an die SD-Karte gesendet wurden. Es hängt vom Stream ab, ob und wie er gepuffert ist und Plattencaches gibt es zu allem übel auch noch (selbst auf Controllern, dann für Teile der FAT). Erst ein fclose() garantiert, dass die Daten auch tatsächlich in das Gerät geschrieben wurden. Wie soll die SD-Karte denn deiner Meinung nach Daten absturzfest schreiben, die sie nie erhalten hat? Ich habe nicht ohne Grund auf einen anderen Thread verlinkt. Lies den.
Chris schrieb: > Somit gehen aufeinanderfolgende Anfragen schnell genug. Aus Interesse - was heißt “schnell genug“?
Welcher FAT library benutzt du ? Ich benutze FATFS Da gibt es die funktion f_sync die sehr wichtig is die chance auf problemen mit spannungsverluss zu minimalisieren. (siehe FATFS docu, 'Critical Section' ) Dabei kannst du am besten den Datensatz 128, 256 oder 512 bytes grosz damit du viel besser planen kannst wie lange jede schreib/leseaktion dauert. Wenn ich mich nicht vergisz, kann man sogar der gleichen file geoefnet halten fuer schreiben und gleich auch (altere) daten auslesen von gleichen file. Ich benutze selber auch ein solches system und benutze ZMODEM protocol um files von micro zum PC ueber zu setzen. Fuer entwicklung benutzte ich immer Tera Term (vorher auch Hyperterm) weil diese terminals gute unterstuetzung fuer ZMODEM haben. Wenn dir ZMODEM zu schwierig ist, dann YMODEM oder ZMODEM. Fuer PC software habe ich nachdem auch ZMODEM implementiert und bin sehr zufrienden damit. Patrick
@bla: Ich schötze 50-100 ms pro Datensatz, inklusive Schreiben in die DB, Debug, Ausgaben und Unterbrechung durch Dinge, die der Controller sonst noch so tut (auch während des Datenbrufs wird zwischen 2 Abrufen die Hauptschleife durchlaufen). Für meine Anwendung reicht es so auf jeden Fall.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.