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
--- /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;
+}
--- /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
--- /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
--- /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
--- /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
--- /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();
+ }
+}
--- /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
--- /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
--- /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
--- /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