Medir Frecuencia Cardíaca con Sensor KY-039 y Arduino
Componentes y suministros
| Componente | Cantidad |
|---|---|
| Arduino UNO | 1 |
| Sensor de latido KY-039 | 1 |
| Cables jumper (genéricos) | 1 set |
Sobre el proyecto
En el kit de 37 sensores para Arduino hay un sensor de latido. El nombre promete demasiado: no entrega un valor digital por I2C con las pulsaciones por minuto (BPM), sino solo un valor analógico (0-1023) que indica cuánta luz infrarroja recibe el fototransistor, es decir, cuánto lo sombrea el dedo.
En resumen: colocas el dedo entre el LED IR y el fototransistor. Tu latido dilata los vasos sanguíneos del dedo, filtrando la luz IR y creando una señal pulsátil. Este proyecto explica cómo convertir esa señal en una frecuencia cardíaca como 66 BPM.
Los pasos sencillos
Si lees y representas directamente los valores del KY-039, obtienes una señal muy ruidosa (enteros entre ~360-383).
Calculando la media de las últimas 20 lecturas, la curva se suaviza y ya se aprecian los latidos.
Hay un pequeño patrón zigzag causado por la luz artificial (50 Hz en Europa). Para eliminarlo, promediamos las lecturas durante ventanas de 20 ms (un ciclo de 50 Hz). En países de 60 Hz, usar 16.67 ms.
Con un array de 5 promedios de 20 ms, la señal queda limpia. Detectamos el latido buscando n valores crecientes consecutivos (umbral 5). Al detectar la subida, medimos el tiempo desde el latido anterior y calculamos BPM con promedio ponderado de los últimos 3 latidos.
Código final optimizado
#define samp_siz 4
#define rise_threshold 4
int sensorPin = A0;
void setup() {
Serial.begin(9600);
}
void loop() {
float reads[samp_siz], sum = 0;
long int now, ptr = 0;
float last, reader, start;
float first = 0, second = 0, third = 0, before = 0, print_value = 0;
bool rising = false;
int rise_count = 0;
int n;
long int last_beat = 0;
for (int i = 0; i < samp_siz; i++) reads[i] = 0;
while (1) {
// Promedio durante 20 ms para eliminar ruido 50 Hz
n = 0;
start = millis();
reader = 0;
do {
reader += analogRead(sensorPin);
n++;
now = millis();
} while (now < start + 20);
reader /= n;
// Mantener suma móvil de últimas lecturas
sum -= reads[ptr];
sum += reader;
reads[ptr] = reader;
last = sum / samp_siz;
// Detectar curva ascendente = latido
if (last > before) {
rise_count++;
if (!rising && rise_count > rise_threshold) {
rising = true;
first = millis() - last_beat;
last_beat = millis();
// Promedio ponderado: 40% último, 30% penúltimo, 30% antepenúltimo
print_value = 60000.0 / (0.4 * first + 0.3 * second + 0.3 * third);
Serial.print(print_value);
Serial.print('\n');
third = second;
second = first;
}
} else {
rising = false;
rise_count = 0;
}
before = last;
ptr++;
ptr %= samp_siz;
}
}
#define samp_siz 4
#define rise_threshold 4
int sensorPin = A0;
void setup() {
Serial.begin(9600);
}
void loop() {
float reads[samp_siz], sum = 0;
long int now, ptr = 0;
float last, reader, start;
float first = 0, second = 0, third = 0, before = 0, print_value = 0;
bool rising = false;
int rise_count = 0;
int n;
long int last_beat = 0;
for (int i = 0; i < samp_siz; i++) reads[i] = 0;
while (1) {
// Promedio durante 20 ms para eliminar ruido 50 Hz
n = 0;
start = millis();
reader = 0;
do {
reader += analogRead(sensorPin);
n++;
now = millis();
} while (now < start + 20);
reader /= n;
// Mantener suma móvil de últimas lecturas
sum -= reads[ptr];
sum += reader;
reads[ptr] = reader;
last = sum / samp_siz;
// Detectar curva ascendente = latido
if (last > before) {
rise_count++;
if (!rising && rise_count > rise_threshold) {
rising = true;
first = millis() - last_beat;
last_beat = millis();
// Promedio ponderado: 40% último, 30% penúltimo, 30% antepenúltimo
print_value = 60000.0 / (0.4 * first + 0.3 * second + 0.3 * third);
Serial.print(print_value);
Serial.print('\n');
third = second;
second = first;
}
} else {
rising = false;
rise_count = 0;
}
before = last;
ptr++;
ptr %= samp_siz;
}
}
Funciona bastante bien. El LED RX del Arduino parpadea sincronizado con el latido porque cada cálculo imprime por serie. Si el dedo se mueve, hay lecturas erróneas.
Mejoras futuras
- Calcular BPM sobre una ventana de 15 segundos: guardar 15 valores, calcular media, descartar los 5 más alejados y recalcular.
- Adaptar umbrales para diferentes morfologías de pulso (caída más pronunciada, taquicardia >180 BPM).
- Probar en más personas.
Esquemático
Conexión simple: KY-039 VCC → 5V, GND → GND, Signal → A0. Ver imagen adjunta.
Proceso de manufactura
- Transmisión de Datos de Temperatura en Tiempo Real desde ppDAQC Pi‑Plate a InitialState con Raspberry Pi Headless
- Construya un Sistema de Monitorización de Temperatura y Luz Multi‑Sensor con Raspberry Pi y DS18B20
- De Luz Solar de $4 a Nodo Sensor Inalámbrico Inteligente: MSP430 + nRF24L01+ para Monitorización de Jardín
- Sensor de ocupación para baño con Raspberry Pi: alertas por voz y SMS mediante Twilio
- Leer pulsos de frecuencia cardíaca con Windows 10 IoT Core en Raspberry Pi: guía paso a paso
- Tutorial sensor de luz Raspberry Pi: cablear LDR y leer niveles de luz con Python
- Sensor de Luz Digital – Enciende un LED con Luz Ambiental usando Windows 10 IoT Core
- Sistema de Monitorización Remota de Frecuencia Cardíaca con Alertas AWS IoT
- Sensores de Color: Funcionamiento y Aplicaciones Clave
- Sensor de Luz Ambiente OPT3007 Ultra-Fino – Medición Precisa para Dispositivos Inteligentes
- Sensores de Luz Ambiente: Funcionalidad, Diseño y Aplicaciones Clave