python – Function for mechanical fan

Question:

I am programming an application of a mechanical respirator, in python, raspberry Pi 4 and arduino uno, with a ventilator for medical use (with breathing mask) that shows the flow in a graph by means of mathplotlib and a Sensirion smf3000 sensor, so far I can measure the flow through the sensor, modify the speed of the ventilator thanks to a Slidebar and show the data when the patient breathes in and breathes in a graph in real time.

The problem I find is to program a function where once I have defined the speed of the ventilator it detects when the patient inspires and immediately raises its speed to help the patient breathe and also that when it detects that the person is exhaling it lowers its speed, I hope can you help me.

Example: with the slidebar I set the fan speed at 50, the patient inhales and the fan must increase its speed to 80, conclude inhaling and begin to exhale, the fan must reduce its speed to 15 and so on.

I share the code in python:

from tkinter import *
import numpy as np
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from matplotlib import style
import serial as ser
import time
from PIL import ImageTk,Image
from matplotlib.widgets import Slider
import argparse
import sys
import requests
import RPi.GPIO as GPIO

global condicion, data
flujo=0
ciclo=0

data = np.array([])
condicion = False

#creamos la ventana principal
raiz = Tk()

raiz.title("Respirador")
raiz.geometry("850x420")
raiz.config(bg="gray")

#agregamos los frames

frame1 = Frame(raiz)
frame1.grid(row=0, column=0)
frame1.config(bg="gray")
frame1.config(width="520", height="335")

contenedor = Frame(raiz)
contenedor.grid(row=0, column=1)
contenedor.config(bg="red")
contenedor.config(width="220", height="400")


fValores = Frame(contenedor)
fValores.grid(row=0, column=0)
fValores.config(bg="gray", highlightbackground="black", highlightthicknes=1)
fValores.config(width="320", height="130")

lbTemperatura = Label(fValores, text= "TEMPERATURA:", bg="gray", fg="white", font=("Times New Roman", 12) ).place(x=70, y=15)
#lbvalorTemperatura = Label(fValores, text="", bg="gray", fg="blue", font=("Times New Roman", 10)).place(x=70 y= 40)

lbOxigeno = Label(fValores, text= "NIVEL DE OXIGENO:", bg="gray", fg="white", font=("Times New Roman", 12)).place(x=50, y=85)
#lbvalorOxigeno = Label(fValores, text="", bg="gray", fg="blue", font=("Times New Roman", 10)).place(x=135, y= 85)

ffoto = Frame(contenedor)
ffoto.grid(row=1, column=0)
ffoto.config(bg="gray", highlightbackground="black", highlightthicknes=1)
ffoto.config(width="320", height="130")

foto_paciente = ImageTk.PhotoImage(Image.open("perfil.png"))
 
lbFoto = Label(ffoto, image = foto_paciente).place(x=15, y=15)


lbNombre = Label(ffoto, text="Nombre: ", bg="gray", fg="white", font=("Times New Roman", 10)).place(x=145, y=12)
lbEdad = Label(ffoto, text="Edad: ", bg="gray", fg="white", font=("Times New Roman", 10)).place(x=145, y=37)
lbSexo = Label(ffoto, text="Sexo: ", bg="gray", fg="white", font=("Times New Roman", 10)).place(x=145, y=62)
lbUnidad = Label(ffoto, text="Unidad: ", bg="gray", fg="white", font=("Times New Roman", 10)).place(x=145, y=87)
lbfFolio = Label(ffoto, text="Folio: ", bg="gray", fg="white", font=("Times New Roman", 10)).place(x=145, y=112)

# valores del paciente

lbNombre_valor = Label(ffoto, text="valor", bg="gray", fg="blue", font=("Times New Roman", 10)).place(x=195, y=12)
lbEdad_valor = Label(ffoto, text="valor", bg="gray", fg="blue", font=("Times New Roman", 10)).place(x=185, y=37)
lbSexo_valor = Label(ffoto, text="valor", bg="gray", fg="blue", font=("Times New Roman", 10)).place(x=185, y=62)
lbUnidad_valor = Label(ffoto, text="valor", bg="gray", fg="blue", font=("Times New Roman", 10)).place(x=195, y=87)
lbfFolio_valor = Label(ffoto, text="valor", bg="gray", fg="blue", font=("Times New Roman", 10)).place(x=185, y=112)

#control de escala

fControlador = Frame(contenedor)
fControlador.grid(row=2, column=0)
fControlador.config(bg="gray", highlightbackground="black", highlightthicknes=1)
fControlador.config(width="320", height="150")

lbControl = Label(fControlador, text="Nivel de Flujo: ", bg="gray", fg="white", font=("Times New Roman", 18)).place(x=30, y=3)

Slider_control = Scale(fControlador, from_=10, to=99, length=200,
                       resolution=1, orient=HORIZONTAL,
                       command=getFlujo,
                       tickinterval = 20,
                       fg = 'white',
                       bg = 'black').place(x=40, y=55)

#creamos los botones

raiz.update();

btnIniciar = Button(raiz, text = "Iniciar", font = ('calibri', 12),
                  command = lambda: plot_Iniciar())

btnIniciar.place(x=150, y=350)


raiz.update();

btnDetener = Button(raiz, text = "Detener", font = ('calibri', 12),
                  command = lambda: plot_Detener())

btnDetener.place(x=btnIniciar.winfo_x()+btnIniciar.winfo_reqwidth() + 80, y = 350)


#eventos de la ventana

def cerrar_ventana():
    raiz.destroy()

raiz.protocol("WM_DELETE_WINDOW", cerrar_ventana)

GPIO.setmode(GPIO.BOARD)
GPIO.setup(32,GPIO.OUT)

pin32 = GPIO.PWM(32,100)
pin32.start(0)


###iniciar la conexion serial
s = ser.Serial('/dev/ttyACM0', 9600)
s.reset_input_buffer()

#aqui se adquieren los datos de la conexion serial
def plot_data():
    global data

    if(condicion == True):

        a = s.readline()
        a.decode()
        flujo = a.decode()
        lbvalorTemperatura = Label(fValores, text= flujo + "    ", bg="gray", fg="blue", font=("Times New Roman", 10)).place(x=135, y= 40)


        if len(data) < 10:
            data = np.append(data, float(a[0:4]))
        else:
            data[0:9] = data[1:10]
            data[9] = float(a[0:4])
        lines.set_xdata(np.arange(0,len(data)))
        lines.set_ydata(data)
        
        canvas.draw()

    
    raiz.after(1, plot_data)


#creamos la grafica
style.use("dark_background")
fig = Figure();
ax = fig.add_subplot(111)

ax.set_title('FLUJO')
ax.set_xlabel("Tiempo")
ax.set_ylabel("Nivel de flujo")
ax.set_xlim(0, 10)
ax.set_ylim(0, 20)
lines = ax.plot([],[])[0]

canvas = FigureCanvasTkAgg(fig, master = frame1)
canvas.get_tk_widget().place(x=5, y =0, width= 500, height = 280)
canvas.draw()

#detener la transmision de datos con el boton detener
def plot_Iniciar():
    global condicion
    condicion = True
    s.reset_input_buffer()

#iniciar la transmision de datos con el boton iniciar
def plot_Detener():
    global condicion
    condicion = False

#aqui se define el valor del slider para controlar la velocidad del ventilador   
def getFlujo(val):
    ciclo=20
    print(val)
    lbvalorOxigeno = Label(fValores, text=val, bg="gray", fg="blue", font=("Digital", 15)).place(x=135, y= 110)
    ciclo= val
    pin32.ChangeDutyCycle(int(ciclo))


raiz.after(1, plot_data)
raiz.mainloop()

Answer:

I do not have deep knowledge of Python and without having the hardware with which to replicate what you have I do not dare to write the code for you, but I will share some aspects on the subject, although almost certainly more than one you have already taken into account and studied before starting your project.

Considering this is a vital support equipment, operational safety has to be key and central throughout the design.

It first completely isolates the implementation of fan operation and control from the visual interface implementation, even at the process level. They must be independent processes.

The main control parameter to consider is the air pressure, then its flow. If the pressure you measure decreases and becomes negative (if it becomes almost zero and remains the patient is most likely disconnected) it means that the patient is inhaling and that the air flow provided by the ventilator may not be sufficient. If it is positive, then you have to apply control mechanisms to limit it.

There are several modes of operation of the fans, you can search and study them, but when it is in "support" mode and not "forced" you want to have a positive pressure that can sometimes be quasi-constant. Forced ventilation is risky, it can cause capillary rupture in the alveoli if it is not controlled correctly, so I do not think it is a good starting point,

The breathing cycle, very simplified, is inspiration-> pause-> expiration-> pause … etc … The pause is not the sigh, nor the deep inspiration, which occurs once every around 100 cycles, but a moment in which the direction of the air flow in the trachea changes and therefore there is a change in pressure gradient. To detect phase changes you can do it with the pressure sensor.

If you have the sensors and everything connected and available, you can set the fan to a constant speed, connect the mask, and capture the data for 15 ~ 20 cycles with normal breathing. The simplest mode of control you can use is through a PID controller, there are a few open source projects available that you can experiment with.

I would suggest, if possible, that you create an OpenSource project with the hardware data, diagrams, etc., that others could reproduce and collaborate, although there are already several similar projects and you may be interested in collaborating on one.

Scroll to Top