{"id":4327,"date":"2019-04-03T20:00:03","date_gmt":"2019-04-03T18:00:03","guid":{"rendered":"https:\/\/msalamon.pl\/?p=4327"},"modified":"2025-12-27T19:55:26","modified_gmt":"2025-12-27T18:55:26","slug":"my-finger-is-pulsing-the-max30102-pulse-oximeter-under-stm32-control","status":"publish","type":"post","link":"https:\/\/msalamon.pl\/en\/my-finger-is-pulsing-the-max30102-pulse-oximeter-under-stm32-control\/","title":{"rendered":"My Finger Is Pulsing!!! The MAX30102 Pulse Oximeter Under STM32 Control."},"content":{"rendered":"\n<p>Probably every one of my readers&nbsp;has dealt with some wearable device, the so-called&nbsp;<em>wearables<\/em>. Even the simplest fitness bands have a built-in heart rate sensor that most often measures at the user\u2019s wrist. It turns out this isn\u2019t some mysterious technology available only to a closed group of consumer electronics manufacturers. We can also perform a simple measurement of our own heart rate right at our desks. All it takes is the right circuit.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">MAX30102 in healthcare<\/h1>\n\n\n\n<p>The company&nbsp;<em>Maxim<\/em>&nbsp;has released several sensors for heart rate measurement. One of them is today\u2019s subject, the <a href=\"https:\/\/sklep.msalamon.pl\/?s=max30102&amp;post_type=product&amp;utm_source=blog&amp;utm_medium=article&amp;utm_campaign=max30102&amp;utm_content=Text\">MAX30102<\/a> (<a data-e-disable-page-transition=\"true\" class=\"download-link\" title=\"\" href=\"https:\/\/msalamon.pl\/download\/720\/?tmstv=1766859692\" rel=\"nofollow\" id=\"download-link-720\" data-redirect=\"false\"><br>MAX30102 Datasheet<\/a><br>). In essence, it\u2019s a 2-in-1 device because in addition to pulse it can also measure SpO2, i.e., blood oxygen saturation. This sensor is often used in smartphones, for example in Samsung devices.<\/p>\n\n\n\n<p>The sensor requires 1.8 V to power the logic. In addition, a second voltage is required to power the LEDs. This can be 3.3 V, but the LEDs can also comfortably handle 5 V. The module has built-in LED power at 5 V, so you don\u2019t need to worry about it.<\/p>\n\n\n\n<p>This sensor has two built-in LEDs that illuminate the tissue being measured \u2014 one red and one infrared. A photodetector is used to capture the signal.<\/p>\n\n\n\n<p>The device features very low current consumption of just 0.7&nbsp;\u00b5A in&nbsp;<em>Shutdown<\/em> mode. The total current drawn by the sensor in active state mainly depends on the LED current, which is configured in software. Excluding the LEDs, the sensor itself needs about 600&nbsp;\u00b5A to operate. Each LED current is software-controlled in the range 0\u201351 mA. Huh! 51 mA per LED seems quite a lot. I didn\u2019t dare to push that much current\u2026<\/p>\n\n\n\n<p>The sensor has an INT interrupt output. It can be used to signal several events. In my opinion, the most useful ones are signaling a new sample and an approaching FIFO overflow.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/sklep.msalamon.pl\/produkt\/pulsometr-max30102\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=max30102&amp;utm_content=max30102\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"341\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/04\/MAX30102_baner-1024x341.jpg\" alt=\"\" class=\"wp-image-946\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/04\/MAX30102_baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/04\/MAX30102_baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/04\/MAX30102_baner-768x256.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/04\/MAX30102_baner-24x8.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/04\/MAX30102_baner-36x12.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/04\/MAX30102_baner-160x53.jpg 160w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/04\/MAX30102_baner.jpg 1200w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Reflective heart rate and SpO2 measurement<\/h2>\n\n\n\n<p>The measurement used by our <a href=\"https:\/\/sklep.msalamon.pl\/?s=max30102&amp;post_type=product&amp;utm_source=blog&amp;utm_medium=article&amp;utm_campaign=max30102&amp;utm_content=Text\">Max<\/a> is called reflective because the LEDs and the photodetector are on the same side of the finger. Light emitted by the LEDs reflects off the finger (more precisely, the hemoglobin) and returns to the detector.<\/p>\n\n\n\n<p>Blood oxygen saturation is measured thanks to the absorption of light by hemoglobin. Poorly oxygenated hemoglobin absorbs light at a wavelength of about 660 nm, while highly oxygenated hemoglobin absorbs more light at a wavelength of 940 nm. The LEDs built into the <a href=\"https:\/\/sklep.msalamon.pl\/?s=max30102&amp;post_type=product&amp;utm_source=blog&amp;utm_medium=article&amp;utm_campaign=max30102&amp;utm_content=Text\">MAX30102<\/a> have wavelengths of 660 nm and 880 nm. Although they don\u2019t hit the IR wavelength perfectly, it still works correctly. The oxygenation level is determined using the ratio of the light transmission for these two wavelengths.<\/p>\n\n\n\n<p>The waveform of the reflected light has the pulse wave as its main component, from which it\u2019s easy to read the pulse. Below I\u2019ve included a plot for the red LED, captured from my finger using the sensor in question.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"1576\" height=\"828\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples.jpg\" alt=\"\" class=\"wp-image-723\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples.jpg 1576w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples-300x158.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples-1024x538.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples-768x403.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples-1536x807.jpg 1536w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples-24x13.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples-36x19.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_samples-152x80.jpg 152w\" sizes=\"auto, (max-width: 1576px) 100vw, 1576px\" \/><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Diagram and CubeMX<\/h2>\n\n\n\n<p>Today I\u2019ll use a <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/nucleo-f401re\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=dlaczegostm32&amp;utm_content=Text\">Nucleo with the STM32F401RE<\/a>.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><a href=\"https:\/\/sklep.msalamon.pl\/kategoria-produktu\/dev-boardy\/stm32-nucleo\/?utm_source=blog&amp;utm_medium=banner&amp;utm_campaign=max30102&amp;utm_content=nucleo\"><img loading=\"lazy\" decoding=\"async\" width=\"1200\" height=\"400\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg\" alt=\"\" class=\"wp-image-1593\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner.jpg 1200w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-300x100.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-1024x341.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2020\/07\/Nucleo-64-baner-768x256.jpg 768w\" sizes=\"auto, (max-width: 1200px) 100vw, 1200px\" \/><\/a><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>The sensor wiring diagram is simple thanks to the complete module we buy. Unfortunately, the pull-ups on the module are connected to 1.8 V, which doesn\u2019t quite want to cooperate with an STM32 running at 3.3 V \u2014 at least not with my unit. That\u2019s why I added external pull-ups to the module\u2019s supply voltage, and configured the I2C and INT pins in the microcontroller as&nbsp;<em>No pull-up and no pull-down.<\/em><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"1300\" height=\"680\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_schematic.jpg\" alt=\"\" class=\"wp-image-729\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_schematic.jpg 1300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_schematic-300x157.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_schematic-1024x536.jpg 1024w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_schematic-768x402.jpg 768w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_schematic-24x13.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_schematic-36x19.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_schematic-153x80.jpg 153w\" sizes=\"auto, (max-width: 1300px) 100vw, 1300px\" \/><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>Set I2C to 400 kHz right away.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"736\" height=\"364\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_i2c_settings.jpg\" alt=\"\" class=\"wp-image-725\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_i2c_settings.jpg 736w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_i2c_settings-300x148.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_i2c_settings-24x12.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_i2c_settings-36x18.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_i2c_settings-160x80.jpg 160w\" sizes=\"auto, (max-width: 736px) 100vw, 736px\" \/><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>You also need an interrupt input reacting to a falling edge. Make sure Cube has enabled this interrupt during initialization. Sometimes it\u2019s a pain. Preempting the comments \u2013 yes, yes, HAL is the worst in the world because of that \ud83d\ude42<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"634\" height=\"146\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_int_settings.jpg\" alt=\"\" class=\"wp-image-726\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_int_settings.jpg 634w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_int_settings-300x69.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_int_settings-24x6.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_int_settings-36x8.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_int_settings-160x37.jpg 160w\" sizes=\"auto, (max-width: 634px) 100vw, 634px\" \/><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Library<\/h2>\n\n\n\n<p>Let\u2019s move on to the most interesting part \u2014 how to use our sensor. Maxim provides example code on its site for Arduino and mbed. While the peak search algorithm, converting them to heart rate, and the algorithm calculating blood oxygenation are fine, the sample collection example is poor.<\/p>\n\n\n\n<p>Unfortunately, the main example\u2019s loop is dominated by waiting for an interrupt from the sensor. Then samples are fetched and processed. After first collecting 500, and then every 100 samples, the algorithm is run. In my opinion, the samples are needlessly shifted left after the calculations. A microcontroller should not sit and wait for an interrupt on purpose! In addition, the LEDs are always at full power, whether a finger is placed on the sensor or not. What a waste of current\u2026<\/p>\n\n\n\n<p>The example also won\u2019t work with any configuration other than 100 samples per second. I fixed that.<\/p>\n\n\n\n<p>I based my library on a simple state machine with four states, while samples are continuously collected in the interrupt. Samples go into a ring buffer, so you don\u2019t need to shift them after every algorithm run. What happens in these wild machine states?<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><strong>MAX30102_STATE_BEGIN<\/strong>. The finger is not placed on the sensor. The red LED is off, IR at minimum. The IR LED is used to detect a finger on the sensor. Whenever the finger is removed from the sensor, the program returns to this state and turns off the LEDs.<\/li>\n\n\n\n<li><strong>MAX30102_STATE_CALIBRATE<\/strong>. After placing the finger, the buffer needs to be filled with \u2018valid data\u2019. By default, calculations are performed on samples from the last 5 seconds. Calibration, i.e., filling the buffer, will therefore take that long.<\/li>\n\n\n\n<li><strong>MAX30102_STATE_CALCULATE_HR<\/strong>. After collecting a sufficient number of samples, calculations are performed according to Maxim\u2019s algorithm. In this step, the ring buffer is advanced by the number of samples corresponding to one second.<\/li>\n\n\n\n<li><strong>MAX30102_STATE_COLLECT_NEXT_PORTION<\/strong>. In this state, the sensor handler remains until the next portion of samples (i.e., from the next second) is collected. After that it goes to state number 3 and the cycle repeats until the finger is removed from the sensor.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Using the library<\/h3>\n\n\n\n<p>I wrote the library to make it as simple to use as possible. It all boils down to a few steps. First, initialize the library and the sensor by passing which I2C interface is used.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">MAX30102_STATUS Max30102_Init(I2C_HandleTypeDef *i2c);\n<\/pre>\n\n\n\n<p>The next important point is interrupt handling. You need to override HAL\u2019s built-in weak function.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\/* USER CODE BEGIN 4 *\/\nvoid HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)\n{\n\tif (GPIO_Pin == INT_Pin)\n\t{\n\t\tMax30102_InterruptCallback();\n\t}\n}\n&lt;!-- USER CODE END 4 --><\/pre>\n\n\n\n<p>The last requirement is to put the function responsible for handling the state machine into the main loop.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">void Max30102_Task(void);<\/pre>\n\n\n\n<p>Now you can read the current heart rate and oxygen saturation values from the library using two functions.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">int32_t Max30102_GetHeartRate(void);\nint32_t Max30102_GetSpO2Value(void);<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Additional configuration<\/h3>\n\n\n\n<p>By default, the library and sensor are configured for a 5-second measurement and 100 samples per second. You can modify this freely. The library will automatically adjust buffer sizes for the samples and adapt the calculations.<\/p>\n\n\n\n<p>Additionally, there\u2019s a setting for the number of samples to trigger the <em>FIFO<\/em>&nbsp;<em>Almost Full<\/em> interrupt. If for some reason samples aren\u2019t being received in time, they will be queued in the sensor. Queueing the configured number of samples will trigger the corresponding interrupt. Upon detecting this interrupt, the library will read all available samples in the buffer, clearing it at the same time. The buffer can hold up to 32 samples. The default value of 17 is the minimum that triggers the interrupt. For me this is sufficient, though I haven\u2019t tested the sensor in a real battle. Perhaps in a more complex application the sensor will perform better with a higher value.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"c\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">#define MAX30102_MEASUREMENT_SECONDS 5\n#define MAX30102_SAMPLES_PER_SECOND 100 \/\/ 50, 100, 200, 400, 800, 100, 1600, 3200 sample rating\n#define MAX30102_FIFO_ALMOST_FULL_SAMPLES 17<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Demonstration<\/h2>\n\n\n\n<p>My example code prints the calculated values to the terminal.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter\"><img loading=\"lazy\" decoding=\"async\" width=\"660\" height=\"414\" src=\"http:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_measurement_terminal.jpg\" alt=\"\" class=\"wp-image-728\" srcset=\"https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_measurement_terminal.jpg 660w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_measurement_terminal-300x188.jpg 300w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_measurement_terminal-24x15.jpg 24w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_measurement_terminal-36x23.jpg 36w, https:\/\/msalamon.pl\/wp-content\/uploads\/2019\/03\/max30102_measurement_terminal-128x80.jpg 128w\" sizes=\"auto, (max-width: 660px) 100vw, 660px\" \/><\/figure>\n<\/div>\n\n\n<p><\/p>\n\n\n\n<p>Pretty low heart rate for sitting and typing code. And I thought it was a stressful job \ud83d\ude42 Also see how the LEDs are automatically turned on and off depending on whether a finger is placed on the sensor.<\/p>\n\n\n\n<p><iframe width=\"750\" height=\"422\" loading=\"lazy\" title=\"MAX30102 pulsometer auto LED dimmimg\" src=\"https:\/\/www.youtube.com\/embed\/um9pxZxYW9E?feature=oembed&amp;enablejsapi=1&amp;origin=http:\/\/msalamon.pl\" frameborder=\"0\" allow=\"accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture\" allowfullscreen=\"allowfullscreen\"><\/iframe><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary<\/h2>\n\n\n\n<p><a href=\"https:\/\/sklep.msalamon.pl\/?s=max30102&amp;post_type=product&amp;utm_source=blog&amp;utm_medium=article&amp;utm_campaign=max30102&amp;utm_content=Text\">Maxim\u2019s pulse oximeter<\/a> is a very interesting device. It works correctly, although it will sometimes \u201cstutter,\u201d jumping with the computed heart rate. I\u2019m not great at signal processing \u2014 I got a 3 in the lab \ud83d\ude41 If someone could take a look at the algorithm code and improve it, I\u2019d be immensely grateful! On the other hand, my smartphone behaves similarly, so perhaps it\u2019s not the fault of a lame algorithm or my program.<\/p>\n\n\n\n<p>Wearables today can measure heart rate at the wrist. Unfortunately, this sensor doesn\u2019t do well in that location. I dug into a bit of theory and for wrist measurements one more, green LED is recommended. You can see this e.g. in the fitness bands or running watches mentioned at the beginning. I think this sensor \u2014 just like on a phone \u2014 can be treated more as a curiosity than a precise measuring instrument. Continuous finger measurement might only be useful in a hospital. I don\u2019t see much use for myself. Maybe you do? <strong>Share it in the comments!<\/strong><\/p>\n\n\n\n<p>The full project along with the library can be found, as usual, on my GitHub: <a href=\"https:\/\/github.com\/lamik\/MAX30102_STM32_HAL\" target=\"_blank\" rel=\"noopener\">link<\/a><\/p>\n\n\n\n<p><span>If you\u2019ve noticed any mistake, disagree with something, would like to add something important, or just feel like discussing this topic, write a comment. Remember that the discussion should be polite and in accordance with the rules of the Polish language.<\/span><\/p>\n\n\n\n<p>If you liked the sensor, you can get it in <a href=\"https:\/\/sklep.msalamon.pl\/produkt\/pulsometr-max30102\/?utm_source=blog&amp;utm_medium=article&amp;utm_campaign=max30102&amp;utm_content=Text\">my store<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Contest results from the previous post<\/h2>\n\n\n\n<p>Recently I <a href=\"http:\/\/msalamon.pl\/przeswietlam-ksiazke-mikrokontrolery-stm32-dla-poczatkujacych\/\">reviewed<\/a> the book \u201cMikrokontrolery STM32 dla pocz\u0105tkuj\u0105cych,\u201d which is based on the HAL libraries I use. On this occasion I organized a contest in which 4 sets of the book + KA-NUCLEO-F411CEv2 evaluation board were up for grabs. Without further ado, here are the winners I selected:<\/p>\n\n\n\n<p>1.<strong> Szymon Fronc <\/strong>with the comment:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>A review just as it should be \u2014 concise and to the point \u2014 building appetite and willingness to embark on an adventure with something that for someone like me is a further step in playing with microcontrollers. I think STMs can broaden horizons (something new beyond the Arduino I\u2019ve used so far), which I would gladly try if I managed to snag such a set for myself. Time to enter the contest&nbsp;<img decoding=\"async\" draggable=\"false\" class=\"emoji\" alt=\"?\" src=\"https:\/\/s.w.org\/images\/core\/emoji\/11.2.0\/svg\/1f600.svg\">&nbsp;Best regards and I\u2019m waiting for more interesting reviews!<\/em><\/p>\n<\/blockquote>\n\n\n\n<p>2. <strong>&nbsp;Mateusz <\/strong>with the comment:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>A good, factual review that primarily encourages the \u201cfreshmen gang\u201d to start their adventure with STM microcontrollers and frankly discourages more experienced programmers from the book. I myself have been into AVR family microcontrollers for a long time and I must admit I\u2019ve been approaching STMs like a dog approaches a hedgehog! It\u2019s great that there are people who infect others with their passion. Thank you and best regards!<\/em><\/p>\n<\/blockquote>\n\n\n\n<p>3.&nbsp;<strong>Franek<\/strong> with the comment:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>A good review, it\u2019s clear the author is actually experienced in the subject and is interested in the book. Also a big plus that it\u2019s an honest review showing the book from the perspective of its flaws and explicitly stating who its audience is.<\/em><\/p>\n<\/blockquote>\n\n\n\n<p>4. <strong>Norbert&nbsp;<\/strong>with the comment:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p><em>The review certainly encouraged me to take part in the contest; perhaps it will translate into approaching the \u201cmonster\u201d that is the vast capabilities of STM32 microcontrollers. If you present a cross-section starting from beginner proposals to the more advanced \u2014 I\u2019m all in for such publications.<\/em><\/p>\n<\/blockquote>\n\n\n\n<p><strong>Congratulations!<\/strong> I\u2019ll contact you by email to finalize the contest. Cheers!<\/p>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;4327&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;0&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;0&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;0&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;0\\\/5 - (0 votes)&quot;,&quot;size&quot;:&quot;24&quot;,&quot;title&quot;:&quot;My Finger Is Pulsing!!! The MAX30102 Pulse Oximeter Under STM32 Control.&quot;,&quot;width&quot;:&quot;0&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} - ({count} {votes})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 0px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 0px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 24px; height: 24px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 19.2px;\">\n            <span class=\"kksr-muted\"><\/span>\n    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>Probably every one of my readers&nbsp;has dealt with some wearable device, the so-called&nbsp;wearables. Even the simplest fitness bands have a built-in heart rate sensor that most often measures at the user\u2019s wrist. It turns out this isn\u2019t some mysterious technology available only to a closed group of consumer electronics manufacturers. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":3149,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":"","_links_to":"","_links_to_target":""},"categories":[160],"tags":[175,176,174,177],"class_list":["post-4327","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-stm32","tag-electronics","tag-programming","tag-stm32","tag-stm32cubemx"],"_links":{"self":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4327","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/comments?post=4327"}],"version-history":[{"count":3,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4327\/revisions"}],"predecessor-version":[{"id":4436,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/posts\/4327\/revisions\/4436"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media\/3149"}],"wp:attachment":[{"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/media?parent=4327"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/categories?post=4327"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/msalamon.pl\/en\/wp-json\/wp\/v2\/tags?post=4327"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}