Industrielle Fertigung
Industrielles Internet der Dinge | Industrielle Materialien | Gerätewartung und Reparatur | Industrielle Programmierung |
home  MfgRobots >> Industrielle Fertigung >  >> Manufacturing Technology >> Herstellungsprozess

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

Komponenten und Zubehör

Bauteil Menge Details
Infrarot-Emitter (18°) 1 Wellenlänge zwischen 805 nm und 1000 nm
Photodiode LPT80A 1 Breitbandig, muss Rot (600 nm) und Infrarot (940 nm) empfangen können
5 mm LED: Rot 1 Wellenlänge ca. 600 nm
Widerstand 10 kΩ 1
Widerstand 330 Ω 2
DFRobot I2C 16x2 Arduino LCD Display Modul 1 Oder ein anderes LCD zur Anzeige der Werte
Arduino UNO 1
Jumper-Wires (diverse) 1
High-Brightness LED, Weiß 1 Optional: dritte LED zur Umgebungslicht-Stabilisierung (siehe Verbesserungsabschnitt)

Benötigte Werkzeuge und Maschinen

Apps und Online-Dienste

Über dieses Projekt

In dieser Zeit der Isolation habe ich ein Pulsoximeter aus Bauteilen gebaut, die ich bereits zu Hause hatte. Ein Pulsoximeter besteht im Grunde nur aus zwei LEDs und einer Photodiode.

Ich bin kein Medizinexperte und kann zum jetzigen Projektstand nicht garantieren, dass dieses Gerät diagnostischen Wert hat. Es ist jedoch ein hervorragendes Lernprojekt, um die Funktionsweise zu verstehen, und mit einigen Verbesserungen könnte es zu einem selbstgebauten medizinischen Werkzeug werden.

Sauerstoffsättigung und COVID-19

In dieser außergewöhnlichen Zeit haben wir viel über Viren, Lungen, Masken, Seife und Händewaschen gelernt. Symptome wie Husten, Fieber und Atemnot sind bekannt. Eine Möglichkeit, Atemnot zu messen, ist die Bestimmung des Sauerstoffgehalts im Blut.

Dies geschieht indirekt mit einem medizinischen Gerät namens Pulsoximeter. Sie haben es wahrscheinlich schon gesehen: ein nicht-invasives Gerät, das auf den Finger gesetzt wird und mit pulsierendem Licht misst.

Normalerweise liegt die Sauerstoffsättigung (SpO₂) bei gesunden Menschen bei ≥ 95 %. Fällt sie unter 90 % und treten Husten und Fieber auf, ist das ein Warnsignal.

Wenn Maker ein Pulsoximeter selbst bauen könnten, ließen sich Infektionen leichter erkennen und Menschen könnten fundierter entscheiden, wann ein Krankenhausbesuch nötig ist – und wann es nur eine Panikattacke ist.

Schritt 1: Verstehen, wie ein Herzschlag-Sensor funktioniert

Ich begann mit einem KY-039 Herzschlag-Sensor aus einem gängigen Sensorkit. Wie im Schaltplan zu sehen, besteht er nur aus einer Infrarot-LED, die eine Photodiode beleuchtet, plus zwei Widerstände zum Schutz der LED und zur Auswertung des winzigen Signals.

Falls Sie keinen KY-039 haben, können Sie den Sensor mit wenigen Bauteilen selbst aufbauen.

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

Der Finger wird zwischen Sensor und Photodiode gelegt:

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

Das von der IR-LED emittierte Licht wird teilweise von Nagel, Haut und Gewebe absorbiert – aber nicht konstant, da sich die Absorption mit dem Blutfluss in den Venen ändert. Bei jedem Herzschlag wird Blut in die Venen gepresst, die Lichtabsorption ändert sich. Den vom Photodiode erzeugten Strom können wir messen.

Der KY-039 hat einen S (Signal)-Pin, über den dieser sich ändernde Wert ausgelesen wird.

Schritt 2: Herzfrequenz durch Zählen der Signalpeaks messen

Das Auslesen eines variablen Signals ist nicht trivial: Rauschen, niedrige Amplitude und notwendige Signalverarbeitung erschweren die Auswertung.

Ein hilfreicher Beitrag von Johan Ha erklärt, wie man den gleitenden Durchschnitt bildet und das 50-Hz-Netzbrummen (durch künstliches Licht) herausfiltert. Der Trick: Ein Array speichert die letzten X Werte, der älteste wird entfernt, der neueste hinzugefügt. Zudem wird ein Anstieg erkannt, wenn N aufeinanderfolgende Werte steigen (rise_threshold).

Mit dem Arduino Serial Plotter (oder Tools wie SerialPlot) lässt sich ein passender Schwellenwert finden. Ist er zu hoch oder zu niedrig, werden Schläge übersehen oder der dicrotische Einschnitt fälschlich als Schlag gewertet.

Einmal die Peaks erkannt, zählt man sie oder berechnet die Zeit zwischen mehreren Schlägen für die BPM-Rate (Schläge pro Minute).

Schritt 3: Pulsoximeter bauen (KY-039 modifizieren) zur Bestimmung der Sauerstoffsättigung

Blut absorbiert Licht wellenlängenabhängig. Rotes Licht (~600 nm) wird von sauerstoffreichem Blut stärker absorbiert als Infrarot (~950 nm). Durch Vergleich der Messwerte beider LEDs lässt sich der Sauerstoffgehalt bestimmen – der Wert heißt SpO₂% (periphere kapillare Sauerstoffsättigung).

Da ich einen KY-039 hatte, modifizierte ich ihn: Er hat nur eine IR-LED. Ich fügte eine ROTE LED hinzu, trennte die IR-LED von Vcc und verband beide LEDs über je einen 330-Ω-Widerstand mit zwei verschiedenen Arduino-Pins.

(Falls Sie keinen KY-039 zum Modifizieren haben: Der Selbstbau ist simpel – zwei LEDs, eine Photodiode, drei Widerstände, der Schaltplan ist einfach!)

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

So kann die IR-LED eingeschaltet und der S-Pin ausgelesen werden, dann IR aus, ROT an, und wieder S-Pin auslesen.

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

Plottet man beide Signale, zeigen die IR-Werte konstant niedrigere Werte als die Rot-Werte.

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

Wichtig: Fingerspitze korrekt auf der Photodiode platzieren, LEDs sollten den Nagel berühren. Bei guter Signalqualität die Position nicht mehr ändern.

Da die Signale schwach und rauschempfindlich sind, ist gutes Umgebungslicht essenziell. Finger nicht bewegen, Lichtverhältnisse nicht ändern – schon ein Schatten auf dem Sensor verfälscht alles.

Schritt 4: Wie wird SpO₂% berechnet?

Die Sauerstoffsättigung ist der Anteil sauerstoffgesättigten Hämoglobins am Gesamthämoglobin. Sie ist Funktion eines Parameters R (Quelle: wissenschaftliche Arbeit des Politecnico di Milano), berechnet aus Min/Max-Werten beider Signale:

R = ((REDmax - REDmin) / REDmin) / ((IRmax - IRmin) / IRmin)

Jedes Gerät hat sein eigenes R; eine Kalibrierung ist nötig, um die Kurve (Funktion) zu finden, die R mit SpO₂% verknüpft.

Wir haben Peaks gezählt, nun brauchen wir Max/Min der beiden Kurven (ROT und IR). Dazu bestimmen wir die "Periode" des Herzschlags (Dauer eines Schlages in ms) und teilen durch die Abtastrate (40 ms: 20 ms IR, dann 20 ms ROT), um die Anzahl Samples pro Periode zu erhalten. Die Periode ist die Zeit zwischen zwei aufsteigenden Signalflanken.

So analysiere ich die letzten L Samples (L = Periode / 40), gespeichert in einem Array, um REDmax, REDmin, IRmax, IRmin zu finden. Damit berechne ich R – bei jedem Schlag neu.

Schritt 5: Von R zu SpO₂% – Kalibrierung des Pulsoximeters

Der Zusammenhang zwischen R und SpO₂ lässt sich durch eine Gerade approximieren:

SpO₂ = K × R + M

Zwei Punkte (Paare aus SpO₂ und R) genügen, um K und M zu bestimmen. Dazu braucht man ein Referenz-Pulsoximeter.

  1. Normal atmen, SpO₂ und R vom Eigenbau ablesen, notieren.
  2. Luft anhalten, 10–20 s warten: SpO₂ am Referenzgerät sinkt, R am Eigenbau steigt. Vor dem Ohnmachtsgefühl Werte notieren.
  3. Lineares Gleichungssystem lösen → K und M für Ihr Gerät.

Nun lassen sich bpm und SpO₂ für jede R-Messung berechnen.

Ich habe ein Display ergänzt, das Werte nur anzeigt, wenn mindestens 5 Periodenmessungen stabil sind (±10 % der Periodenlänge). So werden Ausreißer durch schlechte Bauteile, Lichtänderungen oder Fingerbewegungen ausgefiltert. Der Wert c zeigt an, wie viele stabile Messungen der Anzeige zugrunde liegen.

Projektverbesserung: Umgebungslicht-Unabhängigkeit

Nach einigen Tagen stellte ich fest: Die Messungen hängen zu stark vom Umgebungslicht ab – untauglich für den realen Einsatz. An sonnigen Tagen waren Ergebnisse besser als bei bewölktem Himmel oder abends mit Kunstlicht.

Lösung: Eine dritte LED (weiß, immer an), die den Finger konstant beleuchtet.

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

Die Messung erfolgt zusätzlich unter einem schwarzen Tuch, um Umgebungslicht komplett auszuschließen.

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

Die Ergebnisse sind nun stabil und umgebungslichtunabhängig. Eine Neukalibrierung war nötig – im Video sieht man, dass nach wenigen Sekunden korrekt bpm und SpO₂% angezeigt werden.

Code

oximeter-diy-ver-0.92.ino (Arduino)

/* 
 * DIY-Pulsoximeter v.0.92 (kleine Korrekturen)
 * durch Modifikation eines KY-039 Herzschlag-Sensors
 * oder mit IR-LED, roter LED und Photodiode
 * https://hackaday.io/project/170752-oximeter-do-it-yourself 
 */
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

#define maxperiod_siz 80  // max. Samples pro Periode
#define measures 10       // Anzahl gespeicherter Perioden
#define samp_siz 4        // Samples für gleitenden Durchschnitt
#define rise_threshold 3  // ansteigende Werte für Peak-Erkennung

LiquidCrystal_I2C lcd(0x3F, 16, 2);

int T = 20;               // Slot in ms zum Einlesen eines Sensors
int sensorPin = A1;
int REDLed = 3;
int IRLed = 4;

// Benutzerdefinierte Zeichen für LCD
byte sym[3][8] = {
  { B00000, B01010, B11111, B11111, B01110, B00100, B00000, B00000 },
  { B00000, B00000, B00000, B11000, B00100, B01000, B10000, B11100 },
  { B00000, B00100, B01010, B00010, B00100, B00100, B00000, B00100 }
};

void setup() {
  Serial.begin(9600);
  Serial.flush();
  pinMode(sensorPin, INPUT);
  pinMode(REDLed, OUTPUT);
  pinMode(IRLed, OUTPUT);
  lcd.init();
  lcd.backlight();
  digitalWrite(REDLed, LOW);
  digitalWrite(IRLed, LOW);
  for (int i = 0; i < 8; i++) lcd.createChar(i, sym[i]);
}

void loop() {
  bool finger_status = true;
  float readsIR[samp_siz], sumIR, lastIR, reader, start;
  float readsRED[samp_siz], sumRED, lastRED;
  int period = 0, samples = 0, samplesCounter = 0;
  float readsIRMM[maxperiod_siz], readsREDMM[maxperiod_siz];
  int ptrMM = 0;
  for (int i = 0; i < maxperiod_siz; i++) { readsIRMM[i] = 0; readsREDMM[i] = 0; }
  float IRmax = 0, IRmin = 0, REDmax = 0, REDmin = 0;
  double R = 0;
  float measuresR[measures];
  int measuresPeriods[measures];
  int m = 0;
  for (int i = 0; i < measures; i++) { measuresPeriods[i] = 0; measuresR[i] = 0; }
  int ptr = 0;
  float beforeIR = 0;
  bool rising = false;
  int rise_count = 0, n = 0;
  long int last_beat = 0;
  for (int i = 0; i < samp_siz; i++) { readsIR[i] = 0; readsRED[i] = 0; }
  sumIR = 0; sumRED = 0; ptr = 0;

  while (1) {
    // --- IR LED EIN ---
    digitalWrite(REDLed, LOW);
    digitalWrite(IRLed, HIGH);
    // Durchschnitt über 20 ms (eliminiert 50-Hz-Netzbrummen)
    n = 0; start = millis(); reader = 0.;
    do { reader += analogRead(sensorPin); n++; } while (millis() < start + T);
    reader /= n;
    // Gleitender Durchschnitt
    sumIR -= readsIR[ptr];
    sumIR += reader;
    readsIR[ptr] = reader;
    lastIR = sumIR / samp_siz;

    // --- ROTE LED EIN ---
    digitalWrite(REDLed, HIGH);
    digitalWrite(IRLed, LOW);
    n = 0; start = millis(); reader = 0.;
    do { reader += analogRead(sensorPin); n++; } while (millis() < start + T);
    reader /= n;
    sumRED -= readsRED[ptr];
    sumRED += reader;
    readsRED[ptr] = reader;
    lastRED = sumRED / samp_siz;

    // --- R-BERECHNUNG ---
    readsIRMM[ptrMM] = lastIR;
    readsREDMM[ptrMM] = lastRED;
    ptrMM = (ptrMM + 1) % maxperiod_siz;
    samplesCounter++;

    if (samplesCounter >= samples) {
      samplesCounter = 0;
      IRmax = 0; IRmin = 1023;
      REDmax = 0; REDmin = 1023;
      for (int i = 0; i < maxperiod_siz; i++) {
        if (readsIRMM[i] > IRmax) IRmax = readsIRMM[i];
        if (readsIRMM[i] > 0 && readsIRMM[i] < IRmin) IRmin = readsIRMM[i];
        readsIRMM[i] = 0;
        if (readsREDMM[i] > REDmax) REDmax = readsREDMM[i];
        if (readsREDMM[i] > 0 && readsREDMM[i] < REDmin) REDmin = readsREDMM[i];
        readsREDMM[i] = 0;
      }
      R = ((REDmax - REDmin) / REDmin) / ((IRmax - IRmin) / IRmin);
    }

    // Finger-Erkennung: ROT-Kurve sollte über IR liegen
    if (lastRED < lastIR) {
      if (finger_status) {
        finger_status = false;
        lcd.clear();
        lcd.setCursor(0, 0);
        lcd.print("Kein Finger?");
      }
    } else {
      if (!finger_status) {
        lcd.clear();
        finger_status = true;
        lcd.setCursor(10, 0); lcd.print("c=");
        lcd.setCursor(0, 0); lcd.print("bpm");
        lcd.setCursor(0, 1); lcd.print("SpO"); lcd.write(1);
        lcd.setCursor(10, 1); lcd.print("R=");
      }
    }

    float avR = 0; int avBPM = 0;
    if (finger_status) {
      // Peak-Erkennung an IR-Signal
      if (lastIR > beforeIR) {
        rise_count++;
        if (!rising && rise_count > rise_threshold) {
          lcd.setCursor(3, 0); lcd.write(0); // Herz-Symbol
          rising = true;
          measuresR[m] = R;
          measuresPeriods[m] = millis() - last_beat;
          last_beat = millis();
          int period = 0;
          for (int i = 0; i < measures; i++) period += measuresPeriods[i];
          period = period / measures;
          samples = period / (2 * T);
          int avPeriod = 0, c = 0;
          for (int i = 1; i < measures; i++) {
            if (measuresPeriods[i] < measuresPeriods[i-1] * 1.1 && 
                measuresPeriods[i] > measuresPeriods[i-1] / 1.1) {
              c++; avPeriod += measuresPeriods[i]; avR += measuresR[i];
            }
          }
          m = (m + 1) % measures;
          lcd.setCursor(12, 0); lcd.print(String(c) + " ");
          avBPM = 60000 / (avPeriod / c);
          avR = avR / c;
          lcd.setCursor(12, 1);
          if (c == 0) lcd.print(" "); else lcd.print(String(avR) + " ");
          if (c > 4) {
            // Kalibrierung: SpO2 = -19 * R + 112 (beispielhaft, anpassen!)
            int SpO2 = -19 * R + 112;
            lcd.setCursor(4, 0);
            if (avBPM > 40 && avBPM < 220) lcd.print(String(avBPM) + " ");
            lcd.setCursor(4, 1);
            if (SpO2 > 70 && SpO2 < 150) lcd.print(" " + String(SpO2) + "% ");
          } else {
            if (c < 3) {
              lcd.setCursor(3, 0); lcd.write(2); // Fragezeichen
              lcd.setCursor(4, 1); lcd.write(2);
            }
          }
        }
      } else {
        rising = false; rise_count = 0;
        lcd.setCursor(3, 0); lcd.print(" ");
      }
      beforeIR = lastIR;
    }

    // Serial Plotter Ausgabe
    Serial.print(lastIR); Serial.print(","); Serial.print(lastRED); Serial.println();
    ptr = (ptr + 1) % samp_siz;
  }
}

Schaltpläne

Ich habe einen KY-039 modifiziert; da dieser nicht in der Fritzing-Bibliothek ist, habe ich ihn aus den Einzelbauteilen nachgebaut (eine passende Photodiode fehlte in der Bibliothek).

DIY-Pulsoximeter bauen: Infrarot-Sensor mit Arduino, LCD & Bauteile-Anleitung

Ein einfaches Pulsoximeter zur Messung des Blutsauerstoffs lässt sich durch Modifikation des KY-039 oder durch kompletten Selbstbau realisieren.

Herstellungsprozess

  1. PIR-Sensor Stromsparschaltung: Design, Betrieb und Anwendungen
  2. Welche LiDAR Wellenlänge wird Automotive Applikationen dominieren?
  3. DIY Infrarot-Bewegungsmelder für Raspberry Pi – Schritt-für-Schritt-Anleitung
  4. LED mit PIR-Bewegungsmelder am Raspberry Pi steuern
  5. Günstiger Raspberry Pi Bodenfeuchtesensor für smarte Bewässerung
  6. DS18B20 Temperatursensor am Raspberry Pi testen
  7. Raspberry Pi Heimtemperaturmonitor mit MCP9808, InfluxDB & Grafana bauen
  8. Sensor Telemetrie 2.0.1 – Fortschrittliche Temperaturüberwachung mit Azure IoT Hub
  9. Emotionssensor & EEG: Günstige Hardware für Echtzeit-Emotionserkennung
  10. Präzise Infrarot-Temperaturmessung für farbempfindliche Prozesse