Tue 16 March 2021

Bloodlight: Designing a Heart Rate Sensor with STM32, LEDs and Photodiode

In April of 2020, with COVID-19 on the rise, health monitoring became a hot topic in the tech industry, and Codethink decided it was a good time to further build on our existing medical/fitness story.

The Bloodlight project was designed to test for new health metrics in the blood by testing multiple LED wavelengths of light at high-frequency and accuracy. We hoped to process the signals to determine the most appropriate wavelengths for low-power sensing and find new potential data in the signals. Our holy-grail would be HbA1C detection for diabetes, but other metrics such as carbon monoxide inhalation, hydration and breathing rate are also possible.


PPG sensors work by shining light at or through tissue and measuring the light absorbed over time to provide a pulse waveform. Pulse oximeters use multiple wavelengths and compare the absorption to calculate an approximate pulse oxygen concentration.

At their simplest, a PPG sensor is an LED and a photodiode, with a mechanism to block light from travelling directly from the LED to the photodiode.

Revision 1: STM32F303

For our first hardware, we designed a small circuit board that housed 16 wavelengths of LED between 470nm and 1650nm and four photodiodes to cover the ranges efficiently.

Bloodlight hardware image

We selected the STM32F303 because we find the tooling and documentation around STM microcontrollers to be very clear, simple and compatible with Linux. It was also well supported by libopencm3, which is our preferred library.

Additionally, the STM32F303 supports OPAMPs, which allowed us to use an additional amplification stage, which was a good way to de-risk our trans-impedance amplifier (TIA) design. These were not supported by libopencm3, but were well documented in the datasheets, so we made a patch here >>

We were fortunate that our TIA design gave reasonable ranges, but the on-chip OPAMPs still allowed us to zoom in reasonably on the signal.

The design worked well, and with sufficient oversample (typically 512), we could read a strong pulse signal. Oversampling limits were ultimately limited by the CPU frequency, as oversampling for each ADC channel was done in software.

Image showing clear pulse with diastolic

Revision 2: STM32G474

We were pleased with the original design, but we wanted to improve on it significantly. Somewhat optimistically, we wanted to achieve a few orders of magnitude improvement in signal-strength and reduce the board’s size at the same time and for a similar cost.

OPAMPs: Offset & Scaling

The OPAMPs on the STM32G4 series offer an interesting feature; they allow the DAC output to be used as a DC offset for the incoming signal. Since most of our data is a small signal riding on a high DC offset, this feature allows us to scale the ADC values within the correct range. While this added an additional calibration stage, this feature alone allowed us to produce visible heartbeats without any oversampling required.

The improvement in signal quality varies per wavelength of light, but a reasonable estimation for the improvement of signal quality is between 4-64x.

Hardware Oversampling & Clock Frequency

The ADC's on STM32G4 provide hardware oversampling. While this is not a unique feature among microcontrollers, it significantly reduced our CPU overhead since we could get hardware oversample by 16x without any loss of accuracy and reduce the software oversample used by the same factor. Combined with the fact that the STM32G4 supports 170MHz compared to the 72MHz of the original, these features reduced CPU usage by a factor of 38.

Analog Improvements

In our first revision, we needed a large diode to ensure that the VDDA pin never provided more than 0.4V more in voltage than the VDD.

The STM32G4 separated VDDA from VREF and moved some potentially noisy components like the PLL out of the analogue domain, which provided an opportunity for the reduced component count and noise.

Our second revision has a dedicated voltage reference for the TIAs and analogue voltage reference and can power the analogue domain by simply filtering the main supply. This both simplified the board and offered the opportunity to reduce noise further.

Library Support While STM32G4 hardware provided numerous improvements, it was not yet supported by our favoured libopencm3 library, so we needed to contribute a patch series for it here >>

The patches required for STM32G4 were fairly significant, but the documentation was accurate, and most peripherals were backwards compatible.

Hardware Improvements

In addition to the improvements provided by the MCU upgrade, we also refined the LED voltages and TIA resistors based on results from our initial testing, which in some cases provided a signal strength improvement of more than 4x.

The two boards

The board size was reduced by about 1cm in each dimension.


We achieved a significant improvement in signal quality which allowed us to achieve a feat we'd considered a long-shot: We could record pulses at audio frequencies (48kHz+) with full resolution (16-bit).


This chart shows the improvements between revision 1 and revision 2. Both hardware revisions have the same photodiode and LED parts. With a 250Hz acquisition, overall accuracy was increased by 192 times when picking up a pulse from a reflection of 528nm green light and 124 times for 590nm orange light. For both revisions, acquisition configuration parameters were chosen to maximise the signal's strength up to the hardware's limits, including the use of software oversampling.

Not only could revision 2 record pulses at very high resolution and frequency, but it could also directly record audio from vibrating objects.

Bang in a table recorded using Bloodlight

Music recorded using Bloodlight

Source & Contributions

Our project is fully open-source, so please feel free to download, copy and contribute to both the hardware and software for this project.

Related blog posts:

Other Articles

Get in touch to find out how Codethink can help you

sales@codethink.co.uk +44 161 660 9930