
Commit error: Download the source code rd117_arduino.zip file
Download detective conan movie 21 sub indo mp4 | 2009 buell blast service manual pdf download |
Bird box yts torrent download | Download game rise of tomb raider pc terbaru |
The cannabis encyclopedia jorge cervantes torrent download | Star wars the last jedi full movie free download |
Download hitfilm 4 express free | Kisekae maker free no download |
Microsoft sql 2008 free download | Ps4 download code |
Pulse Oximeter With Much Improved Precision
Introduction: Pulse Oximeter With Much Improved Precision
If you recently visited a doctor, chances are that your basic vital signs were examined by a nurse. Weight, height, blood pressure, as well as heart rate (HR) and oxygen saturation in peripheral blood (SpO2). Perhaps, the last two were obtained from a red-glowing electronic finger probe that displayed relevant numbers on a tiny screen in minutes. That probe is called pulse oximeter and you can find all the basic info about it here.
One can easily buy a simple pulse oximeter, sure, but where is the fun in it? I have decided to build my own, first for the heck of it, but more importantly with a specific application in mind: nocturnal oximetry where both HR and SpO2 data would be continuously collected overnight and recorded on a micro SD card. Instructables already contains several projects of this kind, e.g., two involving Arduino here and here, and one utilizing Raspberry Pi. Mine uses slightly newer sensor MAX from MAXIM Integrated and Adafruit's Feather M0 Adalogger for control and data recording.
Our project is thus not particularly innovative in terms of hardware and as such would not be worth writing this Instructable, but in the process of creating it I have made crucial advances in software that allowed me to extract data from MAX with much higher consistency and much less noise than software written by MAXIM for this sensor. The performance of our signal processing algorithm is illustrated in the above chart where the two top graphs contain overnight heart rate and oxygen saturation calculated from raw signals by our method (identified by "RF"), while the bottom two graphs show MAXIM's results produced from exactly the same signals. Standard deviations for HR are bpm and bpm, and for SpO2 % and %, for RF and MAXIM, respectively.
(Both RF graphs correspond to minimal autocorrelation threshold of and no limit on R / IR correlation; see Steps 4 and 5 for explanation of these terms.)
Step 1: Hardware
- Pulse oximeter and heart rate sensor MAX system board from MAXIM Integrated, Inc.
- Feather M0 Adalogger from Adafruit, Inc.
- Lithium Ion Battery from Adafruit, Inc.
Connections:
- Adalogger pins SCL and SDA to corresponding SCL and SDA pins on MAX board
- Adalogger pin 10 to pin INT on MAX board
- Adalogger GND to MAX board GND
- Adalogger 3V to MAX VIN
Step 2: Digital Signals Returned by MAX
The principles of the sensor operation are very simple: two LEDs, one red ( nm) and one infrared ( nm, IR) shine light through human skin. The light is partially absorbed by underlying tissues, including peripheral blood. Sensor's photodetector collects reflected light at both wavelengths and returns two corresponding relative intensities using I2C protocol. Since absorption spectra for oxygenated and deoxygenated hemoglobin differ for both wavelengths, the reflected light has a variable component as the amount of arterial blood that is present under the skin pulses with each heartbeat. Figuring out heart rate and oxygen saturation is up to the signal processing software.
Examples of raw signals (IR channel only) are illustrated in the above images. One can notice a periodic component overlaid on a variable baseline that is shifting due to multiple factors mentioned in the Wikipedia page. Motion induced artifacts are particularly annoying since they may mask the useful HR signal and cause bogus results. Hence, advanced commercial oximeters feature accelerometers that help nullify these artifacts.
I may add an accelerometer to the next version of my oximeter, but for nocturnal HR/SpO2 recording, when sensor remains motionless most of the time, it is sufficient to detect and omit distorted signals.
The MAX sensor itself comes in a tiny surface-mounted package, but MAXIM graciously offers a breakout board (System Board ) plus signal processing software for Arduino and mbed - all in the reference design package MAXREFDES#. I happily bought it expecting to just solder some wires between the sensor and Adalogger and have a working, good oximeter in a single day. I adapted the RD_ARDUINO version of MAXIM's software to run on the Adalogger's ARM Cortex M0 processor. Basically, all I had to do was to replace incompatible SofI2C functions in maxcpp by the corresponding Wire library calls. The code compiled fine in the Arduino IDE v and ran on M0 without any errors. The net results, however, were disappointing. In the Introduction step I have already shown very high variance of both HR and SpO2. Naturally, one may claim that I have done something wrong and this was my original thought, too. However, in MAXIM's instructional video you can also observe wildly swinging HR values displayed on the screen. Moreover, comments below video confirm that others have noticed a similar phenomenon, as well.
To make a long story short, after some experimentation I have determined that the sensor is operating OK and an alternative method of digital signal processing results in much better stability. This new method, indicated by "RF", is described in the next steps.
Step 3: Signal Preprocessing
In our implementation, the raw signal is collected at the rate of 25 Hz (same as MAXIM's) for full 4 seconds (MAXIM's software collects only 1 second's worth), resulting in digitized time points per end data point. Each point sequence must be preprocessed in the following way:
- Mean-centering (a.k.a. "removal of the DC component" to electrical engineers). The raw data coming from the sensor is a time series of integers in the 105 range. The useful signal, though, is only a part of light reflected from arterial blood which varies on the order of only 102 - first figure. For meaningful signal processing, it is therefore desirable to subtract the mean from each series point. This part is no different from what the MAXIM software already does. What's different, however, is additional mean-centering of time indices themselves. In other words, instead of indexing series points by numbers from 0 to 99, the new indices are now numbers , , , It may seem weird at first, but thanks to this procedure the signal curve's "center of gravity" coincides with the origin of the coordinate system (second figure). This fact becomes quite useful in the next step.
- Baseline leveling. Another look at the waveforms shown in Step 2 illustrates that the baseline of real oximetry signals is far from being horizontally flat, but varies through different slopes. Third figure shows a mean-centered IR signal (blue curve) and its baseline (blue straight line). In this case, the baseline's slope is negative. The signal processing method described ahead requires baseline to be horizontal. This can be achieved by simply subtracting the baseline from the mean-centered signal. Thanks to the mean-centering of both the Y and the X coordinates, the baseline's intercept is zero and its slope equation is particularly simple, as shown in the fourth rushbrookrathbone.co.uk baseline-leveled signal is shown by orange curve in the third figure.
Thus preprocessed signal is ready for the next step.
Step 4: The Workhorse: Autocorrelation Function
Returning back to the usual 1,,n indexing, the first figure shows definition of the autocorrelation function rm - a quantity found to be very useful in detecting signal's periodicity as well as quality. It is simply a normalized scalar product of the signal's time series with itself shifted by lag m. In our application, though, it is convenient to scale each autocorrelation value with respect to its value at lag = 0, i.e., use relative autocorrelation defined by rm / r0.
Plot of the relative autocorrelation of a typical good quality IR signal is shown in the second figure. As expected, it's value at lag = 0 is at its global maximum equal to 1. The next (local) maximum occurs at lag = 23 and equals to The presence of local minima and maxima in autocorrelation plot is easy to understand: as the signal shifts to the right its peaks interfere destructively with each other at first, but at certain point the interference becomes constructive and achieves maximum at the lag equal to the average period of the signal.
The last phrase is crucial: in order to determine the average time period between peaks, from which one can calculate signal's frequency (i.e., heart rate) it is sufficient to find the first local maximum of the autocorrelation function! By default, MAX samples analog input at a rate of 25 points per second, therefore at given m the period in seconds is equal to m / This leads to heart rate expressed in beats per minute (bpm) by:
HR = 60*25 / m = / m
Of course, it is not necessary to do expensive calculations of rm at all lag values. Our algorithm makes the first guess of heart rate = 60 bpm, which corresponds to m = Autocorrelation function is evaluated at that point and compared to the value at its left neighbor, m = If the neighbors value is higher, then the march continues to the left until rm-1 < rm. Thus determined final m is then returned as the lag at maximum. The next iteration starts from that value instead of 25 and the whole process repeats. If the first left neighbor is lower, then the above routine marches lag points to the right in similar manner. Most of the time, lag at maximum requires just a few evaluations of the autocorrelation function. In addition, maximum and minimum acceptable lags (corresponding to minimal and maximal heart rate, respectively) are used as limiting values.
The above works very well for good quality signals, but the real world is far from ideal. Some signals come out distorted, mostly due to motion artifacts. Such a signal is shown in the third figure. Poor periodicity is reflected in the shape of its autocorrelation function as well as in low value, , of the first local maximum at m = Compare it to the maximum value of determined for the good quality signal. Along with lag limiting values, therefore, the value of rm / r0 at maximum is a good indicator of signal quality and a requirement for it to exceed certain threshold may be used to filter out motion artifacts. The "RF" graphs shown in the introductions resulted from such threshold equal to
Step 5: Determining Oxygen Saturation
The previous step was sufficient for determining heart rate. The SpO2 requires more work. First, the so far neglected signal in the red (R) channel must be taken into account. Next, the ratio of red to infrared signals, Z = R/IR, both reflected off the arterial blood, is calculated. The "arterial blood" part is crucial, since most of the light is actually reflected off tissues and venous blood. How to pick portion of the signal corresponding to arterial blood? Well, this is the pulsatile component that varies with each heartbeat. In words of electrical engineers, it's the "AC part", while the remaining reflected light is the "DC part". Since absolute intensities of R and IR light are not commensurate, the Z ratio is calculated from relative intensities, as shown in the first figure. In terms of actually calculated quantities, I use root-mean-square (RMS) of the mean-centered, baseline-leveled signal, y, to the already known mean of the raw signal, <Y>; see second figure. The Z ratio is only half of the work, however. The nonlinear sensor response requires an empirical calibration between Z and the final SpO2 values. I took the calibration equation from MAXIM's code:
SpO2 = (*Z + )*Z +
Keep in mind this equation is valid only for MAX design board purchased in ! It is likely that MAXIM may recalibrate its sensors at a later date.
The above procedure still produces a lot of false SpO2 readings. The red channel suffers from many artifacts, just like the IR one. It is reasonable to assume that both signals should be strongly correlated. In fact, good quality signals, like the example in third figure, do correlate very well. The Pearson correlation coefficient is in this case as high as This is not always the case, as illustrated in the fourth figure. Although the IR signal would pass the heart rate quality filter with its rm / r0 = , the distorted R signal results in a poor correlation coefficient between the two equal to only This observation offers the second quality filter: having the correlation coefficient between channels greater than certain threshold.
The last two figures exemplify net effect of such quality filtering. First, the measured oxygen saturation is plotted with HR quality threshold of , but without the SpO2 filter. Next plot results from filtering out poor HR and SpO2 results at the rm / r0 and correlation coefficient thresholds. Overall, poor data points amounting to 12% of the total were filtered out by the stricter regime.
In our code the correlation coefficient, cc, is calculated according to the formula in fifth figure, where y represents the mean-centered, baseline-leveled signal, whereas r0 was defined in the previous step.
Step 6: The Source Code
The C source code for this project, formatted for the Arduino IDE, is available from our Github account at the following link:
rushbrookrathbone.co.uk
Its Readme page describes individual components.
I would like to take a moment to praise Adafruit for making such an excellent product as M0-based Adalogger. Its fast 48 MHz ARM Cortex M0 processor, with lots of RAM, certainly helped make this project viable, while directly attached SD card reader (plus Adafruit's SD library) remove all the hobbyist's pains associated with real time storage of large amounts of data.
1 Person Made This Project!
Did you make this project? Share it with us!
Recommendations
Battery Powered Contest
Plywood Challenge
Plastic Contest
67 Discussions
Hello, is there anyone succeed reading die temperature from MAX sensor by the code provided by MolecularD? I will appreciate for your help.
HI, have you tried this sensor on the wrist?
I tried it on the finger, the results were fine, could get above 99% spo2 and valid heart rate.
But when I tried it on the wrist, I could not get any results of both, all
On the wrist:
The raw value of Red is lower than IR about
On the finger:
The raw value of Red is only lower than IR about
You can use the MAX It works on the wrist
No, I haven't. It won't work on the wrist because of motion artifacts.
I looked more in detail at your code and I must first congratulate for the professional way in which you wrote the code and commented it, from 4 MAX drivers that I found, yours is by far the easiest to understand and use.
And now my question, I am running your driver on an ESP32 board that is connected to the MAX board only by SCL SDA (INT is not connected), so for your code the LEDs are lighting, but of course there is no measurement as the uC is waiting for the interrupt line.
But other drivers do not use interrupt line, is it possible to do a small modification to the code so that it works without an interrupt line?
I will check the datasheet in detail and try to understand how to do that by myself, but if you have a tip it would be really helpful.
Hmm, all drivers I know use interrupt line. Can you elaborate on your "other drivers do not use interrupt line"? Please provide links to relevant examples.
The one that I tried and the examples work on my board is the SparkFun Arduino library that I downloaded from Arduino library manager. The code is very similar to yours.
The heart rate seems to be okish, but the SPO2 is quite unstable.
I also tried another driver from GitHub, this is from the guy that made also the Flexiplot tool, but this driver is not working for the , the LEDs are not starting.
For your driver, I tried to modify it and disable the interrupts and also enable the FIFO rollover, but still the values that I get are not ok.
Thanks for the support!
Anyway, I took a look at the role of INT signal in my code (rushbrookrathbone.co.uk) - it's only use is detection of the new data availability in the main signal loop:
while(digitalRead(oxiInt)==1); //wait until the interrupt pin asserts
The analogous loop in SparkFun library (rushbrookrathbone.co.uk) features the following calls instead:
while (rushbrookrathbone.co.ukble() == false) //do we have new data? |
rushbrookrathbone.co.uk(); //Check the sensor for new data |
See the enclosed images. Thus, it's rather obvious what to do. I might add that the rushbrookrathbone.co.uk() is complicated and rather expensive. If the sensor's hardware offers an instant INT signal replacing it, then why not use it?
Hello, thanks again for the answer.
I followed your advice (kind of), and made a fork of your sketch rushbrookrathbone.co.uk
I used the Sparkfun MAX library calls instead of yours to read samples from the sensor, and after filling the sample array I use your algorithm.
But even if the samples look ok, I use FlexiPlot to see them and they are very clean (see Image), the calculation of the HeartRate is wrong by some multiplication factor, and the SPO2 is totally wrong.
I modified the samples to and the average samples to 8 and the HeartRate seems to be realistic, but the SPO2 is even more wrong.
I suspect that you have some defines inside your driver that are also used in the algorithm and these could cause these problems, I will try tomorrow to check it more in detail but if you have some tips it would be great.
Btw I also made your driver kind of work without the interrupt line, by commenting all the INTERRUPT register reads, but the samples that I get are always the same value, so that's why I used the MAX library, as I could not understand what I was doing wrong.
I know it would have been much easier to just solder an interrupt line, but then I wouldn't have learned so much.
Is your algorithm similar to what is done here: rushbrookrathbone.co.uk ?
I find the explanations very good, also the usage of the FlexiPlot tool helps a lot to understand what is happening.
Yes, algorithm_by_RF.h uses defines that are different from yours, so start your debugging from there.
Raivis Strogonovs' approach is interesting and well explained, but it's very different from mine.
The FlexiPlot you have attached (thank you) has period of s between peaks. Hence, it corresponds to an approximate HR = 62 bpm. No such HR is reported in the Serial Monitor capture, your second image, thus I am assuming FlexiPlot does not represent raw data for any of these outputs.
Actually the Flexiplot I posted is corresponding to that Serial data, but I never checked the period from the Flexiplot, I saw that it is under 1s so I supposed it is correct to what the Serial data is showing.
Thanks for the observation!
Hi.
Your code is amazing. i am using it for small project ventilator.
Normally the I2C of arduino is 0V(logic 0), 5V(0). But the I2C of Max is 0V(logic0) (logic 1) and the MAX board have k pull up resistors to V. The first time i connect Max to arduino it did not work with any codes i found in github, and i ask the supplier "is that sensor died" then they tell me to make k pull-up resistors for SDA SCL to 5V and it works. Can you guys help understand Why i have to do that and did that have any bad effect to the signal or microchip. I google it but still cant understand
Thank you so much
Hello, I am trying to use the MAX PCB with ESP32 based board.
I wired the SCL, SDA to pins 21, 22 directly and GND, VIN to GND and V of the ESP32 board.
The I2C doesn't seem to work in any sketch, I checked also an I2C Scanner and it tells that it can't find any device.
I measured and there is voltage on the pins on the board.
Any ideas how to make it work?
Both the ESP32 and the MAX use V so it should be no problem to connect directly?
Sorry, I have never used ESP32 board.
See my comment above, worked after pull-up fix for the board. Seems to be a design error.
I did not use this code yet, actually only for the SparkFun MAX library the examples work, but the measurements for the SPO2 are very unstable.
I will try this repository next week to on my ESP32 board and feedback if the sensor works.
Basically for ESP32 if you just change board and port in Arduino IDE it works to compile and upload if you have the ESP32 packages installed.
I found this fix on another site, my understanding that the pull-up to V was a design error. Anyway I've cut the track to V and soldered a wire to V and the sensor started working for me too. MAX
Even though the I2C might work in the way you described for some time, you should never mix and 5 V logic on the same bus. Otherwise, you will shorten lifespan of your sensor. Here are two links that contain detailed explanation:
rushbrookrathbone.co.uk
rushbrookrathbone.co.uk
Hello, I am wondering if this project can be ported on an ESP32 based board without very big difficulties.
I have a TTGO board which has an OLED screen which is perfect for displaying the heart rate and SPO2, also it seems to be fast enough to maybe even show the Heart Rate Graphic.
Anyway I will give it a try.
Hello
I have noticed that both your code and the Maxim reference code set the value of register 0x10 (#defined to REG_PILOT_PA) in the set up to 0x7f. However, in my datasheet (Rev 1 10/18) for the MAX register 0x10 is not defined. I've tried removing the line that writes to this register and it seems to make no difference on my STM32 port. Any ideas what this register does?
Well, I am no expert regarding Maxim hardware, thus I simply copied Maxim's reference settings and never touched them again. However, I have done some Internet search and found out that in _other_ Maxim sensor this register controls the current of IR LED in proximity mode. This is an extra function that is activated when the sensor enters standby state, awaiting the object sensed. For more details see:
rushbrookrathbone.co.uk
rushbrookrathbone.co.uk
0 thoughts to “Download the source code rd117_arduino.zip file”