DIY Heartrate Monitor

Ever used an old fashioned heartrate monitor that had the band that goes around your torso? How could we receive the signal from one of those and display it on a simple display?

For this project, I used a polar heartrate band that I had knocking around. These bands transmit a 5.3kHz signal each time your heart rate beats. I have seen some designs for receivers online, but to simplify the project I found a company that make little receiver modules you can buy.

Kyto fitness technology https://kytofitness.com/collections/heart-rate-monitor has a little module that does just the thing. The KYTO2800A has only 4 pins:

PIN DESCRIPTION

Pin Name    I/O Function
1 GND —— Power Ground
2 VCC —— Power Supply voltage
3 EN    —— Power enable (Active Low)
4 OUT —— Data output (Active High)

Kyto Fitness KYTO2800A heartbeat signal receiver.

The spec says it operates for a range of 2.4V ~ 3.0V, but the remarks below state the input voltage is 3.3, so I ventured to powering it from the 3.3V supply of the little development board I decided to use, the WEMOS D1-mini.

Output from the module

The output is a 3.3 V positive polarity pulse that lasts around 20 mS every time there is a heart beat signal from the polar band detected. By counting the time between the pulses, we can work out the period of the heartrate, and from that calculate the frequency, or more usefully, the beats per minute.

WEMOS D1-mini & OLED

The WEMOS D1-mini is an ESP8266 based board, and there are many modules or shields available for it. For this project, I chose the little OLED shield, so that I could display the heartrate on it. I selected the ESP8266, so that I could also create connectivity to enable data to be sent to a server if I wished.

The back of the WEMOS D1-mini showing the wires that connect it to the heartbeat signal receiver.
View of the OLED shield mounted on the WEMOS D1-mini

Hooking the two up is very easy, the ground on each board is connected together. The 3.3 V of the WEMOS module is connected to the Vcc of the Kyto module. The out from the module is connected to D0, so we can detect when there is a heartbeat. The EN pin on the module needs to be pulled to GND to enable the module. This can be achieved with a resistor, any value from 300 ohms up to several kilo ohms would do. I think I used a 1k resistor.

The schematic

Coding up

I used Arduino IDE to program the WEMOS D1 mini board. The code is given below.

/*
  Heartbeat ESP8266
  ------
  This example code uses WEMOS D1 mini, with an OLED display shield
  connected to Kyto2800A heartbeat module.
  Author: Jonathan Rodgers
*/
#include <Arduino.h>
#if defined(ESP8266)
  #include <ESP8266WiFi.h>
  #include <ESPAsyncTCP.h>
#elif defined(ESP32)
  #include <WiFi.h>
  #include <AsyncTCP.h>
#endif
#include <ESPAsyncWebServer.h>
#include <WebSerial.h>


#include <Wire.h>
//#include <LOLIN_I2C_BUTTON.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define OLED_RESET -1
Adafruit_SSD1306 display(OLED_RESET);

#define PIN_HB D0

AsyncWebServer server(80);

const char* ssid = "yourSSID"; // Your WiFi SSID
const char* password = "yourPassword"; // Your WiFi Password



/* Message callback of WebSerial */
void recvMsg(uint8_t *data, size_t len){
  WebSerial.println("Received Data...");
  String d = "";
  for(int i=0; i < len; i++){
    d += char(data[i]);
  }
  WebSerial.println(d);
  Serial.println(d);

  if( d == "ledon" ){
    digitalWrite(LED_BUILTIN, LOW);  // Turn the LED on by making the voltage HIGH
    Serial.println("O");
  }
  if( d == "ledoff" ){
    digitalWrite(LED_BUILTIN, HIGH);  // Turn the LED off by making the voltage HIGH
    Serial.println("F");
  }
}

byte number = 0;

void setup() {
    //pinMode(LED_BUILTIN, OUTPUT);     // Initialize the LED_BUILTIN pin as an output
    pinMode(PIN_HB, INPUT);

    display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
    display.clearDisplay();
    display.setTextSize(2);
    display.setTextColor(WHITE);
    display.println(number);
    display.println();
    display.print("-   +");

    display.display();

    
    Serial.begin(115200);
    Serial.print("Serial debug started OK. Attempting to connect to wifi...");
    
    WiFi.mode(WIFI_STA);
    WiFi.begin(ssid, password);
    if (WiFi.waitForConnectResult() != WL_CONNECTED) {
        Serial.printf("WiFi Failed!\n");
        return;
    }
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP());
    // WebSerial is accessible at "<IP Address>/webserial" in browser
    WebSerial.begin(&server);
    /* Attach Message Callback */
    WebSerial.msgCallback(recvMsg);

    server.begin();
}


  unsigned long lastpulse=0;
  unsigned long thispulse=0;
  unsigned long pulsewidth;
  
void loop() {

  float rate;

  int buttonState;
  buttonState = digitalRead(PIN_HB);
  
  if (buttonState == HIGH) {
    lastpulse=thispulse;
    thispulse=millis();
    pulsewidth=thispulse-lastpulse;

    rate= (1000.0/(float)pulsewidth) * 60.0;
    
    Serial.print(lastpulse);
    Serial.print(" ");
    Serial.print(thispulse);
    Serial.print(" ");
    Serial.print(pulsewidth);
    Serial.print(" ");
    Serial.println(rate);


    display.setTextSize(2);
    display.setTextColor(WHITE);
    number = (byte)rate;
    
    display.clearDisplay();
    display.setCursor(0,0);
    display.println(number);
    display.println();
    display.print(".");
    display.display();
    delay(25);
    display.clearDisplay();
    display.setCursor(0,0);
    display.println(number);
    display.println();
    display.print("");
    display.display();
  } 
  
}

To further this idea, now that we have the heartrate, we could put in limit alarms, for example, to show you when you are exercising within a target zone. A log of your heartrate could be sent to a server, and a second my second log of your work outs could be logged and graphed later.

An especially cheeky thought I had was to combine it with a ‘tens’ machine, and when the beats per minute dropped too low during a workout, it could give you an electric shock to encourage you to get going again.

Leave a comment

Your email address will not be published. Required fields are marked *