Simple Heartbeat Sketch

Hey Guys,

I've come a little stuck in my quest to get my heartbeat data into arduino. My setup is as below and as you can see features the Sparkfun HRMI board to receive the HR data from a standard Polar belt transmitter and send it to arduino in an 'easy' to use format.

As all I want to do is simply turn on an LED when my heart rate goes over 80 I figured I could adapt the example code from the HRMI designer Dan Julio 404 Not Found
However all of his code was written for arduino 0012 so now with my fresh 0017 just throws up a bunch of errors :frowning:

Are there any of sources I can look to? I read another post that suggested I had to convert the analogue pulses in a sketch, but I'm not really that processing savvy to be honest

Any help would be great!

The code should still work. What error messages did you see? Post the code and the error messages. I'm sure we can help you get it working.

Hey Paul,

Thanks for answering this one too :slight_smile:

After a few hours I actually got the code to compile and upload correctly (moved the code from seperate .h files into the main sketch) However I'm not seeing anything in the serial monitor even though the HRMI's status light is blinking to signify its receiving my heart rate data.

Heres the code...

/*
 * Simple Arduino-based program to read values from the HRMI using the I2C interface
 *
 * Connections
 *    Arduino            HRMI
 *    -----------------------
 *      +5                +5 (Power for the HRMI)
 *      GND               GND
 *      Analog In 5       TX (I2C SCL) (recommend 4.7 kOhm pullup)
 *      Analog In 4       RX (I2C SDA) (recommend 4.7 kOhm pullup)
 *
 *
 * Note: By default the Arduino Wiring library is limited to a maximum
 *       I2C read of 32 bytes.  The Get Heartrate command is limited
 *       by this code to a maximum of 30 values (for a max I2C packet
 *       of 32 bytes).
 * 
 */

#include "Wire.h"
//#include "hrmi_funcs.h"

static void hrmi_open()
{
  Wire.begin();
}

/*
 * Configuration Information
 *
 * Change these constants to match your specific configuration.  These
 * values are the factory default (no OP1-OP7 jumpers installed).  Jumper
 * OP1 should be installed and jumper SJ1 removed.
 *
 * HRMI_HOST_BAUDRATE should be set to the baudrate the host will use
 *   to communicate with the Arduino over the serial interface.
 *
 * HRMI_I2C_ADDR should be set to the I2C address the HRMI is configured
 *   with.
 */
#define HRMI_HOST_BAUDRATE 9600
#define HRMI_I2C_ADDR      127


/*
 * Program constants
 */
#define MAX_IN_BUFFSIZE 16


/*
 * Global variables
 */
char serInStr[MAX_IN_BUFFSIZE];   // Serial input string array
int numEntries = 0;               // Number of HR values to request
int numRspBytes;                  // Number of Response bytes to read
byte i2cRspArray[34];                // I2C response array, sized to read 32 HR values
byte hrmi_addr = HRMI_I2C_ADDR;   // I2C address to use

/*
 * Functions from hrmi_func.h
 */
static void hrmiCmdArg(byte addr, byte cmd, byte arg)
{
  Wire.beginTransmission(addr);
  Wire.send(cmd);
  Wire.send(arg);
  Wire.endTransmission();
}


/*
 * hrmiCmd: Send a Command with no argument
 */
static void hrmiCmd(byte addr, byte cmd)
{
  Wire.beginTransmission(addr);
  Wire.send(cmd);
  Wire.endTransmission();
}


/*
 * hrmiGetData: Get the specified number of data bytes from the HRMI into
 *              an array.
 */
static int hrmiGetData(byte addr, byte numBytes, byte* dataArray)
{
  Wire.requestFrom(addr, numBytes);
  if (Wire.available()) {
    for (int i=0; i<numBytes; i++)
      dataArray[i] = Wire.receive();
    return(0);
  }
  return(-1);
}

// --------------------------------------------------------------------- //

/*
 * Arduino initialization code segment
 */
void setup()
{
  // Initialize the I2C communication
  hrmi_open();

  // Initialize the serial interface
  Serial.begin(HRMI_HOST_BAUDRATE);
}


/*
 * Arduino main code loop
 */
void loop()
{
  // Request a set of heart rate values
  hrmiCmdArg(hrmi_addr, 'G', (byte) numEntries);
      
  // Get the response from the HRMI
  numRspBytes = numEntries + 2;
  if (hrmiGetData(hrmi_addr, numRspBytes, i2cRspArray) != -1) {
    // send the results back on the serial interface in ASCII form
    Serial.print("Request "); Serial.print(numEntries); Serial.print(" => ");
    for (int i=0; i<numRspBytes; i++) {
      Serial.print(i2cRspArray[i], DEC);
      Serial.print(" ");
    }
    Serial.println();
   }
    
  // Setup to do it again
  if (++numEntries > 30)
      numEntries = 0;
  delay(1000);                  // Delay 1 second between commands
}

I think it may be something to do with the jumpers on the HRMI board but his configuration notes in the sketch are a little confusing. He says

Change these constants to match your specific configuration. These

  • values are the factory default (no OP1-OP7 jumpers installed). Jumper
  • OP1 should be installed and jumper SJ1 removed.

Should I de-solder the factory SP1 jumper and solder one onto OP1?

Many thanks!

YES. If you want to use I2C, desolder SJ1 and solder OP0 (test these with a multimeter to make sure they are done properly)
Also, read the hrmi.pdf file that is on sparkfun.com- it is very helpful in getting started.

I think it may be something to do with the jumpers on the HRMI board but his configuration notes in the sketch are a little confusing. He says Quote:
Change these constants to match your specific configuration. These

  • values are the factory default (no OP1-OP7 jumpers installed). Jumper
  • OP1 should be installed and jumper SJ1 removed.

Should I de-solder the factory SP1 jumper and solder one onto OP1?

Hi,

I literally have the same setup with the arduino and HRMI sensor...I am getting HRMI data through the arduino serial fine but i need it to read the value from the sensor and then send it as an analog pin value using firmata.....mainly because i am using the firmata sketch to allow my arduino to talk to flash.

I'm having trouble because to get the heart rate data you have to send it a specific request (G1 followed by a carriage return)

Any ideas how i would do that? i a noob when it comes to Arduinos and would appreciate any help/advice given

Adam

Firmata has methods that allow you to send strings to the Arduino, and get string responses back. So, you could send the Arduino a command ("Get heartrate").

Unfortunately, once you have the heart rate data, you will not be able to "send it as an analog pin value". The analog pins are read only, and the function that reads an analog pin can't be tricked into getting some other data, instead.

Me and my mate managed to get it so that the Arduino analogue pins sent the number '3' to Flash...

Show the code...

/*
  Copyright (C) 2006-2008 Hans-Christoph Steiner.  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.
 
  See file LICENSE.txt for further informations on licensing terms.
 */

/* 
 * TODO: add Servo support using setPinMode(pin, SERVO);
 * TODO: use Program Control to load stored profiles from EEPROM
 */

#include <EEPROM.h>
#include <Firmata.h>

/*==============================================================================
 * GLOBAL VARIABLES
 *============================================================================*/

/* analog inputs */
int analogInputsToReport = 0; // bitwise array to store pin reporting
int analogPin = 0; // counter for reading analog pins

/* digital pins */
byte reportPINs[TOTAL_PORTS];   // PIN == input port
byte previousPINs[TOTAL_PORTS]; // PIN == input port
byte pinStatus[TOTAL_DIGITAL_PINS]; // store pin status, default OUTPUT
byte portStatus[TOTAL_PORTS];

/* timer variables */
unsigned long currentMillis;     // store the current value from millis()
unsigned long nextExecuteMillis; // for comparison with currentMillis


/*==============================================================================
 * FUNCTIONS                                                                
 *============================================================================*/

void outputPort(byte portNumber, byte portValue)
{
  portValue = portValue &~ portStatus[portNumber];
  if(previousPINs[portNumber] != portValue) {
        Firmata.sendDigitalPort(portNumber, portValue); 
        previousPINs[portNumber] = portValue;
        Firmata.sendDigitalPort(portNumber, portValue); 
    }
}

/* -----------------------------------------------------------------------------
 * check all the active digital inputs for change of state, then add any events
 * to the Serial output queue using Serial.print() */
void checkDigitalInputs(void) 
{
    byte i, tmp;
    for(i=0; i < TOTAL_PORTS; i++) {
        if(reportPINs[i]) {
            switch(i) {
            case 0: outputPort(0, PIND &~ B00000011); break; // ignore Rx/Tx 0/1
            case 1: outputPort(1, PINB); break;
            case ANALOG_PORT: outputPort(ANALOG_PORT, PINC); break;
            }
        }
    }
}

// -----------------------------------------------------------------------------
/* sets the pin mode to the correct state and sets the relevant bits in the
 * two bit-arrays that track Digital I/O and PWM status
 */
void setPinModeCallback(byte pin, int mode) {
    byte port = 0;
    byte offset = 0;

    if (pin < 8) {
      port = 0;
      offset = 0;
    } else if (pin < 14) {
      port = 1;
      offset = 8;     
    } else if (pin < 22) {
      port = 2;
      offset = 14;
    }

    if(pin > 1) { // ignore RxTx (pins 0 and 1)
        pinStatus[pin] = mode;
        switch(mode) {
        case INPUT:
            pinMode(pin, INPUT);
            portStatus[port] = portStatus[port] &~ (1 << (pin - offset));
            break;
        case OUTPUT:
            digitalWrite(pin, LOW); // disable PWM
        case PWM:
            pinMode(pin, OUTPUT);
            portStatus[port] = portStatus[port] | (1 << (pin - offset));
            break;
        //case ANALOG: // TODO figure this out
        default:
            Firmata.sendString("");
        }
        // TODO: save status to EEPROM here, if changed
    }
}

void analogWriteCallback(byte pin, int value)
{
    setPinModeCallback(pin,PWM);
    analogWrite(pin, value);
}

void digitalWriteCallback(byte port, int value)
{
    switch(port) {
    case 0: // pins 2-7 (don't change Rx/Tx, pins 0 and 1)
        // 0xFF03 == B1111111100000011    0x03 == B00000011
        PORTD = (value &~ 0xFF03) | (PORTD & 0x03);
        break;
    case 1: // pins 8-13 (14,15 are disabled for the crystal) 
        PORTB = (byte)value;
        break;
    case 2: // analog pins used as digital
        PORTC = (byte)value;
        break;
    }
}

// -----------------------------------------------------------------------------
/* sets bits in a bit array (int) to toggle the reporting of the analogIns
 */
//void FirmataClass::setAnalogPinReporting(byte pin, byte state) {
//}
void reportAnalogCallback(byte pin, int value)
{
    if(value == 0) {
        analogInputsToReport = analogInputsToReport &~ (1 << pin);
    }
    else { // everything but 0 enables reporting of that pin
        analogInputsToReport = analogInputsToReport | (1 << pin);
    }
    // TODO: save status to EEPROM here, if changed
}

void reportDigitalCallback(byte port, int value)
{
    reportPINs[port] = (byte)value;
    if(port == ANALOG_PORT) // turn off analog reporting when used as digital
        analogInputsToReport = 0;
}

/*==============================================================================
 * SETUP()
 *============================================================================*/
void setup() 
{
    byte i;

    Firmata.setFirmwareVersion(2, 0);

    Firmata.attach(ANALOG_MESSAGE, analogWriteCallback);
    Firmata.attach(DIGITAL_MESSAGE, digitalWriteCallback);
    Firmata.attach(REPORT_ANALOG, reportAnalogCallback);
    Firmata.attach(REPORT_DIGITAL, reportDigitalCallback);
    Firmata.attach(SET_PIN_MODE, setPinModeCallback);

    portStatus[0] = B00000011;  // ignore Tx/RX pins
    portStatus[1] = B11000000;  // ignore 14/15 pins 
    portStatus[2] = B00000000;

//    for(i=0; i<TOTAL_DIGITAL_PINS; ++i) { // TODO make this work with analogs
    for(i=0; i<14; ++i) {
        setPinModeCallback(i,OUTPUT);
    }
    // set all outputs to 0 to make sure internal pull-up resistors are off
    PORTB = 0; // pins 8-15
    PORTC = 0; // analog port
    PORTD = 0; // pins 0-7

    // TODO rethink the init, perhaps it should report analog on default
    for(i=0; i<TOTAL_PORTS; ++i) {
        reportPINs[i] = false;
    }
    // TODO: load state from EEPROM here

    /* send digital inputs here, if enabled, to set the initial state on the
     * host computer, since once in the loop(), this firmware will only send
     * digital data on change. */
    if(reportPINs[0]) outputPort(0, PIND &~ B00000011); // ignore Rx/Tx 0/1
    if(reportPINs[1]) outputPort(1, PINB);
    if(reportPINs[ANALOG_PORT]) outputPort(ANALOG_PORT, PINC);

    Firmata.begin();
}

/*==============================================================================
 * LOOP()
 *============================================================================*/
void loop() 
{
/* DIGITALREAD - as fast as possible, check for changes and output them to the
 * FTDI buffer using Serial.print()  */
    checkDigitalInputs();  
    currentMillis = millis();
    if(currentMillis > nextExecuteMillis) {  
        nextExecuteMillis = currentMillis + 19; // run this every 20ms
        /* SERIALREAD - Serial.read() uses a 128 byte circular buffer, so handle
         * all serialReads at once, i.e. empty the buffer */
        while(Firmata.available())
            Firmata.processInput();
        /* SEND FTDI WRITE BUFFER - make sure that the FTDI buffer doesn't go over
         * 60 bytes. use a timer to sending an event character every 4 ms to
         * trigger the buffer to dump. */
      
        /* ANALOGREAD - right after the event character, do all of the
         * analogReads().  These only need to be done every 4ms. */
        for(analogPin=0;analogPin<TOTAL_ANALOG_PINS;analogPin++) {
            [b]if( analogInputsToReport & (1 << analogPin) ) {
                Firmata.sendAnalog(analogPin, 3);
            }[/b]
        }
    }
}

that bit in bold outputs '3' in Flash when told to read data from the analogue pins...

when '3' is taken out and replaced with the below

Firmata.sendAnalog(analogPin, analogRead(analogPin));

regular analogue data is read from the pins