Project tutorial

Arduino 101 Curie iOS Pulse Sensor © GPL3+

Arduino 101 and the his BLE Bluethooth feature connection for monitoring the pulse on nRF Toolbox.

  • 1,091 views
  • 0 comments
  • 5 respects

Components and supplies

Apps and online services

About this project

I want use the Genuino 101 by Intel and Arduino for a project that regards health, but I want to use all the characteristics of the Arduino 101. One of most important characteristics of this board, I think, is the BLE connection. Then I want to connect my phone with Arduino 101 for my Autogenic Training. I can monitor my pulse during the sessions. The monitoring is possible by use of the pulse sensor AMPED by pulsesensor.com. I have used many other sensors, but pulse sensor is the best for me! It's a really accurate and simple sensor. The hardware has only three wires: 5v, GND and SIGNAL. You can connect the signal to your Arduino 101. I use the 3.3v power because it is better for the Arduino 101 board. The board, in fact, also supports the 5v input, but it's better to use the 3.3v input.

The Hardware connections

The hardware connections are very simple. You must be connect the sensor GND to GND pin of Arduino 101, the sensor VIN to 3.3V pin on the Arduino 101 board, and Signal pin of pulse sensor to A0 pin on the Arduino 101 board.

The software

The software tools are libraries, code, and phone app for iOS or Android.

The Phone APP

You can read the value by using an app on your phone. The app that you can use with the BPM rate monitor is nRF app. This is a free app for iOS or Android.

Follow the link and download the app:

Pulse Sensor BPM

The first library is PulseSensorBPM.h by Bradford Needham, North Plains, Oregon, USA @bneedhamia, https://www.needhamia.com. Download the libraries by: https://github.com/bneedhamia/PulseSensorBPM.

Curie BLE

The second library is CurieBLE.h - official libraries of CurieBLE. Open the library manager on Arduino IDE, and search Curie BLE. Also, you must add the Arduino 101 board by clicking "Board Manager" and adding Arduino 101 board. I use a code from the Arduino official site (Arduino.cc) to connect the Arduino 101 to the phone. The code is really simple and intuitive. This code send A0 value to nRF app.

/* 
  Copyright (c) 2015 Intel Corporation.  All rights reserved. 
  This library is free software; you can redistribute it and/or 
  modify it under the terms of the GNU Lesser General Public 
  License as published by the Free Software Foundation; either 
  version 2.1 of the License, or (at your option) any later version. 
  This library is distributed in the hope that it will be useful, 
  but WITHOUT ANY WARRANTY; without even the implied warranty of 
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  Lesser General Public License for more details. 
  You should have received a copy of the GNU Lesser General Public 
  License along with this library; if not, write to the Free Software 
  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA 
*/ 
/* 
  This sketch example partially implements the standard Bluetooth Low-Energy Heart Rate service. 
  For more information: https://developer.bluetooth.org/gatt/services/Pages/ServicesHome.aspx 
*/ 
#include <CurieBle.h> 
BLEPeripheral blePeripheral;       // BLE Peripheral Device (the board you're programming) 
BLEService heartRateService("180D"); // BLE Heart Rate Service 
// BLE Heart Rate Measurement Characteristic" 
BLECharacteristic heartRateChar("2A37",  // standard 16-bit characteristic UUID 
   BLERead | BLENotify, 2);  // remote clients will be able to get notifications if this characteristic changes 
                             // the characteristic is 2 bytes long as the first field needs to be "Flags" as per BLE specifications 
                             // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml 
int oldHeartRate = 0;  // last heart rate reading from analog input 
long previousMillis = 0;  // last time the heart rate was checked, in ms 
void setup() { 
 Serial.begin(9600);    // initialize serial communication 
 pinMode(13, OUTPUT);   // initialize the LED on pin 13 to indicate when a central is connected 
 /* Set a local name for the BLE device 
    This name will appear in advertising packets 
    and can be used by remote devices to identify this BLE device 
    The name can be changed but maybe be truncated based on space left in advertisement packet */ 
 blePeripheral.setLocalName("HeartRateSketch"); 
 blePeripheral.setAdvertisedServiceUuid(heartRateService.uuid());  // add the service UUID 
 blePeripheral.addAttribute(heartRateService);   // Add the BLE Heart Rate service 
 blePeripheral.addAttribute(heartRateChar); // add the Heart Rate Measurement characteristic 
 /* Now activate the BLE device.  It will start continuously transmitting BLE 
    advertising packets and will be visible to remote BLE central devices 
    until it receives a new connection */ 
 blePeripheral.begin(); 
 Serial.println("Bluetooth device active, waiting for connections..."); 
} 
void loop() { 
 // listen for BLE peripherals to connect: 
 BLECentral central = blePeripheral.central(); 
 // if a central is connected to peripheral: 
 if (central) { 
   Serial.print("Connected to central: "); 
   // print the central's MAC address: 
   Serial.println(central.address()); 
   // turn on the LED to indicate the connection: 
   digitalWrite(13, HIGH); 
   // check the heart rate measurement every 200ms 
   // as long as the central is still connected: 
   while (central.connected()) { 
     long currentMillis = millis(); 
     // if 200ms have passed, check the heart rate measurement: 
     if (currentMillis - previousMillis >= 200) { 
       previousMillis = currentMillis; 
       updateHeartRate(); 
     } 
   } 
   // when the central disconnects, turn off the LED: 
   digitalWrite(13, LOW); 
   Serial.print("Disconnected from central: "); 
   Serial.println(central.address()); 
 } 
} 
void updateHeartRate() { 
 /* Read the current voltage level on the A0 analog input pin. 
    This is used here to simulate the heart rate's measurement. 
 */ 
 int heartRateMeasurement = analogRead(A0); 
 int heartRate = map(heartRateMeasurement, 0, 1023, 0, 100); 
 if (heartRate != oldHeartRate) {      // if the heart rate has changed 
   Serial.print("Heart Rate is now: "); // print it 
   Serial.println(heartRate); 
   const unsigned char heartRateCharArray[2] = { 0, (char)heartRate }; 
   heartRateChar.setValue(heartRateCharArray, 2);  // and update the heart rate measurement characteristic 
   oldHeartRate = heartRate;           // save the level for next comparison 
 } 
}

This code is good for testing your board connection. You can also send many kinds of values through the BLE connection.

The Code

The code for the project is below. Upload the code and connect the board to nRF Connect APP.

/* 
 Giovanni Gentile 
 February 2016 
 Arduino 101 Pulse sensor 
*/
#include <CurieBLE.h> 
#include <PulseSensorBPM.h> 
const boolean HAS_A_REF = false; //BUG? analogReference(EXTERNAL) causes a compile error on Arduino 101. 
const int PIN_INPUT = A0; 
const int PIN_BLINK = 13;        // Pin 13 is the on-board LED 
const int PIN_FADE = 3;          // must be a pin that supports PWM. 
const unsigned long MICROS_PER_READ = 2 * 1000L; 
const boolean REPORT_JITTER_AND_HANG = false; 
const long OFFSET_MICROS = 1L;  // NOTE: must be non-negative 
unsigned long wantMicros; 
long minJitterMicros; 
long maxJitterMicros; 
unsigned long lastReportMicros; 
byte samplesUntilReport; 
const byte SAMPLES_PER_SERIAL_SAMPLE = 20; 
// PWM steps per fade step.  More fades faster; less fades slower. 
const int PWM_STEPS_PER_FADE = 12; 
int fadePWM; 
PulseSensorBPM pulseDetector(PIN_INPUT, MICROS_PER_READ / 1000L); 
BLEPeripheral blePeripheral;       // BLE Peripheral Device (the board you're programming) 
BLEService heartRateService("180D"); // BLE Heart Rate Service 
// BLE Heart Rate Measurement Characteristic" 
BLECharacteristic heartRateChar("2A37",  // standard 16-bit characteristic UUID 
   BLERead | BLENotify, 2);  // remote clients will be able to get notifications if this characteristic changes 
                             // the characteristic is 2 bytes long as the first field needs to be "Flags" as per BLE specifications 
                             // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml 
int oldHeartRate = 0;  // last heart rate reading from analog input 
long previousMillis = 0;  // last time the heart rate was checked, in ms 
void setup() { 
 Serial.begin(115200);    // initialize serial communication 
 pinMode(13, OUTPUT);   // initialize the LED on pin 13 to indicate when a central is connected 
 /* Set a local name for the BLE device 
    This name will appear in advertising packets 
    and can be used by remote devices to identify this BLE device 
    The name can be changed but maybe be truncated based on space left in advertisement packet */ 
 blePeripheral.setLocalName("GianniCuore"); 
 blePeripheral.setAdvertisedServiceUuid(heartRateService.uuid());  // add the service UUID 
 blePeripheral.addAttribute(heartRateService);   // Add the BLE Heart Rate service 
 blePeripheral.addAttribute(heartRateChar); // add the Heart Rate Measurement characteristic 
 /* Now activate the BLE device.  It will start continuously transmitting BLE 
    advertising packets and will be visible to remote BLE central devices 
    until it receives a new connection */ 
 blePeripheral.begin(); 
 Serial.println("Bluetooth device active, waiting for connections..."); 
 if (HAS_A_REF) { 
   //BUG? Causes a compile error on Arduino 101: analogReference(EXTERNAL); 
 } 
 // PIN_INPUT is set up by the pulseDetector constructor. 
 pinMode(PIN_BLINK, OUTPUT); 
 digitalWrite(PIN_BLINK, LOW); 
 pinMode(PIN_FADE, OUTPUT); 
 fadePWM = 0; 
 analogWrite(PIN_FADE, fadePWM);   // sets PWM duty cycle 
 // Setup our reporting and jitter measurement. 
 samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; 
 lastReportMicros = 0L; 
 resetJitter(); 
 // wait one sample interval before starting to search for pulses. 
 wantMicros = micros() + MICROS_PER_READ; 
} 
void loop() { 
 BLECentral central = blePeripheral.central(); 
 // if a central is connected to peripheral: 
 if (central) { 
   Serial.print("Connected to central: "); 
   // print the central's MAC address: 
   Serial.println(central.address()); 
   // turn on the LED to indicate the connection: 
   digitalWrite(13, HIGH); 
   // check the heart rate measurement every 200ms 
   // as long as the central is still connected: 
   while (central.connected()) { 
     long currentMillis = millis(); 
     // if 200ms have passed, check the heart rate measurement: 
     if (currentMillis - previousMillis >= 2) { 
       previousMillis = currentMillis; 
       updateHeartRate(); 
     } 
   } 
   // when the central disconnects, turn off the LED: 
   digitalWrite(13, LOW); 
   Serial.print("Disconnected from central: "); 
   Serial.println(central.address()); 
 } 
} 
void updateHeartRate() { 
 /* Read the current voltage level on the A0 analog input pin. 
    This is used here to simulate the heart rate's measurement. 
 */ 
  unsigned long nowMicros = micros(); 
 if ((long) (wantMicros - nowMicros) > 1000L) { 
   return;  // we have time to do other things 
 } 
 if ((long) (wantMicros - nowMicros) > 3L + OFFSET_MICROS) { 
   delayMicroseconds((unsigned int) (wantMicros - nowMicros) - OFFSET_MICROS); 
   nowMicros = micros();     
 } 
long jitterMicros = (long) (nowMicros - wantMicros); 
 if (minJitterMicros > jitterMicros) { 
   minJitterMicros = jitterMicros; 
 } 
 if (maxJitterMicros < jitterMicros) { 
   maxJitterMicros = jitterMicros; 
 } 
 /* 
  * If desired, after 60 seconds of running, 
  * report our measured Jitter and hang. 
  *  
  * NOTE: this mode won't work with the Processing Sketch. 
  * It's designed for debug only. 
  */ 
 if (REPORT_JITTER_AND_HANG 
     && (long) (nowMicros - lastReportMicros) > 60000000L) { 
   lastReportMicros = nowMicros; 
   Serial.print(F("Jitter (min, max) = ")); 
   Serial.print(minJitterMicros); 
   Serial.print(F(", ")); 
   Serial.print(maxJitterMicros); 
   Serial.println(); 
   resetJitter(); 
   //hang because our prints are incompatible with the Processing Sketch 
   for (;;) { } 
 } 
 wantMicros = nowMicros + MICROS_PER_READ; 
 boolean QS = pulseDetector.readSensor(); 
 if (pulseDetector.isPulse()) { 
   digitalWrite(PIN_BLINK, HIGH); 
 } else { 
   digitalWrite(PIN_BLINK, LOW); 
 } 
 if (QS) { 
   fadePWM = 255;  // start fading on the start of each beat. 
   analogWrite(PIN_FADE, fadePWM); 
 } 
 /* 
  * Perform our Serial output. We don't worry about timing, because 
  * the documentation for Serial says that "As of version 1.0, 
  * serial transmission is asynchronous; Serial.print() will return 
  * before any characters are transmitted." 
  *  
  * The reader (the Processing Sketch) must read continuously 
  * or else our app will block (stop temporarily). 
  */ 
 /* 
  * Every so often, send the latest Sample to the Processing Sketch. 
  * We don't print every sample, because our baud rate 
  * won't support that much I/O. 
  */ 
 if (--samplesUntilReport == (byte) 0) { 
   samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE; 
   Serial.print('S'); 
   Serial.println(pulseDetector.getSignal()); 
   // Coincidentally, fade the LED a bit. 
   fadePWM -= PWM_STEPS_PER_FADE; 
   if (fadePWM < 0) { 
     fadePWM = 0; 
   } 
   analogWrite(PIN_FADE, fadePWM); 
 } 
 // Every beat, report the heart rate and inter-beat-interval 
 if (QS) { 
   Serial.print('B'); 
   Serial.println(pulseDetector.getBPM()); 
   Serial.print('Q'); 
   Serial.println(pulseDetector.getIBI()); 
 } 
 int heartRate = pulseDetector.getBPM(); 
 if (heartRate != oldHeartRate) { 
   Serial.print("Heart Rate is now: "); // print it 
   Serial.println(heartRate); 
   const unsigned char heartRateCharArray[2] = { 0, (char)heartRate }; 
   heartRateChar.setValue(heartRateCharArray, 2);  // and update the heart rate measurement characteristic 
   oldHeartRate = heartRate;           // save the level for next comparison 
 } 
} 
void resetJitter() { 
 // min = a number so large that any value will be smaller than it; 
 // max = a number so small that any value will be larger than it. 
 minJitterMicros = 60 * 1000L; 
 maxJitterMicros = -1;
}

Schematics

Pulse sensor connections
This is the connection of pulsesensor to Arduino 101 board
Arduino plug in2 1024x1024 kwkx3qfoqf

Code

Pulse Sensor AMPED to nRF Connect appArduino
This code connect the Genuino/Arduino 101 board to nRF app, and send the value of pulse sensor connected to the board.
/*
  Giovanni Gentile
  February 2016
  Arduino 101 Pulse sensor
*/

#include <CurieBLE.h>
#include <PulseSensorBPM.h>

const boolean HAS_A_REF = false; //BUG? analogReference(EXTERNAL) causes a compile error on Arduino 101.
const int PIN_INPUT = A0;
const int PIN_BLINK = 13;        // Pin 13 is the on-board LED
const int PIN_FADE = 3;          // must be a pin that supports PWM.

const unsigned long MICROS_PER_READ = 2 * 1000L;
const boolean REPORT_JITTER_AND_HANG = false;
const long OFFSET_MICROS = 1L;  // NOTE: must be non-negative

unsigned long wantMicros;
long minJitterMicros;
long maxJitterMicros;
unsigned long lastReportMicros;
byte samplesUntilReport;
const byte SAMPLES_PER_SERIAL_SAMPLE = 20;
// PWM steps per fade step.  More fades faster; less fades slower.
const int PWM_STEPS_PER_FADE = 12;
int fadePWM;
PulseSensorBPM pulseDetector(PIN_INPUT, MICROS_PER_READ / 1000L);

BLEPeripheral blePeripheral;       // BLE Peripheral Device (the board you're programming)
BLEService heartRateService("180D"); // BLE Heart Rate Service

// BLE Heart Rate Measurement Characteristic"
BLECharacteristic heartRateChar("2A37",  // standard 16-bit characteristic UUID
    BLERead | BLENotify, 2);  // remote clients will be able to get notifications if this characteristic changes
                              // the characteristic is 2 bytes long as the first field needs to be "Flags" as per BLE specifications
                              // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.heart_rate_measurement.xml

int oldHeartRate = 0;  // last heart rate reading from analog input
long previousMillis = 0;  // last time the heart rate was checked, in ms

void setup() {
  Serial.begin(115200);    // initialize serial communication
  pinMode(13, OUTPUT);   // initialize the LED on pin 13 to indicate when a central is connected

  /* Set a local name for the BLE device
     This name will appear in advertising packets
     and can be used by remote devices to identify this BLE device
     The name can be changed but maybe be truncated based on space left in advertisement packet */
  blePeripheral.setLocalName("GianniCuore");
  
  blePeripheral.setAdvertisedServiceUuid(heartRateService.uuid());  // add the service UUID

  blePeripheral.addAttribute(heartRateService);   // Add the BLE Heart Rate service
  blePeripheral.addAttribute(heartRateChar); // add the Heart Rate Measurement characteristic
  
  /* Now activate the BLE device.  It will start continuously transmitting BLE
     advertising packets and will be visible to remote BLE central devices
     until it receives a new connection */
  blePeripheral.begin();
  Serial.println("Bluetooth device active, waiting for connections...");
  if (HAS_A_REF) {
    //BUG? Causes a compile error on Arduino 101: analogReference(EXTERNAL);
  }
  // PIN_INPUT is set up by the pulseDetector constructor.
  pinMode(PIN_BLINK, OUTPUT);
  digitalWrite(PIN_BLINK, LOW);
  pinMode(PIN_FADE, OUTPUT);
  fadePWM = 0;
  analogWrite(PIN_FADE, fadePWM);   // sets PWM duty cycle

  // Setup our reporting and jitter measurement.
  samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE;
  lastReportMicros = 0L;
  resetJitter();

  // wait one sample interval before starting to search for pulses.
  wantMicros = micros() + MICROS_PER_READ;
}

void loop() {
  
  BLECentral central = blePeripheral.central();

  // if a central is connected to peripheral:
  if (central) {
    Serial.print("Connected to central: ");
    // print the central's MAC address:
    Serial.println(central.address());
    // turn on the LED to indicate the connection:
    digitalWrite(13, HIGH);

    // check the heart rate measurement every 200ms
    // as long as the central is still connected:
    while (central.connected()) {
      long currentMillis = millis();
      // if 200ms have passed, check the heart rate measurement:
      if (currentMillis - previousMillis >= 2) {
        previousMillis = currentMillis;
        updateHeartRate();
      }
    }
    // when the central disconnects, turn off the LED:
    digitalWrite(13, LOW);
    Serial.print("Disconnected from central: ");
    Serial.println(central.address());
  }
  
}

void updateHeartRate() {
  /* Read the current voltage level on the A0 analog input pin.
     This is used here to simulate the heart rate's measurement.
  */
   unsigned long nowMicros = micros();
  if ((long) (wantMicros - nowMicros) > 1000L) {
    return;  // we have time to do other things
  }
  if ((long) (wantMicros - nowMicros) > 3L + OFFSET_MICROS) {
    delayMicroseconds((unsigned int) (wantMicros - nowMicros) - OFFSET_MICROS);
    nowMicros = micros();    
  }
long jitterMicros = (long) (nowMicros - wantMicros);
  if (minJitterMicros > jitterMicros) {
    minJitterMicros = jitterMicros;
  }
  if (maxJitterMicros < jitterMicros) {
    maxJitterMicros = jitterMicros;
  }

  /*
   * If desired, after 60 seconds of running,
   * report our measured Jitter and hang.
   * 
   * NOTE: this mode won't work with the Processing Sketch.
   * It's designed for debug only.
   */
  if (REPORT_JITTER_AND_HANG
      && (long) (nowMicros - lastReportMicros) > 60000000L) {
    lastReportMicros = nowMicros;
    
    Serial.print(F("Jitter (min, max) = "));
    Serial.print(minJitterMicros);
    Serial.print(F(", "));
    Serial.print(maxJitterMicros);
    Serial.println();
    
    resetJitter();

    //hang because our prints are incompatible with the Processing Sketch
    for (;;) { }
  }
  
  wantMicros = nowMicros + MICROS_PER_READ;
  boolean QS = pulseDetector.readSensor();

  if (pulseDetector.isPulse()) {
    digitalWrite(PIN_BLINK, HIGH);
  } else {
    digitalWrite(PIN_BLINK, LOW);
  }

  if (QS) {
    fadePWM = 255;  // start fading on the start of each beat.
    analogWrite(PIN_FADE, fadePWM);
  }


  /*
   * Perform our Serial output. We don't worry about timing, because
   * the documentation for Serial says that "As of version 1.0,
   * serial transmission is asynchronous; Serial.print() will return
   * before any characters are transmitted."
   * 
   * The reader (the Processing Sketch) must read continuously
   * or else our app will block (stop temporarily).
   */

  /*
   * Every so often, send the latest Sample to the Processing Sketch.
   * We don't print every sample, because our baud rate
   * won't support that much I/O.
   */
  if (--samplesUntilReport == (byte) 0) {
    samplesUntilReport = SAMPLES_PER_SERIAL_SAMPLE;

    Serial.print('S');
    Serial.println(pulseDetector.getSignal());

    // Coincidentally, fade the LED a bit.
    fadePWM -= PWM_STEPS_PER_FADE;
    if (fadePWM < 0) {
      fadePWM = 0;
    }
    analogWrite(PIN_FADE, fadePWM);
    
  }

  // Every beat, report the heart rate and inter-beat-interval
  if (QS) {
    Serial.print('B');
    Serial.println(pulseDetector.getBPM());
    Serial.print('Q');
    Serial.println(pulseDetector.getIBI());
  }
  int heartRate = pulseDetector.getBPM();
  if (heartRate != oldHeartRate) {
    Serial.print("Heart Rate is now: "); // print it
    Serial.println(heartRate);
    const unsigned char heartRateCharArray[2] = { 0, (char)heartRate };
    heartRateChar.setValue(heartRateCharArray, 2);  // and update the heart rate measurement characteristic
    oldHeartRate = heartRate;           // save the level for next comparison
  }
}

void resetJitter() {
  // min = a number so large that any value will be smaller than it;
  // max = a number so small that any value will be larger than it.
  minJitterMicros = 60 * 1000L;
  maxJitterMicros = -1;
}

Comments

Similar projects you might like

Pac-Man LED Pixel Panel Costume

Project tutorial by Ben Muller

  • 4,800 views
  • 4 comments
  • 85 respects

LoRa Gateway for DeviceHive

Project tutorial by DeviceHive IoT team

  • 1,320 views
  • 2 comments
  • 17 respects

IoT Bird Feeder with Sigfox and Tweeter

Project showcase by Gaël Porté

  • 388 views
  • 0 comments
  • 7 respects

SmartWay

Project tutorial by Universum

  • 248 views
  • 0 comments
  • 5 respects

Raspberry Pi and Arduino Laptop

Project tutorial by Dante Roumega

  • 17,831 views
  • 6 comments
  • 45 respects

Arduino-Based Automatic Guitar Tuner

Project tutorial by Ben Overzat

  • 3,618 views
  • 0 comments
  • 12 respects
Add projectSign up / Login