I've got some basic filter code setup (but not yet tested).

Dependencies:   BLE_API Queue mbed nRF51822

Fork of BLE_HeartRate by Bluetooth Low Energy

Files at this revision

API Documentation at this revision

Comitter:
roysandberg
Date:
Sun May 17 00:27:16 2015 +0000
Parent:
60:79da561d849b
Child:
62:8e2fbe131b53
Commit message:
Initial release;

Changed in this revision

filter.cpp Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
sampler.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/filter.cpp	Sun May 17 00:27:16 2015 +0000
@@ -0,0 +1,70 @@
+// Bandpass the 1000Hz sampling rate to only allow signals that could be heart rate frequencies
+// https://www.keil.com/pack/doc/CMSIS/DSP/html/arm_fir_example_f32_8c-example.html
+
+#define TEST_LENGTH_SAMPLES  320
+#define BLOCK_SIZE            32
+#define NUM_TAPS              51
+#define PER_MINUTE 60
+
+#define MIN_HEARTRATE (30.0/PER_MINUTE)
+#define MAX_HEARTRATE (120.0/PER_MINUTE)
+
+int blockSize = BLOCK_SIZE;
+int numBlocks = TEST_LENGTH_SAMPLES/BLOCK_SIZE;
+
+static float testOutput[TEST_LENGTH_SAMPLES];
+static float firStateF32[BLOCK_SIZE + NUM_TAPS - 1];
+
+// TODO: USE CORRECT FREQUENCY FOR HEART RATE. 
+// 51-tap FIR filter @1000Hz sampling rate, 30Hz-120Hz pass, 50dB attenuation: http://www.arc.id.au/FilterDesign.html
+float firCoeffs32[NUM_TAPS] = {0.000707, 
+0.000317, 
+-0.000103, 
+0.000151, 
+0.001621, 
+0.004056, 
+0.006124, 
+0.005903, 
+0.002063, 
+-0.004881, 
+-0.012196, 
+-0.016049, 
+-0.013847, 
+-0.006569, 
+0.000551, 
+-0.000000, 
+-0.013828, 
+-0.040240, 
+-0.070297, 
+-0.089104, 
+-0.082037, 
+-0.042270, 
+0.024193, 
+0.098977, 
+0.157748, 
+0.180000, 
+0.157748, 
+0.098977, 
+0.024193, 
+-0.042270, 
+-0.082037, 
+-0.089104, 
+-0.070297, 
+-0.040240, 
+-0.013828, 
+-0.000000, 
+0.000551, 
+-0.006569, 
+-0.013847, 
+-0.016049, 
+-0.012196, 
+-0.004881, 
+0.002063, 
+0.005903, 
+0.006124, 
+0.004056, 
+0.001621, 
+0.000151, 
+-0.000103, 
+0.000317, 
+0.000707};
\ No newline at end of file
--- a/main.cpp	Mon May 11 07:04:53 2015 +0000
+++ b/main.cpp	Sun May 17 00:27:16 2015 +0000
@@ -25,12 +25,17 @@
  * interval.*/
 #define UPDATE_PARAMS_FOR_LONGER_CONNECTION_INTERVAL 0
 
+extern void setup_sampler(void (*function)(uint32_t));
+
 BLEDevice  ble;
 DigitalOut led1(LED1);
 
+volatile uint8_t hrmCounter = 100; // init HRM to 100bps
+
 const static char     DEVICE_NAME[]        = "HRM1";
 static const uint16_t uuid16_list[]        = {GattService::UUID_HEART_RATE_SERVICE,
-                                              GattService::UUID_DEVICE_INFORMATION_SERVICE};
+        GattService::UUID_DEVICE_INFORMATION_SERVICE
+                                             };
 static volatile bool  triggerSensorPolling = false;
 
 void disconnectionCallback(Gap::Handle_t handle, Gap::DisconnectionReason_t reason)
@@ -38,26 +43,37 @@
     ble.startAdvertising(); // restart advertising
 }
 
-void periodicCallback(void)
+//void periodicCallback(void)
+//{
+//    led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */
+//
+//    /* Note that the periodicCallback() executes in interrupt context, so it is safer to do
+//     * heavy-weight sensor polling from the main thread. */
+//    triggerSensorPolling = true;
+//}
+
+void sampler_callback(uint32_t value)
 {
-    led1 = !led1; /* Do blinky on LED1 while we're waiting for BLE events */
-
-    /* Note that the periodicCallback() executes in interrupt context, so it is safer to do
-     * heavy-weight sensor polling from the main thread. */
+    hrmCounter = (uint8_t) (value);
+//    led1 = !led1;
     triggerSensorPolling = true;
 }
 
 int main(void)
 {
     led1 = 1;
-    Ticker ticker;
-    ticker.attach(periodicCallback, 1); // blink LED every second
+    // Ticker ticker;
+    //ticker.attach(periodicCallback, 0.1); // blink LED every second
+
+    printf("Executing code...\n");
+            
+    setup_sampler(&sampler_callback);
 
     ble.init();
     ble.onDisconnection(disconnectionCallback);
 
     /* Setup primary service. */
-    uint8_t hrmCounter = 100; // init HRM to 100bps
+    //uint8_t hrmCounter = 100; // init HRM to 100bps
     HeartRateService hrService(ble, hrmCounter, HeartRateService::LOCATION_FINGER);
 
     /* Setup auxiliary service. */
@@ -78,15 +94,18 @@
         if (triggerSensorPolling && ble.getGapState().connected) {
             triggerSensorPolling = false;
 
-            // Do blocking calls or whatever is necessary for sensor polling.
-            // In our case, we simply update the HRM measurement. 
-            hrmCounter++;
-            
-            //  100 <= HRM bps <=175
-            if (hrmCounter == 175) {
-                hrmCounter = 100;
-            }
-            
+    led1 = !led1;
+
+            printf("v=%d\n", hrmCounter);
+//            // Do blocking calls or whatever is necessary for sensor polling.
+//            // In our case, we simply update the HRM measurement.
+//            hrmCounter++;
+//
+//            //  100 <= HRM bps <=175
+//            if (hrmCounter == 175) {
+//                hrmCounter = 100;
+//            }
+
             // update bps
             hrService.updateHeartRate(hrmCounter);
         } else {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sampler.cpp	Sun May 17 00:27:16 2015 +0000
@@ -0,0 +1,36 @@
+#include "mbed.h"
+
+// Use a ticker (interrupt routine) to sample the ADC at a fast rate (is 200Khz the max?), and generate an average value
+// Use another ticker to grab the average value at 1Khz, which is the maximum useful sampling rate for HRV data collection
+
+
+// These need to divide evenly into each other
+#define SAMPLING_RATE_HZ 4000   
+#define READ_OUT_RATE_HZ 50  
+
+#define SAMPLES_PER_READOUT (SAMPLING_RATE_HZ/READ_OUT_RATE_HZ)
+
+AnalogIn ECG(P0_1);  
+Ticker sampling_rate;
+int NumReadings=0;
+uint32_t ReadingTotal=0;
+void (*SamplingFunction)(uint32_t);
+
+
+void ADC_read()
+{    
+    NumReadings++;
+    ReadingTotal += ECG.read_u16();    
+    if (NumReadings == SAMPLES_PER_READOUT) 
+    {
+        SamplingFunction(ReadingTotal/NumReadings);
+        ReadingTotal = 0;
+        NumReadings = 0;
+    }                                                                                                 
+}
+
+void setup_sampler(void (*function)(uint32_t)) 
+{
+    SamplingFunction = function;
+    sampling_rate.attach(&ADC_read, 1.0/SAMPLING_RATE_HZ);
+}