Create a Simple Pulse Oximeter with EFM32 Tiny Gecko

Hack-a-Gecko project by Pål Øyvind

Explore the capabilities of the EFM32 Tiny Gecko by creating the beginnings of a DIY pulse oximeter.

The theory behind

A pulse oximeter is based around a simple principle: Shine red light and an infrared light through a blood-rich part of the body and measure the difference in absorbed light with a photo sensor (Figure 1). Hemoglobin absorbs red and infrared light differently depending on the oxygen saturation level (Figure 2). Based on this, it is possible to calculate the ratio of oxygenated vs. deoxygenated hemoglobin (Equation 1). For a more in-depth description of the measurement principle and issues, this page on explains it very thoroughly.

Figure 1 Pulse oximeter sensor principle 

Figure 2 Light absorption rates differ between oxygenated and deoxygenated blood 

Equation 1 Oxygenation ratio calculation 

Hardware Implementation

First, I made a very simple LED/sensor harness by wiring up the LEDs and the sensor and putting it all into some leftover black Velcro I had lying around (Figure 3). The Velcro was very helpful in being able to adjust the alignment of the sensor and LEDs and making a good fit for a human finger. In addition, it worked well to block out match of the ambient light.

Figure 3 Velcro based Hack-a-Pulse oximeter sensor harness

I wired up the sensor to a prototyping board containing interfacing the STK (Figure 4).

Figure 4 Interface to STK

The prototype PCB contained only simple external components:

  • RI, R2, Q0, Q1: Drivers for the red and infrared LEDs. (Figure 5)
  • R4, Q2, R3: Constant current drive circuitry. When it is working, OPA0 and Q2 will make sure the voltage across R3 is constant (and thereby the current flowing in the LEDs). The level can also be programmed by the DAC. As I did not have enough time to program the control of the LED current, it is currently limited by the bypass resistor R5 that is hacked in. (Figure 5)
  • R0, C0: Together with OPA2 on the EFM32 Tiny Gecko, this creates a transimpedance amplifier/high-pass filter with a cut-off set by R0*C0 and again set by R0. This converts the photodiode current into a voltage suitable for readout by ADC0. (Figure 6)

Figure 5 LED drive and constant current schematic 

Figure 6 Photo sensor schematic 

In order to read the data from the ADC back to a computer, I used a FTDI USB device connected to USART1 TX on PD0.

Software Implementation

The software tools by Energy Micro made this implementation very simple. It is loosely based on the opamp_to_ADC example from the app note “AN0038 Operational Amplifiers” and USART from “AN008 USART Synchronous Mode.”
The software data allowed me to:

  • Initialize the chip and clocks
  • Configure the hardware and I/O as shown in Figure 4 and Figure 5
  • Set up the USART communication
  • Turn on the red LED
  • Go into an infinite loop
    1. Sampling the ADC
    2. Displaying the readout voltage on the LCD
    3. Sending it via the USART

The PC-side software is based on a stripped-down version of a Patient Monitor demo from Qt to display the results on a graph. I compiled the incoming data in a version of QextSerialPort to grab the data from the COM port that I got when the FTDI driver was installed.

A simple band-pass data filter was also implemented to filter out the DC from the signal and removing high-frequency noise. The results were quite satisfying. (It’s always good to get confirmation that I’m alive :)). This is a screenshot of the PC application output when I wore the sensor on my finger:

Figure 7 Screenshot of PC application displaying a live pulse graph 

Suggested steps to complete the pulse oximeter function:

For those who want to try this project, here are my suggestions for you:

  • Finish the software for the control of the LED current and remove the bypass hack.
  • Add duty cycling between red and infrared LEDs. For now, this has not been implemented, so the oxygenation levels can’t be calculated quite yet. Part of the reason was that the IR LED I used turned out to be a lot brighter than the red LED, and blinded the sensor. I expect that current control should deal with this and make duty-cycling possible.
  • Move the data filtering and interpretation to the Tiny Gecko and display pulse and oxygenation levels on the STK LCD.

Materials Used

  • EFM32 Tiny Gecko STK
  • FTDI 3.3V breakout module from Sparkfun
  • Everlight 383SDRC/S530-A3/H2 red LED
  • Everlight IR383 IR LED
  • Osram BPW34 photodiode 850nm
  • Q0, Q1: BC557 PNP transistors
  • Q2: BC547 NPN transistor
  • R0: 2Mohm
  • C0: 10nF
  • R1, R2, R4: 1kohm
  • R5: 42ohm
  • R3: 6.8ohm
  • cables, prototyping PCB etc
  • Source Files

Source Files

Source code for both the PC application and the EFM32 Tiny Gecko are found here:

This Hack a Gecko project is a result of a “fun hacking session” and are provided as is, free of charge with no guarantees or support from Energy Micro, to partially or fully show and demonstrate EFM32 Gecko microcontroller capabilities. Get inspired, use at own risk, and build some awesome and cool applications.


For technical questions about this project, please use our support forum. The Hack-a-Gecko team will answer your questions directly.

14 thoughts on “Create a Simple Pulse Oximeter with EFM32 Tiny Gecko

  1. Pingback: Pulse oximeter displays blood oxygen levels on a PC

  2. Hi, Pingback

    Thank you for sharing the link with us. We are glad that the project is posted on another site. It would have been even better if the author´s name (Pål Øyvind, not Anders) was correct 😉

  3. Pingback: Single Wavelength Pulse Oximeter »

  4. Hello,

    We can not build the PC application due to missing Qt basic graph etc..
    Is it possible to help us finding all needed code/source/libraries needed to build this project ?

  5. Dear,

    I have two questions about you project and shown results.

    1) We have tryed to compile the available PC code but could not find the needed Qt caled libraries.
    Looking for them on the web resulted in a compiled program that end with a promt ‘unable to execute’
    2) I’m a little bit impressed about the graph shown since usually the AC part of the signal is much less than the DC part.
    How many bits of the AD converter are effectively used in your setup for the AC/DC part of the signal ?

    Hoping to receive some reaction, I remain,


  6. Hi Patrick, thanks for your comments.

    When it comes to getting the Qt libraries to compile, you are absolutely right, there were two parts from the Embedded Widgets demo that I forgot to include in the original upload. I have uploaded those (and a corresponding update to the .pro file) to github now, that should help if you try again. 🙂 If you have further issues with your Qt compile you can also head over to and ask your specific questions there, I’m sure you’ll get help to resolve it quickly.

    About the AC vs DC levels, you are right that the AC signal from the sensor is much smaller than the DC signal. That is where the high-pass filtering and gain provided by R0, C0 and OPA2 comes in, basically filtering out much of the DC component and amplifying the AC component to a suitable level for the ADC. I don’t remember exactly how many bits are effectively being used, but from memory it was at least 7-8 out of 12 bits. I also made some very simple filtering in the software post-processing to further center the signal in the plot window, but this can be vastly improved, I think.

    Hope this helps! 🙂

    • Patrick,
      the IAR embedded workbench Kickstart edition which are included in the Tiny gecko STK has a 32 KB code limit, which is also the maximum flash size for the Tiny Gecko.

      One thing to be aware of is that this project was made before CMSIS V3 was released, so you need to change the include files and definitions of those to be called em_xxx instead of efm32_xxx

      Best regards,

  7. Awesome tutorial! I would like to use your example as a starting point to port this to Arduino and Raspberry Pi which I have, and would like to make a pulse ox. I have some basic questions about the voltage/current and driving the LEDs on my RS232-interfaced pulse oximeter sensor, as I understand the leds are driven in bursts coupled with the sensor read, and they are back-to-back so you reverse voltage to drive one led, then the other, then you read also when no led to get ambient light background to subtract? Looking forward to getting this working with my hardware.

    • Thanks Edy, and happy New Year! I am not familiar with the off-the-shelf sensors, but you are probably right that it’s done that way for many of them. “Charlie-plexing” the diodes that way should work and is an efficient driving scheme in terms of wiring (you don’t need a ground wire for the LEDs anymore).
      Good luck in getting your pulse oximeter up and running!

  8. Pingback: Window of Andrew, JEON

Comments are closed.