Owllink
Desarrollado por: Isam El Mourabet Ben Ahmed, Alexander Mendoza Mendoza y Santiago Nicolás Díaz Tituaña
1- Introducción
Este proyecto desarrolla un sistema portátil y central que permite medir la frecuencia cardíaca (PPM) y la saturación de oxígeno (SpO2) en tiempo real, transmitiendo los datos mediante Bluetooth a un dispositivo externo. La monitorización de parámetros vitales como la frecuencia cardíaca y la saturación de oxígeno en sangre es esencial en aplicaciones médicas, deportivas y de salud en general.
Por lo tanto, se han diseñado dos pulseras para realizar las mediciones, una que sirve como una central estática de medición, y otra que sirve como una pulsera portátil. También se ha diseñado una app totalmente compatible con Owllink para dispositivos iOS, en la que ha un botón para empezar la medición, y además se puede consultar en el apartado de estadísticas todas las mediciones realizadas por el usuario, además de las mediciones realizadas por su círculo familiar (en la pestaña de otras mediciones), ya que se guardan conjuntamente en una base de datos compartida.
2- Objetivo del Proyecto
Objetivo General:
- Diseñar y construir un sistema portátil y central para monitorear la frecuencia cardíaca y la saturación de oxígeno en tiempo real, con comunicación vía Bluetooth.
Objetivos Específicos:
- Integrar el sensor MAX30100 y MAX30102 para la captura de datos biométricos.
- Implementar un programa en Arduino para procesar y transmitir los datos.
- Configurar el módulo HC-06 para enviar información a la aplicación y pantalla.
- Demostrar el funcionamiento del sistema en un prototipo funcional.
3- Componentes del Proyecto
Hardware
- Elego Nano y Elego UNO R3 :
- Controlador principal para procesar los datos y gestionar las comunicaciones.
- Ideal por su tamaño compacto y compatibilidad con múltiples bibliotecas.
- Sensor MAX30100 y MAX30102:
- Captura señales de luz roja e infrarroja reflejadas por los tejidos.
- Calcula parámetros vitales utilizando algoritmos avanzados.
- Módulo Bluetooth HC-06:
- Transmite los datos procesados al dispositivo electrónico.
- Pantalla Oled:
- Visualización de los datos en la muñeca.
- Switch:
- Encender y apagar Owllink.
- Módulo de carga:
- TP–4056
- Otros Componentes:
- Resistencias y cables para conexiones.
- Fuente de alimentación (USB o batería de litio).
Software
- Arduino IDE: Desarrollo y carga del código al Arduino.
- Librerías Utilizadas:
- MAX30105.h: Manejo del sensor MAX30102.
- heartRate.h: Algoritmo para calcular la frecuencia cardíaca.
- spo2_algorithm.h: Algoritmo para calcular SpO2.
- SoftwareSerial.h: Comunicación con el módulo HC-06.
- Aplicación multispositivo:
- Desarrollada en Swift para dispositivos iOS y macOS. (iPhone, iPad y Mac)
- Base de datos Firebase de Google para almacenar los usuarios y las mediciones obtenidas.
- Modelaje 3D: Desarrollado en Freecad para la caja de Owllink
4- Diseño del Sistema
Esquema de Conexiones
- MAX30100 y MAX30102:
- VIN: Conectado a 3.3V del Arduino Nano.
- SDA y SCL: Conectados a los pines A4 (SDA) y A5 (SCL) del Arduino.
- GND: Conectado a tierra.
- HC-06:
- RX: Conectado al pin digital 10 (TX del Arduino).
- TX: Conectado al pin digital 11 (RX del Arduino).
- VIN: Conectado a 5V.
- GND: Conectado a tierra.
- Pantalla Oled:
- VIN: Conectado a 5V del Arduino Nano.
- SDA y SCL: Conectados a los pines A4 (SDA) y A5 (SCL) del Arduino.
- GND: Conectado a tierra.
Diagrama de Conexiones:
5- Retos y Soluciones
Problema: Variaciones en lecturas por presión inconsistente.
- Solución: Mensajes de advertencia cuando la muñeca no está correctamente colocada.
Problema: Problemas de memoria en Arduino Nano.
- Solución: Reducir el tamaño del buffer de muestras para optimizar el uso de memoria
Problema: Interferencias en la comunicación Bluetooth.
- Solución: Implementar limpieza y validación de los comandos recibidos vía Bluetooth.
6- Software
La generación de esta imagen se ha creado mediante un mapa de bits, que se importa como biblioteca («bitmaps.h») en el código de arduino para la pulsera portátil. Este código se comunica con la app de Owllink mediante bluetooth. Al recibir el comando «START», la pulsera va tomando las mediciones correspondientes y las envía a la app con un string, que posteriormente la app separa para sacar los datos correctos.
Código para la pulsera portátil
#include <Wire.h>
#include "MAX30100_PulseOximeter.h"
#include <U8glib.h>
#include <SoftwareSerial.h>
#include "bitmaps.h"
// Configuración de la libreria u8glib para la pantalla
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
//Configura las dimensiones de la pantalla
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
#define ENABLE_MAX30100 1
//Define cada cuanto el sensor obtendrá los datos leídos
#define REPORTING_PERIOD_MS 2000
PulseOximeter pox;
//Define si se ha recibido un comando para empezar la medición, si es TRUE empieza a mandar datos por bluetooth caso contrario no.
bool isSending = false;
// Configuración de Bluetooth
SoftwareSerial bluetooth(9, 10); // D9 = RX, D10 = TX
uint32_t tsLastReport = 0;
void onBeatDetected() {
Serial.println("Latido detectado!");
}
void setup() {
Serial.begin(115200);
bluetooth.begin(9600);
if (!pox.begin()) {
Serial.println("Fallo inicialización de MAX30100");
while (1);
} else {
Serial.println("MAX30100 inicializado con éxito");
}
u8g.firstPage();
do {
//Dibuja la pantalla de inicio de la pulsera
u8g.drawBitmapP(2, 2, 16, 64, bitmap1);
} while (u8g.nextPage());
delay(2000);
pox.setOnBeatDetectedCallback(onBeatDetected);
}
void loop() {
pox.update();
//Llama a las funciones getHeartRate y getSpO2 para recibir los valores
int bpm = pox.getHeartRate();
int spo2 = pox.getSpO2();
// Verificar si se ha recibido un comando Bluetooth
if (bluetooth.available()) {
String command = bluetooth.readStringUntil('\n'); // Lee el comando
command.trim();
if (command == "START") {
isSending = true;
Serial.println("Comando START recibido");
} else if (command == "STOP") {
isSending = false;
Serial.println("Comando STOP recibido..");
}
}
//Si no se ha empezado la medición (Aún no se ha recibido el comando START)
if (!isSending) {
//Dibuja
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setPrintPos((128 - u8g.getStrWidth("Pulsa empezar")) / 2, 30);
u8g.print("Pulsa empezar");
u8g.setPrintPos((128 - u8g.getStrWidth("analisis")) / 2, 50);
u8g.print("analisis");
} while (u8g.nextPage());
return;
}
//Si los valores de spo2 y de bpm son validos y ademas se ha enviado el comando START
if (millis() - tsLastReport > REPORTING_PERIOD_MS && bpm > 0 && spo2 > 0 && isSending) {
//Muestra en el monitor serial los datos que se van a mostrar por pantalla y que se van enviar por bluetooth con el fin de que
//esten sincronizados
Serial.print("LPM: ");
Serial.print(bpm);
Serial.print("SpO2: ");
Serial.println(spo2);
//Se envian los datos por Bluetooth
String data = "HR:" + String(bpm) + ",O2:" + String(spo2);
bluetooth.println(data);
//Y se muestran los datos por la pantalla
mostrarDatos(bpm, spo2);
tsLastReport = millis();
}
}
void mostrarDatos(int bpm, int spo2) {
u8g.firstPage();
do {
u8g.setFont(u8g_font_10x20);
u8g.setPrintPos(20, 30);
u8g.print("PPM: ");
u8g.setPrintPos(80, 30);
u8g.print(bpm);
u8g.setPrintPos(20, 60);
u8g.print("SpO2: ");
u8g.setPrintPos(80, 60);
u8g.print(spo2);
u8g.print("%");
} while (u8g.nextPage());
}
Código para la pulsera con centralita.
Este es el código de arduino para la pulsera con centralita, muy parecido al anterior, aunque este no tiene el mapa de bits, y usa el MAX30102.
#include <Wire.h>
#include "MAX30105.h"
#include <U8glib.h>
#include "heartRate.h"
#include <SoftwareSerial.h>
// Configuración de Bluetooth
SoftwareSerial bluetoothSerial(10, 11); // RX, TX: Pines digitales del Arduino para comunicación Bluetooth
// Inicializar sensor MAX30105
MAX30105 particleSensor;
// Configuración de la pantalla OLED
U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE);
// Parámetros para la medición
const byte RATE_SIZE = 4;
byte rates[RATE_SIZE]; // Buffer para promediar BPM
byte rateSpot = 0;
long lastBeat = 0; // Tiempo del último latido detectado
float beatsPerMinute; // BPM calculado
int beatAvg = 0; // Promedio de BPM
int spo2 = 98; // Nivel de SpO2 calculado inicialmente
bool dataChanged = false; // Bandera para cambios en los datos
bool isSending = false; // Control del envío de datos mediante comando START/STOP
void setup() {
Serial.begin(115200);
bluetoothSerial.begin(9600); // Inicializar Bluetooth
// Mensajes iniciales
Serial.println("HC-06 listo. Escribe 'START' para iniciar medición.");
// Inicializar MAX30105
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
Serial.println("MAX30105 no encontrado. Verifica las conexiones.");
while (1);
}
particleSensor.setup();
particleSensor.setPulseAmplitudeRed(0x0A); // Encender LED rojo con baja intensidad
particleSensor.setPulseAmplitudeGreen(0); // Apagar LED verde
// Mostrar mensaje inicial en pantalla
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setPrintPos((128 - u8g.getStrWidth("Pulsa empezar")) / 2, 30);
u8g.print("Pulsa empezar");
u8g.setPrintPos((128 - u8g.getStrWidth("analisis")) / 2, 50);
u8g.print("analisis");
} while (u8g.nextPage());
}
void loop() {
// Verificar si se recibe un comando por Bluetooth
if (bluetoothSerial.available()) {
// Leer comando completo
String command = bluetoothSerial.readStringUntil('\n');
// Eliminar espacios y caracteres adicionales
command.trim();
if (command == "START") {
isSending = true; // Activar envío de datos
Serial.println("Comando START recibido. Medición habilitada.");
} else if (command == "STOP") {
isSending = false; // Desactivar envío de datos
Serial.println("Comando STOP recibido. Medición detenida.");
}
}
// Mostrar mensaje inicial si no se ha recibido START
if (!isSending) {
u8g.firstPage();
do {
u8g.setFont(u8g_font_9x15);
u8g.setPrintPos((128 - u8g.getStrWidth("Pulsa empezar")) / 2, 30);
u8g.print("Pulsa empezar");
u8g.setPrintPos((128 - u8g.getStrWidth("analisis")) / 2, 50);
u8g.print("analisis");
} while (u8g.nextPage());
return;
}
// Leer valores del sensor
long irValue = particleSensor.getIR();
if (checkForBeat(irValue)) {
long delta = millis() - lastBeat;
lastBeat = millis();
beatsPerMinute = 60 / (delta / 1000.0);
if (beatsPerMinute < 255 && beatsPerMinute > 20) {
// Guardar BPM en buffer
rates[rateSpot++] = (byte)beatsPerMinute;
// Ciclar índice del buffer
rateSpot %= RATE_SIZE;
// Calcular promedio de BPM
beatAvg = 0;
for (byte x = 0; x < RATE_SIZE; x++) {
beatAvg += rates[x];
}
beatAvg /= RATE_SIZE;
// Calcular nivel de SpO2
// Ajustar fórmula para el cálculo real
spo2 = particleSensor.getRed() / 1000;
if (spo2 > 100) spo2 = 100; // Limitar máximo al 100%
if (spo2 < 90) spo2 = 90; // Limitar mínimo al 90%
dataChanged = true;
}
}
// Mostrar BPM y SpO2 en pantalla
if (dataChanged) {
u8g.firstPage();
do {
u8g.setFont(u8g_font_10x20);
u8g.setPrintPos(10, 30);
u8g.print("BPM");
u8g.setPrintPos(20, 60);
u8g.print(beatAvg);
u8g.setPrintPos(80, 30);
u8g.print("SpO2");
u8g.setPrintPos(90, 60);
u8g.print(spo2);
} while (u8g.nextPage());
// Enviar datos por Bluetooth
String data = "HR:" + String(beatAvg) + ",O2:" + String(spo2);
bluetoothSerial.println(data);
Serial.println("Enviado por Bluetooth: " + data);
dataChanged = false;
}
}
Código para la app de iOS
Hemos desarrollado una app para iOS y macOS totalmente compatible y diseñada a medida para que funcione lo mejor posible con las pulseras de Owllink. Usando la base de datos y la autenticación de firebase se consigue un mejor funcionamiento de los datos del usuario.
7- Funcionamiento
8- Presupuesto
Componente | Cantidad | Precio Unitario | Precio Total |
Elego UNO R3 | 1 | 0 € | 0 € |
Elego Nano | 1 | 7 € | 7 € |
MAX30102 | 2 | 4,55 € | 9,10 € |
MAX30100 | 1 | 1,89 € | 1,89 € |
HC-06 | 2 | 2,46 € | 4,92 € |
Pantalla OLED 0,92’ | 2 | 3,39 € | 6,78 € |
Pantalla LCD 1,8’ | 1 | 3,59 € | 3,59 € |
Switch | 25 | 0,04 € | 0,99 € |
Módulo de carga | 1 | 2,02 € | 2,02 € |
Pila de litio | 1 | 5 € | 5 € |
10m de cable de 4 hilos (0,5mm) | 1 | 3,69 € | 3,69 € |
Correa de nylon | 1 | 1,79 € | 1,79 € |
Correa metálica | 1 | 0 € | 0 € |
Cinta adhesiva aluminio-vidrio | 1 | 0 € | 0 € |
PCB de baquelita 7×9 cm | 1 | 4 € | 4€ |
Pilas de 3,3 V | 2 | 1 € | 2 € |
Caja de madera | 1 | 2,15 € | 2,15 € |
Laca de pintura negra | 1 | 2,10 € | 2,10 € |
Estaño | 1 | 3,5 € | 3,5 € |
Protoboard | 3 | 0 € | 0 € |
Caja impresa en 3D | 2 | 0 € | 0 € |
TOTAL | 60,52 € |
9- Conclusión
Owllink se ha desarrollado con los mejores componentes a nuestro alcance, ajustado al máximo el software para obtener la mayor precisión posible, sin olvidarnos de a quién va dirigido, como deportistas, gente mayor y gente a la que apreciamos, por lo tanto, la aplicación y las pulseras son minimalistas y sencillas para la fácil manipulación por parte de los usuarios.