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:

Nernst Equation for pH

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
  1. Rohmessungen (10 Werte):
    [423, 510, 425, 432, 429, 430, 428, 1000, 431, 427]
    ↑ Störung durch elektrisches Rauschen
  2. Nach Sortierung (aufsteigend):
    [423, 425, 427, 428, 429, 430, 431, 432, 510, 1000]
    ↑ ↑ ↑ ↑
    Die extremen Werte (2 niedrigste + 2 höchste) werden verworfen
  3. Verbleibende Werte für Mittelwert (Index 2-7):
    [427, 428, 429, 430, 431, 432] → Mittelwert = 429.5
  4. 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:

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:
  1. [ZUSTAND_NORMAL]
    → Display zeigt aktuellen pH-Wert
    → Taste A führt zur Kalibrierung
  2. [ZUSTAND_KALIB_START]
    → "Kalibrierung starten?"
    → Taste C bestätigt, Taste D bricht ab
  3. [ZUSTAND_ANZAHL_EINGABE]
    → "Wie viele Punkte? (1-3)"
    → Zahl eingeben + Taste C
  4. [ZUSTAND_SOLLWERT_EINGABE]
    → "Punkt X Soll-pH?"
    → Wert eingeben + Taste C
  5. [ZUSTAND_MESSUNG]
    → "Messpunkt X - B=Stop"
    → Taste B beendet Messung
  6. [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).