Heart rate sensor MAX30102


Closed Thread
Results 1 to 40 of 85

Hybrid View

  1. #1
    Join Date
    Oct 2010
    Posts
    413


    Did you find this post helpful? Yes | No

    Default Re: Heart rate sensor MAX30102

    Thanks Richard,

    just wanted to understand it. Now it is clear.

  2. #2
    Join Date
    May 2013
    Location
    australia
    Posts
    2,721


    Did you find this post helpful? Yes | No

    Default Re: Heart rate sensor MAX30102

    now have low pass filtering on pic
    Name:  HB2.jpg
Views: 4909
Size:  139.6 KB
    73 rpm tonight
    Warning I'm not a teacher

  3. #3
    Join Date
    Oct 2010
    Posts
    413


    Did you find this post helpful? Yes | No

    Default Re: Heart rate sensor MAX30102

    That's really impressed Richard.

    Im still working step by step on the code, im still at an early stage.

    Just need to understand the methodology.

  4. #4
    Join Date
    May 2013
    Location
    australia
    Posts
    2,721


    Did you find this post helpful? Yes | No

    Default Re: Heart rate sensor MAX30102

    Name:  HB3.jpg
Views: 5012
Size:  77.9 KB

    try as i might i cannot detect the relevant peaks reliably


    Code:
    '****************************************************************'*  Name    : MAX30102  FOR PIC 18 DEMO                         *
    '*  Author  :  Richard                                          *
    '*  Notice  :                                                   *
    '*          :                                                   *
    '*  Date    : 9/1/2022                                          *
    '*  Version : 1.0                                               *
    '*  Notes   : MAX30102                                          *        
    '*          :18f26k22 @64Mhz                                    *
    '****************************************************************
     
      #CONFIG   ;  The PBP configuration for the PIC18F26K22 is:
        CONFIG FOSC     = INTIO67	       
        CONFIG PLLCFG   = OFF	        ;Oscillator multiplied by 4                       
        CONFIG PRICLKEN  = ON	        ;Primary clock enabled
        CONFIG FCMEN     = OFF	        ;Fail-Safe Clock Monitor disabled
        CONFIG IESO      = OFF	        ;Oscillator Switchover mode disabled
        CONFIG  BOREN    = SBORDIS      ; Brown-out Reset enabled in hardware only (SBOREN is disabled)
        CONFIG  WDTEN    = ON           ; WDT is always enabled. SWDTEN bit has no effect                     ;|
        CONFIG  WDTPS    = 32768        ; 1:32768 ---> HERE enable the watchdog timer with a 1:32768 postscale;|
        CONFIG  PWRTEN   = ON
        CONFIG  HFOFST   = ON           ; HFINTOSC output and ready status are not delayed by the oscillator stable status
        CONFIG  MCLRE    = EXTMCLR      ; MCLR pin enabled, RE3 input pin disabled
        CONFIG  LVP      = OFF          ; Single-Supply ICSP disabled
        CONFIG  XINST    = OFF          ; Instruction set extension and Indexed Addressing mode disabled (Legacy mode)
        CONFIG  DEBUG    = OFF          ; Disabled
        CONFIG  CP0 = OFF             ; Block 0 (000800-003FFFh) not code-protected
        CONFIG  CP1 = OFF             ; Block 1 (004000-007FFFh) not code-protected
        CONFIG  CP2 = OFF             ; Block 2 (008000-00BFFFh) not code-protected
        CONFIG  CP3 = OFF             ; Block 3 (00C000-00FFFFh) not code-protected
        CONFIG  CPB = OFF             ; Boot block (000000-0007FFh) not code-protected
        CONFIG  CPD = OFF             ; Data EEPROM not code-protected
        CONFIG  WRT0 = OFF            ; Block 0 (000800-003FFFh) not write-protected
        CONFIG  WRT1 = OFF            ; Block 1 (004000-007FFFh) not write-protected
        CONFIG  WRT2 = OFF            ; Block 2 (008000-00BFFFh) not write-protected
        CONFIG  WRT3 = OFF            ; Block 3 (00C000-00FFFFh) not write-protected
        CONFIG  WRTC = OFF            ; Configuration registers (300000-3000FFh) not write-protected
        CONFIG  WRTB = OFF            ; Boot Block (000000-0007FFh) not write-protected
        CONFIG  WRTD = OFF            ; Data EEPROM not write-protected
        CONFIG  EBTR0 = OFF           ; Block 0 (000800-003FFFh) not protected from table reads executed in other blocks
        CONFIG  EBTR1 = OFF           ; Block 1 (004000-007FFFh) not protected from table reads executed in other blocks
        CONFIG  EBTR2 = OFF           ; Block 2 (008000-00BFFFh) not protected from table reads executed in other blocks
        CONFIG  EBTR3 = OFF           ; Block 3 (00C000-00FFFFh) not protected from table reads executed in other blocks
        CONFIG  EBTRB = OFF           ; Boot Block (000000-0007FFh) not protected from table reads executed in other blocks
     #ENDCONFIG  
     goto overasm  
     asm
    PutMulResult?D macro Din
        MOVE?BB   Din, R2
        MOVE?BB   Din + 1, R2 + 1
        MOVE?BB   Din + 2, R0
        MOVE?BB   Din + 3, R0 + 1
        RST?RP
      endm
     endasm
     overasm:
    
    
    define  OSC 64
    OSCCON    = %01110000   ; 16Mhz
    OSCTUNE.6 = 1           ; Enable 4x PLL
    while ! osccon2.7 :WEND ; to make sure the pll has stabilised before you run any other code
    
    
       
    TRISC = %11011000                  
    ANSELC=0
    ANSELB=0
    #DEFINE    DEBUGING 1
    DEFINE DEBUG_REG PORTB
    DEFINE DEBUG_BIT 7
    DEFINE DEBUG_BAUD 9600
    DEFINE DEBUG_MODE 0
    
    
    
    
    
    
    SDA     var portc.4  
    SCL     VAR portc.3  
    
    
    
    
    reg        var BYTE
    fifo       var byte[6] bank0
    addr       var byte 
    REVISION   var byte
    PARTID     var byte
    buffer     var word[384] 
    ptr        var byte
    btr        var byte
    index      var word
    average    var byte[4] bank0
    ave        var word
    peaks      var byte[5]
    lastpeak   var word 
    lpfdata    var word[192]
    result     var word ext
    result1    var word ext
    @result  =  _fifo 
    @result1 =  _fifo + 3
    ;-----------------------  REGISTER -------------------------
    REG_TEMP_INT         con $1F
    REG_TEMP_FRAC        con $20
    REG_TEMP_CONFIG      con $21
    REG_PART_REVISION    con $FE
    REG_MODE             con $09
    REG_SPO2_CONFIG      con $0A        ;SPO2_ADC_RGE[6:5]SPO2_SR[4:2]LED_PW[1:0]
    REG_LED1_PA          con $0C
    REG_LED2_PA          con $0D
    FIFO_WR_PTR          con $04        ;FIFO_WR_PTR[4:0]
    FIFO_RD_PTR          con $06
    FIFO_DATA            con $07
    FIFO_CONFIG          con $08        ;FIFO_A_FULL[3:0]   SMP_AVE[7:5]
    OVF_COUNTER          con $05
    STATUS1              con 0          ;A_FULL[7]
    INTERRUPT_EN         con 2
    ' -----------------------------------------------------------------------------|  
    
    
    #IFDEF  DEBUGING
        LATB.7=1
        PAUSE 2000
        DEBUG 13,10,  "READY"
    #ENDIF
        ADDR = $ae     ; addr is  0x57 << 1
        reg = REG_PART_REVISION
        i2cread  SDA,scl,ADDR,reg,[REVISION,PARTID]
        reg = REG_MODE 
        i2cwrite  SDA,scl,ADDR,reg,[3] 
        reg = REG_SPO2_CONFIG
        i2cwrite  SDA,scl,ADDR,reg,[$20]
        reg = REG_LED2_PA 
        i2cwrite  SDA,scl,ADDR,reg,[10]
        reg = REG_LED1_PA 
        i2cwrite  SDA,scl,ADDR,reg,[10]
        reg = FIFO_CONFIG  
        i2cwrite  SDA,scl,ADDR,reg,[16]        
        reg = INTERRUPT_EN  
        i2cwrite  SDA,scl,ADDR,reg,[$E0]
        DEBUG 13,10,  "PART ID: 0X",hex PARTID,"/0X",hex REVISION
    
    
    start:
        reg= REG_TEMP_CONFIG
        i2cwrite  SDA,scl,ADDR,reg,[1] 
        reg= REG_TEMP_INT        
        i2cread  sda,scl,ADDR,reg,[fifo[0],fifo[1]]
        DEBUG 13,10, "TEMP: ",DEC fifo[0],".",DEC2( fifo[1]<<6)
        GOSUB AQUIRE
        pause 2000
    goto start
    
    
    aquire:
        reg= FIFO_WR_PTR
        i2cwrite  SDA,scl,ADDR,reg,[0]
        reg= FIFO_RD_PTR
        i2cwrite  SDA,scl,ADDR,reg,[0]
        reg = OVF_COUNTER
        i2cwrite sda,scl,ADDR,reg,[0]
        asm
        banksel 0 
        clrf _average
        clrf _average+1
        clrf _average+2
        clrf _average+3
        endasm
     FOR index = 0 TO 255 STEP 0
        btr = 0
        reg = STATUS1
        WHILE (btr & 192) = 0
            i2cread  sda,scl,ADDR,reg,[btr]
        WEND
        reg = FIFO_WR_PTR
        i2cread  SDA,scl,ADDR,reg,[Ptr]
        reg = FIFO_RD_PTR
        i2cread  SDA,scl,ADDR,reg,[btr]
        btr = (ptr - btr) &  31
        WHILE btr
            reg = FIFO_DATA           ;---------ir----------;===========red===========
            i2cread  sda,scl,ADDR,reg,[FIFO[5],FIFO[4],FIFO[3],FIFO[2],FIFO[1],FIFO[0]]
            asm         ;ROTATE READING TO GET SUFFICIENT RESOLOUTION INTO lower WORD
        banksel 0
        bcf STATUS,C
        RRCF  _fifo + 2 ,f
        RRCF  _fifo + 1 ,f
        RRCF  _fifo     ,f
        bcf STATUS,C
        RRCF  _fifo + 2 ,f
        RRCF  _fifo + 1 ,f
        RRCF  _fifo     ,f
        bcf STATUS,C
        RRCF  _fifo + 5 ,f
        RRCF  _fifo + 4 ,f
        RRCF  _fifo + 3 ,f
        bcf STATUS,C
        RRCF  _fifo + 5 ,f
        RRCF  _fifo + 4 ,f
        RRCF  _fifo + 3 ,f
        
        movf  _fifo  ,w         ;RED AVERAGE
        addwf  _average,f
        movf  _fifo + 1 ,w
        bnc s1   
        incf   _average + 1,f
    s1    
        addwf  _average + 1,f
        movf  _fifo + 2 ,w
        bnc    s2
        incf   _average + 2,f
    s2    
        bnc     s3
        incf   _average + 3 ,f
    s3  
    endasm
            buffer[index] = result  ;red
            index = index + 1
            buffer[index] = result1 ;ir
            index = index + 1
            btr = btr - 1
        WEND
    NEXT  
        DEBUG 13,10,hex2 average[3],hex2 average[2],hex2 average[1],hex2 average  
        @ PutMulResult?D _average   ;RED AVERAGE
        ave  = DIV32 128            ;RED AVERAGE
        reg = OVF_COUNTER
        i2cread sda,scl,ADDR,reg,[reg ] 
        DEBUG 13,10,"OverFlows ",DEC reg,9,dec ave,9,hex ave
        FOR index = 0 TO 255 STEP 0
            ave = (ave ** 62259) + (buffer[index] ** 3276 )
            lpfdata[index>>1]=ave 
                  ;.....lpf..;;;;----red-----;;;;======-ir-========
            DEBUG 13,10,dec ave,9,dec buffer[index],9,dec buffer[index+1]
            index = index + 2
        NEXT 
     RETURN
    Attached Files Attached Files
    Warning I'm not a teacher

  5. #5
    Join Date
    Apr 2014
    Location
    OK
    Posts
    557


    Did you find this post helpful? Yes | No

    Default Re: Heart rate sensor MAX30102

    In Post #44 your low-pass filter seemed to work perfectly, then in Post #49, it seemed a bit overdone. Then in Post #51 it looks like the low-pass filter is acting like a 500F capacitor! I didn't look at the code, but maybe you could back off on the filter settings for a clearer reading.

  6. #6
    Join Date
    May 2013
    Location
    australia
    Posts
    2,721


    Did you find this post helpful? Yes | No

    Default Re: Heart rate sensor MAX30102

    but maybe you could back off on the filter settings for a clearer reading.
    i have tried many variations , the added aggressiveness of the filter is an attempt to remove the unwanted "little" peaks
    what looks like a nice picture of on a graph does not mean peak detection is easier.
    some of the lp filtering is just added in with excel also, even tried using trend lines in attempt at detecting pos and neg zero crossings
    that's still not 100% effective. dsp is not my forte, dc drift in the readings can be neg or pos going or both or zero.
    fft was a failure because i cannot remove the dc component effectively. i need a better method i have no effective strategy
    Warning I'm not a teacher

  7. #7
    Join Date
    Apr 2014
    Location
    OK
    Posts
    557


    Did you find this post helpful? Yes | No

    Default Re: Heart rate sensor MAX30102

    Here is a Filter algorithm that has served me well with several projects. For the moment I'm going to assume your code serves 2 main functions: read the heart rate MAX30102 and report the results. This means the processor execution time can be maximized for these 2 functions.

    Reporting process is what it is. That's whatever protocol (UART, SPI, I2C, or LCDOUT etc) you have chosen to make use of the processed data.

    As for reading and filtering the MAX30102 data, the first stage is to take multiple ADC reads (or appropriately retrieve the latest data report) each Subroutine CALL and average them. The result gets fed into a circular buffer, where the average is the last X readings, and what you shoot out in your report. It looks something like this:

    Somewhere a CALL (GOSUB) is made to check the input from the MAX30102 sensor. Within the Subroutine, you do this 4X and average the results. The average result is fed into a [4] BYTE/WORD circular buffer. An Accumulator adds all buffer entries and divides the result by the number of entries (calculates an average). The value reported is the average of the circular buffer -- buffered value. With me so far? Let's take it for a test drive, shall we?
    Code:
    b0 VAR BYTE ;Used for FOR/LOOP
    HrVal VAR BYTE ;if using 8-bit ADC, ...VAR WORD for 10-bit, or whatever protocol demands
    HrValBuf VAR BYTE[4] ;again, WORD for 10-bit, this is your 4 immediate ADC (MAX30102 input) reads Buffer
    HrValCir VAR BYTE[4] ;again, WORD for 10-bit, this is your 4 entry Circular Buffer
    HrBufAcc VAR WORD ;Total of all 4 HrValBuf readings
    HrCirAcc Var WORD ;Total of all 4 HrCirBuf readings
    HeartRate VAR BYTE ;again, WORD for 10-bit, End Filtered Result to be Reported or Processed
    
    ...  ;from Somewhere in Code:
    GOSUB Get_HeartRate
    GOSUB Send_HeartRate ;Do something with the HeartRate Value Acquired & Filtered
    ...  ;then Continue Code...
    
    Get_Heartrate:
     HrValCir[3] = HrValCir[2]  ;Rotate the Circular Buffer
     HrValCir[2] = HrValCir[1]
     HrValCir[1] = HrValCir[0]
     FOR b0 = 0 TO 3
      ADCIN MAX30102,HrVal ;or I2CREAD or SPIREAD -- Retrieve a Current Value
      HrValBuf[b0] = HrVal
     NEXT b0
     HrBufAcc = HrValBuf[3] + HrValBuf[2] + HrValBuf[1] + HrValBuf[0] ;Add All 4 Readings...
     HrValCir[0] = HrBufAcc >> 2 ;Same as HrValCir[0] = HrBufAcc / 4 -- Places the Latest Averaged Value into the Circular Buffer
     HrCirAcc = HrValCir[3] + HrValCir[2] + HrValCir[1] + HrValCir[0] ;Totals the Circular Buffer
     HeartRate = HrCirAcc >> 2 ;Same as HeartRate = HrCirAcc / 4, Averages the Circular Buffer
    RETURN
    To change the impact of the filter, you can increase or decrease the size of the HrValBuf[4] buffer and/or the HrValCir[4] buffer. If too jumpy, add more entries for either/both buffer(s). Conversely, to economize, try reducing buffer sizes and see if you still like the results. Changes would have to be reflected in Get_Heartrate:. No, of course this isn't the absolute most efficient code to accomplish the goal, and it is too abbreviated to actually work, but I hope it illustrates an effective filter that has served me well over many projects.
    Last edited by mpgmike; - 15th January 2022 at 01:52.

  8. #8
    Join Date
    May 2013
    Location
    australia
    Posts
    2,721


    Did you find this post helpful? Yes | No

    Default Re: Heart rate sensor MAX30102

    I think Richard is eluding to a process called Fast Fourier Transform
    been there done that.

    Quote Originally Posted by richard View Post
    post#53 i have tried many variations , the added aggressiveness of the filter is an attempt to remove the unwanted "little" peaks
    what looks like a nice picture of on a graph does not mean peak detection is easier.
    some of the lp filtering is just added in with excel also, even tried using trend lines in attempt at detecting pos and neg zero crossings
    that's still not 100% effective. dsp is not my forte, dc drift in the readings can be neg or pos going or both or zero.
    fft was a failure because i cannot remove the dc component effectively. i need a better method i have no effective strategy
    The one that is like Example #1 in the textbooks is simply:
    there is plenty of raw data posted, you can always ask for more. feel free to have a go
    Last edited by richard; - 19th January 2022 at 22:22.
    Warning I'm not a teacher

Similar Threads

  1. New PIC failure rate
    By timmers in forum General
    Replies: 5
    Last Post: - 26th March 2009, 12:11
  2. Rf module baud rate
    By tazntex in forum Serial
    Replies: 4
    Last Post: - 5th August 2008, 18:47
  3. Replies: 6
    Last Post: - 18th January 2008, 08:17
  4. SHIFTOUT Clock rate
    By Brock in forum mel PIC BASIC Pro
    Replies: 10
    Last Post: - 8th July 2006, 23:42
  5. Detect baud rate
    By Dick M in forum mel PIC BASIC Pro
    Replies: 1
    Last Post: - 2nd July 2005, 21:10

Members who have read this thread : 0

You do not have permission to view the list of names.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts