Monitor de Signos Vitales
Utilizando Protocolo MQTT
Monitor de Signos Vitales
Utilizando Protocolo MQTT
a. Primera etapa: Se llevó a cabo la recopilación de datos vinculados a los requisitos requeridos, así como la obtención de información de los equipos comerciales e investigaciones. La información recopilada ha sido fundamental para abordar diversas incógnitas, evaluar los resultados y contar con una orientación hacia los objetivos esperados.
b. Segunda etapa: Estudio del estado actual del sistema de salud en la Posta Médica José Olaya de Chiclayo.
c. Tercera etapa: Elección de componentes se llevó a cabo considerando las opciones disponibles en el mercado de dispositivos electrónicos, seleccionando aquellas que mejor se ajustaran a los objetivos establecidos.
d. Cuarta etapa: El software fue desarrollado utilizando el lenguaje de programación Arduino, así como la elaboración de una base de datos y la implementación de una página web.
i Arduino:
En el entorno de desarrollo para un módulo ESP8266 v3, se programó la placa para establecer la comunicación con dos módulos MAX30105, un sensor MLX90614, una pantalla OLED y un termistor de 80kOhm. Inicialmente, se configuraron los pines digitales GPIO4 y GPIO5, así como el pin analógico A0, para interactuar con dichos módulos. Al iniciar el código, se declararon las variables necesarias y se definieron los pines de entrada y salida, incluyendo el pin analógico A0.
Luego, en la función de configuración inicial (setup), se establecen los pines GPIO4 y GPIO5 como salidas (o entradas, dependiendo de la configuración específica requerida para los módulos). Además, se configuró el ESP8266 v3 para iniciar la comunicación serial a una velocidad de 115200 baudios, permitiendo el envío y recepción de datos a través de este canal. De este modo, se preparó la placa ESP8266 v3 para procesar las señales provenientes de los módulos MAX30105, MLX90614, y el termistor de 80kOhm, monitoreando los pines analógicos y digitales definidos.
conexiones del circuito
e. Código en Arduino:
En primer lugar, el programa se encarga de verificar la conexión WiFi y el estado de conexión al servidor MQTT. Si la conexión se pierde, el sistema intentará reconectarse automáticamente. Una vez asegurada la conectividad, el programa procede a la adquisición de datos desde los sensores conectados al sistema. Se utiliza el sensor MAX30105 para medir la frecuencia cardíaca (BPM) y el nivel de saturación de oxígeno en la sangre (SpO2), mientras que el sensor MLX90614 se encarga de medir la temperatura corporal.
Descarga codigo completo AQUI
#include <Arduino.h> // Librería principal de Arduino
#include <U8g2lib.h> // Librería para manejar la pantalla OLED
#include <Wire.h> // Librería para la comunicación I2C
// Librería para sensor frecuencia cardíaca y SpO2
#include "MAX30105.h" // Librería para el sensor MAX30105
#include "spo2_algorithm.h"// Algoritmo para calcular SpO2
// Librería para temperatura corporal
#include <Adafruit_MLX90614.h>
// Librería para el ESP8266 y EMQX:MQTT
#include <ESP8266WiFi.h> // Librería para la conectividad WiFi en el ESP8266
#include <PubSubClient.h> // Librería para el cliente MQTT
/////definimos los pines para el ESP8266//////////////
#define A0 A0 // ADC0
#define D1 5 // GPIO5
#define D2 4 // GPIO4
#define SDA_PIN D2 // SDA
#define SCL_PIN D1 // SCL
// Configuración WiFi y MQTT
const char* ssid = "******"; // Nombre de Usuario de wifi
const char* password = "******"; // Contraseña de tu wifi
const char* server = "broker.emqx.io"; // Broker de MQTT
int port = 1883; // Puerto MQTT
WiFiClient espClient;
PubSubClient mqttClient(espClient);
// Inicializa la pantalla OLED
U8G2_SSD1327_MIDAS_128X128_1_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE);
// Inicialización de los sensores
MAX30105 particleSensor;
Adafruit_MLX90614 mlx = Adafruit_MLX90614();
// Constantes y variables para el cálculo de la frecuencia cardíaca y SpO2
const byte RATE_SIZE = 4; // Tamaño del buffer para promediar los latidos
int32_t heartRate; // Variable para almacenar la frecuencia cardíaca en BPM
int32_t spo2; // Valor de SpO2
int8_t validHeartRate; // Indicador de validez del cálculo de la frecuencia cardíaca
int8_t validSPO2; // Indicador de validez del cálculo de SpO2
// Variables para la medición de temperatura
float temp;
// Variables para la frecuencia respiratoria
float R1 = 100000; // Resistencia fija del divisor de tensión
float logR2, R2, TEMPERATURA;
float c1 = 2.114990448e-03, c2 = 0.3832381228e-04, c3 = 5.228061052e-07; // Coeficientes de S-H
int cont = 0;
int d = 0;
int c = 0;
float actual = 0;
float anterior = 0;
unsigned long tiempo = 0;
unsigned long tiempo2 = 0;
int frecuenciaRespiratoria = 0;
// Tamaño del buffer de la gráfica de frecuencia respiratoria
const int bufferSize = 128;
int frecuenciaBuffer[bufferSize];
int bufferIndex = 0;
// Definiciones de la imagen del corazón para la pantalla OLED
#define heart_width 16
#define heart_height 16
const unsigned char heart_bits[] PROGMEM = {
0xf8 , 0x00 , 0x7c , 0x01 , 0xfe , 0x02 , 0xfe , 0x05 , 0xfe , 0x0b , 0xfc , 0x1d , 0xf8 , 0x3e , 0x78 , 0x3f , 0xf8 , 0x7e , 0xfc ,
0x3d , 0xfe , 0x1b , 0xfe , 0x0d , 0xfe , 0x06 , 0x7e , 0x03 , 0xfc , 0x00 , 0x38 , 0x00 };
int bpmBuffer[bufferSize]; // Buffer para la gráfica de BPM
#define GRAPH_WIDTH 128
#define GRAPH_HEIGHT 30
#define GRAPH_OFFSET_X 0
#define GRAPH_OFFSET_Y_BPM 95
#define GRAPH_OFFSET_Y_RESP 55
// Función para obtener la temperatura para el cálculo de frecuencia respiratoria
float obtenerTemp();
// Función de conexión WiFi
void wifiInit() {
Serial.print("Conectándose a ");
Serial.println(ssid);
WiFi.begin(ssid, password); // Comienza la conexión WiFi
// Espera hasta que la conexión WiFi se establezca
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("Conectado a WiFi");
Serial.println("Dirección IP: ");
Serial.println(WiFi.localIP());
}
// Callback para MQTT
void callback(char* topic, byte* payload, unsigned int length) {
// Manejar mensajes recibidos en los tópicos suscritos
}
// Función para reconectar al servidor MQTT
void reconnect() {
while (!mqttClient.connected()) {
Serial.print("Intentando conectarse MQTT...");
if (mqttClient.connect("arduinoClient")) {
Serial.println("Conectado");
mqttClient.subscribe("Entrada/01"); // Suscribirse a un tópico
} else {
Serial.print("Fallo, rc=");
Serial.print(mqttClient.state());
Serial.println(" intentar de nuevo en 5 segundos");
delay(5000);
}
}
}
// Función para publicar datos en el servidor MQTT
void mqttPublish() {
if (!mqttClient.connected()) {
reconnect(); // Si no está conectado, intenta reconectar
}
mqttClient.loop(); // Mantiene la conexión MQTT activa
// Publicar BPM y SpO2
char datos1[50]; // Buffer para almacenar el mensaje a publicar
sprintf(datos1, "BPM: %d, SpO2: %d", heartRate, spo2);
mqttClient.publish("sensor/cardio", datos1);
// Publicar Temp y Frecuencia Respiratoria
char datos2[50]; // Buffer para almacenar el mensaje a publicar
sprintf(datos2, "Temp: %.2f C, F.Resp: %d rpm", temp, frecuenciaRespiratoria);
mqttClient.publish("sensor/temperatura_respiratoria", datos2);
}
// Función de configuración
void setup() {
pinMode(LED_BUILTIN, OUTPUT);
Serial.begin(115200); // Inicializa la comunicación serie
Serial.println("Initializing...");
Wire.begin(SDA_PIN, SCL_PIN); // Inicializa la comunicación I2C
mlx.begin(); // Inicializa el sensor de temperatura MLX90614
if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {
Serial.println("MAX30105 was not found. Please check wiring/power.");
while (1);
}
particleSensor.setup(); // Configura el sensor MAX30105 con los ajustes predeterminados
particleSensor.setPulseAmplitudeRed(0x0A); // Configura la amplitud del LED rojo
particleSensor.setPulseAmplitudeGreen(0); // Apaga el LED verde
wifiInit(); // Inicializa la conexión WiFi
mqttClient.setServer(server, port); // Configura el servidor MQTT
mqttClient.setCallback(callback); // Configura el callback de MQTT
// Inicializar el buffer de la gráfica
for (int i = 0; i < bufferSize; i++) {
frecuenciaBuffer[i] = 0;
bpmBuffer[i] = 0;
}
u8g2.begin(); // Inicializa la pantalla OLED
}
// Función del bucle principal
void loop() {
mqttPublish(); // Publica los datos en el servidor MQTT
//frecuencia respiratoria
if (millis() - tiempo >= 1000) {
tiempo2 += 1;
tiempo = millis();
if (tiempo2 >= 240) tiempo2 = 0;
}
if (tiempo2 <= 60) {
TEMPERATURA = obtenerTemp(); // Usar el sensor en A0 para frecuencia respiratoria
actual = TEMPERATURA;
if (actual > anterior + 1.0) {
anterior = actual;
c = 1;
}
if (actual < anterior - 1.0) {
anterior = actual;
c = 0;
}
if (c != d) {
cont += 1; // Contador de respiraciones
d = c;
}
frecuenciaRespiratoria = cont / 2; // Divide entre 2 para obtener el número de respiraciones completas
if (frecuenciaRespiratoria == 0) {
for (int i = 0; i < bufferSize; i++) {
frecuenciaBuffer[i] = 0;
}
} else {
frecuenciaBuffer[bufferIndex] = map(frecuenciaRespiratoria, 0, 60, 20, 100);
}
bufferIndex = (bufferIndex + 1) % bufferSize;
for (int i = 0; i < bufferSize - 1; i++) {
frecuenciaBuffer[i] = frecuenciaBuffer[i + 1];
}
Serial.print("Frecuencia respiratoria: ");
Serial.print(frecuenciaRespiratoria);
Serial.println(" respiraciones/minuto");
}
// Calcula SpO2 y BPM directamente desde la función de SpO2
readSpO2();
if (heartRate > 0) {
bpmBuffer[bufferIndex] = map(heartRate, 0, 255, 20, 100); // Mapear el BPM al rango de la gráfica
} else {
bpmBuffer[bufferIndex] = 0; // Si no se detecta, dibujar línea en 0
}
bufferIndex = (bufferIndex + 1) % bufferSize;
for (int i = 0; i < bufferSize - 1; i++) {
bpmBuffer[i] = bpmBuffer[i + 1];
}
Serial.print("BPM: ");
Serial.println(heartRate); // Muestra BPM calculado desde SpO2
Serial.print("SpO2: ");
Serial.println(spo2);
// Mostrar la temperatura en el serial
temp = mlx.readObjectTempC();
if (isnan(temp) || temp > 100 || temp < -10) {
Serial.println("Error en la lectura de temperatura");
} else {
Serial.print("Temperatura: ");
Serial.println(temp);
}
// Actualizar la pantalla OLED
u8g2.firstPage();
do {
u8g2.setFont(u8g2_font_6x10_tf);
u8g2.setDrawColor(1);
// Mostrar BPM calculado desde SpO2
u8g2.drawStr(60, 16, "BPM: ");
u8g2.setCursor(80, 16);
u8g2.print(heartRate);
u8g2.drawStr(60, 26, "SpO2: ");
u8g2.setCursor(90, 26);
u8g2.print(spo2);
u8g2.drawStr(110, 26, "%");
u8g2.drawXBMP(110, 0, heart_width, heart_height, heart_bits);
u8g2.drawStr(0, 93, "Grafico Frecuencia cardíaca");
// Dibujar la gráfica de BPM
int xPosBPM = 2;
int prevYBPM = GRAPH_OFFSET_Y_BPM + GRAPH_HEIGHT - constrain(bpmBuffer[0], 0, GRAPH_HEIGHT); // Ajustar la posición vertical de la gráfica y limitar los valores
for (int i = 1; i < bufferSize; i++) {
int yPosBPM = GRAPH_OFFSET_Y_BPM + GRAPH_HEIGHT - constrain(bpmBuffer[i], 0, GRAPH_HEIGHT); // Ajustar la posición vertical de la gráfica y limitar los valores
u8g2.drawLine(xPosBPM, prevYBPM, xPosBPM + 5, yPosBPM); // Hacer la onda más ancha
prevYBPM = yPosBPM;
xPosBPM += 5; // Incrementar posición x más para hacer la onda más ancha
}
// Mostrar la temperatura
u8g2.setCursor(0, 8);
u8g2.print("TEMPERATURA");
u8g2.setCursor(20, 25);
u8g2.print(temp, 1);
u8g2.setCursor(50, 25);
u8g2.print("C");
// Dibujar termómetro
u8g2.drawDisc(5, 38, 5, U8G2_DRAW_ALL);
u8g2.drawRFrame(3, 8, 5, 33, 2);
for (int i = 8; i <= 34; i += 2) {
u8g2.drawLine(8, i, 9, i);
}
int tempHeight = map(temp, 0, 100, 8, 30); // de 20 a 40 celsios
u8g2.drawLine(5, 35, 5, 35 - tempHeight);
u8g2.setCursor(0, 50);
u8g2.print("F.RESP=");
u8g2.setCursor(45, 50);
u8g2.print(frecuenciaRespiratoria);
u8g2.print("rpm");
u8g2.setCursor(75, 50);
u8g2.print("Tem:");
u8g2.setCursor(97, 50);
u8g2.print(TEMPERATURA);
u8g2.drawFrame(0, 55, 128, 30); // Dibujar el rectángulo
// Dibujar la gráfica de la frecuencia respiratoria
int xPosResp = 2;
int prevYResp = GRAPH_OFFSET_Y_RESP + GRAPH_HEIGHT - constrain(frecuenciaBuffer[0], 0, GRAPH_HEIGHT); // Ajustar la posición vertical de la gráfica y limitar los valores
for (int i = 1; i < bufferSize; i++) {
int yPosResp = GRAPH_OFFSET_Y_RESP + GRAPH_HEIGHT - constrain(frecuenciaBuffer[i], 0, GRAPH_HEIGHT); // Ajustar la posición vertical de la gráfica y limitar los valores
u8g2.drawLine(xPosResp, prevYResp, xPosResp + 5, yPosResp); // Hacer la onda más ancha
prevYResp = yPosResp;
xPosResp += 5; // Incrementar posición x más para hacer la onda más ancha
}
} while (u8g2.nextPage());
delay(50); // Espera entre lecturas
}
void readSpO2() {
int32_t bufferLength = 100; // Declaración de la variable bufferLength
uint32_t irBuffer[100]; // Datos del sensor de LED infrarrojo
uint32_t redBuffer[100]; // Datos del sensor de LED rojo
for (byte i = 0; i < bufferLength; i++) {
while (particleSensor.available() == false) {
particleSensor.check();
}
redBuffer[i] = particleSensor.getRed();
irBuffer[i] = particleSensor.getIR();
particleSensor.nextSample();
}
maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);
if (!validSPO2) {
spo2 = 0; // Si no hay dedo o no se puede calcular, resetear SpO2
heartRate = 0; // También resetear el BPM
}
}
// Función para obtener la temperatura del sensor
float obtenerTemp() {
int Vo = analogRead(A0); // lectura de A0
R2 = R1 * (1023.0 / (float)Vo - 1.0); // conversión de tensión a resistencia
logR2 = log(R2); // logaritmo de R2 necesario para ecuación
TEMPERATURA = (1.0 / (c1 + c2 * logR2 + c3 * logR2 * logR2 * logR2)); // ecuación S-H
TEMPERATURA = TEMPERATURA - 273.15; // Kelvin a Centígrados (Celsius)
return TEMPERATURA;
}
Prototipo