source ~/devel/projects/course_env/bin/activate
pip install matplotlib
==== Programmstruktur ====
(course_env) pi@raspi88:~/devel/projects/course_temp_logger $ tree
.
├── data
│ ├── temperature.png # wird automatisch erstellt
│ └── temperature.txt # wird automatisch erstellt
└── src
├── core
│ ├── hardware.py # aus dem vorherigen Projekt!
│ ├── __init__.py
│ └── temperature_logger.py
└── temp_logger.py # Hauptprogramm
===== Software =====
Im folgenden Abschnitt wird die vollständige Software zur Datenerfassung, Speicherung und grafischen Darstellung der Temperaturwerte beschrieben.
==== Quellcode ====
=== hardware.py ===
Die Datei ''hardware.py'' (API) bleibt unverändert und wird hier der Vollständigkeit halber erneut vollständig aufgeführt.
++++ Quellcode: ''hardware.py'' |
import glob
import time
# -----------------------------
# API-Funktionen ds18b20
# -----------------------------
SENSOR_TIMEOUT = 1 # Sekunden
def is_sensor():
"""Prüft, ob mindestens ein DS18B20 Sensor angeschlossen ist"""
sensors = glob.glob("/sys/bus/w1/devices/28-*")
return len(sensors) > 0
def get_sensor():
"""Gibt den ersten DS18B20 Sensor zurück oder None"""
if not is_sensor():
return None
sensors = glob.glob("/sys/bus/w1/devices/28-*")
return sensors[0] + "/w1_slave"
def get_temperature():
"""Liest Temperatur vom ersten Sensor aus, None bei Fehler oder Timeout"""
sensor_file = get_sensor()
if sensor_file is None:
return None
start_time = time.time()
while True:
with open(sensor_file, "r") as f:
lines = f.readlines()
if lines[0].strip().endswith("YES"):
break
if time.time() - start_time > SENSOR_TIMEOUT:
return None
time.sleep(0.1)
temp_line = lines[1]
temp_str = temp_line.split("t=")[1]
return float(temp_str) / 1000.0
++++
=== temperature_logger.py ===
In der Datei ''temperature_logger.py'' kann die Variable ''MODE'' die Werte ''local'' oder ''web'' annehmen.
* **local** Das erzeugte Bild wird im lokalen Projektverzeichnis gespeichert.
* **web** Das erzeugte Bild wird im Webverzeichnis gespeichert und kann über den Webserver angezeigt werden.
import time
import threading
from datetime import datetime
from pathlib import Path
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
from core.hardware import get_temperature
# MODE = "local" oder "web"
MODE = "local"
if MODE == "local":
DATA_DIR = Path("~/devel/projects/course_temp_logger/data").expanduser()
elif MODE == "web":
DATA_DIR = Path("/var/www/html/temp_curve").expanduser()
else:
raise ValueError("MODE muss 'local' oder 'web' sein")
DATA_DIR.mkdir(parents=True, exist_ok=True)
DATA_FILE = DATA_DIR / "temperature.txt"
PLOT_FILE = DATA_DIR / "temperature.png"
INTERVAL = 5 # Sekunden (Auflösung / Messintervall)
running = False
thread_started = False
lock = threading.Lock()
def set_interval(seconds: int):
global INTERVAL
if seconds < 1:
seconds = 1
INTERVAL = seconds
def generate_plot():
times = []
temps = []
if DATA_FILE.exists():
with open(DATA_FILE, "r") as f:
for line in f:
parts = line.strip().split()
if len(parts) == 2:
times.append(parts[0])
temps.append(float(parts[1]))
plt.figure()
plt.title("Temperaturverlauf")
plt.xlabel("Zeit")
plt.ylabel("Temperatur (°C)")
if temps:
plt.plot(temps)
step = max(1, len(times) // 10)
plt.xticks(range(0, len(times), step),
times[::step],
rotation=45)
else:
plt.text(
0.5,
0.5,
"Keine Messdaten",
ha="center",
va="center",
transform=plt.gca().transAxes,
)
plt.tight_layout()
plt.savefig(PLOT_FILE)
plt.close()
def worker():
global running
while True:
with lock:
active = running
interval = INTERVAL
if active:
temp = get_temperature()
if temp is not None:
now = datetime.now().strftime("%H:%M:%S")
with open(DATA_FILE, "a") as f:
f.write(f"{now} {temp}\n")
generate_plot()
time.sleep(interval)
else:
time.sleep(0.2)
def ensure_thread():
global thread_started
if not thread_started:
t = threading.Thread(target=worker, daemon=True)
t.start()
thread_started = True
def start():
ensure_thread()
global running
with lock:
running = True
def stop():
global running
with lock:
running = False
def reset():
stop()
DATA_FILE.write_text("")
generate_plot()
++++
=== Hauptprogramm ===
Das eigenständige Programm ''temp_logger.py'' dient als Kommandozeilen-Interface zur Steuerung des //Temperatur-Loggers//.
Es ermöglicht das Starten und Stoppen der Messung, das Setzen des Messintervalls sowie das Zurücksetzen der gespeicherten Daten.
#!/usr/bin/env python3
from core import temperature_logger as temp
PROMPT = "> "
SHORT_HINT = "h = Hilfe"
VERSION = "v1.0.0"
def print_short_banner():
print("Temperatur-Logger " + VERSION)
print("Mit 'h' Hilfe anzeigen.\n")
def print_help():
print("""
VERFÜGBARE BEFEHLE
------------------
start | s Logger starten
stop | p Logger stoppen
reset | r Messwerte zurücksetzen
interval | i Messintervall setzen
status | t Status anzeigen
help | h Diese Hilfe anzeigen
exit | x Programm beenden
""")
def handle_interval(cmd: str):
parts = cmd.split()
if len(parts) == 2 and parts[1].isdigit():
temp.set_interval(int(parts[1]))
print("OK")
else:
print("Verwendung: interval ")
def handle_status():
state = "RUNNING" if temp.running else "STOPPED"
print(f"STATUS : {state}")
print(f"INTERVALL: {temp.INTERVAL}")
def main():
print_short_banner()
while True:
try:
cmd = input(PROMPT).strip().lower()
except (EOFError, KeyboardInterrupt):
print()
temp.stop()
break
if not cmd:
continue
if cmd in ("start", "s"):
temp.start()
elif cmd in ("stop", "p"):
temp.stop()
elif cmd in ("reset", "r"):
temp.reset()
elif cmd.startswith("interval") or cmd.startswith("i "):
handle_interval(cmd)
elif cmd in ("status", "t"):
handle_status()
elif cmd in ("help", "h"):
print_help()
elif cmd in ("exit", "x"):
temp.stop()
break
else:
print(f"Unbekannter Befehl ({SHORT_HINT})")
if __name__ == "__main__":
main()
++++
===== Testen =====
Das Programm kann sowohl **lokal im Terminal** als auch über den **Webbrowser getestet werden**.
Für die Webausgabe gelten die im Abschnitt „Voraussetzungen“ beschriebenen Bedingungen.
chmod 755 temp_logger.py
./temp_logger.py
Temperatur-Logger v1.0.0
Mit 'h' Hilfe anzeigen.
> h
VERFÜGBARE BEFEHLE
------------------
start | s Logger starten
stop | p Logger stoppen
reset | r Messwerte zurücksetzen
interval | i Messintervall setzen
status | t Status anzeigen
help | h Diese Hilfe anzeigen
exit | x Programm beenden
> start
(Programm misst im Hintergrund …)
> stop
>
Nach dem Start erscheint die Kommandozeilen-Oberfläche des Temperatur-Loggers.
Im obigen Beispiel wurde mit dem Befehl ''h'' die Hilfe angezeigt.
Die Befehle können entweder vollständig ausgeschrieben (z. B. ''start'') oder in der angegebenen Kurzform (z. B. ''s'') verwendet werden, um das Programm zu starten.
Die erzeugten Dateien befinden sich im Verzeichnis ''data'':
ls
temperature.png # erzeugte Grafik
temperature.txt # Messdaten
Die Grafik kann anschließend mit folgendem Befehl betrachtet werden:
feh temperature.png
|{{ :raspberry_pi:temp_feh.png?400 |}}|
|Bildausgabe mit dem Befehl ''feh''|
==== Webseite ====
In diesem Abschnitt wird die einfache HTML-Struktur beschrieben, mit der das Projekt über den Webbrowser erreichbar ist und der automatisch erzeugte Temperaturverlauf grafisch dargestellt wird.
Projekt Auswahl
Projekt Auswahl
=== temperature.html ===
Die Datei ''temperature.html'' zeigt das vom Logger erzeugte Bild temperature.png an und ermöglicht so die grafische Darstellung des aktuellen Temperaturverlaufs im Browser.
Temperatur Verlauf
Temperatur Verlauf
Aktueller Temperaturverlauf:
Zurück