Hackster will be offline on Monday, June 15 from 5pm to 7pm PDT to perform some scheduled maintenance.
ProjectsDIY
Published

Wearable Heart Rate Monitoring Device

In this session, we will build a wearable Arduino heart rate monitor to measure the heart rate of a remote patient.

BeginnerFull instructions provided2,236
Wearable Heart Rate Monitoring Device

Things used in this project

Hardware components

Arduino nano R3
×1
nrf24 module
×1
NeoPixel Ring: WS2812 5050 RGB LED
Adafruit NeoPixel Ring: WS2812 5050 RGB LED
×1
Li-Po Battery
×1

Story

Read more

Schematics

Arduino based Wearable Heartbeat Monitoring System Schematic

Code

Arduino based Wearable Heartbeat Monitoring System Source Code

C/C++
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
  #include <avr/power.h>
#endif

#include <SPI.h>
#include <RF24.h>
#include <RF24_config.h>
#include <nRF24L01.h>

int rf_cen = 9; //nRF24 chip enable pin
int rf_cs = 8; //nRF24 CS pin

RF24 rf(rf_cen, rf_cs);
//pipe address - hardcoded on uECG side
uint8_t pipe_rx[8] = {0x0E, 0xE6, 0x0D, 0xA7, 0, 0, 0, 0};

// Which pin on the Arduino is connected to the NeoPixels?
#define PIN            5
// How many NeoPixels are attached to the Arduino?
#define NUMPIXELS      16

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

uint8_t  swapbits(uint8_t a){ //uECG pipe address uses swapped bits order
  // reverse the bit order in a single byte
    uint8_t v = 0;
    if(a & 0x80) v |= 0x01;
    if(a & 0x40) v |= 0x02;
    if(a & 0x20) v |= 0x04;
    if(a & 0x10) v |= 0x08;
    if(a & 0x08) v |= 0x10;
    if(a & 0x04) v |= 0x20;
    if(a & 0x02) v |= 0x40;
    if(a & 0x01) v |= 0x80;
    return v;
}

void setup() {
  pixels.begin(); // This initializes the NeoPixel library.

  for(int i=0;i<NUMPIXELS;i++){
     pixels.setPixelColor(i, pixels.Color(1,1,1));
  }
  pixels.show();

  //nRF24 requires relatively slow SPI, probably would work at 2MHz too
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.beginTransaction(SPISettings(1000000, MSBFIRST, SPI_MODE0));

  for(int x = 0; x < 8; x++) //nRF24 and uECG have different bit order for pipe address
    pipe_rx[x] = swapbits(pipe_rx[x]);

  //configure radio parameters
  rf.begin();
  rf.setDataRate(RF24_1MBPS);
  rf.setAddressWidth(4);
  rf.setChannel(22);
  rf.setRetries(0, 0);
  rf.setAutoAck(0);
  rf.disableDynamicPayloads();
  rf.setPayloadSize(32);
  rf.openReadingPipe(0, pipe_rx);
  rf.setCRCLength(RF24_CRC_DISABLED);
  rf.disableCRC();
  rf.startListening(); //listen for uECG data
  //Note that uECG should be switched into raw data mode (via long button press)
  //in order to send compatible packets, by default it sends data in BLE mode
  //which cannot be received by nRF24
}

long last_pix_upd = 0;
byte in_pack[32];
int rf_bpm = 0;
int bpm_hist[5]; //since we disabled CRC, need to filter incoming data

void loop() 
{
  if(rf.available())
  {
    rf.read(in_pack, 32);
    int bb = in_pack[5]; //BPM is located at the 5th byte of the packet
    //detailed packet structure is in uECG docs

    //since we have no CRC for compatibility reasons, we need to filter
    //incoming data, radio channel could be noisy. We compare last 5
    //received BPM values and use one only if all 5 were the same.
    //Since uECG sends about 100 packets per second, it won't cause
    //any noticeable delay in displaying data
    for(int n = 0; n < 5-1; n++) //shift bpm history array by 1
      bpm_hist[n] = bpm_hist[n+1];
    bpm_hist[4] = bb; //add new bpm value
    for(int n = 0; n < 5; n++) //check if all are equal
      if(bpm_hist[n] != bb) bb = -1;
    if(bb > 0) //if yes - store it as new received BPM
      rf_bpm = bb;
  }  
  long ms = millis();
  if(ms - last_pix_upd > 10) //don't update pixels too often
  {
    int r, g, b;
    last_pix_upd = ms;
    int bpm = rf_bpm;
    int max_bright = 160; //value of maximum brightness, max 255. But you don't always want it at max :)
    float dd = 25; //change in BPM between color tones (blue->green->yellow->pink->red)
    float t1 = 90, t2, t3, t4; //t1 - "base" BPM, lower than t1 would be blue
    t2 = t1 + dd;
    t3 = t2 + dd;
    t4 = t3 + dd;
    //code for changing color depending in which t1...t4 range we are now
    if(bpm < t1){ r = 0; g = 0; b = max_bright; }
    else if(bpm < t2) { r = 0; g = max_bright * (bpm-t1)/dd; b = max_bright - g; }
    else if(bpm < t3) { r = max_bright * (bpm-t2)/dd; g = max_bright - r; b = r/4; }
    else if(bpm < t4) { r = max_bright; g = 0; b = max_bright/2 - max_bright * (bpm-t3)/(2*dd); }
    else {r = max_bright; g = 0; b = 0; }
    int on_pixels = (bpm-80)/8; //since it's intended for running, I'm not
    //showing anything less than 80 BPM, this way it's more sensitive in
    //high load area
    for(int i=0;i<NUMPIXELS;i++)
    {
      //pixels are set from last to first for no particular reason, would
      //work just as fine if set from first to last
      if(i < on_pixels) pixels.setPixelColor(NUMPIXELS-i-1, pixels.Color(r,g,b));
      else pixels.setPixelColor(NUMPIXELS-i-1, pixels.Color(0,0,0)); //turn off all other LEDs
    }
    pixels.show();
  }
}

Credits

ProjectsDIY
6 projects • 8 followers
Electronic projects, Arduino, Raspberry Pi, NodeMCU, Sensor, IoT based projects. Tutorial and instruction for all projects. DIY projects.

Comments