MAX30100 reading data problems (CY8CKIT-059)

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
Benz
Level 2
Level 2
25 sign-ins 10 replies posted 10 sign-ins

Dear Community!

I'm new to implementing sensors with microcontrollers. I'm working with a CY8CKIT-059 and a MAX30100 Spo2 and Heart Rate sensor. My problem is, that I can't read correct, useful data from the max30100. Maybe i'm reading too often, or maybe I should check the SPO2_RDY bit before reading? My teacher said I should create a state-machine for this whole operation, which checks that if the data is ready, then read, if not, wait, etc...

Below you can see my attached workspace which also includes not used stuff int the .h files, sorry for that. The main method can be seen in the main.c file.  I would be greatful if you could suggest a solution for correct timing and reading data. If you have time, I also accept ready-to-use code files to learn from, or a modified version of my code, which works correctly. I would be really happy for it. 

After this problem, my next job is to convert raw data into real Spo2 and Heart-rate data. I say that because if you have a complete project or code which does that, you can post that as well, it helps me a lot. I already checked arduino codes, but arduino works differently, than I have to in PSOC 5 with this CY8CKIT-059.

Thank you very much!

Ben

0 Likes
1 Solution
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

A couple of years ago, I played with MAX30100 (M5Stack) module with a PSoC 4 board.

I could go up to receiving the data

IMG_4010_S.jpg

Attached is the project for PSoC 4 (CY8C4146LQI-S433).

If you can manage to replace the I2C part for 5LP, the project may work,

or I hope that at least max30100.[ch] can be some hints.

Note: Somehow I don't have the sensor right now, so I can not test farther. (Sorry)

moto

 

View solution in original post

28 Replies
EvPa_264126
Level 7
Level 7
500 replies posted 250 replies posted 100 likes received

it seems no one has a max30100...
without max30100 I only see the request of the master (picture)
Can you read the max30100 registers?
have you read about this problem?
i2c.png

0 Likes

Hello, thanks for the quick answer, yes I can read from the MAX30100. I use external 470 Ohm pull up resistors with SDA,SCL and INT to get the correct voltage level.

 

0 Likes
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

A couple of years ago, I played with MAX30100 (M5Stack) module with a PSoC 4 board.

I could go up to receiving the data

IMG_4010_S.jpg

Attached is the project for PSoC 4 (CY8C4146LQI-S433).

If you can manage to replace the I2C part for 5LP, the project may work,

or I hope that at least max30100.[ch] can be some hints.

Note: Somehow I don't have the sensor right now, so I can not test farther. (Sorry)

moto

 

Thank you very much! This should be more than enough to get me back to work!

lock attach
Attachments are accessible only for community members.

Hello! I updated my code based on your previous project what you sent as solution. I have one question:

Benz_0-1615756145829.png

 

the while loop never runs, because the get_raw_values function never returns a number which is bigger than 0.  Going deeper, this is your method:

Benz_1-1615756145956.png

 

The rb_size() also never returns bigger number than 0 ,the first problem was that the compiler couldnt find the rb_size() function as static inline,so I had to redifne it in the .c and .h files., Going more deeper, the  reading:

Benz_2-1615756145405.png

 

Here, the num_to_read is always 0. I think this is the main problem. Normally the read and write pointer is the same, as I check them, so I do not understand why you use AND function. I attached my project, can you please check what could be wrong if you have time? The code is normally the same as yours, there are 1-2 places where I made a change (for example, not static functions, etc.. hope that is not a problem).

The zip file is attached.

Thank you very much!
Ben

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

In my original PSoC 4 project, I called 

max30100_begin()

in

init_hardware()

which comes before 

i2c_write_reg(MAX30100_REG_MODE_CONFIGURATION, 0x40) ; /* RESET */

Could you modify your code and see if the following line returns correct deviceID for MAX30100?

deviceID = max30100_get_part_id();

moto

0 Likes

Dear moto,

The readings are fine, i get the correct deviceID (0x11) back, and I can read and write every register. The problem is that the while loop can not start itself, because of the way num_to_read is being calculated. Its interesting because it seems correct, but I still can not understand why we use AND function between (wr - rd) pointer and fifo depth. So the main problem is i think that num_to_read is always 0, because the wrptr-rdptr=0, or in my case it is always 0, because they have to same value every time.

 

Ben

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

The AND was to mask the number to read which is FIFO_WRITE_POINTER - FIFO_READ_POINTER,

but in some case, it could be a negative number or a very large number and if we try to read the FIFO more than it's allocated memory, memory over run will happen, so that I masked the value to make it within the allocated size of the FIFO. 

Anyway, getting the correct Device ID is a good news.

But num_to_read ==  0 means, the sensor is not updating the write pointer of the fifo.

Meantime, I wonder why I had the following lines after calling init_hardware().

May be I needed them again there. (I'll read it more...)

=====================

    max30100_set_mode(0x03) ; /* MAX30100_MODE_SPO2_HR) */
    max30100_set_leds_current(DEFAULT_IR_LED_CURRENT, DEFAULT_RED_LED_CURRENT) ;
    max30100_set_leds_pulse_width(DEFAULT_PULSE_WIDTH) ;
    max30100_set_sampling_rate(DEFAULT_SAMPLING_RATE) ;
    max30100_set_highres_mode_enabled(1) ;

=====================

Best Regards,

15-Mar-2021

Motoo Tanaka

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

I read you project.

And in max30100.c

===================

void burst_read(uint8 base_address, uint8 *buffer, uint8 length){
    int i;
    I2C_MasterSendStart(MAX30100,I2C_WRITE_XFER_MODE);
    I2C_MasterWriteByte(base_address);
    I2C_MasterSendRestart(MAX30100,I2C_READ_XFER_MODE);
        for(i=0; i < length-1; i++){
            CyDelayUs(100);
            buffer[i]=I2C_MasterReadByte(I2C_NAK_DATA); //<----!!!
        }
        CyDelayUs(100);
    buffer[i]=I2C_MasterReadByte(I2C_NAK_DATA);
    I2C_MasterSendStop();
}

===================

The

I2C_MasterReadByte(I2C_NAK_DATA) ;

in the for (i=0; i < length-1; i++) { } should be

I2C_MasterReadByte(I2C_ACK_DATA) ;

 

Best Regards,

15-Mar-2021

Motoo Tanaka

 

 

0 Likes
lock attach
Attachments are accessible only for community members.

Dear Moto,

First of all, let me thank you that you take lot of time to help me solve this problem.
I made the changes what you wrote before, the program still stuck, it can not enter into the while loop.

Benz_0-1615789584910.png

 

One more thing that maybe can cause the problem, the rb_size() function can only work if I modify it this way:

.h:

Benz_1-1615789661871.png

.c:

Benz_2-1615789716040.png

I dont think that causes the problem but better if we check everything. If i use your default method, which I commented on the first picture, the compiler says undefined reference to rb_size(). You can try it if you uncomment that and clear the sentence in .c file.

In the debug:

Benz_3-1615789858236.png

 

When I get to this point, I never able to get into the while loop. Well the main problem can be I think that when i call read_fifo_data() before that, something is wrong, and a correct reading is not happening..

Maybe read_register function is wrong...?

Benz_4-1615790103200.png

 

You can see here that the num_to_read is 0 again, so we can not enter into the if() . 

I attached again the modified version of the project.


Thank you very much for help!

Ben

 

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Hi,

Maybe read_register function is wrong...?

This alarmed me something!

My bad, I' ve been referencing PSoC 4 i2c_utils.c of mine,  but, yes, I had hard time with PSoC 5LP before.

In my sample for PSoC 5LP

https://community.cypress.com/t5/Code-Examples/MCU-Tester-a-Swiss-Army-Knife-for-PSoC-CY8CKIT-059-ve...

I needed to write totally different read_reg() and write_reg(), some thing like,

=====================

int i2c_read_reg(uint8_t reg_address, uint8_t *value) 
{
    uint8 errStatus = I2C_MSTR_NO_ERROR ;
    errStatus = I2C_MasterSendStart(i2c_slave_address, 0) ; /* write access */
    if (errStatus == I2C_MSTR_NO_ERROR) {
        CyDelay(i2c_delay) ;
        errStatus = I2C_MasterWriteByte(reg_address) ;
    } else {
        print("I2C Send Start Failed!\n\r") ;
    }
    if (errStatus == I2C_MSTR_NO_ERROR) {
        CyDelay(i2c_delay) ;
        errStatus = I2C_MasterSendRestart(i2c_slave_address, 1) ; /* read access */
    } else {
        print("I2C Write Reg Address Failed\n\r") ;
    }
    if (errStatus == I2C_MSTR_NO_ERROR) {
        CyDelay(i2c_delay) ;
        *value = I2C_MasterReadByte(I2C_NAK_DATA) ;
    }
    if (errStatus != I2C_MSTR_NO_ERROR) {
        print("I2C Read Reg Data Failed!\n\r") ;
    }
    errStatus = I2C_MasterSendStop() ;  
    return( errStatus ) ;
}

int i2c_write_reg(uint8_t reg_address, uint8_t value) 
{
    uint8 errStatus = I2C_MSTR_NO_ERROR ;
    errStatus = I2C_MasterSendStart(i2c_slave_address, 0) ; /* write access */
    if (errStatus == I2C_MSTR_NO_ERROR) {
        CyDelay(i2c_delay) ;
        errStatus = I2C_MasterWriteByte(reg_address) ;
    } else {
        print("I2C Send Start Failed!\n\r") ;
    }
    if (errStatus == I2C_MSTR_NO_ERROR) {
        CyDelay(i2c_delay) ;
        errStatus = I2C_MasterWriteByte(value) ;
    }
    if (errStatus != I2C_MSTR_NO_ERROR) {
        print("I2C Write Reg Data Failed!\n\r") ;
    }
    errStatus = I2C_MasterSendStop() ;  
    return( errStatus ) ;
}

=====================

Please refer to i2c_utils.c and i2c_utils.h in my sample program for 5LP above.

Note: Mainly, I needed add some delay between each primitive calls.

 

> One more thing that maybe can cause the problem, the rb_size() function can only work if I modify it this way:

.h:

Yes, I have encountered lots of troubles by using inline, somehow it was working in the sample project, though.

Your solution should be fine,  or me be we can define inline functions in the source code which utilizes the function.

moto

0 Likes

Dear moto,

Well i'm really getting confused and tired now, because i think the functions are fine, or I dont know, they seem fine, I can read every value correctly, but look at this:

Benz_0-1615806839173.png

I modified the function to be able to see the two pointer values. And again, they have the same value, so we are trying to use the & function with 0, thats why num_to_read is always 0. I think still this is the main problem, the functions seems correct for me...

 

Thank you!

Ben

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

As you could read, I also had problem with I2C of 5LP in my sample MCU Tester,

and I spent rather long time to make my "i2c_utils" work.

From the view point of "function" PSoC 4 version seems to be OK, but somehow it did not work for my CY8CKIT-059, so I re-created 5LP version of i2c_utils.c and i2c_utils.h.

Currently my best guess is that i2c_read_regs() function which is reading 4 bytes in burst mode must be having problem.

So I would like to ask to you try following functions from 5LP version of i2c_utils.[ch] in my MCU_Tester sample.

i2c_read_reg()

i2c_read_regs()

Although I'm hoping that they should work, at least they report where problem happened when they encounter a problem.

Best Regards,

15-Mar-2021

Motoo Tanaka

P.S. I wish I could find my MAX30100 module...

 

0 Likes

Dear moto,

Alright, I'll do it. Thank you for you help.

I'll provide feedback as soon as I can.

Ben

lock attach
Attachments are accessible only for community members.

Dear moto,

A very very interesting thing is happening. I didn't make the changes yet to the code, but check this out:

When I program the board, and check the serial monitor, it gets stuck at the start as we could see before:

Benz_0-1615877641197.png

 



BUT, when I unconnect the USB, close the Putty (serial monitor), and when I reconnect the USB and open the putty again, IT WORKS! This is quiet interesting, that when debugging, or after programming the board from PSoC Creator, it's not working, but if I program the board, unplug it, reconnect it, it works.

Why is that happening? 😄

Maybe Power On and Off starts my program correctly? But how can this be?

I made a short video with telephone to demonstrate it.


Thank you!

Ben

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

That sounds interesting!
Can you try either of the following?

(1) Set Tera Term terminal configuration for receive new line to "\n"

(2) Always use "\n\r" at the end of the print() and/or UART_PutString() functions.

Best Regards,

16-Mar-2021

Motoo Tanaka

0 Likes

Dear moto,

I did what you suggested. On this photo you can see, that when I launched PuTTy, it works, I get the data, but when I pressed the program button in PSoC Creator, the same stuck happens.

Benz_0-1615899946524.png

 

 

Well when I do the debug, the num_to_read is still 0. The most interesting question is, that what happens when I plug/unplug the USB, maybe it somehow "resets" the max30100 and it changes num_to_read, so with this the pointers also change, because num_to_read is affected by them. I really don't know what to do now 

Benz_1-1615899946006.png

 

 .

 

Thank you!

Ben

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

It seems that the program is "almost" working.

And as you wrote, may it has something to do with the reset status of MAX30100.

As the M5Stack MAX30100 module I ordered for myself will arrive tomorrow,

may be I can do some debugging to see what is happening.

Best Regards,

16-Mar-2021

Motoo Tanaka

lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

So my sensor arrived and I played with it today.

The good news was that the original project was working with the PSoC 4 board,

but the bad news was that I could reproduce same symptom with yours with PSoC 5LP (CY8CKIT-059).

After struggling a while I noticed a few things

(1) The multiple register read function was not working

(2) Delay between the primitive I2C functions were too long.

For (1) I changed the function to call required number of single register reads.

For (2) I changed CyDelay() to CyDelayUs().

 

I hope that attached updated project should work at your side of the planet, too.

 

Best Regards,

17-Mar-2021

Motoo Tanaka

0 Likes

Dear moto,

Good to hear that, thank you for this hard work! For some fun fact, check this out:

I didn't modify any function, only if I make this change in the read_fifo_data()  :

Benz_0-1615999624272.png

It works 😄 with the old functions, which doesn't have any error receive logic. The only thing that I notice with this change, is that it is slower. I mean I have to wait 2-3s to get the data after putting my finger on the max30100.

I'm an electrical engineer student, I asked professors and they said this is a little "mystery" that it didn't work as we wanted it at the first time. 

Now the new question is that why is it takes 2-3 seconds the data to change... but it is really interesting that checking the SPO2_rdy bit makes it work, but with the num_to_read it's not working.

Thank you again for the passionate work!

Ben

0 Likes
lock attach
Attachments are accessible only for community members.
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

Good Progress!

But there are some more things I'd like to mention.

First of all my calculation of num_to_read was wrong.

It should have been

num_to_read = (fifo_data[0] + MAX30100_FIFO_DEPTH - fifo_data[2] + 0x10) % MAX30100_FIFO_DEPTH ;

Second, the FIFO_DATA (register 0x05) needed to be read at a single transaction (burst read)

And MAX30100_IE_ENB_SPO2_RDY is a bit value in the register MAX30100_REG_INTERRUPT_ENABLE (0x01), so it's always a constant value.

So we needed to do something like

void    max30100_start_spo2_sampling(void) 
{
    uint8_t value ;
    
    value = read_register(MAX30100_REG_INTERRUPT_ENABLE) ;
    value = (value & 0xC0) | 0x30 ;
    write_register(MAX30100_REG_INTERRUPT_ENABLE, value) ;
    
    value = read_register(MAX30100_REG_MODE_CONFIGURATION) ;
    value = (value & 0xF8) | 0x03 ; /* SPO2 enabled */
    write_register(MAX30100_REG_MODE_CONFIGURATION, value) ;
    max30100_set_highres_mode_enabled(1) ;
}
bool    max30100_is_spo2_ready(void) 
{
    uint8_t value ;
    int result = 0 ;
    value = read_register(MAX30100_REG_INTERRUPT_STATUS) ;
    if (value & MAX30100_IS_SPO2_RDY) {
        result = 1 ;
    }
    return(result) ;    
}

Last but not least, so far, we are running the program with the sensor in its free running,

so the sample timing has been at sensor's mercy.

For a precise measurement, the program need to manage the sample timing.

To show you one of the tricks, I implemented "SysTick" method so that the program loop can start measurement at each time interval instead of anytime the sensor feels like going.

You can switch between those methods by changing #if in the main loop.

int main(void)
{
    uint16_t ir, red ;
    
    init_hardware() ;
    
    test_max30100() ;
        
    while(1) {
#if 1 /* freefun */
        if (max30100_update() ) {       
            while(max30100_get_raw_values(&ir, &red)) {
                sprintf(str, "%d, %d\n", ir, red) ;
                print(str) ;
            }
        }
#else /* PIT sample with data buffer */
        if (pit_flag) {
            pit_flag = 0 ;
            if (measure() >= DATA_BUF_LEN) {
                dump_data() ;
                data_index = 0 ;
            }
        }
#endif
    }
}

When I monitored the output of the program using serialplotter

Free Running

002-freerun-sample.JPG

PIT method

001-PIT-sample.JPG

Note: I also printed out the data in each sampling in Free Running,

but I buffered the data and output each block (100 data) in PIT mode

to avoid delay of uart printing.

Yes, there are lots more to think/study about this application,

but allow me to make it/them as your homework 😉

Best Regards,

18-Mar-2021

Motoo Tanaka

P.S. Sorry, right now I don't have an answer to the 2~3 sec delay,

but it seems that the sensor needs some starting up or adjusting time for me.

 

 

 

0 Likes

Dear moto,

Thank you for this awesome work! This should be more than enough to set up data collecting in a correct way. I have one more, last question: Did you make any calculation or method the get the correct SPO2 level or correct Heart Rate from the sensor's raw values?

If not, no problem, i'm just asking because you put a lot of time in this (in the past also).

Thank you very much for this whole discussion!
Ben

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

No, I have not worked on the value calculation of the SPO2 nor Heartrate.

I have seen some library was provided from the vendor (Maxim),

but I have not dived deeper there.

It would be great if you can post your work of these after you finish your project and if it will be shareable.

Anyway, good luck for your project/development!

Best Regards,

18-Mar-2021

Motoo Tanaka

Dear Moto,

I'm still working with the project, but there are one thing I think you can help me with.

There is a free PSoC plotter called SerialPort, I saw on a previous post that you worked (tested) with it before. If I want to use it with my project, to plot MAX30100 data, how can I set it up correctly?

I already did these:
-Unzip files to a folder
-Download serialplot, and opened it, and simply set my port:

 

Benz_1-1616590865641.png

 

Thats all I have done, but i dont think it is enough (correct) .

 

I changed my code a bit so now it only sends IR data to UART, but i dont think using serialplot is this simple as I use it. I think I should attach elements to the top design, I saw that serialplot has its own top design element, but how can I use that in my project? I can not see it when I browse the elements, and maybe there are more settings I have to set, but i'm confused now how to set it up correctly.

Can you explan a correct way to plot IR and maybe RED data together based on my project?
Which settings should I use, and are there more steps that has to done before using it?

 

Thank you,

Ben

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

If you are using my sample,  UART output is being done

            sprintf(str, "%d, %d\n", ir, red) ;
            print(str) ;

So there will be 2 values printed something like

32, 45

So we need to configure the SerialPlot for 2 data separated with comma

001-DataFormat.JPG

And we need to setup Plot tab, too.

Select reasonable range using "Select Range Present",

but enabling "Auto Scale Y Axis" often helps, too.

And we can specify the name of each channel for better view.

002-Plot.JPG

Best Regards,

25-Mar-2021

Motoo Tanaka

0 Likes

Dear Moto,

Thank you very much, it works! So I do not need any top design element like this one? :

Benz_0-1616659965135.png

Because as I understand we already did the UART_PutString and this element would do the same?

One more question, as I tried first I used simple binary format, but I can't clearly understand the difference betveen this binary format and ASCII format in serialplot. Okay, of course i know what ASCII is, but in this situation it confuses me, because our data like:

21000,30000

So the simple binary should be fine-or not? But the SimplePlot only shows the correct data in ASCII format.

Thank you!

Ben

0 Likes
MotooTanaka
Level 9
Level 9
Distributor - Marubun (Japan)
First comment on blog Beta tester First comment on KBA

Dear Ben-san,

 

So I do not need any top design element like this one?

You are correct. You don't have to use such design element.

But mean time you may use such design element if you wish so.

 

One more question, as I tried first I used simple binary format, but I can't clearly understand the difference betveen this binary format and ASCII format in serialplot. 

Let's think about 16-bit data, which represent a value using 2 bytes (16bits).

So 21000 is 0x5208 in hex or 0101 0010 0000 1000  in binary.

If you want to send 21000 using binary format, the hardware needs to send only 2 bytes. And using a serial communication such as UART, it takes at least 10 bits to send a byte, as it requires additional "start bit" and "stop bit". 

So the data sent will be

S 0101 0010 P S 0000 1000 P 

where S is start bit and P is stop bit.

 

On the other hand if you want to send 21000 using ascii format,

you need to send 5 bytes 

'2' '1' '0' '0' '0' 

and their binary representations are

0x32 0x31 0x30 0x30 0x30

So the bits to be sent are

S 0011 0010 P S 0011 0001 P S 0011 0000 P  S 0011 0000 P S 0011 0000 P

Total of (at least) 50 bits are required.

And in ascii format the number of digits can be 1 to 5 (or 6 if there will be sign '-').

This requires more work for the hardware of both sending and receiving.

 

IMHO

Binary format:

Pros: efficient / faster

Cons:  can't be read on a terminal program directly

Ascii format:

Pros: data can be read directly on the terminal

           and easier to manipulate with a text editor,

           and/or use as CSV for Excel like programs

Cons:  less efficient / slower

Please refer to the following discussion, too.

https://community.cypress.com/t5/Code-Examples/How-I-recycle-the-data-aka-Return-of-CCS811/m-p/98877

Best Regards,

26-Mar-2021

Motoo Tanaka

 

0 Likes

Dear Moto,

Thank you very much for this clear description, now I understand everything!

With a lot of thanks,

Ben