Componenti · 18 min di lettura

Sensore battito cardiaco Arduino: Pulse Sensor e KY-039 (BPM)

Sensore battito cardiaco Arduino: collegare Pulse Sensor e KY-039, BPM con PulseSensorPlayground, onda nel Plotter seriale, display OLED, codice ESP32 e Pico.

Un sensore di battito cardiaco per Arduino è un piccolo modulo che segue con la luce il flusso di sangue nel polpastrello o nel lobo dell'orecchio e misura quante volte il cuore batte al minuto, cioè i BPM (battiti al minuto). I due modelli più diffusi nell'elettronica hobbistica sono il Pulse Sensor con scheda rotonda e il KY-039 con LED infrarosso. Entrambi si collegano con tre fili e si leggono da un solo pin analogico, ma sono molto diversi per qualità del segnale e per la difficoltà di scrivere un codice affidabile.

In questa guida imparerai passo passo su quale principio fisico si basa un sensore di battito, come vedere una vera onda del polso nel Plotter seriale, come calcolare i BPM con la libreria PulseSensorPlayground, come elaborare il debole segnale del KY-039 con un algoritmo senza librerie e come mostrare il risultato su un display OLED con un'icona a forma di cuore. Troverai anche il codice per ESP32 e Raspberry Pi Pico, i fattori che falsano la misura e la differenza con il MAX30102. Una nota importante fin da subito: questi sensori non sono dispositivi medici e non vanno usati per nessuna decisione che riguardi la salute.

Sensore di battito Pulse Sensor con cavo di collegamento
Modulo Pulse Sensor: una scheda rotonda di circa 16 mm di diametro che riunisce un LED verde, un sensore di luce e il circuito che amplifica il segnale; viene fornito con un cavo a 3 pin.

Come funziona un sensore di battito? La fotopletismografia (PPG)

Tutti i sensori di battito funzionano con un metodo chiamato fotopletismografia (PPG). Ogni volta che il cuore si contrae invia un'onda di sangue nelle arterie; quando quest'onda raggiunge i piccoli vasi del polpastrello, il volume di sangue nel tessuto aumenta per un attimo. Poiché l'emoglobina del sangue assorbe la luce, più sangue c'è, meno luce viene riflessa dal tessuto o lo attraversa. Il sensore illumina il tessuto con un LED e un sensore di luce misura la luce che ritorna; questa piccola oscillazione regolare della quantità di luce è il polso stesso.

La parte difficile è che quasi tutta la luce misurata è costante. Pelle, muscoli, ossa e sangue nelle vene assorbono sempre la stessa quantità di luce; questa si chiama componente continua (DC) del segnale. La componente alternata (AC), che cambia a ogni battito, di solito è solo l'1–2% della luce totale. Il vero lavoro del sensore è quindi separare e amplificare una piccola onda che poggia su un livello grande e stabile. Un'onda PPG mostra prima una salita rapida fino a un picco (il picco sistolico) e poi una discesa lenta; la piccola tacca a metà discesa e la piccola gobba subito dopo (l'incisura dicrota) sono dovute alla chiusura della valvola aortica. Un buon algoritmo conta ogni battito una sola volta e non scambia questa tacca per un secondo battito.

Conta anche il modo in cui la luce entra nel tessuto. Il Pulse Sensor usa il metodo a riflessione: LED e sensore di luce stanno sulla stessa faccia e misurano la luce diffusa all'indietro dalla pelle. La sua luce verde viene assorbita fortemente dall'emoglobina e dà un segnale chiaro dai vasi appena sotto la pelle; è anche il motivo per cui vedi LED verdi sul retro degli smartwatch. Il KY-039 funziona invece per trasmissione: un LED infrarosso illumina un lato del dito e un fototransistor dall'altro lato misura la luce che lo attraversa. La luce infrarossa penetra più in profondità nel tessuto, ma dato che il KY-039 non ha alcun circuito per amplificare il segnale, l'onda che ne risulta è molto piccola.

CaratteristicaValore del Pulse Sensor
Metodo di misuraPPG a riflessione
Sorgente luminosaLED verde
Sensore di luceAPDS-9008, sensibilità massima a 565 nm
Alimentazione3–5 V
AssorbimentoCirca 4 mA (a 5 V)
UscitaAnalogica; a riposo metà della tensione di alimentazione (~2,5 V a 5 V, ~512 sull'Uno)
Diametro della schedaCirca 16 mm (0,625 pollici)
FiliNell'originale rosso +, nero GND, viola segnale; nei cloni i colori possono cambiare

Pulse Sensor, KY-039 e MAX30102: le differenze

Sensore di battito KY-039 (LED infrarosso e fototransistor)
KY-039: in alto il LED infrarosso piegato sopra il fototransistor, in basso il fototransistor; il dito va in mezzo. Sulla scheda non c'è un amplificatore, quindi il segnale va direttamente al pin analogico.
CaratteristicaPulse SensorKY-039MAX30102
MetodoPPG a riflessionePPG a trasmissione (dito in mezzo)PPG a riflessione
LuceLED verdeLED infrarossoRosso (660 nm) + infrarosso (880 nm)
Amplificatore e filtroSì (amplificatore operazionale)NoADC a 18 bit integrato, soppressione della luce ambientale
UscitaAnalogicaAnalogica (molto debole)Digitale, I2C (indirizzo 0x57)
Alimentazione3–5 V3,3–5 V3,3–5 V secondo il modulo
Qualità del segnaleBuonaDebole, sensibile al rumoreOttima
SpO2 (ossigeno)NoNoSì (con un algoritmo)
LibreriaPulseSensor PlaygroundNessuna libreria prontaSparkFun MAX3010x

Se sei all'inizio e vuoi solo misurare il polso, il Pulse Sensor è la scelta più semplice: dato che il segnale viene amplificato e filtrato sulla scheda, vedrai un'onda chiara nel Plotter seriale già al primo tentativo. L'amplificatore operazionale sul retro porta questa minuscola variazione a un livello che Arduino può leggere; puoi provare come si calcola un guadagno del genere con lo strumento Amplificatore operazionale. Il KY-039 costa poco e si trova nella maggior parte dei kit di sensori, ma la sua uscita si muove solo di pochi passi dell'ADC, quindi un codice semplice a soglia fissa non dà risultati affidabili. Se vuoi misurare anche la saturazione di ossigeno nel sangue (SpO2), la scelta giusta è il MAX30102; gli dedichiamo una sezione apposita più avanti.

Collegamento del sensore di battito: Arduino Uno, ESP32 e Pico

Pin del sensoreArduino UnoESP32Raspberry Pi Pico
+ (VCC, rosso)5V3V33V3(OUT), pin 36
− (GND, nero)GNDGNDGND, pin 38
S (segnale, viola)A0GPIO 34 (ADC1)GP26 (ADC0), pin 31
LED del battito (facoltativo)D13 (LED integrato)GPIO 2 (LED integrato)LED sulla scheda
Gli ingressi analogici di ESP32 e Pico sopportano al massimo 3,3 V. Poiché l'uscita del Pulse Sensor può salire fino alla tensione di alimentazione, su queste schede alimenta il sensore dal pin 3V3, non da 5V o VBUS. Sulle schede clone l'ordine dei pin e i colori dei fili possono essere diversi; leggi sempre le scritte S, + e − sulla scheda prima di collegarla.

La stessa tabella vale per il KY-039: il pin S va all'ingresso analogico, + all'alimentazione e − a GND. Assicurati che il LED infrarosso sia piegato sopra il fototransistor e che il dito ci stia in mezzo. I componenti sul retro del Pulse Sensor sono scoperti; se un dito o una superficie sudata tocca il retro si crea rumore. Per questo è una buona abitudine incollare sul fronte l'adesivo trasparente incluso nel kit e coprire il retro con colla a caldo. Se usi un LED esterno, calcola la sua resistenza con lo strumento Resistenza per LED; la piedinatura della tua scheda la trovi nello strumento Pinout.

Primo test: vedere l'onda grezza del polso nel Plotter seriale

Prima di passare ai BPM, verifica che il sensore veda davvero il polso. Il codice seguente non usa librerie; legge il segnale 100 volte al secondo e lo invia al Plotter seriale. Carica il codice, apri Strumenti > Plotter seriale nell'Arduino IDE e imposta la velocità a 115200 baud. Se non hai mai usato il Plotter seriale, l'articolo Monitor seriale di Arduino spiega come funziona la finestra.

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}

Senza dito, la linea del Pulse Sensor resta piatta intorno a 512 con un leggero tremolio; è metà dell'alimentazione e dimostra che il sensore è collegato bene. Quando appoggi leggermente il polpastrello sul sensore, in pochi secondi compare un'onda che a ogni battito sale in fretta e scende lentamente; la sua ampiezza di solito è tra 100 e 300 passi. Se non vedi l'onda, non passare al codice dei BPM: sistema prima i collegamenti, l'alimentazione e la pressione del dito. Se i picchi diventano una linea piatta incollata a 0 o a 1023, il segnale viene tagliato; di solito il dito si muove o il sensore è premuto troppo forte. Se ti chiedi da dove viene l'intervallo 0–1023 di una lettura analogica, leggi l'articolo ADC e conversione analogico-digitale.

Misurare i BPM con PulseSensorPlayground

  1. Apri il Gestore librerie dell'Arduino IDE, cerca PulseSensor Playground e installala. Per i dettagli sull'installazione delle librerie, guarda l'articolo come aggiungere una libreria ad Arduino.
  2. Collega il sensore come nella tabella: + a 5V, − a GND e S ad A0.
  3. Carica il codice seguente e apri il Plotter seriale a 115200 baud; la prima linea mostra il segnale grezzo e la seconda il valore dei BPM.
  4. Appoggia il dito sul sensore, poggia la mano sul tavolo e resta fermo per 10 secondi; la libreria assesta la sua media nei primi battiti.
  5. Se preferisci vedere i numeri riga per riga nel Monitor seriale, imposta a false la costante PLOTTER nel codice; a ogni battito vengono stampati i BPM e il tempo tra due battiti (IBI).
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}
FunzioneCosa fa
analogInput(pin)Sceglie il pin analogico a cui è collegato il sensore (A0 per impostazione predefinita)
blinkOnPulse(pin)Accende il LED su questo pin finché il segnale è sopra la soglia, cioè durante ogni battito
fadeOnPulse(pin)Accende il LED su un pin PWM a ogni battito e lo spegne lentamente
setThreshold(valore)Soglia iniziale; se non si trovano battiti, la soglia torna a questo valore
begin()Avvia il campionamento; restituisce true se riesce
sawStartOfBeat()Restituisce true se dall'ultima chiamata è iniziato un nuovo battito
getBeatsPerMinute()BPM calcolati dalla media degli ultimi 10 intervalli tra battiti
getInterBeatIntervalMs()Tempo tra gli ultimi due battiti (IBI), in millisecondi
getLatestSample()L'ultimo valore grezzo letto (0–1023)
isInsideBeat()Restituisce true finché il segnale è sopra la soglia

La libreria legge il sensore in background con un interrupt del timer ogni 2 ms (500 volte al secondo), quindi il campionamento resta regolare anche se in loop() esegui operazioni lunghe. A ogni battito memorizza il picco e la valle dell'onda e sposta la soglia esattamente a metà tra i due; il valore che passi a setThreshold() è quindi solo un punto di partenza. Ignora le salite che arrivano entro 250 ms dal battito precedente o prima che siano trascorsi i tre quinti dell'ultimo intervallo, così l'incisura dicrota non viene contata come secondo battito. I BPM si calcolano dalla media degli ultimi 10 intervalli tra battiti; se per 2,5 secondi non viene trovato alcun battito, tutto si azzera e getBeatsPerMinute() restituisce 0.

Scegli la soglia iniziale guardando il segnale grezzo: 30–50 passi sopra il valore più alto che la linea raggiunge senza dito sono un buon punto di partenza (tra 530 e 600 nella maggior parte dei sensori). Se la soglia è troppo bassa, il rumore viene contato come battito anche senza dito; se è troppo alta, i polsi deboli si perdono. Un altro avvertimento: sull'Arduino Uno la libreria usa il Timer1 per il campionamento, perciò analogWrite() non funziona sui pin 9 e 10. Se aggiungi la libreria Servo al progetto, la libreria passa al Timer2; in quel caso a risentirne sono il PWM dei pin 3 e 11 e la funzione tone().

Per una misura precisa: pressione del dito, luce ambientale e movimento

Quasi tutto ciò che falsa una misura del polso dipende da come tieni il sensore, non dal codice. Prova i punti seguenti uno alla volta con il Plotter seriale aperto; vedere come ognuno cambia l'onda è il modo più veloce per capire il sensore.

  • Pressione del dito: premi poco ma in modo costante. Se lo tocchi troppo leggermente, ogni piccolo movimento del dito crea un rumore più grande dell'onda; se premi troppo, schiacci i capillari, il flusso di sangue diminuisce e l'onda si appiattisce o sparisce del tutto. I risultati migliori si ottengono fissando il sensore al dito con una fascetta in velcro.
  • Luce ambientale: la luce del sole e delle lampade entra nel sensore dai lati. Con la rete a 50 Hz, come in Italia, molte lampade sfarfallano 100 volte al secondo e questo sfarfallio finisce nel segnale. Copri completamente i bordi del sensore con il dito o avvolgici sopra una fascia scura.
  • Movimento: un cavo che oscilla, parlare, muovere il dito o camminare producono segnali molto più grandi del polso. Durante la misura appoggia la mano sul tavolo e resta fermo.
  • Mani fredde: in un dito freddo i vasi si restringono e il segnale si indebolisce. Scalda le mani oppure metti il sensore sul lobo dell'orecchio, che risente anche meno dei movimenti.
  • Sudore e sebo: la pelle umida diffonde la luce in modo diverso e sporca la superficie del sensore. Usa l'adesivo protettivo trasparente sul fronte e pulisci ogni tanto il sensore con un panno asciutto.
  • Tempo di assestamento: nei primi 5–10 secondi dopo aver appoggiato il dito il segnale si stabilizza e la media si riempie; non considerare i valori di questo periodo.
C'è un modo semplice per capire se il sensore misura correttamente: conta i battiti sul polso o sul collo per 30 secondi, moltiplica per 2 e confronta il risultato con i BPM sullo schermo. A riposo una differenza di 3–5 BPM è normale; se è maggiore, sistema prima la pressione del dito e la luce ambientale e poi regola la soglia.

Indicatore del battito su display OLED

Il Plotter seriale va bene sulla scrivania, ma un cardiofrequenzimetro portatile ha bisogno di un piccolo schermo. Il codice seguente disegna nella parte alta di un display OLED SSD1306 da 0,96 pollici i BPM a cifre grandi e un cuore che si ingrandisce a ogni battito, e nella parte bassa un'onda del polso in tempo reale che scorre da sinistra a destra. Collega l'OLED ai pin A4 (SDA) e A5 (SCL) dell'Uno e l'alimentazione a 5V e GND; l'installazione della libreria del display e l'impostazione dell'indirizzo sono spiegate nel dettaglio nell'articolo Come usare un display OLED (SSD1306).

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}

Il codice aggiorna lo schermo 25 volte al secondo e aggiunge un punto all'onda a ogni fotogramma, così la larghezza dello schermo mostra circa 5 secondi di registrazione del polso. Alla fine di ogni passata l'onda viene riscalata in base al valore minimo e massimo di quella passata, così che sia un segnale debole sia uno forte entrino nello schermo. Inviare un fotogramma al display via I2C richiede circa 25–30 ms, ma poiché la libreria campiona tramite interrupt, nel frattempo non si perde nessun battito. Sull'Uno il buffer dell'immagine del display occupa 1 KB di RAM; dato che il codice disegna l'onda direttamente sullo schermo senza salvarla in un array, la memoria basta, anche se restano liberi solo circa 450 byte.

Misurare il battito con il KY-039

Nel KY-039 il fototransistor è collegato in serie a una resistenza e la tensione di uscita cambia con la luce che riceve: più luce significa tensione più bassa, meno luce tensione più alta. In mezzo non ci sono né amplificatori né filtri. Con il dito appoggiato, un battito di solito sposta questa tensione solo di 2–10 passi dell'ADC, circa 10–50 mV; anche un piccolo cambiamento della luce ambientale è più grande. Per questo il codice scritto per il Pulse Sensor e una soglia fissa come 550 non funzionano con il KY-039; bisogna prima pulire il segnale e poi usare una soglia che si adatti alla dimensione dell'onda stessa.

Il codice seguente lo fa in quattro passi. Prima legge il pin analogico senza sosta per 20 ms (circa 175 letture) e ne fa la media; questo riduce il rumore casuale e, poiché 20 ms sono esattamente un periodo della rete a 50 Hz, elimina quasi del tutto lo sfarfallio delle lampade. Poi ammorbidisce il segnale con un leggero filtro passa-basso e toglie il livello continuo con una media mobile molto lenta, così resta solo l'onda del polso. Nel terzo passo calcola due inviluppi che seguono i picchi e le valli dell'onda e mette la soglia a metà tra i due. Infine conta un battito quando il segnale attraversa la soglia verso l'alto e ricava i BPM dalla media degli ultimi 8 intervalli.

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}
Costante / valoreFunzione
WINDOW_US = 20000Finestra di media; elimina lo sfarfallio della rete a 50 Hz (nei paesi a 60 Hz usa 16667)
0.5 (smooth)Leggero filtro passa-basso; ammorbidisce i salti improvvisi
0.05 (baseline)Segue il livello continuo in circa 0,4 secondi; elimina le variazioni lente della pressione del dito
0.99 (inviluppi)Velocità di rilascio degli inviluppi di picchi e valli (circa 2 secondi)
MIN_AMP = 1.2Un'onda sotto questa ampiezza picco-picco è considerata rumore
refractoryIl 70% dell'intervallo medio attuale (250–600 ms; 350 ms finché non c'è uno storico, così non si perde un battito veloce); impedisce che l'incisura dicrota conti come secondo battito
mid - amp * 0.25Livello di riarmo; non si cerca un nuovo battito finché l'onda non è scesa abbastanza

Quando carichi il codice e apri il Plotter seriale vedi due linee: Nabiz (polso) mostra l'onda ripulita ed Esik (soglia) la soglia adattiva che passa nel suo centro. Guarda quanto si muove la linea Nabiz senza dito e imposta MIN_AMP poco sopra quel valore picco-picco; abbassalo se il segnale è molto debole. Per saperne di più su come smussare i segnali, leggi l'articolo filtrare i dati dei sensori. Lo stesso codice funziona anche con il Pulse Sensor; in quel caso basta alzare MIN_AMP a circa 20.

Sensore di battito con l'ESP32

Sull'ESP32 alimenta il sensore dal pin 3V3 e collega il segnale a GPIO 34. Questo pin fa parte del gruppo ADC1; i pin ADC2 non si possono leggere con il Wi-Fi attivo, quindi se vuoi inviare i dati del polso su internet devi scegliere uno dei pin ADC1 (GPIO 32–39). L'ADC dell'ESP32 è a 12 bit (0–4095) e, con un'alimentazione di 3,3 V, il Pulse Sensor a riposo dà un valore intorno a 2000.

Le versioni recenti di PulseSensorPlayground supportano anche l'ESP32, ma la libreria usa le funzioni timer della versione 3.x del pacchetto schede ESP32 e con la versione 2.x non compila. Nei suoi esempi porta l'ADC a 10 bit con analogReadResolution(10), così la soglia si indica sulla stessa scala 0–1023 dell'Uno (685 nell'esempio per ESP32). Per non dipendere dalle differenze di versione, qui sotto trovi uno sketch senza libreria che usa lo stesso algoritmo della sezione KY-039; funziona con entrambe le versioni del pacchetto schede.

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 questo codice il segnale viene letto 100 volte al secondo e, poiché l'ADC dell'ESP32 è rumoroso, ogni campione è la media di 8 letture. Soglia e inviluppi sono tarati sulla scala a 12 bit, per questo MIN_AMP vale 80 (circa 65 mV). Il vero punto di forza dell'ESP32 è il Wi-Fi: puoi inviare i BPM in tempo reale a una pagina web tramite WebSocket, mandare un messaggio Telegram quando il polso supera un limite o registrare le misure su Google Sheets.

Misurare il battito con Raspberry Pi Pico e MicroPython

Sul Raspberry Pi Pico collega il pin + del sensore a 3V3(OUT) (pin 36), − a GND (pin 38) e S a GP26 (ADC0, pin 31). In MicroPython read_u16() restituisce un valore da 0 a 65535; il codice lo sposta di 4 bit a destra per portarlo sulla stessa scala 0–4095 dell'ESP32, così le soglie restano uguali sulle due schede. Se salvi il codice sul Pico come main.py da Thonny, partirà da solo ogni volta che la scheda viene accesa.

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")

Il codice segue la stessa logica della versione per ESP32: prende un campione ogni 10 ms, toglie il livello continuo, trova il battito con una soglia adattiva e calcola i BPM dalla media degli ultimi 8 intervalli. A ogni battito si accende il LED sulla scheda e nella finestra Shell di Thonny vengono stampati i BPM e l'intervallo tra i battiti. Se la sintassi di Python è nuova per te, l'articolo basi di MicroPython è un buon punto di partenza.

La differenza con il MAX30102: come si misura l'ossigeno nel sangue (SpO2)

Il MAX30102 è un sensore a chip singolo che Analog Devices (in precedenza Maxim Integrated) ha progettato per misurare la frequenza cardiaca e la saturazione di ossigeno nel sangue. Al suo interno ci sono due LED, rosso (660 nm) e infrarosso (880 nm), un fotodiodo, un circuito di soppressione della luce ambientale e un ADC a 18 bit. Esegue la misura da solo, conserva i campioni in una memoria da 32 valori (FIFO) e li invia come numeri sul bus I2C (indirizzo 0x57). Dato che spariscono il rumore raccolto dai cavi analogici e il limite dell'ADC a 10 bit di Arduino, il suo segnale è molto più pulito di quello del Pulse Sensor.

Il segreto della misura della SpO2 è usare luce di due colori diversi. L'emoglobina che trasporta ossigeno assorbe più luce infrarossa e meno luce rossa; l'emoglobina che ha ceduto il suo ossigeno fa il contrario. Il sensore misura le componenti alternata e continua dell'onda del polso per entrambe le luci e calcola il rapporto R = (AC_rosso / DC_rosso) / (AC_infrarosso / DC_infrarosso); la SpO2 si ricava da questo rapporto con una curva di calibrazione empirica. Pulse Sensor e KY-039 usano luce di un solo colore, quindi non possono calcolare questo rapporto e nemmeno misurare l'ossigeno. La libreria Arduino più usata per il MAX30102 è SparkFun MAX3010x; il suo esempio Example5_HeartRate calcola la frequenza cardiaca ed Example8_SPO2 la saturazione di ossigeno.

Su alcune schede MAX30102 economiche le resistenze di pull-up dell'I2C sono collegate a 1,8 V; un Arduino Uno a 5 V non riconosce questo livello come HIGH e il sensore non viene trovato. Se la tua scheda ha un ponticello di saldatura per scegliere tra 1,8 V e 3,3 V, spostalo sul lato 3,3 V. Anche questi moduli sono molto sensibili alla pressione del dito e ai movimenti.

Un sensore di battito non è un dispositivo medico

I circuiti e il codice di questo articolo sono pensati per lo studio e l'hobby. I moduli Pulse Sensor, KY-039 e MAX30102 non sono stati testati né approvati come dispositivi medici; non fare diagnosi e non prendere decisioni su farmaci o terapie in base ai loro valori. Se hai dolore al petto, palpitazioni, capogiri, svenimenti o fiato corto, non affidarti a un sensore: rivolgiti subito a un medico e, in caso di emergenza, chiama il 112.

Ci sono ragioni concrete. I pulsossimetri e i cardiofrequenzimetri medici vengono calibrati entro limiti di precisione definiti e testati su diverse tonalità di pelle, con basso flusso sanguigno e in movimento; i moduli hobbistici e gli algoritmi che scriviamo non passano questi test. Inoltre misurare il polso non equivale a valutare il ritmo cardiaco: per capire le aritmie serve un ECG, che registra il segnale elettrico del cuore. Come informazione generale, la frequenza cardiaca a riposo di un adulto è di solito tra 60 e 100 battiti al minuto; in chi fa sport con regolarità può scendere a 40–60. Se la tua misura è fuori da questo intervallo, controlla prima il sensore e il codice; se sei ancora preoccupato, parlane con un medico.

Idee di progetti con il sensore di battito

  • Luce che batte con il cuore: a ogni battito accendi in rosso un anello di LED WS2812B e spegnilo lentamente; sposta il colore dal blu al rosso man mano che i BPM salgono.
  • Cardiofrequenzimetro con bip: fai suonare un bip breve con un buzzer ogni volta che sawStartOfBeat() restituisce true; un progetto divertente che imita il suono dei monitor ospedalieri.
  • Cardiofrequenzimetro portatile: metti il codice OLED in una scatola con una batteria e un circuito di ricarica; spegni lo schermo a misura finita per far durare di più la batteria.
  • Test di recupero dopo l'allenamento: misura subito dopo aver finito l'esercizio e calcola di quanti battiti scende il polso nel primo minuto; annota il valore ogni giorno e segui i tuoi progressi.
  • Registratore del battito: scrivi i BPM con l'orario su una scheda SD oppure inviali a Excel e disegnane il grafico.
  • Gioco di respirazione: mostra sullo schermo come cambia il polso quando respiri lentamente e profondamente; vinci quando riesci a portare il polso sotto un valore obiettivo.

Errori comuni e soluzioni

SintomoCausa probabileSoluzione
BPM sempre 0 o "--"Nessun battito rilevato: soglia troppo alta, nessun dito o pin S sull'ingresso sbagliatoControlla l'onda con lo sketch del segnale grezzo; porta la soglia al centro dell'onda
BPM molto alti, tipo 150–220Il rumore o l'incisura dicrota vengono contati come secondo battitoAlza la soglia, tieni fermo il dito, blocca la luce ambientale
Compaiono BPM anche senza ditoSoglia sotto il rumore a riposo; lo sfarfallio delle lampade entra nel segnalePorta la soglia 30–50 passi sopra il valore massimo a riposo e copri il sensore
Segnale piatto fisso a 0 o a 1023+ e − invertiti, filo interrotto, pin S collegato male o sensore guastoControlla i collegamenti con la tabella; usa 3V3 su ESP32 e Pico
L'onda c'è ma si deforma di continuoIl dito si muove o la pressione cambiaFissa il sensore con il velcro, appoggia la mano sul tavolo e resta fermo
Con il KY-039 non compare nessuna ondaLED non piegato sopra il fototransistor o luce ambientale forte che satura il fototransistorPiega il LED in modo che stia sopra il dito, usa il sensore all'ombra, abbassa MIN_AMP
"too few arguments to function 'hw_timer_t* timerBegin(uint8_t, uint16_t, bool)'" sull'ESP32PulseSensorPlayground viene compilata con la versione 2.x del pacchetto schede ESP32Aggiorna il pacchetto schede alla 3.x o usa il codice ESP32 senza libreria di questo articolo
analogWrite() non funziona sul pin 9 o 10La libreria usa il Timer1 sull'UnoPer il PWM usa i pin 3, 5, 6 o 11
Quando cerchi un guasto, parti sempre dal codice più semplice: se il test del segnale grezzo mostra un'onda pulita, l'hardware funziona e il problema è nella soglia o nel codice; se l'onda non c'è, cambiare il codice non serve, quindi sistema prima i collegamenti e il contatto del sensore con il dito.

Il sensore di battito è uno di quei rari progetti che, con un solo pin analogico, insegnano insieme lettura analogica, filtraggio del segnale, interrupt dei timer e grafici in tempo reale, e il risultato lo vedi sul tuo stesso corpo. Se cerchi un progetto per principianti che misuri anch'esso la luce, il sensore di luce LDR è un buon passo successivo. Nell'app BlueGrays trovi progetti che usano i componenti di questo articolo, come la lettura dell'intensità luminosa con un LDR, il buzzer, progetti ESP32 con display OLED e un effetto respiro PWM sul Pico, con schemi elettrici e codice passo passo.

Domande frequenti

Quanto è preciso un sensore di battito con Arduino?

Tenuto bene e con la persona ferma, un Pulse Sensor di solito misura il polso a riposo con un errore di pochi BPM. Movimento, luce ambientale, mani fredde e pressione del dito fanno crescere rapidamente l'errore. Il segnale del KY-039 è molto più debole, quindi le sue letture oscillano di più; nessuno di questi sensori è adatto a misure mediche.

Che differenza c'è tra Pulse Sensor e KY-039?

Il Pulse Sensor usa un LED verde e il metodo a riflessione, amplifica e filtra il segnale con un amplificatore operazionale sulla scheda e a riposo resta a metà dell'alimentazione. Il KY-039 misura la luce infrarossa che attraversa il dito, non ha amplificatore e la sua uscita si muove solo di pochi passi dell'ADC. Per questo il Pulse Sensor funziona facilmente con una libreria pronta, mentre il KY-039 richiede filtraggio e una soglia adattiva.

Si può misurare l'ossigeno nel sangue (SpO2) con un sensore di battito?

Con il Pulse Sensor o il KY-039 no, perché il calcolo della SpO2 si basa sul confronto tra luce di due lunghezze d'onda, rossa e infrarossa. Serve un sensore con due LED come il MAX30102. Anche il valore del MAX30102 è una stima non calibrata e non sostituisce un pulsossimetro medico.

Perché il mio sensore di battito mostra valori molto alti o instabili?

La causa più comune è che il rumore o l'incisura dicrota dell'onda del polso vengono contati come un secondo battito; in quel caso i BPM risultano circa il doppio del valore reale. Alza la soglia, fissa il dito con una fascetta in velcro e ripara il sensore dalla luce. Guardare l'onda grezza nel Plotter seriale mostra subito l'origine del problema.

Il Pulse Sensor funziona con ESP32 e Raspberry Pi Pico?

Sì, ma su entrambe le schede devi alimentare il sensore dal pin 3V3, perché i loro ingressi analogici non sopportano più di 3,3 V. Sull'ESP32 usa un pin ADC1 come GPIO 34 per evitare conflitti con il Wi-Fi, sul Pico GP26, GP27 o GP28. PulseSensorPlayground richiede la versione 3.x del pacchetto schede ESP32; in MicroPython scrivi un tuo algoritmo, come in questo articolo.

Qual è la frequenza cardiaca normale?

Negli adulti a riposo il polso è di solito tra 60 e 100 battiti al minuto; in chi si allena regolarmente può essere di 40–60, mentre nei bambini è più alto. Il polso sale in modo naturale con l'esercizio, lo stress, la caffeina e la febbre. Sono informazioni generali; parla delle tue misure o di eventuali sintomi con un medico.

Vedi tutti i progetti
Misurare l'intensità luminosa con LDR
#11Principiante

Misurare l'intensità luminosa con LDR

Vuoi vedere quanto è luminosa la tua stanza sotto forma di numero? L'LDR, un piccolo componente sensibile alla luce, misura la luce ambiente e l'Arduino scrive il valore sullo schermo del computer ogn

4.6(81)
139
Lettura dell'intensità luminosa con il modulo LDR ESP32
#113Principiante

Lettura dell'intensità luminosa con il modulo LDR ESP32

Monitoraggio in tempo reale sul Serial Monitor del valore grezzo dell'ADC del modulo LDR e della percentuale relativa di luce calcolata dai valori di riferimento al buio e alla luce. Questa percentuale non è una misurazione in lux. Spiega il principio di calibrazione del modulo. È consigliabile eseguirlo prima del Progetto 10, poiché serve a determinare il valore di soglia.

4.6(36)
55
Uso del cicalino
#13Principiante

Uso del cicalino

Ti sei mai chiesto come fa un campanello o un allarme a suonare? L'Arduino fa suonare un piccolo cicalino a intervalli brevi: bip, pausa, bip, pausa. Vedi come l'elettricità diventa suono e fai il pri

4.7(89)
161
Temperatura e umidità con DHT11
#55Intermedio

Temperatura e umidità con DHT11

Quanti gradi ci sono nella tua stanza, e l'aria è umida o secca? Il sensore DHT11 misura temperatura e umidità, e l'Arduino scrive i valori su uno schermo LCD a due righe, aggiornandoli ogni paio di s

4.7(47)
73
Voltmetro digitale con display LCD
#56Intermedio
Premium

Voltmetro digitale con display LCD

Scopri quanta tensione dà una pila o un alimentatore con uno strumento costruito da te. Un partitore fatto di due resistenze abbassa la tensione alta a un livello che la scheda può leggere, e l'Arduin

4.5(27)
36
ESP32 HTTPClient + API meteo (con display OLED SSD1306)
#134Avanzato
Premium

ESP32 HTTPClient + API meteo (con display OLED SSD1306)

In questo progetto, utilizzando l'ESP32, effettuiamo una chiamata REST all'API meteo (wttr.in) tramite Internet utilizzando HTTPClient.h. A differenza della precedente versione con LCD I2C, qui utilizziamo un display OLED SSD1306 (128×64), che consente di utilizzare caratteri di grandi dimensioni, icone vettoriali e una ricca presentazione visiva su un unico schermo. Per le condizioni meteorologiche, 7 diverse icone vettoriali (soleggiato, parzialmente nuvoloso, nuvoloso, piovoso, nevoso, temp

4.3(10)
19
ESP32 Deep Sleep — Risparmio della batteria (OLED + contatore in RTC RAM)
#135Avanzato
Premium

ESP32 Deep Sleep — Risparmio della batteria (OLED + contatore in RTC RAM)

La modalità Deep Sleep dell'ESP32 riduce il consumo di corrente a circa 10 µA spegnendo la CPU e la maggior parte dei dispositivi periferici ed è di fondamentale importanza per i dispositivi IoT alimentati a batteria. In questo progetto utilizziamo tre diverse fonti di risveglio: il timer (15 secondi), un pin esterno (EXT0 — pulsante GPIO 33) e il sensore tattile (T0/GPIO 4). Le variabili contrassegnate con RTC_DATA_ATTR vengono preservate durante la modalità Deep Sleep: il contatore di avvio,

4.3(10)
16
Raspberry Pi Pico PWM LED Fade (Effetto Respiro)
#160Principiante

Raspberry Pi Pico PWM LED Fade (Effetto Respiro)

Il LED non lampeggia più — respira! Con il PWM (modulazione di larghezza d'impulso) del Raspberry Pi Pico, la luminosità sale e scende lentamente. Impara la classe machine.PWM, duty_u16() (luminosità a 16 bit 0–65535), la frequenza PWM, i cicli range() e l'uscita quasi analogica. Base per miscelazione colori RGB, controllo motori, servo e buzzer.

0.0(0)
4