Overview

When designing the Circuit Playground Express, Ladyada purposely placed the light sensor near the NeoPixel LEDs so that the light from the LED may illuminate a finger and the sensor measure the minute changes in light signalling someone's pulse. While not medically accurate, the project is easy to do and demonstrates several concepts without a bunch of specialized materials.

This tutorial will show you how to set up the Circuit Playground Express and will display the heart rate via a NeoPixel.

Products

Circuit Playground Express

PRODUCT ID: 3333
Circuit Playground Express is the next step towards a perfect introduction to electronics and programming. We've taken the original Circuit Playground Classic and...
$24.95
IN STOCK

USB cable - USB A to Micro-B

PRODUCT ID: 592
This here is your standard A to micro-B USB cable, for USB 1.1 or 2.0. Perfect for connecting a PC to your Metro, Feather, Raspberry Pi or other dev-board or...
$2.95
IN STOCK

How It Works

The detection of your pulse via your finger via light is called Photoplethysmogram.

Light shines onto the finger and a light detector measures the light that returns. 

We use green light, which is absorbed by red blood. The redder the blood, the more green light is absorbed. The blood pumping through one's finger will change the absorption of the light. Not by much but enough to measure. These variations can be plotted over time to see the changes.

The readings will also vary by ones breathing and if the finger shifts while taking readings. So make sure you keep your finger still, to avoid unintended light changes.

Often times you'll see blood oxygen clips like these, they keep the finger in place by pressure, and have the LED and sensor at the finger tip:

light_kisspng-pulse-oximetry-finger-pulse-oximeters-blood-monito-pulse-5b10f2f1098833.6571124015278374250391.png
A commercial pulse oximeter, royalty free photo via kisspng

CircuitPython

CircuitPython is being used below. Are you new to using CircuitPython? No worries, there is a full getting started guide here.

Adafruit suggests using the Mu editor to edit your code, print & plot your output, and have an interactive REPL in CircuitPython. You can learn about Mu and its installation in this tutorial.

A Basic Plot Program

The code below takes light readings and plots them out in the plotter function in Mu.

Now open the Plotter window and the Serial window in Mu.

Upload the code below as code.py to the CIRCUITPY flash drive that appears when you plug your Circuit Playground Express into a USB port. 

Download: file
import time

import analogio
import board
import neopixel

def sign(value):
    if value > 0:
        return 1
    if value < 0:
        return -1
    return 0

pixels = neopixel.NeoPixel(board.NEOPIXEL, 10, brightness=1.0)
light = analogio.AnalogIn(board.LIGHT)

# Turn only pixel #1 green
pixels[1] = (0, 255, 0)

# How many light readings per sample
NUM_OVERSAMPLE = 10
# How many samples we take to calculate 'average'
NUM_SAMPLES = 20
samples = [0] * NUM_SAMPLES
lasttime = time.monotonic()

while True:
    for i in range(NUM_SAMPLES):
        # Take NUM_OVERSAMPLE number of readings really fast
        oversample = 0
        for s in range(NUM_OVERSAMPLE):
            oversample += float(light.value)
        # and save the average from the oversamples
        samples[i] = oversample / NUM_OVERSAMPLE  # Find the average

        mean = sum(samples) / float(len(samples))  # take the average
        print((samples[i] - mean,))  # 'center' the reading

        if i > 0:
            # If the sign of the data has changed munus to plus
            # we have one full waveform (2 zero crossings), pulse LED
            if sign(samples[i]-mean) <= 0 and sign(samples[i-1]-mean) > 0:
                pixels[9] = (200, 0, 0)  # Pulse LED
            else:
                pixels[9] = (0, 0, 0)    # Turn LED off

        time.sleep(0.025)  # change to go faster/slower

Place your finger lightly but firmly on the green LED, parallel to the USB cable to you cover the green NeoPixel and the light sensor (next to the eye symbol on the Circuit Playground Express).

The code will start to average the readings over time to come up with good data.

The best readings show a waveform in the Mu Plotter where the readings are varying between approximately 1000 and -1000.

And you should have a screen similar to the one below.

It takes a bit of time to get the reading the right amplitude and for the CircuitPython program to scale the reading centered around zero (this is called "removing the DC offset" in engineering terms).

It doesn't look like your traditional 'heart rate' blip, that's normal! It can look sinusoidal or even square-ish

If the numbers range are too small you are probably pressing to hard. If the numbers are too large or you don't have a varying plot, you may be pressing too lightly. Try different pressures to get numbers and a plot similar to above. The trick is to keep your finger steady, covering both the green NeoPixel and the light sensor, for a few seconds.

With the waveform "dialed in" to be around +1000 to -1000 on the Mu plotter, the blinking of the red NeoPixel LED #9 should be indicative of your pulse. The code checks every time the signal transitions from negative to positive, indicating one full cycle of the heartbeat and blinks the LED at that point.

To get your heart rate in beats per minute you can:

  • Time 15 seconds, count the number of red LED flashes and multiply by 4
  • Time 60 seconds (1 minute) and count the red LED flashes.

The number you get is your heart rate (more or less). Commercial units and doctors & nurses can determine this more accurately but your number should be close, within less than 10%.

What is Happening?

The amount of light hitting the photo sensor on the Circuit Playground Express is changing based on the amount of blood in your finger. As your heart pumps, the blood flow changes and you can see those changes in the plot as changes in light intensity.

This guide was first published on Feb 20, 2019. It was last updated on Feb 20, 2019.