port oxullo library Arduino
Revision 0:010b908e2187, committed 2016-11-25
- Comitter:
- AVELARDEV
- Date:
- Fri Nov 25 00:52:54 2016 +0000
- Commit message:
- max30100 example
Changed in this revision
diff -r 000000000000 -r 010b908e2187 MAX30100.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100.cpp Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,199 @@ +/* +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 "MAX30100.h" + +I2C Wire(I2C_SDA , I2C_SCL ); + +MAX30100::MAX30100() +{ + +} + +bool MAX30100::begin() +{ +// Wire.begin(); +// Wire.setClock(I2C_BUS_SPEED); + + Wire.frequency(I2C_BUS_SPEED); + + if(!setMode(DEFAULT_MODE)) + return false; + if(!setLedsPulseWidth(DEFAULT_PULSE_WIDTH)) + return false; + if(!setSamplingRate(DEFAULT_SAMPLING_RATE)) + return false; + if(!setLedsCurrent(DEFAULT_IR_LED_CURRENT, DEFAULT_RED_LED_CURRENT)) + return false; + if(!setHighresModeEnabled(true)) + return false; + + return true; +} + +bool MAX30100::setMode(Mode mode) +{ + if(!writeRegister(MAX30100_REG_MODE_CONFIGURATION, mode)) + return false; + else + return true; +} + +bool MAX30100::setLedsPulseWidth(LEDPulseWidth ledPulseWidth) +{ + uint8_t previous; + if(readRegister(MAX30100_REG_SPO2_CONFIGURATION, &previous)) + if(!writeRegister(MAX30100_REG_SPO2_CONFIGURATION, (previous & 0xfc) | ledPulseWidth)) + return false; + else + return true; + else + return false; +} + +bool MAX30100::setSamplingRate(SamplingRate samplingRate) +{ + uint8_t previous; + if(readRegister(MAX30100_REG_SPO2_CONFIGURATION, &previous)) + if(!writeRegister(MAX30100_REG_SPO2_CONFIGURATION, (previous & 0xe3) | (samplingRate << 2))) + return false; + else + return true; + else + return false; +} + +bool MAX30100::setLedsCurrent(LEDCurrent irLedCurrent, LEDCurrent redLedCurrent) +{ + if(!writeRegister(MAX30100_REG_LED_CONFIGURATION, redLedCurrent << 4 | irLedCurrent)) + return false; + else + return true; +} + +bool MAX30100::setHighresModeEnabled(bool enabled) +{ + uint8_t previous; + if(readRegister(MAX30100_REG_SPO2_CONFIGURATION, &previous)) + if (enabled) { + if(!writeRegister(MAX30100_REG_SPO2_CONFIGURATION, previous | MAX30100_SPC_SPO2_HI_RES_EN)) + return false; + else + return true; + } else { + if(!writeRegister(MAX30100_REG_SPO2_CONFIGURATION, previous & ~MAX30100_SPC_SPO2_HI_RES_EN)) + return false; + else + return true; + } + else + return false; +} + + +bool MAX30100::update() +{ + if(!readFifoData()) + return false; + else + return true; +} + + +/* +uint8_t MAX30100::readRegister(uint8_t address) +{ + Wire.beginTransmission(MAX30100_I2C_ADDRESS); + Wire.write(address); + Wire.endTransmission(false); + Wire.requestFrom(MAX30100_I2C_ADDRESS, 1); + + return Wire.read(); +} +*/ + +bool MAX30100::readRegister(uint8_t uch_addr, uint8_t *puch_data) +/** +* \brief Read a MAX30102 register +* \par Details +* This function reads a MAX30102 register +* +* \param[in] uch_addr - register address +* \param[out] puch_data - pointer that stores the register data +* +* \retval true on success +*/ +{ + char ch_i2c_data; + ch_i2c_data=uch_addr; + if(Wire.write(I2C_WRITE_ADDR, &ch_i2c_data, 1, true)!=0) + return false; + if(Wire.read(I2C_READ_ADDR, &ch_i2c_data, 1, false)==0) + { + *puch_data=(uint8_t) ch_i2c_data; + return true; + } + else + return false; +} + +/* +void MAX30100::writeRegister(uint8_t address, uint8_t data) +{ + Wire.beginTransmission(MAX30100_I2C_ADDRESS); + Wire.write(address); + Wire.write(data); + Wire.endTransmission(); +} +*/ + +bool MAX30100::writeRegister(uint8_t uch_addr, uint8_t uch_data) +/** +* \brief Write a value to a MAX30102 register +* \par Details +* This function writes a value to a MAX30102 register +* +* \param[in] uch_addr - register address +* \param[in] uch_data - register data +* +* \retval true on success +*/ +{ + char ach_i2c_data[2]; + ach_i2c_data[0]=uch_addr; + ach_i2c_data[1]=uch_data; + + if(Wire.write(I2C_WRITE_ADDR, ach_i2c_data, 2, false)==0) + return true; + else + return false; +} + +bool MAX30100::readFifoData() +{ + char ach_i2c_data[4]; + + ach_i2c_data[0]=MAX30100_REG_FIFO_DATA; + if(Wire.write(I2C_WRITE_ADDR, ach_i2c_data, 1, true)!=0) + return false; + if(Wire.read(I2C_READ_ADDR, ach_i2c_data, 4, false)!=0) + return false; + + // Warning: the values are always left-aligned + rawIRValue = (ach_i2c_data[0] << 8) | ach_i2c_data[1]; + rawRedValue = (ach_i2c_data[2] << 8) | ach_i2c_data[3]; + + return true; +}
diff -r 000000000000 -r 010b908e2187 MAX30100.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100.h Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,58 @@ +/* +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/>. +*/ + +#ifndef MAX30100_H +#define MAX30100_H + +#include <stdint.h> +#include "mbed.h" +#include "MAX30100_Registers.h" + +#define DEFAULT_MODE MAX30100_MODE_HRONLY +#define DEFAULT_SAMPLING_RATE MAX30100_SAMPRATE_100HZ +#define DEFAULT_PULSE_WIDTH MAX30100_SPC_PW_1600US_16BITS +#define DEFAULT_RED_LED_CURRENT MAX30100_LED_CURR_50MA +#define DEFAULT_IR_LED_CURRENT MAX30100_LED_CURR_50MA + +#define I2C_BUS_SPEED 400000UL + +class MAX30100 { +public: + MAX30100(); + bool begin(); + bool setMode(Mode mode); + bool setLedsPulseWidth(LEDPulseWidth ledPulseWidth); + bool setSamplingRate(SamplingRate samplingRate); + bool setLedsCurrent(LEDCurrent irLedCurrent, LEDCurrent redLedCurrent); + bool setHighresModeEnabled(bool enabled); + bool update(); + + uint16_t rawIRValue; + uint16_t rawRedValue; + +private: + /* + uint8_t readRegister(uint8_t address); + void writeRegister(uint8_t address, uint8_t data); + void burstRead(uint8_t baseAddress, uint8_t *buffer, uint8_t length); + void readFifoData(); + */ + + bool writeRegister(uint8_t uch_addr, uint8_t uch_data); + bool readRegister(uint8_t uch_addr, uint8_t *puch_data); + bool readFifoData(); +}; + +#endif \ No newline at end of file
diff -r 000000000000 -r 010b908e2187 MAX30100_BeatDetector.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100_BeatDetector.cpp Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,124 @@ +/* +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 "MAX30100_BeatDetector.h" + +BeatDetector::BeatDetector() : + state(BEATDETECTOR_STATE_INIT), + threshold(BEATDETECTOR_MIN_THRESHOLD), + beatPeriod(0), + lastMaxValue(0), + tsLastBeat(0) +{ + t.start(); +} + +bool BeatDetector::addSample(float sample) +{ + return checkForBeat(sample); +} + +float BeatDetector::getRate() +{ + if (beatPeriod != 0) { + return 1 / beatPeriod * 1000 * 60; + } else { + return 0; + } +} + +float BeatDetector::getCurrentThreshold() +{ + return threshold; +} + +bool BeatDetector::checkForBeat(float sample) +{ + bool beatDetected = false; + + switch (state) { + case BEATDETECTOR_STATE_INIT: + if (t.read_ms() > BEATDETECTOR_INIT_HOLDOFF) { + state = BEATDETECTOR_STATE_WAITING; + } + break; + + case BEATDETECTOR_STATE_WAITING: + if (sample > threshold) { + threshold = std::min((uint32_t)sample, (uint32_t)BEATDETECTOR_MAX_THRESHOLD); + state = BEATDETECTOR_STATE_FOLLOWING_SLOPE; + } + + // Tracking lost, resetting + if (t.read_ms() > BEATDETECTOR_INVALID_READOUT_DELAY) { + beatPeriod = 0; + lastMaxValue = 0; + } + + decreaseThreshold(); + break; + + case BEATDETECTOR_STATE_FOLLOWING_SLOPE: + if (sample < threshold) { + state = BEATDETECTOR_STATE_MAYBE_DETECTED; + } else { + threshold = std::min((uint32_t)sample, (uint32_t)BEATDETECTOR_MAX_THRESHOLD); + } + break; + + case BEATDETECTOR_STATE_MAYBE_DETECTED: + if (sample + BEATDETECTOR_STEP_RESILIENCY < threshold) { + // Found a beat + beatDetected = true; + lastMaxValue = sample; + state = BEATDETECTOR_STATE_MASKING; + float delta = t.read_ms(); + if (delta) { + beatPeriod = BEATDETECTOR_BPFILTER_ALPHA * delta + + (1 - BEATDETECTOR_BPFILTER_ALPHA) * beatPeriod; + } + + t.reset(); + } else { + state = BEATDETECTOR_STATE_FOLLOWING_SLOPE; + } + break; + + case BEATDETECTOR_STATE_MASKING: + if (t.read_ms() > BEATDETECTOR_MASKING_HOLDOFF) { + state = BEATDETECTOR_STATE_WAITING; + } + decreaseThreshold(); + break; + } + + return beatDetected; +} + +void BeatDetector::decreaseThreshold() +{ + // When a valid beat rate readout is present, target the + if (lastMaxValue > 0 && beatPeriod > 0) { + threshold -= lastMaxValue * (1 - BEATDETECTOR_THRESHOLD_FALLOFF_TARGET) / + (beatPeriod / BEATDETECTOR_SAMPLES_PERIOD); + } else { + // Asymptotic decay + threshold *= BEATDETECTOR_THRESHOLD_DECAY_FACTOR; + } + + if (threshold < BEATDETECTOR_MIN_THRESHOLD) { + threshold = BEATDETECTOR_MIN_THRESHOLD; + } +} \ No newline at end of file
diff -r 000000000000 -r 010b908e2187 MAX30100_BeatDetector.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100_BeatDetector.h Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,64 @@ +/* +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/>. +*/ + +#ifndef MAX30100_BEATDETECTOR_H +#define MAX30100_BEATDETECTOR_H + +#include "mbed.h" +#include <stdint.h> +#include <algorithm> + +#define BEATDETECTOR_INIT_HOLDOFF 2000 // in ms, how long to wait before counting +#define BEATDETECTOR_MASKING_HOLDOFF 200 // in ms, non-retriggerable window after beat detection +#define BEATDETECTOR_BPFILTER_ALPHA 0.6 // EMA factor for the beat period value +#define BEATDETECTOR_MIN_THRESHOLD 20 // minimum threshold (filtered) value +#define BEATDETECTOR_MAX_THRESHOLD 800 // maximum threshold (filtered) value +#define BEATDETECTOR_STEP_RESILIENCY 30 // maximum negative jump that triggers the beat edge +#define BEATDETECTOR_THRESHOLD_FALLOFF_TARGET 0.3 // thr chasing factor of the max value when beat +#define BEATDETECTOR_THRESHOLD_DECAY_FACTOR 0.99 // thr chasing factor when no beat +#define BEATDETECTOR_INVALID_READOUT_DELAY 2000 // in ms, no-beat time to cause a reset +#define BEATDETECTOR_SAMPLES_PERIOD 10 // in ms, 1/Fs + + +typedef enum BeatDetectorState { + BEATDETECTOR_STATE_INIT, + BEATDETECTOR_STATE_WAITING, + BEATDETECTOR_STATE_FOLLOWING_SLOPE, + BEATDETECTOR_STATE_MAYBE_DETECTED, + BEATDETECTOR_STATE_MASKING +} BeatDetectorState; + + +class BeatDetector +{ +public: + BeatDetector(); + bool addSample(float sample); + float getRate(); + float getCurrentThreshold(); + +private: + bool checkForBeat(float value); + void decreaseThreshold(); + + BeatDetectorState state; + float threshold; + float beatPeriod; + float lastMaxValue; + uint32_t tsLastBeat; + Timer t; +}; + +#endif \ No newline at end of file
diff -r 000000000000 -r 010b908e2187 MAX30100_Filters.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100_Filters.h Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,71 @@ +/* +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/>. +*/ + +#ifndef MAX30100_FILTERS_H +#define MAX30100_FILTERS_H + +// http://www.schwietering.com/jayduino/filtuino/ +// Low pass butterworth filter order=1 alpha1=0.1 +// Fs=100Hz, Fc=6Hz +class FilterBuLp1 +{ + public: + FilterBuLp1() + { + v[0]=0.0; + } + private: + float v[2]; + public: + float step(float x) //class II + { + v[0] = v[1]; + v[1] = (2.452372752527856026e-1 * x) + + (0.50952544949442879485 * v[0]); + return + (v[0] + v[1]); + } +}; + +// http://sam-koblenski.blogspot.de/2015/11/everyday-dsp-for-programmers-dc-and.html +class DCRemover +{ +public: + DCRemover() : alpha(0), dcw(0) + { + } + DCRemover(float alpha_) : alpha(alpha_), dcw(0) + { + } + + float step(float x) + { + float olddcw = dcw; + dcw = (float)x + alpha * dcw; + + return dcw - olddcw; + } + + float getDCW() + { + return dcw; + } + +private: + float alpha; + float dcw; +}; + +#endif \ No newline at end of file
diff -r 000000000000 -r 010b908e2187 MAX30100_PulseOximeter.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100_PulseOximeter.cpp Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,154 @@ +/* +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(); + } +}
diff -r 000000000000 -r 010b908e2187 MAX30100_PulseOximeter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100_PulseOximeter.h Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,83 @@ +/* +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/>. +*/ + +#ifndef MAX30100_PULSEOXIMETER_H +#define MAX30100_PULSEOXIMETER_H + +#define SAMPLING_FREQUENCY 100 +#define CURRENT_ADJUSTMENT_PERIOD_MS 500 +#define IR_LED_CURRENT MAX30100_LED_CURR_50MA +#define RED_LED_CURRENT_START MAX30100_LED_CURR_27_1MA +#define DC_REMOVER_ALPHA 0.95 + +#include <stdint.h> +#include <vector> + + +#include "MAX30100.h" +#include "MAX30100_BeatDetector.h" +#include "MAX30100_Filters.h" +#include "MAX30100_SpO2Calculator.h" + +typedef enum PulseOximeterState { + PULSEOXIMETER_STATE_INIT, + PULSEOXIMETER_STATE_IDLE, + PULSEOXIMETER_STATE_DETECTING +} PulseOximeterState; + +typedef enum PulseOximeterDebuggingMode { + PULSEOXIMETER_DEBUGGINGMODE_NONE, + PULSEOXIMETER_DEBUGGINGMODE_RAW_VALUES, + PULSEOXIMETER_DEBUGGINGMODE_AC_VALUES, + PULSEOXIMETER_DEBUGGINGMODE_PULSEDETECT +} PulseOximeterDebuggingMode; + + +class PulseOximeter { +public: + PulseOximeter(); + + bool begin(PulseOximeterDebuggingMode debuggingMode_=PULSEOXIMETER_DEBUGGINGMODE_NONE); + void update(); + float getHeartRate(); + uint8_t getSpO2(); + uint8_t getRedLedCurrentBias(); + void setOnBeatDetectedCallback(void (*cb)()); + +private: + void checkSample(); + void checkCurrentBias(); + + PulseOximeterState state; + PulseOximeterDebuggingMode debuggingMode; +// uint32_t tsFirstBeatDetected; +// uint32_t tsLastBeatDetected; +// uint32_t tsLastSample; +// uint32_t tsLastBiasCheck; +// uint32_t tsLastCurrentAdjustment; + BeatDetector beatDetector; + DCRemover irDCRemover; + DCRemover redDCRemover; + FilterBuLp1 lpf; + uint8_t redLedPower; + SpO2Calculator spO2calculator; + + MAX30100 hrm; + + void (*onBeatDetected)(); + Timer t_Sample; + Timer t_CurrentBias; +}; +#endif \ No newline at end of file
diff -r 000000000000 -r 010b908e2187 MAX30100_Registers.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100_Registers.h Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,108 @@ +/* +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/>. +*/ + +#ifndef MAX30100_REGISTERS_H +#define MAX30100_REGISTERS_H + +//#define MAX30100_I2C_ADDRESS 0x57 +#define I2C_WRITE_ADDR 0xAE +#define I2C_READ_ADDR 0xAF + +// Interrupt status register (RO) +#define MAX30100_REG_INTERRUPT_STATUS 0x00 +#define MAX30100_IS_PWR_RDY (1 << 0) +#define MAX30100_IS_SPO2_RDY (1 << 4) +#define MAX30100_IS_HR_RDY (1 << 5) +#define MAX30100_IS_TEMP_RDY (1 << 6) +#define MAX30100_IS_A_FULL (1 << 7) + +// Interrupt enable register +#define MAX30100_REG_INTERRUPT_ENABLE 0x01 +#define MAX30100_IE_ENB_SPO2_RDY (1 << 4) +#define MAX30100_IE_ENB_HR_RDY (1 << 5) +#define MAX30100_IE_ENB_TEMP_RDY (1 << 6) +#define MAX30100_IE_ENB_A_FULL (1 << 7) + +// FIFO control and data registers +#define MAX30100_REG_FIFO_WRITE_POINTER 0x02 +#define MAX30100_REG_FIFO_OVERFLOW_COUNTER 0x03 +#define MAX30100_REG_FIFO_READ_POINTER 0x04 +#define MAX30100_REG_FIFO_DATA 0x05 // Burst read does not autoincrement addr + +// Mode Configuration register +#define MAX30100_REG_MODE_CONFIGURATION 0x06 +#define MAX30100_MC_TEMP_EN (1 << 3) +#define MAX30100_MC_RESET (1 << 6) +#define MAX30100_MC_SHDN (1 << 7) +typedef enum Mode { + MAX30100_MODE_HRONLY = 0x02, + MAX30100_MODE_SPO2_HR = 0x03 +} Mode; + +// SpO2 Configuration register +// Check tables 8 and 9, p19 of the MAX30100 datasheet to see the permissible +// combinations of sampling rates and pulse widths +#define MAX30100_REG_SPO2_CONFIGURATION 0x07 +#define MAX30100_SPC_SPO2_HI_RES_EN (1 << 6) +typedef enum SamplingRate { + MAX30100_SAMPRATE_50HZ = 0x00, + MAX30100_SAMPRATE_100HZ = 0x01, + MAX30100_SAMPRATE_167HZ = 0x02, + MAX30100_SAMPRATE_200HZ = 0x03, + MAX30100_SAMPRATE_400HZ = 0x04, + MAX30100_SAMPRATE_600HZ = 0x05, + MAX30100_SAMPRATE_800HZ = 0x06, + MAX30100_SAMPRATE_1000HZ = 0x07 +} SamplingRate; + +typedef enum LEDPulseWidth { + MAX30100_SPC_PW_200US_13BITS = 0x00, + MAX30100_SPC_PW_400US_14BITS = 0x01, + MAX30100_SPC_PW_800US_15BITS = 0x02, + MAX30100_SPC_PW_1600US_16BITS = 0x03 +} LEDPulseWidth; + +// LED Configuration register +#define MAX30100_REG_LED_CONFIGURATION 0x09 +typedef enum LEDCurrent { + MAX30100_LED_CURR_0MA = 0x00, + MAX30100_LED_CURR_4_4MA = 0x01, + MAX30100_LED_CURR_7_6MA = 0x02, + MAX30100_LED_CURR_11MA = 0x03, + MAX30100_LED_CURR_14_2MA = 0x04, + MAX30100_LED_CURR_17_4MA = 0x05, + MAX30100_LED_CURR_20_8MA = 0x06, + MAX30100_LED_CURR_24MA = 0x07, + MAX30100_LED_CURR_27_1MA = 0x08, + MAX30100_LED_CURR_30_6MA = 0x09, + MAX30100_LED_CURR_33_8MA = 0x0a, + MAX30100_LED_CURR_37MA = 0x0b, + MAX30100_LED_CURR_40_2MA = 0x0c, + MAX30100_LED_CURR_43_6MA = 0x0d, + MAX30100_LED_CURR_46_8MA = 0x0e, + MAX30100_LED_CURR_50MA = 0x0f +} LEDCurrent; + +// Temperature integer part register +#define MAX30100_REG_TEMPERATURE_DATA_INT 0x16 +// Temperature fractional part register +#define MAX30100_REG_TEMPERATURE_DATA_FRAC 0x17 + +// Revision ID register (RO) +#define MAX30100_REG_REVISION_ID 0xfe +// Part ID register +#define MAX30100_REG_PART_ID 0xff + +#endif \ No newline at end of file
diff -r 000000000000 -r 010b908e2187 MAX30100_SpO2Calculator.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100_SpO2Calculator.cpp Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,71 @@ +/* +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 <math.h> + +#include "MAX30100_SpO2Calculator.h" + +// SaO2 Look-up Table +// http://www.ti.com/lit/an/slaa274b/slaa274b.pdf +const uint8_t SpO2Calculator::spO2LUT[43] = {100,100,100,100,99,99,99,99,99,99,98,98,98,98, + 98,97,97,97,97,97,97,96,96,96,96,96,96,95,95, + 95,95,95,95,94,94,94,94,94,93,93,93,93,93}; + +SpO2Calculator::SpO2Calculator() : + irACValueSqSum(0), + redACValueSqSum(0), + beatsDetectedNum(0), + samplesRecorded(0), + spO2(0) +{ +} + +void SpO2Calculator::update(float irACValue, float redACValue, bool beatDetected) +{ + irACValueSqSum += irACValue * irACValue; + redACValueSqSum += redACValue * redACValue; + ++samplesRecorded; + + if (beatDetected) { + ++beatsDetectedNum; + if (beatsDetectedNum == CALCULATE_EVERY_N_BEATS) { + float acSqRatio = 100.0 * log(redACValueSqSum/samplesRecorded) / log(irACValueSqSum/samplesRecorded); + uint8_t index = 0; + + if (acSqRatio > 66) { + index = (uint8_t)acSqRatio - 66; + } else if (acSqRatio > 50) { + index = (uint8_t)acSqRatio - 50; + } + reset(); + + spO2 = spO2LUT[index]; + } + } +} + +void SpO2Calculator::reset() +{ + samplesRecorded = 0; + redACValueSqSum = 0; + irACValueSqSum = 0; + beatsDetectedNum = 0; + spO2 = 0; +} + +uint8_t SpO2Calculator::getSpO2() +{ + return spO2; +} \ No newline at end of file
diff -r 000000000000 -r 010b908e2187 MAX30100_SpO2Calculator.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MAX30100_SpO2Calculator.h Fri Nov 25 00:52:54 2016 +0000 @@ -0,0 +1,41 @@ +/* +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/>. +*/ + +#ifndef MAX30100_SPO2CALCULATOR_H +#define MAX30100_SPO2CALCULATOR_H + +#include <stdint.h> + +#define CALCULATE_EVERY_N_BEATS 3 + +class SpO2Calculator { +public: + SpO2Calculator(); + + void update(float irACValue, float redACValue, bool beatDetected); + void reset(); + uint8_t getSpO2(); + +private: + static const uint8_t spO2LUT[43]; + + float irACValueSqSum; + float redACValueSqSum; + uint8_t beatsDetectedNum; + uint32_t samplesRecorded; + uint8_t spO2; +}; + +#endif \ No newline at end of file