Componentes · 20 min de lectura

Sensor de pulso cardíaco con Arduino: Pulse Sensor y KY-039

Sensor de pulso cardíaco con Arduino: conexión del Pulse Sensor y el KY-039, BPM con PulseSensorPlayground, onda en el Serial Plotter, OLED, ESP32 y Pico.

Un sensor de pulso cardíaco para Arduino es un pequeño módulo que sigue con luz el flujo de sangre en la yema de tu dedo o en el lóbulo de la oreja y mide cuántas veces late tu corazón por minuto, es decir, las BPM (pulsaciones por minuto). Los dos modelos más usados en electrónica de aficionado son el Pulse Sensor de placa redonda y el KY-039 con LED infrarrojo. Ambos se conectan con tres cables y se leen desde un único pin analógico, pero se diferencian mucho en la calidad de la señal y en lo difícil que es escribir un código fiable para ellos.

En esta guía aprenderás paso a paso en qué principio físico se basa un sensor de pulso, cómo ver una onda de pulso real en el Serial Plotter, cómo calcular las BPM con la librería PulseSensorPlayground, cómo procesar la débil señal del KY-039 con un algoritmo sin librería y cómo mostrar el resultado en una pantalla OLED con un icono de corazón. También verás código para ESP32 y Raspberry Pi Pico, los factores que estropean la medida y la diferencia con el MAX30102. Una advertencia importante desde el principio: estos sensores no son dispositivos médicos y no deben usarse para ninguna decisión de salud.

Sensor de pulso Pulse Sensor con cable de conexión
Módulo Pulse Sensor: una placa redonda de unos 16 mm de diámetro que reúne un LED verde, un sensor de luz y el circuito que amplifica la señal; viene con un cable de 3 pines.

¿Cómo funciona un sensor de pulso? Fotopletismografía (PPG)

Todos los sensores de pulso funcionan con un método llamado fotopletismografía (PPG, por sus siglas en inglés). Cada vez que el corazón se contrae envía una onda de sangre a las arterias; cuando esa onda llega a los pequeños vasos de la yema del dedo, el volumen de sangre en el tejido aumenta brevemente. Como la hemoglobina de la sangre absorbe la luz, cuanta más sangre hay, menos luz se refleja desde el tejido o lo atraviesa. El sensor ilumina el tejido con un LED y un sensor de luz mide la luz que vuelve; esa pequeña oscilación regular de la cantidad de luz es el propio pulso.

Lo difícil es que casi toda la luz medida es constante. La piel, el músculo, el hueso y la sangre de las venas absorben siempre la misma cantidad de luz; a esto se le llama componente continua (DC) de la señal. La componente alterna (AC), que cambia con cada latido, suele ser solo un 1–2 % de la luz total. Así que el verdadero trabajo del sensor es separar y amplificar una onda pequeña montada sobre un nivel grande y estable. Una onda PPG muestra primero una subida rápida hasta un pico (el pico sistólico) y después una bajada lenta; la pequeña muesca en mitad de la bajada y el pequeño bulto justo después (la muesca dícrota) se deben al cierre de la válvula aórtica. Un buen algoritmo cuenta cada latido una sola vez y no confunde esa muesca con un segundo latido.

También importa cómo se envía la luz al tejido. El Pulse Sensor usa el método de reflexión: el LED y el sensor de luz están en la misma cara y miden la luz que la piel dispersa de vuelta. Su luz verde la absorbe con fuerza la hemoglobina y da una señal clara de los vasos situados justo bajo la piel; por eso ves LED verdes en la parte trasera de los relojes inteligentes. El KY-039 funciona por transmisión: un LED infrarrojo ilumina un lado del dedo y un fototransistor al otro lado mide la luz que lo atraviesa. La luz infrarroja penetra más en el tejido, pero como el KY-039 no tiene ningún circuito para amplificar la señal, la onda resultante es muy pequeña.

CaracterísticaValor del Pulse Sensor
Método de medidaPPG por reflexión
Fuente de luzLED verde
Sensor de luzAPDS-9008, máxima sensibilidad a 565 nm
Alimentación3–5 V
ConsumoUnos 4 mA (a 5 V)
SalidaAnalógica; en reposo, la mitad de la tensión de alimentación (~2,5 V a 5 V, ~512 en el Uno)
Diámetro de la placaUnos 16 mm (0,625 pulgadas)
CablesEn el original, rojo +, negro GND y morado señal; en los clones los colores pueden cambiar

Pulse Sensor, KY-039 y MAX30102: diferencias

Sensor de latidos KY-039 (LED infrarrojo y fototransistor)
KY-039: arriba, el LED infrarrojo doblado sobre el fototransistor; abajo, el fototransistor. El dedo va entre los dos. La placa no tiene amplificador, así que la señal va directa al pin analógico.
CaracterísticaPulse SensorKY-039MAX30102
MétodoPPG por reflexiónPPG por transmisión (dedo en medio)PPG por reflexión
LuzLED verdeLED infrarrojoRojo (660 nm) + infrarrojo (880 nm)
Amplificador y filtroSí (amplificador operacional)NoADC integrado de 18 bits, cancelación de luz ambiental
SalidaAnalógicaAnalógica (muy débil)Digital, I2C (dirección 0x57)
Alimentación3–5 V3,3–5 V3,3–5 V según el módulo
Calidad de señalBuenaDébil, sensible al ruidoMuy buena
SpO2 (oxígeno)NoNoSí (con un algoritmo)
LibreríaPulseSensor PlaygroundSin librería listaSparkFun MAX3010x

Si estás empezando y solo quieres medir el pulso, el Pulse Sensor es la opción más fácil: como la señal se amplifica y filtra en la propia placa, verás una onda clara en el Serial Plotter desde el primer intento. El amplificador operacional de la parte trasera eleva ese pequeño cambio a un nivel que el Arduino puede leer; puedes probar cómo se calcula una ganancia así con la herramienta Amplificador operacional. El KY-039 es barato y viene en la mayoría de kits de sensores, pero su salida solo se mueve unos pocos pasos del ADC, así que un código sencillo con umbral fijo no da resultados fiables. Si además quieres medir la saturación de oxígeno en sangre (SpO2), la opción correcta es el MAX30102; le dedicamos una sección propia más abajo.

Conexión del sensor de pulso: Arduino Uno, ESP32 y Pico

Pin del sensorArduino UnoESP32Raspberry Pi Pico
+ (VCC, rojo)5V3V33V3(OUT), pin 36
− (GND, negro)GNDGNDGND, pin 38
S (señal, morado)A0GPIO 34 (ADC1)GP26 (ADC0), pin 31
LED de latido (opcional)D13 (LED integrado)GPIO 2 (LED integrado)LED de la placa
Las entradas analógicas del ESP32 y de la Pico soportan como máximo 3,3 V. Como la salida del Pulse Sensor puede subir hasta la tensión de alimentación, en estas placas alimenta el sensor desde el pin 3V3, no desde 5V ni VBUS. En las placas clónicas el orden de los pines y los colores de los cables pueden cambiar; lee siempre las marcas S, + y − de la placa antes de conectarla.

La misma tabla sirve para el KY-039: el pin S va a la entrada analógica, + a la alimentación y − a GND. Asegúrate de que el LED infrarrojo esté doblado sobre el fototransistor y de que el dedo quepa entre ambos. Los componentes de la parte trasera del Pulse Sensor quedan al aire; si un dedo o una superficie sudada tocan esa cara, aparece ruido. Por eso conviene pegar en la parte delantera la pegatina transparente que trae el kit y cubrir la trasera con silicona termofusible. Si usas un LED externo, calcula su resistencia con la herramienta Resistencia para LED; el mapa de pines de tu placa lo tienes en la herramienta Pinout.

Primera prueba: ver la onda de pulso en bruto en el Serial Plotter

Antes de pasar a las BPM, comprueba que el sensor ve de verdad el pulso. El código siguiente no usa ninguna librería; lee la señal 100 veces por segundo y la envía al Serial Plotter. Sube el código, abre Herramientas > Serial Plotter en el Arduino IDE y pon la velocidad en 115200 baudios. Si nunca has usado el Serial Plotter, el artículo Monitor serie de Arduino explica cómo funciona la ventana.

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}

Sin dedo, la línea del Pulse Sensor queda plana alrededor de 512, con un ligero temblor; es la mitad de la alimentación e indica que el sensor está bien conectado. Cuando apoyas suavemente la yema del dedo, en pocos segundos aparece una onda que sube rápido y baja despacio con cada latido; su amplitud suele estar entre 100 y 300 pasos. Si no ves la onda, no sigas con el código de BPM: corrige antes el cableado, la alimentación y la presión del dedo. Si los picos se convierten en una línea plana pegada a 0 o a 1023, la señal se está recortando; normalmente el dedo se mueve o el sensor está demasiado apretado. Si te preguntas de dónde sale el rango 0–1023 de una lectura analógica, consulta el artículo ADC y conversión analógica-digital.

Medir las BPM con PulseSensorPlayground

  1. Abre el Gestor de librerías del Arduino IDE, busca PulseSensor Playground e instálala. Para más detalles sobre la instalación de librerías, mira el artículo cómo añadir una librería a Arduino.
  2. Conecta el sensor como en la tabla: + a 5V, − a GND y S a A0.
  3. Sube el código siguiente y abre el Serial Plotter a 115200 baudios; la primera línea muestra la señal en bruto y la segunda el valor de BPM.
  4. Pon el dedo sobre el sensor, apoya la mano en la mesa y quédate quieto 10 segundos; la librería ajusta su media durante los primeros latidos.
  5. Si prefieres ver los números línea a línea en el Monitor serie, cambia la constante PLOTTER del código a false; en cada latido se imprimen las BPM y el tiempo entre latidos (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}
FunciónQué hace
analogInput(pin)Elige el pin analógico al que está conectado el sensor (A0 por defecto)
blinkOnPulse(pin)Enciende el LED de este pin mientras la señal está por encima del umbral, es decir, durante cada latido
fadeOnPulse(pin)Enciende el LED de un pin PWM con cada latido y lo apaga poco a poco
setThreshold(valor)Umbral inicial; si no se detectan latidos, el umbral vuelve a este valor
begin()Inicia el muestreo; devuelve true si todo va bien
sawStartOfBeat()Devuelve true si ha empezado un latido nuevo desde la última llamada
getBeatsPerMinute()BPM calculadas con la media de los últimos 10 intervalos entre latidos
getInterBeatIntervalMs()Tiempo entre los dos últimos latidos (IBI), en milisegundos
getLatestSample()La última lectura en bruto (0–1023)
isInsideBeat()Devuelve true mientras la señal está por encima del umbral

La librería lee el sensor en segundo plano con una interrupción de temporizador cada 2 ms (500 veces por segundo), así que el muestreo no se altera aunque hagas tareas largas en loop(). En cada latido guarda el pico y el valle de la onda y coloca el umbral justo a mitad de camino entre ambos; es decir, el valor que pasas a setThreshold() es solo un punto de partida. Ignora las subidas que llegan a menos de 250 ms del latido anterior o antes de que pasen tres quintos del último intervalo, de modo que la muesca dícrota no cuenta como un segundo latido. Las BPM se calculan con la media de los últimos 10 intervalos entre latidos; si durante 2,5 segundos no se detecta ningún latido, todo se reinicia y getBeatsPerMinute() devuelve 0.

Elige el umbral inicial mirando la señal en bruto: entre 30 y 50 pasos por encima del valor más alto que alcanza la línea sin dedo es un buen comienzo (entre 530 y 600 en la mayoría de sensores). Si el umbral es demasiado bajo, el ruido cuenta como latido incluso sin dedo; si es demasiado alto, se pierden los pulsos débiles. Otra advertencia: en el Arduino Uno la librería usa el Timer1 para muestrear, por lo que analogWrite() no funciona en los pines 9 y 10. Si añades la librería Servo a tu proyecto, la librería pasa al Timer2; entonces los afectados son el PWM de los pines 3 y 11 y la función tone().

Para medir bien: presión del dedo, luz ambiental y movimiento

Casi todo lo que estropea una medida de pulso tiene que ver con cómo se sujeta el sensor, no con el código. Prueba los puntos siguientes uno a uno con el Serial Plotter abierto; ver cómo cambia la onda con cada uno es la forma más rápida de entender el sensor.

  • Presión del dedo: aprieta poco pero de forma constante. Si lo tocas demasiado suave, cada pequeño movimiento del dedo genera un ruido mayor que la onda; si aprietas demasiado, comprimes los capilares, el flujo de sangre baja y la onda se aplana o incluso desaparece. Los mejores resultados se consiguen sujetando el sensor al dedo con una cinta de velcro.
  • Luz ambiental: la luz del sol y las lámparas se cuelan en el sensor por los lados. Con una red de 50 Hz, como en España, muchas lámparas parpadean 100 veces por segundo (120 en países con red de 60 Hz, como México) y ese parpadeo se mete en la señal. Tapa por completo el entorno del sensor con el dedo o enrolla encima una cinta oscura.
  • Movimiento: un cable que se balancea, hablar, mover el dedo o caminar producen señales mucho mayores que el pulso. Apoya la mano en la mesa y quédate quieto mientras mides.
  • Manos frías: en un dedo frío los vasos se estrechan y la señal se debilita. Calienta las manos o pon el sensor en el lóbulo de la oreja, que además se ve menos afectado por el movimiento.
  • Sudor y grasa: la piel húmeda dispersa la luz de otra manera y ensucia la superficie del sensor. Usa la pegatina protectora transparente en la cara delantera y limpia el sensor de vez en cuando con un paño seco.
  • Tiempo de estabilización: durante los primeros 5–10 segundos después de poner el dedo, la señal se asienta y la media se llena; no tengas en cuenta los valores de ese intervalo.
Hay una forma sencilla de saber si tu sensor mide bien: cuenta el pulso en la muñeca o en el cuello durante 30 segundos, multiplícalo por 2 y compáralo con las BPM de la pantalla. En reposo, una diferencia de 3–5 BPM es normal; si es mayor, corrige primero la presión del dedo y la luz ambiental y después ajusta el umbral.

Indicador de pulso en una pantalla OLED

El Serial Plotter sirve en la mesa, pero un pulsómetro portátil necesita una pequeña pantalla. El código siguiente dibuja en la parte superior de una pantalla OLED SSD1306 de 0,96 pulgadas las BPM en cifras grandes y un corazón que crece con cada latido, y en la parte inferior una onda de pulso en directo que avanza de izquierda a derecha. Conecta la OLED a los pines A4 (SDA) y A5 (SCL) del Uno y su alimentación a 5V y GND; la instalación de la librería de la pantalla y el ajuste de la dirección se explican con detalle en el artículo Cómo usar una pantalla 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}

El código refresca la pantalla 25 veces por segundo y añade un punto a la onda en cada fotograma, así que el ancho de la pantalla muestra unos 5 segundos de registro de pulso. Al final de cada barrido la onda se reescala al valor mínimo y máximo de ese barrido, para que tanto una señal débil como una fuerte quepan en la pantalla. Enviar un fotograma a la pantalla por I2C tarda unos 25–30 ms, pero como la librería muestrea mediante interrupciones, no se pierde ningún latido mientras tanto. En el Uno el búfer de imagen de la pantalla ocupa 1 KB de RAM; como el código dibuja la onda directamente en la pantalla sin guardarla en un array, la memoria alcanza, aunque solo quedan unos 450 bytes libres.

Medir el pulso con el KY-039

En el KY-039 el fototransistor está conectado en serie con una resistencia y la tensión de salida cambia con la luz que recibe: más luz significa menos tensión, y menos luz, más tensión. Entre medias no hay amplificador ni filtro. Con el dedo colocado, un latido suele mover esa tensión solo entre 2 y 10 pasos del ADC, unos 10–50 mV; hasta un pequeño cambio de la luz ambiental es mayor que eso. Por eso el código escrito para el Pulse Sensor y un umbral fijo como 550 no funcionan con el KY-039; primero hay que limpiar la señal y luego usar un umbral que se adapte al tamaño de la propia onda.

El código siguiente lo hace en cuatro pasos. Primero lee el pin analógico sin parar durante 20 ms (unas 175 lecturas) y calcula la media; esto reduce el ruido aleatorio y, como 20 ms es exactamente un periodo de la red de 50 Hz, elimina casi por completo el parpadeo de las lámparas. Después suaviza la señal con un filtro paso bajo ligero y quita el nivel de continua con una media móvil muy lenta, de modo que solo queda la onda de pulso. En el tercer paso calcula dos envolventes que siguen los picos y los valles de la onda y sitúa el umbral en medio de ambas. Por último, se cuenta un latido cuando la señal cruza el umbral hacia arriba y las BPM se obtienen de la media de los últimos 8 intervalos.

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}
Constante / valorFunción
WINDOW_US = 20000Ventana de promedio; elimina el parpadeo de la red de 50 Hz (en países de 60 Hz usa 16667)
0.5 (smooth)Filtro paso bajo ligero; suaviza los saltos bruscos
0.05 (baseline)Sigue el nivel de continua en unos 0,4 segundos; elimina los cambios lentos de presión del dedo
0.99 (envolventes)Velocidad de caída de las envolventes de picos y valles (unos 2 segundos)
MIN_AMP = 1.2Una onda por debajo de esta amplitud pico a pico se considera ruido
refractoryEl 70 % del intervalo medio actual (250–600 ms; 350 ms mientras no hay historial, para no perder un pulso rápido); evita que la muesca dícrota cuente como segundo latido
mid - amp * 0.25Nivel de rearme; no se busca un nuevo latido hasta que la onda baja lo suficiente

Al subir el código y abrir el Serial Plotter verás dos líneas: Nabiz (pulso) muestra la onda limpia y Esik (umbral) el umbral adaptativo que pasa por su centro. Fíjate en cuánto se mueve la línea Nabiz sin dedo y pon MIN_AMP un poco por encima de ese valor pico a pico; bájalo si tu señal es muy débil. Para saber más sobre cómo suavizar señales, consulta el artículo filtrado de datos de sensores. El mismo código funciona también con el Pulse Sensor; en ese caso basta con subir MIN_AMP a unos 20.

Sensor de pulso con el ESP32

En el ESP32, alimenta el sensor desde el pin 3V3 y conecta la señal a GPIO 34. Este pin pertenece al grupo ADC1; los pines ADC2 no se pueden leer con el Wi-Fi activo, así que si vas a enviar los datos de pulso a internet debes elegir uno de los pines ADC1 (GPIO 32–39). El ADC del ESP32 es de 12 bits (0–4095) y, con 3,3 V de alimentación, el Pulse Sensor da en reposo un valor de alrededor de 2000.

Las versiones recientes de PulseSensorPlayground también admiten el ESP32, pero la librería usa las funciones de temporizador de la versión 3.x del paquete de placas ESP32 y no compila con la versión 2.x. En sus ejemplos reduce el ADC a 10 bits con analogReadResolution(10), de modo que el umbral se indica en la misma escala 0–1023 que en el Uno (685 en el ejemplo del ESP32). Para no depender de las diferencias de versión, abajo tienes un sketch sin librería que usa el mismo algoritmo que la sección del KY-039; funciona con las dos versiones del paquete de placas.

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}

En este código la señal se lee 100 veces por segundo y, como el ADC del ESP32 es ruidoso, cada muestra es la media de 8 lecturas. El umbral y las envolventes están ajustados a la escala de 12 bits, por eso MIN_AMP vale 80 (unos 65 mV). El punto fuerte del ESP32 es el Wi-Fi: puedes enviar las BPM en directo a una página web mediante WebSocket, mandar un mensaje de Telegram cuando el pulso supere un límite o guardar tus medidas en Google Sheets.

Medir el pulso con Raspberry Pi Pico y MicroPython

En la Raspberry Pi Pico, conecta el pin + del sensor a 3V3(OUT) (pin 36), − a GND (pin 38) y S a GP26 (ADC0, pin 31). En MicroPython, read_u16() devuelve un valor entre 0 y 65535; el código lo desplaza 4 bits a la derecha para llevarlo a la misma escala 0–4095 del ESP32, de modo que los umbrales son iguales en ambas placas. Si guardas el código en la Pico como main.py desde Thonny, se ejecutará solo cada vez que la placa se encienda.

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

El código sigue la misma lógica que la versión para ESP32: toma una muestra cada 10 ms, quita el nivel de continua, encuentra el latido con un umbral adaptativo y calcula las BPM con la media de los últimos 8 intervalos. En cada latido se enciende el LED de la placa y en la ventana Shell de Thonny se imprimen las BPM y el intervalo entre latidos. Si la sintaxis de Python es nueva para ti, el artículo fundamentos de MicroPython es un buen punto de partida.

Diferencia con el MAX30102: cómo se mide el oxígeno en sangre (SpO2)

El MAX30102 es un sensor de un solo chip que Analog Devices (antes Maxim Integrated) diseñó para medir la frecuencia cardíaca y la saturación de oxígeno en sangre. Dentro lleva dos LED, rojo (660 nm) e infrarrojo (880 nm), un fotodiodo, un circuito de cancelación de luz ambiental y un ADC de 18 bits. Hace la medida por sí mismo, guarda las muestras en una memoria de 32 valores (FIFO) y las envía como números por el bus I2C (dirección 0x57). Como desaparecen el ruido que recogen los cables analógicos y el límite del ADC de 10 bits del Arduino, su señal es mucho más limpia que la del Pulse Sensor.

El secreto de la medida de SpO2 es usar luz de dos colores distintos. La hemoglobina que transporta oxígeno absorbe más luz infrarroja y menos luz roja; la hemoglobina que ya ha cedido su oxígeno hace lo contrario. El sensor mide las componentes alterna y continua de la onda de pulso para ambas luces y calcula la relación R = (AC_rojo / DC_rojo) / (AC_infrarrojo / DC_infrarrojo); la SpO2 se obtiene de esa relación mediante una curva de calibración empírica. El Pulse Sensor y el KY-039 usan luz de un solo color, así que no pueden calcular esa relación y, por tanto, tampoco medir el oxígeno. La librería de Arduino más popular para el MAX30102 es SparkFun MAX3010x; su ejemplo Example5_HeartRate calcula la frecuencia cardíaca y Example8_SPO2, la saturación de oxígeno.

En algunas placas MAX30102 baratas las resistencias pull-up del I2C están conectadas a 1,8 V; un Arduino Uno de 5 V no reconoce ese nivel como HIGH y el sensor no aparece. Si tu placa tiene un puente de soldadura para elegir entre 1,8 V y 3,3 V, pásalo al lado de 3,3 V. Estos módulos también son muy sensibles a la presión del dedo y al movimiento.

Un sensor de pulso no es un dispositivo médico

Los circuitos y el código de este artículo son para uso educativo y de aficionado. Los módulos Pulse Sensor, KY-039 y MAX30102 no se han probado ni aprobado como dispositivos médicos; no hagas diagnósticos ni tomes decisiones sobre medicamentos o tratamientos a partir de sus lecturas. Si tienes dolor en el pecho, palpitaciones, mareos, desmayos o falta de aire, no confíes en un sensor: busca atención médica de inmediato y, en caso de emergencia, llama al número de emergencias de tu país (el 112 en España).

Hay razones concretas para ello. Los pulsioxímetros y monitores de frecuencia cardíaca médicos se calibran según unos límites de precisión determinados y se prueban con distintos tonos de piel, con poco flujo sanguíneo y en movimiento; los módulos de aficionado y los algoritmos que escribimos no pasan esas pruebas. Además, medir el pulso no es lo mismo que evaluar el ritmo cardíaco: para entender las arritmias hace falta un ECG, que registra la señal eléctrica del corazón. Como información general, la frecuencia cardíaca en reposo de un adulto suele estar entre 60 y 100 latidos por minuto; en personas que hacen deporte con regularidad puede bajar a 40–60. Si tu medida queda fuera de ese rango, revisa primero el sensor y el código; si sigues preocupado, consulta a un médico.

Ideas de proyectos con un sensor de pulso

  • Luz que late con tu corazón: en cada latido enciende en rojo un anillo de LED WS2812B y apágalo poco a poco; cambia el color de azul a rojo a medida que suben las BPM.
  • Pulsómetro con pitido: haz sonar un pitido corto con un zumbador cada vez que sawStartOfBeat() devuelva true; un proyecto divertido que imita el sonido de los monitores de hospital.
  • Pulsómetro portátil: mete el código de la OLED en una caja con una batería y un circuito de carga; apaga la pantalla al terminar la medida para que la batería dure más.
  • Prueba de recuperación tras el ejercicio: mide justo al terminar de entrenar y calcula cuántas pulsaciones baja tu pulso en el primer minuto; anota el valor cada día y sigue tu progreso.
  • Registrador de pulso: guarda las BPM con la hora en una tarjeta SD o envíalas a Excel y dibuja una gráfica.
  • Juego de respiración: muestra en la pantalla cómo cambia tu pulso al respirar lenta y profundamente; ganas cuando consigues bajar el pulso por debajo de un valor objetivo.

Errores frecuentes y soluciones

SíntomaCausa probableSolución
BPM siempre en 0 o "--"No se detecta ningún latido: umbral demasiado alto, sin dedo o pin S en la entrada equivocadaComprueba la onda con el sketch de señal en bruto; lleva el umbral al centro de la onda
BPM muy altas, como 150–220El ruido o la muesca dícrota cuentan como segundo latidoSube el umbral, sujeta bien el dedo, bloquea la luz ambiental
Aparecen BPM incluso sin dedoUmbral por debajo del ruido en reposo; el parpadeo de las lámparas entra en la señalPon el umbral 30–50 pasos por encima del valor máximo en reposo y tapa el sensor
Señal plana pegada a 0 o a 1023+ y − invertidos, cable roto, pin S mal conectado o sensor averiadoRevisa el cableado con la tabla; usa 3V3 en el ESP32 y la Pico
Hay onda, pero se deforma continuamenteEl dedo se mueve o la presión cambiaSujeta el sensor con velcro, apoya la mano en la mesa y no te muevas
En el KY-039 no aparece ninguna ondaLED no doblado sobre el fototransistor o luz ambiental fuerte que satura el fototransistorDobla el LED para que quede sobre el dedo, usa el sensor a la sombra, baja MIN_AMP
"too few arguments to function 'hw_timer_t* timerBegin(uint8_t, uint16_t, bool)'" en el ESP32PulseSensorPlayground se compila con la versión 2.x del paquete de placas ESP32Actualiza el paquete de placas a 3.x o usa el código ESP32 sin librería de este artículo
analogWrite() no funciona en el pin 9 o 10La librería usa el Timer1 en el UnoUsa los pines 3, 5, 6 u 11 para PWM
Cuando busques fallos, empieza siempre por el código más sencillo: si la prueba de señal en bruto muestra una onda limpia, el hardware está bien y el problema está en el umbral o en el código; si no hay onda, cambiar el código no servirá de nada, así que corrige primero el cableado y el contacto del sensor con el dedo.

Un sensor de pulso es uno de esos pocos proyectos que, con un solo pin analógico, te enseñan a la vez lectura analógica, filtrado de señales, interrupciones de temporizador y gráficas en tiempo real, y además ves el resultado en tu propio cuerpo. Si quieres un proyecto de iniciación que también mida luz, el sensor de luz LDR es un buen siguiente paso. En la app BlueGrays encontrarás proyectos que usan las piezas de este artículo, como leer la intensidad de luz con una LDR, el zumbador, proyectos ESP32 con pantalla OLED y un efecto de respiración PWM en la Pico, con esquemas de circuito y código paso a paso.

Preguntas frecuentes

¿Qué precisión tiene un sensor de pulso con Arduino?

Bien sujeto y con la persona quieta, un Pulse Sensor suele medir el pulso en reposo con un error de pocas BPM. El movimiento, la luz ambiental, las manos frías y la presión del dedo aumentan el error rápidamente. La señal del KY-039 es mucho más débil, así que sus lecturas varían más; ninguno de estos sensores sirve para una medida médica.

¿Qué diferencia hay entre el Pulse Sensor y el KY-039?

El Pulse Sensor usa un LED verde y el método de reflexión, amplifica y filtra la señal con un amplificador operacional en la placa y en reposo se queda en la mitad de la alimentación. El KY-039 mide la luz infrarroja que atraviesa el dedo, no tiene amplificador y su salida solo se mueve unos pocos pasos del ADC. Por eso el Pulse Sensor funciona fácilmente con una librería lista, mientras que el KY-039 necesita filtrado y un umbral adaptativo.

¿Se puede medir el oxígeno en sangre (SpO2) con un sensor de pulso?

Con el Pulse Sensor o el KY-039 no, porque el cálculo de la SpO2 se basa en comparar luz de dos longitudes de onda, roja e infrarroja. Para eso necesitas un sensor con dos LED, como el MAX30102. Incluso el valor del MAX30102 es una estimación sin calibrar y no sustituye a un pulsioxímetro médico.

¿Por qué mi sensor de pulso marca valores muy altos o inestables?

La causa más habitual es que el ruido o la muesca dícrota de la onda de pulso se cuentan como un segundo latido; en ese caso las BPM salen aproximadamente al doble del valor real. Sube el umbral, sujeta el dedo con una cinta de velcro y protege el sensor de la luz. Mirar la onda en bruto en el Serial Plotter muestra al momento el origen del problema.

¿El Pulse Sensor funciona con ESP32 y Raspberry Pi Pico?

Sí, pero en ambas placas debes alimentar el sensor desde el pin 3V3, porque sus entradas analógicas no soportan más de 3,3 V. En el ESP32 usa un pin ADC1 como GPIO 34 para que no choque con el Wi-Fi, y en la Pico, GP26, GP27 o GP28. PulseSensorPlayground necesita la versión 3.x del paquete de placas ESP32; en MicroPython escribes tu propio algoritmo, como en este artículo.

¿Cuál es la frecuencia cardíaca normal?

En adultos en reposo el pulso suele estar entre 60 y 100 latidos por minuto; en personas que entrenan con regularidad puede ser de 40–60 y en los niños es más alto. El pulso sube de forma natural con el ejercicio, el estrés, la cafeína y la fiebre. Es información general; consulta a un médico sobre tus medidas o cualquier síntoma.

Ver todos los proyectos
Medir la intensidad de luz con LDR
#11Principiante

Medir la intensidad de luz con LDR

¿Quieres ver en números cuánta luz hay en tu habitación? El LDR, una pequeña pieza sensible a la luz, mide la luz ambiente y el Arduino escribe el valor en la pantalla del ordenador cada medio segundo

4.6(81)
139
Lectura de la intensidad luminosa con el módulo LDR del ESP32
#113Principiante

Lectura de la intensidad luminosa con el módulo LDR del ESP32

Visualización en tiempo real en el monitor serie del valor bruto del ADC del módulo LDR y del porcentaje relativo de luz calculado a partir de referencias de oscuridad y claridad. Este porcentaje no es una medición en lux. Explica el funcionamiento de la calibración del módulo. Es recomendable realizarlo antes del Proyecto 10, ya que se utiliza para determinar el valor umbral.

4.6(36)
55
Uso del zumbador
#13Principiante

Uso del zumbador

¿Alguna vez te has preguntado cómo suena un timbre o una alarma? El Arduino hace pitar un pequeño zumbador a intervalos cortos: pip, silencio, pip, silencio. Ves cómo se crea sonido con electricidad y

4.7(89)
161
Temperatura y humedad con DHT11
#55Intermedio

Temperatura y humedad con DHT11

¿Qué temperatura hace en tu habitación, y el aire está húmedo o seco? El sensor DHT11 mide la temperatura y la humedad, y el Arduino escribe los valores en una pantalla LCD de dos líneas, actualizándo

4.7(47)
73
Voltímetro digital con pantalla LCD
#56Intermedio
Premium

Voltímetro digital con pantalla LCD

Descubre cuánto voltaje da una pila o un adaptador con un medidor hecho por ti. Un divisor de dos resistencias baja la tensión alta a un nivel que la placa puede leer, y el Arduino calcula el valor re

4.5(27)
36
ESP32 HTTPClient + API del tiempo (con pantalla OLED SSD1306)
#134Avanzado
Premium

ESP32 HTTPClient + API del tiempo (con pantalla OLED SSD1306)

En este proyecto, utilizamos el ESP32 para realizar una llamada a la API REST de la información meteorológica (wttr.in) a través de Internet, utilizando HTTPClient.h. A diferencia de la versión anterior con pantalla LCD I2C, aquí utilizamos una pantalla OLED SSD1306 (128×64), lo que permite utilizar fuentes grandes, iconos vectoriales y una presentación visual muy rica en una sola pantalla. Para indicar el tiempo, se dibujan en tiempo de ejecución 7 iconos vectoriales diferentes (soleado, parc

4.3(10)
19
Modo de suspensión profunda del ESP32 — Ahorro de batería (OLED + contador en RTC RAM)
#135Avanzado
Premium

Modo de suspensión profunda del ESP32 — Ahorro de batería (OLED + contador en RTC RAM)

El modo «Deep Sleep» del ESP32 reduce el consumo de corriente a unos 10 µA al desactivar la CPU y la mayoría de los periféricos, lo cual es de vital importancia para los dispositivos IoT que funcionan con pilas. En este proyecto utilizamos tres fuentes de activación diferentes: el temporizador (15 s), un pin externo (EXT0 — botón GPIO 33) y el sensor táctil (T0/GPIO 4). Las variables marcadas con RTC_DATA_ATTR se conservan durante el modo Deep Sleep: el contador de arranque, el contador de act

4.3(10)
16
Raspberry Pi Pico PWM LED Fade (Efecto Respiración)
#160Principiante

Raspberry Pi Pico PWM LED Fade (Efecto Respiración)

¡El LED ya no solo parpadea — respira! Con el PWM (modulación por ancho de pulso) del Raspberry Pi Pico, el brillo sube y baja lentamente. Aprende la clase machine.PWM, duty_u16() (brillo de 16 bits 0–65535), la frecuencia PWM, bucles range() y salida tipo analógica. Base para mezcla de color RGB, control de motores, servos y buzzer.

0.0(0)
4