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.

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.
| Caratteristica | Valore del Pulse Sensor |
|---|---|
| Metodo di misura | PPG a riflessione |
| Sorgente luminosa | LED verde |
| Sensore di luce | APDS-9008, sensibilità massima a 565 nm |
| Alimentazione | 3–5 V |
| Assorbimento | Circa 4 mA (a 5 V) |
| Uscita | Analogica; a riposo metà della tensione di alimentazione (~2,5 V a 5 V, ~512 sull'Uno) |
| Diametro della scheda | Circa 16 mm (0,625 pollici) |
| Fili | Nell'originale rosso +, nero GND, viola segnale; nei cloni i colori possono cambiare |
Pulse Sensor, KY-039 e MAX30102: le differenze

| Caratteristica | Pulse Sensor | KY-039 | MAX30102 |
|---|---|---|---|
| Metodo | PPG a riflessione | PPG a trasmissione (dito in mezzo) | PPG a riflessione |
| Luce | LED verde | LED infrarosso | Rosso (660 nm) + infrarosso (880 nm) |
| Amplificatore e filtro | Sì (amplificatore operazionale) | No | ADC a 18 bit integrato, soppressione della luce ambientale |
| Uscita | Analogica | Analogica (molto debole) | Digitale, I2C (indirizzo 0x57) |
| Alimentazione | 3–5 V | 3,3–5 V | 3,3–5 V secondo il modulo |
| Qualità del segnale | Buona | Debole, sensibile al rumore | Ottima |
| SpO2 (ossigeno) | No | No | Sì (con un algoritmo) |
| Libreria | PulseSensor Playground | Nessuna libreria pronta | SparkFun 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 sensore | Arduino Uno | ESP32 | Raspberry Pi Pico |
|---|---|---|---|
| + (VCC, rosso) | 5V | 3V3 | 3V3(OUT), pin 36 |
| − (GND, nero) | GND | GND | GND, pin 38 |
| S (segnale, viola) | A0 | GPIO 34 (ADC1) | GP26 (ADC0), pin 31 |
| LED del battito (facoltativo) | D13 (LED integrato) | GPIO 2 (LED integrato) | LED sulla scheda |
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.
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
- 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.
- Collega il sensore come nella tabella: + a 5V, − a GND e S ad A0.
- 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.
- 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.
- 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).
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}| Funzione | Cosa 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.
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).
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.
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 / valore | Funzione |
|---|---|
| WINDOW_US = 20000 | Finestra 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.2 | Un'onda sotto questa ampiezza picco-picco è considerata rumore |
| refractory | Il 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.25 | Livello 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.
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.
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.
Un sensore di battito non è un dispositivo medico
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
| Sintomo | Causa probabile | Soluzione |
|---|---|---|
| BPM sempre 0 o "--" | Nessun battito rilevato: soglia troppo alta, nessun dito o pin S sull'ingresso sbagliato | Controlla l'onda con lo sketch del segnale grezzo; porta la soglia al centro dell'onda |
| BPM molto alti, tipo 150–220 | Il rumore o l'incisura dicrota vengono contati come secondo battito | Alza la soglia, tieni fermo il dito, blocca la luce ambientale |
| Compaiono BPM anche senza dito | Soglia sotto il rumore a riposo; lo sfarfallio delle lampade entra nel segnale | Porta 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 guasto | Controlla i collegamenti con la tabella; usa 3V3 su ESP32 e Pico |
| L'onda c'è ma si deforma di continuo | Il dito si muove o la pressione cambia | Fissa il sensore con il velcro, appoggia la mano sul tavolo e resta fermo |
| Con il KY-039 non compare nessuna onda | LED non piegato sopra il fototransistor o luce ambientale forte che satura il fototransistor | Piega 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'ESP32 | PulseSensorPlayground viene compilata con la versione 2.x del pacchetto schede ESP32 | Aggiorna il pacchetto schede alla 3.x o usa il codice ESP32 senza libreria di questo articolo |
| analogWrite() non funziona sul pin 9 o 10 | La libreria usa il Timer1 sull'Uno | Per il PWM usa i pin 3, 5, 6 o 11 |
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.
Progetti da realizzare dopo questo articolo
Vedi tutti i progetti
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

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.

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

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

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

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

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,

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.
Altri articoli
App BlueGrays
Costruisci questo circuito passo passo sul telefono
Schema, codice ed elenco componenti in un’unica schermata. Oltre 200 progetti Arduino, ESP32 e Pico da realizzare.
Inquadra con il telefono
Punta la fotocamera sul codice: si apre lo store del tuo telefono.
oppure apri lo store

