Owllink

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.

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.
  • 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: 
    • TP4056
  • Otros Componentes:
    • Resistencias y cables para conexiones.
    • Fuente de alimentación (USB o batería de litio).
  • 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

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:

Proceso de soldadura.
Prototipo de la pulsera portátil.
Ensamblaje de la pulsera con central estática.
Pulsera de la central estática funcionando.
Comparación de la medición con el Apple Watch.
Pulsera portátil mostrando la pantalla de carga.

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.

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.

#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());
}

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;
  }
}

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.

ComponenteCantidadPrecio UnitarioPrecio Total
Elego UNO R310 €0 €
Elego Nano17 €7 € 
MAX3010224,55 €9,10 €
MAX3010011,89 €1,89 €
HC-0622,46 €4,92 €
Pantalla OLED 0,92’23,39 €6,78 €
Pantalla LCD 1,8’13,59 €3,59 €
Switch250,04 €0,99 €
Módulo de carga12,02 €2,02 €
Pila de litio15 €5 €
10m de cable de 4 hilos (0,5mm)13,69 €3,69 €
Correa de nylon11,79 €1,79 €
Correa metálica10 €0 €
Cinta adhesiva aluminio-vidrio10 €0 €
PCB de baquelita 7×9 cm14 €4€
Pilas de 3,3 V21 € 2 €
Caja de madera12,15 €2,15 €
Laca de pintura negra12,10 €2,10 €
Estaño13,5 €3,5 €
Protoboard30 €0 €
Caja impresa en 3D20 €0 €
TOTAL  60,52 €

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.

También te podría gustar...

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *