oled_sensor

Dependencies:   max32630fthr Adafruit_FeatherOLED_3 USBDevice

Files at this revision

API Documentation at this revision

Comitter:
jonaangelica
Date:
Thu Feb 13 14:55:02 2020 +0000
Commit message:
commit;

Changed in this revision

Adafruit_FeatherOLED.lib Show annotated file Show diff for this revision Revisions of this file
MAX30102/MAX30102.cpp Show annotated file Show diff for this revision Revisions of this file
MAX30102/MAX30102.h Show annotated file Show diff for this revision Revisions of this file
USBDevice.lib Show annotated file Show diff for this revision Revisions of this file
algorithm/algorithm.cpp Show annotated file Show diff for this revision Revisions of this file
algorithm/algorithm.h Show annotated file Show diff for this revision Revisions of this file
img/connection.png Show annotated file Show diff for this revision Revisions of this file
img/discovery.png Show annotated file Show diff for this revision Revisions of this file
img/notifications.png Show annotated file Show diff for this revision Revisions of this file
img/register_to_notifications.png Show annotated file Show diff for this revision Revisions of this file
img/scan_result.png Show annotated file Show diff for this revision Revisions of this file
img/start_scan.png 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
max32630fthr.lib Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
shields/TARGET_ST_BLUENRG.lib Show annotated file Show diff for this revision Revisions of this file
source/main.cpp Show annotated file Show diff for this revision Revisions of this file
source/mbed_app.json Show annotated file Show diff for this revision Revisions of this file
source/module.json Show annotated file Show diff for this revision Revisions of this file
source/readme.md Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r cc636f742803 Adafruit_FeatherOLED.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Adafruit_FeatherOLED.lib	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/teams/group2-QM/code/Adafruit_FeatherOLED_3/#4f1803f9d4e7
diff -r 000000000000 -r cc636f742803 MAX30102/MAX30102.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX30102/MAX30102.cpp	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,221 @@
+/** \file max30102.cpp ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: max30102.cpp
+* Description: This module is an embedded controller driver for the MAX30102
+*
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+* char              ch_pmod_value
+* char (array)      s_pmod_s_string[16]
+* float             f_pmod_value
+* int32_t           n_pmod_value
+* int32_t (array)   an_pmod_value[16]
+* int16_t           w_pmod_value
+* int16_t (array)   aw_pmod_value[16]
+* uint16_t          uw_pmod_value
+* uint16_t (array)  auw_pmod_value[16]
+* uint8_t           uch_pmod_value
+* uint8_t (array)   auch_pmod_buffer[16]
+* uint32_t          un_pmod_value
+* int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* 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 MAXIM INTEGRATED 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.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+#include "mbed.h"
+#include "MAX30102.h"
+
+#ifdef TARGET_MAX32630FTHR
+I2C i2c(P3_4, P3_5); // SDA, SCL
+//I2C i2c(I2C1_SDA, I2C1_SCL);
+#else
+I2C i2c(I2C_SDA, I2C_SCL);
+#endif
+
+bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data)
+/**
+* \brief        Write a value to a MAX30102 register
+* \par          Details
+*               This function writes a value to a MAX30102 register
+*
+* \param[in]    uch_addr    - register address
+* \param[in]    uch_data    - register data
+*
+* \retval       true on success
+*/
+{
+  char ach_i2c_data[2];
+  ach_i2c_data[0]=uch_addr;
+  ach_i2c_data[1]=uch_data;
+  
+  if(i2c.write(I2C_WRITE_ADDR, ach_i2c_data, 2, false)==0)
+    return true;
+  else
+    return false;
+}
+
+bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data)
+/**
+* \brief        Read a MAX30102 register
+* \par          Details
+*               This function reads a MAX30102 register
+*
+* \param[in]    uch_addr    - register address
+* \param[out]   puch_data    - pointer that stores the register data
+*
+* \retval       true on success
+*/
+{
+  char ch_i2c_data;
+  ch_i2c_data=uch_addr;
+  if(i2c.write(I2C_WRITE_ADDR, &ch_i2c_data, 1, true)!=0)
+    return false;
+  if(i2c.read(I2C_READ_ADDR, &ch_i2c_data, 1, false)==0)
+  {
+    *puch_data=(uint8_t) ch_i2c_data;
+    return true;
+  }
+  else
+    return false;
+}
+
+bool maxim_max30102_init()
+/**
+* \brief        Initialize the MAX30102
+* \par          Details
+*               This function initializes the MAX30102
+*
+* \param        None
+*
+* \retval       true on success
+*/
+{
+  if(!maxim_max30102_write_reg(REG_INTR_ENABLE_1,0xc0)) // INTR setting
+    return false;
+  if(!maxim_max30102_write_reg(REG_INTR_ENABLE_2,0x00))
+    return false;
+  if(!maxim_max30102_write_reg(REG_FIFO_WR_PTR,0x00))  //FIFO_WR_PTR[4:0]
+    return false;
+  if(!maxim_max30102_write_reg(REG_OVF_COUNTER,0x00))  //OVF_COUNTER[4:0]
+    return false;
+  if(!maxim_max30102_write_reg(REG_FIFO_RD_PTR,0x00))  //FIFO_RD_PTR[4:0]
+    return false;
+  if(!maxim_max30102_write_reg(REG_FIFO_CONFIG,0x0f))  //sample avg = 1, fifo rollover=false, fifo almost full = 17
+    return false;
+  if(!maxim_max30102_write_reg(REG_MODE_CONFIG,0x03))   //0x02 for Red only, 0x03 for SpO2 mode 0x07 multimode LED
+    return false;
+  if(!maxim_max30102_write_reg(REG_SPO2_CONFIG,0x27))  // SPO2_ADC range = 4096nA, SPO2 sample rate (100 Hz), LED pulseWidth (400uS)
+    return false;
+  
+  if(!maxim_max30102_write_reg(REG_LED1_PA,0x24))   //Choose value for ~ 7mA for LED1
+    return false;
+  if(!maxim_max30102_write_reg(REG_LED2_PA,0x24))   // Choose value for ~ 7mA for LED2
+    return false;
+  if(!maxim_max30102_write_reg(REG_PILOT_PA,0x7f))   // Choose value for ~ 25mA for Pilot LED
+    return false;
+  return true;  
+}
+
+bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led)
+/**
+* \brief        Read a set of samples from the MAX30102 FIFO register
+* \par          Details
+*               This function reads a set of samples from the MAX30102 FIFO register
+*
+* \param[out]   *pun_red_led   - pointer that stores the red LED reading data
+* \param[out]   *pun_ir_led    - pointer that stores the IR LED reading data
+*
+* \retval       true on success
+*/
+{
+  uint32_t un_temp;
+  unsigned char uch_temp;
+  *pun_red_led=0;
+  *pun_ir_led=0;
+  char ach_i2c_data[6];
+  
+  //read and clear status register
+  maxim_max30102_read_reg(REG_INTR_STATUS_1, &uch_temp);
+  maxim_max30102_read_reg(REG_INTR_STATUS_2, &uch_temp);
+  
+  ach_i2c_data[0]=REG_FIFO_DATA;
+  if(i2c.write(I2C_WRITE_ADDR, ach_i2c_data, 1, true)!=0)
+    return false;
+  if(i2c.read(I2C_READ_ADDR, ach_i2c_data, 6, false)!=0)
+  {
+    return false;
+  }
+  un_temp=(unsigned char) ach_i2c_data[0];
+  un_temp<<=16;
+  *pun_red_led+=un_temp;
+  un_temp=(unsigned char) ach_i2c_data[1];
+  un_temp<<=8;
+  *pun_red_led+=un_temp;
+  un_temp=(unsigned char) ach_i2c_data[2];
+  *pun_red_led+=un_temp;
+  
+  un_temp=(unsigned char) ach_i2c_data[3];
+  un_temp<<=16;
+  *pun_ir_led+=un_temp;
+  un_temp=(unsigned char) ach_i2c_data[4];
+  un_temp<<=8;
+  *pun_ir_led+=un_temp;
+  un_temp=(unsigned char) ach_i2c_data[5];
+  *pun_ir_led+=un_temp;
+  *pun_red_led&=0x03FFFF;  //Mask MSB [23:18]
+  *pun_ir_led&=0x03FFFF;  //Mask MSB [23:18]
+  
+  
+  return true;
+}
+
+bool maxim_max30102_reset()
+/**
+* \brief        Reset the MAX30102
+* \par          Details
+*               This function resets the MAX30102
+*
+* \param        None
+*
+* \retval       true on success
+*/
+{
+    if(!maxim_max30102_write_reg(REG_MODE_CONFIG,0x40))
+        return false;
+    else
+        return true;    
+}
diff -r 000000000000 -r cc636f742803 MAX30102/MAX30102.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MAX30102/MAX30102.h	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,97 @@
+/** \file max30102.h ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: max30102.h
+* Description: This module is an embedded controller driver header file for MAX30102
+*
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+* char              ch_pmod_value
+* char (array)      s_pmod_s_string[16]
+* float             f_pmod_value
+* int32_t           n_pmod_value
+* int32_t (array)   an_pmod_value[16]
+* int16_t           w_pmod_value
+* int16_t (array)   aw_pmod_value[16]
+* uint16_t          uw_pmod_value
+* uint16_t (array)  auw_pmod_value[16]
+* uint8_t           uch_pmod_value
+* uint8_t (array)   auch_pmod_buffer[16]
+* uint32_t          un_pmod_value
+* int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* 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 MAXIM INTEGRATED 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.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+#ifndef MAX30102_H_
+#define MAX30102_H_
+
+#include "mbed.h"
+
+#define I2C_WRITE_ADDR 0xAE
+#define I2C_READ_ADDR 0xAF
+
+//register addresses
+#define REG_INTR_STATUS_1 0x00
+#define REG_INTR_STATUS_2 0x01
+#define REG_INTR_ENABLE_1 0x02
+#define REG_INTR_ENABLE_2 0x03
+#define REG_FIFO_WR_PTR 0x04
+#define REG_OVF_COUNTER 0x05
+#define REG_FIFO_RD_PTR 0x06
+#define REG_FIFO_DATA 0x07
+#define REG_FIFO_CONFIG 0x08
+#define REG_MODE_CONFIG 0x09
+#define REG_SPO2_CONFIG 0x0A
+#define REG_LED1_PA 0x0C
+#define REG_LED2_PA 0x0D
+#define REG_PILOT_PA 0x10
+#define REG_MULTI_LED_CTRL1 0x11
+#define REG_MULTI_LED_CTRL2 0x12
+#define REG_TEMP_INTR 0x1F
+#define REG_TEMP_FRAC 0x20
+#define REG_TEMP_CONFIG 0x21
+#define REG_PROX_INT_THRESH 0x30
+#define REG_REV_ID 0xFE
+#define REG_PART_ID 0xFF
+
+bool maxim_max30102_init();
+bool maxim_max30102_read_fifo(uint32_t *pun_red_led, uint32_t *pun_ir_led);
+bool maxim_max30102_write_reg(uint8_t uch_addr, uint8_t uch_data);
+bool maxim_max30102_read_reg(uint8_t uch_addr, uint8_t *puch_data);
+bool maxim_max30102_reset(void);
+
+#endif /* MAX30102_H_ */
\ No newline at end of file
diff -r 000000000000 -r cc636f742803 USBDevice.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/USBDevice.lib	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,1 @@
+https://developer.mbed.org/teams/MaximIntegrated/code/USBDevice/#dad310740b28
diff -r 000000000000 -r cc636f742803 algorithm/algorithm.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/algorithm/algorithm.cpp	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,363 @@
+/** \file algorithm.cpp ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: algorithm.cpp
+* Description: This module calculates the heart rate/SpO2 level
+*
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+* char              ch_pmod_value
+* char (array)      s_pmod_s_string[16]
+* float             f_pmod_value
+* int32_t           n_pmod_value
+* int32_t (array)   an_pmod_value[16]
+* int16_t           w_pmod_value
+* int16_t (array)   aw_pmod_value[16]
+* uint16_t          uw_pmod_value
+* uint16_t (array)  auw_pmod_value[16]
+* uint8_t           uch_pmod_value
+* uint8_t (array)   auch_pmod_buffer[16]
+* uint32_t          un_pmod_value
+* int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* 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 MAXIM INTEGRATED 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.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+#include "algorithm.h"
+#include "mbed.h"
+
+void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer,  int32_t n_ir_buffer_length, uint32_t *pun_red_buffer, int32_t *pn_spo2, int8_t *pch_spo2_valid, 
+                              int32_t *pn_heart_rate, int8_t  *pch_hr_valid)
+/**
+* \brief        Calculate the heart rate and SpO2 level
+* \par          Details
+*               By detecting  peaks of PPG cycle and corresponding AC/DC of red/infra-red signal, the ratio for the SPO2 is computed.
+*               Since this algorithm is aiming for Arm M0/M3. formaula for SPO2 did not achieve the accuracy due to register overflow.
+*               Thus, accurate SPO2 is precalculated and save longo uch_spo2_table[] per each ratio.
+*
+* \param[in]    *pun_ir_buffer           - IR sensor data buffer
+* \param[in]    n_ir_buffer_length      - IR sensor data buffer length
+* \param[in]    *pun_red_buffer          - Red sensor data buffer
+* \param[out]    *pn_spo2                - Calculated SpO2 value
+* \param[out]    *pch_spo2_valid         - 1 if the calculated SpO2 value is valid
+* \param[out]    *pn_heart_rate          - Calculated heart rate value
+* \param[out]    *pch_hr_valid           - 1 if the calculated heart rate value is valid
+*
+* \retval       None
+*/
+{
+    uint32_t un_ir_mean ,un_only_once ;
+    int32_t k ,n_i_ratio_count;
+    int32_t i,s ,m, n_exact_ir_valley_locs_count ,n_middle_idx;
+    int32_t n_th1, n_npks,n_c_min;      
+    int32_t an_ir_valley_locs[15] ;
+    int32_t an_exact_ir_valley_locs[15] ;
+    int32_t an_dx_peak_locs[15] ;
+    int32_t n_peak_interval_sum;
+    
+    int32_t n_y_ac, n_x_ac;
+    int32_t n_spo2_calc; 
+    int32_t n_y_dc_max, n_x_dc_max; 
+    int32_t n_y_dc_max_idx, n_x_dc_max_idx; 
+    int32_t an_ratio[5],n_ratio_average; 
+    int32_t n_nume,  n_denom ;
+    // remove DC of ir signal    
+    un_ir_mean =0; 
+    for (k=0 ; k<n_ir_buffer_length ; k++ ) un_ir_mean += pun_ir_buffer[k] ;
+    un_ir_mean =un_ir_mean/n_ir_buffer_length ;
+    for (k=0 ; k<n_ir_buffer_length ; k++ )  an_x[k] =  pun_ir_buffer[k] - un_ir_mean ; 
+    
+    // 4 pt Moving Average
+    for(k=0; k< BUFFER_SIZE-MA4_SIZE; k++){
+        n_denom= ( an_x[k]+an_x[k+1]+ an_x[k+2]+ an_x[k+3]);
+        an_x[k]=  n_denom/(int32_t)4; 
+    }
+
+    // get difference of smoothed IR signal
+    
+    for( k=0; k<BUFFER_SIZE-MA4_SIZE-1;  k++)
+        an_dx[k]= (an_x[k+1]- an_x[k]);
+
+    // 2-pt Moving Average to an_dx
+    for(k=0; k< BUFFER_SIZE-MA4_SIZE-2; k++){
+        an_dx[k] =  ( an_dx[k]+an_dx[k+1])/2 ;
+    }
+    
+    // hamming window
+    // flip wave form so that we can detect valley with peak detector
+    for ( i=0 ; i<BUFFER_SIZE-HAMMING_SIZE-MA4_SIZE-2 ;i++){
+        s= 0;
+        for( k=i; k<i+ HAMMING_SIZE ;k++){
+            s -= an_dx[k] *auw_hamm[k-i] ; 
+                     }
+        an_dx[i]= s/ (int32_t)1146; // divide by sum of auw_hamm 
+    }
+
+ 
+    n_th1=0; // threshold calculation
+    for ( k=0 ; k<BUFFER_SIZE-HAMMING_SIZE ;k++){
+        n_th1 += ((an_dx[k]>0)? an_dx[k] : ((int32_t)0-an_dx[k])) ;
+    }
+    n_th1= n_th1/ ( BUFFER_SIZE-HAMMING_SIZE);
+    // peak location is acutally index for sharpest location of raw signal since we flipped the signal         
+    maxim_find_peaks( an_dx_peak_locs, &n_npks, an_dx, BUFFER_SIZE-HAMMING_SIZE, n_th1, 8, 5 );//peak_height, peak_distance, max_num_peaks 
+
+    n_peak_interval_sum =0;
+    if (n_npks>=2){
+        for (k=1; k<n_npks; k++)
+            n_peak_interval_sum += (an_dx_peak_locs[k]-an_dx_peak_locs[k -1]);
+        n_peak_interval_sum=n_peak_interval_sum/(n_npks-1);
+        *pn_heart_rate=(int32_t)(6000/n_peak_interval_sum);// beats per minutes
+        *pch_hr_valid  = 1;
+    }
+    else  {
+        *pn_heart_rate = -999;
+        *pch_hr_valid  = 0;
+    }
+            
+    for ( k=0 ; k<n_npks ;k++)
+        an_ir_valley_locs[k]=an_dx_peak_locs[k]+HAMMING_SIZE/2; 
+
+
+    // raw value : RED(=y) and IR(=X)
+    // we need to assess DC and AC value of ir and red PPG. 
+    for (k=0 ; k<n_ir_buffer_length ; k++ )  {
+        an_x[k] =  pun_ir_buffer[k] ; 
+        an_y[k] =  pun_red_buffer[k] ; 
+    }
+
+    // find precise min near an_ir_valley_locs
+    n_exact_ir_valley_locs_count =0; 
+    for(k=0 ; k<n_npks ;k++){
+        un_only_once =1;
+        m=an_ir_valley_locs[k];
+        n_c_min= 16777216;//2^24;
+        if (m+5 <  BUFFER_SIZE-HAMMING_SIZE  && m-5 >0){
+            for(i= m-5;i<m+5; i++)
+                if (an_x[i]<n_c_min){
+                    if (un_only_once >0){
+                       un_only_once =0;
+                   } 
+                   n_c_min= an_x[i] ;
+                   an_exact_ir_valley_locs[k]=i;
+                }
+            if (un_only_once ==0)
+                n_exact_ir_valley_locs_count ++ ;
+        }
+    }
+    if (n_exact_ir_valley_locs_count <2 ){
+       *pn_spo2 =  -999 ; // do not use SPO2 since signal ratio is out of range
+       *pch_spo2_valid  = 0; 
+       return;
+    }
+    // 4 pt MA
+    for(k=0; k< BUFFER_SIZE-MA4_SIZE; k++){
+        an_x[k]=( an_x[k]+an_x[k+1]+ an_x[k+2]+ an_x[k+3])/(int32_t)4;
+        an_y[k]=( an_y[k]+an_y[k+1]+ an_y[k+2]+ an_y[k+3])/(int32_t)4;
+    }
+
+    //using an_exact_ir_valley_locs , find ir-red DC andir-red AC for SPO2 calibration ratio
+    //finding AC/DC maximum of raw ir * red between two valley locations
+    n_ratio_average =0; 
+    n_i_ratio_count =0; 
+    
+    for(k=0; k< 5; k++) an_ratio[k]=0;
+    for (k=0; k< n_exact_ir_valley_locs_count; k++){
+        if (an_exact_ir_valley_locs[k] > BUFFER_SIZE ){             
+            *pn_spo2 =  -999 ; // do not use SPO2 since valley loc is out of range
+            *pch_spo2_valid  = 0; 
+            return;
+        }
+    }
+    // find max between two valley locations 
+    // and use ratio betwen AC compoent of Ir & Red and DC compoent of Ir & Red for SPO2 
+
+    for (k=0; k< n_exact_ir_valley_locs_count-1; k++){
+        n_y_dc_max= -16777216 ; 
+        n_x_dc_max= - 16777216; 
+        if (an_exact_ir_valley_locs[k+1]-an_exact_ir_valley_locs[k] >10){
+            for (i=an_exact_ir_valley_locs[k]; i< an_exact_ir_valley_locs[k+1]; i++){
+                if (an_x[i]> n_x_dc_max) {n_x_dc_max =an_x[i];n_x_dc_max_idx =i; }
+                if (an_y[i]> n_y_dc_max) {n_y_dc_max =an_y[i];n_y_dc_max_idx=i;}
+            }
+            n_y_ac= (an_y[an_exact_ir_valley_locs[k+1]] - an_y[an_exact_ir_valley_locs[k] ] )*(n_y_dc_max_idx -an_exact_ir_valley_locs[k]); //red
+            n_y_ac=  an_y[an_exact_ir_valley_locs[k]] + n_y_ac/ (an_exact_ir_valley_locs[k+1] - an_exact_ir_valley_locs[k])  ; 
+        
+        
+            n_y_ac=  an_y[n_y_dc_max_idx] - n_y_ac;    // subracting linear DC compoenents from raw 
+            n_x_ac= (an_x[an_exact_ir_valley_locs[k+1]] - an_x[an_exact_ir_valley_locs[k] ] )*(n_x_dc_max_idx -an_exact_ir_valley_locs[k]); // ir
+            n_x_ac=  an_x[an_exact_ir_valley_locs[k]] + n_x_ac/ (an_exact_ir_valley_locs[k+1] - an_exact_ir_valley_locs[k]); 
+            n_x_ac=  an_x[n_y_dc_max_idx] - n_x_ac;      // subracting linear DC compoenents from raw 
+            n_nume=( n_y_ac *n_x_dc_max)>>7 ; //prepare X100 to preserve floating value
+            n_denom= ( n_x_ac *n_y_dc_max)>>7;
+            if (n_denom>0  && n_i_ratio_count <5 &&  n_nume != 0)
+            {   
+                an_ratio[n_i_ratio_count]= (n_nume*100)/n_denom ; //formular is ( n_y_ac *n_x_dc_max) / ( n_x_ac *n_y_dc_max) ;
+                n_i_ratio_count++;
+            }
+        }
+    }
+
+    maxim_sort_ascend(an_ratio, n_i_ratio_count);
+    n_middle_idx= n_i_ratio_count/2;
+
+    if (n_middle_idx >1)
+        n_ratio_average =( an_ratio[n_middle_idx-1] +an_ratio[n_middle_idx])/2; // use median
+    else
+        n_ratio_average = an_ratio[n_middle_idx ];
+
+    if( n_ratio_average>2 && n_ratio_average <184){
+        n_spo2_calc= uch_spo2_table[n_ratio_average] ;
+        *pn_spo2 = n_spo2_calc ;
+        *pch_spo2_valid  = 1;//  float_SPO2 =  -45.060*n_ratio_average* n_ratio_average/10000 + 30.354 *n_ratio_average/100 + 94.845 ;  // for comparison with table
+    }
+    else{
+        *pn_spo2 =  -999 ; // do not use SPO2 since signal ratio is out of range
+        *pch_spo2_valid  = 0; 
+    }
+}
+
+
+void maxim_find_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num)
+/**
+* \brief        Find peaks
+* \par          Details
+*               Find at most MAX_NUM peaks above MIN_HEIGHT separated by at least MIN_DISTANCE
+*
+* \retval       None
+*/
+{
+    maxim_peaks_above_min_height( pn_locs, pn_npks, pn_x, n_size, n_min_height );
+    maxim_remove_close_peaks( pn_locs, pn_npks, pn_x, n_min_distance );
+    *pn_npks = min( *pn_npks, n_max_num );
+}
+
+void maxim_peaks_above_min_height(int32_t *pn_locs, int32_t *pn_npks, int32_t  *pn_x, int32_t n_size, int32_t n_min_height)
+/**
+* \brief        Find peaks above n_min_height
+* \par          Details
+*               Find all peaks above MIN_HEIGHT
+*
+* \retval       None
+*/
+{
+    int32_t i = 1, n_width;
+    *pn_npks = 0;
+    
+    while (i < n_size-1){
+        if (pn_x[i] > n_min_height && pn_x[i] > pn_x[i-1]){            // find left edge of potential peaks
+            n_width = 1;
+            while (i+n_width < n_size && pn_x[i] == pn_x[i+n_width])    // find flat peaks
+                n_width++;
+            if (pn_x[i] > pn_x[i+n_width] && (*pn_npks) < 15 ){                            // find right edge of peaks
+                pn_locs[(*pn_npks)++] = i;        
+                // for flat peaks, peak location is left edge
+                i += n_width+1;
+            }
+            else
+                i += n_width;
+        }
+        else
+            i++;
+    }
+}
+
+
+void maxim_remove_close_peaks(int32_t *pn_locs, int32_t *pn_npks, int32_t *pn_x,int32_t n_min_distance)
+/**
+* \brief        Remove peaks
+* \par          Details
+*               Remove peaks separated by less than MIN_DISTANCE
+*
+* \retval       None
+*/
+{
+    
+    int32_t i, j, n_old_npks, n_dist;
+    
+    /* Order peaks from large to small */
+    maxim_sort_indices_descend( pn_x, pn_locs, *pn_npks );
+
+    for ( i = -1; i < *pn_npks; i++ ){
+        n_old_npks = *pn_npks;
+        *pn_npks = i+1;
+        for ( j = i+1; j < n_old_npks; j++ ){
+            n_dist =  pn_locs[j] - ( i == -1 ? -1 : pn_locs[i] ); // lag-zero peak of autocorr is at index -1
+            if ( n_dist > n_min_distance || n_dist < -n_min_distance )
+                pn_locs[(*pn_npks)++] = pn_locs[j];
+        }
+    }
+
+    // Resort indices longo ascending order
+    maxim_sort_ascend( pn_locs, *pn_npks );
+}
+
+void maxim_sort_ascend(int32_t *pn_x,int32_t n_size) 
+/**
+* \brief        Sort array
+* \par          Details
+*               Sort array in ascending order (insertion sort algorithm)
+*
+* \retval       None
+*/
+{
+    int32_t i, j, n_temp;
+    for (i = 1; i < n_size; i++) {
+        n_temp = pn_x[i];
+        for (j = i; j > 0 && n_temp < pn_x[j-1]; j--)
+            pn_x[j] = pn_x[j-1];
+        pn_x[j] = n_temp;
+    }
+}
+
+void maxim_sort_indices_descend(int32_t *pn_x, int32_t *pn_indx, int32_t n_size)
+/**
+* \brief        Sort indices
+* \par          Details
+*               Sort indices according to descending order (insertion sort algorithm)
+*
+* \retval       None
+*/ 
+{
+    int32_t i, j, n_temp;
+    for (i = 1; i < n_size; i++) {
+        n_temp = pn_indx[i];
+        for (j = i; j > 0 && pn_x[n_temp] > pn_x[pn_indx[j-1]]; j--)
+            pn_indx[j] = pn_indx[j-1];
+        pn_indx[j] = n_temp;
+    }
+}
+
diff -r 000000000000 -r cc636f742803 algorithm/algorithm.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/algorithm/algorithm.h	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,100 @@
+/** \file algorithm.h ******************************************************
+*
+* Project: MAXREFDES117#
+* Filename: algorithm.c
+* Description: This module is the heart rate/SpO2 calculation algorithm header file
+*
+* Revision History:
+*\n 1-18-2016 Rev 01.00 SK Initial release.
+*\n
+*
+* --------------------------------------------------------------------
+*
+* This code follows the following naming conventions:
+*
+*\n char              ch_pmod_value
+*\n char (array)      s_pmod_s_string[16]
+*\n float             f_pmod_value
+*\n int32_t           n_pmod_value
+*\n int32_t (array)   an_pmod_value[16]
+*\n int16_t           w_pmod_value
+*\n int16_t (array)   aw_pmod_value[16]
+*\n uint16_t          uw_pmod_value
+*\n uint16_t (array)  auw_pmod_value[16]
+*\n uint8_t           uch_pmod_value
+*\n uint8_t (array)   auch_pmod_buffer[16]
+*\n uint32_t          un_pmod_value
+*\n int32_t *         pn_pmod_value
+*
+* ------------------------------------------------------------------------- */
+/*******************************************************************************
+* Copyright (C) 2015 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* 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 MAXIM INTEGRATED 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.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+#ifndef ALGORITHM_H_
+#define ALGORITHM_H_
+
+#include "mbed.h"
+
+#define true 1
+#define false 0
+#define FS 100
+#define BUFFER_SIZE  (FS* 5) 
+#define HR_FIFO_SIZE 7
+#define MA4_SIZE  4 // DO NOT CHANGE
+#define HAMMING_SIZE  5// DO NOT CHANGE
+#define min(x,y) ((x) < (y) ? (x) : (y))
+
+const uint16_t auw_hamm[31]={ 41,    276,    512,    276,     41 }; //Hamm=  long16(512* hamming(5)');
+//SPO2table is computed as  -45.060*ratioAverage* ratioAverage + 30.354 *ratioAverage + 94.845 ;
+const uint8_t uch_spo2_table[184]={ 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 97, 98, 98, 98, 98, 98, 99, 99, 99, 99, 
+                            99, 99, 99, 99, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 
+                            100, 100, 100, 100, 99, 99, 99, 99, 99, 99, 99, 99, 98, 98, 98, 98, 98, 98, 97, 97, 
+                            97, 97, 96, 96, 96, 96, 95, 95, 95, 94, 94, 94, 93, 93, 93, 92, 92, 92, 91, 91, 
+                            90, 90, 89, 89, 89, 88, 88, 87, 87, 86, 86, 85, 85, 84, 84, 83, 82, 82, 81, 81, 
+                            80, 80, 79, 78, 78, 77, 76, 76, 75, 74, 74, 73, 72, 72, 71, 70, 69, 69, 68, 67, 
+                            66, 66, 65, 64, 63, 62, 62, 61, 60, 59, 58, 57, 56, 56, 55, 54, 53, 52, 51, 50, 
+                            49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 31, 30, 29, 
+                            28, 27, 26, 25, 23, 22, 21, 20, 19, 17, 16, 15, 14, 12, 11, 10, 9, 7, 6, 5, 
+                            3, 2, 1 } ;
+static  int32_t an_dx[ BUFFER_SIZE-MA4_SIZE]; // delta
+static  int32_t an_x[ BUFFER_SIZE]; //ir
+static  int32_t an_y[ BUFFER_SIZE]; //red
+
+
+void maxim_heart_rate_and_oxygen_saturation(uint32_t *pun_ir_buffer ,  int32_t n_ir_buffer_length, uint32_t *pun_red_buffer ,   int32_t *pn_spo2, int8_t *pch_spo2_valid ,  int32_t *pn_heart_rate , int8_t  *pch_hr_valid);
+void maxim_find_peaks( int32_t *pn_locs, int32_t *pn_npks,  int32_t *pn_x, int32_t n_size, int32_t n_min_height, int32_t n_min_distance, int32_t n_max_num );
+void maxim_peaks_above_min_height( int32_t *pn_locs, int32_t *pn_npks,  int32_t *pn_x, int32_t n_size, int32_t n_min_height );
+void maxim_remove_close_peaks( int32_t *pn_locs, int32_t *pn_npks,   int32_t  *pn_x, int32_t n_min_distance );
+void maxim_sort_ascend( int32_t *pn_x, int32_t n_size );
+void maxim_sort_indices_descend(  int32_t  *pn_x, int32_t *pn_indx, int32_t n_size);
+
+#endif /* ALGORITHM_H_ */
\ No newline at end of file
diff -r 000000000000 -r cc636f742803 img/connection.png
Binary file img/connection.png has changed
diff -r 000000000000 -r cc636f742803 img/discovery.png
Binary file img/discovery.png has changed
diff -r 000000000000 -r cc636f742803 img/notifications.png
Binary file img/notifications.png has changed
diff -r 000000000000 -r cc636f742803 img/register_to_notifications.png
Binary file img/register_to_notifications.png has changed
diff -r 000000000000 -r cc636f742803 img/scan_result.png
Binary file img/scan_result.png has changed
diff -r 000000000000 -r cc636f742803 img/start_scan.png
Binary file img/start_scan.png has changed
diff -r 000000000000 -r cc636f742803 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,302 @@
+/*******************************************************************************
+* Copyright (C) 2018 Maxim Integrated Products, Inc., All Rights Reserved.
+*
+* 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 MAXIM INTEGRATED 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.
+*
+* Except as contained in this notice, the name of Maxim Integrated
+* Products, Inc. shall not be used except as stated in the Maxim Integrated
+* Products, Inc. Branding Policy.
+*
+* The mere transfer of this software does not imply any licenses
+* of trade secrets, proprietary technology, copyrights, patents,
+* trademarks, maskwork rights, or any other form of intellectual
+* property whatsoever. Maxim Integrated Products, Inc. retains all
+* ownership rights.
+*******************************************************************************
+*/
+#include <events/mbed_events.h>
+#include "mbed.h"
+#include "max32630fthr.h"
+#include "USBSerial.h"
+#include "Adafruit_SSD1306.h"//include the adafruit library for the oled display
+#include "ble/BLE.h"//for bluetooth services
+#include "ble/Gap.h"
+#include "ble/services/HeartRateService.h"
+#include "algorithm.h"
+#include "MAX30102.h"
+
+
+#define MAX_BRIGHTNESS 255
+
+MAX32630FTHR pegasus(MAX32630FTHR::VIO_3V3);
+
+// Hardware serial port over DAPLink
+Serial daplink(USBTX, USBRX);          // Use USB debug probe for serial link
+Serial uart(P2_1, P2_0);
+
+//initialise the i2c port connections
+I2C i2c(P3_4, P3_5); // SDA, SCL
+
+Adafruit_SSD1306_I2c oled(i2c); //declare the oled display and its port connection type (I2C)
+
+
+//variables used for HRM service
+uint32_t aun_ir_buffer[500]; //IR LED sensor data
+int32_t n_ir_buffer_length;    //data length
+uint32_t aun_red_buffer[500];    //Red LED sensor data
+int32_t n_sp02; //SPO2 value
+int8_t ch_spo2_valid;   //indicator to show if the SP02 calculation is valid
+int32_t n_heart_rate;   //heart rate value
+int8_t  ch_hr_valid;    //indicator to show if the heart rate calculation is valid
+uint8_t uch_dummy;
+uint32_t un_min, un_max, un_prev_data;  //variables to calculate the on-board LED brightness that reflects the heartbeats
+int i;
+int32_t n_brightness;
+float f_temp;
+uint8_t hrmCounter;
+
+
+#ifdef TARGET_MAX32630FTHR
+PwmOut led1(LED_RED);    //initializes the pwm output that connects to the on board LED
+DigitalIn INT(P3_0);  //pin P30 connects to the interrupt output pin of the MAX30102
+#endif
+
+static HeartRateService *hrServicePtr;
+
+static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE);
+
+void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
+{
+    BLE::Instance().gap().startAdvertising(); // restart advertising
+}
+
+
+// Virtual serial port over USB
+USBSerial microUSB; 
+DigitalOut rLED(LED1);
+DigitalOut gLED(LED2);
+DigitalOut bLED(LED3);
+
+
+
+
+
+
+//functions for HRMSensor
+void checkHR(){
+    
+    i=0;
+    un_min=0x3FFFF;
+    un_max=0;
+        
+    //dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
+    for(i=100;i<500;i++)
+    {
+        aun_red_buffer[i-100]=aun_red_buffer[i];
+        aun_ir_buffer[i-100]=aun_ir_buffer[i];
+            
+        //update the signal min and max
+        if(un_min>aun_red_buffer[i])
+        un_min=aun_red_buffer[i];
+        if(un_max<aun_red_buffer[i])
+        un_max=aun_red_buffer[i];
+    }
+        
+    //take 100 sets of samples before calculating the heart rate.
+    for(i=400;i<500;i++)
+    {
+        un_prev_data=aun_red_buffer[i-1];
+        while(INT.read()==1);
+        maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));
+        
+        if(aun_red_buffer[i]>un_prev_data)
+        {
+            f_temp=aun_red_buffer[i]-un_prev_data;
+            f_temp/=(un_max-un_min);
+            f_temp*=MAX_BRIGHTNESS;
+            n_brightness-=(int)f_temp;
+            if(n_brightness<0)
+                n_brightness=0;
+        }
+        else
+        {
+            f_temp=un_prev_data-aun_red_buffer[i];
+            f_temp/=(un_max-un_min);
+            f_temp*=MAX_BRIGHTNESS;
+            n_brightness+=(int)f_temp;
+            if(n_brightness>MAX_BRIGHTNESS)
+                n_brightness=MAX_BRIGHTNESS;
+        }
+#if defined(TARGET_KL25Z) || defined(TARGET_MAX32630FTHR)
+        led1.write(1-(float)n_brightness/256);
+#endif
+
+
+    }
+    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
+}
+
+void updateSensorValue() {
+    // Do blocking calls or whatever is necessary for sensor polling.
+    // In our case, we simply update the HRM measurement.
+
+    
+    checkHR();
+            
+    hrmCounter = n_heart_rate;
+        
+    hrServicePtr->updateHeartRate(hrmCounter);
+}
+
+void periodicCallback(void)
+{
+
+    if (BLE::Instance().getGapState().connected) {
+        eventQueue.call(updateSensorValue);
+    }
+}
+
+
+
+void onBleInitError(BLE &ble, ble_error_t error)
+{
+    (void)ble;
+    (void)error;
+   /* Initialization error handling should go here */
+}
+
+void printMacAddress()
+{
+    /* Print out device MAC address to the console*/
+    Gap::AddressType_t addr_type;
+    Gap::Address_t address;
+    BLE::Instance().gap().getAddress(&addr_type, address);
+    printf("DEVICE MAC ADDRESS: ");
+    for (int i = 5; i >= 1; i--){
+        printf("%02x:", address[i]);
+    }
+    printf("%02x\r\n", address[0]);
+}
+
+void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
+{
+    BLE&        ble   = params->ble;
+    ble_error_t error = params->error;
+
+    if (error != BLE_ERROR_NONE) {
+        onBleInitError(ble, error);
+        return;
+    }
+
+    if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
+        return;
+    }
+
+    ble.gap().onDisconnection(disconnectionCallback);
+
+    /* Setup primary service. */
+    hrServicePtr = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_WRIST);//initially LOCATION_FINGER
+
+    /* Setup advertising. */
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
+    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    ble.gap().setAdvertisingInterval(1000); /* 1000ms */
+    ble.gap().startAdvertising();
+
+    printMacAddress();
+}
+
+void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
+    BLE &ble = BLE::Instance();
+    eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
+}
+
+public void HRMSensorInitialise(){
+        maxim_max30102_reset(); //resets the MAX30102
+
+    
+    //read and clear status register
+    maxim_max30102_read_reg(0,&uch_dummy);
+    
+    
+    maxim_max30102_init();  //initializes the MAX30102
+    
+    n_brightness=0;
+    un_min=0x3FFFF;
+    un_max=0;
+    
+    n_ir_buffer_length=500; //buffer length of 100 stores 5 seconds of samples running at 100sps
+    
+    //read the first 500 samples, and determine the signal range
+    for(i=0;i<n_ir_buffer_length;i++)
+    {
+        while(INT.read()==1);   //wait until the interrupt pin asserts
+        
+        maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));  //read from MAX30102 FIFO
+            
+        if(un_min>aun_red_buffer[i])
+            un_min=aun_red_buffer[i];    //update signal min
+        if(un_max<aun_red_buffer[i])
+            un_max=aun_red_buffer[i];    //update signal max
+            
+    }
+    un_prev_data=aun_red_buffer[i];
+    
+    //calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
+    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); 
+    }
+
+public void HRMSensorTask(){
+    //task for checking the sensor value and sending to connected device
+        eventQueue.call_every(500, periodicCallback);
+ 
+        BLE &ble = BLE::Instance();
+        ble.onEventsToProcess(scheduleBleEventsProcessing);
+        ble.init(bleInitComplete);
+        eventQueue.dispatch_forever();
+    }
+
+public void OledDisplayTask(){
+    }//task for manipulating the oled display
+
+// main() runs in its own thread in the OS
+// (note the calls to Thread::wait below for delays)
+int main()
+{
+    int c;
+
+    daplink.printf("daplink serial port\r\n");
+    microUSB.printf("micro USB serial port\r\n");
+    rLED = LED_ON;
+    gLED = LED_ON;
+    bLED = LED_OFF;
+
+    rLED = LED_OFF;
+    HRMSensorInitialise();
+
+    while(1) {
+        OledDisplayTask();
+        HRMSensor();
+        Pork
+    }
+}
+
diff -r 000000000000 -r cc636f742803 max32630fthr.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/max32630fthr.lib	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,1 @@
+http://developer.mbed.org/teams/MaximIntegrated/code/max32630fthr/#8f6e6a800f2f
diff -r 000000000000 -r cc636f742803 mbed-os.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed-os.lib	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/mbed-os/#78474a5129e18e136cc7e872adbaa5b74fbb8f6a
diff -r 000000000000 -r cc636f742803 shields/TARGET_ST_BLUENRG.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/shields/TARGET_ST_BLUENRG.lib	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,1 @@
+https://github.com/ARMmbed/ble-x-nucleo-idb0xa1/#b630517008bbe47592927cc8e5dfcd2e5b9de968
diff -r 000000000000 -r cc636f742803 source/main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/main.cpp	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,243 @@
+/* mbed Microcontroller Library
+ * Copyright (c) 2006-2015 ARM Limited
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <events/mbed_events.h>
+#include <mbed.h>
+#include "ble/BLE.h"
+#include "ble/Gap.h"
+#include "ble/services/HeartRateService.h"
+#include "algorithm.h"
+#include "MAX30102.h"
+
+#define MAX_BRIGHTNESS 255
+
+
+
+DigitalOut led2(LED2, 1);
+
+uint32_t aun_ir_buffer[500]; //IR LED sensor data
+int32_t n_ir_buffer_length;    //data length
+uint32_t aun_red_buffer[500];    //Red LED sensor data
+int32_t n_sp02; //SPO2 value
+int8_t ch_spo2_valid;   //indicator to show if the SP02 calculation is valid
+int32_t n_heart_rate;   //heart rate value
+int8_t  ch_hr_valid;    //indicator to show if the heart rate calculation is valid
+uint8_t uch_dummy;
+uint32_t un_min, un_max, un_prev_data;  //variables to calculate the on-board LED brightness that reflects the heartbeats
+int i;
+int32_t n_brightness;
+float f_temp;
+uint8_t hrmCounter;
+
+
+#ifdef TARGET_MAX32630FTHR
+PwmOut led1(LED_RED);    //initializes the pwm output that connects to the on board LED
+DigitalIn INT(P3_0);  //pin P30 connects to the interrupt output pin of the MAX30102
+#endif
+
+const static char     DEVICE_NAME[] = "HRM";
+static const uint16_t uuid16_list[] = {GattService::UUID_HEART_RATE_SERVICE};
+
+static HeartRateService *hrServicePtr;
+
+static EventQueue eventQueue(/* event count */ 16 * EVENTS_EVENT_SIZE);
+
+void disconnectionCallback(const Gap::DisconnectionCallbackParams_t *params)
+{
+    BLE::Instance().gap().startAdvertising(); // restart advertising
+}
+
+void checkHR(){
+    
+    i=0;
+    un_min=0x3FFFF;
+    un_max=0;
+        
+    //dumping the first 100 sets of samples in the memory and shift the last 400 sets of samples to the top
+    for(i=100;i<500;i++)
+    {
+        aun_red_buffer[i-100]=aun_red_buffer[i];
+        aun_ir_buffer[i-100]=aun_ir_buffer[i];
+            
+        //update the signal min and max
+        if(un_min>aun_red_buffer[i])
+        un_min=aun_red_buffer[i];
+        if(un_max<aun_red_buffer[i])
+        un_max=aun_red_buffer[i];
+    }
+        
+    //take 100 sets of samples before calculating the heart rate.
+    for(i=400;i<500;i++)
+    {
+        un_prev_data=aun_red_buffer[i-1];
+        while(INT.read()==1);
+        maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));
+        
+        if(aun_red_buffer[i]>un_prev_data)
+        {
+            f_temp=aun_red_buffer[i]-un_prev_data;
+            f_temp/=(un_max-un_min);
+            f_temp*=MAX_BRIGHTNESS;
+            n_brightness-=(int)f_temp;
+            if(n_brightness<0)
+                n_brightness=0;
+        }
+        else
+        {
+            f_temp=un_prev_data-aun_red_buffer[i];
+            f_temp/=(un_max-un_min);
+            f_temp*=MAX_BRIGHTNESS;
+            n_brightness+=(int)f_temp;
+            if(n_brightness>MAX_BRIGHTNESS)
+                n_brightness=MAX_BRIGHTNESS;
+        }
+#if defined(TARGET_KL25Z) || defined(TARGET_MAX32630FTHR)
+        led1.write(1-(float)n_brightness/256);
+#endif
+
+
+    }
+    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid);
+}
+
+void updateSensorValue() {
+    // Do blocking calls or whatever is necessary for sensor polling.
+    // In our case, we simply update the HRM measurement.
+
+    
+    checkHR();
+            
+    hrmCounter = n_heart_rate;
+        
+    hrServicePtr->updateHeartRate(hrmCounter);
+}
+
+void periodicCallback(void)
+{
+
+    if (BLE::Instance().getGapState().connected) {
+        eventQueue.call(updateSensorValue);
+    }
+}
+
+
+
+void onBleInitError(BLE &ble, ble_error_t error)
+{
+    (void)ble;
+    (void)error;
+   /* Initialization error handling should go here */
+}
+
+void printMacAddress()
+{
+    /* Print out device MAC address to the console*/
+    Gap::AddressType_t addr_type;
+    Gap::Address_t address;
+    BLE::Instance().gap().getAddress(&addr_type, address);
+    printf("DEVICE MAC ADDRESS: ");
+    for (int i = 5; i >= 1; i--){
+        printf("%02x:", address[i]);
+    }
+    printf("%02x\r\n", address[0]);
+}
+
+void bleInitComplete(BLE::InitializationCompleteCallbackContext *params)
+{
+    BLE&        ble   = params->ble;
+    ble_error_t error = params->error;
+
+    if (error != BLE_ERROR_NONE) {
+        onBleInitError(ble, error);
+        return;
+    }
+
+    if (ble.getInstanceID() != BLE::DEFAULT_INSTANCE) {
+        return;
+    }
+
+    ble.gap().onDisconnection(disconnectionCallback);
+
+    /* Setup primary service. */
+    hrServicePtr = new HeartRateService(ble, hrmCounter, HeartRateService::LOCATION_WRIST);//initially LOCATION_FINGER
+
+    /* Setup advertising. */
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::BREDR_NOT_SUPPORTED | GapAdvertisingData::LE_GENERAL_DISCOVERABLE);
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LIST_16BIT_SERVICE_IDS, (uint8_t *)uuid16_list, sizeof(uuid16_list));
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::GENERIC_HEART_RATE_SENSOR);
+    ble.gap().accumulateAdvertisingPayload(GapAdvertisingData::COMPLETE_LOCAL_NAME, (uint8_t *)DEVICE_NAME, sizeof(DEVICE_NAME));
+    ble.gap().setAdvertisingType(GapAdvertisingParams::ADV_CONNECTABLE_UNDIRECTED);
+    ble.gap().setAdvertisingInterval(1000); /* 1000ms */
+    ble.gap().startAdvertising();
+
+    printMacAddress();
+}
+
+void scheduleBleEventsProcessing(BLE::OnEventsToProcessCallbackContext* context) {
+    BLE &ble = BLE::Instance();
+    eventQueue.call(Callback<void()>(&ble, &BLE::processEvents));
+}
+
+int main()
+{
+    maxim_max30102_reset(); //resets the MAX30102
+
+    
+    //read and clear status register
+    maxim_max30102_read_reg(0,&uch_dummy);
+    
+    
+    maxim_max30102_init();  //initializes the MAX30102
+    
+    n_brightness=0;
+    un_min=0x3FFFF;
+    un_max=0;
+    
+    n_ir_buffer_length=500; //buffer length of 100 stores 5 seconds of samples running at 100sps
+    
+    //read the first 500 samples, and determine the signal range
+    for(i=0;i<n_ir_buffer_length;i++)
+    {
+        while(INT.read()==1);   //wait until the interrupt pin asserts
+        
+        maxim_max30102_read_fifo((aun_red_buffer+i), (aun_ir_buffer+i));  //read from MAX30102 FIFO
+            
+        if(un_min>aun_red_buffer[i])
+            un_min=aun_red_buffer[i];    //update signal min
+        if(un_max<aun_red_buffer[i])
+            un_max=aun_red_buffer[i];    //update signal max
+            
+    }
+    un_prev_data=aun_red_buffer[i];
+    
+    //calculate heart rate and SpO2 after first 500 samples (first 5 seconds of samples)
+    maxim_heart_rate_and_oxygen_saturation(aun_ir_buffer, n_ir_buffer_length, aun_red_buffer, &n_sp02, &ch_spo2_valid, &n_heart_rate, &ch_hr_valid); 
+    
+    
+    while(1)
+    {
+        
+        eventQueue.call_every(500, periodicCallback);
+ 
+        BLE &ble = BLE::Instance();
+        ble.onEventsToProcess(scheduleBleEventsProcessing);
+        ble.init(bleInitComplete);
+        
+        eventQueue.dispatch_forever();
+             
+        
+    }
+}
diff -r 000000000000 -r cc636f742803 source/mbed_app.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/mbed_app.json	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,16 @@
+{
+    "target_overrides": {
+        "K64F": {
+            "target.features_add": ["BLE"],
+            "target.extra_labels_add": ["ST_BLUENRG"]
+        },
+        "NUCLEO_F401RE": {
+            "target.features_add": ["BLE"],
+            "target.extra_labels_add": ["ST_BLUENRG"]
+        },
+        "DISCO_L475VG_IOT01A": {
+            "target.features_add": ["BLE"],
+            "target.extra_labels_add": ["ST_BLUENRG"]
+        }
+    }
+}
diff -r 000000000000 -r cc636f742803 source/module.json
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/module.json	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,16 @@
+{
+  "name": "ble-heartrate",
+  "version": "0.0.1",
+  "description": "BLE Heartreate example, building with yotta",
+  "licenses": [
+    {
+      "url": "https://spdx.org/licenses/Apache-2.0",
+      "type": "Apache-2.0"
+    }
+  ],
+  "dependencies": {
+    "ble": "^2.0.0"
+  },
+  "targetDependencies": {},
+  "bin": "./source"
+}
diff -r 000000000000 -r cc636f742803 source/readme.md
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/source/readme.md	Thu Feb 13 14:55:02 2020 +0000
@@ -0,0 +1,66 @@
+# BLE Heart Rate Monitor
+
+This application transmits a heart rate value using the [Bluetooth SIG Heart Rate Profile](https://developer.bluetooth.org/TechnologyOverview/Pages/HRP.aspx). The heart rate value is provided by the application itself, not by a sensor, so that you don't have to get a sensor just to run the example.
+
+Technical details are better presented [in the mbed Classic equivalent of this example](https://developer.mbed.org/teams/Bluetooth-Low-Energy/code/BLE_HeartRate/).
+
+# Running the application
+
+## Requirements
+
+To see the heart rate information on your phone, download Panobike for [iOS](https://itunes.apple.com/gb/app/panobike/id567403997?mt=8) or [Android](https://play.google.com/store/apps/details?id=com.topeak.panobike&hl=en).
+
+You could also use a generic BLE scanners:
+
+- [nRF Master Control Panel](https://play.google.com/store/apps/details?id=no.nordicsemi.android.mcp) for Android.
+
+- [LightBlue](https://itunes.apple.com/gb/app/lightblue-bluetooth-low-energy/id557428110?mt=8) for iPhone.
+
+Hardware requirements are in the [main readme](https://github.com/ARMmbed/mbed-os-example-ble/blob/master/README.md).
+
+## Building instructions
+
+Building instructions for all samples are in the [main readme](https://github.com/ARMmbed/mbed-os-example-ble/blob/master/README.md).
+
+## Checking for success
+
+**Note:** Screens captures depicted below show what is expected from this example if the scanner used is *nRF Master Control Panel* version 4.0.5. If you encounter any difficulties consider trying another scanner or another version of nRF Master Control Panel. Alternative scanners may require reference to their manuals.
+
+1. Build the application and install it on your board as explained in the building instructions.
+1. Open the BLE scanner on your phone.
+1. Start a scan.
+
+    ![](img/start_scan.png)
+
+    **figure 1** How to start scan using nRF Master Control Panel 4.0.5
+
+1. Find your device; it should be named `HRM`.
+
+    ![](img/scan_result.png)
+
+    **figure 2** Scan results using nRF Master Control Panel 4.0.5
+
+1. Establish a connection with your device.
+
+    ![](img/connection.png)
+
+    **figure 3**  How to establish a connection using Master Control Panel 4.0.5
+
+1. Discover the services and the characteristics on the device. The *Heart Rate* service has the UUID `0x180D` and includes the *Heart Rate Measurement* characteristic which has the UUID `0x2A37`.
+
+    ![](img/discovery.png)
+
+    **figure 4** Representation of the Heart Rate service using Master Control Panel 4.0.5
+
+1. Register for the notifications sent by the *Heart Rate Measurement* characteristic.
+
+    ![](img/register_to_notifications.png)
+
+    **figure 5** How to register to notifications using Master Control Panel 4.0.5
+
+
+1. You should see the heart rate value change every half second. It begins at 100, goes up to 175 (in steps of 1), resets to 100 and so on.
+
+    ![](img/notifications.png)
+
+    **figure 6** Notifications view using Master Control Panel 4.0.5