Kiran Singh
Published © LGPL

MaskSpoT

Check for mask induced asphyxia using an SPO2-sensor instrumented face-mask, with a real-time monitor on an Adafruit IO dashboard.

IntermediateFull instructions provided4 hours625
MaskSpoT

Things used in this project

Hardware components

SparkFun Particle Sensor Breakout - MAX30105
SparkFun Particle Sensor Breakout - MAX30105
×1
Adafruit Feather HUZZAH with ESP8266 WiFi
Adafruit Feather HUZZAH with ESP8266 WiFi
×1
Adafruit Perma Proto Bonnet Mini Kit
Adafruit Perma Proto Bonnet Mini Kit
×1
Battery Holder, AAA x 2
Battery Holder, AAA x 2
Needs 2x AAA batteries as well. This is optional, 3.3V LiMAh batteries will work fine as well.
×1
RS PRO Single Core 0.23mm diameter Copper Wire, 1600m Long
Optional - useful as connectors on proto board and to MAX30105 with minimal elastic resistance.
×1
USB-A to Micro-USB Cable
USB-A to Micro-USB Cable
For debugging code
×1

Software apps and online services

Arduino IDE
Arduino IDE
Adafruit IO IoT Platform

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Wire Stripper & Cutter, 32-20 AWG / 0.05-0.5mm² Solid & Stranded Wires
Wire Stripper & Cutter, 32-20 AWG / 0.05-0.5mm² Solid & Stranded Wires

Story

Read more

Schematics

MaskSpoT Circuit diagram

Circuit Diagram

Code

MaskSPoT code

C/C++
Arduino sketch file to transfer data from MaskSPoT sensor to Adfruit dashboard
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"
#include "config.h"

/* set up the feeds for the BME280 */
AdafruitIO_Feed *maskOnFeed = io.feed("maskon");
AdafruitIO_Feed *SPO2Feed = io.feed("spo2");
int maskOnReading = 0;
int SPO2Reading = 0;

// Delay between sensor reads, in seconds
#define READ_DELAY 5

/* Set up MAX30105 */
MAX30105 particleSensor;

#define MAX_BRIGHTNESS 255
#define IR_THRESH_MASKOFF 180000

uint32_t irBuffer[100]; //infrared LED sensor data
uint32_t redBuffer[100];  //red LED sensor data

int32_t bufferLength; //data length
int32_t spo2; //SPO2 value
int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
int32_t heartRate; //heart rate value
int8_t validHeartRate; //indicator to show if the heart rate calculation is valid

byte pulseLED = 13; //Must be on PWM pin
byte readLED = 2; //Blinks with each data read


void setup() {
  // put your setup code here, to run once:

  Serial.begin(115200); // initialize serial communication at 115200 bits per second:

  delay(500) ;
  
  pinMode(pulseLED, OUTPUT);
  pinMode(readLED, OUTPUT);

  /* Init Sensor*/
  if (!particleSensor.begin(Wire, I2C_SPEED_FAST) ) // Use default I2C port, 400kHz speed
  {
    Serial.println(F("MAX30105 not found - checking wiring/power") );
    while (1);
  }

  /* start sampling */
  Serial.read();

  /* sensor signals */
  byte ledBrightness = 60 ;
  byte sampleAverage = 4 ;
  byte ledMode = 2 ;
  byte sampleRate = 100 ;
  int pulseWidth = 411 ;
  int adcRange = 4096 ;

  /* configure sensor with above settings */
  particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange) ;

  /* Setup Wifi and Adafruit IO */
   // Call the Adafruit IP Env Logger
  Serial.println("Adafruit IO Environmental Logger");

  // connect to io.adafruit.com
  Serial.print("Connecting to Adafruit IO");
  io.connect();

  // wait for a connection
  
  while (io.status() < AIO_CONNECTED)
  {
    Serial.print(".");
    Serial.println(io.statusText());
    delay(30000);
  }

  // we are connected
  Serial.println();
  Serial.println(io.statusText());

}

void loop() {

  /* Keep io connection running   */
  io.run();

  /* Setup size */
  bufferLength = 100; // buffer length of 100logs 4 seconds of samples at 25sps

  /* Read first 100 samples: determine signal range*/
  for (byte i = 0; i < bufferLength; i++)
  {
    while (particleSensor.available() == false )
      particleSensor.check() ; // Check sensor for new data

    redBuffer[i] = particleSensor.getRed() ;
    irBuffer[i] = particleSensor.getIR() ;
    particleSensor.nextSample() ; // Get next sample 

    /* Report for debugging */
    Serial.print(".");
    
  }
    Serial.println( F("Complete first reading") );

  /* Calculate heart rate, SPO2 after first 100 samples - first 4 secds*/
  maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate );

  /* Take samples from MAX30102: HR and SPO2 calculated every second */
 while (1) 
 {
    /* Shift last 75 samples up*/
    for (byte i=25; i<100; i++)
    {
      redBuffer[i-25] = redBuffer[i];
      irBuffer[i-25] = irBuffer[i];
    }

    /* Get latest 25 samples*/
    for (byte i=75; i<100; i++)
    {
      /* Check if data is updated */
      while (particleSensor.available() == false )
        particleSensor.check() ; // Check sensor for new data
  
      digitalWrite(readLED, !digitalRead(readLED)); // Blink with every data read
  
      /* Update buffers */
      redBuffer[i] = particleSensor.getRed();
      irBuffer[i] = particleSensor.getIR();
      particleSensor.nextSample(); // get next sample

      /* Report for debugging */
      Serial.print(".");      
    }
    Serial.println(F("Ready to update HR, SPO2"));
  
    /* After 25 samples: recompute heartrate and SPO2 */
    maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate );

    if (irBuffer[bufferLength-1] < IR_THRESH_MASKOFF) 
      maskOnReading = 0;
     else
      maskOnReading = 1;
        
    if (validSPO2 == 1) 
    {
       SPO2Reading = spo2 ; // Use last SPO2 reading until valid reading 
       if (maskOnReading == 0 )
        SPO2Reading = 0;
    }
    
    Serial.print(F("SPO2="));
    Serial.print(SPO2Reading, DEC);
    Serial.print(F(", Valid SPO2="));
    Serial.print(validSPO2, DEC);
    Serial.print(F(", Mask On = "));
    Serial.print(maskOnReading, DEC);
    Serial.print(F(", irBuffer= "));
    Serial.print(irBuffer[bufferLength-1], DEC);

  
    // send data to Adafruit IO feeds
    
    maskOnFeed->save(maskOnReading);
    SPO2Feed->save(SPO2Reading);
  
    // delay the polled loop
    delay(READ_DELAY * 1000);

 }
  
}

config.h file

C/C++
header file used by Arduino sketch
// visit io.adafruit.com if you need to create an account,
// or if you need your Adafruit IO key.
#define IO_USERNAME  "hobbeslobs"
#define IO_KEY       xxxx // populate with key

/******************************* WIFI **************************************/

// the AdafruitIO_WiFi client will work with the following boards:
//   - HUZZAH ESP8266 Breakout -> https://www.adafruit.com/products/2471
//   - Feather HUZZAH ESP8266 -> https://www.adafruit.com/products/2821

#define WIFI_SSID xxxxx   // placeholder for Wifi SSID
#define WIFI_PASS xxxxx   // placeholder for password

#include "AdafruitIO_WiFi.h"
AdafruitIO_WiFi io(IO_USERNAME, IO_KEY, WIFI_SSID, WIFI_PASS);

Adafruit IO Arduino Repo

To send data to Arduino IO

MAX30105 Sensor library

SPO2 calculations deployed

Credits

Kiran Singh

Kiran Singh

1 project • 1 follower

Comments