import onewire, ds18x20, time
from machine import Pin
# ── Konstanten ──────────────────────
TEMP_MAX = 8.0 # Oberer Grenzwert (Alarm)
TEMP_MIN = -30.0 # Unterer Grenzwert (Alarm)
ERR_LOW = -127.0
ERR_HIGH = 85.0 # Power-On-Fehlerwert
# ── Hardware ────────────────────────
pin_data = Pin(4)
pin_led = Pin(2, Pin.OUT)
pin_buzz = Pin(15, Pin.OUT)
ow = onewire.OneWire(pin_data)
ds = ds18x20.DS18X20(ow)
roms = ds.scan()
# ── Hilfsfunktionen ─────────────────
def is_valid(t):
return t is not None \
and t != ERR_LOW \
and t != ERR_HIGH
def set_alarm(state):
pin_led.value(state)
pin_buzz.value(state)
# ── Hauptfunktion ───────────────────
def measure_cycle():
ds.convert_temp() # Wandlung starten
time.sleep_ms(750) # Warten auf ADC
alarm = False
for rom in roms:
t = ds.read_temp(rom)
if not is_valid(t):
alarm = True # Fehler → Fail-Safe
continue
if t > TEMP_MAX or t < TEMP_MIN:
alarm = True
set_alarm(alarm)
# ── Startprüfung ────────────────────
if not roms:
set_alarm(True) # Keine Sensoren → Alarm
# ── Hauptschleife ───────────────────
while True:
measure_cycle()
time.sleep(30) # 30 s Takt
# Fehlerwerte des DS18B20: -127.0 = Sensor nicht gefunden (kein Pull-Up / Verbindungsfehler).
# 85.0 = Power-On-Reset-Wert (noch keine gültige Messung). Beide müssen als Fehler behandelt werden.
#######################
# Hardware & Firmware #
#######################
# F1 – Welcher Widerstandswert und welche Schaltung sind für den Pull-Up am 1-Wire-Bus korrekt?
## 4,7 kΩ zwischen VCC (3,3 V) und DQ – parallel zur Datenleitung, nicht in Reihe
# F2 – Beide DS18B20-Sensoren sollen am gleichen GPIO-Pin betrieben werden. Was ist dabei zu beachten?
## Beide DQ-Pins werden parallel verbunden. Es wird nur ein Pull-Up-Widerstand benötigt. Jeder Sensor wird über seine individuelle 64-Bit-ROM-Adresse einzeln angesprochen.
# F3 – In Wokwi gibt ds.scan() eine leere Liste zurück. Was ist die wahrscheinlichste Ursache?
## Der Pull-Up-Widerstand fehlt oder ist falsch verdrahtet – dadurch gibt es keinen definierten HIGH-Pegel auf der DQ-Leitung
#######################
# NumPy · Matplotlib #
#######################
################### Aufgabe 3.1
# Die Rohdaten sollen in eine 4×4-Matrix umgewandelt und bereinigt werden.
# Fehlerwerte (-127.0) müssen durch NaN ersetzt werden.
import numpy as np
raw = [3.1, 3.4, 3.2, 3.0, 4.1, 4.0, -127.0, 4.3,
9.2, 8.8, 9.5, 9.1, 3.3, 3.1, 3.4, 3.2]
# 1. Liste in 4×4-Matrix umwandeln
matrix = np.array(raw).reshape((4,4))
# 2. Fehlerwerte durch NaN ersetzen
matrix = matrix.astype(float)
matrix[matrix == -127.0] = np.nan
################### Aufgabe 3.2
# Auf Basis der bereinigten Matrix sollen folgende Analysen durchgeführt werden.
# Der Alarmgrenzwert liegt bei TEMP_MAX = 8.0 °C.
TEMP_MAX = 8.0
# A: Anzahl der Überschreitungen (ohne NaN)
hot_mask = matrix > TEMP_MAX
n_hot = np.sum(hot_mask)
# B: Mittlere Temperatur der gesamten Matrix (NaN ignorieren)
mean_temp = np.nanmean(matrix)
# C: Höchster Messwert (NaN ignorieren)
max_temp = np.nanmax(matrix)
# D: Zeile mit den meisten Alarmen ermitteln
# hot_mask ist eine boolesche 4×4-Matrix
alarms_per_row = hot_mask.sum(1)
alarm_row = np.argmax(alarms_per_row)
# E: Prüfen ob ein Sensor ausgefallen ist
has_error = np.any(np.isnan(matrix))
################### Aufgabe 3.3
### -1 Grau / Fehler Wert ist NaN
### 0 Grün / OK Temperatur < 6,0 °C
### 1 Gelb / Warnung 6,0 °C ≤ Temperatur ≤ 8,0 °C
### 2 Rot / Alarm Temperatur > 8,0 °C
status = np.zeros((4, 4))
for r in range(4):
for c in range(4):
v = matrix[r, c]
if np.isnan(v):
status[r, c] = -1
elif v > 8.0:
status[r, c] = 2
elif v >= 6.0:
status[r, c] = 1
else:
status[r, c] = 0
# 1
# Datenaufbereitung: Alle vier Listen per np.array().reshape(4,4) in Matrizen umwandeln.
# Fehlerwerte −127.0 und 85.0 jeweils durch np.nan ersetzen.
# Statistische Auswertung: Für jede der vier Zonen: Mittelwert,
# Maximum, Minimum – NaN-sicher mit np.nanmean(), np.nanmax(), np.nanmin(). Ausgabe formatiert per print().
# Alarm-Erkennung: Für jede Zone: Anzahl der Überschreitungen (T > TEMP_MAX) und
# Anzahl der Sensorausfälle (np.isnan()). Per print() ausgeben.
# Cluster-Analyse für Zone 2: Prüfe für jeden Hotspot (T > 8.0 °C),
# ob ein direkter Nachbar (oben/unten/links/rechts) ebenfalls heiß ist.
# Ausgabe: is_cluster_detected (True/False) und Anzahl der Cluster-Zellen.
# Ampel-Statusmatrizen + Visualisierung: Erstelle für alle vier Zonen
# je eine Statusmatrix mit den Codes −1 (NaN) / 0 (T < 6.0 °C) / 1 (6.0–8.0 °C) / 2 (T > 8.0 °C).
# Stelle alle vier Matrizen in einem plt.subplot(2,2,...)-Raster als farbige Heatmap dar (Grau/Grün/Gelb/Rot).
# Titel pro Subplot: Zonenname. Legende/Colorbar hinzufügen.
# Rohdaten – nicht verändern
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
raw_zone1 = [2.1, 2.4, -127.0, 2.2, 2.0, 2.3, 2.1, 2.5, 1.9, 2.2, 2.4, 2.1, 2.3, 2.0, 2.2, 2.1]
raw_zone2 = [2.5, 9.3, 9.7, 2.4, 9.1, 9.8, 2.3, 2.5, 2.2, 2.4, 9.4, 9.2, 2.1, 2.3, 2.0, 85.0]
raw_zone3 = [2.0, 1.9, 2.1, 2.3, 2.2, 2.0, 1.8, 2.1, 2.3, 2.2, 2.0, 2.1, 1.9, 2.2, 2.0, 2.1]
raw_zone4 = [-127.0, -127.0, 2.1, 2.3, 2.0, 2.2, 2.1, 2.3, 2.2, 2.0, 2.1, 2.2, 2.3, 2.1, 2.0, 2.2]
TEMP_MAX = 8.0
# ---------------------------------------------------------
# 1. Datenaufbereitung
# ---------------------------------------------------------
def bereite_daten_auf(raw_liste):
matrix = np.array(raw_liste).reshape(4, 4)
# Fehlerwerte durch np.nan ersetzen
matrix[matrix == -127.0] = np.nan
matrix[matrix == 85.0] = np.nan
return matrix
zonen = [
bereite_daten_auf(raw_zone1),
bereite_daten_auf(raw_zone2),
bereite_daten_auf(raw_zone3),
bereite_daten_auf(raw_zone4)
]
zonen_namen = ["Zone 1", "Zone 2", "Zone 3", "Zone 4"]
# ---------------------------------------------------------
# 2. Statistische Auswertung & 3. Alarm-Erkennung
# ---------------------------------------------------------
print("--- Statistische Auswertung & Alarm-Erkennung ---")
for name, z in zip(zonen_namen, zonen):
# Statistik (NaN-sicher)
mittelwert = np.nanmean(z)
maximum = np.nanmax(z)
minimum = np.nanmin(z)
# Alarme (NaN wird bei > automatisch als False gewertet)
ueberschreitungen = np.sum(z > TEMP_MAX)
ausfaelle = np.sum(np.isnan(z))
print(f"{name}:")
print(f" Mittelwert: {mittelwert:.2f}°C, Max: {maximum:.2f}°C, Min: {minimum:.2f}°C")
print(f" Überschreitungen (> {TEMP_MAX}°C): {ueberschreitungen}")
print(f" Sensorausfälle: {ausfaelle}\n")
# ---------------------------------------------------------
# 4. Cluster-Analyse für Zone 2
# ---------------------------------------------------------
print("--- Cluster-Analyse für Zone 2 ---")
zone2 = zonen[1]
is_cluster_detected = False
cluster_zellen = 0
# Finde alle Hotspots in Zone 2
hotspots = zone2 > TEMP_MAX
# Prüfe jeden Punkt im 4x4 Gitter
for r in range(4):
for c in range(4):
if hotspots[r, c]:
hat_heissen_nachbarn = False
# Oben prüfen
if r > 0 and hotspots[r-1, c]: hat_heissen_nachbarn = True
# Unten prüfen
if r < 3 and hotspots[r+1, c]: hat_heissen_nachbarn = True
# Links prüfen
if c > 0 and hotspots[r, c-1]: hat_heissen_nachbarn = True
# Rechts prüfen
if c < 3 and hotspots[r, c+1]: hat_heissen_nachbarn = True
if hat_heissen_nachbarn:
is_cluster_detected = True
cluster_zellen += 1
print(f"is_cluster_detected: {is_cluster_detected}")
print(f"Anzahl der Cluster-Zellen: {cluster_zellen}\n")
# ---------------------------------------------------------
# 5. Ampel-Statusmatrizen + Visualisierung
# ---------------------------------------------------------
# Farbschema: Grau (-1), Grün (0), Gelb (1), Rot (2)
cmap = ListedColormap(['gray', 'green', 'yellow', 'red'])
fig, axes = plt.subplots(2, 2, figsize=(8, 8))
for i, (ax, z) in enumerate(zip(axes.flat, zonen)):
# Leere Statusmatrix erstellen
status = np.zeros((4, 4))
# Werte gemäß Vorgabe mappen
status[np.isnan(z)] = -1
status[(z < 6.0) & ~np.isnan(z)] = 0
status[(z >= 6.0) & (z <= 8.0) & ~np.isnan(z)] = 1
status[z > 8.0] = 2
# Heatmap zeichnen (vmin und vmax zentrieren die 4 Farben exakt auf die Werte)
im = ax.imshow(status, cmap=cmap, vmin=-1.5, vmax=2.5)
ax.set_title(zonen_namen[i])
# Achsen-Ticks ausblenden für einen saubereren Look
ax.set_xticks([])
ax.set_yticks([])
# Globale Legende / Colorbar
cbar = fig.colorbar(im, ax=axes.ravel().tolist(), ticks=[-1, 0, 1, 2], shrink=0.8)
cbar.set_ticklabels(['NaN (Ausfall)', '< 6.0 °C (Ok)', '6.0 - 8.0 °C (Warnung)', '> 8.0 °C (Alarm)'])
plt.suptitle("Ampel-Statusmatrizen der Kühlzellen", fontsize=14, fontweight='bold')
plt.show()
##############
# Sicherheit #
##############
## Identifiziere mindestens fünf Teilsysteme oder Schnittstellen,
## die besonderen Schutz benötigen. Wähle die fünf zutreffenden aus:
# DS18B20-Sensorik (1-Wire-Bus): Messwerte können gefälscht oder unterdrückt werden
# MQTT-Kommunikation (WLAN): Datenpakete können abgehört oder manipuliert werden
# ESP32-Firmware: Kompromittierter Code kann Sicherheitslogik deaktivieren
# Aktorik (LED, Summer, ggf. Relais): Manipulation verhindert Alarmauslösung
# Energieversorgung des ESP32: Ausfall legt die gesamte Überwachung lahm
# Physischer Zugang zum Steuerungsschrank (Hardware-Sabotage)
##################################
# Sicherheitsrisiken analysieren #
##################################
## Szenario A: Ein Angreifer sendet gefälschte MQTT-Nachrichten
## mit erfundenen Temperaturwerten an den Server.
# Der Leitstand zeigt falsche Statuswerte an – reale Temperaturprobleme werden nicht erkannt.
# Ware verdirbt unbemerkt.
## Szenario B: Jemand spielt über eine ungesicherte OTA-Update-Schnittstelle
## eine manipulierte Firmware auf den ESP32 auf.
# Die Alarmgrenzwerte werden deaktiviert – bei Überhitzung löst kein Alarm aus.
# Physische Schäden an der Kühlanlage oder den Lebensmitteln sind möglich.
## Szenario C: Ein DoS-Angriff überflutet das WLAN-Netzwerk mit Datenpaketen.
# Die MQTT-Verbindung bricht ab – keine Telemetriedaten erreichen den Server.
# Fernüberwachung und Alarmierung sind nicht mehr möglich (Blindflug).
## Szenario D: Die Stromversorgung des ESP32 wird physisch unterbrochen (z.B. durch gezogenes Netzkabel).
# Die gesamte Überwachung fällt sofort aus – kein Alarm, keine Datenübertragung, keine Grenzwertprüfung.
# Der Zustand der Kühlzellen ist unbekannt.
###############################
# Schutzmaßnahmen formulieren #
###############################
## Gegenmaßnahme zu Szenario A (gefälschte MQTT-Nachrichten):
# MQTTS einsetzen (MQTT über TLS) und Client-Zertifikate zur Authentifizierung verwenden
# nur verifizierte Clients können publizieren
## Gegenmaßnahme zu Szenario B (manipulierte Firmware via OTA):
# Firmware-Updates nur akzeptieren wenn sie digital signiert sind.
# OTA-Schnittstelle durch Authentifizierung absichern.
## Gegenmaßnahme zu Szenario C (DoS-Angriff auf WLAN):
# IoT-Geräte in separates VLAN isolieren. Lokale Alarmlogik (LED/Summer) unabhängig vom Netzwerk implementieren,
# damit sie auch bei WLAN-Ausfall funktioniert.
## Gegenmaßnahme zu Szenario D (Stromausfall / Sabotage der Versorgung):
# Unterbrechungsfreie Stromversorgung (USV) für den ESP32 einsetzen. Steuerungsschrank physisch abschließen
# und Zugang auf autorisiertes Personal beschränken.