IoT用クラウドサービス「Ambient」と心拍センサーを使った心拍モニターです。心拍センサー「Pulse Sensor Amped」の値をmbed「Simple IoT Board」で読み、「Ambient」に送信してモニターします。 https://ambidata.io
Dependencies: AmbientLib SimpleIoTBoardLib mbed
pulseSensor.cpp@4:ec1aa2beefce, 2016-06-13 (annotated)
- Committer:
- AmbientData
- Date:
- Mon Jun 13 12:08:40 2016 +0000
- Revision:
- 4:ec1aa2beefce
- Parent:
- 1:0053efdb355e
AmbientLib updated
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
AmbientData | 1:0053efdb355e | 1 | /* |
AmbientData | 1:0053efdb355e | 2 | * The MIT License (MIT) |
AmbientData | 1:0053efdb355e | 3 | * |
AmbientData | 1:0053efdb355e | 4 | * Copyright (c) 2015 Pulse Sensor |
AmbientData | 1:0053efdb355e | 5 | * |
AmbientData | 1:0053efdb355e | 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
AmbientData | 1:0053efdb355e | 7 | * of this software and associated documentation files (the "Software"), to deal |
AmbientData | 1:0053efdb355e | 8 | * in the Software without restriction, including without limitation the rights |
AmbientData | 1:0053efdb355e | 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
AmbientData | 1:0053efdb355e | 10 | * copies of the Software, and to permit persons to whom the Software is |
AmbientData | 1:0053efdb355e | 11 | * furnished to do so, subject to the following conditions: |
AmbientData | 1:0053efdb355e | 12 | * |
AmbientData | 1:0053efdb355e | 13 | * The above copyright notice and this permission notice shall be included in all |
AmbientData | 1:0053efdb355e | 14 | * copies or substantial portions of the Software. |
AmbientData | 1:0053efdb355e | 15 | * |
AmbientData | 1:0053efdb355e | 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
AmbientData | 1:0053efdb355e | 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
AmbientData | 1:0053efdb355e | 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
AmbientData | 1:0053efdb355e | 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
AmbientData | 1:0053efdb355e | 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
AmbientData | 1:0053efdb355e | 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
AmbientData | 1:0053efdb355e | 22 | * SOFTWARE. |
AmbientData | 1:0053efdb355e | 23 | */ |
AmbientData | 0:16d0c9ce5afb | 24 | #include "mbed.h" |
AmbientData | 0:16d0c9ce5afb | 25 | |
AmbientData | 0:16d0c9ce5afb | 26 | AnalogIn pulsePin(dp13); |
AmbientData | 0:16d0c9ce5afb | 27 | DigitalOut led(dp6); |
AmbientData | 0:16d0c9ce5afb | 28 | |
AmbientData | 0:16d0c9ce5afb | 29 | #define SAMPLING 2000.0f // Sampling period in micro seconds 2msec |
AmbientData | 0:16d0c9ce5afb | 30 | |
AmbientData | 0:16d0c9ce5afb | 31 | volatile int rate[10]; // array to hold last ten IBI values |
AmbientData | 0:16d0c9ce5afb | 32 | volatile unsigned long sampleCounter = 0; // used to determine pulse timing |
AmbientData | 0:16d0c9ce5afb | 33 | volatile unsigned long lastBeatTime = 0; // used to find IBI |
AmbientData | 0:16d0c9ce5afb | 34 | volatile int P =512; // used to find peak in pulse wave, seeded |
AmbientData | 0:16d0c9ce5afb | 35 | volatile int T = 512; // used to find trough in pulse wave, seeded |
AmbientData | 0:16d0c9ce5afb | 36 | volatile int thresh = 525; // used to find instant moment of heart beat, seeded |
AmbientData | 0:16d0c9ce5afb | 37 | volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded |
AmbientData | 0:16d0c9ce5afb | 38 | volatile bool firstBeat = true; // used to seed rate array so we startup with reasonable BPM |
AmbientData | 0:16d0c9ce5afb | 39 | volatile bool secondBeat = false; // used to seed rate array so we startup with reasonable BPM |
AmbientData | 0:16d0c9ce5afb | 40 | |
AmbientData | 0:16d0c9ce5afb | 41 | // Volatile Variables, used in the interrupt service routine! |
AmbientData | 0:16d0c9ce5afb | 42 | volatile int BPM; // int that holds raw Analog in 0. updated every 2mS |
AmbientData | 0:16d0c9ce5afb | 43 | volatile int Signal; // holds the incoming raw data |
AmbientData | 0:16d0c9ce5afb | 44 | volatile int IBI = 600; // int that holds the time interval between beats! Must be seeded! |
AmbientData | 0:16d0c9ce5afb | 45 | volatile bool Pulse = false; // "True" when User's live heartbeat is detected. "False" when not a "live beat". |
AmbientData | 0:16d0c9ce5afb | 46 | volatile bool QS = false; // becomes true when Arduoino finds a beat. |
AmbientData | 0:16d0c9ce5afb | 47 | |
AmbientData | 0:16d0c9ce5afb | 48 | Ticker t2; |
AmbientData | 0:16d0c9ce5afb | 49 | |
AmbientData | 0:16d0c9ce5afb | 50 | // THIS IS THE TICKER INTERRUPT SERVICE ROUTINE. |
AmbientData | 0:16d0c9ce5afb | 51 | // Ticker makes sure that we take a reading every 2 miliseconds |
AmbientData | 0:16d0c9ce5afb | 52 | void sampling() { // triggered every 2 miliseconds |
AmbientData | 0:16d0c9ce5afb | 53 | Signal = (int)(pulsePin.read()*1023); // read the Pulse Sensor |
AmbientData | 0:16d0c9ce5afb | 54 | sampleCounter += 2; // keep track of the time in mS with this variable |
AmbientData | 0:16d0c9ce5afb | 55 | int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise |
AmbientData | 0:16d0c9ce5afb | 56 | |
AmbientData | 0:16d0c9ce5afb | 57 | // find the peak and trough of the pulse wave |
AmbientData | 0:16d0c9ce5afb | 58 | if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI |
AmbientData | 0:16d0c9ce5afb | 59 | if (Signal < T){ // T is the trough |
AmbientData | 0:16d0c9ce5afb | 60 | T = Signal; // keep track of lowest point in pulse wave |
AmbientData | 0:16d0c9ce5afb | 61 | } |
AmbientData | 0:16d0c9ce5afb | 62 | } |
AmbientData | 0:16d0c9ce5afb | 63 | |
AmbientData | 0:16d0c9ce5afb | 64 | if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise |
AmbientData | 0:16d0c9ce5afb | 65 | P = Signal; // P is the peak |
AmbientData | 0:16d0c9ce5afb | 66 | } // keep track of highest point in pulse wave |
AmbientData | 0:16d0c9ce5afb | 67 | |
AmbientData | 0:16d0c9ce5afb | 68 | // NOW IT'S TIME TO LOOK FOR THE HEART BEAT |
AmbientData | 0:16d0c9ce5afb | 69 | // signal surges up in value every time there is a pulse |
AmbientData | 0:16d0c9ce5afb | 70 | if (N > 250){ // avoid high frequency noise |
AmbientData | 0:16d0c9ce5afb | 71 | if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){ |
AmbientData | 0:16d0c9ce5afb | 72 | Pulse = true; // set the Pulse flag when we think there is a pulse |
AmbientData | 0:16d0c9ce5afb | 73 | IBI = sampleCounter - lastBeatTime; // measure time between beats in mS |
AmbientData | 0:16d0c9ce5afb | 74 | lastBeatTime = sampleCounter; // keep track of time for next pulse |
AmbientData | 0:16d0c9ce5afb | 75 | |
AmbientData | 0:16d0c9ce5afb | 76 | if(secondBeat){ // if this is the second beat, if secondBeat == TRUE |
AmbientData | 0:16d0c9ce5afb | 77 | secondBeat = false; // clear secondBeat flag |
AmbientData | 0:16d0c9ce5afb | 78 | for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup |
AmbientData | 0:16d0c9ce5afb | 79 | rate[i] = IBI; |
AmbientData | 0:16d0c9ce5afb | 80 | } |
AmbientData | 0:16d0c9ce5afb | 81 | } |
AmbientData | 0:16d0c9ce5afb | 82 | |
AmbientData | 0:16d0c9ce5afb | 83 | if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE |
AmbientData | 0:16d0c9ce5afb | 84 | firstBeat = false; // clear firstBeat flag |
AmbientData | 0:16d0c9ce5afb | 85 | secondBeat = true; // set the second beat flag |
AmbientData | 0:16d0c9ce5afb | 86 | return; // IBI value is unreliable so discard it |
AmbientData | 0:16d0c9ce5afb | 87 | } |
AmbientData | 0:16d0c9ce5afb | 88 | |
AmbientData | 0:16d0c9ce5afb | 89 | // keep a running total of the last 10 IBI values |
AmbientData | 0:16d0c9ce5afb | 90 | unsigned runningTotal = 0; // clear the runningTotal variable |
AmbientData | 0:16d0c9ce5afb | 91 | |
AmbientData | 0:16d0c9ce5afb | 92 | for(int i=0; i<=8; i++){ // shift data in the rate array |
AmbientData | 0:16d0c9ce5afb | 93 | rate[i] = rate[i+1]; // and drop the oldest IBI value |
AmbientData | 0:16d0c9ce5afb | 94 | runningTotal += rate[i]; // add up the 9 oldest IBI values |
AmbientData | 0:16d0c9ce5afb | 95 | } |
AmbientData | 0:16d0c9ce5afb | 96 | |
AmbientData | 0:16d0c9ce5afb | 97 | rate[9] = IBI; // add the latest IBI to the rate array |
AmbientData | 0:16d0c9ce5afb | 98 | runningTotal += rate[9]; // add the latest IBI to runningTotal |
AmbientData | 0:16d0c9ce5afb | 99 | runningTotal /= 10; // average the last 10 IBI values |
AmbientData | 0:16d0c9ce5afb | 100 | BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM! |
AmbientData | 0:16d0c9ce5afb | 101 | QS = true; // set Quantified Self flag |
AmbientData | 0:16d0c9ce5afb | 102 | // QS FLAG IS NOT CLEARED INSIDE THIS ISR |
AmbientData | 0:16d0c9ce5afb | 103 | led = 1; |
AmbientData | 0:16d0c9ce5afb | 104 | } |
AmbientData | 0:16d0c9ce5afb | 105 | } |
AmbientData | 0:16d0c9ce5afb | 106 | |
AmbientData | 0:16d0c9ce5afb | 107 | if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over |
AmbientData | 0:16d0c9ce5afb | 108 | Pulse = false; // reset the Pulse flag so we can do it again |
AmbientData | 0:16d0c9ce5afb | 109 | amp = P - T; // get amplitude of the pulse wave |
AmbientData | 0:16d0c9ce5afb | 110 | thresh = amp/2 + T; // set thresh at 50% of the amplitude |
AmbientData | 0:16d0c9ce5afb | 111 | P = thresh; // reset these for next time |
AmbientData | 0:16d0c9ce5afb | 112 | T = thresh; |
AmbientData | 0:16d0c9ce5afb | 113 | led = 0; |
AmbientData | 0:16d0c9ce5afb | 114 | } |
AmbientData | 0:16d0c9ce5afb | 115 | |
AmbientData | 0:16d0c9ce5afb | 116 | if (N > 2500){ // if 2.5 seconds go by without a beat |
AmbientData | 0:16d0c9ce5afb | 117 | thresh = 512; // set thresh default |
AmbientData | 0:16d0c9ce5afb | 118 | P = 512; // set P default |
AmbientData | 0:16d0c9ce5afb | 119 | T = 512; // set T default |
AmbientData | 0:16d0c9ce5afb | 120 | lastBeatTime = sampleCounter; // bring the lastBeatTime up to date |
AmbientData | 0:16d0c9ce5afb | 121 | firstBeat = true; // set these to avoid noise |
AmbientData | 0:16d0c9ce5afb | 122 | secondBeat = false; // when we get the heartbeat back |
AmbientData | 0:16d0c9ce5afb | 123 | } |
AmbientData | 0:16d0c9ce5afb | 124 | |
AmbientData | 0:16d0c9ce5afb | 125 | } |
AmbientData | 0:16d0c9ce5afb | 126 | |
AmbientData | 0:16d0c9ce5afb | 127 | void interruptSetup(){ |
AmbientData | 0:16d0c9ce5afb | 128 | // Initializes Ticker to throw an interrupt every 2mS. |
AmbientData | 0:16d0c9ce5afb | 129 | t2.attach_us(&sampling, SAMPLING); |
AmbientData | 0:16d0c9ce5afb | 130 | } |
AmbientData | 0:16d0c9ce5afb | 131 | |
AmbientData | 0:16d0c9ce5afb | 132 |