Arduino Nabız Sensörü: Pulse Sensor ve KY-039 ile BPM Ölçme
Arduino nabız sensörü nasıl çalışır? Pulse Sensor ve KY-039 bağlantısı, PulseSensorPlayground ile BPM, Seri Çizici'de nabız dalgası, OLED, ESP32 ve Pico kodu.
Arduino nabız sensörü, parmak ucundaki ya da kulak memesindeki kan akışını ışıkla izleyerek kalbinin dakikada kaç kez attığını, yani BPM (beats per minute) değerini ölçmeni sağlayan küçük bir modüldür. Hobi elektroniğinde en çok kullanılan iki model, yuvarlak kartlı Pulse Sensor ile kızılötesi LED'li KY-039'dur. İkisi de üç kabloyla bağlanır ve tek bir analog pinden okunur; ama sinyal kalitesi ve güvenilir kod yazmanın zorluğu bakımından aralarında büyük fark vardır.
Bu rehberde nabız sensörünün hangi fiziksel ilkeyle çalıştığını, Seri Çizici'de (Serial Plotter) gerçek bir nabız dalgasını nasıl göreceğini, PulseSensorPlayground kütüphanesiyle BPM hesaplamayı, KY-039'un zayıf sinyalini kütüphanesiz bir algoritmayla işlemeyi ve sonucu OLED ekranda kalp simgesiyle göstermeyi adım adım öğreneceksin. ESP32 ve Raspberry Pi Pico kodları, ölçümü bozan etkenler ve MAX30102 ile farkı da yazının içinde. Baştan önemli bir not: bu sensörler tıbbi cihaz değildir ve sağlıkla ilgili hiçbir karar için kullanılmamalıdır.

Nabız sensörü nasıl çalışır? Fotopletismografi (PPG)
Nabız sensörlerinin hepsi fotopletismografi (PPG, photoplethysmography) denen yöntemle çalışır. Kalp her kasıldığında atardamarlara bir kan dalgası gönderir; bu dalga parmak ucundaki küçük damarlara ulaştığında dokudaki kan hacmi kısa bir süre artar. Kandaki hemoglobin ışığı soğurduğu için kan hacmi arttıkça dokudan geri yansıyan ya da dokunun içinden geçen ışık biraz azalır. Sensör bir LED ile dokuyu aydınlatır, bir ışık sensörü de dönen ışığı ölçer; ışık miktarındaki bu küçük ve düzenli dalgalanma nabzın kendisidir.
İşin zor tarafı, ölçülen ışığın neredeyse tamamının sabit olmasıdır. Deri, kas, kemik ve toplardamarlardaki kan her an aynı miktarda ışık soğurur; buna sinyalin DC bileşeni denir. Kalp atışıyla değişen AC bileşeni ise toplam ışığın genellikle yalnızca yüzde 1–2'si kadardır. Yani sensörün asıl işi, büyük ve sabit bir seviyenin üzerindeki küçük bir dalgayı ayırıp büyütmektir. Bir PPG dalgasında önce hızlı bir yükseliş ve tepe (sistolik tepe), ardından yavaş bir iniş görülür; inişin ortasındaki küçük çentik ve hemen sonraki ufak tümsek (dikrotik çentik) aort kapağının kapanmasından kaynaklanır. İyi bir algoritma her kalp atışını bir kez saymalı, bu çentiği ikinci bir atış sanmamalıdır.
Işığın dokuya nasıl gönderildiği de önemlidir. Pulse Sensor yansıma yöntemini kullanır: LED ile ışık sensörü aynı yüzdedir ve deriden geri saçılan ışık ölçülür. Kullandığı yeşil ışık hemoglobin tarafından güçlü biçimde soğurulur ve derinin hemen altındaki damarlardan belirgin bir sinyal verir; akıllı saatlerin arkasında yeşil LED görmenin nedeni de budur. KY-039 ise geçirgen yöntemle çalışır: kızılötesi LED parmağın bir tarafını aydınlatır, karşıdaki fototransistör parmağın içinden geçen ışığı ölçer. Kızılötesi ışık dokuya daha derin girer, ama KY-039'da sinyali yükseltecek bir devre olmadığı için ortaya çıkan dalga çok küçüktür.
| Özellik | Pulse Sensor değeri |
|---|---|
| Ölçüm yöntemi | Yansımalı PPG |
| Işık kaynağı | Yeşil LED |
| Işık sensörü | APDS-9008, en duyarlı olduğu dalga boyu 565 nm |
| Besleme | 3–5 V |
| Akım | Yaklaşık 4 mA (5 V'ta) |
| Çıkış | Analog; boştayken beslemenin yarısı (5 V'ta ~2,5 V, Uno'da ~512) |
| Kart çapı | Yaklaşık 16 mm (0,625 inç) |
| Kablolar | Orijinalde kırmızı +, siyah GND, mor sinyal; klonlarda renkler değişebilir |
Pulse Sensor, KY-039 ve MAX30102 farkı

| Özellik | Pulse Sensor | KY-039 | MAX30102 |
|---|---|---|---|
| Yöntem | Yansımalı PPG | Geçirgen PPG (parmak arada) | Yansımalı PPG |
| Işık | Yeşil LED | Kızılötesi LED | Kırmızı (660 nm) + kızılötesi (880 nm) |
| Yükseltme ve filtre | Var (op-amp) | Yok | Dahili 18 bit ADC, ortam ışığı bastırma |
| Çıkış | Analog | Analog (çok zayıf) | Dijital, I2C (adres 0x57) |
| Besleme | 3–5 V | 3,3–5 V | Modüle göre 3,3–5 V |
| Sinyal kalitesi | İyi | Zayıf, gürültüye açık | Çok iyi |
| SpO2 (oksijen) | Yok | Yok | Var (algoritmayla) |
| Kütüphane | PulseSensor Playground | Hazır kütüphane yok | SparkFun MAX3010x |
Yeni başlıyorsan ve yalnızca nabız ölçmek istiyorsan Pulse Sensor en kolay seçimdir: sinyal kartın üzerinde yükseltilip süzüldüğü için Seri Çizici'de ilk denemede net bir dalga görürsün. Kartın arkasındaki op-amp bu küçük değişimi Arduino'nun okuyabileceği seviyeye yükseltir; böyle bir kazancın nasıl hesaplandığını Op-amp aracında deneyebilirsin. KY-039 ucuzdur ve sensör setlerinin çoğunda bulunur, ama çıkışı yalnızca birkaç ADC adımı oynar; bu yüzden sabit eşikli basit kodlarla güvenilir sonuç vermez. Kandaki oksijen doygunluğunu (SpO2) da ölçmek istiyorsan doğru seçim MAX30102'dir; ona aşağıda ayrı bir bölümde değiniyoruz.
Nabız sensörü bağlantısı: Arduino Uno, ESP32 ve Pico
| Sensör pini | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| + (VCC, kırmızı) | 5V | 3V3 | 3V3(OUT), pin 36 |
| − (GND, siyah) | GND | GND | GND, pin 38 |
| S (sinyal, mor) | A0 | GPIO 34 (ADC1) | GP26 (ADC0), pin 31 |
| Atış LED'i (isteğe bağlı) | D13 (dahili LED) | GPIO 2 (dahili LED) | Kart üzerindeki LED |
KY-039'da da aynı tablo geçerlidir: S pini analog girişe, + pini beslemeye, − pini GND'ye gider. Kızılötesi LED'in fototransistörün üzerine doğru bükülü olduğundan ve parmağının ikisinin arasına sığdığından emin ol. Pulse Sensor'ün arka yüzündeki elemanlar açıktadır; parmağın ya da terli bir yüzeyin arkaya değmesi gürültü yaratır. Bu yüzden ön yüze setle gelen şeffaf etiketi yapıştırıp arka yüzü sıcak silikonla kaplamak iyi bir alışkanlıktır. Harici bir LED kullanacaksan direncini LED direnci aracıyla hesapla; kartının pin haritasını da Pinout aracında görebilirsin.
İlk test: ham nabız dalgasını Seri Çizici'de görmek
BPM hesaplamaya geçmeden önce sensörün gerçekten nabız gördüğünü doğrula. Aşağıdaki kod hiçbir kütüphane kullanmaz; sinyali saniyede 100 kez okuyup Seri Çizici'ye gönderir. Kodu yükle, Arduino IDE'de Araçlar > Seri Çizici'yi aç ve hızı 115200 baud yap. Seri Çizici'yi daha önce kullanmadıysan Arduino Seri Monitör yazısı pencerenin nasıl çalıştığını anlatır.
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}Parmak yokken Pulse Sensor'ün çizgisi 512 civarında, biraz titreyerek düz durur; bu beslemenin yarısıdır ve sensörün doğru bağlandığını gösterir. Parmak ucunu sensöre hafifçe koyduğunda birkaç saniye içinde her kalp atışında hızla yükselip yavaşça inen bir dalga belirir; genliği genellikle 100–300 adım arasındadır. Dalgayı görmüyorsan BPM kodlarına geçme: önce bağlantıyı, beslemeyi ve parmak basıncını düzelt. Tepe noktaları 0'a ya da 1023'e yapışmış düz bir çizgiye dönüşüyorsa sinyal kırpılıyordur; genellikle parmak hareket ediyor ya da sensör fazla sıkıştırılıyordur. Analog okumanın 0–1023 aralığının nereden geldiğini merak ediyorsan ADC ve analog-dijital dönüşüm yazısına bakabilirsin.
PulseSensorPlayground ile BPM ölçme
- Arduino IDE'de Kütüphane Yöneticisi'ni aç, PulseSensor Playground yazıp kütüphaneyi kur. Kütüphane kurmayla ilgili ayrıntılar için Arduino'ya kütüphane ekleme yazısına bakabilirsin.
- Sensörü tablodaki gibi bağla: + pinini 5V'a, − pinini GND'ye, S pinini A0'a.
- Aşağıdaki kodu yükle ve Seri Çizici'yi 115200 baud ile aç; ilk çizgi ham sinyali, ikinci çizgi BPM değerini gösterir.
- Parmağını sensöre koy, elini masaya dayayıp 10 saniye kıpırdamadan bekle; kütüphane ilk birkaç atışta ortalamayı oturtur.
- Sayıları Seri Monitör'de satır satır görmek istersen koddaki PLOTTER sabitini false yap; her atışta BPM ve atışlar arası süre (IBI) yazdırılır.
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}| Fonksiyon | Görevi |
|---|---|
| analogInput(pin) | Sensörün bağlı olduğu analog pini seçer (varsayılan A0) |
| blinkOnPulse(pin) | Sinyal eşiğin üzerindeyken, yani her atış sırasında bu pindeki LED'i yakar |
| fadeOnPulse(pin) | PWM pinindeki LED'i her atışta yakıp yavaşça söndürür |
| setThreshold(değer) | Başlangıç eşiği; atış bulunamayınca eşik bu değere döner |
| begin() | Örneklemeyi başlatır; başarılıysa true döndürür |
| sawStartOfBeat() | Son çağrıdan beri yeni bir atış başladıysa true döndürür |
| getBeatsPerMinute() | Son 10 atış aralığının ortalamasından hesaplanan BPM |
| getInterBeatIntervalMs() | Son iki atış arasındaki süre (IBI), milisaniye |
| getLatestSample() | Son okunan ham değer (0–1023) |
| isInsideBeat() | Sinyal eşiğin üzerindeyken true döndürür |
Kütüphane sensörü arka planda, bir zamanlayıcı kesmesiyle her 2 ms'de bir (saniyede 500 kez) okur; bu yüzden loop() içinde uzun işler yapsan bile örnekleme aksamaz. Her atışta dalganın tepe ve çukur değerlerini kaydeder ve eşiği bu ikisinin tam ortasına taşır; yani setThreshold() ile verdiğin değer yalnızca bir başlangıç noktasıdır. Bir önceki atıştan sonraki 250 ms içinde ya da son atış aralığının beşte üçü dolmadan gelen yükselişleri dikkate almaz; böylece dikrotik çentik ikinci bir atış sayılmaz. BPM, son 10 atış aralığının ortalamasından hesaplanır; 2,5 saniye boyunca atış bulunamazsa her şey sıfırlanır ve getBeatsPerMinute() 0 döndürür.
Başlangıç eşiğini ham sinyale bakarak seç: parmak yokken çizginin çıktığı en yüksek değerin 30–50 adım üstü iyi bir başlangıçtır (çoğu sensörde 530–600 arası). Eşik çok düşükse parmak yokken bile gürültü atış sayılır, çok yüksekse zayıf nabızlar kaçar. Bir uyarı daha: Arduino Uno'da kütüphane örnekleme için Timer1'i kullanır, bu yüzden 9 ve 10 numaralı pinlerde analogWrite() çalışmaz. Projene Servo kütüphanesini eklersen kütüphane Timer2'ye geçer; bu kez 3 ve 11 numaralı pinlerdeki PWM ile tone() fonksiyonu etkilenir.
Doğru ölçüm için: parmak basıncı, ortam ışığı ve hareket
Nabız ölçümünü bozan şeylerin çoğu kodla değil, sensörün nasıl tutulduğuyla ilgilidir. Seri Çizici açıkken aşağıdakileri tek tek dene; her birinin dalgayı nasıl değiştirdiğini görmek sensörü anlamanın en hızlı yoludur.
- Parmak basıncı: Hafif ama sabit bastır. Çok hafif dokunursan parmağın her kıpırtısı dalgadan büyük bir gürültü yaratır; çok sert bastırırsan kılcal damarları sıkıştırırsın, kan akışı azalır ve dalga basıklaşır, hatta tamamen kaybolur. En iyi sonuç, sensör cırt cırtlı bir bantla parmağa sarıldığında alınır.
- Ortam ışığı: Güneş ışığı ve lambalar sensöre yandan sızar. Türkiye'de şebeke 50 Hz olduğu için birçok lamba saniyede 100 kez titrer ve bu titreşim sinyale karışır. Sensörün çevresini parmağınla tamamen kapat ya da üstüne koyu renkli bir bant sar.
- Hareket: Kablonun sallanması, konuşmak, parmağı oynatmak ya da yürümek nabızdan çok daha büyük sinyaller üretir. Ölçüm sırasında elini masaya dayayıp kıpırdamadan dur.
- Soğuk eller: Üşüyen parmakta damarlar daralır ve sinyal zayıflar. Ellerini ısıt ya da sensörü kulak memesine tak; kulak memesi hareketten de daha az etkilenir.
- Ter ve yağ: Nemli deri ışığı farklı saçar ve sensörün yüzeyini kirletir. Ön yüzdeki şeffaf koruyucu etiketi kullan ve sensörü ara sıra kuru bir bezle sil.
- Oturma süresi: Parmağını koyduktan sonraki ilk 5–10 saniyede sinyal oturur ve ortalama dolar; bu sürede gördüğün değerleri dikkate alma.
OLED ekranda nabız göstergesi
Seri Çizici masada iş görür, ama taşınabilir bir nabız ölçer için küçük bir ekran gerekir. Aşağıdaki kod 0,96 inçlik SSD1306 OLED ekranın üst kısmına büyük rakamlarla BPM değerini ve her atışta büyüyen bir kalp simgesini, alt kısmına da soldan sağa taranan canlı bir nabız dalgasını çizer. OLED'i Uno'nun A4 (SDA) ve A5 (SCL) pinlerine, beslemesini 5V ve GND'ye bağla; ekran kütüphanesinin kurulumu ve adres ayarı OLED Ekran (SSD1306) Kullanımı yazısında ayrıntılı anlatılıyor.
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}Kod ekranı saniyede 25 kez yeniler ve her karede dalgaya bir nokta ekler; böylece ekran genişliği yaklaşık 5 saniyelik bir nabız kaydını gösterir. Dalga her taramanın sonunda o taramadaki en küçük ve en büyük değere göre yeniden ölçeklenir, bu sayede zayıf da olsa güçlü de olsa sinyal ekrana sığar. Bir kareyi I2C üzerinden ekrana göndermek yaklaşık 25–30 ms sürer, ama kütüphane örneklemeyi kesmeyle yaptığı için bu sırada hiçbir atış kaçmaz. Uno'da ekranın görüntü tamponu 1 KB RAM kaplar; kod dalgayı bir dizide saklamadan doğrudan ekrana çizdiği için bellek yeter, ama geriye yalnızca 450 bayt kadar boş yer kalır.
KY-039 ile nabız ölçme
KY-039'da fototransistör bir dirençle seri bağlıdır ve çıkış gerilimi gelen ışığa göre değişir: ışık arttıkça gerilim düşer, azaldıkça yükselir. Arada ne yükseltici ne de filtre vardır. Parmak yerleştirildiğinde kalp atışı bu gerilimi çoğu zaman yalnızca 2–10 ADC adımı, yani yaklaşık 10–50 mV oynatır; ortam ışığındaki küçük bir değişim bile bundan büyüktür. Bu yüzden Pulse Sensor için yazılmış kodlar ve 550 gibi sabit bir eşik KY-039'da işe yaramaz; sinyali önce temizlemek, sonra dalganın kendi boyuna göre ayarlanan bir eşik kullanmak gerekir.
Aşağıdaki kod bunu dört adımda yapar. Önce analog pini 20 ms boyunca durmadan okuyup (yaklaşık 175 okuma) ortalamasını alır; bu hem rastgele gürültüyü azaltır hem de 20 ms tam olarak 50 Hz şebekenin bir periyodu olduğu için lambaların titreşimini neredeyse tamamen yok eder. Sonra hafif bir alçak geçiren süzgeçle sinyali yumuşatır ve çok yavaş hareket eden bir ortalamayla DC seviyeyi çıkarır; geriye yalnızca nabız dalgası kalır. Üçüncü adımda dalganın tepelerini ve çukurlarını izleyen iki zarf hesaplanır ve eşik ikisinin ortasına konur. Son olarak sinyal eşiği yukarı doğru kestiğinde bir atış sayılır ve son 8 atış aralığının ortalamasından BPM bulunur.
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}| Sabit / değer | Görevi |
|---|---|
| WINDOW_US = 20000 | Ortalama penceresi; 50 Hz şebeke titreşimini siler (60 Hz ülkelerde 16667 yap) |
| 0.5 (smooth) | Hafif alçak geçiren süzgeç; ani sıçramaları yumuşatır |
| 0.05 (baseline) | DC seviyeyi yaklaşık 0,4 saniyede izler; parmak basıncındaki yavaş değişimleri çıkarır |
| 0.99 (zarflar) | Tepe ve çukur zarflarının bırakılma hızı (yaklaşık 2 saniye) |
| MIN_AMP = 1.2 | Bu tepe-tepe genliğin altındaki dalga gürültü sayılır |
| refractory | Son ortalama aralığın %70'i (250–600 ms; geçmiş yokken 350 ms, böylece hızlı nabız da kaçmaz); dikrotik çentiğin ikinci atış sayılmasını önler |
| mid - amp * 0.25 | Yeniden kurma seviyesi; dalga yeterince inmeden yeni atış aranmaz |
Kodu yükleyip Seri Çizici'yi açtığında iki çizgi görürsün: Nabiz temizlenmiş dalgayı, Esik ise onun ortasından geçen uyarlanır eşiği gösterir. Parmak yokken Nabiz çizgisinin ne kadar oynadığına bak ve MIN_AMP'i bu oynamanın tepe-tepe değerinin biraz üstüne ayarla; sinyalin çok zayıfsa değeri düşür. Sinyal süzme teknikleri hakkında daha fazlası için sensör verisi filtreleme yazısına bakabilirsin. Aynı kod Pulse Sensor ile de çalışır; o durumda MIN_AMP'i 20 civarına çıkarman yeterlidir.
ESP32 ile nabız sensörü
ESP32'de sensörü 3V3 pininden besle ve sinyali GPIO 34'e bağla. Bu pin ADC1 grubundadır; ADC2 pinleri Wi-Fi açıkken okunamadığı için nabız verisini internete göndereceksen ADC1 pinlerinden (GPIO 32–39) birini seçmen şarttır. ESP32'nin ADC'si 12 bittir (0–4095) ve 3,3 V beslemeyle Pulse Sensor boştayken 2000 civarında bir değer verir.
PulseSensorPlayground'ın güncel sürümleri ESP32'yi de destekler, ama kütüphane ESP32 kart paketinin 3.x sürümündeki zamanlayıcı fonksiyonlarını kullanır ve 2.x sürümüyle derlenmez. Örneklerinde analogReadResolution(10) ile ADC'yi 10 bite indirir, böylece eşik Uno'daki gibi 0–1023 ölçeğinde verilir (ESP32 örneğinde 685). Sürüm farklarından etkilenmemek için aşağıda kütüphanesiz, KY-039 bölümündeki algoritmanın aynısını kullanan bir kod var; kart paketinin her iki sürümünde de çalışır.
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}Bu kodda sinyal saniyede 100 kez okunur ve ESP32'nin ADC'si gürültülü olduğu için her örnek 8 okumanın ortalamasıdır. Eşik ve zarf değerleri 12 bitlik ölçeğe göre ayarlandığından MIN_AMP 80'dir (yaklaşık 65 mV). ESP32'nin asıl gücü Wi-Fi'dir: BPM değerini WebSocket ile bir web sayfasına canlı gönderebilir, nabız belli bir sınırı aşınca Telegram'dan mesaj atabilir ya da ölçümlerini Google Sheets'e kaydedebilirsin.
Raspberry Pi Pico ve MicroPython ile nabız ölçme
Raspberry Pi Pico'da sensörün + pinini 3V3(OUT) (pin 36), − pinini GND (pin 38), S pinini GP26 (ADC0, pin 31) pinine bağla. MicroPython'da read_u16() değeri 0–65535 aralığında döndürür; kod bunu 4 bit sağa kaydırarak ESP32 ile aynı 0–4095 ölçeğine indirir, böylece eşik değerleri iki kartta da aynı kalır. Kodu Thonny'de main.py adıyla Pico'ya kaydedersen kart her açıldığında kendiliğinden çalışır.
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")Kod ESP32'dekiyle aynı mantıkla çalışır: her 10 ms'de bir örnek alır, DC seviyeyi çıkarır, uyarlanır eşikle atışı bulur ve son 8 aralığın ortalamasından BPM hesaplar. Atış anında kartın üzerindeki LED yanar, Thonny'nin kabuk (Shell) penceresine de BPM ile atışlar arası süre yazılır. Python sözdizimine yabancıysan MicroPython temelleri yazısı iyi bir başlangıç olur.
MAX30102 ile farkı: kandaki oksijen (SpO2) nasıl ölçülür?
MAX30102, Analog Devices'ın (eski adıyla Maxim Integrated) nabız ve kandaki oksijen doygunluğu ölçümü için geliştirdiği tek yongalı bir sensördür. İçinde kırmızı (660 nm) ve kızılötesi (880 nm) iki LED, bir fotodiyot, ortam ışığını bastıran bir devre ve 18 bitlik bir ADC bulunur. Ölçümü kendisi yapar, örnekleri 32 değerlik bir bellekte (FIFO) biriktirir ve I2C hattından (adres 0x57) sayı olarak gönderir. Analog kabloların gürültü toplaması ve Arduino'nun 10 bitlik ADC sınırı ortadan kalktığı için sinyali Pulse Sensor'ünkinden çok daha temizdir.
SpO2 ölçümünün sırrı iki farklı renkte ışık kullanmaktır. Oksijen taşıyan hemoglobin kızılötesi ışığı daha çok, kırmızı ışığı daha az soğurur; oksijenini bırakmış hemoglobinde durum tersinedir. Sensör her iki ışık için nabız dalgasının AC ve DC bileşenlerini ölçer ve R = (AC_kırmızı / DC_kırmızı) / (AC_kızılötesi / DC_kızılötesi) oranını hesaplar; SpO2 bu orandan deneysel bir kalibrasyon eğrisiyle bulunur. Tek renk ışık kullanan Pulse Sensor ve KY-039 bu oranı hesaplayamaz, yani bu sensörlerle oksijen ölçülemez. Arduino'da MAX30102 için en yaygın kütüphane SparkFun MAX3010x'tir; Example5_HeartRate örneği nabzı, Example8_SPO2 örneği oksijen doygunluğunu hesaplar.
Nabız sensörü tıbbi cihaz değildir
Bunun birkaç somut nedeni var. Tıbbi nabız ve oksijen ölçerler belirli doğruluk sınırlarına göre kalibre edilir; farklı ten renklerinde, düşük kan akışında ve hareket halinde test edilir. Hobi modülleri ve bizim yazdığımız algoritmalar bu testlerden geçmez. Ayrıca nabız ölçmek kalp ritmini değerlendirmek demek değildir: ritim bozukluklarını anlamak için kalbin elektriksel sinyalini kaydeden EKG gerekir. Genel bir bilgi olarak, dinlenme halindeki bir yetişkinde nabız çoğunlukla dakikada 60–100 arasındadır; düzenli spor yapan kişilerde 40–60 arasına inebilir. Kendi ölçümün bu aralığın dışında kalıyorsa önce sensörü ve kodu kontrol et; endişen sürüyorsa bir doktora danış.
Nabız sensörü ile proje fikirleri
- Kalp atışıyla yanan ışık: Her atışta bir WS2812B LED halkasını kırmızı yakıp yavaşça söndür; BPM arttıkça rengi maviden kırmızıya kaydır.
- Bip sesli nabız ölçer: sawStartOfBeat() her true döndürdüğünde bir buzzer ile kısa bir bip çal; hastane monitörlerinin sesini taklit eden eğlenceli bir proje.
- Taşınabilir nabız ölçer: OLED kodunu bir pil ve şarj devresiyle birlikte bir kutuya yerleştir; ölçüm bitince ekranı kapatarak pil ömrünü uzat.
- Spor sonrası toparlanma testi: Egzersiz biter bitmez ölç ve ilk 1 dakikada nabzının kaç atım düştüğünü hesapla; değeri her gün kaydedip gelişimini izle.
- Nabız kaydedici: BPM değerlerini zaman bilgisiyle bir SD karta yaz ya da Excel'e aktarıp grafiğini çiz.
- Nefes egzersizi oyunu: Yavaş ve derin nefes alırken nabzının nasıl değiştiğini ekranda göster; nabzını belirlediğin bir değerin altına indirince oyunu kazan.
Sık yapılan hatalar ve çözümleri
| Belirti | Olası sebep | Çözüm |
|---|---|---|
| BPM hep 0 ya da "--" | Atış algılanmıyor: eşik çok yüksek, parmak yok ya da S pini yanlış girişte | Ham sinyal koduyla dalgayı kontrol et; eşiği dalganın ortasına çek |
| BPM 150–220 gibi çok yüksek | Gürültü ya da dikrotik çentik ikinci atış sayılıyor | Eşiği yükselt, parmağı sabitle, ortam ışığını kes |
| Parmak yokken de BPM görünüyor | Eşik boştaki gürültünün altında; lamba titreşimi sinyale karışıyor | Eşiği boştaki en yüksek değerin 30–50 adım üstüne al, sensörün üstünü kapat |
| Sinyal 0'da ya da 1023'te düz duruyor | + ve − ters, kablo kopuk, S pini yanlış yerde ya da sensör bozuk | Kablolamayı tablodan kontrol et; ESP32 ve Pico'da 3V3 kullan |
| Dalga var ama sürekli bozuluyor | Parmak hareket ediyor ya da basınç değişiyor | Sensörü cırt cırtlı bantla sar, elini masaya dayayıp kıpırdama |
| KY-039'da dalga hiç görünmüyor | LED fototransistörün üzerine bükülmemiş ya da güçlü ortam ışığı fototransistörü doyuruyor | LED'i parmağın üstüne gelecek şekilde bük, sensörü gölgede kullan, MIN_AMP'i düşür |
| ESP32'de "too few arguments to function 'hw_timer_t* timerBegin(uint8_t, uint16_t, bool)'" | PulseSensorPlayground, ESP32 kart paketinin 2.x sürümüyle derleniyor | Kart paketini 3.x'e güncelle ya da yazıdaki kütüphanesiz ESP32 kodunu kullan |
| 9 ya da 10 numaralı pinde analogWrite() çalışmıyor | Kütüphane Uno'da Timer1'i kullanıyor | PWM için 3, 5, 6 ya da 11 numaralı pinleri kullan |
Nabız sensörü; analog okuma, sinyal süzme, zamanlayıcı kesmeleri ve gerçek zamanlı grafik çizme gibi birçok konuyu tek bir analog pinle aynı anda öğreten ve sonucunu kendi vücudunda gördüğün nadir projelerden biridir. Benzer şekilde ışık ölçen bir başlangıç projesi istersen LDR ışık sensörü iyi bir sonraki adım olur. BlueGrays uygulamasında LDR ile ışık şiddeti okuma, buzzer, OLED ekranlı ESP32 projeleri ve Pico ile PWM nefes efekti gibi bu yazıdaki parçaları kullanan projeleri adım adım devre şemaları ve kodlarıyla bulabilirsin.
Sıkça sorulan sorular
Arduino nabız sensörü ne kadar doğru ölçer?
Pulse Sensor, iyi tutulduğunda ve kişi hareketsizken dinlenme nabzını genellikle birkaç BPM farkla ölçer. Hareket, ortam ışığı, soğuk eller ve parmak basıncı hatayı hızla büyütür. KY-039'un sinyali çok daha zayıf olduğu için sonuçları daha oynaktır; bu sensörlerin hiçbiri tıbbi ölçüm için uygun değildir.
Pulse Sensor ile KY-039 arasındaki fark nedir?
Pulse Sensor yeşil LED ve yansıma yöntemi kullanır, sinyali kartın üzerindeki op-amp ile yükseltip süzer ve boştayken beslemenin yarısında durur. KY-039 ise kızılötesi LED ile parmağın içinden geçen ışığı ölçer, kartında yükseltici yoktur ve çıkışı yalnızca birkaç ADC adımı oynar. Bu yüzden Pulse Sensor hazır kütüphaneyle kolayca çalışır, KY-039 ise süzme ve uyarlanır eşik gerektirir.
Nabız sensörü ile kandaki oksijen (SpO2) ölçülebilir mi?
Pulse Sensor ve KY-039 ile ölçülemez, çünkü SpO2 hesabı kırmızı ve kızılötesi olmak üzere iki farklı dalga boyundaki ışığın karşılaştırılmasına dayanır. Bunun için MAX30102 gibi iki LED'li bir sensör gerekir. MAX30102'nin verdiği değer de kalibre edilmemiş bir tahmindir ve tıbbi oksimetrenin yerini tutmaz.
Nabız sensörüm neden çok yüksek ya da değişken değer gösteriyor?
En sık sebep, gürültünün ya da nabız dalgasındaki dikrotik çentiğin ikinci bir atış sayılmasıdır; bu durumda BPM gerçek değerin yaklaşık iki katı çıkar. Eşiği yükselt, parmağını cırt cırtlı bantla sabitle ve sensörün çevresini ışığa karşı kapat. Seri Çizici'de ham dalgaya bakmak sorunun kaynağını hemen gösterir.
Pulse Sensor ESP32 ve Raspberry Pi Pico ile çalışır mı?
Evet, ama iki kartta da sensörü 3V3 pininden beslemelisin, çünkü analog girişleri 3,3 V'tan yüksek gerilime dayanmaz. ESP32'de Wi-Fi ile çakışmaması için GPIO 34 gibi bir ADC1 pini, Pico'da GP26, GP27 ya da GP28 kullanılır. PulseSensorPlayground ESP32'de kart paketinin 3.x sürümünü ister; MicroPython'da ise bu yazıdaki gibi kendi algoritmanı yazman gerekir.
Normal nabız kaç olmalı?
Dinlenme halindeki yetişkinlerde nabız çoğunlukla dakikada 60–100 arasındadır; düzenli spor yapanlarda 40–60 arasında olabilir, çocuklarda ise daha yüksektir. Nabız egzersiz, stres, kafein ve ateşle doğal olarak artar. Bu bilgi genel amaçlıdır; ölçüm sonuçların ya da şikâyetlerin için bir doktora danış.
Bu yazıdan sonra yapabileceğin projeler
Tüm projeleri gör
Ldr İle Işık Şiddeti Okuma
Odanın ne kadar aydınlık olduğunu sayıyla görmek ister misin? Işığa duyarlı küçük bir parça olan LDR ortam ışığını ölçer ve Arduino bu değeri yarım saniyede bir bilgisayar ekranına yazar. Elinle üstün

ESP32 LDR Modülü ile Işık Şiddeti Okuma
LDR modülünün ham ADC değerini ve karanlık/aydınlık referanslarına göre hesaplanan göreli ışık yüzdesini Serial Monitor'da gerçek zamanlı izleme. Bu yüzde lux ölçümü değildir. Modülün kalibrasyon mantığını öğretir. Proje 10'un öncesinde yapılması ideal; çünkü eşik değerini belirlemek için kullanılır.

Buzzer Kullanımı
Kapı zili ya da alarm sesi nasıl çıkar, hiç merak ettin mi? Arduino küçük bir buzzer'ı kısa aralıklarla öttürür: bip, sus, bip, sus. Sesin elektrikle nasıl üretildiğini görür, ileride melodi çalan pro

DHT11 İle Sıcaklık Ve Nem
Odan kaç derece, hava nemli mi, kuru mu? DHT11 sensörü sıcaklığı ve nemi ölçer, Arduino da bu değerleri iki satırlık LCD ekrana yazar ve birkaç saniyede bir yeniler. Sensöre bir şey olursa ekranda hat

LCD Ekran ile Dijital Voltmetre
Pilin ya da adaptörün ne kadar gerilim verdiğini kendi yaptığın ölçü aletiyle öğren. İki dirençten oluşan bir bölücü yüksek gerilimi kartın okuyabileceği seviyeye indirir, Arduino da gerçek değeri hes

ESP32 HTTPClient + Hava Durumu API (SSD1306 OLED Ekranlı)
Bu projede ESP32 ile internet üzerinden hava durumu API'sine (wttr.in) HTTPClient.h kullanarak REST API çağrısı yapıyoruz. Bir önceki I2C LCD versiyonundan farklı olarak burada SSD1306 OLED (128×64) ekran kullanıyoruz — büyük font, vector ikonlar ve tek ekranda zengin görsel sunum mümkün oluyor. Hava durumu için 7 farklı vector ikon (güneşli, parçalı bulutlu, bulutlu, yağmurlu, karlı, fırtınalı, sisli) runtime'da çiziliyor (bitmap dosyası gerektirmez). Sıcaklık 3x büyük fontla ortada, hava dur

ESP32 Deep Sleep — Pil Tasarrufu (OLED + RTC RAM Sayaç)
ESP32'nin Deep Sleep modu, CPU ve çoğu çevre birimini kapatarak akım tüketimini ~10µA seviyesine düşürür ve pille çalışan IoT cihazları için kritik öneme sahiptir. Bu projede üç farklı uyandırma kaynağını kullanıyoruz: zamanlayıcı (timer, 15sn), harici pin (EXT0 — buton GPIO 33) ve touch (T0/GPIO 4). RTC_DATA_ATTR ile işaretlenen değişkenler deep sleep boyunca korunur — boot sayacı, timer wake sayacı ve buton wake sayacı kalıcı olarak izlenir. SSD1306 OLED ekranda her uyanışta wake reason, say

Raspberry Pi Pico PWM ile LED Fade (Nefes Efekti)
LED artık sadece yanıp sönmüyor — nefes alıyor! Raspberry Pi Pico'nun PWM (Pulse Width Modulation — darbe genişlik modülasyonu) özelliğiyle LED'in parlaklığı yavaş yavaş artar, sonra yavaş yavaş azalır. Uyku modundaki MacBook ışığı gibi sakin bir "nefes" efekti. Öğrenme: machine.PWM sınıfı (pini PWM çıkışına çevirme), duty_u16() (0–65535 arası 16-bit parlaklık değeri), PWM frekansı (freq(1000) — saniyede 1000 kez aç-kapa, göz titremeyi görmez), range() ile adım adım döngü
Diğer yazılar
BlueGrays uygulaması
Bu devreyi telefonunda adım adım kur
Devre şeması, kod ve parça listesi tek ekranda. 200'den fazla Arduino, ESP32 ve Pico projesi seni bekliyor.
Telefonunla tara
Kamerayı koda tut; telefonunun uygulama mağazası açılır.
ya da mağazada aç

