Die Funktionsweise des pH-Meters
Das pH-Meter arbeitet nach dem Prinzip der potentiometrischen Messung,
bei der die elektromotorische Kraft (EMF) einer galvanischen Zelle gemessen wird.
Die pH-Messkette besteht aus zwei Elektroden: einer
pH-sensitiven Glaselektrode mit einer speziellen Silikatglas-Membran,
die für H⁺-Ionen permeabel ist und an deren Oberfläche sich durch Ionenaustausch
eine dünne, gelartige Belegungsschicht ausbildet, sowie einer Referenzelektrode,
typischerweise vom Typ Ag/AgCl, die ein stabiles Potential durch eine definierte
Chloridionenaktivität in ihrer mit gesättigter Kaliumchloridlösung (3M oder gesättigt)
gefüllten Kammer bereitstellt.
Das Potential wird durch die Nernst-Gleichung beschrieben:
Der Medianfilter-Prozess
Quelle: KI-deepseek
Der Median, auch Zentralwert genannt, ist ein statistisches Lagemaß, das einen Datensatz in zwei gleich große Hälften teilt.
Um den Median zu bestimmen, werden die Daten zunächst in eine aufsteigende Reihenfolge sortiert.
Bei einer ungeraden Anzahl von Werten (n) ist der Median der Wert in der Mitte der sortierten Liste,
genauer gesagt an der Position (n+1)/2, ansonsten das arithmetische Mittel der beiden mittleren Werte,
also der Werte an den Positionen n/2 und n/2 + 1.
Visualisierung des Medianfilter-Prozesses
-
Rohmessungen (10 Werte):
[423, 510, 425, 432, 429, 430, 428, 1000, 431, 427]
↑
Störung durch elektrisches Rauschen - Nach Sortierung (aufsteigend):
[423, 425, 427, 428, 429, 430, 431, 432, 510, 1000]
↑ ↑ ↑ ↑
Die extremen Werte (2 niedrigste + 2 höchste)
werden verworfen - Verbleibende Werte für Mittelwert (Index 2-7):
[427, 428, 429, 430, 431, 432]
→ Mittelwert = 429.5 - Vergleich:
Ohne Filterung: Durchschnitt = 493.5 (stark verfälscht!)
Mit Filterung: Ergebnis = 429.5 (repräsentativer Wert)
// 1. 10 Messwerte aufnehmen
for(int i = 0; i < 10; i++) {
messPuffer[i] = analogRead(A0); // Lies Wert von pH-Sensor
delay(30); // Kurze Pause zwischen Messungen
}
// 2. Werte sortieren (Bubble-Sort, einfach aber langsam)
for(int i = 0; i < 9; i++) {
for(int j = i+1; j < 10; j++) {
if(messPuffer[i] > messPuffer[j]) {
// Werte tauschen
int temp = messPuffer[i];
messPuffer[i] = messPuffer[j];
messPuffer[j] = temp;
}
}
}
// 3. Mittelwert der mittleren 6 Werte berechnen
// Index 2-7 (6 Werte), da Index 0-1 und 8-9 wegfallen
mittelwert = 0;
for(int i = 2; i < 8; i++) {
mittelwert += messPuffer[i];
}
// 4. Rohwert in pH umrechnen
// Formel: pH = -5.70 * Spannung + 28.55 + Offset
float spannung = (float)mittelwert * 5.0 / 1024 / 6;
aktuellerPH = -5.70 * spannung + 28.55 + kalibrierOffset;
}
Das Prinzip der State Machine
Quelle: KI-deepseek
Eine State Machine ist ein fundamentaler Entwurfsansatz in der Programmierung,
der besonders für interaktive Systeme und Embedded-Devices unverzichtbar ist.
Das Konzept basiert auf einem einfachen, aber mächtigen Prinzip:
Ein System kann sich zu jedem Zeitpunkt nur in einem von mehreren klar definierten Zuständen befinden.
Statt vieler if-Abfragen verwenden wir Zustände.
Diese Technik ist besonders wertvoll für Systeme mit klar abgrenzbaren Betriebsmodi,
wie sie in Messgeräten, Automatisierungssystemen und Benutzerinterfaces häufig vorkommen.
Eine State machine basiert auf vier Kernelementen:
- Zustände(States): Diskrete Betriebsmodi des Systems
- Ereignisse (Events): Auslöser für Zustandsänderungen
- Transitionen: Regeln für zulässige Zustandsübergänge
- Aktionen: Operationen, die in bestimmten Zuständen ausgeführt werden
Vorteile der State Machine
| Aspekt |
Ohne State Machine |
Mit State Machine |
| Programmstruktur |
Viele verschachtelte if-Abfragen |
Klare, getrennte Zustände |
| Wartbarkeit |
Schwer zu erweitern und zu debuggen |
Einfache Erweiterung durch neue Zustände |
| Fehlerbehandlung |
Komplexe Fehlerbedingungen | Jeder Zustand definiert eigene Regeln |
| Lesbarkeit |
Unübersichtlicher Code | Logisch strukturierter Code |
Zustandsdiagramm des pH-Meters:
- [ZUSTAND_NORMAL]
→ Display zeigt aktuellen pH-Wert
→ Taste A führt zur Kalibrierung -
[ZUSTAND_KALIB_START]
→ "Kalibrierung starten?"
→ Taste C bestätigt, Taste D bricht ab
- [ZUSTAND_ANZAHL_EINGABE]
→ "Wie viele Punkte? (1-3)"
→ Zahl eingeben + Taste C - [ZUSTAND_SOLLWERT_EINGABE]
→ "Punkt X Soll-pH?"
→ Wert eingeben + Taste C - [ZUSTAND_MESSUNG]
→ "Messpunkt X - B=Stop"
→ Taste B beendet Messung - [ZUSTAND_ERGEBNIS]
→ "Kalibrierung OK! Offset: X.XX"
→ Taste C zurück zum Normalmodus
Implementierungsmuster in C/C++
Zustandsdefinition
Die erste Implementierungsentscheidung
betrifft die Repräsentation von Zuständen. Typischerweise verwendet man dazu Enumerationen:
enum ProgrammZustand { ZUSTAND_NORMAL, // Normale pH-Messung
ZUSTAND_KALIB_START, // Kalibrierung starten
ZUSTAND_ANZAHL_EINGABE, // Anzahl Punkte eingeben
ZUSTAND_SOLLWERT_EINGABE, // Soll-pH-Wert eingeben
ZUSTAND_MESSUNG, // Messung läuft ZUSTAND_ERGEBNIS
// Kalibrierergebnis anzeigen
};
ProgrammZustand aktuellerZustand = ZUSTAND_NORMAL; // Startzustand
Diese Enumeration definiert den kompletten Zustandsraum des Systems.
Jeder Zustand sollte eine klar umrissene semantische Bedeutung haben.
Die VariableaktuellerZustand speichert,
in welchem Zustand wir uns gerade befinden.
Beim Starten beginnt das Programm immer im ZUSTAND_NORMAL.
Ereignisdefinition
Parallel zu den Zuständen definiert man Ereignisse,
die Zustandswechsel auslösen können, wobei in diesem Fall implizit die Events durch Tastencodes
definiert sind.
typedef enum {
EVENT_NONE, // Kein Ereignis
EVENT_BUTTON_PRESSED, // Benutzereingabe
EVENT_TIMEOUT, // Zeitereignis
EVENT_MEASUREMENT_DONE, // Messung abgeschlossen
EVENT_CALIBRATION_COMPLETE, // Kalibrierung fertig
EVENT_ERROR_DETECTED // Fehler erkannt
} SystemEvent;
Die Hauptschleife-Der Dispatcher
Zustandhandler-Architektur: Die loop()-Funktion
überprüft in jedem Durchlauf durch Switch-Case-Anweisungen in welchem Zustand sich das System befindet:
void loop() {
// 1. Prüfen, ob eine Taste gedrückt wurde
char gedrueckteTaste = meinKeypad.getKey();
if (gedrueckteTaste) {
verarbeiteTaste(gedrueckteTaste); // Taste verarbeiten
}
// 2. Je nach Zustand unterschiedliche Aktionen
switch(aktuellerZustand) {
case ZUSTAND_NORMAL:
// Im Normalmodus regelmäßig pH messen
messeRegelmaessigPH();
break;
case ZUSTAND_MESSUNG:
// Im Messmodus Kalibrierung durchführen
if (aktuellerPunkt < anzahlPunkte &&
!kalibrierPunkte[aktuellerPunkt].erledigt) {
messeKalibrierung();
zeigeKalibrierMessung();
}
break;
// In anderen Zuständen passiert im Loop nichts Besonderes
default:
break;
}
delay(10);
}
Event-Handler: Tastenverarbeitung je nach Zustand
Event-Handler für Tastenereignisse:Die gleiche Taste
kann in verschiedenen Zuständen unterschiedliche Aktionen auslösen:
// Je nach Taste unterschiedliche Aktionen ausführen:
void verarbeiteTaste(char taste) {
switch(taste)
{
// Taste A: Kalibrierung starten (nur im Normalmodus)
case 'A':
if (aktuellerZustand == ZUSTAND_NORMAL) {
starteKalibrierung();
}
break;
// Taste B: Messung beenden (nur im Messmodus)
case 'B':
if (aktuellerZustand == ZUSTAND_MESSUNG) {
beendeMessung();
}
break;
// Taste C: BESTÄTIGEN (je nach Zustand unterschiedlich!)
case 'C':
bestaetigeEingabe();
break;
// Taste D: ABBRECHEN (wenn nicht im Normalmodus)
case 'D':
if (aktuellerZustand != ZUSTAND_NORMAL) {
abbrechenKalibrierung();
}
break;
// ... weitere Tasten je nach Zustand
}
}
->Arduino-Programmcode für das pH-Meter zum Herunterladen (Quelle: KI-deepseek).