|
|
|||||||||||||||
|
|
|||||||||||||||
|
|
|||||||||||||||
|
1. Der erste Arduino-Sketch führt die Speicherung der Messdaten durch: gespeichert werden einmal die Zeit und der dazugehörige Temperaturwert in einer CSV-Datei, die sich auf einer Micro-SD-Card abgelegt wird. Dabei gibt es die Möglichkeit, das wirkliche aktuelle Datum zu speichern. Dies erfordert aber einen Webserver, da man mit Hilfe der Library time.h die Zeit aus dem Internet holen muss. So ein Datum hat dann die Form 2026-01-30 21:50:01. Für die graphische Darstellung ist die Beschriftung der Zeitachse mit dieser Angabe mit zusätzlichem Aufwand verbunden. Geht aber. Die andere Möglichkeit besteht darin, mit Hilfe von delay(Millisekunden) die Messungen jeweils nach der Verzögerung durch delay() den nächsten Temperaturwert zu speichern. Der Zeitwert besteht hier nur aus einer Zahl. Um die Angabe noch kürzer zu machen, kann man den Wert durch 1000 teilen und erhält damit als Einheit Sekunden. 2. Die Übermittlung der Messwerte geschieht über die serielle Schnittstelle an das Python-Programm. Dazu benötigt man einen weiteren Arduino-Sketch, der dieen Vorgang durchführt. 3. Dann startet man das Python-Programm, das so eingerichtet ist, dass man für den Start des Übertragungsvorgangs den Reset-Button am ESP32 drücken muss. Anschließend werden die durch Anzahl und Abstand festgelegten Messwerte erzeugt. |
|||||||||||||||
|
Als erstes soll der Arduino-Sketch mit Hilfe von delay(Millisekunden) die Messwerte auf die Micro-Karte
schreiben. Die Werte umfassen einmal eine Zeitangabe und dann natürlich den Temperaturwert. Ohne großen
Aufwand kann man folgendes realisieren: Man setzt die Anzahl der Messpunkt und den Abstand der Messungen fest. Die Anzahl wird durch eine
for-Schleife realisiert und der Abstand durch Einfügen des Befehls delay(Millisekunden). Das bedeutet im Besonderen, dass man
für jede einzelne Messung in den Programmcode gehen und dort die entsprechenden Variablen verändern muss. So geht man vor: der Arduino-Sketch wird hochgeladen. Danach geht man in den seriellen Monitor und startet mit Drücken des Reset-Buttons beim ESP32. Man wird jetzt aufgefordert, s in den seriellen Monitor einzugeben. Jetzt werden die einzelnen Messungen durchgeführt. Ist dies geschehen, meldet das Programm Messvorgang abgeschlossen Ausgabe auf dem seriellen Monitor 18013 korfmann System startet... SD-Karte bereit. Bereit. 's' eingeben zum Starten der Messung. Messung gestartet. Messung 1, 23.87 °C Messung 2, 24.00 °C Messung 3, 25.12 °C Messung 4, 26.19 °C Messung 5, 26.87 °C Messung 6, 27.56 °C Messung 7, 28.06 °C Messung 8, 28.00 °C Messung 9, 27.87 °C Messung 10, 27.75 °C Messvorgang abgeschlossen. |
![]() |
||||||||||||||
![]()
Die erzeugte Datei heißt daten_50.csv und hat folgenden Inhalt: Initialisiere SD-Karte... SD-Karte erkannt. Inhalt von /daten_50.csv: Zeit_ms;Temperatur_C 0,83.75 30,83.12 .... 1410,53.88 1440,53.50 1470,53.19 Datei wurde vollständig gelesen Die Datei kann hier heruntergeladen werden. Die Datei daten_50.csv |
Arduino-Sketch zur Erzeugung der Messreihe #include <OneWire.h> #include <DallasTemperature.h> #include <SPI.h> #include <SD.h> / ---------------- Einstellungen ---------------- #define ONE_WIRE_BUS 2 // GPIO für DS18B20 #define SD_CS 5 // Chip Select SD-Karte const int anzahlMessungen = 50; // Anzahl der Messungen const unsigned long messAbstandMs = 30000; // 30 Sekunden // ------------------------------------------------ // Objekte OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); File datei; // Status bool messungGestartet = false; // ------------------------------------------------ void setup() {Serial.begin(115200); Serial.println("System startet..."); // Temperatursensor initialisieren sensors.begin(); // SD-Karte initialisieren if (!SD.begin(SD_CS)) { Serial.println("SD-Karte nicht gefunden!"); while (true);} Serial.println("SD-Karte bereit."); // CSV-Header schreiben (optional) datei = SD.open("/daten_50.csv", FILE_APPEND); if (datei) { datei.println("Zeit_ms;Temperatur_C"); datei.close(); } Serial.println("Bereit. 's' eingeben zum Starten der Messung.");} // ------------------------------------------------ void loop() {// Start per Serial Monitor if (!messungGestartet && Serial.available() > 0) { char cmd = Serial.read(); if (cmd == 's') { messungGestartet = true; Serial.println("Messung gestartet."); startMessung();} } } // ------------------------------------------------ void startMessung() { for (int i = 0; i < anzahlMessungen; i++) { float temperatur = sensors.getTempCByIndex(0); unsigned long zeit = millis(); // Ausgabe im Serial Monitor Serial.print("Messung "); Serial.print(i + 1); Serial.print(", "); Serial.print(temperatur); Serial.println(" °C"); // Daten auf SD-Karte speichern if (datei) { // datei.print(zeit); datei.print(i*messAbstandMs/1000);// das sind dann Sekunden datei.print(","); datei.println(temperatur); datei.close(); } else {Serial.println("Fehler beim Schreiben auf SD!");} delay(messAbstandMs);} Serial.println("Messvorgang abgeschlossen.");} |
||||||||||||||
|
Die Messwerte sollen jetzt mit Hilfe von Python/Matplotlib grafisch dargestellt werden.
Die Werte werden über die serielle Schnittstelle für das Python-Programm zur Verfügung
gestellt. Das bedeutet, dass mit einem Arduino-Sketch die Daten in die serielle Schnittstelle
transportiert werden. Der Sketch wird also hochgeladen. Dann wird das entsprechende Python-Programm
gestartet. Man erhält die Meldung Empfange Daten vom ESP32.... Jetzt muss
der Reset-Buttton am ESP32 gedrückt werden. Nach einer gewissen Zeit erscheint die Grafik und die
Medlung fertig. Mit dem Befehl im Python-Programm plt.savefig("grafik_24_01_2026_01.png")
wird die Grafik im Verzeichnis, in dem das Python-Programm abgespeichert ist, abgelegt und kann weiter in
anderen Programmen verwendet werden. Arduino-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("/daten_50.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() |
||||||||||||||
![]() | |||||||||||||||
|
|
|||||||||||||||
|
Der Sketch, der im vorigen Beispiel durch Ändern im Code die Anzahl bzw. Abstand für die Messreihe benutzt wurde,
wird jetzt komfortabler gestaltet. Im Webbrowser kann man jetzt die Anzahl der Messungen und den Abstand zwischen den
Messungen einstellen. Hat man dies getan, startet man den Vorgang durch Drücken des Start-Buttons. Hier wird
jetzt die IP-Adreese angezeigt. Diese gibt man in den Browser und startet damit den Webserver. In der Oberfläche
gibt man dann die Anzahl der Messungen und den Abstand ein und drückt den Start-Button, um die Messung zu beginnen. Die
Messungen werden entsprechend den Eingaben durchgeführt. Bei Beendigung des Messvorgangs
meldet sich der Sketch mit Messung abgeschlossen. Anzeige im seriellen Monitor SD Karte bereit. Verbinde WLAN....... . WLAN verbunden IP Adresse: 192.168.2.102 Webserver gestartet. 2026-02-12 18:10:03,84.06 2026-02-12 18:10:33,83.69 ... 2026-02-12 18:34:03,54.44 2026-02-12 18:34:33,54.06 === Messung abgeschlossen === Die Datei kann hier heruntergeladen werden. Die Datei log_80.csv ![]() ![]() ![]() |
Arduino-Sketch zur Durchführung der Messung #include <WiFi.h> #include <WebServer.h> #include <OneWire.h> #include <DallasTemperature.h> #include <SPI.h> #include <SD.h> #include <time.h> // ================= WLAN DATEN ================= const char* ssid = "WLAN-PDGCKM"; const char* password = "4535897740339072"; // ================= PIN DEFINITIONS ================= #define ONE_WIRE_BUS 2 #define SD_CS 5 // ================= OBJEKTE ================= WebServer server(80); OneWire oneWire(ONE_WIRE_BUS); DallasTemperature sensors(&oneWire); // ================= ZEIT ================= const char* ntpServer = "pool.ntp.org"; const long gmtOffset_sec = 3600; // Deutschland const int daylightOffset_sec = 3600; // Sommerzeit // ================= MESSVARIABLEN ================= bool measurementRunning = false; bool measurementFinished = false; int totalMeasurements = 0; int intervalSeconds = 0; int currentMeasurement = 0; unsigned long lastMeasurementTime = 0; File logFile; // ================= ZEITSTEMPEL ================= String getTimeStamp() { struct tm timeinfo; if (!getLocalTime(&timeinfo)) { return "NO_TIME"; } char buffer[25]; strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", &timeinfo); return String(buffer);} // ================= HTML SEITE ================= String htmlPage() { String html = "<!DOCTYPE html><html><head><meta charset='UTF-8'>"; html += "<title>ESP32 Temperatur Logger</title></head>"; html += "<body>"; html += "<h2>Temperatur Messung</h2>"; html += "<form action='/start' method='get'>"; html += "Anzahl Messungen: "; html += "<input type='number' name='count' min='1' required><br><br>"; html += "Abstand (Sekunden): "; html += "<input type='number' name='interval' min='1' required><br><br>"; html += "<input type='submit' value='Start'>"; html += "</form><br>"; html += "<div id='status'>Bereit</div>"; // JavaScript für Live-Status html += "<script>>"; html += "setInterval(function(){"; html += "fetch('/status')"; html += ".then(response => response.json())"; html += ".then(data => {"; html += "if(data.running){"; html += "document.getElementById('status').innerHTML = " "'Messung läuft... (' + data.current + '/' + data.total + ')';"; html += "} else if(data.finished){"; html += "document.getElementById('status').innerHTML = " "'<span style=\"color:green;font-weight:bold;\">Messung abgeschlossen!</span>';"; html += "} else {"; html += "document.getElementById('status').innerHTML = 'Bereit';"; html += "}"; html += "});"; html += "}, 1000);"; // jede Sekunde aktualisieren html += "</script>"; html += "</body></html>"; return html;} // ================= WEB HANDLER ================= void handleRoot() { server.send(200, "text/html", htmlPage());} void handleStart() { if (server.hasArg("count") && server.hasArg("interval")) { totalMeasurements = server.arg("count").toInt(); intervalSeconds = server.arg("interval").toInt(); if (totalMeasurements <= 0 || intervalSeconds <= 0) { server.send(200, "text/html", "<h3>Ungültige Eingabe!</h3><a href='/'>Zurück</a>"); return;} currentMeasurement = 0; measurementRunning = true; measurementFinished = false; logFile = SD.open("/log_80.csv", FILE_APPEND); if (logFile) { logFile.println("Timestamp,Temperature_C"); logFile.flush();} lastMeasurementTime = millis();} server.send(200, "text/html", htmlPage());} void handleStatus() { String json = "{"; json += "\"running\":" + String(measurementRunning ? "true" : "false") + ","; json += "\"finished\":" + String(measurementFinished ? "true" : "false") + ","; json += "\"current\":" + String(currentMeasurement) + ","; json += "\"total\":" + String(totalMeasurements); json += "}"; server.send(200, "application/json", json);} // ================= SETUP ================= void setup() { Serial.begin(115200); server.on("/status", handleStatus); sensors.begin(); // SD Karte initialisieren if (!SD.begin(SD_CS)) { Serial.println("SD Karte Fehler!"); } else { Serial.println("SD Karte bereit.");} // WLAN verbinden WiFi.begin(ssid, password); Serial.print("Verbinde WLAN"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print(".");} Serial.println("\nWLAN verbunden"); Serial.print("IP Adresse: "); Serial.println(WiFi.localIP()); // Zeit konfigurieren configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); // Webserver server.on("/", handleRoot); server.on("/start", handleStart); server.begin(); Serial.println("Webserver gestartet.");} // ================= LOOP ================= void loop() { server.handleClient(); if (measurementRunning) { if (millis() - lastMeasurementTime >= intervalSeconds * 1000UL) { lastMeasurementTime = millis(); sensors.requestTemperatures(); float temperature = sensors.getTempCByIndex(0); String timestamp = getTimeStamp(); String line = timestamp + "," + String(temperature); if (logFile) { logFile.println(line); logFile.flush();} Serial.println(line); currentMeasurement++; if (currentMeasurement >= totalMeasurements) { measurementRunning = false; if (logFile) {logFile.close();} Serial.println("=== Messung abgeschlossen ===");} } } } |
||||||||||||||
|
Jetzt kommen wir zur graphischen Darstellung einer CSV-Datei mit Python/Matplotlib. Die Werte der Datei müssen
über die serielle Schnittstelle dem Pythonprogramm zur Verfügung gestellt werden. Dazu benötigt
man wie oben einen Arduino-Sketch. Arduino-Sketch #include <SPI.> #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..."); Serial.println("Fehler: SD-Karte konnte nicht initialisiert werden."); return; } Serial.println("SD-Karte erkannt."); // Datei öffnen File file= SD.open("/log_80.csv"); // File file = SD.open("/daten.csv"); if (!file) { Serial.println("Fehler: Datei '/log_80.csv' konnte nicht geöffnet werden."); return;} Dieser Sketch wird hochgeladen. Zur Kontrolle kann man den seriellen Monitor öffnen und sieht dann die Daten der angegebenen Datei. Als nächstes muss man das Python-Programm aufrufen und starten. Zuvor sollte der serielle Monitor wieder geschlossen werden. Man erhält die Meldung:Empfange Daten.... Man drückt den Reset-Button auf dem ESP32. Das bewirkt, dass die Daten in den seriellen Monitor geladen werden und vom Python-Programm gelesen werden können. Nach kurzer Zeit wird dann die Grafik mit den Messwerten angezeigt. |
Das Python-Programm import serial import matplotlib.pyplot as plt from datetime import datetime import time ser = serial.Serial('/dev/ttyUSB0', 115200, timeout=1) times = [] values = [] last_data_time = time.time() timeout_seconds = 3 print("Empfange Daten...") while True: line = ser.readline().decode('utf-8', errors='ignore').strip() if line: last_data_time = time.time() if time.time() - last_data_time > timeout_seconds: print("Datenübertragung beendet") break if line.count(",") != 1: continue try: ts, val = line.split(",") times.append(datetime.strptime(ts, "%Y-%m-%d %H:%M:%S")) values.append(float(val)) except ValueError: continue ser.close() if times: plt.figure(figsize=(10,4)) plt.plot(times, values, marker='o') plt.xlabel("Zeit") plt.ylabel("Temperatur (°C)") plt.grid(True) plt.tight_layout() plt.savefig("Grafik_12_02_2026_02.png") plt.show() else: |
||||||||||||||
![]() | |||||||||||||||
|
|
|||||||||||||||
|
| |||||||||||||||
|
Von Python aus kann man auch direkt auf die CSV-Datei zugreifen. Man muss also keinen Arduino-Sketch hochladen,
um die Daten über die serielle Schnittstelle dem Python-Programm zur Verfügung zu stellen. Dafür muss
aber die CSV-Datei in einen Ordner des RPi kopiert werden. Am einfachsten geschieht das dadurch, dass man die Microcard
über einen Card-Reader mit dem RPi verbindet und die benötigte Datei am besten in den Ordner kopiert, indem
das Python-Programm gespeichert ist. Es gibt die Möglichkeit ohne Umstecken der Micro-Card. Für diesen Methode benötigt man jedoch zwei Programme. Als erstes benötigt man einen Arduino-Sketch, der die Messdaten in die serielle Schnittstelle kopiert. Anschließend wird ein Python-Programm gestartet, das diese Daten liest und in eine CSV-Datei auf dem Raspberry Pi schreibt. Dabei kann man den Dateinamen ändern und auch der Speicherort kann gewählt werden. Arduino-Sketch #include <SPI.h > #include <SD.h > #define SD_CS 5 // ggf. anpassen File file; void setup() { Serial.begin(115200); delay(2000); while (!Serial); if (!SD.begin(SD_CS)) { Serial.println("ERROR:SD_FAIL"); return; } file = SD.open("/log_80.csv", FILE_READ); if (!file) { Serial.println("ERROR:FILE_FAIL"); return;} // Dateiname senden Serial.println("FILENAME:log_80.csv"); // Dateigröße senden Serial.print("SIZE:"); Serial.println(file.size()); // Kleine Pause, damit Python bereit ist delay(200); // Datei senden uint8_t buffer[512]; while (file.available()) { int len = file.read(buffer, sizeof(buffer)); Serial.write(buffer, len);} file.close();} void loop() {} Dieser Sketch wird hochgeladen. Arduino-Sketch: Übertragung einer CSV-Datei auf den RPi |
Der Python-Code import serial PORT = "/dev/ttyUSB0" BAUD = 115200 ZIELORDNER = "/home/montblanc/csv_logs" # eventuell montblanc-Namen ändern in z.B. pi os.makedirs(ZIELORDNER, exist_ok=True) ser = serial.Serial(PORT, BAUD, timeout=5) print("Warte auf Dateiname...") # Solange lesen bis FILENAME kommt while True: line = ser.readline().decode(errors="ignore").strip() print("Empfangen:", line) # Debug-Ausgabe if line.startswith("FILENAME:"): filename = line.split(":", 1)[1] break print("Dateiname:", filename) # Jetzt Größe lesen while True: line = ser.readline().decode(errors="ignore").strip() print("Empfangen:", line) if line.startswith("SIZE:"): filesize = int(line.split(":", 1)[1]) break print(f"Empfange {filename} ({filesize} Bytes)") filepath = os.path.join(ZIELORDNER, filename) with open(filepath, "wb") as f: remaining = filesize while remaining > 0: chunk = ser.read(min(512, remaining)) if not chunk: break f.write(chunk) remaining -= len(chunk) ser.close() print("Übertragung abgeschlossen!") print("Gespeichert unter:", filepath) |
||||||||||||||
|
| |||||||||||||||
|
|||||||||||||||
![]() |
|||||||||||||||