|
|
|||||||||||||||
|
|||||||||||||||
|
|
|||||||||||||||
Um die Temperaturwerte über einen längeren Zeitraum zu bearbeiten, werden diese über einen längeren Zeitraum
erfasst und auf einer Micro-SD-Card gespeichert. Dazu werden als erstes die Grundlagen für
die Speicherung und Auswertung mit Hilfe einer Micro-Sd-Karte dargestellt.![]() |
![]() |
||||||||||||||
![]()
|
Das erste Programm schreibt Daten in eine Datei mit dem Dateinamen esp32.txt und gibt die Daten über den
seriellen Monitor aus.
Achtung:Vor dem Dateinamen muss ein / (Slash) eingefügt werden #include <SD.h > #define PIN_SPI_CS 5 // ESP32 pin GPIO5 File myFile; void setup() { Serial.begin(115200); if (!SD.begin(PIN_SPI_CS)) { while (1) { Serial.println(F("SD CARD FAILED, OR NOT PRESENT!")); delay(1000); } } Serial.println(F("SD CARD INITIALIZED.")); // Datei öffnen zum Schreiben myFile = SD.open("/esp32.txt", FILE_WRITE); if (myFile) { myFile.println("ESP32"); // Schreibe den Text ESP32 myFile.println("SD-Card Beispiel"); // Schreibe eine weitere Zeile in die Datei myFile.close(); } else { Serial.print(F("SD Card: Beim Versuch, die Datei zu öffnen, ist ein Problem aufgetreten esp32.txt")); } // Datei zum Lesen öffnen myFile = SD.open("/esp32.txt", FILE_READ); if (myFile) { while (myFile.available()) { char ch = myFile.read(); // Es werden die Buchstaben einer nach dem andern gelesen Serial.print(ch); // Die Buchstaben werden im seriellen Monitor geschrieben } myFile.close();// Die Datei wird geschlossen } else {Serial.print(F("SD Card: Beim Versuch, die Datei zu öffnen, ist ein Problem aufgetreten esp32.txt")); } } void loop() {} |
||||||||||||||
|
Für die Programmierung des ESP32 mit der Arduino-IDE gibt es eine Reihe von Befehlen, mit denen
man auf die Micro-SD-Card zugreifen kann. Als Ausgangslage sind folgende Vereinbarungen vorausgesetzt: #include <SD.h > #define PIN_SPI_CS 5 // ESP32 pin GPIO5 File myFile; Folgende Befehle sind verfügbar: 1. Datei öffnen myFile = SD.open("/test.txt"); 2. Datei im Schreibmodus öffnen myFile = SD.open("/test.txt", FILE_WRITE); 3. Datei schließen myFile.close(); 4. In die Datei schreiben myFile.println("Hallo"); myFile.print("Wert: "); myFile.println(123); |
5. Datei lesen while (myFile.available()) { Serial.write(myFile.read());} Oder zeilenweise: String line = myFile.readStringUntil('\n'); 6. Datei löschen SD.remove("/test.txt"); 7. Ordner erstellen SD.mkdir("/daten"); 8. Ordner löschen SD.rmdir("/daten"); 9. Dateigröße abfragen Serial.println(file.size()); 10. Prüfen, ob Datei existiert if (SD.exists("/test.txt")) { Serial.println("Datei existiert");} |
||||||||||||||
|
Im folgenden werden einige Programme zusammengestellt, die für die Programmierung der SD-Card nützlich
seine können. 1. Programm, das alle Dateien auf der vorhandenen SD-Card auflistet. #include <FS.h > #include <SPI.h > #include <SD.h > void listFiles(const char *dirname, uint8_t levels) { File root = SD.open(dirname); if (!root) { Serial.println("Fehler beim Öffnen"); return; } if (!root.isDirectory()) return; File file = root.openNextFile(); while (file) { Serial.println(file.name()); file = root.openNextFile(); } } void setup() { Serial.begin(115200); SD.begin(5); // je nach SD-Adapter listFiles("/", 0);} void loop() {} |
Hier ist die Ausgabe im seriellen Monitor: mode:DIO, clock div:1 load:0x3fff0030,len:4916 load:0x40078000,len:16436 load:0x40080400,len:4 ho 8 tail 4 room 4 load:0x40080404,len:3524 entry 0x400805b8 random_log.csv random_10.csv numbers.txt random_numbers.txt temp_log.csv zufallszahlen.txt zahlen.csv zahlen_mit_timestamp.csv temperatur_log.csv temperatur.csv |
||||||||||||||
|
2. Programm, das die Daten einer bekannten Datei im seriellen Monitor ausgibt. #include <SPI.h > #include <SD.h > // CS-Pin der SD-Karte (anpassen je nach Modul!) const int chipSelect = 5; void setup() { Serial.begin(115200); while (!Serial) {} Serial.println("Initialisiere SD-Karte..."); if (!SD.begin(chipSelect)) { Serial.println("Fehler: SD-Karte konnte nicht initialisiert werden."); return;} Serial.println("SD-Karte erkannt."); // Datei Öffnen File file = SD.open("/Zufallszahlen.txt"); if (!file) { Serial.println("Fehler: Datei '/Zufallszahlen.txt' konnte nicht geöffnet werden."); return;} Serial.println("\nInhalt von /zufallszahlen.txt:\n"); // Datei zeilenweise ausgeben while (file.available()) { String line = file.readStringUntil('\n'); Serial.println(line); } file.close(); Serial.println("\nDatei wurde vollständig gelesen.");} void loop() { } |
Hier ist die Ausgabe im seriellen Monitor: 63 9 38 73 34 2 30 54 67 84 Datei wurde vollständig gelesen. |
||||||||||||||
|
3. Programm, mit dem 10 Zufallszahlen gespeichert werden und dann im seriellen Monitor ausgegeben werden #include <SPI.h > #include <SD.h > const int chipSelect = 5; // Beispiel für CS-Pin (anpassen, je nach Verbindung) void setup() { Serial.begin(115200); while (!Serial) { } // Warten, bis serieller Monitor bereit ist Serial.println("SD-Karteninitialisierung..."); if (!SD.begin(chipSelect)) { Serial.println("SD-Karte erkannt."); File file = SD.open("/zahlen.txt", FILE_WRITE); if (!file) { Serial.println("Fehler beim Öffnen der Datei zum Schreiben."); return; } Serial.println("Zufallszahlen werden gespeichert..."); randomSeed(analogRead(0)); // Initialisierung des Zufallsgenerators for (int i = 0; i < 10; i++) { int zahl = random(0, 100); // Zufallszahl zwischen 0 und 99 file.println(zahl); } file.close(); Serial.println("Zahlen wurden gespeichert.\n"); // Jetzt lesen wir die Datei und geben sie im seriellen Monitor aus file = SD.open("/zahlen.txt"); if (!file) { Serial.println("Fehler beim Öffnen der Datei zum Lesen."); return; } Serial.println("Inhalt von /zahlen.txt:"); while (file.available()) { Serial.write(file.read()); } file.close();} void loop() {} |
Hier ist die Ausgabe im seriellen Monitor: Zufallszahlen: 26 73 98 36 49 81 65 36 14 21 |
||||||||||||||
|
4. Programm, mit dem 10 Zufallszahlen in einer CSV-Datei gespeichert werden und dann im seriellen
Monitor ausgegeben werden #include <SPI.h > #include <SD.h > const int chipSelect = 5; // Anpassen je nach deinem SD-Modul void setup() { Serial.begin(115200); while (!Serial) {} Serial.println("Initialisiere SD-Karte..."); if (!SD.begin(chipSelect)) { return;} Serial.println("SD-Karte erkannt."); // Datei zum Schreiben Öffnen (wird überschrieben) Serial.println("Fehler beim Öffnen der Datei zum Schreiben."); return;} Serial.println("Schreibe Zufallszahlen in zahlen.csv..."); // Überschrift (optional) file.println("Index,Zahl"); // Zufallszahlen erzeugen und schreiben randomSeed(analogRead(0)); // Zufallsgenerator initialisieren for (int i = 0; i < 10; i++) { int zahl = random(0, 100); // Zahl von 0 bis 99 file.print(i); file.print(","); file.println(zahl);} file.close(); Serial.println("Zahlen wurden gespeichert.\n"); // Datei öffnen und auslesen file = SD.open("/zahlen.csv"); if (!file) { Serial.println("Fehler beim Öffnen der Datei zum Lesen."); return;} Serial.println("Inhalt von zahlen.csv:"); while (file.available()) { Serial.write(file.read());} file.close();} void loop() {} |
Eine CSV-Datei (Comma-Separated Values) ist eine einfache Textdatei, die tabellarische Daten speichert,
wobei Werte durch ein Trennzeichen (meist ein Komma, oft auch ein Semikolon) getrennt sind,
wodurch sie sich ideal zum Austausch von Daten zwischen verschiedenen Programmen wie
Tabellenkalkulationen (Excel, Google Sheets) oder Datenbanken eignet. Textdatei: Es ist eine reine Textdatei, die von fast jedem Programm gelesen werden kann. Struktur: Jede Zeile ist ein Datensatz, und die Spalten (Felder) werden durch ein Zeichen getrennt. Dateiendung: .csv. Hier ist die Ausgabe im seriellen Monitor: Initialisiere SD-Karte... Schreibe Zufallszahlen in zahlen.csv... Zahlen wurden gespeichert. Inhalt von zahlen.csv: Index,Zahl 0,60 1,96 2,30 3,69 4,17 5,50 6,35 7,17 8,87 9,28 |
||||||||||||||
|
5. Programm, mit dem 10 Zufallszahlen mit einem sog. timestamp in einer CSV-Datei gespeichert werden und dann im seriellen
Monitor ausgegeben werden #include <SPI.h > #include <SD.h > #include < WiFi.h > #include < time.h > // WLAN-Zugangsdaten const char* ssid = "WLAN-PDGCKM"; const char* password = "4535897740339072"; const int chipSelect = 5;// SD-Karten CS-Pin (anpassen!) // NTP-Zeiteinstellungen const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 3600; // MEZ = +1 Stunde const int daylightOffset_sec = 3600; // Sommerzeit = +1h void setup() { Serial.begin(115200); while (!Serial) {} // WLAN verbinden Serial.printf("Verbinde mit WLAN: %s\n", ssid); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print(".");} Serial.println("\nWLAN verbunden."); // Zeit synchronisieren configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); struct tm timeinfo; if (!getLocalTime(&timeinfo)) { Serial.println("Zeit konnte nicht synchronisiert werden."); return; } Serial.println("Zeit synchronisiert."); // SD-Karte initialisieren Serial.println("Initialisiere SD-Karte..."); if (!SD.begin(chipSelect)) { Serial.println("SD-Karte konnte nicht initialisiert werden."); return; } Serial.println("SD-Karte bereit."); // Datei Öffnen (überschreibt bestehende Datei) File file = SD.open("/zahlen_mit_timestamp.csv", FILE_WRITE); if (!file) { Serial.println("Fehler beim Öffnen der Datei zum Schreiben."); return; } // CSV-Header schreiben file.println("Zeit,Zufallszahl"); randomSeed(analogRead(0)); // Zufall initialisieren // 10 Zufallszahlen erzeugen und mit Zeitstempel speichern for (int i = 0; i < 10; i++) { int zahl = random(0, 100); // Zufallszahl 0 bis 99 // Aktuelle Zeit holen if (!getLocalTime(&timeinfo)) { Serial.println("Fehler beim Holen der Uhrzeit."); continue; } // Zeitformat erstellen char timeString[20]; strftime(timeString, sizeof(timeString), "%Y-%m-%d %H:%M:%S", &timeinfo); // In Datei schreiben file.print(timeString); file.print(","); file.println(zahl); delay(1000); } file.close(); Serial.println("Zahlen wurden gespeichert.\n"); // Datei Öffnen zum Lesen file = SD.open("/zahlen_mit_timestamp.csv"); if (!file) { Serial.println("Fehler beim Öffnen der Datei zum Lesen."); return;} Serial.println("Inhalt von zahlen_mit_timestamp.csv:"); while (file.available()) { Serial.write(file.read()); } file.close();} void loop() {} |
Hier ist die Ausgabe im seriellen Monitor: Verbinde mit WLAN: WLAN-PDGCKM .......... WLAN verbunden. Zeit synchronisiert. Initialisiere SD-Karte... SD-Karte bereit. Zahlen wurden gespeichert. Inhalt von zahlen_mit_timestamp.csv: Zeit,Zufallszahl 2026-01-04 18:32:54,75 2026-01-04 18:32:55,53 2026-01-04 18:32:56,3 2026-01-04 18:32:57,13 2026-01-04 18:32:58,97 2026-01-04 18:32:59,23 2026-01-04 18:33:00,19 2026-01-04 18:33:01,23 2026-01-04 18:33:02,75 2026-01-04 18:33:03,36 |
||||||||||||||
|
|
|||||||||||||||
|
Im folgenden Teil werden die auf einer SD-Card gespeicherten Daten mit der
Processing-4-IDE graphisch dargestellt. Dazu gibt es zwei Möglichkeiten: 1. Die SD-Card wird in einen SD-Card-Reader gesteckt und der Zugriff erfolgt vom Processing-4-Programm direkt auf die Datei. Man benötigt also keinen Sketch in der Arduino-IDE. 2. Die Datei wird über die Arduino-IDE in der seriellen Schnittstelle geöffnet und über diese Schnittstelle hat das Processing-Programm Zugriff auf die Daten. Bevor damit begonnen wird, sollen noch einige sinnvolle Befehle in der Processing-IDE angegeben werden. Manchmal ist es sinnvoll, eine andere Schriftart zu benutzen. Dazu ist es hilfreich, zu wissen, welche Schriftarten überhaupt zur Verfügung stehen. Denn bei der Benutzung der Schriftarten ist es wichtig, den Namen genau zu wissen, da nur der richtige Eintrag akzeptiert wird. Nachfolgend der Processing-Code: void setup() { String[] fonts = PFont.list(); printArray(fonts); } Mit diesem Programm werden alle verfügbaren Schriftarten ausgedruckt und zwar im unteren Ausgabefeld der Processing-IDE. Benutztwurde Processing 4 mit der Hardware Raspberry Pi 5 und dem OS bookworm-64 Bit. Es sind sehr viele Schriftarten verfügbar – ungefähr 500. ![]() |
Es folgt ein Programm, mit dem man einen Text mit einer angegebenen Schriftart ausgibt und dieses
Fenster als Bilddatei abspeichert. Diese Bilddatei wird in dem Ordner gespeichert, indem
der Processing-Code abgelegt ist. PFont font; void setup() { font =createFont("Noto Serif SemiCondensed Bold",26); textFont(font); // ab jetzt erfolgt die Ausgabe mit der vereinbarten Schriftart } void draw() { background(255); fill(0); text("Hallo Processing!", 50, 100); saveFrame("bild_font.png"); // speichert im Sketch-Ordner. Der Name der Bilddatei ist frei wählbar. noLoop();} ![]() Achtung: Die Schriftarten variieren je nach Betriebssystem. So ist z.B. Arial unter dem OS-bookworm beim Rasberry Pi nicht verfügbar. |
||||||||||||||
|
Jetzt kommen wir zur graphischen Darstellung einer CSV-Datei mit Hilfe der Processing 4 - IDE. Die CSV-Datei muss
dabei im Unterordner data des Sketch-Ordners liegen, in dem der Processing-Sketch gespeichert wurde. Hier muss
man auf folgendes achten: der Sketch-Ordner wird erst dann erzeugt, wenn Processsing geschlossen wird. Dann muss
der Code abgespeichert werden. Also muss man, bevor man das Programm ausführt, erst einmal abspeichern.
Jetzt ist der Ordner bekannt. Man verzweigt dahin und erzeugt ein Verzeichnis data. In dieses kopiert man die CSV-Datei,
die man benutzen will. Processing-Code Table table; float[] values; String[] timestamps; PFont myFont; void setup() { size(1000, 600); background(255); myFont=createFont("Arial",16); table = loadTable("zahlen_mit_timestamp.csv", "header"); int rowCount = table.getRowCount(); values = new float[rowCount]; timestamps = new String[rowCount]; for (int i = 0; i < rowCount; i++) { TableRow row = table.getRow(i); values[i] = row.getFloat("Zufallszahl"); timestamps[i] = row.getString("Zeit"); } drawGraph();} void drawGraph() { float margin = 80; float graphWidth = width - 2 * margin; float graphHeight = height - 2 * margin; // Skalierung float minValue = min(values); float maxValue = max(values); // Achsen zeichnen stroke(0); strokeWeight(1); line(margin, margin, margin, height - margin); // Y-Achse line(margin, height - margin, width - margin, height - margin); // X-Achse |
// Achsenbeschriftung fill(0); textAlign(CENTER); //textSize(14); textFont(myFont); text("Zeit", width / 2, height - 30); pushMatrix(); translate(30, height / 2); rotate(-HALF_PI); text("Wert", 0, 0); popMatrix(); // Y-Achse Skalierung int yTicks = 5; for (int i = 0; i <= yTicks; i++) { float y = map(i, 0, yTicks, height - margin, margin); float valueLabel = map(i, 0, yTicks, minValue, maxValue); stroke(200); line(margin, y, width - margin, y); // horizontale Hilfslinien fill(0); textAlign(RIGHT, CENTER); text(nf(valueLabel, 1, 1), margin - 10, y); } // Datenpunkte & Linien zeichnen float xSpacing = graphWidth / (values.length - 1); stroke(50, 100, 250); strokeWeight(2);// Dicke des Graphen wird vereinbart noFill(); beginShape(); for (int i = 0; i < values.length; i++) { float x = margin + i * xSpacing; float y = map(values[i], minValue, maxValue, height - margin, margin); vertex(x, y); } endShape(); // X-Achse Beschriftungen (alle n-ten Zeitstempel) int xLabelStep = max(1, values.length / 10); // max 10 Labels textSize(9); textAlign(CENTER, TOP); for (int i = 0; i < values.length; i += xLabelStep) { float x = margin + i * xSpacing; text(timestamps[i], x, height - margin + 5); } saveFrame("Graph_CSV_Datei.png"); // speichert im Sketch-Ordner noLoop();} Processing-Code für graphische Darstellung einer CSV-Datei |
||||||||||||||
![]() | |||||||||||||||
| Graphische Ausgabe der Daten von einer SD-Card mit Hilfe von Processing 4 über die serielle Schnittstelle | |||||||||||||||
|
Im folgenden Abschnitt werden die erhobenen Daten, die auf einer SD-Card gespeichert wurden, in der
Processing-IDE graphische dargestellt. Der Zugriff auf die vorhandenen Daten geschieht über die serielle Schnittstelle. Man
braucht also die CSV-Datei nicht in den Ordner des Sketch-Programms der Processing-IDE zu verschieben. Als erstes müssen mit einem Arduino-Sketch die Daten über die serielle Schnittstelle für andere Programme (hier: Processing 4) zugänglich gemacht werden. Dies geschieht mit folgendem kurzen Programm. Arduino-Sketch #include <SPI.h > #include <SD.h > // CS-Pin der SD-Karte (anpassen je nach Modul!) const int chipSelect = 5; void setup() { Serial.begin(115200); while (!Serial) {} Serial.println("Initialisiere SD-Karte..."); if (!SD.begin(chipSelect)) { Serial.println("Fehler: SD-Karte konnte nicht initialisiert werden."); return;} Serial.println("SD-Karte erkannt."); // Datei öffnen File file = SD.open("/zufall.csv"); if (!file) { Serial.println("Fehler: Datei '/zufall.csv' konnte nicht geöffnet werden."); return;} Serial.println("\nInhalt von /zufall.csv:\n"); // Datei zeilenweise ausgeben while (file.available()) { String line = file.readStringUntil('\n'); Serial.println(line); delay(100);} file.close(); Serial.println("\nDatei wurde vollständig gelesen.");} void loop() {} Dieser Sketch muss jetzt hochgeladen werden, damit die Daten mit Hilfe der seriellen Schnittstelle zur Verfügung gestellt werden können. Zur Kontrolle kann jetzt in den seriellen Monitor geschaltet werden. Dazu sollte der Reset-Button am ESP32 gedrückt werden. Wenn danach der Processing-Code gestartet wird, muss der serielle Monitor geschlossen werden, um den Port für den Processing-Code freizugeben. Die beiden Sketche können hier heruntergeladen werden. ![]() Die Angaben für die Zeitangabe (Beispiel: 2026-01-04 18:32:54)sind sehr lang, so dass die einzelnen Werte sich überschneiden. Bei kürzerem Text für die Zeitangabe, z.B. 1, 2, 3 usw., ist dies kein Problem. Bei dieser Länge jedoch würde man die Beschriftung besser um 90 Grad drehen, also eine senkrechte Beschriftung angeben. Hinweis für die Darstellung in Processing 4: Die Dicke der Linie des Graphen kann man mit folgendem Befehl verändern: strokeWeight(4);- je größer der Zahlenwert je dicker die Linie. Auch die Farbe des Graphen kann man ändern: stroke(100, 100, 250); - Angabe im RGB-Modus. Die erzeugt Grafik man man seperat als Bilddatei mit folgendem Befehl abspeichern: saveFrame("Graph_CSV_Datei.png"); - die Datei wird im Ordner der Sketch-Datei abgespeichert. |
Processing-Sketch import processing.serial.*; PFont font; Serial myPort; ArrayList ArrayList int margin = 50; void setup() { size(1000, 600); background(255); font =createFont( "Noto Serif SemiCondensed Medium",26); textFont(font); println(Serial.list()); myPort = new Serial(this, "/dev/ttyUSB0", 115200); // Unter Windows COM-Port eintragen myPort.bufferUntil('\n');} void draw() { background(255); if (temps.size() < 2) return; // mindestens 2 Werte // Max/Min Temperatur float tMax = temps.get(0); float tMin = temps.get(0); for (float t : temps) { if (t > tMax) tMax = t; if (t < tMin) tMin = t; } if (tMax == tMin) tMax = tMin + 1; // Achsen stroke(0); line(margin, height-margin, width-margin, height-margin); // X-Achse line(margin, margin, margin, height-margin); // Y-Achse // Gridlines Y int ySteps = 5; for (int i = 0; i <= ySteps; i++) { float yVal = map(i, 0, ySteps, height-margin, margin); float tempVal = map(i, 0, ySteps, tMin, tMax); stroke(200); line(margin, yVal, width-margin, yVal); fill(0); textAlign(RIGHT, CENTER); text(nf(tempVal, 1, 1) + " °C", margin-10, yVal); } // X-Achse: Zeit als Strings int n = temps.size(); for (int i = 0; i < n; i++) { float xVal = map(i, 0, n-1, margin, width-margin); stroke(200); line(xVal, height-margin, xVal, margin); fill(0); textAlign(CENTER, TOP); text(times.get(i), xVal, height-margin+5); } // Kurve stroke(255, 0, 0); noFill(); beginShape(); for (int i = 0; i < n; i++) { float x = map(i, 0, n-1, margin, width-margin); float y = map(temps.get(i), tMin, tMax, height-margin, margin); vertex(x, y);} endShape(); // Achsenbeschriftung fill(0); textAlign(CENTER); text("Zeit", width/2, height-10); pushMatrix(); translate(15, height/2); rotate(-HALF_PI); text("Temperatur (°C)", 0, 0); popMatrix();} void serialEvent(Serial myPort) { String line = myPort.readStringUntil('\n'); if (line != null) { line = line.trim(); if (line.length() > 0) { String[] parts = split(line, ','); if (parts.length == 2) { try { String timeStr = parts[0]; float temp = float(parts[1]); if (!Float.isNaN(temp)) { times.add(timeStr); temps.add(temp); } } catch (Exception e) { println("Ungültige Zeile: " + line);} } } } saveFrame("Graph_Processing_Waagrecht.png"); // Fenster wird als PNG_Datei gespeichert } |
||||||||||||||
![]() Hier ist die Beschriftung senkrecht angebracht. Der Programm-Code für Processing ist fast identisch bis auf den Befehl rotate(-HALF_PI); // 90° nach links drehen, der den String um 90 Grad dreht. Der Quellcode des Processing- Sketch kann hier heruntergeladen werden. | |||||||||||||||
|
|
|||||||||||||||
| Graphische Ausgabe der Daten von einer SD-Card mit Hilfe von Python/Matplotlib über den direkten Zugriff auf die SD-Card | |||||||||||||||
|
Ähnlich wie mit der Processing-IDE gibt es auch mit Python zwei Möglichkeiten des Zugriffs auf die SD-Card
und der graphischen Darstellung der gespeicherten Daten. Die einfachere Möglichkeit ist der direkte Zugriff auf
die (CSV-)Datei. Die zu bearbeitende Datei muss hier im selben Verzeichnis liegen, in dem auch der Python-Code abgespeichert wurde.
Das bedeutet insbesondere, dass man bevor man den Code ausführt, diesen abspeichern muss, um den Ort des
Abspeicherns zu wissen. In diesen Ordner kopiert man dann die entsprechende CSV-Datei. Das soll als erstes durchgeführt werden. ![]() |
Der Python-Code import matplotlib.pyplot as plt import pandas as pd # CSV-Datei einlesen df = pd.read_csv("daten.csv", parse_dates=["Datum"]) # Daten anzeigen (optional) print(df) # Plot erstellen plt.figure(figsize=(10, 5)) plt.plot(df["Datum"], df["Temperatur"], marker="o", linestyle="-") # Formatierung plt.title("Temperaturverlauf") plt.xlabel("Datum") plt.ylabel("Temperatur (°C)") plt.grid(True) # wenn die Beschriftung zu lang für die waagrechte Anordnung ist. # kann man den Text auch drehen - hier um 45 Grad plt.xticks(rotation=45) plt.tight_layout() plt.savefig("CSV_Datei_direkter_Zugriff_25_01_2026.png") plt.show() |
||||||||||||||
| Graphische Ausgabe der Daten von einer SD-Card mit Hilfe von Python/Matplotlib mit dem Zugriff über die serielle Schnittstelle auf die Daten der SD-Card. | |||||||||||||||
|
Als erstes wird der Arduino-Sketch hochgeladen, der die Daten der CSV-Datei über die serielle Schnittstelle zur
Verfügung stellt. Zur Überprüfung der Daten kann man den seriellen Monitor aufrufen (Reset-Button am ESP32 drücken).
Dann ruft man das Python-Programm auf, nicht ohne den seriellen Monitor wegen der Belegung des Ports zu schließen.
Jetzt muss auch hier noch einmal der Reset-Button gedrückt werden, um Zugriff auf die Daten über die serielle Schnittstelle
zu erhalten. Arduibo-Sketch #include <SPI.h > #include <SD.h > File file; void setup() { Serial.begin(115200); while (!Serial); if (!SD.begin(5)) { // CS-Pin ggf. anpassen Serial.println("SD Fehler"); return; } file = SD.open("/zahlen.csv"); if (!file) { Serial.println("Datei nicht gefunden"); return;} // CSV senden while (file.available()) { Serial.println(file.readStringUntil('\n')); delay(5); // kleine Bremse für Python } file.close(); Serial.println("EOF"); // Marker für Python } void loop() {} |
Der Python-Code import serial import matplotlib.pyplot as plt ser = serial.Serial( port='/dev/ttyUSB0', # ggf. /dev/ttyACM0 baudrate=115200, timeout=1 ) x = [] y = [] print("Empfange Daten vom ESP32...") while True: line = ser.readline().decode('utf-8').strip() if not line: continue if line == "EOF": print("Fertig") break try: idx, val = line.split(',') x.append(int(idx)) y.append(float(val)) except ValueError: pass ser.close() plt.figure(figsize=(8, 4)) plt.plot(x, y, marker='o') plt.xlabel("Index") plt.ylabel("Wert") plt.title("ESP32 Messwerte (SD → Serial → Raspberry Pi)") plt.grid(True) plt.savefig("grafik_24_01_2026.png") plt.show() |
||||||||||||||
![]() |
|||||||||||||||