Está en la página 1de 34

Universidad Autónoma de Nuevo León

Facultad de Medicina
Telemedicina

Actividad Final
Producto Integrador de Aprendizaje (PIA)

Fecha de Inicio Fecha de Entrega

24 de Mayo del 2023 5 de Junio del 2023

Profesor:
Ing. Manuel Francisco Vicencio
Ing. Oscar E. Cervantes García

Integrantes:

Jesús Alejandro Juárez González 1618144

Daniela Morales Leal 1877360

VÍctor Manuel Castañeda de León 1378118

Bryan David Garrido 1841262

Juan Carlos Jimenez


Resumen
Resumen conciso del proyecto y los logros destacados.

1
Introducción
El presente reporte técnico tiene como objetivo proporcionar una visión
general de la implementación de una plataforma de telemedicina en un ambiente
hospitalario. Dicha iniciativa se desarrolla a partir de la intención de poder brindar
servicios médicos remotos, los cuales permitan a los pacientes acceder a una
atención médica que sea eficiente y conveniente, sea desde la comodidad de sus
hogares o bien, en cualquier ubicación disponible en el horario oportuno.

La plataforma de telemedicina se diseñó con la intención de facilitar la


comunicación entre los pacientes y personal médico. Lo anterior da pie a consultas
virtuales, seguimiento de tratamientos y gestión de historial médico de manera
segura y confidencial. Dentro del entorno hospitalario, se integran distintos
miembros clave, ya sea como usuarios o proveedores de soporte para brindar este
servicio; médicos generales, especialistas, enfermeros, personal administrativo,
soporte TI e ingenieros pueden participar en este proyecto con un fin común.

El siguiente reporte aborda los aspectos clave del proyecto, tales como la
arquitectura de la plataforma, los procesos de integración entre los usuarios, las
funciones principales, los desafíos más probables y consideraciones de seguridad.
Con ello, se espera brindar una comprensión clara y detallada de la plataforma, así
como de los beneficios y mejoras que se esperan.

Descripción del escenario


En un entorno hospitalario, se busca implementar una plataforma de
telemedicina para mejorar el proceso de programación y consulta virtual de
pacientes que requieren cirugía. El objetivo es facilitar el acceso a servicios de
atención médica, agilizar el proceso de programación de citas y mejorar la
comunicación entre el personal médico y los pacientes.

La plataforma de telemedicina ofrece las siguientes funcionalidades:

Programación de citas virtuales: Los pacientes que requieren cirugía pueden


programar citas virtuales con su médico especialista a través de la plataforma.
Durante la cita virtual, pueden discutir los detalles de la cirugía, compartir
información médica relevante y aclarar cualquier duda o inquietud.

Registro de información del paciente: La plataforma permite a los pacientes


proporcionar información detallada sobre su historial médico, incluyendo alergias,
medicamentos actuales y resultados de pruebas médicas previas. Esta información
es fundamental para que el médico pueda tomar decisiones informadas y garantizar
la seguridad del paciente durante el proceso quirúrgico.

2
Monitoreo de equipo médico asociado: La plataforma recopila
constantemente datos de temperatura y humedad de la máquina de anestesia
asignada a cada paciente que pasa por proceso de cirugía. Esto proporciona un
monitoreo continuo de las condiciones ambientales en las que opera la máquina
durante el procedimiento quirúrgico. Cualquier desviación de los rangos predefinidos
puede alertar al personal médico y de mantenimiento sobre posibles problemas o
necesidades de ajuste.

Análisis de radiografías de tórax mediante IA: Como parte del proceso


preoperatorio, los pacientes deben proporcionar radiografías de tórax recientes a
través de la plataforma de telemedicina. Estas imágenes son analizadas por
algoritmos de IA especializados para identificar posibles complicaciones pulmonares
o anomalías que puedan afectar la cirugía. Todo lo anterior permite al paciente
acudir a cualquier establecimiento de salud que le proporcione dicho servicio, según
su disponibilidad de tiempo y cercanía. Los resultados del análisis de las imágenes
se presentan después al médico o especialista durante una cita virtual, para
después emitir un diagnóstico más completo y planificar un tratamiento mejor.

Objetivos
El proyecto de implementación de la plataforma de telemedicina tiene como
objetivo principal mejorar el acceso y la calidad de los servicios de atención médica,
brindando a los pacientes la posibilidad de recibir atención médica remota y
oportuna. Los objetivos específicos del proyecto son los siguientes:

a) Desarrollo de un Minimum Viable Product (MVP), de la plataforma de


telemedicina, el cual cumpla con las necesidades básicas de los usuarios,
como la programación de citas virtuales, la comunicación segura y
confidencial entre médicos y pacientes, y el acceso a información médica
relevante. El diseño servirá como una versión inicial de la plataforma que se
irá mejorando y ampliando en etapas posteriores.
b) Mejora de accesibilidad de atención médica. Se busca eliminar las barreras
de movilidad e inaccesibilidad, permitiendo a los pacientes acceder a los
servicios de consulta médica de manera remota.
c) Integración de IA. La plataforma se beneficiará de la integración de
tecnologías como la inteligencia artificial para mejorar la precisión de los
diagnósticos, proporcionando recomendaciones personalizadas de
tratamiento y agilizando la interpretación de datos médicos.
d) Garantizar la seguridad y privacidad de datos. La plataforma se diseña y
desarrolla con la idea de la seguridad y protección de datos mediante
protocolos de encriptación, autenticación de usuarios y auditorías de
seguridad, lo cual nos permite prevenir accesos no autorizados y proteger la
integridad de la información de los usuarios.

3
Arquitectura
Descripción general

Nuestro sistema está basado en una plataforma de telemedicina, la cual se


ha diseñado con la intención de brindar servicios de atención médica remota y de
manera digital. Está destinada a pacientes de distintas ubicaciones geográficas,
principalmente pensado para aquellos que tienen inaccesibilidad en medio de
transporte o tiempo limitado. El objetivo es proporcionar una solución viable y
realista que permita facilitar la comunicación entre el personal profesional de salud y
pacientes, asegurando la protección de datos y privacidad de sus usuarios.

Nuestro sistema puede ser visto como un conjunto modular, pues tiene
componentes que han sido diseñados de manera independiente y que realizan una
función específica.

El sistema también es escalable, ya que puede ser modificado, es flexible


para ser adaptado y crecer en función de las necesidades que puedan llegar a
presentarse. Gracias a una función de almacenamiento externo, dicho sistema
puede crecer en términos de capacidad de almacenamiento y recursos. Se permite
la integración de nuevos componentes que ayudan al monitoreo ya sea de variables
asociadas al paciente o bien, al equipo que funcione para brindarle la atención
médica.

Componentes principales

A continuación se mencionan las partes más importantes que integran


nuestro sistema:

Plataforma Web:
Incluye una interfaz de usuario con la que interactúan los pacientes y
el personal asociado, como médicos, soporte, recepción u otros departamentos
involucrados. Contiene el acceso al registro médico de pacientes, citas, monitoreo
de variables fisiológicas y de equipo médico, así como resultados de pruebas.
Incluye también la identificación de usuarios, se accede a la base de datos y se
procesa la información de los dispositivos externos IoT.

Base de datos:
Aquí se almacena toda la información relacionada con los pacientes,
citas médicas, resultados de pruebas, registros médicos, monitoreo de dispositivos,
etc. Se usa una base de datos relacional a través de PostgreSQL.

Dispositivos IoT:

4
Se utilizan para recopilar datos de carácter médico, tales como
variables fisiológicas de pacientes y también para monitorear el rendimiento del
equipo médico asociado a éste durante una intervención. Los dispositivos envían los
datos a la plataforma de telemedicina para su procesamiento y visualización.

Análisis de imágenes médicas basados en IA:


La parte de análisis de imágenes médicas basados en IA consta de
tres partes, un código maestro el cual hace uso de aprendizaje de transferencia
usando como base la arquitectura vgg-16. La segunda parte contiene un modelo
reducido para reducir la cantidad de parámetros y finalmente el modelo estudiante el
cual aprende de los modelos anteriores. Esto permite la realización de análisis en un
dispositivo.

Tecnologías utilizadas y su justificación

Medición de variables en tiempo real a través del módulo Arduino:


La utilización de un microcontrolador Arduino permite la captura precisa
y en tiempo real de variables médicas varias, a través de la integración de sensores
modulares fáciles de operar. La esencia de la plataforma de código abierto de
Arduino permite una amplia variedad de hardware disponible para su uso, lo que lo
convierte en una opción flexible y escalable para implementar la ejecución de
dispositivos IoT en la plataforma de telemedicina.

Conexión a internet y transferencia de datos mediante Wi-Fi:


La conexión a internet mediante el módulo para nuestro
microcontrolador permite la transferencia rápida y segura de datos entre los
dispositivos IoT y la plataforma de telemedicina. El uso de Wi-Fi garantiza la
conectividad accesible y rápida, lo que es ventajoso para el monitoreo en tiempo
real y la transmisión de datos médicos. Dicho método facilita la su implementación
en diferentes entornos.

Interpretación de imágenes mediante inteligencia artificial:


La configuración de la red neuronal permite el análisis de imágenes
médicas por medio de un dispositivo IoT. La forma en la que funciona es haciendo
uso del código maestro el cual es un modelo pre-entrenado de arquitectura
VGG-16, de forma que captura las características generales, luego está el model
reducido el cual usó menos parámetro haciéndolo más eficiente y de menor
consumo de recursos, finalmente el modelo estudiante, haciendo uso de la técnica
de destilación funge como un modelo aún más comprimido lo que le permite ser
usado en dispositivos de recursos limitados.

Almacenamiento, modificación y acceso a bases de datos:


La utilización de una base de datos como PostgreSQL proporciona un
almacenamiento seguro y eficiente de la información relacionada con los pacientes

5
registrados en la plataforma, así como de sus citas, resultados, registros médicos,
etc. Una base de datos permite una gestión eficaz de grandes volúmenes de datos.
El acceso y modificación de la base de datos permiten el registro y la actualización
de la información médica de manera rápida y precisa, lo que facilita la colaboración
entre el equipo técnico y médico que utiliza la plataforma, por ende, provee la
oportunidad de mejor toma de decisiones basadas en datos actualizados
constantemente.

Diagramas

A continuación se describe un diagrama general de los componentes


principales de nuestra plataforma.

El usuario, en este caso el médico o especialista que tiene bajo asignación un


paciente accede a la plataforma mediante una autenticación de usuario. El médico
proporciona sus credenciales de inicio de sesión. La plataforma web utiliza
protocolos de seguridad estándar para proteger la comunicación y garantizar la
privacidad de datos.

Una vez autenticado, el médico obtiene permisos específicos para cceder y


visualizar los datos del paciente. Además, tiene el permiso de visualizar información
en tiempo real de un dispositivo médico asociado al paciente en la plataforma de ser
el caso. Estos datos son proporcionados por un bloque de hardware que integra el
dispositivo Arduino, el módulo de conexión Wi-Fi ESP8266 y un sensor en este caso
de temperatura.

El bloque del microcontrolador se encarga de obtener los datos de


temperatura del equipo médico asociado, como una máquina de anestesia

6
siguiendo con la ejemplificación. La información es capturada por el sensor y
procesada por el microcontrolador, indicando la correcta operación de dicho
dispositivo médico en el caso de una intervención con nuestro paciente. Todos estos
datos se envían a la plataforma, la cual valida su recepción.

La plataforma tiene acceso a la base de datos en PostgreSQL, donde se


almacenan y gestionan los datos médicos relevantes, tales como el historial del uso
y rendimiento del equipo de anestesia y registros médicos del paciente de sus citas
en el centro hospitalario. La plataforma establece una conexión segura con la base
de datos y la información médica es modificada y actualizada conforme sea
necesario.

Funcionalidades Principales

Interacciones entre usuarios y la plataforma

7
Medidas de seguridad implementadas para proteger los datos

Gestión de identidad y acceso

Implementación técnica
Detalles sobre la implementación de la plataforma, incluyendo los lenguajes de
programación utilizados, las bibliotecas o frameworks empleados, y cualquier otro
aspecto relevante.
Lenguajes de programación y frameworks utilizados

Descripción de la base de datos utilizada

Integración de tecnologías adicionales (IA e IoT)


Aprendizaje de transferencia: El aprendizaje de transferencia es una técnica en el
campo del aprendizaje automático donde se aprovecha el conocimiento aprendido
por un modelo pre-entrenado en una tarea específica para mejorar el rendimiento en
una tarea relacionada. En lugar de entrenar un modelo desde cero, se utiliza un
modelo base ya entrenado en un conjunto de datos grande y diverso, como la
arquitectura VGG-16.

Arquitectura VGG-16: La arquitectura VGG-16 es una red neuronal convolucional


ampliamente utilizada en el campo de la visión por computadora. Consta de 16
capas, incluyendo capas convolucionales, capas de pooling y capas totalmente
conectadas. VGG-16 se destaca por su simplicidad y uniformidad, utilizando
convoluciones 3x3 y max pooling 2x2 en todas las capas. Esta arquitectura ha
demostrado un buen rendimiento en tareas de clasificación de imágenes y se utiliza
comúnmente como base para el aprendizaje de transferencia.

8
1. numpy: La librería NumPy es fundamental para el cálculo numérico en
Python. Proporciona estructuras de datos eficientes y de alto rendimiento,
como matrices multidimensionales, y funciones para operaciones
matemáticas y manipulación de datos.
2. matplotlib.pyplot: Esta librería se utiliza para la visualización de datos y la
generación de gráficos en Python. Proporciona una amplia gama de
funciones para crear y personalizar gráficos, diagramas y visualizaciones.
3. sklearn.model_selection: Scikit-learn es una librería ampliamente utilizada
para el aprendizaje automático en Python. La sublibrería model_selection
ofrece herramientas para dividir los datos en conjuntos de entrenamiento y
prueba, lo que facilita la evaluación y validación de los modelos.
4. PIL.Image: El módulo Image de la librería Pillow (fork de PIL) se utiliza para
trabajar con imágenes en Python. Proporciona una amplia gama de funciones
para cargar, manipular y guardar imágenes en diversos formatos.
5. tensorflow: TensorFlow es una biblioteca popular y de alto rendimiento para el
aprendizaje automático y la inteligencia artificial. Proporciona un entorno de
programación eficiente para construir y entrenar redes neuronales, así como
herramientas para el despliegue y la inferencia de modelos.
6. tensorflow.keras: Keras es una API de alto nivel que se ejecuta sobre
TensorFlow y facilita la construcción y entrenamiento de modelos de
aprendizaje profundo. Proporciona una interfaz sencilla y elegante para
definir capas, arquitecturas de modelos y funciones de pérdida, y ofrece una
amplia gama de utilidades para el procesamiento de datos.
7. tensorflow.keras.layers: Este módulo de Keras proporciona una variedad de
capas neuronales que se pueden utilizar para construir arquitecturas de redes
neuronales personalizadas. Incluye capas convolucionales, capas de pooling,
capas de normalización y muchas otras capas comunes utilizadas en el
procesamiento de imágenes y otras tareas.
8. tensorflow.keras.models: Este módulo proporciona la clase Model, que se
utiliza para crear y entrenar modelos de aprendizaje profundo en TensorFlow.
Permite definir modelos mediante la composición de capas y proporciona
métodos para compilar, entrenar y evaluar los modelos.
9. tensorflow.keras.losses: Este módulo contiene varias funciones de pérdida
que se utilizan para medir la discrepancia entre las salidas predichas y las
salidas reales durante el entrenamiento de los modelos. Incluye funciones
como SparseCategoricalCrossentropy y CategoricalCrossentropy.
10. tensorflow.keras.optimizers: Este módulo proporciona optimizadores que se
utilizan para ajustar los pesos y los sesgos de los modelos durante el
entrenamiento. Ofrece algoritmos populares como Adam, que se utiliza
comúnmente en el aprendizaje profundo debido a su eficacia y rapidez.
11. tensorflow.keras.callbacks: Este módulo contiene clases que se utilizan como
devoluciones de llamada durante el entrenamiento de los modelos. Las
devoluciones de llamada, como EarlyStopping y ModelCheckpoint, permiten
controlar el progreso del entrenamiento y realizar

9
Implementación de dispositivos IoT

Pruebas realizadas y enfoque de calidad

Resultados
Se logró mejorar el acceso y la calidad de los servicios de atención médica,
brindando a los pacientes la posibilidad de recibir atención médica remota y
oportuna, cumpliendo así el objetivo principal del proyecto.

Resumen de los logros y resultados del proyecto

Reflexiones sobre los objetivos alcanzados y áreas de mejora


Se desarrolló un Minimum VIable Product (MVP) que cumple con las
necesidades básicas de los usuarios, sirviendo así como una versión inicial de la
plataforma; se mejoró la accesibilidad de atención médica, permitiendo acceder a
los servicios de consulta médica de manera remota; se integró una Inteligencia
Artificial que mejoró la precisión de los diagnósticos, al dar recomendaciones
personalizadas de tratamiento y agilizando la interpretación; se logró garantizar la
seguridad y la privacidad de los datos, implementando protocolos de encriptación.
Futuras mejoras y ampliaciones propuestas

Conclusiones y Recomendaciones
Conclusiones obtenidas del proyecto, lecciones aprendidas y recomendaciones para
futuras mejoras o expansiones.

Anexos
Documentación adicional relevante, como el modelo destilado utilizado, el informe
del entrenamiento del modelo de IA, archivos de código fuente, capturas de pantalla
u otros materiales respaldatorios.

Código IA usado:
https://colab.research.google.com/drive/1bCBAxV0HT8lL4uUgDCiafxRTksujkGFk?u
sp=sharing

Intentos para código IA:

10
https://colab.research.google.com/drive/1R5tDBo_bSqGJLjwThWD-tMQfDTmiCh2f?
usp=sharing

Figura xxxxx. Portada de plataforma de telemedicina con ingreso desde dispositivo


portátil iPad Mini.

Figura xxxxx. Dashboard de la plataforma para el usuario doctor_1 con nombre


ficticio Dr. Telemédico Biomédico con ingreso desde dispositivo iPad Mini.
Obsérvese la falta de ajuste del fondo, será necesario ajustar las dimensiones de la

11
tabla para que no sobrepase los límites de la página cuando se visualiza en modo
horizontal.

Figura xxxxx. Dispositivo IoT modelo Wemos D1 R1 con procesador ESP8266


implementado con lectura/escritura desde memoria microSD externa y con sensor
de temperatura y humedad relativa modelo DHT11.

12
Figura xxxxx. Dispositivo IoT implementado. Obsérvese el adaptador con la
memoria microSD externa y el sensor de temperatura y humedad relativa DHT11.

13
Figura xxxxx. Evidencia de lectura en tiempo real con monitor serie de Arduino IDE.

Figura xxxxx. Archivo de datos creado directamente en la memoria microSD con el


registro de las lecturas del sensor.

14
Figura xxxxx. Archivo de texto leído desde la memoria microSD con el registro de
temperatura y humedad sensadas, obsérvese la etiqueta de tiempo en milisegundos
desde el inicio del programa de sensado.

15
Figura xxxxx. Página del dispositivo IoT o sensor de temperatura y humedad
visualizada desde el dispositivo móvil iPhone 13 Pro. Aunque se observan todos los
datos, el tamaño de la fuente podría ajustarse de mejor forma.

Código ESP (Arduino IDE)


#include <ESP8266WiFi.h>
#include <ESP8266WiFiMulti.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <SPI.h>

16
#include <DHT.h>
#include <Ticker.h>
ESP8266WiFiMulti WiFiMulti;
ESP8266WebServer server(80);
WiFiClient wifiClient;
File configFile;

String deviceName = "Dispositivo IoT Telemedicina - Sensor de Temperatura y Humedad


Relativa";

#include <WebSocketsServer.h>
WebSocketsServer webSocket = WebSocketsServer(81);
#include "conexiones.h"
#include "respuestasHTML.h"
#define LED 2
#define DHTTYPE DHT11 // Sensor DHT 11
#define DHTPin D8 // PIN AL QUE SE CONECTA EL sensor

int interval = 1000;


unsigned long previousMillis = 0;
unsigned long ultimo_post = 0;
unsigned long current_millis = 0;
const uint32_t TiempoEsperaWifi = 5000;
String texto = "temperatura=";
float temperatura;
float humedad;
float temperaturaAnterior = 0.0;
float humedadAnterior = 0.0;

String nombre = "";


String ingreso = "";
String tiempo = "";

#include <EEPROM.h>
#define CONFIG_ADDRESS 0

struct Config {
String nombre;
String ingreso;
String tiempo;
};

DHT dht(DHTPin, DHTTYPE); //Inicializar el sensor DHT11


Ticker myTicker; // Crear un Ticker que llama a leerSensor cada 1000ms (1 segundo)

17
bool readSensorFlag = false;

// Definir el pin para la tarjeta microSD


#ifndef SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SS;
#else // SDCARD_SS_PIN
const uint8_t SD_CS_PIN = SDCARD_SS_PIN;
#endif // SDCARD_SS_PIN

#include <SD.h>

Config configuracion;

void guardarConfiguracion();
String SendHTML(float temperatura,float humedad);

extern Ticker myTicker; // Crear un Ticker


extern bool readSensorFlag; // Bandera para indicar cuando leer el sensor

void leerSensor();

void tickFunction() {
readSensorFlag = true;
}

void setup() {
Serial.begin(115200);
dht.begin(); // Inicializa la lectura del sensor
myTicker.attach(3, tickFunction);
readSensorFlag = false;

EEPROM.begin(sizeof(configuracion));
EEPROM.get(CONFIG_ADDRESS, configuracion);
EEPROM.end();

WiFiMulti.addAP(ssid_1, password_1);
WiFiMulti.addAP(ssid_2, password_2);
Serial.println("Estableciendo conexion WiFi...");
while (WiFiMulti.run(TiempoEsperaWifi) != WL_CONNECTED) {
delay(100);
Serial.print(".");
}
Serial.println("");
Serial.println("Conexion exitosa!");
Serial.print("Direccion IP: ");

18
Serial.println(WiFi.localIP());

webSocket.begin();
Serial.println("Servidor HTTP iniciado.");
webSocket.onEvent(webSocketEvent);

server.on("/", []() {
char html[sizeof(index_html) + deviceName.length() + 1];
sprintf(html, index_html, deviceName.c_str());
server.send(200, "text/html", html);
});

server.on("/configuracion", []() {
String personalizacion_html = "";

personalizacion_html = String(personalization_1) + nombre;


personalizacion_html += String(personalization_2) + ingreso;
personalizacion_html += String(personalization_3) + tiempo +
String(personalization_4);

server.send(200, "text/html", personalizacion_html);


});

server.on("/personalizar", []() {
String message = "";
if (server.arg("Nombre") == "") {
message += "\nNo se recibio un nuevo nombre";
} else {
message += "\nNombre recibido = ";
nombre = server.arg("Nombre");
message += server.arg("Nombre");
}

if (server.arg("Ingreso") == "") {
message += "\nNo se recibio fecha de ingreso";
} else {
message += "\nIngreso en = ";
ingreso = server.arg("Ingreso");
message += server.arg("Ingreso");
}

if (server.arg("Tiempo") == "") {
message += "\nNo se recibio tiempo de uso";
} else {
message += "\nTiempo de uso = ";

19
tiempo = server.arg("Tiempo");
message += server.arg("Tiempo");
}

Serial.println(message);

server.send(200, "text/plain", message);


});

server.begin();

pinMode(LED, OUTPUT);

// Inicializar la comunicación con la tarjeta microSD


if (!SD.begin(SD_CS_PIN)) {
Serial.println("Error al inicializar la tarjeta microSD.");
while (1);
}

Serial.println("Tarjeta microSD inicializada correctamente.");

if (SD.exists("config.txt")) {
Serial.println("Leyendo archivo de configuracion");
configFile = SD.open("config.txt");
if (configFile) {
// Leer la configuración desde el archivo y usarla.
configFile.close();
}
} else {
Serial.println("Creando archivo de configuracion");
configFile = SD.open("config.txt", FILE_WRITE);
if (configFile) {
// Escribir configuración por defecto en el archivo.
configFile.close();
}
}
}

void loop() {
if (WiFi.status() == WL_CONNECTED) {
server.handleClient();
webSocket.loop();

unsigned long now = millis();


if ((unsigned long)(now - previousMillis) >= interval) {

20
String str = String(temperaturaAnterior) + "," + String(temperatura) + "," +
String(humedadAnterior) + "," + String(humedad);
int str_len = str.length() + 1;
char char_array[str_len];
str.toCharArray(char_array, str_len);
webSocket.broadcastTXT(char_array);
previousMillis = now;
}

current_millis = millis();
if (current_millis - ultimo_post > 30000) {
ultimo_post = current_millis;
leerSensor();

File dataFile = SD.open("/data.txt", FILE_WRITE);


if (dataFile) {
unsigned long timestamp = millis();
String dataWithTimestamp = String(timestamp) + ": " + texto;
dataFile.println(dataWithTimestamp);
dataFile.close();
Serial.println("Datos guardados en la tarjeta microSD");
} else {
Serial.println("Error al abrir el archivo en la tarjeta microSD");
}

HTTPClient http;
http.begin(wifiClient, "http://192.168.1.70:80/temperatura");
http.addHeader("Content-Type", "application/x-www-form-urlencoded");
int httpCod = http.POST(texto);
if (httpCod == 200) {
String respuesta = http.getString();
Serial.println("El servidor respondió: ");
Serial.println(respuesta);
} else {
Serial.println("El servidor respondió: ");
Serial.println(httpCod);
}
http.end();
}
if (readSensorFlag) {
leerSensor();
readSensorFlag = false;
}
}
}

21
void leerSensor() {
digitalWrite(LED, !(digitalRead(LED)));
humedadAnterior = humedad; // Almacena el valor anterior de humedad
temperaturaAnterior = temperatura; // Almacena el valor anterior de temperatura
humedad = dht.readHumidity();
temperatura = dht.readTemperature();

if (isnan(humedad) || isnan(temperatura))
{
Serial.println("Error al recibir datos del sensor!");
return;
}
server.send(200, "text/html", SendHTML(temperatura,humedad));
Serial.print("Temperatura medida: ");
Serial.println(temperatura);
Serial.print("Humedad medida: ");
Serial.println(humedad);
texto = "temperatura=" + String(temperatura) + "&humedad=" + String(humedad);
}

void guardarConfiguracion() {
EEPROM.begin(sizeof(configuracion));
EEPROM.put(CONFIG_ADDRESS, configuracion);
EEPROM.commit();
EEPROM.end();
}

// Definición de la función SendHTML()


String SendHTML(float temperatura,float humedad){
String ptr = "<!DOCTYPE html> <html>\n";
ptr +="<head><meta name=\"viewport\" content=\"width=device-width,
initial-scale=1.0, user-scalable=no\">\n";
ptr +="<title>IoT Monitoring Web Server</title>\n";
// CSS to style the table
ptr +="<style> table { width: 100%; background-color: #f1f1c1; } h1 {color:
#0052cc; } h2 {color: #0052cc; } h3 {color: #0052cc; } </style>\n";
ptr +="</head>\n";
ptr +="<body>\n";
ptr +="<h1>ESP32 Web Server Reading sensor values</h1>\n";
ptr +="<h2>Temperatura: "+String(temperatura)+"</h2>\n";
ptr +="<h2>Humedad: "+String(humedad)+"</h2>\n";
ptr +="</body>\n";
ptr +="</html>\n";
return ptr;

22
}

Código respuestasHTML.h (Arduino IDE)


// Respuesta HTML almacenada en memoria
// El contenido puede ser modificado para su pagina de preferencia
char index_html[] PROGMEM = R"=====(
<html>
<head><title>Termohigrometro IoT</title></head>
<body style='background-color: #DAF7A6;'>
<img
src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcTLyLJZmcjLgCItSwzw3-b4Sb
1wYe5PrQqQKw&usqp=CAU" alt="Logo FIME - UANL">
<span style='color: #003366;'>
<h1>Lectura actual de temperatura y humedad.</h1>
<p><strong>Nombre del dispositivo: %s</strong></p>
<p>Ultima temperatura sensada: <span id='lastTemp'>-</span></p>
<p>Temperatura actual: <span id='currentTemp'>-</span></p>
<p>Ultima humedad sensada: <span id='lastHum'>-</span></p>
<p>Humedad actual: <span id='currentHum'>-</span></p>
<p><button type='button' id='BTN_SEND_BACK'>
Send info to ESP32</button>
</p></span></body>
<script>
var Socket;
document.getElementById('BTN_SEND_BACK').addEventListener('click',
button_send_back);
function init() {
Socket = new WebSocket('ws://' + window.location.hostname + ':81/');
Socket.onmessage = function(event) { processCommand(event); };
}
function button_send_back() { Socket.send('Sending back some random stuff'); }
function processCommand(event) {
var tempData = event.data.split(',');
if (tempData.length >= 4) {
document.getElementById('lastTemp').innerHTML = tempData[0];
document.getElementById('currentTemp').innerHTML = tempData[1];
document.getElementById('lastHum').innerHTML = tempData[2];
document.getElementById('currentHum').innerHTML = tempData[3];
}
}
window.onload = function(event) { init(); }
</script></html>
)=====";

const char personalization_html[5000] PROGMEM={};

23
const char personalization_1[] PROGMEM = R"=====(
<html>
<head>
<title>Configuracion</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.cs
s" rel="stylesheet"
integrity="sha384-KK94CHFLLe+nY2dmCWGMq91rCGa5gtU4mk92HdvYe+M/SXH301p5ILy+dN9+nJOZ"
crossorigin="anonymous">
</head>

<body style='background-color: #EEEEEE;'>


<span style='color: #003366;'>
<h1>Configuracion del dispositivo.</h1>
</span>

<div style="width: 300px;">


<table class="table">
<tr>
<th>Paciente ID</th>
<th>Registro</th>
<th>Tiempo</th>
</tr>
<tr>
<td>
)=====";

const char personalization_2[] PROGMEM = R"=====(


</td>
<td>
)=====";

const char personalization_3[] PROGMEM = R"=====(


</td>
<td>
)=====";

const char personalization_4[] PROGMEM = R"=====(


</td>
</tr>
</table>
</div>

24
</body>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.m
in.js"
integrity="sha384-ENjdO4Dr2bkBIFxQpeoTz1HIcje39Wm4jDKdf19U8gI4ddQ3GYNS7NTKfAdVQSZe"
crossorigin="anonymous"></script>
</html>
)=====";

void webSocketEvent(byte num, WStype_t type, uint8_t * payload, size_t length) {


// Funcion minima para manejar conexion bidireccional del WebSocket
switch (type) {
case WStype_DISCONNECTED:
Serial.println("Client " + String(num) + " disconnected");
break;
case WStype_CONNECTED:
Serial.println("Client " + String(num) + " connected");
webSocket.sendTXT(num, deviceName);
break;
case WStype_TEXT:
for (int i=0; i<length; i++) {
Serial.print((char)payload[i]);
}
Serial.println("");
break;
}
}

Código Python
import logging
from flask import Flask, render_template, request, redirect, url_for
import os
import requests
import json
import datetime
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager, UserMixin, login_user,
login_required, logout_user, current_user

app = Flask(__name__)
app.config['SECRET_KEY'] = 's4d56f7ghuidu87fbnkiuh'

gunicorn_error_logger = logging.getLogger('gunicorn.error')
app.logger.handlers.extend(gunicorn_error_logger.handlers)

25
app.logger.setLevel(logging.DEBUG)

db_user = "tmuser"
db_pswdr = "tmpassword"
db_host = "localhost:5432/"
db_name = "tmdatabase"

app.config['SQLALCHEMY_DATABASE_URI'] =
'postgresql://'+db_user+':'+db_pswdr+'@'+db_host+db_name

db = SQLAlchemy(app)

login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'login'

class accounts(UserMixin, db.Model):


id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(50))
password = db.Column(db.String(50))
email = db.Column(db.String(255))
created_on = db.Column(db.DateTime)
full_name = db.Column(db.String(150))
verif_status = db.Column(db.String(50))
role_id = db.Column(db.Integer)

class consultas(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer)
created_on = db.Column(db.DateTime)
motivo = db.Column(db.String(150))
diagnostico = db.Column(db.String(150))
prescripcion = db.Column(db.String(150))
incapacidad = db.Column(db.String(50))
dispositivo = db.Column(db.Integer)

class dispositivos(db.Model):
id = db.Column(db.Integer, primary_key=True)
device_number = db.Column(db.Integer)
assigned_to = db.Column(db.Integer)
assigned_period = db.Column(db.String(50))
assigned_date = db.Column(db.DateTime)
remote_ip = db.Column(db.String(50))

26
def processGArequest(uname,upwrd):
if (uname=="manuel@me.com" and upwrd=="root"):
response = redirect(url_for("dashboard",user_id='0'))
return response

quser = accounts.query.filter_by(username=uname).first()
if quser:
if quser.password==upwrd: # or
check_password_hash(quser.password,upwrd):
print("User validated through DB, right password used")
login_user(quser)
response = redirect(url_for("dashboard",user_id=quser.id))
response.set_cookie('telemed_app', str(quser.id))
return response
return render_template('access.html')

@app.route("/")
def home():
return render_template("index.html")

# This function is not yet implemented


@app.route('/login', methods = ['GET','POST'])
def login():
return render_template("access.html")

@app.route('/gaccess', methods = ['GET','POST'])


def gaccess(): # Get access
uname = request.form['uname']
upwrd = request.form['upwrd']
print("Trying to login \n\t User: {} \n\t Password:
{}".format(uname,upwrd))
return processGArequest(uname,upwrd)

@app.route('/dashboard/<user_id>')
def dashboard(user_id):
qaccount = accounts.query.filter_by(id=user_id).first()
rol_id = qaccount.role_id

if str(rol_id) == '0': # Administrador


usuarios = {}
return
render_template('board_admin.html',uname=user_id,usuarios=usuarios)

27
elif str(rol_id) == '1': # Paciente
estudios = consultas.query.filter_by(user_id=int(user_id))
return
render_template('board_patient.html',uname=qaccount.full_name,consultas
=estudios,rol_id=rol_id)
elif str(rol_id) == '2': # Enfermero
pacientes = accounts.query.filter_by(role_id=1)
# print("Pacientes found: ",pacientes)
return
render_template('board_nurse.html',uname=qaccount.full_name,pacientes=p
acientes)
elif str(rol_id) == '3': # Medico
pacientes = accounts.query.filter_by(role_id=1)
return
render_template('board_doctor.html',uname=qaccount.full_name,casos=paci
entes)
elif str(rol_id) == '4': # Medico especialista
pacientes = accounts.query.filter_by(role_id=1)
return
render_template('board_doctor.html',uname=qaccount.full_name,casos=paci
entes)
# elif str(ur_projects.id) == '5': # Investigador
# return
render_template('boards/manager-board.html',uname=qaccount.full_name,pd
ata=ur_projects)

@app.route('/expediente/<user_id>')
def expediente(user_id):
historial = consultas.query.filter_by(user_id=int(user_id))
paciente = accounts.query.filter_by(id=int(user_id)).first()
req_id = request.cookies.get('telemed_app')
print("\tLooking for req_id user :",req_id)
qaccount = accounts.query.filter_by(id=req_id).first()
rol_id = qaccount.role_id
# Roles definidos en las bases de datos
# if str(rol_id) == '0': # Administrador
# elif str(rol_id) == '1': # Paciente
# elif str(rol_id) == '2': # Enfermero
# elif str(rol_id) == '3': # Medico
# elif str(rol_id) == '4': # Medico especialista
# elif str(rol_id) == '5': # Investigador
return render_template('board_expediente.html',
uname=paciente.full_name,

28
patId=user_id,
urol=rol_id,
consultas=historial)

@app.route('/agregar/<user_id>')
def agregar(user_id):
historial = consultas.query.filter_by(user_id=int(user_id))
paciente = accounts.query.filter_by(id=int(user_id)).first()
req_id = request.cookies.get('telemed_app')
print("\tLooking for req_id user :",req_id)
qaccount = accounts.query.filter_by(id=req_id).first()
rol_id = qaccount.role_id

return render_template('board_nuevoRecord.html',
uname=paciente.full_name,
patId=user_id,
urol=rol_id,
consultas=historial)

@app.route('/salvarregistro/<user_id>', methods = ['POST'])


def salvarregistro(user_id):
db.session.add(consultas(
user_id = user_id,
created_on = str(datetime.datetime.now()).split(".")[0], #
"date_time",
motivo = request.form['nuevoMotivo'],
diagnostico = request.form['nuevoRegistro'],
prescripcion = request.form['nuevaPrescripcion'],
incapacidad = request.form['nuevaIncapacidad']
))
db.session.commit()

return redirect(url_for("expediente",user_id=user_id))

@app.route('/dispositivo/<int:device_id>')
def dispositivo(device_id):
configuracion =
dispositivos.query.filter_by(device_number=int(device_id)).first ()
paciente =
accounts.query.filter_by(id=int(configuracion.assigned_to)).first()
req_id = request.cookies.get('telemed_app')

29
consulta =
consultas.query.filter_by(dispositivo=configuracion.device_number).firs
t()

qaccount = accounts.query.filter_by(id=req_id).first()
rol_id = qaccount.role_id
if int(rol_id) > 2:
return render_template('board_dispositivo.html',
devID=configuracion.device_number,
uname=paciente. full_name,
consulta=consulta,

assigned_period=configuracion.assigned_period,

assigned_date=configuracion.assigned_date)

@app.route('/visualizar/<device_id>')
def visualizar (device_id):
configuracion =
dispositivos.query.filter_by(device_number=int(device_id)).first()
paciente =
accounts.query.filter_by(id=int(configuracion.assigned_to)).first()
req_id = request.cookies.get('telemed_app')

return redirect("http://"+configuracion.remote_ip)

@app.route('/home_page', methods=['GET'])
def shortenurl():
if request.method == 'GET':
return redirect('/')

@app.route("/1378118")
def welcome():
return {"message":"Bienvenido, Victor Manuel Castaneda de Leon"}

@app.route("/admin/delete_records")
def delete_records():
return {"message":"registros eliminados correctamente"}

@app.route("/admin/create_records")
def create_records():
return {"message":"registros eliminados correctamente"}

30
@login_manager.user_loader
def load_user(user_id):
return accounts.query.get(int(user_id))

@app.route("/logout")
def logout():
logout_user()
return redirect('/')

if __name__ == '__main__':
app.run(host='0.0.0.0', port=82, debug=True)

Código init.sql
create table accounts (
id serial PRIMARY KEY,
username VARCHAR (50),
password VARCHAR (50),
email VARCHAR (255),
created_on TIMESTAMP,
full_name VARCHAR (150),
verif_status VARCHAR (50),
role_id INT
);

insert into accounts(username,password,full_name,email,role_id)


values ('paciente_1','entrar','Juan Lopez','pac01@mail.me',1);
insert into accounts(username,password,full_name,email,role_id)
values ('paciente_2','entrar','Maria Garza','pac02@mail.me',1);
insert into accounts(username,password,full_name,email,role_id)
values ('paciente_3','entrar','Samuel Ruiz','pac03@mail.me',1);

insert into accounts(username,password,full_name,email,role_id)


values ('doctor_1','entrar','Telemedico Biomedico','doc01@mail.me',3);
insert into accounts(username,password,full_name,email,role_id)
values ('doctor_2','entrar','Luis Pedraza','doc02@mail.me',3);
insert into accounts(username,password,full_name,email,role_id)
values ('doctor_3','entrar','Javier Su','doc03@mail.me',3);

insert into accounts(username,password,full_name,email,role_id)


values ('nurse_1','entrar','Ricardo Juarez','nurse01@mail.me',2);
insert into accounts(username,password,full_name,email,role_id)
values ('nurse_2','entrar','Lazaro Esau','nurse02@mail.me',2);
insert into accounts(username,password,full_name,email,role_id)
values ('nurse_3','entrar','Diana Velasco','nurse03@mail.me',2);

31
insert into accounts(username,password,full_name,email,role_id)
values ('especialista_1','entrar','Ronald McMiller','esp01@mail.me',4);
insert into accounts(username,password,full_name,email,role_id)
values ('especialista_2','entrar','Juliana Shou','esp02@mail.me',4);
insert into accounts(username,password,full_name,email,role_id)
values ('especialista_3','entrar','David Jackson','esp03@mail.me',4);

create table consultas (


id serial PRIMARY KEY,
user_id INT,
created_on TIMESTAMP,
motivo VARCHAR (150),
diagnostico VARCHAR (150),
prescripcion VARCHAR (150),
incapacidad VARCHAR (50),
dispositivo INT
);

insert into consultas (


user_id,created_on,motivo,diagnostico,prescripcion,incapacidad)
values
(1,'2023-01-12 07:40:16','Fractura pierna','Fractura femur grado 3','Paracetamo
c/8h','7 dias');

insert into consultas (


user_id,created_on,motivo,diagnostico,prescripcion,incapacidad)
values
(1,'2023-01-23 12:10:15','Retiro de yeso','Recupera movilidad','N/A','N/A');

insert into consultas (


user_id,created_on,motivo,diagnostico,prescripcion,incapacidad,dispositivo)
values
(1,'2023-01-30 07:10:25','Dolor intenso en pierna','Microfractura accidental
detectada','Diclofenaco c/6h','N/A',3);

create table dispositivos (


id serial PRIMARY KEY,
device_number INT,
assigned_to INT,
assigned_by INT,
assigned_date TIMESTAMP,
assigned_period VARCHAR (20),
tipo_registro VARCHAR (20),
remote_IP VARCHAR (20)

32
);

insert into dispositivos (


device_number, assigned_to, assigned_date, assigned_period, remote_IP)
values
(112233, 3, '2023-05-06 12:13:05', '1 semana', '192.168.1.70');

33

También podría gustarte