1 year ago.

nRF52-DK & MAX30003 WING ecg sensor

Hi all,

I have been working on a project on the nRF52 development kit and with the ecg sensor breakout board MAX30003 WING.

Ideally, I would like the device to collect all the ecg samples and also the R-to-R intervals and broadcasting it through bluetooth. It would be great to have the ability to only collect the raw data and the R-to-R intervals only upon the interrupt call from MAX30003 WING.

I found some ecg demo projects on mbed but they are written for MAX32630FTHR. I then referred to the demos and tried to integrate it's function on nRF52 DK. Similar to the demo, I am using SPI to communicate with the ecg sensor.

After writing the project, there are some problems that surfaced that I could not understand nor fix,

1. No interrupt calls were triggered from the ecg sensor despite the ecg FIFO being full. From what I have understood from the datasheet, there are a few interrupts that the MAX30003 WING will trigger, including R-to-R interrupt, but none of it were actually being called. Therefore, the no readings are coming in.

2. Setting aside the interrupt calls, I would like to see a working set of data and went ahead to reading the ecg FIFO directly instead. When displayed on the serial plotter, there are actually data coming through and being read, but it is nothing near from a QRS wave and resembles nothing like an ecg signal. R-to-R readings are present but they were not accurate and it seems like they were almost random and arbitrary.

With these problems in mind, I have also discovered that the VDD pin on the nRF52 DK board were not supplying 3.3v as there is a diode that were shown in the schematics that's dropping the voltage by 0.4v0.5v. As the ecg board requires a 3.3v supply and there is a voltage regulator on the board that could take in 5v, I have then tried to run the code with the 3v3 pin of MAX30003WING connected to the 5v pin on nRF52 DK, but the results is similar as before.

Can anyone that might have worked on any of the both devices that could enlighten me on these issues? Thank you so much.

Here is the code that I have pieced up using the sample demo project found on the forum

#include <mbed.h>
#include "MAX30003.h"

void ecgConfig (MAX30003 &ecgAFE); // Config function prototype

/* ECG FIFO nearly full callback */
/*volatile bool ecgFIFOIntFlag = 0; 
void ecgFIFO_callback()  {
    
    ecgFIFOIntFlag = 1;

}  
*/

int main() {
    const int EINT_STATUS_MASK =  1 << 23;
    const int FIFO_OVF_MASK =  0x7;
    const int FIFO_VALID_SAMPLE_MASK =  0x0;
    const int FIFO_FAST_SAMPLE_MASK =  0x1;
    const int ETAG_BITS_MASK = 0x7;
    
    printf("START\r\n");
    
    SPI spi(SPI_PSELMOSI1, SPI_PSELMISO1, SPI_PSELSCK1); 
    DigitalOut cs(SPI_PSELSS1);
    // InterruptIn ecgFIFO_int(p15); // PIN 15 is D5
    ecgFIFO_int.fall(&ecgFIFO_callback);
    ecgFIFO_int.enable_irq();
    
    MAX30003 ecgAFE(spi, SPI_PSELSS1); // New MAX30003 on spiBus, CS = SPI_PSELSS0
    
    ecgConfig( ecgAFE ); // Config ECG 
    ecgAFE.writeRegister( MAX30003::SYNCH , 0);    

    uint32_t ecgFIFO, RtoR, readECGSamples, idx, ETAG[32], status;
    int16_t ecgSample[32];
    float BPM;
    
    const int EINT_STATUS =  1 << 23;
    const int RTOR_STATUS =  1 << 10; 
    const int RTOR_REG_OFFSET = 10;
    const float RTOR_LSB_RES = 0.0078125f;
    const int FIFO_OVF =  0x7;
    const int FIFO_VALID_SAMPLE =  0x0;
    const int FIFO_FAST_SAMPLE =  0x1;
    const int ETAG_BITS = 0x7;
            
    while(1) {
        readECGSamples = 0;                        // Reset sample counter
                
        do {
            ecgFIFO = ecgAFE.readRegister( MAX30003::ECG_FIFO );       // Read FIFO
            ecgSample[readECGSamples] = ecgFIFO >> 8;                  // Isolate voltage data
            ETAG[readECGSamples] = ( ecgFIFO >> 3 ) & ETAG_BITS;        // Isolate ETAG
            readECGSamples++;                                          // Increment sample counter
                
            // Check that sample is not last sample in FIFO                                              
        } 
        while ( ETAG[readECGSamples-1] == FIFO_VALID_SAMPLE || ETAG[readECGSamples-1] == FIFO_FAST_SAMPLE ); 
                
        printf("%d samples read from FIFO \r\n", readECGSamples);
                
        // Check if FIFO has overflowed
        if( ETAG[readECGSamples - 1] == FIFO_OVF ){                  
            ecgAFE.writeRegister( MAX30003::FIFO_RST , 0); // Reset FIFO
                    
        }
                
                // Print results 
        for( idx = 0; idx < readECGSamples; idx++ ) {
            printf("Sample : %6d, \tETAG : 0x%x\r\n", ecgSample[idx], ETAG[idx]);           
        }
        printf("\r\n\r\n\r\n"); 
        
        // Read back ECG samples from the FIFO 
        if( ecgFIFOIntFlag ) {
            printf("Interrupt triggered\r\n");
            ecgFIFOIntFlag = 0; 
            status = ecgAFE.readRegister( MAX30003::STATUS );      // Read the STATUS register
            printf("Status : 0x%x\r\n", status);
            
            // Check if R-to-R interrupt asserted
            if( ( status & RTOR_STATUS ) == RTOR_STATUS ){           
            
                printf("R-to-R Interrupt \r\n");
                
                // Read RtoR register
                RtoR = ecgAFE.readRegister( MAX30003::RTOR ) >>  RTOR_REG_OFFSET;   
                
                // Convert to BPM
                BPM = 1.0f / ( RtoR * RTOR_LSB_RES / 60.0f );   
                
                // Print RtoR              
                printf("RtoR : %d\r\n\r\n", RtoR);                   
                
            } 
            
            // Check if EINT interrupt asserted
            if ( ( status & EINT_STATUS ) == EINT_STATUS ) {     
            
                printf("FIFO Interrupt \r\n");
                readECGSamples = 0;                        // Reset sample counter
                
                do {
                    ecgFIFO = ecgAFE.readRegister( MAX30003::ECG_FIFO );       // Read FIFO
                    ecgSample[readECGSamples] = ecgFIFO >> 8;                  // Isolate voltage data
                    ETAG[readECGSamples] = ( ecgFIFO >> 3 ) & ETAG_BITS;  // Isolate ETAG
                    readECGSamples++;                                          // Increment sample counter
                
                // Check that sample is not last sample in FIFO                                              
                } while ( ETAG[readECGSamples-1] == FIFO_VALID_SAMPLE || 
                          ETAG[readECGSamples-1] == FIFO_FAST_SAMPLE ); 
            
                printf("%d samples read from FIFO \r\n", readECGSamples);
                
                // Check if FIFO has overflowed
                if( ETAG[readECGSamples - 1] == FIFO_OVF ){                  
                    ecgAFE.writeRegister( MAX30003::FIFO_RST , 0); // Reset FIFO
                    
                }
                
                // Print results 
                for( idx = 0; idx < readECGSamples; idx++ ) {
                    printf("Sample : %6d, \tETAG : 0x%x\r\n", ecgSample[idx], ETAG[idx]);           
                }
                printf("\r\n\r\n\r\n"); 
                                         
            }
                               
            }
        }
    }

void ecgConfig(MAX30003& ecgAFE) { 
 
    // Reset ECG to clear registers
    ecgAFE.writeRegister( MAX30003::SW_RST , 0);
    
    // General config register setting
    MAX30003::GeneralConfiguration_u CNFG_GEN_r;
    CNFG_GEN_r.bits.en_ecg = 1;     // Enable ECG channel
    CNFG_GEN_r.bits.rbiasn = 1;     // Enable resistive bias on negative input
    CNFG_GEN_r.bits.rbiasp = 1;     // Enable resistive bias on positive input
    CNFG_GEN_r.bits.en_rbias = 1;   // Enable resistive bias
    CNFG_GEN_r.bits.imag = 2;       // Current magnitude = 10nA
    CNFG_GEN_r.bits.en_dcloff = 1;  // Enable DC lead-off detection   
    ecgAFE.writeRegister( MAX30003::CNFG_GEN , CNFG_GEN_r.all);
        
    
    // ECG Config register setting
    MAX30003::ECGConfiguration_u CNFG_ECG_r;
    CNFG_ECG_r.bits.dlpf = 1;       // Digital LPF cutoff = 40Hz
    CNFG_ECG_r.bits.dhpf = 1;       // Digital HPF cutoff = 0.5Hz
    CNFG_ECG_r.bits.gain = 3;       // ECG gain = 160V/V
    CNFG_ECG_r.bits.rate = 2;       // Sample rate = 128 sps
    ecgAFE.writeRegister( MAX30003::CNFG_ECG , CNFG_ECG_r.all);
      
    
    //R-to-R configuration
    MAX30003::RtoR1Configuration_u CNFG_RTOR_r;
    CNFG_RTOR_r.bits.en_rtor = 1;           // Enable R-to-R detection
    ecgAFE.writeRegister( MAX30003::CNFG_RTOR1 , CNFG_RTOR_r.all);
       
        
    //Manage interrupts register setting
    MAX30003::ManageInterrupts_u MNG_INT_r;
    MNG_INT_r.bits.efit = 0b00011;          // Assert EINT w/ 4 unread samples
    MNG_INT_r.bits.clr_rrint = 0b01;        // Clear R-to-R on RTOR reg. read back
    ecgAFE.writeRegister( MAX30003::MNGR_INT , MNG_INT_r.all);
    
    
    //Enable interrupts register setting
    MAX30003::EnableInterrupts_u EN_INT_r;
    EN_INT_r.all = 1;
    EN_INT_r.bits.en_eint = 1;              // Enable EINT interrupt
    EN_INT_r.bits.en_rrint = 1;             // Disable R-to-R interrupt OR 1 = enable
    EN_INT_r.bits.intb_type = 3;            // Open-drain NMOS with internal pullup
    ecgAFE.writeRegister( MAX30003::EN_INT , EN_INT_r.all);
       
       
    //Dyanmic modes config
    MAX30003::ManageDynamicModes_u MNG_DYN_r;
    MNG_DYN_r.bits.fast = 0;                // Fast recovery mode disabled
    ecgAFE.writeRegister( MAX30003::MNGR_DYN , MNG_DYN_r.all);
    
    // MUX Config
    MAX30003::MuxConfiguration_u CNFG_MUX_r;
    CNFG_MUX_r.bits.openn = 0;          // Connect ECGN to AFE channel
    CNFG_MUX_r.bits.openp = 0;          // Connect ECGP to AFE channel
    ecgAFE.writeRegister( MAX30003::CNFG_EMUX , CNFG_MUX_r.all);
       
    return;
}  
Be the first to answer this question.