Bauteile · 17 Min. Lesezeit

Pulssensor am Arduino: Herzfrequenz mit Pulse Sensor und KY-039

Pulssensor am Arduino: Pulse Sensor und KY-039 anschließen, BPM mit PulseSensorPlayground, Pulswelle im Seriellen Plotter, OLED-Anzeige, ESP32 und Pico.

Ein Pulssensor am Arduino ist ein kleines Modul, das den Blutfluss in deiner Fingerkuppe oder deinem Ohrläppchen mit Licht verfolgt und misst, wie oft dein Herz pro Minute schlägt, also die BPM (beats per minute, Schläge pro Minute). Die zwei verbreitetsten Modelle in der Hobbyelektronik sind der runde Pulse Sensor und der KY-039 mit Infrarot-LED. Beide werden mit drei Kabeln angeschlossen und über einen einzigen Analogpin gelesen, unterscheiden sich aber stark in der Signalqualität und darin, wie schwer zuverlässiger Code für sie zu schreiben ist.

In dieser Anleitung lernst du Schritt für Schritt, nach welchem physikalischen Prinzip ein Pulssensor arbeitet, wie du im Seriellen Plotter eine echte Pulswelle siehst, wie du mit der Bibliothek PulseSensorPlayground die BPM berechnest, wie du das schwache Signal des KY-039 mit einem Algorithmus ohne Bibliothek auswertest und wie du das Ergebnis mit einem Herzsymbol auf einem OLED-Display anzeigst. Code für ESP32 und Raspberry Pi Pico, die Faktoren, die eine Messung verfälschen, und der Unterschied zum MAX30102 sind ebenfalls dabei. Ein wichtiger Hinweis vorab: Diese Sensoren sind keine Medizinprodukte und dürfen für keine gesundheitliche Entscheidung verwendet werden.

Pulse Sensor Pulssensor mit Anschlusskabel
Pulse-Sensor-Modul: Die runde Platine mit etwa 16 mm Durchmesser vereint eine grüne LED, einen Lichtsensor und die Schaltung, die das Signal verstärkt; sie wird mit einem 3-poligen Kabel geliefert.

Wie funktioniert ein Pulssensor? Photoplethysmografie (PPG)

Alle Pulssensoren arbeiten mit einem Verfahren namens Photoplethysmografie (PPG). Bei jedem Herzschlag schickt das Herz eine Blutwelle in die Arterien; erreicht diese Welle die kleinen Gefäße in deiner Fingerkuppe, steigt das Blutvolumen im Gewebe kurz an. Weil das Hämoglobin im Blut Licht absorbiert, wird umso weniger Licht vom Gewebe zurückgestreut oder durchgelassen, je mehr Blut vorhanden ist. Der Sensor beleuchtet das Gewebe mit einer LED, und ein Lichtsensor misst das zurückkommende Licht; diese kleine, regelmäßige Schwankung der Lichtmenge ist der Puls selbst.

Die Schwierigkeit liegt darin, dass fast das gesamte gemessene Licht konstant ist. Haut, Muskeln, Knochen und das Blut in den Venen absorbieren ständig gleich viel Licht; das nennt man den Gleichanteil (DC) des Signals. Der Wechselanteil (AC), der sich mit jedem Herzschlag ändert, macht meist nur etwa 1–2 % des gesamten Lichts aus. Die eigentliche Aufgabe des Sensors ist also, eine kleine Welle auf einem großen, gleichbleibenden Pegel herauszulösen und zu verstärken. Eine PPG-Welle zeigt zuerst einen schnellen Anstieg zu einer Spitze (systolische Spitze) und danach einen langsamen Abfall; die kleine Kerbe mitten im Abfall und der kleine Buckel direkt danach (die dikrote Inzisur) entstehen beim Schließen der Aortenklappe. Ein guter Algorithmus zählt jeden Herzschlag genau einmal und hält diese Kerbe nicht für einen zweiten Schlag.

Auch wie das Licht ins Gewebe gelangt, spielt eine Rolle. Der Pulse Sensor nutzt das Reflexionsverfahren: LED und Lichtsensor liegen auf derselben Seite und messen das von der Haut zurückgestreute Licht. Sein grünes Licht wird von Hämoglobin stark absorbiert und liefert ein deutliches Signal aus den Gefäßen direkt unter der Haut; deshalb siehst du auch auf der Rückseite von Smartwatches grüne LEDs. Der KY-039 arbeitet im Durchlichtverfahren: Eine Infrarot-LED beleuchtet eine Seite des Fingers, und ein Fototransistor auf der anderen Seite misst das durchgelassene Licht. Infrarotlicht dringt tiefer ins Gewebe ein, aber weil der KY-039 keine Verstärkerschaltung hat, ist die entstehende Welle sehr klein.

MerkmalWert beim Pulse Sensor
MessverfahrenReflexions-PPG
LichtquelleGrüne LED
LichtsensorAPDS-9008, höchste Empfindlichkeit bei 565 nm
Versorgung3–5 V
StromaufnahmeEtwa 4 mA (bei 5 V)
AusgangAnalog; im Ruhezustand die halbe Versorgungsspannung (~2,5 V bei 5 V, ~512 am Uno)
PlatinendurchmesserEtwa 16 mm (0,625 Zoll)
KabelBeim Original rot +, schwarz GND, lila Signal; bei Klonen können die Farben abweichen

Pulse Sensor, KY-039 und MAX30102 im Vergleich

KY-039 Herzschlagsensor (Infrarot-LED und Fototransistor)
KY-039: oben die über den Fototransistor gebogene Infrarot-LED, darunter der Fototransistor; dein Finger kommt dazwischen. Auf der Platine gibt es keinen Verstärker, das Signal geht direkt an den Analogpin.
MerkmalPulse SensorKY-039MAX30102
VerfahrenReflexions-PPGDurchlicht-PPG (Finger dazwischen)Reflexions-PPG
LichtGrüne LEDInfrarot-LEDRot (660 nm) + Infrarot (880 nm)
Verstärker und FilterJa (Operationsverstärker)NeinIntegrierter 18-Bit-ADC, Umgebungslichtunterdrückung
AusgangAnalogAnalog (sehr schwach)Digital, I2C (Adresse 0x57)
Versorgung3–5 V3,3–5 VJe nach Modul 3,3–5 V
SignalqualitätGutSchwach, störanfälligSehr gut
SpO2 (Sauerstoff)NeinNeinJa (per Algorithmus)
BibliothekPulseSensor PlaygroundKeine fertige BibliothekSparkFun MAX3010x

Wenn du Einsteiger bist und nur deinen Puls messen willst, ist der Pulse Sensor die einfachste Wahl: Weil das Signal auf der Platine verstärkt und gefiltert wird, siehst du schon beim ersten Versuch eine klare Welle im Seriellen Plotter. Der Operationsverstärker auf der Rückseite hebt die winzige Änderung auf einen Pegel, den der Arduino lesen kann; wie man eine solche Verstärkung berechnet, kannst du im Tool Op-Amp ausprobieren. Der KY-039 ist günstig und in den meisten Sensor-Sets enthalten, aber sein Ausgang bewegt sich nur um wenige ADC-Stufen, deshalb liefert einfacher Code mit festem Schwellwert keine zuverlässigen Ergebnisse. Willst du auch die Sauerstoffsättigung im Blut (SpO2) messen, ist der MAX30102 die richtige Wahl; ihm widmen wir weiter unten einen eigenen Abschnitt.

Pulssensor anschließen: Arduino Uno, ESP32 und Pico

SensorpinArduino UnoESP32Raspberry Pi Pico
+ (VCC, rot)5V3V33V3(OUT), Pin 36
− (GND, schwarz)GNDGNDGND, Pin 38
S (Signal, lila)A0GPIO 34 (ADC1)GP26 (ADC0), Pin 31
Schlag-LED (optional)D13 (eingebaute LED)GPIO 2 (eingebaute LED)LED auf der Platine
Die Analogeingänge von ESP32 und Pico vertragen höchstens 3,3 V. Da der Ausgang des Pulse Sensors bis zur Versorgungsspannung steigen kann, versorge den Sensor an diesen Boards über den 3V3-Pin, nicht über 5V oder VBUS. Bei Klon-Platinen können Pinreihenfolge und Kabelfarben abweichen; lies vor dem Anschließen immer die Beschriftungen S, + und − auf der Platine.

Für den KY-039 gilt dieselbe Tabelle: Der S-Pin kommt an den Analogeingang, + an die Versorgung und − an GND. Achte darauf, dass die Infrarot-LED über den Fototransistor gebogen ist und dein Finger dazwischen passt. Die Bauteile auf der Rückseite des Pulse Sensors liegen frei; berührt ein Finger oder eine verschwitzte Fläche die Rückseite, entsteht Rauschen. Deshalb ist es eine gute Gewohnheit, den durchsichtigen Aufkleber aus dem Set auf die Vorderseite zu kleben und die Rückseite mit Heißkleber abzudecken. Nutzt du eine externe LED, berechne ihren Widerstand mit dem Tool LED-Vorwiderstand; die Pinbelegung deines Boards findest du im Tool Pinout.

Erster Test: die rohe Pulswelle im Seriellen Plotter sehen

Bevor du zu den BPM übergehst, prüfe, ob der Sensor wirklich einen Puls sieht. Der folgende Code nutzt keine Bibliothek; er liest das Signal 100-mal pro Sekunde und schickt es an den Seriellen Plotter. Lade den Code hoch, öffne in der Arduino IDE Werkzeuge > Serieller Plotter und stelle die Geschwindigkeit auf 115200 Baud. Wenn du den Seriellen Plotter noch nie benutzt hast, erklärt der Artikel Arduino Serieller Monitor, wie das Fenster funktioniert.

C++ (Arduino IDE)
1// Nabız sensörü ham sinyal testi (Seri Çizici) / Pulse sensor raw signal test (Serial Plotter)
2const int PULSE_PIN = A0;              // sensörün S (mor) kablosu / sensor S (purple) wire
3const unsigned long PERIOD_MS = 10;    // 10 ms = saniyede 100 örnek / 10 ms = 100 samples per second
4
5unsigned long lastSample = 0;
6
7void setup() {
8  Serial.begin(115200);                // Seri Çizici'de de 115200 seç / pick 115200 in the Serial Plotter too
9  lastSample = millis();
10}
11
12void loop() {
13  if (millis() - lastSample >= PERIOD_MS) {
14    lastSample += PERIOD_MS;           // sabit aralıkla örnekle / sample at a fixed interval
15    int raw = analogRead(PULSE_PIN);   // 0-1023 arası değer / value between 0 and 1023
16    Serial.print("Sinyal:");
17    Serial.print(raw);
18    Serial.println(",Orta:512");       // boştaki orta seviye çizgisi / idle midpoint line
19  }
20}

Ohne Finger liegt die Linie des Pulse Sensors mit leichtem Zittern flach bei etwa 512; das ist die halbe Versorgungsspannung und zeigt, dass der Sensor richtig angeschlossen ist. Legst du deine Fingerkuppe leicht auf den Sensor, erscheint innerhalb weniger Sekunden eine Welle, die bei jedem Herzschlag schnell steigt und langsam fällt; ihre Amplitude liegt meist zwischen 100 und 300 Stufen. Siehst du keine Welle, mach nicht mit dem BPM-Code weiter: Korrigiere zuerst Verkabelung, Versorgung und Fingerdruck. Werden die Spitzen zu einer geraden Linie, die bei 0 oder 1023 klebt, wird das Signal abgeschnitten; meist bewegt sich der Finger oder der Sensor wird zu fest gedrückt. Wenn du wissen willst, woher der Bereich 0–1023 eines Analogwerts kommt, lies den Artikel ADC und Analog-Digital-Wandlung.

BPM messen mit PulseSensorPlayground

  1. Öffne in der Arduino IDE den Bibliotheksverwalter, suche nach PulseSensor Playground und installiere die Bibliothek. Details zum Installieren von Bibliotheken findest du im Artikel Arduino-Bibliothek hinzufügen.
  2. Schließe den Sensor wie in der Tabelle an: + an 5V, − an GND und S an A0.
  3. Lade den folgenden Code hoch und öffne den Seriellen Plotter mit 115200 Baud; die erste Linie zeigt das Rohsignal, die zweite den BPM-Wert.
  4. Lege deinen Finger auf den Sensor, stütze die Hand auf den Tisch und halte 10 Sekunden still; die Bibliothek bildet ihren Mittelwert über die ersten Schläge.
  5. Möchtest du die Zahlen lieber Zeile für Zeile im Seriellen Monitor sehen, setze die Konstante PLOTTER im Code auf false; dann werden bei jedem Schlag die BPM und die Zeit zwischen den Schlägen (IBI) ausgegeben.
C++ (Arduino IDE)
1// PulseSensor Playground ile BPM ölçme / Measuring BPM with PulseSensor Playground
2#include <PulseSensorPlayground.h>
3
4const int PULSE_PIN = A0;              // sensörün S (mor) kablosu / sensor S (purple) wire
5const int LED_PIN = LED_BUILTIN;       // her atışta yanan LED (D13) / LED lit on each beat (D13)
6const int THRESHOLD = 550;             // başlangıç eşiği, boşta ~512 / starting threshold, idle ~512
7const bool PLOTTER = true;             // true: Seri Çizici, false: Seri Monitör / true: Serial Plotter, false: Serial Monitor
8
9PulseSensorPlayground pulseSensor;     // kütüphane nesnesi / library object
10unsigned long lastPrint = 0;
11
12void setup() {
13  Serial.begin(115200);
14  pulseSensor.analogInput(PULSE_PIN);
15  pulseSensor.blinkOnPulse(LED_PIN);
16  pulseSensor.setThreshold(THRESHOLD);
17  if (!pulseSensor.begin()) {          // 2 ms'lik zamanlayıcıyı başlatır / starts the 2 ms timer
18    Serial.println(F("PulseSensor baslatilamadi / could not start"));
19    while (true) {}
20  }
21}
22
23void loop() {
24  // Atış anı: BPM ve atışlar arası süre / moment of a beat: BPM and inter-beat interval
25  if (pulseSensor.sawStartOfBeat() && !PLOTTER) {
26    Serial.print(F("BPM: "));
27    Serial.print(pulseSensor.getBeatsPerMinute());
28    Serial.print(F("  IBI: "));
29    Serial.print(pulseSensor.getInterBeatIntervalMs());
30    Serial.println(F(" ms"));
31  }
32
33  // 20 ms'de bir çizim satırı / one plotter line every 20 ms
34  if (PLOTTER && millis() - lastPrint >= 20) {
35    lastPrint = millis();
36    Serial.print(F("Sinyal:"));
37    Serial.print(pulseSensor.getLatestSample());
38    Serial.print(F(",BPM:"));
39    Serial.println(pulseSensor.getBeatsPerMinute());
40  }
41}
FunktionAufgabe
analogInput(pin)Wählt den Analogpin, an dem der Sensor hängt (Standard A0)
blinkOnPulse(pin)Lässt die LED an diesem Pin leuchten, solange das Signal über dem Schwellwert liegt, also während jedes Schlags
fadeOnPulse(pin)Lässt die LED an einem PWM-Pin bei jedem Schlag aufleuchten und langsam verblassen
setThreshold(wert)Startschwellwert; wird kein Schlag gefunden, kehrt der Schwellwert zu diesem Wert zurück
begin()Startet die Abtastung; liefert bei Erfolg true
sawStartOfBeat()Liefert true, wenn seit dem letzten Aufruf ein neuer Schlag begonnen hat
getBeatsPerMinute()BPM aus dem Mittelwert der letzten 10 Schlagabstände
getInterBeatIntervalMs()Zeit zwischen den letzten beiden Schlägen (IBI) in Millisekunden
getLatestSample()Der zuletzt gelesene Rohwert (0–1023)
isInsideBeat()Liefert true, solange das Signal über dem Schwellwert liegt

Die Bibliothek liest den Sensor im Hintergrund per Timer-Interrupt alle 2 ms (500-mal pro Sekunde) aus, sodass die Abtastung gleichmäßig bleibt, auch wenn du in loop() lange Aufgaben erledigst. Bei jedem Schlag speichert sie Spitze und Tal der Welle und setzt den Schwellwert genau in die Mitte dazwischen; der Wert, den du setThreshold() übergibst, ist also nur ein Startpunkt. Anstiege, die innerhalb von 250 ms nach dem letzten Schlag oder vor Ablauf von drei Fünfteln des letzten Schlagabstands kommen, ignoriert sie, damit die dikrote Inzisur nicht als zweiter Schlag zählt. Die BPM werden aus dem Mittelwert der letzten 10 Schlagabstände berechnet; wird 2,5 Sekunden lang kein Schlag gefunden, setzt sich alles zurück und getBeatsPerMinute() liefert 0.

Wähle den Startschwellwert anhand des Rohsignals: 30–50 Stufen über dem höchsten Wert, den die Linie ohne Finger erreicht, sind ein guter Anfang (bei den meisten Sensoren zwischen 530 und 600). Ist der Schwellwert zu niedrig, wird auch ohne Finger Rauschen als Schlag gezählt; ist er zu hoch, gehen schwache Pulse verloren. Noch ein Hinweis: Am Arduino Uno nutzt die Bibliothek Timer1 für die Abtastung, deshalb funktioniert analogWrite() an den Pins 9 und 10 nicht. Fügst du deinem Projekt die Servo-Bibliothek hinzu, wechselt die Bibliothek zu Timer2; dann sind stattdessen PWM an den Pins 3 und 11 sowie die Funktion tone() betroffen.

Für eine genaue Messung: Fingerdruck, Umgebungslicht und Bewegung

Die meisten Dinge, die eine Pulsmessung verfälschen, haben mit der Haltung des Sensors zu tun, nicht mit dem Code. Probiere die folgenden Punkte bei geöffnetem Seriellen Plotter einzeln aus; zu sehen, wie jeder davon die Welle verändert, ist der schnellste Weg, den Sensor zu verstehen.

  • Fingerdruck: Drücke leicht, aber gleichmäßig. Berührst du den Sensor zu leicht, erzeugt jedes Zucken des Fingers ein Rauschen, das größer ist als die Welle; drückst du zu fest, quetschst du die Kapillaren ab, der Blutfluss sinkt und die Welle wird flach oder verschwindet ganz. Die besten Ergebnisse erzielst du, wenn du den Sensor mit einem Klettband um den Finger wickelst.
  • Umgebungslicht: Sonnenlicht und Lampen dringen seitlich in den Sensor ein. Im 50-Hz-Netz flackern viele Lampen 100-mal pro Sekunde, und dieses Flackern gelangt ins Signal. Decke die Umgebung des Sensors mit dem Finger vollständig ab oder wickle ein dunkles Band darüber.
  • Bewegung: Ein schwingendes Kabel, Sprechen, Fingerbewegungen oder Gehen erzeugen Signale, die viel größer sind als der Puls. Stütze die Hand während der Messung auf den Tisch und halte still.
  • Kalte Hände: In einem kalten Finger verengen sich die Gefäße und das Signal wird schwächer. Wärme deine Hände auf oder klemme den Sensor ans Ohrläppchen; das Ohrläppchen reagiert außerdem weniger auf Bewegung.
  • Schweiß und Hautfett: Feuchte Haut streut Licht anders und verschmutzt die Sensoroberfläche. Nutze den durchsichtigen Schutzaufkleber auf der Vorderseite und wische den Sensor ab und zu mit einem trockenen Tuch ab.
  • Einschwingzeit: In den ersten 5–10 Sekunden nach dem Auflegen des Fingers beruhigt sich das Signal und der Mittelwert füllt sich; ignoriere die Werte aus dieser Zeit.
Es gibt einen einfachen Weg zu prüfen, ob dein Sensor richtig misst: Zähle 30 Sekunden lang den Puls an deinem Handgelenk oder Hals, multipliziere mit 2 und vergleiche das Ergebnis mit den BPM auf dem Bildschirm. In Ruhe ist eine Abweichung von 3–5 BPM normal; ist sie größer, korrigiere zuerst Fingerdruck und Umgebungslicht und passe dann den Schwellwert an.

Pulsanzeige auf dem OLED-Display

Der Serielle Plotter reicht am Schreibtisch, aber ein tragbarer Pulsmesser braucht einen kleinen Bildschirm. Der folgende Code zeichnet im oberen Teil eines 0,96-Zoll-OLED-Displays mit SSD1306 die BPM in großen Ziffern und ein Herzsymbol, das bei jedem Schlag größer wird, und im unteren Teil eine live von links nach rechts laufende Pulswelle. Verbinde das OLED mit den Pins A4 (SDA) und A5 (SCL) des Uno und seine Versorgung mit 5V und GND; die Installation der Display-Bibliothek und das Einstellen der Adresse erklärt der Artikel OLED-Display (SSD1306) verwenden ausführlich.

C++ (Arduino IDE)
1// Pulse Sensor + 0,96" OLED: BPM, kalp simgesi ve nabız dalgası / BPM, heart icon and pulse wave
2#include <Wire.h>
3#include <Adafruit_GFX.h>
4#include <Adafruit_SSD1306.h>
5#include <PulseSensorPlayground.h>
6
7Adafruit_SSD1306 display(128, 64, &Wire, -1);  // 128x64, reset pini yok / no reset pin
8PulseSensorPlayground pulseSensor;
9
10const int PULSE_PIN = A0;              // sensörün S pini / sensor S pin
11const int THRESHOLD = 550;             // başlangıç eşiği / starting threshold
12const int GRAPH_TOP = 24;              // dalga alanının üst kenarı / top edge of the wave area
13const int GRAPH_H = 40;                // dalga alanının yüksekliği / height of the wave area
14
15int x = 0;                             // tarama sütunu (0-127) / sweep column (0-127)
16int lastY = GRAPH_TOP + GRAPH_H / 2;   // önceki noktanın y'si / y of the previous point
17int lo = 1023, hi = 0;                 // bu taramadaki en küçük/büyük değer / min/max in this sweep
18int scaleLo = 400, scaleHi = 624;      // çizimde kullanılan aralık / range used for drawing
19unsigned long lastDraw = 0, beatTime = 0;
20
21// İki daire ve bir üçgenle kalp çiz / draw a heart from two circles and a triangle
22void drawHeart(int cx, int cy, bool big) {
23  int r = big ? 6 : 4;                 // atış anında büyük kalp / bigger heart on a beat
24  display.fillCircle(cx - r, cy - r / 2, r, SSD1306_WHITE);
25  display.fillCircle(cx + r, cy - r / 2, r, SSD1306_WHITE);
26  display.fillTriangle(cx - 2 * r, cy, cx + 2 * r, cy, cx, cy + 2 * r, SSD1306_WHITE);
27}
28
29void setup() {
30  Serial.begin(115200);
31  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {  // I2C adresi 0x3C / I2C address 0x3C
32    Serial.println(F("SSD1306 allocation failed"));
33    while (true) {}
34  }
35  display.clearDisplay();
36  pulseSensor.analogInput(PULSE_PIN);
37  pulseSensor.setThreshold(THRESHOLD);
38  pulseSensor.begin();                 // örnekleme kesmeyle sürer / sampling runs in an interrupt
39}
40
41void loop() {
42  if (pulseSensor.sawStartOfBeat()) beatTime = millis();  // atış anını sakla / store the beat time
43
44  if (millis() - lastDraw < 40) return; // saniyede 25 kare / 25 frames per second
45  lastDraw = millis();
46
47  // Alt bölüm: soldan sağa taranan nabız dalgası / bottom: pulse wave swept left to right
48  int s = pulseSensor.getLatestSample();
49  if (s < lo) lo = s;
50  if (s > hi) hi = s;
51  int y = map(constrain(s, scaleLo, scaleHi), scaleLo, scaleHi, GRAPH_TOP + GRAPH_H - 1, GRAPH_TOP);
52  display.drawFastVLine(x, GRAPH_TOP, GRAPH_H, SSD1306_BLACK);              // eski sütunu sil / erase the old column
53  display.drawFastVLine((x + 1) % 128, GRAPH_TOP, GRAPH_H, SSD1306_BLACK);  // önündeki boşluk / gap ahead
54  if (x > 0) display.drawLine(x - 1, lastY, x, y, SSD1306_WHITE);
55  lastY = y;
56  if (++x >= 128) {                    // tarama bitti: ölçeği yenile / sweep done: refresh the scale
57    x = 0;
58    if (hi - lo > 20) { scaleLo = lo; scaleHi = hi; }
59    lo = 1023;
60    hi = 0;
61  }
62
63  // Üst bölüm: kalp ve BPM / top: heart and BPM
64  display.fillRect(0, 0, 128, GRAPH_TOP, SSD1306_BLACK);
65  drawHeart(14, 10, millis() - beatTime < 150);
66  int bpm = pulseSensor.getBeatsPerMinute();
67  display.setTextColor(SSD1306_WHITE);
68  display.setTextSize(3);
69  display.setCursor(40, 0);
70  if (bpm > 0) display.print(bpm);
71  else display.print(F("--"));         // parmak yok / no finger
72  display.setTextSize(1);
73  display.setCursor(104, 14);
74  display.print(F("BPM"));
75  display.display();                   // tamponu ekrana gönder / push the buffer to the screen
76}

Der Code aktualisiert den Bildschirm 25-mal pro Sekunde und fügt der Welle in jedem Bild einen Punkt hinzu; so zeigt die Bildschirmbreite etwa 5 Sekunden Pulsaufzeichnung. Am Ende jedes Durchlaufs wird die Welle auf den kleinsten und größten Wert dieses Durchlaufs neu skaliert, damit ein schwaches wie ein starkes Signal auf den Bildschirm passt. Ein Bild per I2C ans Display zu senden dauert etwa 25–30 ms, aber weil die Bibliothek per Interrupt abtastet, geht dabei kein Schlag verloren. Am Uno belegt der Bildpuffer des Displays 1 KB RAM; da der Code die Welle direkt auf den Bildschirm zeichnet, ohne sie in einem Array zu speichern, reicht der Speicher, auch wenn nur etwa 450 Byte frei bleiben.

Herzfrequenz messen mit dem KY-039

Beim KY-039 ist der Fototransistor mit einem Widerstand in Reihe geschaltet, und die Ausgangsspannung ändert sich mit dem einfallenden Licht: Mehr Licht bedeutet eine niedrigere Spannung, weniger Licht eine höhere. Dazwischen gibt es weder Verstärker noch Filter. Mit aufgelegtem Finger verschiebt ein Herzschlag diese Spannung meist nur um 2–10 ADC-Stufen, also etwa 10–50 mV; schon eine kleine Änderung des Umgebungslichts ist größer. Deshalb funktionieren Code für den Pulse Sensor und ein fester Schwellwert wie 550 beim KY-039 nicht; du musst das Signal erst säubern und dann einen Schwellwert verwenden, der sich an die Größe der Welle selbst anpasst.

Der folgende Code erledigt das in vier Schritten. Zuerst liest er den Analogpin 20 ms lang ununterbrochen aus (etwa 175 Messungen) und bildet den Mittelwert; das senkt zufälliges Rauschen und löscht das Lampenflackern fast vollständig aus, weil 20 ms genau eine Periode des 50-Hz-Netzes sind. Dann glättet er das Signal mit einem leichten Tiefpassfilter und entfernt den Gleichanteil mit einem sehr langsamen gleitenden Mittelwert, sodass nur die Pulswelle übrig bleibt. Im dritten Schritt berechnet er zwei Hüllkurven, die den Spitzen und Tälern der Welle folgen, und legt den Schwellwert in ihre Mitte. Zum Schluss wird ein Schlag gezählt, wenn das Signal den Schwellwert nach oben kreuzt, und die BPM ergeben sich aus dem Mittelwert der letzten 8 Schlagabstände.

C++ (Arduino IDE)
1// KY-039 ile nabız ölçme (kütüphanesiz) / Heart rate with the KY-039 (no library)
2const int SENSOR_PIN = A0;             // KY-039 S pini / KY-039 S pin
3const int LED_PIN = LED_BUILTIN;       // atışta yanan LED / LED lit on each beat
4const bool PLOTTER = true;             // true: Seri Çizici, false: Seri Monitör / true: Serial Plotter, false: Serial Monitor
5const unsigned long WINDOW_US = 20000; // 20 ms = 50 Hz şebekenin bir periyodu / one period of 50 Hz mains
6const float MIN_AMP = 1.2;             // tepe-tepe gürültü sınırı (ADC birimi) / peak-to-peak noise limit (ADC units)
7const int AVG_BEATS = 8;               // BPM için ortalanan aralık sayısı / intervals averaged for BPM
8
9float smooth, baseline;                // süzülmüş sinyal ve DC seviye / filtered signal and DC level
10float posEnv = 0, negEnv = 0;          // tepe ve çukur zarfları / peak and trough envelopes
11bool armed = true;                     // yeni atışa hazır mı / ready for a new beat
12unsigned long lastBeat = 0;            // son atışın zamanı / time of the last beat
13unsigned int ibi[AVG_BEATS];           // atışlar arası süreler (ms) / inter-beat intervals (ms)
14int ibiCount = 0, ibiIndex = 0, bpm = 0;
15
16// 20 ms boyunca okuyup ortalama al / read for 20 ms and average
17float readWindow() {
18  unsigned long start = micros();
19  long sum = 0;
20  int n = 0;
21  while (micros() - start < WINDOW_US) {
22    sum += analogRead(SENSOR_PIN);
23    n++;
24  }
25  return (float)sum / n;
26}
27
28float averageIbi() {
29  if (ibiCount == 0) return 500.0;     // geçmiş yoksa 120 BPM varsay (ilk bekleme 350 ms) / assume 120 BPM without history (first wait 350 ms)
30  unsigned long sum = 0;
31  for (int i = 0; i < ibiCount; i++) sum += ibi[i];
32  return (float)sum / ibiCount;
33}
34
35void addInterval(unsigned int ms) {
36  ibi[ibiIndex] = ms;                  // halka tampon / ring buffer
37  ibiIndex = (ibiIndex + 1) % AVG_BEATS;
38  if (ibiCount < AVG_BEATS) ibiCount++;
39  bpm = (int)(60000.0 / averageIbi() + 0.5);
40}
41
42void setup() {
43  Serial.begin(115200);
44  pinMode(LED_PIN, OUTPUT);
45  smooth = baseline = readWindow();    // başlangıç seviyesi / starting level
46}
47
48void loop() {
49  float x = readWindow();              // saniyede 50 örnek / 50 samples per second
50  unsigned long now = millis();
51
52  smooth += (x - smooth) * 0.5;        // hafif alçak geçiren süzgeç / light low-pass filter
53  baseline += (smooth - baseline) * 0.05;  // yavaş DC takibi (~0,4 s) / slow DC tracking (~0.4 s)
54  float ac = smooth - baseline;        // yalnız nabız dalgası / pulse wave only
55
56  // Zarflar: uçları hemen yakala, yavaşça bırak / envelopes: catch extremes fast, release slowly
57  posEnv = (ac > posEnv) ? ac : posEnv * 0.99;
58  negEnv = (ac < negEnv) ? ac : negEnv * 0.99;
59  float amp = posEnv - negEnv;         // tepe-tepe genlik / peak-to-peak amplitude
60  float mid = (posEnv + negEnv) / 2;   // eşik: dalganın ortası / threshold: middle of the wave
61
62  unsigned long sinceBeat = now - lastBeat;
63  float refractory = constrain(averageIbi() * 0.7, 250.0, 600.0);  // dikrotik çentiği atla / skip the dicrotic notch
64
65  if (armed && amp > MIN_AMP && ac > mid && sinceBeat > refractory) {
66    armed = false;                     // atış bulundu / beat found
67    digitalWrite(LED_PIN, HIGH);
68    if (sinceBeat < 2000) addInterval(sinceBeat);  // 30 BPM'den yavaşı alma / ignore gaps slower than 30 BPM
69    lastBeat = now;
70    if (!PLOTTER && bpm > 0) {
71      Serial.print(F("BPM: "));
72      Serial.println(bpm);
73    }
74  }
75  if (!armed && ac < mid - amp * 0.25) {  // dalga yeterince indi: yeniden hazır / wave fell enough: re-arm
76    armed = true;
77    digitalWrite(LED_PIN, LOW);
78  }
79  if (sinceBeat > 2500 && ibiCount > 0) {  // 2,5 s atış yok: sıfırla / no beat for 2.5 s: reset
80    ibiCount = 0;
81    bpm = 0;
82  }
83
84  if (PLOTTER) {
85    Serial.print(F("Nabiz:"));
86    Serial.print(ac);
87    Serial.print(F(",Esik:"));
88    Serial.println(mid);
89  }
90}
Konstante / WertAufgabe
WINDOW_US = 20000Mittelungsfenster; löscht das 50-Hz-Netzflackern (in 60-Hz-Ländern 16667 verwenden)
0.5 (smooth)Leichter Tiefpassfilter; glättet plötzliche Sprünge
0.05 (baseline)Folgt dem Gleichanteil in etwa 0,4 Sekunden; entfernt langsame Änderungen des Fingerdrucks
0.99 (Hüllkurven)Abklinggeschwindigkeit der Spitzen- und Talhüllkurve (etwa 2 Sekunden)
MIN_AMP = 1.2Eine Welle unter dieser Spitze-Spitze-Amplitude gilt als Rauschen
refractory70 % des aktuellen mittleren Abstands (250–600 ms; ohne Vorgeschichte 350 ms, damit auch ein schneller Puls erkannt wird); verhindert, dass die dikrote Inzisur als zweiter Schlag zählt
mid - amp * 0.25Pegel zum erneuten Scharfschalten; erst wenn die Welle weit genug gefallen ist, wird ein neuer Schlag gesucht

Wenn du den Code hochlädst und den Seriellen Plotter öffnest, siehst du zwei Linien: Nabiz (Puls) zeigt die bereinigte Welle und Esik (Schwellwert) den adaptiven Schwellwert, der durch ihre Mitte läuft. Prüfe, wie stark sich die Nabiz-Linie ohne Finger bewegt, und setze MIN_AMP etwas über diesen Spitze-Spitze-Wert; ist dein Signal sehr schwach, senke ihn. Mehr über das Glätten von Signalen erfährst du im Artikel Sensordaten filtern. Derselbe Code funktioniert auch mit dem Pulse Sensor; dann musst du MIN_AMP nur auf etwa 20 erhöhen.

Pulssensor mit dem ESP32

Versorge den Sensor am ESP32 über den 3V3-Pin und verbinde das Signal mit GPIO 34. Dieser Pin gehört zur ADC1-Gruppe; ADC2-Pins lassen sich bei eingeschaltetem WLAN nicht lesen. Wenn du die Pulsdaten ins Internet schicken willst, musst du also einen der ADC1-Pins (GPIO 32–39) wählen. Der ADC des ESP32 hat 12 Bit (0–4095), und bei 3,3 V Versorgung liefert der Pulse Sensor im Ruhezustand einen Wert um 2000.

Aktuelle Versionen von PulseSensorPlayground unterstützen auch den ESP32, aber die Bibliothek nutzt die Timer-Funktionen der Version 3.x des ESP32-Boardpakets und lässt sich mit Version 2.x nicht kompilieren. In ihren Beispielen senkt sie den ADC mit analogReadResolution(10) auf 10 Bit, sodass der Schwellwert wie am Uno auf der Skala 0–1023 angegeben wird (im ESP32-Beispiel 685). Damit dich Versionsunterschiede nicht betreffen, findest du unten einen Sketch ohne Bibliothek, der denselben Algorithmus wie im KY-039-Abschnitt nutzt; er läuft mit beiden Versionen des Boardpakets.

C++ (Arduino IDE)
1// ESP32 + Pulse Sensor: kütüphanesiz BPM / BPM without a library
2const int SENSOR_PIN = 34;             // ADC1 pini, Wi-Fi açıkken de çalışır / ADC1 pin, works with Wi-Fi on
3const int LED_PIN = 2;                 // çoğu DevKit'te dahili LED / built-in LED on most DevKits
4const unsigned long PERIOD_MS = 10;    // saniyede 100 örnek / 100 samples per second
5const float MIN_AMP = 80;              // 12 bitte tepe-tepe gürültü sınırı / peak-to-peak noise limit in 12-bit units
6const int AVG_BEATS = 8;               // BPM için ortalanan aralık sayısı / intervals averaged for BPM
7
8float smooth, baseline, posEnv = 0, negEnv = 0;
9bool armed = true;
10unsigned long lastSample = 0, lastBeat = 0;
11unsigned long ibi[AVG_BEATS];
12int ibiCount = 0, ibiIndex = 0, bpm = 0;
13
14// 8 okumanın ortalaması: ESP32 ADC'si gürültülüdür / average of 8 reads: the ESP32 ADC is noisy
15float readSensor() {
16  long sum = 0;
17  for (int i = 0; i < 8; i++) sum += analogRead(SENSOR_PIN);
18  return sum / 8.0;
19}
20
21float averageIbi() {
22  if (ibiCount == 0) return 500.0;     // geçmiş yoksa 120 BPM varsay (ilk bekleme 350 ms) / assume 120 BPM without history (first wait 350 ms)
23  unsigned long sum = 0;
24  for (int i = 0; i < ibiCount; i++) sum += ibi[i];
25  return (float)sum / ibiCount;
26}
27
28void setup() {
29  Serial.begin(115200);
30  pinMode(LED_PIN, OUTPUT);
31  analogReadResolution(12);            // 0-4095 arası değer / values from 0 to 4095
32  smooth = baseline = readSensor();
33  lastSample = millis();
34}
35
36void loop() {
37  if (millis() - lastSample < PERIOD_MS) return;
38  lastSample += PERIOD_MS;             // sabit aralıkla örnekle / sample at a fixed interval
39  unsigned long now = millis();
40
41  float x = readSensor();
42  smooth += (x - smooth) * 0.3;        // alçak geçiren süzgeç (~6 Hz) / low-pass filter (~6 Hz)
43  baseline += (smooth - baseline) * 0.02;  // DC takibi (~0,5 s) / DC tracking (~0.5 s)
44  float ac = smooth - baseline;
45
46  posEnv = (ac > posEnv) ? ac : posEnv * 0.995;  // tepe zarfı / peak envelope
47  negEnv = (ac < negEnv) ? ac : negEnv * 0.995;  // çukur zarfı / trough envelope
48  float amp = posEnv - negEnv;
49  float mid = (posEnv + negEnv) / 2;
50
51  unsigned long sinceBeat = now - lastBeat;
52  float refractory = constrain(averageIbi() * 0.7, 250.0, 600.0);
53
54  if (armed && amp > MIN_AMP && ac > mid && sinceBeat > refractory) {
55    armed = false;
56    digitalWrite(LED_PIN, HIGH);
57    if (sinceBeat < 2000) {            // 30-240 BPM aralığı / 30-240 BPM range
58      ibi[ibiIndex] = sinceBeat;
59      ibiIndex = (ibiIndex + 1) % AVG_BEATS;
60      if (ibiCount < AVG_BEATS) ibiCount++;
61      bpm = (int)(60000.0 / averageIbi() + 0.5);
62      Serial.printf("BPM: %d  IBI: %lu ms\n", bpm, sinceBeat);
63    }
64    lastBeat = now;
65  }
66  if (!armed && ac < mid - amp * 0.25) {
67    armed = true;
68    digitalWrite(LED_PIN, LOW);
69  }
70  if (sinceBeat > 2500 && ibiCount > 0) {  // parmak çekildi / finger removed
71    ibiCount = 0;
72    bpm = 0;
73    Serial.println("Parmak yok / no finger");
74  }
75}

In diesem Code wird das Signal 100-mal pro Sekunde gelesen, und weil der ADC des ESP32 verrauscht ist, ist jede Probe der Mittelwert aus 8 Messungen. Schwellwert und Hüllkurven sind auf die 12-Bit-Skala abgestimmt, daher ist MIN_AMP 80 (etwa 65 mV). Die eigentliche Stärke des ESP32 ist das WLAN: Du kannst die BPM live per WebSocket an eine Webseite senden, eine Telegram-Nachricht verschicken, wenn der Puls eine Grenze überschreitet, oder deine Messungen in Google Sheets protokollieren.

Herzfrequenz messen mit Raspberry Pi Pico und MicroPython

Verbinde am Raspberry Pi Pico den Pin + des Sensors mit 3V3(OUT) (Pin 36), − mit GND (Pin 38) und S mit GP26 (ADC0, Pin 31). In MicroPython liefert read_u16() einen Wert von 0 bis 65535; der Code schiebt ihn um 4 Bit nach rechts und bringt ihn so auf dieselbe Skala 0–4095 wie beim ESP32, damit die Schwellwerte auf beiden Boards gleich bleiben. Speicherst du den Code in Thonny als main.py auf dem Pico, startet er bei jedem Einschalten des Boards von selbst.

MicroPython
1# Raspberry Pi Pico + Pulse Sensor: MicroPython ile BPM / BPM with MicroPython
2from machine import ADC, Pin
3import time
4
5sensor = ADC(26)                   # GP26 = ADC0, sensörün S pini / sensor S pin
6led = Pin("LED", Pin.OUT)          # kartın üzerindeki LED / on-board LED
7
8PERIOD_MS = 10                     # saniyede 100 örnek / 100 samples per second
9MIN_AMP = 80                       # 12 bitte tepe-tepe gürültü sınırı / peak-to-peak noise limit (12-bit)
10AVG_BEATS = 8                      # BPM için ortalanan aralık / intervals averaged for BPM
11
12def read12():
13    # 4 okumanın ortalaması, 0-4095 / average of 4 reads, 0-4095
14    s = 0
15    for _ in range(4):
16        s += sensor.read_u16() >> 4
17    return s / 4
18
19smooth = baseline = read12()
20pos_env = neg_env = 0.0
21armed = True
22ibis = []
23bpm = 0
24last_beat = time.ticks_ms()
25next_t = time.ticks_ms()
26
27while True:
28    while time.ticks_diff(time.ticks_ms(), next_t) < 0:
29        pass                       # bir sonraki örnek zamanını bekle / wait for the next sample time
30    next_t = time.ticks_add(next_t, PERIOD_MS)
31    now = time.ticks_ms()
32
33    x = read12()
34    smooth += (x - smooth) * 0.3            # alçak geçiren süzgeç / low-pass filter
35    baseline += (smooth - baseline) * 0.02  # DC takibi / DC tracking
36    ac = smooth - baseline
37
38    pos_env = ac if ac > pos_env else pos_env * 0.995
39    neg_env = ac if ac < neg_env else neg_env * 0.995
40    amp = pos_env - neg_env
41    mid = (pos_env + neg_env) / 2
42
43    since = time.ticks_diff(now, last_beat)
44    avg_ibi = sum(ibis) / len(ibis) if ibis else 500   # geçmiş yoksa 120 BPM varsay / assume 120 BPM without history
45    refractory = min(max(avg_ibi * 0.7, 250), 600)   # dikrotik çentiği atla / skip the dicrotic notch
46
47    if armed and amp > MIN_AMP and ac > mid and since > refractory:
48        armed = False
49        led.on()
50        if since < 2000:                    # 30 BPM'den yavaşı alma / ignore gaps slower than 30 BPM
51            ibis.append(since)
52            if len(ibis) > AVG_BEATS:
53                ibis.pop(0)
54            bpm = round(60000 / (sum(ibis) / len(ibis)))
55            print("BPM:", bpm, "IBI:", since)
56        last_beat = now
57    if not armed and ac < mid - amp * 0.25:
58        armed = True
59        led.off()
60    if since > 2500 and ibis:               # parmak çekildi / finger removed
61        ibis = []
62        bpm = 0
63        print("Parmak yok / no finger")

Der Code arbeitet nach derselben Logik wie die ESP32-Version: Er nimmt alle 10 ms eine Probe, entfernt den Gleichanteil, findet den Schlag mit einem adaptiven Schwellwert und berechnet die BPM aus dem Mittelwert der letzten 8 Abstände. Bei jedem Schlag leuchtet die LED auf der Platine auf, und BPM sowie Schlagabstand erscheinen im Shell-Fenster von Thonny. Ist dir die Python-Syntax neu, ist der Artikel MicroPython-Grundlagen ein guter Einstieg.

Unterschied zum MAX30102: Sauerstoffsättigung (SpO2) messen

Der MAX30102 ist ein Ein-Chip-Sensor, den Analog Devices (früher Maxim Integrated) für die Messung von Herzfrequenz und Sauerstoffsättigung im Blut entwickelt hat. Er enthält zwei LEDs, rot (660 nm) und infrarot (880 nm), eine Fotodiode, eine Schaltung zur Unterdrückung des Umgebungslichts und einen 18-Bit-ADC. Er misst selbst, speichert die Proben in einem Speicher für 32 Werte (FIFO) und sendet sie als Zahlen über den I2C-Bus (Adresse 0x57). Weil Störungen auf analogen Leitungen und die 10-Bit-Grenze des Arduino-ADC wegfallen, ist sein Signal viel sauberer als das des Pulse Sensors.

Das Geheimnis der SpO2-Messung ist Licht in zwei verschiedenen Farben. Sauerstoffbeladenes Hämoglobin absorbiert mehr Infrarot- und weniger Rotlicht; Hämoglobin, das seinen Sauerstoff abgegeben hat, verhält sich umgekehrt. Der Sensor misst für beide Lichtarten Wechsel- und Gleichanteil der Pulswelle und berechnet das Verhältnis R = (AC_rot / DC_rot) / (AC_infrarot / DC_infrarot); die SpO2 ergibt sich daraus über eine empirische Kalibrierkurve. Pulse Sensor und KY-039 arbeiten mit Licht nur einer Farbe, können dieses Verhältnis also nicht bilden und damit keinen Sauerstoff messen. Die beliebteste Arduino-Bibliothek für den MAX30102 ist SparkFun MAX3010x; ihr Beispiel Example5_HeartRate berechnet die Herzfrequenz, Example8_SPO2 die Sauerstoffsättigung.

Bei manchen günstigen MAX30102-Platinen hängen die I2C-Pull-up-Widerstände an 1,8 V; ein Arduino Uno mit 5 V erkennt diesen Pegel nicht als HIGH, deshalb wird der Sensor nicht gefunden. Hat deine Platine eine Lötbrücke zur Wahl zwischen 1,8 V und 3,3 V, setze sie auf 3,3 V. Auch diese Module reagieren sehr empfindlich auf Fingerdruck und Bewegung.

Ein Pulssensor ist kein Medizinprodukt

Die Schaltungen und der Code in diesem Artikel sind für Lern- und Hobbyzwecke gedacht. Pulse Sensor, KY-039 und MAX30102 sind nicht als Medizinprodukte geprüft oder zugelassen; stelle anhand ihrer Werte keine Diagnose und triff keine Entscheidungen über Medikamente oder Behandlungen. Hast du Brustschmerzen, Herzrasen, Schwindel, Ohnmachtsanfälle oder Atemnot, verlass dich nicht auf einen Sensor: Hol dir sofort ärztliche Hilfe und ruf im Notfall die 112 an.

Dafür gibt es konkrete Gründe. Medizinische Pulsoximeter und Herzfrequenzmessgeräte werden auf bestimmte Genauigkeitsgrenzen kalibriert und an verschiedenen Hauttönen, bei geringer Durchblutung und in Bewegung getestet; Hobbymodule und die Algorithmen, die wir schreiben, durchlaufen diese Tests nicht. Außerdem ist Pulsmessen nicht dasselbe wie die Beurteilung des Herzrhythmus: Um Rhythmusstörungen zu erkennen, braucht man ein EKG, das die elektrischen Signale des Herzens aufzeichnet. Als allgemeine Information: Der Ruhepuls eines Erwachsenen liegt meist zwischen 60 und 100 Schlägen pro Minute; bei Menschen, die regelmäßig Sport treiben, kann er auf 40–60 sinken. Liegt dein Messwert außerhalb dieses Bereichs, prüfe zuerst Sensor und Code; machst du dir weiter Sorgen, sprich mit einer Ärztin oder einem Arzt.

Projektideen mit dem Pulssensor

  • Licht im Herzschlag: Lass bei jedem Schlag einen WS2812B-LED-Ring rot aufleuchten und langsam verblassen; verschiebe die Farbe mit steigenden BPM von Blau nach Rot.
  • Pulsmesser mit Piepton: Spiel jedes Mal, wenn sawStartOfBeat() true liefert, einen kurzen Piepton über einen Buzzer; ein lustiges Projekt, das den Klang von Krankenhausmonitoren nachahmt.
  • Tragbarer Pulsmesser: Bau den OLED-Code mit Akku und Ladeschaltung in ein Gehäuse; schalte den Bildschirm nach der Messung ab, damit der Akku länger hält.
  • Erholungstest nach dem Sport: Miss direkt nach dem Training und berechne, um wie viele Schläge dein Puls in der ersten Minute sinkt; notiere den Wert jeden Tag und verfolge deinen Fortschritt.
  • Pulslogger: Schreib die BPM-Werte mit Zeitstempel auf eine SD-Karte oder übertrage sie nach Excel und zeichne ein Diagramm.
  • Atemübungsspiel: Zeig auf dem Bildschirm, wie sich dein Puls bei langsamer, tiefer Atmung verändert; gewonnen hast du, wenn dein Puls unter einen Zielwert fällt.

Häufige Fehler und Lösungen

SymptomMögliche UrsacheLösung
BPM immer 0 oder "--"Kein Schlag erkannt: Schwellwert zu hoch, kein Finger oder S-Pin am falschen EingangPrüfe die Welle mit dem Rohsignal-Sketch; setze den Schwellwert in die Mitte der Welle
BPM sehr hoch, etwa 150–220Rauschen oder die dikrote Inzisur wird als zweiter Schlag gezähltSchwellwert erhöhen, Finger ruhig halten, Umgebungslicht abschirmen
BPM erscheint auch ohne FingerSchwellwert unter dem Ruherauschen; Lampenflackern gelangt ins SignalSchwellwert 30–50 Stufen über den höchsten Ruhewert setzen, Sensor abdecken
Signal klebt flach bei 0 oder 1023+ und − vertauscht, Kabelbruch, S-Pin falsch angeschlossen oder Sensor defektVerkabelung mit der Tabelle prüfen; an ESP32 und Pico 3V3 verwenden
Welle vorhanden, bricht aber ständig einFinger bewegt sich oder der Druck ändert sichSensor mit Klettband befestigen, Hand auf den Tisch stützen und stillhalten
Beim KY-039 ist gar keine Welle zu sehenLED nicht über den Fototransistor gebogen oder starkes Umgebungslicht sättigt den FototransistorLED so biegen, dass sie über dem Finger liegt, Sensor im Schatten benutzen, MIN_AMP senken
"too few arguments to function 'hw_timer_t* timerBegin(uint8_t, uint16_t, bool)'" am ESP32PulseSensorPlayground wird mit Version 2.x des ESP32-Boardpakets kompiliertBoardpaket auf 3.x aktualisieren oder den ESP32-Code ohne Bibliothek aus diesem Artikel nutzen
analogWrite() funktioniert an Pin 9 oder 10 nichtDie Bibliothek nutzt am Uno Timer1Für PWM die Pins 3, 5, 6 oder 11 verwenden
Fang bei der Fehlersuche immer mit dem einfachsten Code an: Zeigt der Rohsignaltest eine saubere Welle, ist die Hardware in Ordnung und das Problem liegt am Schwellwert oder im Code; gibt es keine Welle, hilft es nichts, den Code zu ändern. Korrigiere dann zuerst die Verkabelung und den Kontakt des Sensors zum Finger.

Ein Pulssensor ist eines der seltenen Projekte, die mit einem einzigen Analogpin gleichzeitig Analogmessung, Signalfilterung, Timer-Interrupts und Echtzeitdiagramme vermitteln, und das Ergebnis siehst du an deinem eigenen Körper. Wenn du ein Einsteigerprojekt suchst, das ebenfalls Licht misst, ist der LDR-Lichtsensor ein guter nächster Schritt. In der BlueGrays-App findest du Projekte mit den Bauteilen aus diesem Artikel, etwa Lichtstärke mit einem LDR messen, den Buzzer, ESP32-Projekte mit OLED-Display und einen PWM-Atemeffekt am Pico, jeweils mit Schritt-für-Schritt-Schaltplänen und Code.

Häufige Fragen

Wie genau misst ein Pulssensor am Arduino?

Gut gehalten und bei ruhiger Haltung misst ein Pulse Sensor den Ruhepuls meist auf wenige BPM genau. Bewegung, Umgebungslicht, kalte Hände und Fingerdruck vergrößern den Fehler schnell. Das Signal des KY-039 ist viel schwächer, deshalb schwanken seine Werte stärker; keiner dieser Sensoren eignet sich für medizinische Messungen.

Was ist der Unterschied zwischen Pulse Sensor und KY-039?

Der Pulse Sensor nutzt eine grüne LED und das Reflexionsverfahren, verstärkt und filtert das Signal mit einem Operationsverstärker auf der Platine und liegt im Ruhezustand bei der halben Versorgungsspannung. Der KY-039 misst das Infrarotlicht, das durch den Finger dringt, hat keinen Verstärker, und sein Ausgang bewegt sich nur um wenige ADC-Stufen. Deshalb funktioniert der Pulse Sensor einfach mit einer fertigen Bibliothek, während der KY-039 Filterung und einen adaptiven Schwellwert braucht.

Kann ein Pulssensor die Sauerstoffsättigung (SpO2) messen?

Mit dem Pulse Sensor oder dem KY-039 nicht, denn die SpO2-Berechnung beruht auf dem Vergleich von Licht zweier Wellenlängen, Rot und Infrarot. Dafür brauchst du einen Sensor mit zwei LEDs wie den MAX30102. Auch dessen Wert ist eine unkalibrierte Schätzung und ersetzt kein medizinisches Pulsoximeter.

Warum zeigt mein Pulssensor sehr hohe oder schwankende Werte?

Die häufigste Ursache ist, dass Rauschen oder die dikrote Inzisur der Pulswelle als zweiter Schlag gezählt wird; dann liegen die BPM etwa beim Doppelten des echten Werts. Erhöhe den Schwellwert, fixiere den Finger mit einem Klettband und schirme den Sensor gegen Licht ab. Ein Blick auf die Rohwelle im Seriellen Plotter zeigt die Ursache sofort.

Funktioniert der Pulse Sensor mit ESP32 und Raspberry Pi Pico?

Ja, aber an beiden Boards solltest du den Sensor über den 3V3-Pin versorgen, weil ihre Analogeingänge nicht mehr als 3,3 V vertragen. Am ESP32 nimmst du einen ADC1-Pin wie GPIO 34, damit es keinen Konflikt mit dem WLAN gibt, am Pico GP26, GP27 oder GP28. PulseSensorPlayground braucht Version 3.x des ESP32-Boardpakets; in MicroPython schreibst du wie in diesem Artikel einen eigenen Algorithmus.

Wie hoch ist ein normaler Puls?

Bei Erwachsenen in Ruhe liegt der Puls meist zwischen 60 und 100 Schlägen pro Minute; bei regelmäßig trainierenden Menschen kann er 40–60 betragen, bei Kindern ist er höher. Der Puls steigt bei Bewegung, Stress, Koffein und Fieber ganz natürlich an. Das ist eine allgemeine Information; sprich über deine Messwerte oder Beschwerden mit einer Ärztin oder einem Arzt.

Alle Projekte ansehen
Lichtstärke messen mit LDR
#11Anfänger

Lichtstärke messen mit LDR

Willst du sehen, wie hell dein Zimmer ist, und zwar als Zahl? Der LDR, ein kleines lichtempfindliches Bauteil, misst das Umgebungslicht, und der Arduino schreibt den Wert jede halbe Sekunde auf deinen

4.6(81)
138
Messung der Lichtintensität mit dem ESP32-LDR-Modul
#113Anfänger

Messung der Lichtintensität mit dem ESP32-LDR-Modul

Echtzeit-Anzeige des ADC-Rohwerts des LDR-Moduls und des anhand von Dunkel- und Hellreferenzwerten berechneten relativen Lichtwerts in Prozent im Serial Monitor. Dieser Prozentwert ist keine Lux-Messung. Vermittelt die Kalibrierungslogik des Moduls. Sollte idealerweise vor Projekt 10 durchgeführt werden, da es zur Festlegung des Schwellenwerts dient.

4.6(36)
55
Verwendung des Summers
#13Anfänger

Verwendung des Summers

Hast du dich mal gefragt, wie eine Türklingel oder ein Alarm seinen Ton erzeugt? Der Arduino lässt einen kleinen Summer in kurzen Abständen piepen: piep, Pause, piep, Pause. Du siehst, wie Strom zu Kl

4.7(89)
161
Temperatur und Luftfeuchtigkeit mit DHT11
#55Mittel

Temperatur und Luftfeuchtigkeit mit DHT11

Wie warm ist dein Zimmer, und ist die Luft feucht oder trocken? Der DHT11-Sensor misst Temperatur und Luftfeuchtigkeit, und der Arduino schreibt die Werte auf ein zweizeiliges LCD und aktualisiert sie

4.7(47)
73
Digitales Voltmeter mit LCD-Anzeige
#56Mittel
Premium

Digitales Voltmeter mit LCD-Anzeige

Finde mit einem selbstgebauten Messgerät heraus, wie viel Spannung eine Batterie oder ein Netzteil liefert. Ein Teiler aus zwei Widerständen senkt hohe Spannungen auf ein Niveau, das die Platine lesen

4.5(27)
36
ESP32 HTTPClient + Wetter-API (mit SSD1306-OLED-Display)
#134Fortgeschritten
Premium

ESP32 HTTPClient + Wetter-API (mit SSD1306-OLED-Display)

In diesem Projekt führen wir mit dem ESP32 über das Internet mithilfe von HTTPClient.h einen REST-API-Aufruf an die Wetter-API (wttr.in) durch. Im Gegensatz zur vorherigen I2C-LCD-Version verwenden wir hier ein SSD1306-OLED-Display (128×64) – dies ermöglicht große Schriftarten, Vektor-Symbole und eine reichhaltige visuelle Darstellung auf einem einzigen Bildschirm. Für die Wetteranzeige werden 7 verschiedene Vektor-Symbole (sonnig, teilweise bewölkt, bewölkt, regnerisch, schneereich, stürmisch

4.3(10)
19
ESP32 Deep Sleep – Batteriesparmodus (OLED + RTC-RAM-Zähler)
#135Fortgeschritten
Premium

ESP32 Deep Sleep – Batteriesparmodus (OLED + RTC-RAM-Zähler)

Der Deep-Sleep-Modus des ESP32 senkt den Stromverbrauch auf etwa 10 µA, indem er die CPU und die meisten Peripheriegeräte abschaltet, und ist für batteriebetriebene IoT-Geräte von entscheidender Bedeutung. In diesem Projekt verwenden wir drei verschiedene Weckquellen: den Timer (15 s), einen externen Pin (EXT0 – Taste GPIO 33) und die Berührungserkennung (T0/GPIO 4). Mit RTC_DATA_ATTR markierte Variablen bleiben während des Deep-Sleep-Modus erhalten – der Boot-Zähler, der Timer-Wake-Zähler und

4.3(10)
16
Raspberry Pi Pico PWM LED Fade (Atem-Effekt)
#160Anfänger

Raspberry Pi Pico PWM LED Fade (Atem-Effekt)

Die LED blinkt nicht mehr nur — sie atmet! Mit der PWM (Pulsweitenmodulation) des Raspberry Pi Pico steigt die Helligkeit langsam an und fällt wieder ab. Lerne die machine.PWM-Klasse, duty_u16() (16-Bit-Helligkeit 0–65535), PWM-Frequenz, range()-Schleifen und analogähnliche Ausgabe. Grundlage für RGB-Farbmischung, Motorsteuerung, Servos und Buzzer.

0.0(0)
4