port oxullo library Arduino
MAX30100_PulseOximeter.cpp@0:010b908e2187, 2016-11-25 (annotated)
- Committer:
- AVELARDEV
- Date:
- Fri Nov 25 00:52:54 2016 +0000
- Revision:
- 0:010b908e2187
max30100 example
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
AVELARDEV | 0:010b908e2187 | 1 | /* |
AVELARDEV | 0:010b908e2187 | 2 | Arduino-MAX30100 oximetry / heart rate integrated sensor library |
AVELARDEV | 0:010b908e2187 | 3 | Copyright (C) 2016 OXullo Intersecans <x@brainrapers.org> |
AVELARDEV | 0:010b908e2187 | 4 | This program is free software: you can redistribute it and/or modify |
AVELARDEV | 0:010b908e2187 | 5 | it under the terms of the GNU General Public License as published by |
AVELARDEV | 0:010b908e2187 | 6 | the Free Software Foundation, either version 3 of the License, or |
AVELARDEV | 0:010b908e2187 | 7 | (at your option) any later version. |
AVELARDEV | 0:010b908e2187 | 8 | This program is distributed in the hope that it will be useful, |
AVELARDEV | 0:010b908e2187 | 9 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
AVELARDEV | 0:010b908e2187 | 10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
AVELARDEV | 0:010b908e2187 | 11 | GNU General Public License for more details. |
AVELARDEV | 0:010b908e2187 | 12 | You should have received a copy of the GNU General Public License |
AVELARDEV | 0:010b908e2187 | 13 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
AVELARDEV | 0:010b908e2187 | 14 | */ |
AVELARDEV | 0:010b908e2187 | 15 | |
AVELARDEV | 0:010b908e2187 | 16 | #include <mbed.h> |
AVELARDEV | 0:010b908e2187 | 17 | #include "MAX30100_PulseOximeter.h" |
AVELARDEV | 0:010b908e2187 | 18 | |
AVELARDEV | 0:010b908e2187 | 19 | |
AVELARDEV | 0:010b908e2187 | 20 | PulseOximeter::PulseOximeter() : |
AVELARDEV | 0:010b908e2187 | 21 | state(PULSEOXIMETER_STATE_INIT), |
AVELARDEV | 0:010b908e2187 | 22 | // tsFirstBeatDetected(0), |
AVELARDEV | 0:010b908e2187 | 23 | // tsLastBeatDetected(0), |
AVELARDEV | 0:010b908e2187 | 24 | // tsLastSample(0), |
AVELARDEV | 0:010b908e2187 | 25 | // tsLastBiasCheck(0), |
AVELARDEV | 0:010b908e2187 | 26 | // tsLastCurrentAdjustment(0), |
AVELARDEV | 0:010b908e2187 | 27 | redLedPower((uint8_t)RED_LED_CURRENT_START), |
AVELARDEV | 0:010b908e2187 | 28 | onBeatDetected(NULL) |
AVELARDEV | 0:010b908e2187 | 29 | { |
AVELARDEV | 0:010b908e2187 | 30 | } |
AVELARDEV | 0:010b908e2187 | 31 | |
AVELARDEV | 0:010b908e2187 | 32 | bool PulseOximeter::begin(PulseOximeterDebuggingMode debuggingMode_) |
AVELARDEV | 0:010b908e2187 | 33 | { |
AVELARDEV | 0:010b908e2187 | 34 | debuggingMode = debuggingMode_; |
AVELARDEV | 0:010b908e2187 | 35 | |
AVELARDEV | 0:010b908e2187 | 36 | if(!hrm.begin()) |
AVELARDEV | 0:010b908e2187 | 37 | return false; |
AVELARDEV | 0:010b908e2187 | 38 | if(!hrm.setMode(MAX30100_MODE_SPO2_HR)) |
AVELARDEV | 0:010b908e2187 | 39 | return false; |
AVELARDEV | 0:010b908e2187 | 40 | if(!hrm.setLedsCurrent(IR_LED_CURRENT, RED_LED_CURRENT_START)) |
AVELARDEV | 0:010b908e2187 | 41 | return false; |
AVELARDEV | 0:010b908e2187 | 42 | |
AVELARDEV | 0:010b908e2187 | 43 | irDCRemover = DCRemover(DC_REMOVER_ALPHA); |
AVELARDEV | 0:010b908e2187 | 44 | redDCRemover = DCRemover(DC_REMOVER_ALPHA); |
AVELARDEV | 0:010b908e2187 | 45 | |
AVELARDEV | 0:010b908e2187 | 46 | state = PULSEOXIMETER_STATE_IDLE; |
AVELARDEV | 0:010b908e2187 | 47 | return true; |
AVELARDEV | 0:010b908e2187 | 48 | } |
AVELARDEV | 0:010b908e2187 | 49 | |
AVELARDEV | 0:010b908e2187 | 50 | |
AVELARDEV | 0:010b908e2187 | 51 | void PulseOximeter::update() |
AVELARDEV | 0:010b908e2187 | 52 | { |
AVELARDEV | 0:010b908e2187 | 53 | checkSample(); |
AVELARDEV | 0:010b908e2187 | 54 | checkCurrentBias(); |
AVELARDEV | 0:010b908e2187 | 55 | } |
AVELARDEV | 0:010b908e2187 | 56 | |
AVELARDEV | 0:010b908e2187 | 57 | float PulseOximeter::getHeartRate() |
AVELARDEV | 0:010b908e2187 | 58 | { |
AVELARDEV | 0:010b908e2187 | 59 | return beatDetector.getRate(); |
AVELARDEV | 0:010b908e2187 | 60 | } |
AVELARDEV | 0:010b908e2187 | 61 | |
AVELARDEV | 0:010b908e2187 | 62 | uint8_t PulseOximeter::getSpO2() |
AVELARDEV | 0:010b908e2187 | 63 | { |
AVELARDEV | 0:010b908e2187 | 64 | return spO2calculator.getSpO2(); |
AVELARDEV | 0:010b908e2187 | 65 | } |
AVELARDEV | 0:010b908e2187 | 66 | |
AVELARDEV | 0:010b908e2187 | 67 | uint8_t PulseOximeter::getRedLedCurrentBias() |
AVELARDEV | 0:010b908e2187 | 68 | { |
AVELARDEV | 0:010b908e2187 | 69 | return redLedPower; |
AVELARDEV | 0:010b908e2187 | 70 | } |
AVELARDEV | 0:010b908e2187 | 71 | |
AVELARDEV | 0:010b908e2187 | 72 | void PulseOximeter::setOnBeatDetectedCallback(void (*cb)()) |
AVELARDEV | 0:010b908e2187 | 73 | { |
AVELARDEV | 0:010b908e2187 | 74 | onBeatDetected = cb; |
AVELARDEV | 0:010b908e2187 | 75 | t_Sample.start(); |
AVELARDEV | 0:010b908e2187 | 76 | t_CurrentBias.start(); |
AVELARDEV | 0:010b908e2187 | 77 | } |
AVELARDEV | 0:010b908e2187 | 78 | |
AVELARDEV | 0:010b908e2187 | 79 | void PulseOximeter::checkSample() |
AVELARDEV | 0:010b908e2187 | 80 | { |
AVELARDEV | 0:010b908e2187 | 81 | if (t_Sample.read_ms() > 1.0 / SAMPLING_FREQUENCY * 1000.0) { |
AVELARDEV | 0:010b908e2187 | 82 | t_Sample.reset(); |
AVELARDEV | 0:010b908e2187 | 83 | if(hrm.update()){ |
AVELARDEV | 0:010b908e2187 | 84 | |
AVELARDEV | 0:010b908e2187 | 85 | float irACValue = irDCRemover.step(hrm.rawIRValue); |
AVELARDEV | 0:010b908e2187 | 86 | float redACValue = redDCRemover.step(hrm.rawRedValue); |
AVELARDEV | 0:010b908e2187 | 87 | |
AVELARDEV | 0:010b908e2187 | 88 | // The signal fed to the beat detector is mirrored since the cleanest monotonic spike is below zero |
AVELARDEV | 0:010b908e2187 | 89 | float filteredPulseValue = lpf.step(-irACValue); |
AVELARDEV | 0:010b908e2187 | 90 | bool beatDetected = beatDetector.addSample(filteredPulseValue); |
AVELARDEV | 0:010b908e2187 | 91 | |
AVELARDEV | 0:010b908e2187 | 92 | if (beatDetector.getRate() > 0) { |
AVELARDEV | 0:010b908e2187 | 93 | state = PULSEOXIMETER_STATE_DETECTING; |
AVELARDEV | 0:010b908e2187 | 94 | spO2calculator.update(irACValue, redACValue, beatDetected); |
AVELARDEV | 0:010b908e2187 | 95 | } else if (state == PULSEOXIMETER_STATE_DETECTING) { |
AVELARDEV | 0:010b908e2187 | 96 | state = PULSEOXIMETER_STATE_IDLE; |
AVELARDEV | 0:010b908e2187 | 97 | spO2calculator.reset(); |
AVELARDEV | 0:010b908e2187 | 98 | } |
AVELARDEV | 0:010b908e2187 | 99 | /* |
AVELARDEV | 0:010b908e2187 | 100 | switch (debuggingMode) { |
AVELARDEV | 0:010b908e2187 | 101 | case PULSEOXIMETER_DEBUGGINGMODE_RAW_VALUES: |
AVELARDEV | 0:010b908e2187 | 102 | Serial.print("R:"); |
AVELARDEV | 0:010b908e2187 | 103 | Serial.print(hrm.rawIRValue); |
AVELARDEV | 0:010b908e2187 | 104 | Serial.print(","); |
AVELARDEV | 0:010b908e2187 | 105 | Serial.println(hrm.rawRedValue); |
AVELARDEV | 0:010b908e2187 | 106 | break; |
AVELARDEV | 0:010b908e2187 | 107 | |
AVELARDEV | 0:010b908e2187 | 108 | case PULSEOXIMETER_DEBUGGINGMODE_AC_VALUES: |
AVELARDEV | 0:010b908e2187 | 109 | Serial.print("R:"); |
AVELARDEV | 0:010b908e2187 | 110 | Serial.print(irACValue); |
AVELARDEV | 0:010b908e2187 | 111 | Serial.print(","); |
AVELARDEV | 0:010b908e2187 | 112 | Serial.println(redACValue); |
AVELARDEV | 0:010b908e2187 | 113 | break; |
AVELARDEV | 0:010b908e2187 | 114 | |
AVELARDEV | 0:010b908e2187 | 115 | case PULSEOXIMETER_DEBUGGINGMODE_PULSEDETECT: |
AVELARDEV | 0:010b908e2187 | 116 | Serial.print("R:"); |
AVELARDEV | 0:010b908e2187 | 117 | Serial.print(filteredPulseValue); |
AVELARDEV | 0:010b908e2187 | 118 | Serial.print(","); |
AVELARDEV | 0:010b908e2187 | 119 | Serial.println(beatDetector->getCurrentThreshold()); |
AVELARDEV | 0:010b908e2187 | 120 | break; |
AVELARDEV | 0:010b908e2187 | 121 | |
AVELARDEV | 0:010b908e2187 | 122 | default: |
AVELARDEV | 0:010b908e2187 | 123 | break; |
AVELARDEV | 0:010b908e2187 | 124 | } |
AVELARDEV | 0:010b908e2187 | 125 | */ |
AVELARDEV | 0:010b908e2187 | 126 | if (beatDetected && onBeatDetected) { |
AVELARDEV | 0:010b908e2187 | 127 | onBeatDetected(); |
AVELARDEV | 0:010b908e2187 | 128 | } |
AVELARDEV | 0:010b908e2187 | 129 | } |
AVELARDEV | 0:010b908e2187 | 130 | } |
AVELARDEV | 0:010b908e2187 | 131 | } |
AVELARDEV | 0:010b908e2187 | 132 | |
AVELARDEV | 0:010b908e2187 | 133 | void PulseOximeter::checkCurrentBias() |
AVELARDEV | 0:010b908e2187 | 134 | { |
AVELARDEV | 0:010b908e2187 | 135 | // Follower that adjusts the red led current in order to have comparable DC baselines between |
AVELARDEV | 0:010b908e2187 | 136 | // red and IR leds. The numbers are really magic: the less possible to avoid oscillations |
AVELARDEV | 0:010b908e2187 | 137 | if (t_CurrentBias.read_ms() > CURRENT_ADJUSTMENT_PERIOD_MS) { |
AVELARDEV | 0:010b908e2187 | 138 | bool changed = false; |
AVELARDEV | 0:010b908e2187 | 139 | if (irDCRemover.getDCW() - redDCRemover.getDCW() > 70000 && redLedPower < MAX30100_LED_CURR_50MA) { |
AVELARDEV | 0:010b908e2187 | 140 | ++redLedPower; |
AVELARDEV | 0:010b908e2187 | 141 | changed = true; |
AVELARDEV | 0:010b908e2187 | 142 | } else if (redDCRemover.getDCW() - irDCRemover.getDCW() > 70000 && redLedPower > 0) { |
AVELARDEV | 0:010b908e2187 | 143 | --redLedPower; |
AVELARDEV | 0:010b908e2187 | 144 | changed = true; |
AVELARDEV | 0:010b908e2187 | 145 | } |
AVELARDEV | 0:010b908e2187 | 146 | |
AVELARDEV | 0:010b908e2187 | 147 | if (changed) { |
AVELARDEV | 0:010b908e2187 | 148 | hrm.setLedsCurrent(IR_LED_CURRENT, (LEDCurrent)redLedPower); |
AVELARDEV | 0:010b908e2187 | 149 | //tsLastCurrentAdjustment = millis(); |
AVELARDEV | 0:010b908e2187 | 150 | } |
AVELARDEV | 0:010b908e2187 | 151 | |
AVELARDEV | 0:010b908e2187 | 152 | t_CurrentBias.reset(); |
AVELARDEV | 0:010b908e2187 | 153 | } |
AVELARDEV | 0:010b908e2187 | 154 | } |