Mig Mad
Published © CC BY

Emergency DIY Ventilator

ReaMima, is the open and free hardware design so that anyone can help by building respirators with easily available materials.

AdvancedWork in progress10 hours1,365
Emergency DIY Ventilator

Things used in this project

Hardware components

ESP32 Basic Core IoT Development Kit
M5Stack ESP32 Basic Core IoT Development Kit
×1
SparkFun Atmospheric Sensor Breakout - BME280
SparkFun Atmospheric Sensor Breakout - BME280
×1
BME680 Breakout
Pimoroni BME680 Breakout
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Scissor, Electrician
Scissor, Electrician

Story

Read more

Schematics

basic scheme of operation

Code

Reamima Code

Arduino
Reamima Code
/*******************************************************************
Prueba Miguel para ReaMima
                Marzo 2020
 V 0.6
*******************************************************************/

#include <M5Stack.h>
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>

/***************************************************************************
 This is a library for the BME280 humidity, temperature & pressure sensor
 Designed specifically to work with the Adafruit BME280 Breakout
 ----> http://www.adafruit.com/products/2650
 These sensors use I2C or SPI to communicate, 2 or 4 pins are required
 to interface. The device's I2C address is either 0x76 or 0x77.
 Adafruit invests time and resources providing this open source code,
 please support Adafruit andopen-source hardware by purchasing products
 from Adafruit!
 Written by Limor Fried & Kevin Townsend for Adafruit Industries.
 BSD license, all text above must be included in any redistribution
 See the LICENSE file for details.
***************************************************************************/

#define BME_SCK 22 //SCL 22 el conector Grove Integrado
#define BME_MOSI 21 //SDA 21 el connector Grove Integrado
#define SEALEVELPRESSURE_HPA (1013.25)

Adafruit_BME280 bme; // I2C

float k = 2 * 3.1416 /360 ;

unsigned long delayTime,prevMillis,interval,tacu;
int temp;float presion;float humedad;
int fase=1; // fase =1, inspiracion, 2 pausa, 3 expiracion.
long resp=0; // contador de respiraciones
int alarma=0; // Codigo de alarma

float frecuencia = 1.5;
int ciclo,volumenCorriente=500,ti,tp,te,tv,tc,tiempocambio,cambiando=0; // VolumenCorriente, 6-10 ml/kg de peso ideal
int IE1=1,IE2=1;// IE, relacion inspiracion/expiracion, 1:3, epoc 1:4 IE1=1;IE2=3
float presionInicio,presionPico,presionMeseta,PEEP = 2.0;
float presionResistida,difPresion,alarmaMaxima=6.0,alarmaBanda=2.0,sensibilidad,IPAP,EPAP;
float preInst [330],preMedia [330]; 
float preAlarma;

int bomba1 = 2 ;int bomba2 = 5; // Salidas digitales reles inspiracion

// PresionPico, máximo inicial, Presion resistida = pico - meseta, 
// Velocidad flujo respiratorio, de 60 a 120 l/min
// Peep aprox. 5cm+, presion base para mantener abiertos alveolos
// Sensibilidad, si detecta -2cm aire ayuda a respirar
// IPAP presion positiva inspiracion, 10-15 cm H2O , EPAP , presion de expiracion, 5-8 cm H2O
// Para SDRA, 6ml/kg, F=25/min, Vflujo=60 L/min, Peep 15 cm H2O
// ti,tp,te,tv,tc,tacu; tiempo de inspiracion,pausa,expiracion,visualización, ciclo=ti+tp+te, tacu tiempo acumulado


//__________________________ SET UP __________________________________________________________
void setup() {
   M5.begin();
  Serial.begin(115200);
  Serial.println(F("Mig Mad "));
  
  pinMode(bomba1,OUTPUT);
  pinMode(bomba2, OUTPUT); 
  
  bool status;

  status = bme.begin(0x76);  
  if (!status) {
    Serial.println("No se puede encontrar un sensor de presion valido, comprueba el cableado!");
    while (1);
  }

  delayTime = 10;

  Serial.println();
  M5.Lcd.fillScreen(TFT_BLACK);
   M5.Lcd.setTextColor(TFT_RED, TFT_BLACK);
  M5.Lcd.drawString(" Espera inicio", 80, 160, 4);

   M5.Lcd.setTextColor(TFT_BLACK,TFT_YELLOW ); M5.Lcd.fillTriangle(180, 120,250,0, 320, 120, TFT_YELLOW); 
   M5.Lcd.drawString("ESPERA", 200, 90, 4);  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);

   
   M5.update();
}


//______________________________________________ Loop _________________________________________________
void loop() { 
  calculos();
  tiempos();
  sensor();
  dibuja();
  cambia();
  switch (fase){
    case 1: //Inspiracion
     inspiracion();
    break;
    case 3:  // Expiración
     expiracion();
    break;
    default: 
    break;
  }
  
  alarmas();
 M5.update();
}
//____________________________________________________________________________________________________

void inspiracion(){
   digitalWrite(bomba1, HIGH);digitalWrite(bomba2, LOW); 
if (preInst[ciclo]>=presionPico){presionPico=preInst[ciclo];}
if (ciclo==1 && resp>=1){presionPico=0.0;}
if (resp>=1){
 if (resp>=1){
M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); 
   M5.Lcd.drawFloat(presionPico,2, 10, 190, 2);  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);  
Serial.print(" fase ");Serial.println(fase );

}}}

void pausa(){
  
}
void expiracion(){
   digitalWrite(bomba1, LOW);digitalWrite(bomba2, HIGH); 
}
void cambia(){
 
if (M5.BtnA.wasReleased()) {cambiando+=1;Serial.print(" Cambiando = ++++++1 ");}
if (cambiando>10){cambiando=0;}

 Serial.print(" Cambiando ");Serial.println(cambiando); 
switch (cambiando){
    case 1: //Frecuencia
    M5.Lcd.setTextColor(TFT_BLACK,TFT_WHITE);
    M5.Lcd.drawFloat(frecuencia,0, 60, 190, 2);
if (M5.BtnB.wasReleased()) {frecuencia+=1;}
if (M5.BtnC.wasReleased()) {frecuencia-=1;}
if (frecuencia>9){frecuencia=9;}if (frecuencia<1){frecuencia=0;} // Limites
    break;
    case 2: //PEEP
   M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
   M5.Lcd.drawFloat(frecuencia,0, 60, 190, 2);
   M5.Lcd.setTextColor(TFT_BLACK,TFT_WHITE);
   M5.Lcd.drawFloat(PEEP,1, 98, 190, 2);
if (M5.BtnB.wasReleased()) {PEEP+=0.2;}
if (M5.BtnC.wasReleased()) {PEEP-=0.2;}
if (PEEP>20.0){PEEP=20.0;}if (PEEP<0.2){PEEP=0.0;} // Limites
    break;
    case 3:  // Volumen medio
   M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
   M5.Lcd.drawFloat(PEEP,1, 98, 190, 2);
   M5.Lcd.setTextColor(TFT_BLACK,TFT_WHITE);
     M5.Lcd.drawFloat(volumenCorriente,0, 138, 190, 2);
if (M5.BtnB.wasReleased()) {volumenCorriente+=50.0;}
if (M5.BtnC.wasReleased()) {volumenCorriente-=50.0;}
if (volumenCorriente>900.0){volumenCorriente=900.0;}if (volumenCorriente<100.0){volumenCorriente=100.0;} // Limites

    break;
     case 4:  // IE1
   M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
    M5.Lcd.drawFloat(volumenCorriente,0, 138, 190, 2);
   M5.Lcd.setTextColor(TFT_BLACK,TFT_WHITE);
     M5.Lcd.drawFloat(IE1,0, 185, 190, 2);
if (M5.BtnB.wasReleased()) {IE1+=1;}
if (M5.BtnC.wasReleased()) {IE1-=1;}
if (IE1>10){IE1=10;}if (IE1<1){IE1=1;} // Limites

    break;
     case 5:  // IE1
   M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
     M5.Lcd.drawFloat(IE1,0, 185, 190, 2); 
   M5.Lcd.setTextColor(TFT_BLACK,TFT_WHITE);
     M5.Lcd.drawFloat(IE2,0, 220, 190, 2); 
if (M5.BtnB.wasReleased()) {IE2+=1;}
if (M5.BtnC.wasReleased()) {IE2-=1;}
if (IE2>10){IE2=10;}if (IE2<1){IE2=1;} // Limites

    break;
        case 6:  // IE1
   M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
     M5.Lcd.drawFloat(IE2,0, 220, 190, 2); 
   M5.Lcd.setTextColor(TFT_BLACK,TFT_WHITE);
     M5.Lcd.drawFloat(alarmaBanda,0, 270, 190, 2); 
if (M5.BtnB.wasReleased()) {alarmaBanda+=0.5;}
if (M5.BtnC.wasReleased()) {alarmaBanda-=0.5;}
if (alarmaBanda>10.0){alarmaBanda=10.0;}if (alarmaBanda<0.5){alarmaBanda=0.5;} // Limites

    break;
    default:
          case 7:  // IE1
   M5.Lcd.setTextColor(TFT_WHITE,TFT_BLACK);
     M5.Lcd.drawFloat(alarmaBanda,0, 270, 190, 2); 
  cambiando=0;

    break;
  
  
   M5.Lcd.drawFloat(alarmaBanda,0, 270, 190, 2); 
    break;
  }
   } 

void calculos()
{
  tc=60000/frecuencia;  ti=(tc/(IE1+IE2))*IE1;  te=tc-ti; tv=tc/320;
  alarmaMaxima= alarmaBanda + 2.0;
  }   

void tiempos(){
// Va calculando el tiempo en milisegundos
// Calculo de intervalo con proteccion de desborde
if (millis() - prevMillis <= 0) {
  interval = prevMillis - millis();
}
else
 {
  interval = millis()-prevMillis;
  } 
prevMillis = millis();

tacu += interval; 
if (tacu>= tc){tacu=0;resp=resp+1;}
if (tacu<=ti){fase=1;}else{fase=3;}
ciclo = tacu/tv;
if (ciclo<=1){presionInicio=presion;}
if (presion-presionInicio>0){
preInst[ciclo]=presion-presionInicio;}
else
{preInst[ciclo]=0;
  }
if (resp>1){preMedia[ciclo]=((preMedia[ciclo]*5.0)+preInst[ciclo])/6.0;}else{preMedia[ciclo]=preInst[ciclo];}

 Serial.print(" tiempo ciclo ");Serial.print(interval); Serial.print(" tacu ");Serial.print(tacu);
  //Serial.print(" P inst");Serial.print(preInst[ciclo]); Serial.print(" P media");Serial.print(preMedia[ciclo]); 
  
  Serial.print(" Respiraciones : ");Serial.print(resp);
 Serial.print(" ciclo ");Serial.println(ciclo);
}
void dibuja() {
// Dibuja solo la primera vez
if (ciclo==1){M5.Lcd.fillScreen(BLACK);  M5.Lcd.drawLine( 0,120,320,120,0x7bef);
   M5.Lcd.drawLine( 0,165,320,165,WHITE);M5.Lcd.drawLine( 0,210,320,210,WHITE);M5.Lcd.drawLine( 319,165,319,210,WHITE);
   M5.Lcd.drawLine( 0,165,0,210,WHITE);M5.Lcd.drawLine( 55,165,55,210,WHITE); // Frec
   M5.Lcd.drawLine( 95,165,95,210,WHITE);M5.Lcd.drawLine( 135,165,135,210,WHITE);
   M5.Lcd.drawLine( 178,165,178,210,WHITE);  M5.Lcd.drawLine( 255,165,255,210,WHITE);
   M5.Lcd.drawLine( 0,0,0,120,0x7BEF);  M5.Lcd.setTextColor(TFT_RED, TFT_BLACK);
   
   M5.Lcd.drawString("P. Pico",5,170,2); M5.Lcd.drawString("Frec",60,170,2); M5.Lcd.drawString("PEEP",98,170,2);
   M5.Lcd.drawString("Vm",148 ,170,2);
   M5.Lcd.drawString("I E",205 ,170,2);
   M5.Lcd.drawString("P .Desvio",260 ,170,2);
   M5.Lcd.setTextColor(TFT_ORANGE, TFT_BLACK);
   M5.Lcd.drawString("Temp C : ",5 ,140,2);
   M5.Lcd.drawString("Hum % :",98 ,140,2);
    M5.Lcd.setTextColor(TFT_YELLOW, TFT_BLACK);
   M5.Lcd.drawFloat(temp,1, 60, 140, 2);
   M5.Lcd.drawFloat(humedad,1,160 , 140, 2); 
        
 // Valores
   M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK); 
   M5.Lcd.drawFloat(frecuencia,0, 60, 190, 2);
   M5.Lcd.drawFloat(PEEP,1, 98, 190, 2);
   M5.Lcd.drawFloat(volumenCorriente,0, 138, 190, 2);
   M5.Lcd.drawFloat(IE1,0, 185, 190, 2);  M5.Lcd.drawString(":",210 ,190,2);
   M5.Lcd.drawFloat(IE2,0, 220, 190, 2); 
   M5.Lcd.drawFloat(alarmaBanda,0, 270, 190, 2); 
    
  // Cursores
   M5.Lcd.fillTriangle(55, 215, 55, 235, 65, 225, WHITE); 
   M5.Lcd.fillTriangle(145, 239, 160, 215, 175, 239, WHITE); 
   M5.Lcd.fillTriangle(250, 215, 265, 239, 280, 215, WHITE); 


   ///// Grafica en tiempo real 
  if (resp>1){
  for ( int  i = 1 ; i<320 ; i++)
    {    float a2 = i , a1= i-1  ;
         float b2= 100 -( preMedia[i]*50) , b1= 100 - (preMedia[(i-1)]*50) ;  
          M5.Lcd.drawLine(a1, b1, a2, b2,0xFF80 ); 
    }  
  }
}
 ///// Grafica en tiempo real y valores en tiempo real 
  else{
  

      float x2 = ciclo, x1 = x2-1  ;        
      float y2= 120 - (preInst[ciclo]   * 50), y1= 120 - (preInst[(ciclo-1)]   * 50) ; 
      M5.Lcd.drawLine(x1, y1, x2, y2,RED );   
  }
  }


void sensor()
{
    temp = bme.readTemperature();
  presion = bme.readPressure() / 100.0F;
  humedad = bme.readHumidity();
  
  Serial.print("Temperatura = ");  Serial.print(temp);  Serial.println(" *C");
  
  Serial.print("Presion = ");  Serial.print(presion);  Serial.println(" hPa");
  
  Serial.print("Presion = ");  Serial.print(presion / 98.0665F);  Serial.println(" cm H2O");
 
  Serial.print("Humedad = ");  Serial.print(humedad);  Serial.println(" %");

  Serial.println();
}
void alarmas()
{
  
if(preInst[ciclo]>=preMedia[ciclo]+alarmaBanda && alarma==0){alarma=2;}  // Aviso presion superior a la tendencia 5 ultimas respiraciones  
if(preInst[ciclo]> alarmaMaxima && resp>=1){alarma=1;preAlarma=preInst[ciclo];}  // Aviso presion > presion pico alarma


if(alarma==1){   
   M5.Speaker.tone(461, 50); //frequency 461, with a duration of 200ms
   M5.Speaker.tone(661, 50); //frequency 661, with a duration of 200ms
   M5.Lcd.setTextColor(TFT_BLACK,TFT_YELLOW ); M5.Lcd.fillRect(180, 0,320,40, TFT_YELLOW); 
   M5.Lcd.drawString("ALARMA", 200, 10, 4);  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);
 Serial.print("*****************ALARMA 1 Sobrepresion= ");  Serial.println(preAlarma);
 
if ((ciclo/25)*25==(ciclo/50)*50){M5.Lcd.setTextColor(TFT_BLACK,TFT_YELLOW);M5.Lcd.drawString("P.Pico Alta", 230, 210, 2);}
else{M5.Lcd.setTextColor(TFT_YELLOW,TFT_BLACK);M5.Lcd.drawString("P.Pico Alta", 230, 210, 2);}

if (M5.BtnC.wasReleased()) {
    alarma=0;}
}
if(alarma==2){   
   M5.Speaker.tone(461, 10); //frequency 461, with a duration of 200ms
   M5.Speaker.tone(661, 50); //frequency 661, with a duration of 200ms
   M5.Lcd.setTextColor(TFT_BLACK,TFT_ORANGE ); M5.Lcd.fillRect(220, 0 , 320, 40, TFT_ORANGE); 
   M5.Lcd.drawString("AVISO", 230, 10, 4);  M5.Lcd.setTextColor(TFT_WHITE, TFT_BLACK);

if ((ciclo/25)*25==(ciclo/50)*50){M5.Lcd.setTextColor(TFT_BLACK,TFT_ORANGE);M5.Lcd.drawString("Tendencia", 230, 210, 2);}
else{M5.Lcd.setTextColor(TFT_ORANGE,TFT_BLACK);M5.Lcd.drawString("Tendencia", 230, 210, 2);}

if (M5.BtnC.wasReleased()) {
    alarma=0;}
}


}

Credits

Mig Mad

Mig Mad

1 project • 2 followers
Engineer
Thanks to Miguel Madrid .

Comments