IoT用クラウドサービス「Ambient」と心拍センサーを使った心拍モニターです。心拍センサー「Pulse Sensor Amped」の値をmbed「Simple IoT Board」で読み、「Ambient」に送信してモニターします。 https://ambidata.io
Dependencies: AmbientLib SimpleIoTBoardLib mbed
pulseSensor.cpp@0:16d0c9ce5afb, 2016-06-04 (annotated)
- Committer:
- AmbientData
- Date:
- Sat Jun 04 02:35:48 2016 +0000
- Revision:
- 0:16d0c9ce5afb
- Child:
- 1:0053efdb355e
Initial submission
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
AmbientData | 0:16d0c9ce5afb | 1 | #include "mbed.h" |
AmbientData | 0:16d0c9ce5afb | 2 | |
AmbientData | 0:16d0c9ce5afb | 3 | AnalogIn pulsePin(dp13); |
AmbientData | 0:16d0c9ce5afb | 4 | DigitalOut led(dp6); |
AmbientData | 0:16d0c9ce5afb | 5 | |
AmbientData | 0:16d0c9ce5afb | 6 | #define SAMPLING 2000.0f // Sampling period in micro seconds 2msec |
AmbientData | 0:16d0c9ce5afb | 7 | |
AmbientData | 0:16d0c9ce5afb | 8 | volatile int rate[10]; // array to hold last ten IBI values |
AmbientData | 0:16d0c9ce5afb | 9 | volatile unsigned long sampleCounter = 0; // used to determine pulse timing |
AmbientData | 0:16d0c9ce5afb | 10 | volatile unsigned long lastBeatTime = 0; // used to find IBI |
AmbientData | 0:16d0c9ce5afb | 11 | volatile int P =512; // used to find peak in pulse wave, seeded |
AmbientData | 0:16d0c9ce5afb | 12 | volatile int T = 512; // used to find trough in pulse wave, seeded |
AmbientData | 0:16d0c9ce5afb | 13 | volatile int thresh = 525; // used to find instant moment of heart beat, seeded |
AmbientData | 0:16d0c9ce5afb | 14 | volatile int amp = 100; // used to hold amplitude of pulse waveform, seeded |
AmbientData | 0:16d0c9ce5afb | 15 | volatile bool firstBeat = true; // used to seed rate array so we startup with reasonable BPM |
AmbientData | 0:16d0c9ce5afb | 16 | volatile bool secondBeat = false; // used to seed rate array so we startup with reasonable BPM |
AmbientData | 0:16d0c9ce5afb | 17 | |
AmbientData | 0:16d0c9ce5afb | 18 | // Volatile Variables, used in the interrupt service routine! |
AmbientData | 0:16d0c9ce5afb | 19 | volatile int BPM; // int that holds raw Analog in 0. updated every 2mS |
AmbientData | 0:16d0c9ce5afb | 20 | volatile int Signal; // holds the incoming raw data |
AmbientData | 0:16d0c9ce5afb | 21 | volatile int IBI = 600; // int that holds the time interval between beats! Must be seeded! |
AmbientData | 0:16d0c9ce5afb | 22 | volatile bool Pulse = false; // "True" when User's live heartbeat is detected. "False" when not a "live beat". |
AmbientData | 0:16d0c9ce5afb | 23 | volatile bool QS = false; // becomes true when Arduoino finds a beat. |
AmbientData | 0:16d0c9ce5afb | 24 | |
AmbientData | 0:16d0c9ce5afb | 25 | Ticker t2; |
AmbientData | 0:16d0c9ce5afb | 26 | |
AmbientData | 0:16d0c9ce5afb | 27 | // THIS IS THE TICKER INTERRUPT SERVICE ROUTINE. |
AmbientData | 0:16d0c9ce5afb | 28 | // Ticker makes sure that we take a reading every 2 miliseconds |
AmbientData | 0:16d0c9ce5afb | 29 | void sampling() { // triggered every 2 miliseconds |
AmbientData | 0:16d0c9ce5afb | 30 | Signal = (int)(pulsePin.read()*1023); // read the Pulse Sensor |
AmbientData | 0:16d0c9ce5afb | 31 | sampleCounter += 2; // keep track of the time in mS with this variable |
AmbientData | 0:16d0c9ce5afb | 32 | int N = sampleCounter - lastBeatTime; // monitor the time since the last beat to avoid noise |
AmbientData | 0:16d0c9ce5afb | 33 | |
AmbientData | 0:16d0c9ce5afb | 34 | // find the peak and trough of the pulse wave |
AmbientData | 0:16d0c9ce5afb | 35 | if(Signal < thresh && N > (IBI/5)*3){ // avoid dichrotic noise by waiting 3/5 of last IBI |
AmbientData | 0:16d0c9ce5afb | 36 | if (Signal < T){ // T is the trough |
AmbientData | 0:16d0c9ce5afb | 37 | T = Signal; // keep track of lowest point in pulse wave |
AmbientData | 0:16d0c9ce5afb | 38 | } |
AmbientData | 0:16d0c9ce5afb | 39 | } |
AmbientData | 0:16d0c9ce5afb | 40 | |
AmbientData | 0:16d0c9ce5afb | 41 | if(Signal > thresh && Signal > P){ // thresh condition helps avoid noise |
AmbientData | 0:16d0c9ce5afb | 42 | P = Signal; // P is the peak |
AmbientData | 0:16d0c9ce5afb | 43 | } // keep track of highest point in pulse wave |
AmbientData | 0:16d0c9ce5afb | 44 | |
AmbientData | 0:16d0c9ce5afb | 45 | // NOW IT'S TIME TO LOOK FOR THE HEART BEAT |
AmbientData | 0:16d0c9ce5afb | 46 | // signal surges up in value every time there is a pulse |
AmbientData | 0:16d0c9ce5afb | 47 | if (N > 250){ // avoid high frequency noise |
AmbientData | 0:16d0c9ce5afb | 48 | if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ){ |
AmbientData | 0:16d0c9ce5afb | 49 | Pulse = true; // set the Pulse flag when we think there is a pulse |
AmbientData | 0:16d0c9ce5afb | 50 | IBI = sampleCounter - lastBeatTime; // measure time between beats in mS |
AmbientData | 0:16d0c9ce5afb | 51 | lastBeatTime = sampleCounter; // keep track of time for next pulse |
AmbientData | 0:16d0c9ce5afb | 52 | |
AmbientData | 0:16d0c9ce5afb | 53 | if(secondBeat){ // if this is the second beat, if secondBeat == TRUE |
AmbientData | 0:16d0c9ce5afb | 54 | secondBeat = false; // clear secondBeat flag |
AmbientData | 0:16d0c9ce5afb | 55 | for(int i=0; i<=9; i++){ // seed the running total to get a realisitic BPM at startup |
AmbientData | 0:16d0c9ce5afb | 56 | rate[i] = IBI; |
AmbientData | 0:16d0c9ce5afb | 57 | } |
AmbientData | 0:16d0c9ce5afb | 58 | } |
AmbientData | 0:16d0c9ce5afb | 59 | |
AmbientData | 0:16d0c9ce5afb | 60 | if(firstBeat){ // if it's the first time we found a beat, if firstBeat == TRUE |
AmbientData | 0:16d0c9ce5afb | 61 | firstBeat = false; // clear firstBeat flag |
AmbientData | 0:16d0c9ce5afb | 62 | secondBeat = true; // set the second beat flag |
AmbientData | 0:16d0c9ce5afb | 63 | return; // IBI value is unreliable so discard it |
AmbientData | 0:16d0c9ce5afb | 64 | } |
AmbientData | 0:16d0c9ce5afb | 65 | |
AmbientData | 0:16d0c9ce5afb | 66 | // keep a running total of the last 10 IBI values |
AmbientData | 0:16d0c9ce5afb | 67 | unsigned runningTotal = 0; // clear the runningTotal variable |
AmbientData | 0:16d0c9ce5afb | 68 | |
AmbientData | 0:16d0c9ce5afb | 69 | for(int i=0; i<=8; i++){ // shift data in the rate array |
AmbientData | 0:16d0c9ce5afb | 70 | rate[i] = rate[i+1]; // and drop the oldest IBI value |
AmbientData | 0:16d0c9ce5afb | 71 | runningTotal += rate[i]; // add up the 9 oldest IBI values |
AmbientData | 0:16d0c9ce5afb | 72 | } |
AmbientData | 0:16d0c9ce5afb | 73 | |
AmbientData | 0:16d0c9ce5afb | 74 | rate[9] = IBI; // add the latest IBI to the rate array |
AmbientData | 0:16d0c9ce5afb | 75 | runningTotal += rate[9]; // add the latest IBI to runningTotal |
AmbientData | 0:16d0c9ce5afb | 76 | runningTotal /= 10; // average the last 10 IBI values |
AmbientData | 0:16d0c9ce5afb | 77 | BPM = 60000/runningTotal; // how many beats can fit into a minute? that's BPM! |
AmbientData | 0:16d0c9ce5afb | 78 | QS = true; // set Quantified Self flag |
AmbientData | 0:16d0c9ce5afb | 79 | // QS FLAG IS NOT CLEARED INSIDE THIS ISR |
AmbientData | 0:16d0c9ce5afb | 80 | led = 1; |
AmbientData | 0:16d0c9ce5afb | 81 | } |
AmbientData | 0:16d0c9ce5afb | 82 | } |
AmbientData | 0:16d0c9ce5afb | 83 | |
AmbientData | 0:16d0c9ce5afb | 84 | if (Signal < thresh && Pulse == true){ // when the values are going down, the beat is over |
AmbientData | 0:16d0c9ce5afb | 85 | Pulse = false; // reset the Pulse flag so we can do it again |
AmbientData | 0:16d0c9ce5afb | 86 | amp = P - T; // get amplitude of the pulse wave |
AmbientData | 0:16d0c9ce5afb | 87 | thresh = amp/2 + T; // set thresh at 50% of the amplitude |
AmbientData | 0:16d0c9ce5afb | 88 | P = thresh; // reset these for next time |
AmbientData | 0:16d0c9ce5afb | 89 | T = thresh; |
AmbientData | 0:16d0c9ce5afb | 90 | led = 0; |
AmbientData | 0:16d0c9ce5afb | 91 | } |
AmbientData | 0:16d0c9ce5afb | 92 | |
AmbientData | 0:16d0c9ce5afb | 93 | if (N > 2500){ // if 2.5 seconds go by without a beat |
AmbientData | 0:16d0c9ce5afb | 94 | thresh = 512; // set thresh default |
AmbientData | 0:16d0c9ce5afb | 95 | P = 512; // set P default |
AmbientData | 0:16d0c9ce5afb | 96 | T = 512; // set T default |
AmbientData | 0:16d0c9ce5afb | 97 | lastBeatTime = sampleCounter; // bring the lastBeatTime up to date |
AmbientData | 0:16d0c9ce5afb | 98 | firstBeat = true; // set these to avoid noise |
AmbientData | 0:16d0c9ce5afb | 99 | secondBeat = false; // when we get the heartbeat back |
AmbientData | 0:16d0c9ce5afb | 100 | } |
AmbientData | 0:16d0c9ce5afb | 101 | |
AmbientData | 0:16d0c9ce5afb | 102 | } |
AmbientData | 0:16d0c9ce5afb | 103 | |
AmbientData | 0:16d0c9ce5afb | 104 | void interruptSetup(){ |
AmbientData | 0:16d0c9ce5afb | 105 | // Initializes Ticker to throw an interrupt every 2mS. |
AmbientData | 0:16d0c9ce5afb | 106 | t2.attach_us(&sampling, SAMPLING); |
AmbientData | 0:16d0c9ce5afb | 107 | } |
AmbientData | 0:16d0c9ce5afb | 108 | |
AmbientData | 0:16d0c9ce5afb | 109 |