Library for interfacing with a 'Pulse Sensor Amped', from http://pulsesensor.myshopify.com

Dependents:   finalProject PulseSense HealthCare_Graduation 4180project ... more

Simple mbed application for monitoring a pulse sensor

#include "mbed.h"
#include "PulseSensor.h"

Serial pc(USBTX, USBRX);
    

/** Print the data in a format that can be parsed by the 
 *  Processing application available here: http://pulsesensor.myshopify.com/pages/code-and-guide
 */
void sendDataToProcessing(char symbol, int data)
{
    pc.printf("%c%d\r\n", symbol, data);
}



int main() {
    
    PulseSensor sensor(p15, sendDataToProcessing);

    pc.baud(115200);
    
    sensor.start();

    while(1) {
    }
}

/media/uploads/donalm/mbed_pulsesensor_scaled.png

Files at this revision

API Documentation at this revision

Comitter:
donalm
Date:
Sun Feb 09 15:37:19 2014 +0000
Commit message:
Initial version.

Changed in this revision

PulseSensor.cpp Show annotated file Show diff for this revision Revisions of this file
PulseSensor.h Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r e80a245c4d0d PulseSensor.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PulseSensor.cpp	Sun Feb 09 15:37:19 2014 +0000
@@ -0,0 +1,152 @@
+#include "PulseSensor.h"
+
+
+PulseSensor::PulseSensor(PinName analogPin, void (*printDataCallback)(char,int), int callbackRateMs)
+{
+    _started = false;
+    
+    _pAin = new AnalogIn(analogPin);
+    
+    _callbackRateMs = callbackRateMs;
+    
+    _printDataCallback = printDataCallback;
+}
+
+
+PulseSensor::~PulseSensor()
+{
+    delete _pAin;
+}
+
+
+void PulseSensor::process_data_ticker_callback(void)
+{
+    _printDataCallback('S', Signal);     // send Processing the raw Pulse Sensor data
+    if (QS == true) { // Quantified Self flag is true when a heartbeat is found
+        //fadeRate = 255;                  // Set 'fadeRate' Variable to 255 to fade LED with pulse
+        _printDataCallback('B',BPM);   // send heart rate with a 'B' prefix
+        _printDataCallback('Q',IBI);   // send time between beats with a 'Q' prefix
+        QS = false;                      // reset the Quantified Self flag for next time
+    }
+}
+
+
+void PulseSensor::sensor_ticker_callback(void)
+{
+    Signal = 1023 * _pAin->read();              // read the Pulse Sensor
+    
+    
+    sampleCounter += 2;                         // keep track of the time in mS with this variable
+    int N = sampleCounter - lastBeatTime;       // monitor the time since the last beat to avoid noise
+
+    //  find the peak and trough of the pulse wave
+    if(Signal < thresh && N > (IBI/5)*3) {      // avoid dichrotic noise by waiting 3/5 of last IBI
+        if (Signal < T) {                       // T is the trough
+            T = Signal;                         // keep track of lowest point in pulse wave
+        }
+    }
+
+    if(Signal > thresh && Signal > P) {         // thresh condition helps avoid noise
+        P = Signal;                             // P is the peak
+    }                                        // keep track of highest point in pulse wave
+
+    //  NOW IT'S TIME TO LOOK FOR THE HEART BEAT
+    // signal surges up in value every time there is a pulse
+    if (N > 250) {                                  // avoid high frequency noise
+        if ( (Signal > thresh) && (Pulse == false) && (N > (IBI/5)*3) ) {
+            Pulse = true;                               // set the Pulse flag when we think there is a pulse
+            //digitalWrite(blinkPin,HIGH);                // turn on pin 13 LED
+            IBI = sampleCounter - lastBeatTime;         // measure time between beats in mS
+            lastBeatTime = sampleCounter;               // keep track of time for next pulse
+
+            if(firstBeat) {                        // if it's the first time we found a beat, if firstBeat == TRUE
+                firstBeat = false;                 // clear firstBeat flag
+                return;                            // IBI value is unreliable so discard it
+            }
+            if(secondBeat) {                       // if this is the second beat, if secondBeat == TRUE
+                secondBeat = false;                 // clear secondBeat flag
+                for(int i=0; i<=9; i++) {        // seed the running total to get a realisitic BPM at startup
+                    rate[i] = IBI;
+                }
+            }
+
+            // keep a running total of the last 10 IBI values
+            long runningTotal = 0;                   // clear the runningTotal variable
+
+            for(int i=0; i<=8; i++) {               // shift data in the rate array
+                rate[i] = rate[i+1];              // and drop the oldest IBI value
+                runningTotal += rate[i];          // add up the 9 oldest IBI values
+            }
+
+            rate[9] = IBI;                          // add the latest IBI to the rate array
+            runningTotal += rate[9];                // add the latest IBI to runningTotal
+            runningTotal /= 10;                     // average the last 10 IBI values
+            BPM = 60000/runningTotal;               // how many beats can fit into a minute? that's BPM!
+            QS = true;                              // set Quantified Self flag
+            // QS FLAG IS NOT CLEARED INSIDE THIS ISR
+        }
+    }
+
+    if (Signal < thresh && Pulse == true) {    // when the values are going down, the beat is over
+        Pulse = false;                         // reset the Pulse flag so we can do it again
+        amp = P - T;                           // get amplitude of the pulse wave
+        thresh = amp/2 + T;                    // set thresh at 50% of the amplitude
+        P = thresh;                            // reset these for next time
+        T = thresh;
+    }
+
+    if (N > 2500) {                            // if 2.5 seconds go by without a beat
+        thresh = 512;                          // set thresh default
+        P = 512;                               // set P default
+        T = 512;                               // set T default
+        lastBeatTime = sampleCounter;          // bring the lastBeatTime up to date
+        firstBeat = true;                      // set these to avoid noise
+        secondBeat = true;                     // when we get the heartbeat back
+    }
+}
+
+
+bool PulseSensor::start()
+{
+    if (_started == false)
+    {
+        sampleCounter = 0;
+        lastBeatTime = 0;
+        P =512;
+        T = 512;
+        thresh = 512;
+        amp = 100;
+        firstBeat = true;
+        secondBeat = true;
+        
+        BPM=0;
+        Signal=0;
+        IBI = 600;
+        Pulse = false;
+        QS = false;
+        
+        _pulseSensorTicker.attach(this, &PulseSensor::sensor_ticker_callback, ((float)_sensorTickRateMs/1000));
+        _processDataTicker.attach(this, &PulseSensor::process_data_ticker_callback,  ((float)_callbackRateMs/1000));
+        _started = true;
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
+
+bool PulseSensor::stop()
+{
+    if(_started == true)
+    {
+        _pulseSensorTicker.detach();
+        _processDataTicker.detach();
+        _started = false;
+        return true;
+    }
+    else
+    {
+        return false;
+    }
+}
\ No newline at end of file
diff -r 000000000000 -r e80a245c4d0d PulseSensor.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/PulseSensor.h	Sun Feb 09 15:37:19 2014 +0000
@@ -0,0 +1,72 @@
+#ifndef PULSE_SENSOR_H
+#define PULSE_SENSOR_H
+
+#include "mbed.h"
+
+
+/**
+ * Class for interfacing with a http://pulsesensor.myshopify.com/ 'Pulse Sensor Amped'.
+ * The contents of this class are based on the "Pulse Sensor Amped 1.1" Arduino Sketch.
+ *
+ * When using this class for the first time, it is recommended that you use the Processing 
+ * GUI app available http://pulsesensor.myshopify.com/pages/code-and-guide. Using this, you
+ * will easily be able to verify the operating of your sensor, and the integration of this
+ * class into your mbed project. 
+ */
+ 
+class PulseSensor
+{
+    private:
+        volatile int rate[10];                    // used to hold last ten IBI values
+        volatile unsigned long sampleCounter;          // used to determine pulse timing
+        volatile unsigned long lastBeatTime;           // used to find the inter beat interval
+        volatile int P;                      // used to find peak in pulse wave
+        volatile int T;                     // used to find trough in pulse wave
+        volatile int thresh;                // used to find instant moment of heart beat
+        volatile int amp;                   // used to hold amplitude of pulse waveform
+        volatile bool firstBeat;        // used to seed rate array so we startup with reasonable BPM
+        volatile bool secondBeat;       // used to seed rate array so we startup with reasonable BPM
+        
+        // these variables are volatile because they are used during the interrupt service routine!
+        volatile int BPM;                   // used to hold the pulse rate
+        volatile int Signal;                // holds the incoming raw data
+        volatile int IBI;             // holds the time between beats, the Inter-Beat Interval
+        volatile bool Pulse;        // true when pulse wave is high, false when it's low
+        volatile bool QS;           // becomes true when a beat is found
+    
+    
+        void (*_printDataCallback)(char,int);
+        static const int _sensorTickRateMs = 2;
+        int       _callbackRateMs;
+        bool      _started;
+        
+        AnalogIn *_pAin;
+        Ticker    _pulseSensorTicker;
+        Ticker    _processDataTicker;
+        
+        void sensor_ticker_callback(void);
+        void process_data_ticker_callback(void);
+    
+    public:
+        /** PulseSensor Constructor - Note this does not start the reading of the sensor.
+         * @param   analogPin Name of the analog pin that the sensor is connected to.
+         * @param   printDataCallback Pointer to function which will be called to print the latest data. Output format available here: http://pulsesensor.myshopify.com/pages/code-and-guide
+         * @param   callbackRateMs Rate at which the printDataCallback is to be called, recommended is 20ms for graphing of pulse signal.
+         */
+        PulseSensor(PinName analogPin, void (*printDataCallback)(char,int), int callbackRateMs=20);
+        
+        /** Destructor */
+        ~PulseSensor();
+        
+        /** Start reading the Pulse Sensor, and sending current readings to the print data callback.
+         * @returns true if reading of the sensor is started, false if reading was aleady in progress.
+         */
+        bool start();
+        
+        /** Stops the current reading of the Pulse Senson.
+         * @return true if reading is stopped, false if reading was already stopped.
+         */
+        bool stop();
+};
+
+#endif
\ No newline at end of file