Labortagebuch - Arduino - Raspberry Pi
10.02.2026 - ESP32 - SD-Card - Data Logger - DS18B20
1. Messreihe mit variabler Anzahl und variablem Abstand zu den Messungen
2. Messreihe mit variabler Anzahl und variablem Abstand zu den Messungen mit einem Webserver
3. ESP32 - graphische Darstellung mit Python/Matplotlib
1. Messreihe mit variabler Anzahl und variablem Abstand zu den Messungen
Durchführung einer Temperaturmessung und anschließende graphische Darstellung der Messwerte mit Python/Matplotlib
Die Vorgehensweise sieht so aus:
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 Verkabelung des Sensors DS18B20
Die Verkabelung
SD-Card-Modul ESP32
VCC VIN(5V)
GND GND
CS GPIO 5
SCK GPIO 18
MISO GPIO 19
MOSI GPIO 23


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.");}

Arduino-Sketch zur Messung mit variabler Anzahl der Messungen und variablem Abstand
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() {}

Arduino-Sketch, um die Daten in die ser. Schnittstelle zu bringen
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()

Python-Code: Graphische Darstellung mit Python/Matplotlib Zugriff über ser. Schnittstelle

2. Messreihe mit variabler Anzahl und variablem Abstand zu den Messungen mit einem Webserver
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 ===");} }
} }

Arduino-Sketch zur Messung mit variabler Anzahl der Messungen und variablem Abstand mit einem Webserver
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;}

Arduino-Sketch, um die Daten in die ser. Schnittstelle zu bringen-Webserver

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:
3. ESP32 - graphische Darstellung mit Python/Matplotlib mit direktem Zugriff auf die CSV-Datei
a) Kopieren einer CSV-Datei von der SD-Card auf den Raspberry Pi
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)

Python-Code für die Übertragung einer CSV-Datei auf den RPi

b) Graphische Darstellung einer Datei auf dem Raspberry Pi mit Python/Matplotlib
Der Python-Code zur graphischen Darstellung


import pandas as pd
import matplotlib.pyplot as plt
CSV_DATEI = "/home/montblanc/csv_logs/log_80.csv"
# CSV mit Header einlesen
df = pd.read_csv(CSV_DATEI)
# Spalten ggf. umbenennen (falls nötig)
df.columns = ["Zeit", "Temperatur"]
# Zeit konvertieren
df["Zeit"] = pd.to_datetime(df["Zeit"])
# Plot
plt.figure(figsize=(10, 5))
plt.plot(df["Zeit"], df["Temperatur"], marker="o")
plt.xlabel("Zeit")
plt.ylabel("Temperatur (°C)")
plt.title("Temperaturverlauf")
plt.grid(True)
plt.xticks(rotation=45)
plt.tight_layout()
plt.savefig("CSV_Datei_dir_Zugriff_log_80.png")
plt.show()

Python-Code für Grafik mit direktem Zugriff auf die CSV-Datei