Library for the Grove Earbud Heartrate Sensor
Dependents: BLE_Police_HRM_Earbud df-2014-salesforce-hrm-k64f BLE_HeartRate_ppm emoSound ... more
Revision 0:35588fbd6d5c, committed 2014-09-25
- Comitter:
- ansond
- Date:
- Thu Sep 25 21:34:48 2014 +0000
- Child:
- 1:ea14b019224f
- Commit message:
- initial checkin
Changed in this revision
GroveEarbudSensor.cpp | Show annotated file Show diff for this revision Revisions of this file |
GroveEarbudSensor.h | Show annotated file Show diff for this revision Revisions of this file |
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GroveEarbudSensor.cpp Thu Sep 25 21:34:48 2014 +0000 @@ -0,0 +1,129 @@ +/* Copyright C2014 ARM, MIT License + * + * Author: Doug Anson (doug.anson@arm.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files the "Software", to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + #include "GroveEarbudSensor.h" + + // Console logging + #define LOG_CONSOLE(...) { if (this->m_pc != NULL) this->m_pc->printf(__VA_ARGS__); } + + // Our instance + GroveEarbudSensor *_grove_earbud_sensor_instance = NULL; + + // interrupt function + void __grove_earbud_sensor_interrupt() { if (_grove_earbud_sensor_instance != NULL) _grove_earbud_sensor_instance->interrupt(); } + + // constructor + GroveEarbudSensor::GroveEarbudSensor(InterruptIn *rx,Serial *pc) { + _grove_earbud_sensor_instance = this; + this->m_rx = rx; + this->m_pc = pc; + this->m_sub = 0; + this->m_counter = 0; + this->m_data_effect = true; + this->m_cb_fn = NULL; + this->m_cb_data = NULL; + this->m_heartrate = HEARTRATE_OFF; + this->m_timer = new Timer(); + + // register the interrupt handler + if (this->m_rx != NULL) this->m_rx->rise(&__grove_earbud_sensor_interrupt); + + // start the timer and initialize the summation array + if (this->m_timer != NULL) { + // start the timer + this->m_timer->start(); + + // initialize the summation array + this->initSummationArray(); + } + } + + // destructor + GroveEarbudSensor::~GroveEarbudSensor() { + if (this->m_timer != NULL) delete this->m_timer; + } + + // initialize the summation array + void GroveEarbudSensor::initSummationArray(void) { + for(int i=0;i<(NUM_SLOTS-1);++i) this->m_temp[i]=0; + this->m_temp[NUM_SLOTS-1] = this->m_timer->read_ms(); + } + + // register callback + void GroveEarbudSensor::registerCallback(GroveEarbudSensorCallback *cb_fn,void *cb_data) { + this->m_cb_fn = cb_fn; + this->m_cb_data = cb_data; + } + + // get the current heartrate + float GroveEarbudSensor::getHeartRate(void) { return this->m_heartrate; } + + // summation method + internal callback to fire any registered callback fns + void GroveEarbudSensor::sumAndInvokeCallback(void) { + if(this->m_data_effect) { + // summation + int tmp = 60 * (NUM_SLOTS-1) * 1000; + this->m_heartrate = tmp/(this->m_temp[NUM_SLOTS-1]-this->m_temp[0]); + + // DEBUG/Log + LOG_CONSOLE("heartrate: %d bpm\r\n",this->m_heartrate); + + // invoke any callbacks we might have + if (this->m_cb_fn != NULL) { + // invoke the callback + LOG_CONSOLE("invoking callback with heartrate = %d bpm\r\n",this->m_heartrate); + (*this->m_cb_fn)(this->m_heartrate,this->m_cb_data); + } + } + this->m_data_effect = 1; //sign bit +} + + // interrupt() method for earbud + void GroveEarbudSensor::interrupt() { + this->m_temp[this->m_counter] = this->m_timer->read_ms(); + switch(this->m_counter) { + case 0: + this->m_sub=this->m_temp[this->m_counter]-this->m_temp[NUM_SLOTS-1]; + break; + default: + this->m_sub=this->m_temp[this->m_counter]-this->m_temp[this->m_counter-1]; + break; + } + if(this->m_sub > HEARTPULSE_DUTY) { + this->m_data_effect = 0; //sign bit + this->m_counter = 0; + LOG_CONSOLE("heartrate measure error. Restarting timer..\r\n"); + this->initSummationArray(); + this->m_timer->stop(); + this->m_timer->start(); + } + if (this->m_counter == (NUM_SLOTS-1) && this->m_data_effect) { + this->m_counter = 0; + this->sumAndInvokeCallback(); + } + else if(this->m_counter != (NUM_SLOTS-1) && this->m_data_effect) { + this->m_counter++; + } + else { + this->m_counter = 0; + this->m_data_effect = 1; + } + } + \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/GroveEarbudSensor.h Thu Sep 25 21:34:48 2014 +0000 @@ -0,0 +1,148 @@ +/* Copyright C2014 ARM, MIT License + * + * Author: Doug Anson (doug.anson@arm.com) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software + * and associated documentation files the "Software", to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + #ifndef _GROVE_EARBUD_SENSOR_H_ + #define _GROVE_EARBUD_SENSOR_H_ + + + #include "mbed.h" + + // ********* BEGIN Tunables ***************** + + #define NUM_SLOTS 11 // set higher for greater accuracy (but slower callback frequency).. + #define HEARTPULSE_DUTY 2000 // Change to follow your system's request. System returns error if the duty overtrips by 2 seconds. (in MS) + + #define HEARTRATE_OFF 0 // earbud sensor is offline + #define HEARTRATE_MIN 10 // min heartrate + #define HEARTRATE_MAX 250 // max heartrate + + // ********* END Tunables ***************** + + // Callback function signature + typedef void (GroveEarbudSensorCallback)(float,void *); + + /** + * GroveEarbudSensor + * GroveEarbudSensor a simple API to receive heartrate telemetry from the Grove Earbud Sensor + * + * Based upon/Credit: http://www.seeedstudio.com/wiki/Grove_-_Ear-clip_Heart_Rate_Sensor + * + * Example Project: http://mbed.org/users/ansond/code/grove-earbud-sensor-sample/ + * + * @code + + #include "mbed.h" + +// Console +Serial pc(USBTX,USBRX); + +// Blinky +DigitalOut led(LED1); + +// Our sensor as an InterruptIn +InterruptIn sensor(D0); + +// Grove Earbud Sensor include +#include "GroveEarbudSensor.h" + +// callback for receiving heartrate values +void heartrateCallback(float heartrate,void *data) { + pc.printf("Callback: heartrate = %.1f\r\n",heartrate); +} + +int main() +{ + // announce + pc.printf("Grove Earbud Sensor Example v1.0.0\r\n"); + + // allocate the earbud sensor + pc.printf("Allocating earbud sensor instance...\r\n"); + GroveEarbudSensor earbud(&sensor); + + // register our callback function + pc.printf("registering callback...\r\n"); + earbud.registerCallback(heartrateCallback); + + // begin main loop + pc.printf("Beginning main loop...\r\n"); + while (true) { + // blink... + led = !led; + wait(0.5); + + // we can also call directly + //pc.printf("Direct: heartrate = %.1f\r\n",earbud.getHeartRate()); + } +} + + * @endcode + */ + + class GroveEarbudSensor { + private: + Serial *m_pc; + InterruptIn *m_rx; + volatile unsigned long m_temp[NUM_SLOTS]; + volatile unsigned long m_sub; + volatile unsigned char m_counter; + volatile bool m_data_effect; + float m_heartrate; + GroveEarbudSensorCallback *m_cb_fn; + void *m_cb_data; + Timer *m_timer; + + public: + /** + Default constructor + @param rx input pin (for use in InterruptIn(rx)) + @param pc input Serial instance for debugging + */ + GroveEarbudSensor(InterruptIn *rx,Serial *pc = NULL); + + /** + Default destructor + */ + virtual ~GroveEarbudSensor(); + + /** + registerCallback - Register callback function + @param cb_fn - callback function of type GroveEarbudSensorCallback + @param cb_data - optional callback data to provide upon callback invocation (default: NULL) + */ + void registerCallback(GroveEarbudSensorCallback *cb_fn,void *cb_data = NULL); + + /** + getHeartRate - get the last sampled heartrate + @return heartrate - the last calculated heartrate (may also be one of HEARTRATE_OFF, HEARTRATE_MIN, or HEARTRATE_MAX) + */ + float getHeartRate(void); + + /** + interrupt() - interrupt handler for our instance - not normally invoked manually + */ + void interrupt(void); + + protected: + void initSummationArray(void); + void sumAndInvokeCallback(void); + }; + + #endif // _GROVE_EARBUD_SENSOR_H_ + \ No newline at end of file