m

Fork of MAX30100 by Eduardo Avelar

MAX30100_PulseOximeter.cpp

Committer:
AVELARDEV
Date:
2016-11-25
Revision:
0:010b908e2187

File content as of revision 0:010b908e2187:

/*
Arduino-MAX30100 oximetry / heart rate integrated sensor library
Copyright (C) 2016  OXullo Intersecans <x@brainrapers.org>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <mbed.h>
#include "MAX30100_PulseOximeter.h"


PulseOximeter::PulseOximeter() :
    state(PULSEOXIMETER_STATE_INIT),
//    tsFirstBeatDetected(0),
//    tsLastBeatDetected(0),
//    tsLastSample(0),
//    tsLastBiasCheck(0),
//    tsLastCurrentAdjustment(0),
    redLedPower((uint8_t)RED_LED_CURRENT_START),
    onBeatDetected(NULL)
{
}

bool PulseOximeter::begin(PulseOximeterDebuggingMode debuggingMode_)
{
    debuggingMode = debuggingMode_;

    if(!hrm.begin())
        return false;
    if(!hrm.setMode(MAX30100_MODE_SPO2_HR))
        return false;
    if(!hrm.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT_START))
        return false;

    irDCRemover = DCRemover(DC_REMOVER_ALPHA);
    redDCRemover = DCRemover(DC_REMOVER_ALPHA);

    state = PULSEOXIMETER_STATE_IDLE;
    return true;
}


void PulseOximeter::update()
{
    checkSample();
    checkCurrentBias();   
}

float PulseOximeter::getHeartRate()
{
    return beatDetector.getRate();
}

uint8_t PulseOximeter::getSpO2()
{
    return spO2calculator.getSpO2();
}

uint8_t PulseOximeter::getRedLedCurrentBias()
{
    return redLedPower;
}

void PulseOximeter::setOnBeatDetectedCallback(void (*cb)())
{
    onBeatDetected = cb;
    t_Sample.start();
    t_CurrentBias.start();
}

void PulseOximeter::checkSample()
{
    if (t_Sample.read_ms() > 1.0 / SAMPLING_FREQUENCY * 1000.0) {
        t_Sample.reset();
        if(hrm.update()){
            
            float irACValue = irDCRemover.step(hrm.rawIRValue);
            float redACValue = redDCRemover.step(hrm.rawRedValue);
    
            // The signal fed to the beat detector is mirrored since the cleanest monotonic spike is below zero
            float filteredPulseValue = lpf.step(-irACValue);
            bool beatDetected = beatDetector.addSample(filteredPulseValue);
    
            if (beatDetector.getRate() > 0) {
                state = PULSEOXIMETER_STATE_DETECTING;
                spO2calculator.update(irACValue, redACValue, beatDetected);
            } else if (state == PULSEOXIMETER_STATE_DETECTING) {
                state = PULSEOXIMETER_STATE_IDLE;
                spO2calculator.reset();
            }
            /*
            switch (debuggingMode) {
                case PULSEOXIMETER_DEBUGGINGMODE_RAW_VALUES:
                    Serial.print("R:");
                    Serial.print(hrm.rawIRValue);
                    Serial.print(",");
                    Serial.println(hrm.rawRedValue);
                    break;
    
                case PULSEOXIMETER_DEBUGGINGMODE_AC_VALUES:
                    Serial.print("R:");
                    Serial.print(irACValue);
                    Serial.print(",");
                    Serial.println(redACValue);
                    break;
    
                case PULSEOXIMETER_DEBUGGINGMODE_PULSEDETECT:
                    Serial.print("R:");
                    Serial.print(filteredPulseValue);
                    Serial.print(",");
                    Serial.println(beatDetector->getCurrentThreshold());
                    break;
    
                default:
                    break;
            }
            */
            if (beatDetected && onBeatDetected) {
                onBeatDetected();
            }
        }
    }
}

void PulseOximeter::checkCurrentBias()
{
    // Follower that adjusts the red led current in order to have comparable DC baselines between
    // red and IR leds. The numbers are really magic: the less possible to avoid oscillations
    if (t_CurrentBias.read_ms() > CURRENT_ADJUSTMENT_PERIOD_MS) {
        bool changed = false;
        if (irDCRemover.getDCW() - redDCRemover.getDCW() > 70000 && redLedPower < MAX30100_LED_CURR_50MA) {
            ++redLedPower;
            changed = true;
        } else if (redDCRemover.getDCW() - irDCRemover.getDCW() > 70000 && redLedPower > 0) {
            --redLedPower;
            changed = true;
        }

        if (changed) {
            hrm.setLedsCurrent(IR_LED_CURRENT, (LEDCurrent)redLedPower);
            //tsLastCurrentAdjustment = millis();
        }

        t_CurrentBias.reset();
    }
}