PDA

View Full Version : Heart rate sensor MAX30102



astanapane
- 16th August 2020, 19:15
Dear all,

the covid-19 period, i have spent lots of time designing a 3D printed Adaptable medical Mask for covid-19. It was succesful using bio-compatible materials

(here's the liink: https://www.linkedin.com/feed/update/urn:li:activity:6667176630277947393/)

I have printed free of charge, for the kids in school about 50 of face shields in order to be safe inside the lectures.

The same i did for the professional 3D printed Adaptable Medical Mask, for Hospitals. Just printed couple of those for testing.

Now i would like to prepare a free of charge again low cost electronic heart rate circuit, for my sons classroom. Kids are in total 15 in class.


As it is my first time using I2C protocol, i would like your help in order to establish a connection with the heart sensor MAX30102.

If anyone have tried this sensor i would be much appreciated if you could share a part of your code.

I have in front of me the manual of the MAX30102 and all the control and register modes. Actually i'm reading this 2 days now, and really as a newbie, cant understand where to start from.

I will use the PIC18F26K22 at 16Mhz as from the PICBASIC manual I2C protocol needs to run up to 20MHZ using the I2CWRITE or I2CREAD software commands. (i hope i'm right)

Next the following ports will be used.




LATC.1 for Data
LATC.2 for Clock

also reserved two more ports for external serial display

LATC.6 for TX
LATC.7 for RX



Some MAX30102 registers


;-------------------------------------------------------------|
;------------------------ max30102 registers------------------|
;-------------------------------------------------------------|
define I2C_WRITE_ADDR $AE ; b10101110
define I2C_READ_ADDR $AF ; b10101111
define I2C_ID_ADDR $57 ; 7-bit version of the above
;define I2C_ID_ADDR 57h ; 7-bit version of the above

;//register addresses
;---------------------- STATUS REGISTER -----------------------
define REG_INTR_STATUS_1 $00
define REG_INTR_STATUS_2 $01

define REG_INTR_ENABLE_1 $02
define REG_INTR_ENABLE_2 $03

;----------------------- FIFO REGISTERS -----------------------
define REG_FIFO_WR_PTR $04 ; FIFO WRITE POINTER REG ADDR IS 0X04
define REG_OVF_COUNTER $05
define REG_FIFO_RD_PTR $06
define REG_FIFO_DATA $07

;---------------------- CONFIG REGISTER -----------------------
define REG_FIFO_CONFIG $08
define REG_MODE_CONFIG $09
define REG_SPO2_CONFIG $0A
define REG_LED1_PA $0C
define REG_LED2_PA $0D


define REG_PILOT_PA $10
define REG_MULTI_LED_CTRL1 $11
define REG_MULTI_LED_CTRL2 $12

;----------------------- TEMP REGISTER -------------------------
define REG_TEMP_INTR $1F
define REG_TEMP_FRAC $20
define REG_TEMP_CONFIG $21
define REG_PROX_INT_THRESH $30

;----------------------- PART ID REGISTER ----------------------
define REG_REV_ID $FE
define REG_PART_ID $FF


Now i'm trying to understand based on the manual how could i communicate with sensor and at least see it alive (any led light on the sensor)

Really looking for your help.

astanapane
- 16th August 2020, 22:00
on the first post i think i made a mistake with Defines.

Is this the correct way to assign a constant value to the name?



;-------------------------------------------------------------|
;------------------------ max30102 registers------------------|
;-------------------------------------------------------------|
I2C_WRITE_ADDR con $AE ; b10101110
I2C_READ_ADDR con $AF ; b10101111
I2C_ID_ADDR con $57 ; 7-bit version of the above
;I2C_ID_ADDR con 57h ; 7-bit version of the above

;//register addresses
;---------------------- STATUS REGISTER -----------------------
REG_INTR_STATUS_1 con $00
REG_INTR_STATUS_2 con $01

REG_INTR_ENABLE_1 con $02
REG_INTR_ENABLE_2 con $03

;----------------------- FIFO REGISTERS -----------------------
REG_FIFO_WR_PTR con $04 ; FIFO WRITE POINTER REG ADDR IS 0X04
REG_OVF_COUNTER con $05
REG_FIFO_RD_PTR con $06
REG_FIFO_DATA con $07

;---------------------- CONFIG REGISTER -----------------------
REG_FIFO_CONFIG con $08
REG_MODE_CONFIG con $09
REG_SPO2_CONFIG con $0A
REG_LED1_PA con $0C
REG_LED2_PA con $0D


REG_PILOT_PA con $10
REG_MULTI_LED_CTRL1 con $11
REG_MULTI_LED_CTRL2 con $12

;----------------------- TEMP REGISTER -------------------------
REG_TEMP_INTR con $1F
REG_TEMP_FRAC con $20
REG_TEMP_CONFIG con $21
REG_PROX_INT_THRESH con $30

;----------------------- PART ID REGISTER ----------------------
REG_REV_ID con $FE
REG_PART_ID con $FF

richard
- 17th August 2020, 10:42
LATC.1 for Data
LATC.2 for Clock


a recipe for failure in the making ?
use of LATx ports in "highlevel" commands is incorrect and will not work as expected




Is this the correct way to assign a constant value to the name?

a quick little exercise trying to compile this will reveal the differences between defines and constants

reg var byte'#define bite_me 5 ;try one or the other of these two or niether
'bite_me con 5 ; "


REG_INTR_STATUS_1 con $00
REG_INTR_STATUS_2 con $01
REG_INTR_STATUS_2 con $07
REG_INTR_STATUS_3 con bite_me


define REG_INTR_ENABLE_1 $02
define REG_INTR_ENABLE_2 bite_me
define REG_INTR_ENABLE_2 1


#define REG_INTR_ENABLE_3 $02
#define REG_INTR_ENABLE_4 bite_me
#define REG_INTR_ENABLE_4 1


loopy:


reg = bite_me
reg = REG_INTR_ENABLE_3
reg = REG_INTR_ENABLE_1
reg = REG_INTR_STATUS_1


goto loopy

astanapane
- 17th August 2020, 13:50
Thanks for the reply.

do you mean that i cannot use the following format?


DataPin var LATC.1
ClockPin var LATC.2

richard
- 17th August 2020, 15:03
if that will become

I2CREAD DataPin, ClockPin, Control,{Address,}[Var{,Var...}]{,Label}

then yes that is not allowable and will cause difficult to determine bugs.
you have to remember pbp is ancient technology, latx registers did not exist when it was created
it never gets any significant updates

astanapane
- 17th August 2020, 22:09
It is a bit consfusing for me.

But i will try to figure it out.

My goal is to communicate with this heart sensor. First step is to send this command to light up the LED and then try to read the information needed.

I have tried this I2C ping program http://www.picbasic.co.uk/forum/showthread.php?t=20185, in order to see if the sensor responds with the ACK. (i though that the sensor was dead)

richard
- 18th August 2020, 01:29
this i2c sniffer will usually do the job




DEFINE I2C_SLOW 1
anselA=0 'dig i/o
ANSELC=0
TRISC= %11111111
TRISA= %11111110


SCL var Portc.1 ' I2C Clock PortA.3
SDA var Portc.0 ' I2C Data PortA.2
I VAR BYTE
addr var byte
rd var byte

porta.0=1
DEFINE DEBUG_REG PORTA
DEFINE DEBUG_BIT 0 ; if not used for pwr
DEFINE DEBUG_BAUD 38400
DEFINE DEBUG_MODE 0
pause 2000
Debug "Start",13 ,10
addr=0


for I = $02 to $fe step 2
I2Cread SDA,SCL,i,addr,[rd] ; or I2Cread SDA,SCL,i,[rd] ;
Debug "found addr ",#i , 13,10
nak:
Next I
end

astanapane
- 20th August 2020, 20:05
thanks for the book.

on the other hand, i would like to continue with this project as it is important for me and the kids in school.

I have done the first attempt to communicate with the sensor but no luck.

Based on the Datasheet MAX30102, device address, or slave id: %1010111, consists of 7 bit, B7-B1,and the MSB-7 is transmitted first followed by the remaining bits. B0 is LSB which can be 0 if we write or 1 if we read.

Then again from the manual explains that for the first write operation, we send the slave id as the first byte!....followed by the register address byte and then one or more data.

From the PBP manual, and from this link http://www.picbasic.co.uk/forum/content.php?r=165-Serial-EEPROM-Part-1

It is clear that I2CWRITE and I2CREAD command takes care of the write or read bit, also for the ACK.

8924

Now i have tried to send the following:

if PBP and command I2CWRITE takes care of the 8th bit then how could i manage to correspond it in the code.




i2cwrite sda,scl,%1010111,$04,error ' this is slave id cosnist of 7 bits.



If i send the above command nothing will happen, and the data line will be offset from the clock.

8925

Now using the following code:


i2cwrite sda,scl,%10101110,$04,error

i get this on the scope:

8926

then i tried the following:


i2cwrite sda,scl,%10101110,$04,error
pause 100
i2cwrite sda,scl,%10101110,$04,error

and the result on the scope: it looks strange again.

8927

richard
- 21st August 2020, 01:03
did you try my i2c sniffer ?


i2cwrite sda,scl,%10101110,$04,error

how can that work ? you are attempting to write to the FIFO_WR_PTR yet providing no data to be written .

i2cwrite sda,scl,$ae,$04,[1],error

in general code snippets are a waste of time
what pins , how are they set
a schematic

astanapane
- 21st August 2020, 10:51
Richard i havent used your sniffer code. Will do today it today.

The breadboarding....i think is clear as schematic, if you like schematic i could also make one.

8928

SCL is set as input in TRISC


TRISC = %10000100 ' portc.7 and portc.2 set as input.

all other unused ports are configured as output and set to low.

SDA var portc.1
SCL var portc.2

Now regarding the FIFO_WR_PTR it is not clear to me in the manual what data to write to it.

8929

for example for the REG_MODE_CONFIG which is $09

8930

there is a detailed info of the bits need to be set.

8931

For someone that is the first time working with a peripheral or a new method, it should be so many years a detailed info for configuration. At least a step by step guide.

I'm not talking about you, but for all the others were in this forum for so many years. There were few guys were really helping. We lost a chief who was really made detailed explanations like talking to a baby.

If we need to keep PBP alive, we need to start from 0. Here is not a plave to learn from skratch, and i agree. But if anyone loves PBP, we need to do that. Attract newbies like me, that would like to learn.

Anyway, i will get in focus on this project, because it matters to me. If anyone would like to get involved and have in house a MAX30102, i would be really happy if could help me out as well.

Richard i understand that you have no time and you have really other things to focus. Really appreciate your help, and i think you are one of the persons help in here.

Many thanks.

richard
- 21st August 2020, 11:12
The breadboarding....i think is clear as schematic, if you like schematic i could also make one

not really , i looked at 3 modules the mikroe one is 3.3v only .the one mouser/e14 sell is 3.3 or 5v and has a logic level converter on board
another provides no schematic and only vague spec no schema, i cannot identify your module

by the look of your cro traces you are belting 5v in on the scl,sda pins , better hope its 5v tolerant


TRISC = %10000100 ' portc.7 and portc.2 set as input.
is wrong both must be input , the routine will switch tris as req
if the module have 3.3 v pull ups might be better to use the open collector/drain method for i2c command
[edit] forget that ,its open collecter by default.



all other unused ports are configured as output and set to low. unnecessary



SDA var portc.1
SCL var portc.2
conflicts with your tris statement anyway



Now regarding the FIFO_WR_PTR it is not clear to me in the manual what data to write to it.

me either but it will do something if everything else is correct

richard
- 21st August 2020, 11:47
For someone that is the first time working with a peripheral or a new method, it should be so many years a detailed info for configuration. At least a step by step guide.


I'm not talking about you, but for all the others were in this forum for so many years. There were few guys were really helping. We lost a chief who was really made detailed explanations like talking to a baby.


If we need to keep PBP alive, we need to start from 0. Here is not a plave to learn from skratch, and i agree. But if anyone loves PBP, we need to do that. Attract newbies like me, that would like to learn


if you are familiar with i2c protocol and the vagaries of the device you have studied and intend to use then the pbp manual
tells you everything you need to know to communicate with your device with pbp's i2c commands.
the pbp manual is not there to teach i2c protocol to a novice , its raison d'etre is to explain usage of pbp implementation.

having said that you can always ask questions on forums but really the onus is on you to do the ground work. its your project.
its a pretty tall order to expect a complete novice tutorial for every device you can pluck out of the air from unpaid volunteers
there are still few here willing to have a go at helping but don't forget dt was paid support , that resource has never been replaced.
leo will usually help but he has not ventured onto this forum for years and years afaik.

astanapane
- 21st August 2020, 12:55
Changed to input both portc.1 and portc.2

This is the module.

https://reedpaper.wordpress.com/2018/08/22/pulse-oximeter-max30100-max30102-how-to-fix-wrong-board/

I have also following the "fix". Well module is 3.3V but i have never seen in all of the Arduino connections any note.

I have changed the connections and used the 3.3V output from the Display to power the rest of the circuit.

I dont beleive that i have burned the module as this is the result before i change the voltage supply from 5V to 3.3V.


i2cwrite sda,scl,$AE,$04
pause 100

This is the proof of ACK in 9th bit. As from the data sheet if the Id is the right one, the module pulls down the SDA during 9th bit to acknowledge receipt of data.

8932

richard
- 21st August 2020, 13:22
i2cwrite sda,scl,$AE,$04

i see you're still torturing the device sending incomplete write resister i2c control messages at it

astanapane
- 21st August 2020, 15:16
yes i havent send any data yet as i'm searching in the i2c how to do that corresponds to the max30102 manual.

At least i know sensor is responded by pulling SDA down on 9th bit. This is something for me.

astanapane
- 21st August 2020, 21:46
i've done many tries. I just want to see if the red LED inside the sensor is alive. At least by sending a command to activate it.




This is as from the pseudo code the first step. I really do not know if that is the right way to send Device ID + write mode
Then send the FIFO_WR_PTR (i still dont know what are the bits needs to be configured)
REPEAT START

addr = $04 ; FIFO WRITE POINTER REG ADDR IS 0X04
i2cwrite SDA,scl,contwr
i2cwrite SDA,scl,contwr,addr
i2cwrite SDA,scl,contwr
i2cwrite SDA,scl,contwr,addr

Apart from pseudo code in the datasheet, Next i tried to send mode for the heart rate mode only

addr = $09 ' mode configuration
i2cwrite SDA,scl,contwr,addr,[modled],error ' ModLED = $02 as per data sheet heart rate mode only

In the end i tried to send the led pulse amplitute.

addr = $0C ' led pulse amplitute
i2cwrite SDA,scl,contwr,addr,[led1_pa],error' LED1_PA = $3F , 12.6 mA


no luck......keep walking.

richard
- 3rd September 2020, 10:44
given that you can buy these for < $20us delivered to your door i doubt its worth while making them other than as an intellectual exercise
https://au.banggood.com/BOXYM-oFit-2-Finger-Clamp-Pulse-Oximeter-Finger-Blood-Oxygen-Saturometro-Heart-De-Oximeter-Portable-Pulse-Oximetro-Monitor-p-1530616.html?rmmds=flashdeals&amp;cur_warehouse=CN
i have tried the sparkfun arduino demos and was not particularly impressed with accuracy or stability of readings obtained
{maybe my heart is inaccurate and unstable}
the raspberrypi code goes about it quite differently&nbsp; with some dsp done on data collected in a fixed sampling period [not tried it].
i have no experience with these things , i don't know if reverse engineering the arduino code is even worthwhile
but no matter how you go about it the sort of digital signal processing needed will be lots and lots of hard work with pbp
do you have a plan ? have you established optimal device settings to get the best readings ? how much data is needed and from where ?
reading and writing to the device is child's play, setting it up to get meaningful data and being able to extract information from that data is another story

Ioannis
- 3rd September 2020, 15:33
I double what Richard pointed.

Ioannis

astanapane
- 4th September 2020, 11:01
Thanks both for your kind reply.

I will find the way. I know it is quite difficult and options are limited to use Arduino.

richard
- 4th September 2020, 11:21
i tried the arduino code again with the sensor attached to my finger with a rubber band , it works a bit better that way
do you have a stl model for an enclosure ? . its difficult to make any sense of wildly unstable readings

i'm thinking a ESP8285 M3 board would make a better platform and allow use of arduino libs if they can be proven to be adequate
ESP8285's these cut down esp8266's are very cheap and still have more processing power than a bucket full of pic chips they are quite happy to run on two aaa batteries and consume little energy if wifi not engaged

astanapane
- 4th September 2020, 11:32
Hi Richard,

once i have all the components (including the arduino board or ESP8285) then i will be ready to design the 3D cad model and printed.

I will order some arduino and ESP and check them out.

Ioannis
- 4th September 2020, 13:07
Use of such finger sensors need to have your finger cleaned from oil with alcohol (isoprop. alcohol or similar).

It is necessary for the sensors to keep working with consistency.

Ioannis

richard
- 4th September 2020, 13:21
thanks ioannis, with out the repeatable test conditions its all guess work

i might see if i can print this
https://www.thingiverse.com/thing:4395147

Ioannis
- 4th September 2020, 14:49
Interesting 3D print!

Ioannis

astanapane
- 9th January 2022, 13:20
I will try once again to find out how could i make this sensor work.

Anyone is interested of helping in creating a simple driver for this sensor, please any help is much appreciated.

At the moment i have changed the diagram. For being aligned with the following code please check the test board as well.

9132

Up to now the componets im using:

1. PIC18F26K22
2. A Crystal 16Mhz
3. 2 capacitors 22p
4. 2 capacitors 100n
5. 3 LEDs, RED, ORANGE, BLUE
6. couple of resistors, 4.7K, 100Ohm, 220Ohm
7. An LCD screen from 4D Systems
8. MAX30102 Heart Rate Sensor

I have managed with the following code to understand and complete easy tasks like:

1. to read the PART NUMBER ID of the sensor based on the manual.
2. How to light up the LEDs (IR and RED LEDs) Cant really understand how to send the right command for controlling the current)





'************************************************* ********************************
@ ERRORLEVEL -306 ; this command prevents the compiler to give you a notice of *
; crossing page boundary - make sure bits are set *
'************************************************* ********************************

#CONFIG ; The PBP configuration for the PIC18F26K22 is:

CONFIG FOSC = HSHP ; HS oscillator (high power &gt; 16 MHz)
CONFIG PLLCFG = ON ;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

;*---------------------------------------------------------------------------------------------------------|
;*---------------------------------------------------------------------------------------------------------|
'define I2C_SLOW 1
define OSC 64

INCLUDE "modedefs.bas"
INCLUDE "ALLDIGITAL.pbp"

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

TRISA = %00000000
TRISB = %00000000
TRISC = %10011000

'----------------------- At start all PORTS LOW -------------------------|
'------------------------------------------------------------------------|
PORTA = 0 'make low all ports at A range |
PORTB = 0 'make low all ports at B range |
PORTC = 0 'make low all ports at C range |
PORTE = 0 'make low all ports at E range |
'------------------------------------------------------------------------|

'-------------------------- COMPARATORS OFF -----------------------------|
'------------------------------------------------------------------------|
CM1CON0.7 = 0 'Disable comparator1 |
CM2CON0.7 = 0 'Disable comparator2 |
'------------------------------------------------------------------------|

'*----------------------- | EUART 1 Configuration | --------------------------|

DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 160 ' 38400 Baud @ 64MHz, -0.08%
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
'*-----------------------------------------------------------------------------|

ORANGE var LATA.2 ' ORANGE LED TO INDICATE POWER ON OF THE PIC
BLUE var LATC.0 ' BLUE LED TO INDICATE SENSOR WOKRING CONDITION
RED VAR LATC.5 ' RED LED TO INDICATE ERROR
SDA var portc.4 ' DATA PIN
SCL VAR portc.3 ' CLOCK PIN

;-------------------------------------------------------------|
;------------------------ max30102 registers------------------|
;-------------------------------------------------------------|
Timeout cON 2000
i var byte
mydata var BYTE
fifowr var byte
fiford var byte
fifoD1 var byte
fifoD2 var byte
fifoD3 var byte

ContWR var byte ; $AE or b10101110
ContRD var byte ; $AF or b10101111
ContRD = $AF ; b10101111
ContWR = $AE ; b10101110
addr var byte ; this must be defined as variable word

'I2C_ID_ADDR con $57 ; 7-bit version of the above
'define I2C_ID_ADDR 57h ; 7-bit version of the above

;//register addresses
;---------------------- STATUS REGISTER -----------------------
REG_INTR_STATUS_1 var byte ' con $00
REG_INTR_STATUS_2 var byte ' con $01

REG_INTR_ENABLE_1 var byte ' con $02
REG_INTR_ENABLE_2 var byte 'con $03

;----------------------- FIFO REGISTERS -----------------------
REG_FIFO_WR_PTR var byte 'con $04 ; FIFO WRITE POINTER REG ADDR IS 0X04
REG_OVF_COUNTER var byte 'con $05
REG_FIFO_RD_PTR var byte 'con $06
REG_FIFO_DATA var byte 'con $07

;---------------------- CONFIG REGISTER -----------------------
REG_FIFO_CONFIG var byte 'con $08
REG_MODE_CONFIG var byte 'con $09
REG_SPO2_CONFIG var byte 'con $0A
REG_LED1_PA var byte 'con $0C
REG_LED2_PA var byte 'con $0D


REG_PILOT_PA var byte 'con $10
REG_MULTI_LED_CTRL1 var byte 'con $11
REG_MULTI_LED_CTRL2 var byte 'con $12

;----------------------- TEMP REGISTER -------------------------
REG_TEMP_INTR var byte 'con $1F
REG_TEMP_FRAC var byte 'con $20
REG_TEMP_CONFIG var byte 'con $21
REG_PROX_INT_THRESH var byte 'con $30

;----------------------- PART ID REGISTER ----------------------
REG_PART_ID var byte
' -----------------------------------------------------------------------------|
' indication of the working code

for i= 0 to 3
orange = 1
pause 100
orange = 0
pause 50
next i
orange = 1

init:
low blue
low red
pause 100

REG_PART_ID = $FF ' this is the part id reg adress which gives hex 15
i2cread SDA,scl,contrd,REG_PART_ID,[mydata],error

'************************************************* ******************************
' if %00000010 is used then only IR LED for heart rate mode is set *
' if %00000011 is used then only RED & IR LED for SPO2 mode is set *
' if %00000111 is used then Multi Led Mode is set *
'************************************************* ******************************

addr = $09 ' MODE CONFIGURATION
i2cwrite SDA,scl,contwr,addr,%00000010,error ' ModLED = $02 as per data sheet heart rate mode, $03 SpO2 mode
'pause 100

addr = $0C 'LED PULSE AMLIDUDE FOR for LED1_PA[7:0] 0x0C
i2cwrite SDA,scl,Contwr,addr,$3f,error' if LED1_PA = $3F , 12.6 mA , ($7F is 25.4 mA)
'pause 100

'************************************************* *******************************

REG_FIFO_DATA = $07
i2cread sda,scl,contrd,REG_FIFO_DATA,[fifod1,fifod2,fifod3],error
'************************************************* *******************************

start:
HSEROUT [$73,$03,$04,$11,$ff,$ff,"PART ID: ",hex mydata,"h",$00]
Hserin timeout,error,[wait(6)]

HSEROUT [$73,$05,$0A,$11,$07,$E0,"FIFO DT: ",$00] ' lime color $07,$E0
Hserin timeout,error,[wait(6)]

HSEROUT [$73,$03,$0C,$11,$07,$E0,dec fifod1,dec fifod2,dec fifod3,$00] ' lime color $07,$E0
Hserin timeout,error,[wait(6)]
'goto start

blueled:
high blue
PAUSE 300
goto init

error:
high RED
pause 100
end

richard
- 10th January 2022, 23:56
it looks like there is not much interest in this project

there are a number of issues i can see, starting at the top with the physicals

1, why a xtal osc, getting a stable xtal osc to work on a bread board is not guaranteed.
xtal pins are often too thin for reliable connection and the capacitive coupling between strips is high.
the internal osc is easily stable enough for this project and so much simpler.
2, you provide nil detail on the display specs, the 3.3 v supply from lcd is it stable and capable of suppling the project ?

astanapane
- 11th January 2022, 08:35
Thanks for the reply.

I will give it a try as i would like to make this sensor work. I just need some guidance if possible.

1. I will remove the external crystal, less part the better, in case for this project the internal is stable enough.
2. The 4D Systems display, is an easy display to use, as they have on board chip and loaded a ready made driver for ease of use. I have this display couple of years now and it works as expected.

You can find the datashet of the display here (https://4dsystems.com.au/mwdownloads/download/link/id/261/) and the SERIAL COMMAND REFERENCE (https://4dsystems.com.au/mwdownloads/download/link/id/145/)

Regarding the output 3.3V from the display, looks like it is quite stable as i measured it. The Sensor's Power Supply: 3.3V~5V, so i could also use 5V.

richard
- 11th January 2022, 10:20
There is your first major hurdle

9133

each led in 30102 can draw 50ma the two leds on your board 20ma each , the pic chip maybe another 20ma.
at least use a 470uF smoothing cap but another 3.3v power source would be better

the led can be lit
https://youtu.be/wYIggIHlJTI

richard
- 11th January 2022, 10:35
mine has 3.3v written on it but the data sheet says the led supply can be 6v and the other sda scl pins are 6v tolerant
maybe mine has a cheap 1.8v regulator , it works fine at 3.3v

astanapane
- 11th January 2022, 17:16
well noted,

1. Changed the crystal and used only the interal


CONFIG FOSC = INTIO67 ; Internal oscillator block

2. i've changed the circuit from 3.3V which was the output of the Display, and supplied the circuit with the PICKIT2 5V. I checked that this specific circuit draws up to 80mA. (USB 2 can deliver 100mA and at its maximum 500mA)

MAX30102 can be supplied with 5V as from the following link. (https://reedpaper.wordpress.com/2018/08/22/pulse-oximeter-max30100-max30102-how-to-fix-wrong-board/)

9135

I have also applied the fix they have from the given link, and measured the output voltage at 1.8V which goes to the MAX30102 chip on the tiny board. So everything looks good now.

9136

9137

9134

astanapane
- 11th January 2022, 19:57
up to now this is the code and the output.....



'************************************************* ********************************
@ ERRORLEVEL -306 ; this command prevents the compiler to give you a notice of *
; crossing page boundary - make sure bits are set *
'************************************************* ********************************

#CONFIG ; The PBP configuration for the PIC18F26K22 is:

CONFIG FOSC = INTIO67 ; Internal oscillator block
CONFIG PLLCFG = ON ; 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

;*---------------------------------------------------------------------------------------------------------|
;*---------------------------------------------------------------------------------------------------------|
'define I2C_SLOW 1
define OSC 64

INCLUDE "modedefs.bas"
INCLUDE "ALLDIGITAL.pbp"

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

TRISA = %00000000
TRISB = %00000000
TRISC = %10011000

'----------------------- At start all PORTS LOW -------------------------|
'------------------------------------------------------------------------|
PORTA = 0 'make low all ports at A range |
PORTB = 0 'make low all ports at B range |
PORTC = 0 'make low all ports at C range |
PORTE = 0 'make low all ports at E range |
'------------------------------------------------------------------------|

'-------------------------- COMPARATORS OFF -----------------------------|
'------------------------------------------------------------------------|
CM1CON0.7 = 0 'Disable comparator1 |
CM2CON0.7 = 0 'Disable comparator2 |
'------------------------------------------------------------------------|

'*----------------------- | EUART 1 Configuration | --------------------------|

DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 160 ' 38400 Baud @ 64MHz, -0.08%
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
'*-----------------------------------------------------------------------------|

ORANGE var LATA.2 ' ORANGE LED TO INDICATE POWER ON OF THE PIC
GREEN var LATC.0 ' BLUE LED TO INDICATE SENSOR WOKRING CONDITION
RED VAR LATC.5 ' RED LED TO INDICATE ERROR
SDA var portc.4 ' DATA PIN
SCL VAR portc.3 ' CLOCK PIN

;-------------------------------------------------------------|
;---------------------- variables ------------------|
;-------------------------------------------------------------|
Timeout cON 2000
i var byte
mydata var BYTE
fifowr var byte
fiford var byte
fifoD1 var byte
fifoD2 var byte
fifoD3 var byte

ContWR var byte ; $AE or b10101110
ContRD var byte ; $AF or b10101111
ContRD = $AF ; b10101111
ContWR = $AE ; b10101110
addr var byte ; this must be defined as variable word

'I2C_ID_ADDR con $57 ; 7-bit version of the above
'define I2C_ID_ADDR 57h ; 7-bit version of the above

;//register addresses
;---------------------- STATUS REGISTER -----------------------
REG_INTR_STATUS_1 var byte 'con $00
REG_INTR_STATUS_2 var byte 'con $01

REG_INTR_ENABLE_1 var byte 'con $02
REG_INTR_ENABLE_2 var byte 'con $03

;----------------------- FIFO REGISTERS -----------------------
REG_FIFO_WR_PTR var byte 'con $04 ; FIFO WRITE POINTER REG ADDR IS 0X04
REG_OVF_COUNTER var byte 'con $05
REG_FIFO_RD_PTR var byte 'con $06
REG_FIFO_DATA var byte 'con $07

;---------------------- CONFIG REGISTER -----------------------
REG_FIFO_CONFIG var byte 'con $08
REG_MODE_CONFIG var byte 'con $09
REG_SPO2_CONFIG var byte 'con $0A
REG_LED1_PA var byte 'con $0C
REG_LED2_PA var byte 'con $0D


REG_PILOT_PA var byte 'con $10
REG_MULTI_LED_CTRL1 var byte 'con $11
REG_MULTI_LED_CTRL2 var byte 'con $12

;----------------------- TEMP REGISTER -------------------------
REG_TEMP_INTR var byte 'con $1F
REG_TEMP_FRAC var byte 'con $20
REG_TEMP_CONFIG var byte 'con $21
REG_PROX_INT_THRESH var byte 'con $30

;----------------------- PART ID REGISTER ----------------------
REG_PART_ID var byte
' -----------------------------------------------------------------------------|
' -----------------------------------------------------------------------------|
' [ LCD Initialization ] |
'------------------------------------------------------------------------------|
' FIRST TIME BOOTUP: We give plenty of time for tbe LCD '

pause 1000
HSEROUT[$55] ' uOLED Initialize this is the 'U' character of autoband rate to LCD
Hserin timeout,error,[wait(6)]
HSEROUT [$45] ' clear the LCD
Hserin timeout,error,[wait(6)]
hserout[$73,$00,$03,$11,$ff,$ff,"HEART RATE SENSOR",$00]
Hserin timeout,error,[wait(6)]
Hserout[$4F,$01] ; Set Transparent-Opaque Text - 4Fhex
Hserin timeout,error,[wait(6)]
pause 500
HSEROUT [$45] ' clear the LCD
Hserin timeout,error,[wait(6)]

' indication of the working code

for i= 0 to 3
orange = 1
pause 100
orange = 0
pause 50
next i
orange = 1

init:
low green
low red
pause 100

REG_PART_ID = $FF ' this is the part id reg adress which gives hex 15
i2cread SDA,scl,contrd,REG_PART_ID,[mydata],error

'************************************************* ******************************
' if %00000010 is used then only IR LED for heart rate mode is set *
' if %00000011 is used then only RED & IR LED for SPO2 mode is set *
' if %00000111 is used then Multi Led Mode is set *
'************************************************* ******************************

addr = $09 ' MODE CONFIGURATION
i2cwrite SDA,scl,contwr,addr,%00000010,error ' ModLED = $02 as per data sheet heart rate mode, $03 SpO2 mode
'pause 100

addr = $0C 'LED PULSE AMLIDUDE FOR for LED1_PA[7:0] 0x0C
i2cwrite SDA,scl,Contwr,addr,$3f,error' if LED1_PA = $3F , 12.6 mA , ($7F is 25.4 mA)
'pause 100
'************************************************* *******************************
REG_FIFO_WR_PTR = $04 ; FIFO WRITE POINTER REG ADDR IS 0X04
i2cwrite SDA,scl,contrd,REG_FIFO_WR_PTR,[fifowr],error

REG_FIFO_RD_PTR = $06 ; FIFO READ POINTER REG ADDR IS 0X06
i2cread SDA,scl,contrd,REG_FIFO_rd_PTR,[fiford],error

REG_FIFO_DATA = $07
i2cread sda,scl,contrd,REG_FIFO_DATA,[fifod1,fifod2,fifod3],error
'************************************************* *******************************

start:
HSEROUT [$73,$03,$04,$11,$ff,$ff,"PART ID: ",hex mydata,"h",$00]
Hserin timeout,error,[wait(6)]

HSEROUT [$73,$05,$0A,$11,$07,$E0,"FIFO DT: ",$00] ' lime color $07,$E0
Hserin timeout,error,[wait(6)]

HSEROUT [$73,$04,$0C,$11,$F8,$1F,dec fifod1,dec fifod2,dec fifod3,$00] ' fuchsia color $F8, $1F
Hserin timeout,error,[wait(6)]
'goto start

blueled:
high green
PAUSE 300
goto init

error:
high RED
pause 100
end

9140

9139

astanapane
- 11th January 2022, 20:34
Ok the FIFODT, FIFO DATA is not the one we are looking for.

Just tried to see what could i get by requesting it using the following code. Also the code is not right but that is why i would like your help in here.


REG_FIFO_DATA = $07
i2cread sda,scl,contrd,REG_FIFO_DATA,[fifod1,fifod2,fifod3],error

HSEROUT [$73,$04,$0C,$11,$F8,$1F,dec fifod1,dec fifod2,dec fifod3,$00] ' fuchsia color $F8, $1F
Hserin timeout,error,[wait(6)]


First i would like to retrieve the raw data of the IR LED.

richard
- 11th January 2022, 22:19
my code , you should know i have no idea :-
1, what led pwr to use
2, what led pwidth to use
3, what sample rate to use
4, what sample resolution to use
5, if there are any fifo overflows and if it matters much
6, what led the readings represents
7, how to interpret the data
8, how much data to collect [i assume at least 2 seconds worth]

when graphed i see no regular pattern that could be a heart beat
there is a difference between finger and no finger pattern





'************************************************* ***************'* Name : MAX30102 FOR PIC 18 test *
'* 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


'define I2C_SLOW 1
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


#DEFINE DEBUGING 1


#IFNDEF DEBUGING
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 160 ' 38400 Baud @ 64MHz, -0.08%
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
#ELSE
DEFINE DEBUG_REG PORTB
DEFINE DEBUG_BIT 7
DEFINE DEBUG_BAUD 9600
DEFINE DEBUG_MODE 0
ANSELB=0
LATB.7=1
#ENDIF
SDA var portc.4 ' DATA PIN
SCL VAR portc.3 ' CLOCK PIN


Timeout cON 2000
reg var BYTE
fifo var byte[6]
addr var byte
REVISION var byte
PARTID var byte
buffer var byte[1152]
ptr var byte
btr var byte
index var word
;----------------------- 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]
STATUS1 con 0 ;A_FULL[7]


' -----------------------------------------------------------------------------|


#IFDEF DEBUGING
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,[$30]
reg = REG_LED2_PA
i2cwrite SDA,scl,ADDR,reg,[2]
reg = REG_LED1_PA
i2cwrite SDA,scl,ADDR,reg,[2]
reg = FIFO_CONFIG
i2cwrite SDA,scl,ADDR,reg,[16]

#IFNDEF DEBUGING
HSEROUT [$73,$03,$04,$11,$ff,$ff,"PART ID: 0X",hex PARTID,"/0X", hex REVISION,$00]
Hserin timeout,error,[wait(6)]
#ELSE
DEBUG 13,10, "PART ID: 0X",hex PARTID,"/0X",hex REVISION
#ENDIF


'************************************************* *******************************
start:
reg= REG_TEMP_CONFIG
i2cwrite SDA,scl,ADDR,reg,[1]
reg= REG_TEMP_INT
i2cread sda,scl,ADDR,reg,[fifo[0],fifo[1]]
#IFNDEF DEBUGING
HSEROUT [$73,$05,$0A,$11,$07,$E0,"TEMP: ",DEC fifo[0],".",DEC2( fifo[1]<<6),$00]
Hserin timeout,error,[wait(6)]
#ELSE
DEBUG 13,10, "TEMP: ",DEC fifo[0],".",DEC2( fifo[1]<<6)
#ENDIF
GOSUB AQUIRE
pause 5000
goto start


aquire:
reg= FIFO_WR_PTR
i2cwrite SDA,scl,ADDR,reg,[0]
reg= FIFO_RD_PTR
i2cwrite SDA,scl,ADDR,reg,[0]

FOR index = 0 TO 1151 STEP 0

btr =0
reg= STATUS1
WHILE btr&$80=0
i2cread sda,scl,ADDR,reg,[btr]

WEND
reg= FIFO_RD_PTR
i2cread SDA,scl,ADDR,reg,[btr]
btr = (btr- ptr) & 31
WHILE btr
reg = FIFO_DATA
i2cread sda,scl,ADDR,reg,[buffer[index],buffer[index+1],buffer[index+0],buffer[index+5],buffer[index+4],buffer[index+3]]
index=index+6
btr=btr-1
ptr = (Ptr+1) & 31
WEND
pause 200 ; no sure about this either
NEXT
FOR index = 0 TO 1151 STEP 0
DEBUG 13,10,HEX2 buffer[index+0],HEX2 buffer[index+1],9,HEX2 buffer[index+3],HEX2 buffer[index+4]
index=index+6
NEXT
RETURN

astanapane
- 11th January 2022, 23:05
thanks Richard,

i will read carefully your code and will come back because i have couple of questions.

In the begging i got confused the way you use addr and reg names inside the I2Cread or write command.



ADDR = $ae ; addr is 0x57 << 1
reg= REG_PART_REVISION
i2cread SDA,scl,ADDR,reg,[REVISION,PARTID]


I followed the manual example: so i wont get consfused.



I2CWRITE PORTA.0,PORTA.1,cont,addr,[B2]

richard
- 11th January 2022, 23:11
I followed the manual example: so i wont get consfused.

not really

addr is the i2c device address , reg is the register you are accessing. that example you refer is for an eeprom
where those words make a little sense, in this context they are totally misleading

astanapane
- 11th January 2022, 23:40
not really

addr is the i2c device address , reg is the register you are accessing. that example you refer is for an eeprom
where those words make a little sense, in this context they are totally misleading

once again you are right. I will change the names in the code as well.

One more question. What is the difference on the following?

With the [] and without them. I noticed that i get completely different results. The ones with the [] looks like is the right one as once i place them, i can control the values of the LED's pulse amplidute. Now it is visible the difference.



addr = $09 ' MODE CONFIGURATION
i2cwrite SDA,scl,contwr,addr,[%00000011],error ' ModLED = $02 as per data sheet heart rate mode, $03 SpO2 mode
pause 10

addr = $0A ' SpO2 mode configuration
i2cwrite SDA,scl,contwr,addr,[%01111101],error ' check the manual for this configuration
pause 100

addr = $0C 'LED PULSE AMLIDUDE FOR for LED1_PA[7:0] 0x0C
i2cwrite SDA,scl,Contwr,addr,[$03],error' if LED1_PA = $3F , 12.6 mA , ($7F is 25.4 mA)
pause 10

addr = $0D 'LED PULSE AMLIDUDE FOR LED2_PA[7:0] 0x0D
i2cwrite SDA,scl,contwr,addr,[$03],error ' if LED2_PA = $1F , 6.2 mA , ($7F is 25.4 mA)
pause 10


and the following:



addr = $09 ' MODE CONFIGURATION
i2cwrite SDA,scl,contwr,addr,%00000011,error ' ModLED = $02 as per data sheet heart rate mode, $03 SpO2 mode
pause 10

addr = $0A ' SpO2 mode configuration
i2cwrite SDA,scl,contwr,addr,%01111101,error ' check the manual for this configuration
pause 100

addr = $0C 'LED PULSE AMLIDUDE FOR for LED1_PA[7:0] 0x0C
i2cwrite SDA,scl,Contwr,addr,$03,error' if LED1_PA = $3F , 12.6 mA , ($7F is 25.4 mA)
pause 10

addr = $0D 'LED PULSE AMLIDUDE FOR LED2_PA[7:0] 0x0D
i2cwrite SDA,scl,contwr,addr,$03,error ' if LED2_PA = $1F , 6.2 mA , ($7F is 25.4 mA)
pause 10

richard
- 11th January 2022, 23:47
no brackets means no data will be written

richard
- 11th January 2022, 23:50
fifo read was stuffed up


reg = FIFO_DATA
i2cread sda,scl,ADDR,reg,[buffer[index+2],buffer[index+1],buffer[index+0],buffer[index+5],buffer[index+4],buffer[index+3]]



the fifo print out is incorrect also , not yet solved

astanapane
- 12th January 2022, 00:17
my code , you should know i have no idea :-
1, what led pwr to use
2, what led pwidth to use
3, what sample rate to use
4, what sample resolution to use
5, if there are any fifo overflows and if it matters much
6, what led the readings represents
7, how to interpret the data
8, how much data to collect [i assume at least 2 seconds worth]


most of the drivers i've seen over the net are using the following...


1, what led pwr to use : 0x1F about 6.2 mA
2, what led pwidth to use : 411 which is 18 ADC BIT RESOLUTION based on the manual.
3, what sample rate to use : sample rate per second 100

a configuration should also include: sampleAverage = 8

4, what sample resolution to use :
5, if there are any fifo overflows and if it matters much.
6, what led the readings represents. I will search and find out.
7, how to interpret the data....
8, how much data to collect [i assume at least 2 seconds worth]

richard
- 12th January 2022, 03:31
this looks like something, probably needs a lpf and sum of the squares function to remove dc drift


'************************************************* ***************'* 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


'define I2C_SLOW 1
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


#DEFINE DEBUGING 1


#IFNDEF DEBUGING
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 160 ' 38400 Baud @ 64MHz, -0.08%
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
#ELSE
DEFINE DEBUG_REG PORTB
DEFINE DEBUG_BIT 7
DEFINE DEBUG_BAUD 9600
DEFINE DEBUG_MODE 0
ANSELB=0
LATB.7=1
#ENDIF
SDA var portc.4 ' DATA PIN
SCL VAR portc.3 ' CLOCK PIN


Timeout cON 2000
reg var BYTE
fifo var byte[6]
addr var byte
REVISION var byte
PARTID var byte
buffer var byte[1152]
ptr var byte
btr var byte
index var word
result var word ext
@result = _fifo
;----------------------- 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]
STATUS1 con 0 ;A_FULL[7]


' -----------------------------------------------------------------------------|


#IFDEF DEBUGING
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,[9]
reg = REG_LED1_PA
i2cwrite SDA,scl,ADDR,reg,[9]
reg = FIFO_CONFIG
i2cwrite SDA,scl,ADDR,reg,[16]

#IFNDEF DEBUGING
HSEROUT [$73,$03,$04,$11,$ff,$ff,"PART ID: 0X",hex PARTID,"/0X", hex REVISION,$00]
Hserin timeout,error,[wait(6)]
#ELSE
DEBUG 13,10, "PART ID: 0X",hex PARTID,"/0X",hex REVISION
#ENDIF


'************************************************* *******************************
start:
reg= REG_TEMP_CONFIG
i2cwrite SDA,scl,ADDR,reg,[1]
reg= REG_TEMP_INT
i2cread sda,scl,ADDR,reg,[fifo[0],fifo[1]]
#IFNDEF DEBUGING
HSEROUT [$73,$05,$0A,$11,$07,$E0,"TEMP: ",DEC fifo[0],".",DEC2( fifo[1]<<6),$00]
Hserin timeout,error,[wait(6)]
#ELSE
DEBUG 13,10, "TEMP: ",DEC fifo[0],".",DEC2( fifo[1]<<6)
#ENDIF
GOSUB AQUIRE
pause 5000
goto start


aquire:
reg= FIFO_WR_PTR
i2cwrite SDA,scl,ADDR,reg,[0]
reg= FIFO_RD_PTR
i2cwrite SDA,scl,ADDR,reg,[0]
pause 200
FOR index = 0 TO 1151 STEP 0

btr = 0
reg = STATUS1
WHILE btr&$80 = 0
i2cread sda,scl,ADDR,reg,[btr]
WEND

reg = FIFO_RD_PTR
i2cread SDA,scl,ADDR,reg,[btr]
btr = (btr - ptr) & 31
WHILE btr
reg = FIFO_DATA
i2cread sda,scl,ADDR,reg,[buffer[index + 2],buffer[index + 1],buffer[index + 0],buffer[index + 5],buffer[index + 4],buffer[index + 3]]
index = index + 6
btr = btr - 1
ptr = (Ptr + 1) & 31
WEND
pause 200
DEBUG "."
NEXT
FOR index = 0 TO 1151 STEP 0
for ptr = 0 to 2
fifo[ptr] = buffer[index + ptr]
next
DEBUG 13,10;, hex2 fifo[2] , hex2 fifo[1], hex2 fifo[0]
' asm ;ROTATE READING TO GET SUFFICIENT RESOLOUTION INTO UPPER WORD
' banksel _fifo
' 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
' banksel 0
' endasm
DEBUG 9,HEX4 result
index = index + 3
' DEBUG 13,10, hex2 buffer[index+2],hex2 buffer[index+1],hex2 buffer[index],9 ,HEX4 result
' DEBUG 9, hex2 fifo[2] , hex2 fifo[1], hex2 fifo[0]
for ptr = 0 to 2
fifo[ptr] = buffer[index + ptr]
next

' DEBUG 9, hex2 fifo[2] , hex2 fifo[1], hex2 fifo[0]
' asm ;ROTATE READING TO GET SUFFICIENT RESOLOUTION INTO UPPER WORD
' banksel _fifo
' 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
' banksel 0
' endasm
DEBUG 9,HEX4 result
index = index + 3
NEXT
RETURN

richard
- 12th January 2022, 09:38
its alive i think
9142
gives me a 63 rpm

astanapane
- 12th January 2022, 12:07
Richard,

looking at your code,i'm learning lots of things.

All the registers you have are constants. And that is what i've thought also in the beggining.


;----------------------- 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]
STATUS1 con 0 ;A_FULL[7]


' -----------------------------------------------------------------------------|

But in the manual it says that we never use constants for the I2C command.

What i have realised is that you refer to the



reg var byte


everytime you need to asign a constant value to it. Is that right? with that way we alter the restriction of using constants. (or am i wrong?)

Sencod question.

I see you are always using the Write Address:



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,[9]
reg = REG_LED1_PA
i2cwrite SDA,scl,ADDR,reg,[9]
reg = FIFO_CONFIG
i2cwrite SDA,scl,ADDR,reg,[16]


even if you want to read from:



reg= REG_TEMP_INT
i2cread sda,scl,ADDR,reg,[fifo[0],fifo[1]]


im confused here. When do we use the write address and when do we use the read address!!!

astanapane
- 12th January 2022, 12:12
its alive i think
9142
gives me a 63 rpm

wow, ok little by little because i need to undestand what you have done.

apart from that 63 bpm is quite ok and looks healthy.

richard
- 12th January 2022, 12:47
Low pass filter in excel

9143

makes peak detection look possible



'************************************************* ***************'* 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


'define I2C_SLOW 1
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


#DEFINE DEBUGING 1


#IFNDEF DEBUGING
DEFINE HSER_RCSTA 90h ' Enable serial port & continuous receive
DEFINE HSER_TXSTA 24h ' Enable transmit, BRGH = 1
DEFINE HSER_CLROERR 1 ' Clear overflow automatically
DEFINE HSER_SPBRG 160 ' 38400 Baud @ 64MHz, -0.08%
SPBRGH = 1
BAUDCON.3 = 1 ' Enable 16 bit baudrate generator
#ELSE
DEFINE DEBUG_REG PORTB
DEFINE DEBUG_BIT 7
DEFINE DEBUG_BAUD 9600
DEFINE DEBUG_MODE 0
ANSELB=0
LATB.7=1
#ENDIF
SDA var portc.4 ' DATA PIN
SCL VAR portc.3 ' CLOCK PIN


Timeout cON 2000
reg var BYTE
fifo var byte[6]
addr var byte
REVISION var byte
PARTID var byte
buffer var byte[1152]
ptr var byte
btr var byte
index var word
result var word ext
@result = _fifo
;----------------------- 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
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]
#IFNDEF DEBUGING
HSEROUT [$73,$03,$04,$11,$ff,$ff,"PART ID: 0X",hex PARTID,"/0X", hex REVISION,$00]
Hserin timeout,error,[wait(6)]
#ELSE
DEBUG 13,10, "PART ID: 0X",hex PARTID,"/0X",hex REVISION
#ENDIF


'************************************************* *******************************
start:
reg= REG_TEMP_CONFIG
i2cwrite SDA,scl,ADDR,reg,[1]
reg= REG_TEMP_INT
i2cread sda,scl,ADDR,reg,[fifo[0],fifo[1]]
#IFNDEF DEBUGING
HSEROUT [$73,$05,$0A,$11,$07,$E0,"TEMP: ",DEC fifo[0],".",DEC2( fifo[1]<<6),$00]
Hserin timeout,error,[wait(6)]
#ELSE
DEBUG 13,10, "TEMP: ",DEC fifo[0],".",DEC2( fifo[1]<<6)
#ENDIF
GOSUB AQUIRE
pause 5000
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]

FOR index = 0 TO 1151 STEP 0


btr = 0
reg = STATUS1
WHILE (btr&192) = 0
PAUSE 10
i2cread sda,scl,ADDR,reg,[btr]
DEBUG "Y" ,DEC 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
DEBUG "Z" ,DEC btr
WHILE btr
reg = FIFO_DATA
i2cread sda,scl,ADDR,reg,[buffer[index + 2],buffer[index + 1],buffer[index + 0],buffer[index + 5],buffer[index + 4],buffer[index + 3]]
index = index + 6
btr = btr - 1
WEND

NEXT
reg = OVF_COUNTER
i2cread sda,scl,ADDR,reg,[reg ]
DEBUG 13,10,"OF ",DEC reg

FOR index = 0 TO 1151 STEP 0
for ptr = 0 to 2
fifo[ptr] = buffer[index + ptr]
next
DEBUG 13,10;, hex2 fifo[2] , hex2 fifo[1], hex2 fifo[0]
asm ;ROTATE READING TO GET SUFFICIENT RESOLOUTION INTO UPPER WORD
banksel _fifo
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
banksel 0
endasm
DEBUG 9,HEX4 result
index = index + 3
' DEBUG 13,10, hex2 buffer[index+2],hex2 buffer[index+1],hex2 buffer[index],9 ,HEX4 result
' DEBUG 9, hex2 fifo[2] , hex2 fifo[1], hex2 fifo[0]
for ptr = 0 to 2
fifo[ptr] = buffer[index + ptr]
next

' DEBUG 9, hex2 fifo[2] , hex2 fifo[1], hex2 fifo[0]
asm ;ROTATE READING TO GET SUFFICIENT RESOLOUTION INTO UPPER WORD
banksel _fifo
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
banksel 0
endasm
DEBUG 9,HEX4 result
index = index + 3
NEXT
RETURN

richard
- 12th January 2022, 12:59
But in the manual it says that we never use constants for the I2C command.



What i have realised is that you refer to the



reg var byte


everytime you need to asign a constant value to it. Is that right? with that way we alter the restriction of using constants. (or am i wrong?)


in pbp constants are 16 bit, they are fine in i2c when a 16 bit value is appropriate and the endianness matches
if you use a var there is never any doubt, you get what you asked for but the endianness must match




im confused here. When do we use the write address and when do we use the read address!!!
there is no such thing as write address or read address in pbp there is only the address

astanapane
- 12th January 2022, 14:16
there is no such thing as write address or read address in pbp there is only the address

there is an adress of 7 bits....then PBP is taking care for the Read or Write bit.

The following is from the manual of the MAX30102.

9144

But in the program under the registers we are using the AE which is related to write address. Isnt it?


ADDR = $ae ; addr is 0x57 << 1

richard
- 13th January 2022, 00:51
But in the program under the registers we are using the AE which is related to write address. Isnt it?
you are trying to create a problem that does not exist there is no such thing as write address or read address in pbp there is only the address
a pbp i2c address is the 7bit manufacturer's address left shifted 1 bit, the only variation is for crappy ancient eeproms where the "page"
address forms part of the address[control] , but bit0 is still under pbp control regardless

you don't quibble the hserout command adding start and stop bits to your serial transmission or even calculating the parity and inserting it also

9145

why should the i2c "high level" commands not be able to set the r/w bit as required, after all that's why you use pbp. it stops you needing to dig into the bowels of i2c transactions

the manual

9146

lets not hear any more of this nonsense

astanapane
- 13th January 2022, 08:53
Thanks Richard,

just wanted to understand it. Now it is clear.

richard
- 13th January 2022, 11:37
now have low pass filtering on pic
9147
73 rpm tonight

astanapane
- 14th January 2022, 11:06
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.

richard
- 14th January 2022, 23:04
9151

try as i might i cannot detect the relevant peaks reliably




'************************************************* ***************'* 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

mpgmike
- 14th January 2022, 23:37
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.

richard
- 15th January 2022, 00:38
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

mpgmike
- 15th January 2022, 02:44
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?


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.

richard
- 15th January 2022, 05:47
the filter is not really the issue, its the variability of the data that is problematical, every 128 sample is subtly different.
light leakage, finger pressure and position and even finger compression time lead to wildly variable readings. my max30102 housing is probable not ideal either
i use for this filter
fv = fv*x + nv*(y)

where x+y = 1, fv = average nv = new value

in pbp terms x y
ave = (ave ** 60000) + (buffer[index] ** 5535 ) ;60000 + 5535 = 65535
or
ave = (ave */ 250) + (buffer[index] */ 5 ) ;250+ 5 = 255

depending on finesse required, the introduced phase shift has no bearing on the result either.
it may not be as fast as your method but has better control and in this case speed is of no importance

richard
- 15th January 2022, 06:51
well my sensor was not 5v tolerant its now a random number generator , in the bin
i give up

astanapane
- 15th January 2022, 10:47
Richard which sensor did you have? Could you please let us know? for avoiding it in the future.

Based on cardiovascular medical graph, the post #44 and post #49 looks fine.

I think #49 it is a bit better which doesnt mean the in #44 the graph is not acceptable.

richard
- 15th January 2022, 11:24
my module was clearly marked as 3.3v its the one with slots in the board to tie it on your finger
the code has little if any effect on the graphical result. the result depends on your finger and its placement
ambient light etc, my holder is a little tight it constricts circulation a bit and i need to change fingers every 10 minutes to get good data
i found a spare [must have bought 2] i will be more careful with this one.

i still cannot get a algo to find the right peaks. on a graph it looks so obvious even in the crappy reads beats are distinctive, by the absolute shits computers are stupid. breaking the search into 32 sample blocks gets me the block highest highs and lowest lows but there is no guarantee the high in a block is actually a peak or whether continues to ramp up into the next block

astanapane
- 15th January 2022, 11:59
I guess you have this one

9153

https://www.teachmemicro.com/max30102-an-improved-heart-rate-sensor-for-arduino/

I also have 2 of those, but in the schematic the input voltage is 3.3V to 5V.

9154

or you are talking about the logic level? So i also need to take care about it and supply the PIC with 3.3V.

richard
- 15th January 2022, 12:08
interesting mine has no jumper fitted, its not dead just gives random results , temp works ok still
led still comes on
i will solder the jumper and try again

Ioannis
- 15th January 2022, 12:35
It is very important to:

1. have the finger cleaned with alcohol before testing and also clean the sensor with alcohol too.
2. Not pressing hard the finger on the sensor, the shell of the sensor should be lightly tight on the finger.
3. No ambient light should come in the sensor area. Most if not all commercial devices are black for a purpose.

Hope this helps.

Ioannis

richard
- 15th January 2022, 12:49
i cleaned the screen with metho it had a spec of gunge in the wrong spot now restored. mine will not run for long at 5v 3-4 minutes then the readings are crap, jumper made nil difference. i notice the led got substantially brighter when it goes strange. 5v is a no go for this unit

richard
- 15th January 2022, 12:55
this kind of defines the problem

9155

peaks are easy to see, my 32 sample search yields these peaks its not easy
sample
5
51
95
98

raw data



lpf red ir
4235 4275 4435
4236 4250 4443
4238 4259 4440
4241 4264 4450
4243 4265 4482
4244 4254 4471
4244 4247 4446
4243 4244 4425
4243 4250 4424
4242 4243 4394
4242 4248 4397
4241 4244 4372
4239 4232 4376
4235 4223 4377
4231 4214 4375
4227 4214 4364
4224 4212 4385
4220 4205 4382
4218 4210 4403
4218 4225 4401
4217 4216 4398
4217 4229 4398
4217 4224 4400
4218 4238 4410
4220 4244 4412
4223 4250 4401
4225 4248 4398
4226 4245 4402
4228 4247 4398
4230 4248 4386
4233 4256 4402
4232 4238 4390
4233 4250 4400
4236 4262 4405
4238 4256 4400
4237 4237 4405
4236 4235 4400
4236 4245 4420
4234 4227 4431
4235 4251 4442
4234 4234 4437
4235 4252 4432
4236 4248 4420
4239 4265 4455
4241 4263 4440
4243 4267 4436
4246 4276 4439
4245 4249 4424
4246 4255 4433
4249 4278 4442
4250 4262 4442
4252 4271 4427
4252 4254 4429
4250 4242 4408
4246 4228 4399
4241 4221 4399
4234 4195 4378
4229 4205 4378
4224 4200 4395
4220 4204 4381
4216 4200 4363
4212 4195 4377
4209 4198 4372
4208 4208 4365
4207 4215 4366
4209 4227 4375
4212 4240 4371
4213 4227 4371
4214 4229 4358
4215 4229 4353
4215 4220 4360
4215 4218 4356
4215 4222 4357
4217 4233 4365
4217 4228 4360
4218 4232 4366
4217 4222 4364
4217 4230 4366
4217 4224 4373
4216 4220 4391
4217 4233 4381
4215 4211 4391
4215 4221 4401
4215 4222 4404
4215 4222 4411
4215 4220 4425
4215 4221 4418
4216 4226 4413
4217 4233 4427
4218 4238 4421
4219 4237 4416
4221 4245 4428
4222 4237 4421
4226 4261 4422
4229 4255 4436
4233 4263 4432
4236 4269 4437
4240 4272 4423
4241 4258 4431
4241 4252 4403
4240 4246 4381
4236 4223 4368
4231 4207 4343
4227 4213 4334
4223 4206 4341
4220 4211 4338
4218 4213 4334
4214 4194 4325
4213 4209 4336
4211 4201 4326
4210 4210 4328
4209 4211 4327
4207 4205 4321
4205 4195 4332
4203 4199 4324
4203 4213 4349
4201 4194 4352
4200 4202 4341
4198 4200 4338
4198 4203 4333
4197 4197 4348
4195 4188 4356
4194 4194 4377
4194 4204 4371
4194 4202 4383
4193 4195 4408
4193 4206 4383
4194 4209 4387

astanapane
- 15th January 2022, 13:31
results from PICKIT UART TOOL at 9600.



RED IR
8875 9120
8859 9127
8867 9128
8868 9127
8871 9116
8875 9121
8880 9115
8873 9115
8870 9115
8876 9123
8881 9127
8880 9114
8875 9118
8877 9129
8873 9114
8879 9127
8895 9119
8879 9129
8870 9128
8880 9130
8897 9139
8888 9116
8880 9123
8886 9146
8889 9124
8890 9119
8897 9113
8891 9122
8872 9134
8896 9124
8885 9121
8875 9121
8882 9130
8885 9129
8892 9125
8894 9132
8884 9117
8888 9127
8881 9116
8878 9124
8876 9112
8864 9117
8873 9120
8867 9113
8879 9123
8873 9125
8865 9119
8872 9131
8873 9118
8878 9124
8877 9123
8878 9119
8878 9116
8891 9108
8878 9110
8869 9140
8877 9119
8870 9119
8865 9116
8878 9120
8882 9126
8884 9138
8877 9130
8879 9116
8883 9127
8883 9128
8887 9140
8890 9126
8876 9107
8884 9116
8875 9121
8888 9138
8893 9117
8885 9116
8895 9114
8876 9128
8891 9128
8893 9130
8889 9123
8881 9114
8886 9119
8876 9116
8883 9126
8876 9118
8875 9126
8866 9115
8882 9115
8866 9113
8882 9118
8880 9118
8885 9108
8877 9123
8880 9125
8869 9122
8888 9123
8871 9119
8881 9118
8876 9117
8890 9136
8879 9115
8891 9124
8886 9115
8891 9107
8884 9110
8886 9128
8899 9117
8886 9138
8883 9133
8902 9130
8890 9132
8877 9124
8889 9131
8895 9127
8884 9132
8887 9127
8885 9120
8893 9139
8895 9135
8875 9131
8889 9109
8891 9110
8881 9121
8875 9123
8889 9126
8867 9124
8873 9111
8875 9115
8875 9109
8865 9102
8874 9119
8890 9124
8886 9121
8879 9115
8877 9114
8876 9118
8878 9106
8867 9114
8874 9110
8881 9121
8877 9120
8885 9113
8873 9107
8889 9127
8876 9127
8889 9115
8874 9110
8888 9119
8877 9131
8884 9108
8888 9114
8882 9116
8881 9110
8891 9113
8871 9114
8879 9107
8882 9115
8877 9115
8886 9114
8880 9118
8874 9100
8864 9112
8864 9112
8872 9102
8867 9114
8855 9092
8865 9117
8873 9104
8861 9115
8855 9113
8868 9099
8849 9098
8875 9109
8868 9111
8861 9108
8872 9100
8873 9113
8871 9123
8863 9100
8866 9110
8874 9106
8870 9102
8884 9100
8873 9117
8865 9109
8860 9111
8861 9117
8872 9102
8868 9107
8885 9111
8869 9098
8866 9116
8876 9106



9156

The following is the circuit board, which is changed and added a 3.3V step up/ step down Voltage regulator. Just ot be on a safe side.

9157

9158

The following is a second measurement which based on a samsung's mobile measurement it suppose to be close at 78 bpm.



RED IR
8492 8577
8489 8590
8493 8576
8494 8592
8500 8601
8499 8596
8509 8588
8504 8580
8504 8586
8514 8588
8507 8585
8504 8583
8513 8580
8514 8580
8517 8594
8514 8588
8512 8584
8499 8575
8507 8590
8514 8580
8499 8573
8505 8607
8504 8575
8489 8574
8498 8586
8496 8579
8510 8582
8498 8580
8499 8578
8537 8574
8505 8576
8497 8563
8503 8585
8500 8568
8488 8581
8505 8582
8506 8589
8521 8576
8497 8588
8509 8583
8507 8583
8499 8583
8497 8575
8502 8580
8486 8569
8505 8579
8507 8571
8499 8576
8505 8572
8502 8580
8498 8570
8499 8569
8496 8571
8485 8572
8496 8564
8511 8580
8499 8570
8498 8562
8497 8578
8499 8581
8493 8656
8497 8579
8503 8573
8507 8584
8492 8570
8499 8565
8495 8572
8501 8573
8511 8576
8502 8568
8506 8562
8499 8557
8498 8572
8492 8563
8505 8555
8503 8567
8494 8565
8493 8570
8502 8573
8489 8564
8493 8558
8507 8565
8505 8574
8499 8571
8497 8576
8493 8574
8489 8566
8498 8568
8496 8569
8495 8578
8517 8570
8515 8564
8506 8556
8509 8571
8506 8572
8497 8565
8508 8570
8499 8571
8506 8566
8443 8575
8498 8571
8501 8568
8500 8562
8512 8569
8497 8565
8510 8573
8499 8563
8508 8555
8526 8566
8513 8566
8513 8582
8505 8562
8511 8585
8492 8564
8511 8582
8514 8563
8502 8570
8506 8570
8518 8563
8513 8570
8508 8575
8505 8586
8510 8560
8519 8572
8516 8580
8511 8584
8506 8562
8496 8565
8519 8570
8502 8575
8631 8578
8510 8570
8529 8571
8516 8562
8515 8566
8510 8568
8514 8579
8513 8581
8518 8573
8524 8578
8504 8569
8508 8559
8515 8569
8515 8575
8512 8580
8514 8555
8525 8583
8519 8577
8517 8572
8507 8571
8516 8572
8521 8571
8525 8581
8505 8563
8522 8565
8518 8569
8516 8559
8515 8578
8521 8567
8502 8571
8522 8577
8528 8586
8529 8581
8518 8579
8507 8562
8520 8571
8512 8560
8518 8572
8531 8550
8507 8576
8503 8568
8515 8578
8509 8573
8518 8570
8517 8566
8508 8567
8509 8570
8520 8572
8518 8585
8513 8565
8521 8566
8525 8569
8515 8578
8515 8573
8517 8567
8515 8578
8510 8565
8515 8572
8531 8564
8516 8563
8519 8560
8514 8571

astanapane
- 15th January 2022, 14:11
For everyone info,

found this interesting article.

https://morf.lv/implementing-pulse-oximeter-using-max30100

astanapane
- 16th January 2022, 00:19
wrong info above, as i captured ascii and not hex values.
I'll try to fix that on the following message.

richard
- 16th January 2022, 00:56
if you are using excel then its easier to import dec rather than hex, i changed to dec

astanapane
- 16th January 2022, 01:12
if you are using excel then its easier to import dec rather than hex, i changed to dec

just tried. Let me see the values on the excel. :)



0 8028 1F5C
8053 8551 8259
8076 8533 8249
8100 8563 8254
8120 8541 8251
8140 8553 8244
8159 8548 8249
8178 8543 8247
8195 8535 8249
8211 8534 8260
8226 8540 8230
8240 8536 8251
8254 8562 8236
8268 8554 8259
8280 8528 8251
8292 8556 8247
8303 8534 8257
8314 8551 8254
8324 8541 8255
8334 8545 8243
8344 8550 8247
8353 8552 8248
8362 8557 8283
8370 8546 8242
8378 8549 8267
8385 8539 8259
8392 8548 8244
8399 8543 8256
8406 8549 8250
8412 8561 8261
8417 8536 8275
8422 8533 8268
8427 8544 8259
8432 8557 8239
8436 8539 8257
8441 8545 8235
8445 8545 8259
8449 8545 8245
8453 8549 8245
8456 8537 8263
8459 8540 8228
8463 8550 8249
8465 8540 8234
8468 8558 8242
8469 8521 8249
8472 8551 8249
8474 8532 8262
8477 8547 8243
8480 8554 8268
8481 8540 8241
8483 8554 8256
8485 8552 8264
8487 8554 8230
8489 8548 8249
8490 8532 8239
8491 8540 8257
8492 8525 8265
8494 8549 8243
8495 8526 8265
8496 8527 8249
8498 8556 8249
8499 8524 8275
8500 8539 8255
8500 8541 8257
8500 8535 8239
8501 8547 8251
8501 8530 8266
8502 8546 8248
8502 8532 8262
8503 8557 8253
8504 8551 8254
8503 8522 8257
8504 8554 8233
8503 8519 8256
8503 8534 8234
8503 8530 8246
8503 8528 8236
8504 8550 8259
8504 8534 8258
8504 8542 8229
8504 8538 8247
8505 8548 8243
8507 8563 8256
8508 8543 8267
8508 8538 8241
8508 8532 8258
8509 8543 8263
8510 8547 8259
8510 8527 8254
8510 8536 8247
8510 8534 8261
8510 8537 8238
8510 8536 8253
8511 8543 8250
8512 8546 8260
8511 8518 8266
8512 8548 8240
8512 8533 8261
8513 8545 8242
8515 8575 8270
8515 8541 8275
8515 8536 8236
8515 8534 8252
8515 8541 8251
8516 8559 8264
8516 8524 8250
8516 8529 8251
8516 8526 8254
8517 8543 8235
8517 8532 8254
8517 8532 8236
8518 8545 8243
8517 8518 8258
8517 8535 8240
8517 8534 8241
8517 8539 8245
8517 8541 8261
8517 8532 8250
8517 8531 8243
8517 8530 8260
8517 8534 8249
8517 8528 8246
8517 8527 8249
8518 8557 8255
8518 8531 8250
8518 8537 8233
8518 8541 8250
8518 8534 8252
8518 8539 8250

astanapane
- 16th January 2022, 02:06
Just transfered those values from the PICKIT serial terminal to excel. No conversion.

9160

richard
- 16th January 2022, 02:40
tidied up a bit and swaped to using ir data [still in mode 3 for now]



'************************************************* ***************'* 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[8]
lastpeak var word
lastlow var word
lpfdata var word[192]
result var word ext
result1 var word ext
pulse var word
@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:
index=0
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
while index <256
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 && (index < 256))
reg = FIFO_DATA ;---------ir----------;===========red===========
i2cread sda,scl,ADDR,reg,[FIFO[2],FIFO[1],FIFO[0],FIFO[5],FIFO[4],FIFO[3]]
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
btfsc STATUS,C
incfsz _fifo + 1 ,w
addwf _average + 1,f
btfsc STATUS,C
incf _average + 2,f
endasm
buffer[index] = result ;ir
index = index + 1
buffer[index] = result1 ;red
index = index + 1
btr = btr - 1
WEND
wend



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 2
ave = (ave ** 57000) + (buffer[index] ** 8535 )
lpfdata[index>>1]=ave
;.....lpf..;;;;----red-----;;;;======-ir-========
DEBUG 13,10,dec ave,9,dec buffer[index],9,dec buffer[index+1]

NEXT
' RETURN

lastpeak = lpfdata[0]
lastlow = lpfdata[0]
asm
banksel _peaks
clrf _peaks
clrf _peaks+1
clrf _peaks+2
clrf _peaks+3
clrf _peaks+4
clrf _peaks+5
clrf _peaks+6
clrf _peaks+7
banksel 0
endasm
DEBUG 13,10,"lpf"
lastpeak = lpfdata[0]
lastlow = lpfdata[0]
for index = 0 TO 31 ;p1
if (lpfdata[index] > lastpeak) then
lastpeak = lpfdata[index]
peaks[4] = index :peaks[5]=index :peaks[6]=index :peaks[7]=index
endif
if (lpfdata[index] < lastlow) then
lastlow = lpfdata[index]
peaks[0] = index :peaks[1]=index :peaks[2]=index :peaks[3]=index
endif
NEXT
lastpeak = lpfdata[32]
lastlow = lpfdata[32]
for index = 32 TO 63 ;p2
if (lpfdata[index] >lastpeak) then
lastpeak = lpfdata[index]
peaks[5] = index :peaks[6]=index :peaks[7]=index
endif
if (lpfdata[index] <lastlow) then
lastlow = lpfdata[index]
peaks[1] = index :peaks[2]=index :peaks[3]=index
endif
NEXT

lastpeak = lpfdata[64]
lastlow = lpfdata[64]
for index = 64 TO 95 ;p3
if (lpfdata[index] >lastpeak) then
lastpeak = lpfdata[index]
peaks[6]= index :peaks[7]=index
endif
if (lpfdata[index] < lastlow) then
lastlow= lpfdata[index]
peaks[2]= index :peaks[3]=index
endif
NEXT

lastpeak = lpfdata[96]
lastlow = lpfdata[96]
for index = 96 TO 127 ;p4
if (lpfdata[index] >lastpeak) then
lastpeak = lpfdata[index]
peaks[7] = index
endif
if (lpfdata[index] <lastlow) then
lastlow = lpfdata[index]
peaks[3] = index
endif
NEXT

DEBUG 13,10,dec peaks[4],9,dec peaks[0];,9, dec lpfdata[peaks[0]]-lpfdata[peaks[1]]
DEBUG 13,10,dec peaks[5],9,dec peaks[1]
DEBUG 13,10,dec peaks[6],9,dec peaks[2]
DEBUG 13,10,dec peaks[7],9,dec peaks[3]





' pulse = 3000/abs( btr-ptr)

' DEBUG 13,10,dec ptr , 9,dec btr , 9,dec pulse


RETURN

astanapane
- 16th January 2022, 13:49
Richard thanks for your effort.

These are the new results base on the last change on IR values.



8744 8760 9019
8745 8759 8995
8743 8740 9001
8744 8754 9012
8746 8764 8991
8745 8751 9002
8745 8755 8986
8743 8744 9003
8742 8739 9008
8743 8756 9000
8743 8752 9008
8745 8763 9023
8746 8762 8973
8743 8738 9011
8742 8745 8982
8739 8728 8985
8736 8726 9005
8735 8738 8990
8733 8727 8996
8733 8741 9001
8732 8736 8986
8730 8726 8996
8731 8750 8999
8730 8731 8983
8728 8725 9024
8730 8751 9002
8727 8720 9014
8729 8748 9014
8731 8751 8986
8731 8742 8998
8732 8749 8989
8732 8741 9001
8734 8761 9010
8736 8757 9001
8733 8722 9026
8732 8736 9022
8734 8756 8978
8734 8741 8988
8735 8752 8991
8735 8743 9010
8738 8762 9018
8737 8745 8992
8738 8746 8991
8741 8771 8995
8739 8734 8996
8740 8756 9010
8740 8753 9009
8741 8754 9002
8742 8759 8991
8740 8733 9001
8741 8755 9001
8739 8735 8978
8737 8733 8997
8738 8746 8981
8734 8720 8992
8733 8732 9004
8732 8735 8981
8730 8725 9004
8731 8750 8979
8730 8735 8983
8730 8743 8986
8730 8742 8982
8729 8732 8989
8731 8748 9004
8732 8752 8981
8728 8715 8981
8730 8748 9002
8729 8732 8999
8732 8758 9003
8732 8742 8990
8733 8747 9005
8734 8752 8991
8734 8742 8993
8736 8756 9007
8737 8753 8988
8737 8739 9004
8738 8750 8992
8737 8745 8993
8739 8759 9003
8740 8754 8975
8739 8743 8987
8740 8754 8996
8739 8743 8998
8741 8763 8999
8741 8749 8978
8740 8743 8996
8741 8756 8987
8740 8740 8993
8740 8747 9005
8739 8742 8977
8735 8720 8977
8734 8737 8984
8733 8732 8985
8733 8739 8986
8733 8743 8982
8730 8722 8990
8728 8729 8986
8726 8717 8999
8726 8731 8988
8726 8735 8983
8725 8729 8987
8727 8751 8989
8727 8732 8976
8727 8733 8997
8729 8751 8988
8730 8740 9005
8731 8749 8983
8732 8750 8984
8734 8755 8997
8737 8766 8971
8736 8733 8986
8738 8760 8969
8737 8740 8990
8740 8762 9001
8741 8755 8973
8739 8735 8997
8740 8761 8971
8740 8747 8985
8741 8758 8974
8740 8744 8990
8742 8763 9014
8744 8763 8977
8741 8727 8978
8739 8732 8981
8737 8734 8980
8736 8736 8988
8736 8742 8964
8733 8717 8974


9163

astanapane
- 17th January 2022, 22:01
Richard,

i think i post #41 and #44, the graph was a bit better in terms of highs and lows. Dont know if the last configuration and modification in LPF made it easier to identify those highs and lows.

mpgmike
- 17th January 2022, 22:52
Have you checked to see if you actually have a pulse for this thing to read? Maybe you're dead and don't even know it?? Try it on someone else as a verification? Just joking...

astanapane
- 17th January 2022, 23:33
Have you checked to see if you actually have a pulse for this thing to read? Maybe you're dead and don't even know it?? Try it on someone else as a verification? Just joking...

hehehehe, sure i will do that. Just to check what will be the values on a different person, as i know my average Heart pulses are always around 82-90 Bpm.

I will find someone with much lower and more normal heart pulses like 62-75 bpm.

The following is my measurement right now.



lpf
31 11
63 11
68 83
68 109
TEMP: 22.68
000F7E26
OverFlows 0 7932 1EFC
7928 7915 7838
7927 7925 7843
7926 7929 7818
7924 7921 7833
7924 7938 7815
7924 7933 7810
7919 7899 7812
7917 7916 7824
7919 7940 7811
7917 7916 7819
7918 7936 7825
7918 7928 7810
7918 7930 7820
7918 7930 7813
7918 7931 7813
7918 7926 7823
7919 7933 7812
7920 7935 7824
7921 7932 7821
7922 7935 7820
7921 7923 7823
7920 7922 7828
7921 7932 7825
7923 7943 7815
7922 7921 7819
7923 7939 7810
7924 7936 7823
7923 7925 7828
7926 7950 7827
7924 7923 7825
7924 7936 7821
7925 7942 7836
7923 7924 7812
7922 7921 7824
7924 7943 7818
7923 7929 7813
7924 7935 7825
7924 7933 7830
7922 7917 7824
7924 7943 7831
7925 7946 7811
7924 7928 7822
7924 7938 7839
7926 7952 7803
7925 7928 7827
7926 7941 7839
7927 7947 7819
7924 7916 7816
7927 7957 7842
7930 7958 7826
7930 7933 7820
7930 7932 7822
7931 7947 7821
7929 7925 7825
7930 7943 7827
7930 7935 7814
7928 7918 7840
7928 7936 7818
7927 7929 7827
7925 7919 7829
7925 7937 7819
7926 7946 7822
7926 7936 7812
7925 7929 7828
7925 7938 7827
7925 7933 7816
7925 7936 7834
7925 7938 7822
7928 7957 7821
7927 7928 7820
7928 7940 7809
7929 7942 7814
7927 7918 7830
7927 7938 7802
7926 7925 7814
7926 7932 7825
7926 7932 7813
7924 7923 7809
7924 7936 7830
7925 7940 7831
7926 7942 7824
7925 7926 7826
7923 7924 7811
7923 7928 7826
7926 7950 7834
7925 7930 7808
7924 7930 7826
7924 7932 7842
7926 7948 7807
7926 7934 7817
7927 7941 7831
7927 7937 7815
7925 7917 7833
7926 7945 7813
7926 7932 7796
7924 7922 7818
7924 7939 7806
7922 7921 7812
7924 7940 7811
7924 7936 7808
7922 7918 7821
7921 7924 7823
7923 7943 7804
7923 7925 7809
7924 7934 7817
7924 7939 7815
7923 7931 7806
7921 7915 7816
7921 7925 7809
7920 7923 7822
7920 7925 7822
7922 7947 7808
7922 7925 7817
7922 7929 7817
7922 7928 7820
7922 7926 7823
7923 7933 7832
7927 7959 7804
7925 7919 7812
7926 7942 7822
7924 7918 7809
7924 7935 7818
7924 7936 7813
7922 7923 7813
7923 7937 7818
7925 7941 7801
7922 7914 7818
7922 7929 7836
lpf
0 7
52 33
71 82
117 109
TEMP: 24.96




(measured as well with the Samsung Galaxy Sensor 80 Bpm)

richard
- 19th January 2022, 09:48
looks like simplistic lowpass filtering of the data to obtain pulse will never give a
reliable and repeatable result, it can deliver a nice picture but that is as far as it goes.
i will need to learn a bit more about dsp techniques. first step, remove dc component i think.
don't hold your breath, got more paid work this week

astanapane
- 19th January 2022, 14:45
Richard this is already lots of work, and i dont know how to thank you for all this.

I believe that this will be useful for many people in the forum in the end. (even if noone else is involved).

mpgmike
- 19th January 2022, 22:51
I think Richard is eluding to a process called Fast Fourier Transform, FFT for short. The filter is a mathematical equation applied to each result. In many cases, the previous result is part of the equation. FFT is used to filter noise out of cell phone signals, among other things. The magic is getting the FFT formula right for your application. I've looked into it briefly, just enough to get an idea of what it's about, but not enough to actually use it effectively. I do know there are "standard" FFTs that are go-to works-every-time-its-tried that would probably work for you.

The one that is like Example #1 in the textbooks is simply:


if Result > Previous
Plot += 1
elseif Result < Previous
Plot -= 1
endif
Previous = Result

This simply plots trends of increasing or decreasing values, without specific concern for the actual value.

richard
- 19th January 2022, 23:14
I think Richard is eluding to a process called Fast Fourier Transform
been there done that.


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

mpgmike
- 20th January 2022, 04:25
there is plenty of raw data posted, you can always ask for more. feel free to have a go



I've looked into it briefly, just enough to get an idea of what it's about, but not enough to actually use it effectively.

Thanks for the generous offer :)

astanapane
- 26th January 2022, 18:03
I did an other test on a different person. Those info are from a woman.

Richard have done a very good job up to now. I think the LPF should not be so aggressive.

9176



LPF RED IR
7278 7275 7077
7277 7266 7038
7275 7252 7048
7273 7242 7018
7270 7233 7012
7268 7247 7003
7266 7243 6991
7264 7248 6987
7263 7264 7005
7260 7231 7002
7257 7239 7008
7256 7242 7025
7256 7264 7015
7255 7249 7039
7255 7262 7037
7255 7267 7062
7254 7258 7047
7255 7286 7075
7255 7274 7086
7256 7289 7082
7257 7289 7096
7257 7281 7083
7257 7280 7105
7258 7284 7089
7259 7293 7124
7260 7291 7150
7260 7299 7122
7260 7299 7144
7261 7303 7143
7263 7329 7164
7264 7308 7173
7265 7307 7183
7266 7313 7163
7268 7322 7167
7269 7317 7171
7270 7317 7193
7273 7343 7198
7275 7329 7205
7277 7332 7194
7279 7330 7224
7281 7333 7201
7281 7318 7166
7280 7282 7125
7278 7265 7068
7276 7245 7029
7273 7240 7003
7270 7226 7016
7267 7234 6989
7263 7210 6988
7259 7209 6977
7257 7239 6970
7254 7221 6966
7252 7227 6977
7249 7214 6998
7247 7223 6998
7245 7233 6996
7243 7238 6996
7241 7239 7010
7239 7241 6995
7238 7233 7028
7237 7228 7036
7237 7261 7039
7236 7240 7049
7237 7262 7052
7237 7248 7062
7238 7265 7085
7239 7278 7108
7241 7286 7106
7242 7294 7136
7243 7286 7141
7244 7299 7148
7246 7302 7182
7248 7309 7159
7250 7318 7173
7252 7306 7192
7255 7331 7192
7256 7296 7212
7259 7329 7187
7261 7308 7226
7263 7338 7212
7265 7323 7224
7267 7327 7225
7269 7341 7225
7270 7305 7198
7270 7294 7140
7268 7256 7103
7266 7243 7041
7263 7241 7053
7260 7240 7029
7257 7234 7023
7255 7229 7021
7254 7245 7008
7251 7217 7044
7250 7243 7013
7248 7237 7033
7247 7245 7045
7247 7265 7060
7246 7255 7066
7246 7263 7070
7246 7265 7096
7246 7272 7075
7245 7260 7100
7245 7281 7103
7246 7287 7113
7246 7281 7131
7247 7288 7130
7248 7282 7159
7250 7306 7147
7251 7296 7171
7253 7319 7173
7255 7314 7190
7257 7314 7191
7260 7324 7204
7261 7302 7234
7263 7331 7204
7265 7323 7222
7266 7316 7215
7268 7329 7238
7269 7305 7243
7271 7339 7232
7273 7335 7243
7276 7348 7232
7278 7333 7257
7280 7325 7248
7281 7325 7229
7279 7278 7144
7277 7259 7086
7275 7244 7065

richard
- 27th January 2022, 04:44
I think the LPF should not be so aggressive.

if you want automatic pulse measurement that remains to be seen, a pretty picture is a different story


you can adjust filter as you see fit



the filter is not really the issue, its the variability of the data that is problematical, every 128 sample is subtly different.
light leakage, finger pressure and position and even finger compression time lead to wildly variable readings. my max30102 housing is probable not ideal either
i use for this filter
fv = fv*x + nv*(y)

where x+y = 1, fv = average nv = new value

in pbp terms x y
ave = (ave ** 60000) + (buffer[index] ** 5535 ) ;60000 + 5535 = 65535
or
ave = (ave */ 250) + (buffer[index] */ 5 ) ;250+ 5 = 255

depending on finesse required, the introduced phase shift has no bearing on the result either.
it may not be as fast as your method but has better control and in this case speed is of no importance




@ 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 2
ave = (ave ** 57000) + (buffer[index] ** 8535 )
lpfdata[index>>1]=ave
;.....lpf..;;;;----red-----;;;;======-ir-========
DEBUG 13,10,dec ave,9,dec buffer[index],9,dec buffer[index+1]

NEXT
' RETURN

astanapane
- 4th February 2022, 19:27
sorry for my late response. I was really busy these days.

thanks for the info. I will do some changes and tests. :)

Ioannis
- 4th February 2022, 19:36
I do not know if it is suitable for this project, but I had good results with Darrels Taylor ADC oversampling routines (http://dt.picbasic.co.uk/CODEX/AnalogueOverSampling).

Also Kalman filters maybe of use. I find them difficult to implement though.

Ioannis

richard
- 5th February 2022, 00:53
no amount of low pass filtering will remove the dc component, ultimately it just makes it worse.
the dc component needs to be removed to make detection of the beat peaks reliable.
there is plenty of raw data posted, you can always ask for more. feel free to have a go

Ioannis
- 5th February 2022, 12:54
I am not currently on this project. Was disappointed and set it aside. Removing DC component with firmware I think is too difficult.

The oversampling of DT routines were tested and applied on other projects with amazing results (costing in program speed of course, nothing is free).

Ioannis