MAX30100 pulse oxi sensor is not working with ESP32

Hi, I'm doing a project where I have to constantly monitor health (heart rate and spo2) of the patient and also his/her motion i.e. if he/she falls down then I have to notify the caregivers.

In this project, I'm using MAX30100 Pulse oxi sensor, NEO-6M GPS, ESP32, MPU6050 Accelerometer/Gyroscope. Here is the code that I'm using for fall detection and health monitoring -

#include <TinyGPS++.h>
#include "MAX30100_PulseOximeter.h"
#include <Blynk.h>
#include <Arduino.h>
#include <HardwareSerial.h>
#include <WiFi.h>
#include <HTTPClient.h>
#include <Wire.h>             
#include <BlynkSimpleEsp32.h>
#define REPORTING_PERIOD_MS 1000
#define BLYNK_PRINT Serial

void sendSMS();
String serverName = "https://maker.ifttt.com/trigger/FALL/with/key/xxxxxxxxxxxxxxxxxxxxxxxxx";

WidgetLCD lcd(V5);

boolean fall = false; //stores if a fall has occurred
boolean trigger1=false; //stores if first trigger (lower threshold) has occurred
boolean trigger2=false; //stores if second trigger (upper threshold) has occurred
boolean trigger3=false; //stores if third trigger (orientation change) has occurred
byte trigger1count=0; //stores the counts past since trigger 1 was set true
byte trigger2count=0; //stores the counts past since trigger 2 was set true
byte trigger3count=0; //stores the counts past since trigger 3 was set true
int angleChange=0, c=0;

const int MPU_addr=0x68;
int16_t accelX, accelY, accelZ, gyroX, gyroY, gyroZ;
float gForceX, gForceY, gForceZ,rotX, rotY, rotZ,Tmp;

uint32_t tsLastReport = 0;
PulseOximeter pox;
float BPM=1.00, SpO2=1.00;

float latitude , longitude;
String  latitude_string , longitiude_string;
char auth[] = "xxxxxxxxxxxxxxxxxxxx"; 

WidgetMap myMap(V0); 
WiFiClient client;
TinyGPSPlus gps;
HardwareSerial SerialGPS(2);

void onBeatDetected()
{
    Serial.println("Beat Detected!");
}

void setup()
{
  Serial.begin(115200);
  WiFi.begin("xxxxxxxxxxx", "cccccccccccc");

  uint32_t notConnectedCounter = 0;
  while (WiFi.status() != WL_CONNECTED) {
      delay(100);
      Serial.println("Wifi connecting...");
      notConnectedCounter++;
      if(notConnectedCounter > 50) { // Reset board if not connected after 5s
          Serial.println("Resetting due to Wifi not connecting...");
          ESP.restart();
      }
  }
  Serial.print("Wifi connected, IP address: ");
  Serial.println(WiFi.localIP());
 
  Blynk.begin(auth, "xxxxxxxxxxxxx", "cccccccccccccc");
  
  Wire.begin();
  Wire.beginTransmission(MPU_addr);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);
  
  SerialGPS.begin(9600, SERIAL_8N1, 16, 17);
  delay(1000);
  
  pinMode(19, OUTPUT);
  Serial.print("Initializing Pulse Oximeter..");
 
    if (!pox.begin())
    {
         Serial.println("FAILED");
         for(;;);
    }
    else
    {
         Serial.println("SUCCESS");
         pox.setOnBeatDetectedCallback(onBeatDetected);
    }
        pox.setIRLedCurrent(MAX30100_LED_CURR_7_6MA);
    Blynk.virtualWrite(V0, "clr"); 
 
}
void loop()
{
    Wire.beginTransmission(MPU_addr);
    Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
    Wire.endTransmission(false);
    Wire.requestFrom(MPU_addr,14,true);  // request a total of 14 registers
    accelX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)
    accelY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
    accelZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
    Tmp=Wire.read()<<8|Wire.read();  // 0x41 (TEMP_OUT_H) & 0x42 (TEMP_OUT_L)
    gyroX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
    gyroY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
    gyroZ=Wire.read()<<8|Wire.read();

    gForceX = (accelX-2050) / 16384.0;
    gForceY = (accelY-77) / 16384.0; 
    gForceZ = (accelZ-1947) / 16384.0;
    
      rotX = (gyroX+270) / 131.0;
      rotY = (gyroY-351) / 131.0; 
      rotZ = (gyroZ+136) / 131.0;
    
      // calculating Amplitute vactor for 3 axis
     float Raw_Amp = pow(pow(gForceX,2)+pow(gForceY,2)+pow(gForceZ,2),0.5);
     int Amp = Raw_Amp * 10;  // Mulitiplied by 10 bcz values are between 0 to 1
     //Serial.println(Amp);
    
     angleChange = pow(pow(rotX,2)+pow(rotY,2)+pow(rotZ,2),0.5);
    
    ///////////// FALL DETECTION //////////////
     if (Amp<=2 && trigger2==false){ //if AM breaks lower threshold (0.4g)
       trigger1=true;
       Serial.println("TRIGGER 1 ACTIVATED");
       }
     if (trigger1==true){
       trigger1count++;
       if (Amp>=9){ //if AM breaks upper threshold (3g)
         trigger2=true;
         Serial.println("TRIGGER 2 ACTIVATED");
         trigger1=false; trigger1count=0;
         }
     }
     if (trigger2==true){
       trigger2count++;
       angleChange = pow(pow(rotX,2)+pow(rotY,2)+pow(rotZ,2),0.5); Serial.println(angleChange);
       if (angleChange>=30 && angleChange<=400){ //if orientation changes by between 80-100 degrees
         trigger3=true; trigger2=false; trigger2count=0;
         Serial.println(angleChange);
         Serial.println("TRIGGER 3 ACTIVATED");
           }
       }
     if (trigger3==true){
        trigger3count++;
        if (trigger3count>=10){ 
           angleChange = pow(pow(rotX,2)+pow(rotY,2)+pow(rotZ,2),0.5);
           //delay(10);
           Serial.println(angleChange); 
           if ((angleChange>=0) && (angleChange<=10)){ //if orientation changes remains between 0-10 degrees
               fall=true; trigger3=false; trigger3count=0;
               Serial.println(angleChange);
                 }
           else{ //user regained normal orientation
              trigger3=false; trigger3count=0;
              Serial.println("TRIGGER 3 DEACTIVATED");
           }
         }
      }
    
      if (fall==true){ //in event of a fall detection
       Serial.println("FALL DETECTED");
       sendSMS(); 
       fall=false;
       }
     if (trigger2count>=6){ //allow 0.5s for orientation change
       trigger2=false; trigger2count=0;
       Serial.println("TRIGGER 2 DECACTIVATED");
       }
     if (trigger1count>=6){ //allow 0.5s for AM to break upper threshold
       trigger1=false; trigger1count=0;
       Serial.println("TRIGGER 1 DECACTIVATED");
       }
      delay(100);
    
      pox.update();
        
      BPM = pox.getHeartRate();
      SpO2 = pox.getSpO2();
      
      if (SerialGPS.available() > 0)
      {
        if (gps.encode(SerialGPS.read()))
        {
          if (gps.location.isValid())
          {
            latitude = gps.location.lat();
            latitude_string = String(latitude , 6);
            longitude = gps.location.lng();
            longitiude_string = String(longitude , 6);
          }
        }  
        }
    
        if (millis() - tsLastReport > REPORTING_PERIOD_MS)
        {
          Blynk.run();
          //Blynk.virtualWrite(V0, 1, latitude, longitude, "Location");
          Blynk.virtualWrite(V3, BPM);
          Blynk.virtualWrite(V4, SpO2);
          Serial.print("Acceleration: ");
          Serial.println(Amp);
          Serial.print("Angle change: ");
          Serial.println(angleChange);
    
          Serial.print("Heart rate:");
          Serial.print(BPM);
          Serial.print(" bpm / SpO2:");
          Serial.print(SpO2);
          Serial.println(" %");
    
          Serial.print("Latitude = ");
          Serial.println(latitude_string);
          Serial.print("Longitude = ");
          Serial.println(longitiude_string);
          
          tsLastReport = millis();
        }
  
}

void sendSMS()
{
    Serial.println("FALL DETECTED");
    HTTPClient http;

    String serverPath = serverName + "?value1=1234";
      
      // Your Domain name with URL path or IP address with path
    http.begin(serverPath.c_str());
      
      // Send HTTP GET request
    int httpResponseCode = http.GET();
    Serial.println(httpResponseCode);
    http.end();
    for(;;);
}

Here the problem is MAX30100 pulse sensor is not working and the rest are working well. When I removed the fall detection code then the pulse sensor works fine but now for some reason it doesn't work. Can you please help me regarding this?

@Koepel Will you please help regarding this?

Is pow(x,0.5) the same as sqrt(x) :thinking: I think so. Then it is the resulting vector.

This offset seems very specific:

  gForceX = (accelX-2050) / 16384.0;
  gForceY = (accelY-77) / 16384.0; 
  gForceZ = (accelZ-1947) / 16384.0;

If the sensor is rotated a little, then the offset is wrong.
You could measure the increase/decrease of the resulting vector.

The MPU-6050 has an internal programmable 'dmp'. I think there is fall detection code for the 'dmp'. Some sensors have it already built-in.

I did not check the structure of the code. You can start by making the text of the code look better. Make consistent use of indents, put every comma, every space at the right place.
Have you investigated how the PulseOximeter library works ? That was an open end for me in your previous question. The library fails if it can not keep track of the heart beat.

The Blynk, SMS, and HTTP functions can take some time and there is a delay of 100ms. I can't tell how responsive the code is.

How serious is this project ? You should not use Arduino for life threatening situations.

The library I am using is very peculiar when it comes to running this line of code:

pox.update();

This line updates the sensor readings and is placed inside the loop(). The author of the library emphasizes that the time between calling this function should be less than 10 ms. As it turns out, you can adjust the sampling frequency of the MAX30100 but in the library, that frequency is set to 100 Hz. Editing the sampling frequency requires the FIR coefficients of the filter used to process the data from the MAX30100.

In short, you can't put a delay of more than 10 ms or do anything that takes that time inside loop() using this code.

This is what I found out

Thanks, that makes sense and explains it.

The ESP32 runs FreeRTOS and can create extra tasks.
Without knowing the FreeRTOS, it is possible to create a few tasks if you follow this example:
https://randomnerdtutorials.com/esp32-dual-core-arduino-ide/
I would pin the task to Core 1, because Core 0 is running the Wifi things of the ESP32 and Core 1 is for Arduino things.

One task should keep the pox.update() running.
Then you have to find a way to pass that information to the other task via a global variable.

Without the ESP32, you would have to add a extra Arduino board, dedicated to run the pox.update().

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.