Nano 33 BLE , Correct UUID syntax 2A37 for HR

Hello everyone,

I am trying to send my Heart Rate measurements from my Nano 33 BLE to my phone and trying to use the UUID="2A37" but I have problems understanding the syntax I must use.
I got my information from here:
https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml
I would like to get only my Heart Rate Measurement (bpm) and that the Sensor is connected to the wrist without the Energy expended or the RR interval. I dont understand how to use the flag, and what to send through the characteristic in order to get my results.
My code is here:

//libraries for SERIAL, MAX30105, SEN15805 (TMP117), HR algorithm 
#include <Wire.h>
#include "MAX30105.h"
#include <SparkFun_TMP117.h> // Used to send and recieve specific information from our sensor
#include "heartRate.h"




//init for HR algorithm
const byte RATE_SIZE = 4; //Increase this for more averaging. 4 is good.
byte rates[RATE_SIZE]; //Array of heart rates
byte rateSpot = 0;
long lastBeat = 0; //Time at which the last beat occurred
float beatsPerMinute;
int beatAvg;
int countAvgBeat; //counter of how many beatAvg measurements we got
uint8_t sumBeat=0;  // sum of all the beatAvg measurements in 10sec / every 250 measurements or in 5 sec / every 125 measurements
//uint8_t Flags= 0b10000000;
//uint16_t C2;
//STORED on Ram for BLE
int16_t stored_temp=0;  // value that we sent through BLE



// The default address of the device is 0x48 = (GND)
TMP117 sensor; // Initalize sensor for temperature

//The default address of the device is 0x57
MAX30105 particleSensor;  //Initialize sensor for HR and SP02

//attempt to store shit in txt file
#include <iostream>   //fixed
#include <fstream>
//for precision 0.01
#include <iomanip>      // std::setprecision 

//BLE library
#include <ArduinoBLE.h>
//#define BLE_Temperature_Service_UUID     "1234567-1111-123t-TEMP-123458ya993h"   //random UUID
#define BLE_Temperature_Service_UUID     "1809"   //random UUID
//#define BLE_SP02_Service_UUID            "3456789-2222-345s-SP02-123458ya992h"   //random UUID

#define BLE_SP02_Service_UUID            "1822"   //random UUID
//#define BLE_HR_Service_UUID              "4567891-3333-456h-HR42-123458ya995h"  //random UUID
#define BLE_HR_Service_UUID              "180D"  //random UUID

#define BLE_Temperature_UUID             "2A6E"        //from 16-bit UUID NumbersDocument
#define BLE_SP02_UUID                    "2A5E"       //from 16-bit UUID NumbersDocument
#define BLE_HR_UUID                      "2A37"       //from 16-bit UUID NumbersDocument
//#define BLE_HR_UUID                       "39635fg3-4dh2-4660-bc38-6840efej55c1"    //random UUID


BLEService temperatureService( BLE_Temperature_Service_UUID );  // Service for temperature measurements
BLEService spo2Service(BLE_SP02_Service_UUID);  //Service for SP02 measurements 
BLEService HeartRateService(BLE_HR_Service_UUID ); //Service for heart rate measurements

BLEIntCharacteristic temperatureCharacteristic( BLE_Temperature_UUID ,BLERead | BLENotify ); // remote clients will only be able to read this
BLEIntCharacteristic spo2Characteristic( BLE_SP02_UUID, BLERead | BLENotify );
BLEIntCharacteristic heartrateCharacteristic( BLE_HR_UUID , BLERead | BLENotify );


//byte array uint8_t gr[4];

void setup() {
  // put your setup code here, to run once:
   //MAX30105 INIT for HEARTBEAT
    Serial.begin(115200);
    while (!Serial);

    // Initialize sensor
    if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
    {
      Serial.println("MAX30105 was not found. Please check wiring/power. ");
      while (1);
    }
    Serial.println("Place your index finger on the sensor with steady pressure.");

    ///SETUP configuration vars for HEARTBEAT///
    //We need Use 6.4mA for LED drive,Red+IR, sampleRate=50, adcRange = 16384, sampleAverage = 4, pulseWidth=?
    Serial.println("Initializing for HeartBeat");
    particleSensor.setup(); //Configure sensor. Use 6.4mA for LED drive

    particleSensor.setPulseAmplitudeRed(0x0A); //Turn Red LED to low to indicate sensor is running
    particleSensor.setPulseAmplitudeGreen(0); //Turn off Green LED

    //END_HeartBeat//

    //MAX30105 Init for SP02 algorithm
    Serial.begin(115200); // initialize serial communication at 115200 bits per second:
    while (!Serial);

    // Initialize sensor
    if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
    {
      Serial.println(F("MAX30105 was not found. Please check wiring/power."));
      while (1);
    }

    ///SETUP configuration vars for SP02 ///
    //Default is 0x1F which gets us 6.4mA
    //powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch
    //powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch
    //powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch
    //powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch
    byte ledBrightness = 60; //Options: 0=Off to 255=50mA
    byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
    byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
    byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
    //The longer the pulse width the longer range of detection you'll have
    //At 69us and 0.4mA it's about 2 inches
    //At 411us and 0.4mA it's about 6 inches
    int pulseWidth = 411; //Options: 69 get us 15 bit res, 118 get us 16bit res, 215 get us 17 bit res, 411 get us 18bit res
    int adcRange = 4096; //Options: 2048, 4096, 8192, 16384

    particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings

    //END_SP02//

    //TEMPERATURE CODE Init

    Serial.begin(115200);
    while (!Serial);
    Wire.setClock(400000);   // Set clock speed to be the fastest for better communication (fast mode)
    //Check if TMP117 is working and start the readings
    Serial.println("TMP117 Init for Readings");
    if (sensor.begin() == true) // Function to check if the sensor will correctly self-identify with the proper Device ID/Address
    {
      Serial.println("Begin measurements");
    }
    else
    {
      Serial.println("Device failed to setup- Freezing code.");
      while (1); // Runs forever
    }

    //END_TEMP//


    //BLE
  Serial.begin(9600);    // initialize serial communication
  while (!Serial);

  if (!BLE.begin()) {   // initialize BLE
    Serial.println("starting BLE failed!");
    while (1);
  }
  //set advertised local name and service
  BLE.setDeviceName("Arduino Nano 33 BLE device");
  BLE.setLocalName("Arduino Nano 33 BLE");  // Set name for connection
  BLE.setAdvertisedService(temperatureService); // Advertise service
  
 // BLE.setAdvertisedService(spo2Service); // Advertise service

  BLE.setAdvertisedService(HeartRateService);
  
  //BLE and characteristics for temperatureService
  temperatureService.addCharacteristic(temperatureCharacteristic); // Add characteristic to service


  //BLE and characteristics for spo2Service
  spo2Service.addCharacteristic(spo2Characteristic);

  //Characteristics for Heart Rate Service
  HeartRateService.addCharacteristic(heartrateCharacteristic);
  
  //add Service
  BLE.addService(temperatureService); 
  BLE.addService(spo2Service);
  BLE.addService(HeartRateService);
  

  BLE.advertise();  // Start advertising
  Serial.print("Peripheral device MAC: ");
  Serial.println(BLE.address());
  Serial.println("Waiting for connections...");
  
}

void loop() {
  // put your main code here, to run repeatedly:
     BLEDevice central = BLE.central();                    //Waits for BLE Central device to connect

  if (central)                                          
  {
    Serial.print("Connected to central: ");
    Serial.println(central.address());

    while(central.connected()){
      heartbeat();
      uint8_t Flags= 0b00000000;
      uint16_t C1 = sumBeat;
      uint16_t C3 = 0x00;
      uint16_t C4 = 0x00;
      //static const byte HRBLE= (Flags, C2, C3, C4);
    static const uint64_t HRBLE= (C4 | C3 | C1 | Flags);
      Serial.println(HRBLE, BIN);
       heartrateCharacteristic.writeValue(HRBLE);
    }
    
  }
}

void heartbeat() {
 

  //Heart beat calc and prints
  long irValue = particleSensor.getIR();
*
  if (checkForBeat(irValue) == true)
  {
    //We sensed a beat!
    long delta = millis() - lastBeat;
    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 33)
    {
      rates[rateSpot++] = (byte)beatsPerMinute; //Store this reading in the array
      rateSpot %= RATE_SIZE; //Wrap variable

      //Take average of readings
      beatAvg = 0;
      for (byte x = 0 ; x < RATE_SIZE ; x++)
        beatAvg += rates[x];
      beatAvg /= RATE_SIZE;
    }
  }
 
  //  Serial.print("IR=");
  //  Serial.print(irValue);
    Serial.print(", BPM=");
    Serial.print(beatsPerMinute);
  Serial.print(" Avg BPM=");
  Serial.print(beatAvg);
  sumBeat = beatAvg ;
  countAvgBeat++;
  Serial.println();

//  if (irValue < 50000)
  //  Serial.print(" No finger?");

 // Serial.println();

}

I tried many different things but still I only get syntax errors. The same thing applies to UUID="2A5E" for Sp02 measurements which uses a flag.
Any guidance on how we use flags and send multiple values through BLE characteristics?
Thanks in advance!

Here is an example for a Heart Rate Service. The heart rate is simulated by reading A0.

/*
  This example creates a BLE peripheral with a Heart Rate Service

  The circuit:
  - Arduino Nano 33 BLE / BLE Sense
  - Arduino Nano 33 IoT
  - Arduino Nano RP2040 Connect
  

  You can use a generic BLE central app, like BLE Scanner (iOS and Android) or
  nRF Connect (Android), to interact with the services and characteristics
  created in this sketch.
  Silicon Labs EFR Connect and Infineon/Cypress CySmart can decode Heart Rate Service.

  This example code is in the public domain.
*/

#include <ArduinoBLE.h>

//----------------------------------------------------------------------------------------------------------------------
// BLE UUIDs
//----------------------------------------------------------------------------------------------------------------------

// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_measurement.xml
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.heart_rate_control_point.xml
// https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.body_sensor_location.xml

#define BLE_UUID_HEART_RATE_SERVICE               "180D"
#define BLE_UUID_HEART_RATE_MEASURMENT            "2A37"
#define BLE_UUID_BODY_SENSOR_LOCATION             "2A38"
#define BLE_UUID_HEART_RATE_CONTROL_POINT         "2A39"

//----------------------------------------------------------------------------------------------------------------------
// BLE Heart Rate Measurment
//----------------------------------------------------------------------------------------------------------------------

// Constants for flags in Heart Rate Measurement (see XML link)
#define HRM_VALUE_FORMAT_8BIT                     0
#define HRM_VALUE_FORMAT_16BIT                    1
#define HRM_SENSOR_CONTACT_NOT_SUPPORTED          ( 0 << 1 )
#define HRM_SENSOR_CONTACT_NOT_DETECTED           ( 2 << 1 )
#define HRM_SENSOR_CONTACT_DETECTED               ( 3 << 1 )
#define HRM_ENERGY_EXPENDED_NOT_PRESENT           ( 0 << 3 )
#define HRM_ENERGY_EXPENDED_PRESENT               ( 1 << 3 )


enum { BODY_SENSOR_LOCATION_OTHER = 0,
       BODY_SENSOR_LOCATION_CHEST,
       BODY_SENSOR_LOCATION_WRIST,
       BODY_SENSOR_LOCATION_FINGER,
       BODY_SENSOR_LOCATION_HAND,
       BODY_SENSOR_LOCATION_EAR_LOBE,
       BODY_SENSOR_LOCATION_FOOT
     };


typedef struct __attribute__( ( packed ) )
{
  uint8_t flags;
  uint8_t heartRate;
} heart_rate_measurment_t;


union heart_rate_measurment_u
{
  struct __attribute__( ( packed ) )
  {
    heart_rate_measurment_t values;
  };
  uint8_t bytes[ sizeof( heart_rate_measurment_t ) ];
};

union heart_rate_measurment_u heartRate = { .values = { .flags = 0, .heartRate = 0 } };

//----------------------------------------------------------------------------------------------------------------------
// BLE
//----------------------------------------------------------------------------------------------------------------------

#define BLE_DEVICE_NAME                           "Arduino HRM"
#define BLE_LOCAL_NAME                            "Arduino HRM"

BLEService heartRateService( BLE_UUID_HEART_RATE_SERVICE );
BLECharacteristic heartRateCharacteristic( BLE_UUID_HEART_RATE_MEASURMENT, BLERead | BLENotify, sizeof heartRate.bytes );
BLEUnsignedCharCharacteristic bodySensorLocationCharacteristic( BLE_UUID_BODY_SENSOR_LOCATION, BLERead );

//----------------------------------------------------------------------------------------------------------------------
// APP & I/O
//----------------------------------------------------------------------------------------------------------------------

#define BLE_LED_PIN                               LED_BUILTIN
#define HEART_BEAT_PIN                            A0
#define SENSOR_UPDATE_INTERVAL                    (1000)


typedef struct __attribute__( ( packed ) )
{
  uint8_t heartRate;
  bool sensorContact = false;
  bool updated = false;
} sensor_data_t;

sensor_data_t sensorData;


void setup()
{
  Serial.begin( 9600 );
//  while ( !Serial );

  Serial.println( "BLE Example Heart Rate Service" );

  pinMode( BLE_LED_PIN, OUTPUT );
  pinMode( HEART_BEAT_PIN, INPUT );
  analogReadResolution( 10 );

  if ( !setupBleMode() )
  {
    Serial.println( "Failed to initialize BLE!" );
    while ( 1 );
  }
  else
  {
    Serial.println( "BLE initialized. Waiting for clients to connect." );
  }
}


void loop()
{
  bleTask();
  if ( sensorTask() )
  {
    printTask();
  }
}


void printTask()
{
  Serial.print( "HR: " );
  Serial.print( sensorData.heartRate );
  Serial.println( " bpm" );
}


bool sensorTask()
{
  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis < SENSOR_UPDATE_INTERVAL )
  {
    return false;
  }
  previousMillis = currentMillis;

  uint16_t heartRate = analogRead( HEART_BEAT_PIN );
  sensorData.heartRate = ( uint8_t ) map( heartRate, 0, 1023, 40, 210 );

  if ( sensorData.heartRate < 60 )
  {
    sensorData.sensorContact = false;
  }
  else
  {
    sensorData.sensorContact = true;
  }
  sensorData.updated = true;
  return true;
}


bool setupBleMode()
{
  if ( !BLE.begin() )
  {
    return false;
  }

  // set advertised local name and service UUID
  BLE.setDeviceName( BLE_DEVICE_NAME );
  BLE.setLocalName( BLE_LOCAL_NAME );
  BLE.setAdvertisedService( heartRateService );

  // BLE add characteristics
  heartRateService.addCharacteristic( heartRateCharacteristic );
  heartRateService.addCharacteristic( bodySensorLocationCharacteristic );

  // add service
  BLE.addService( heartRateService );

  // set the initial value for the characeristic
  heartRateCharacteristic.writeValue( heartRate.bytes, sizeof heartRate.bytes );
  bodySensorLocationCharacteristic.writeValue( BODY_SENSOR_LOCATION_WRIST );

  // set BLE event handlers
  BLE.setEventHandler( BLEConnected, blePeripheralConnectHandler );
  BLE.setEventHandler( BLEDisconnected, blePeripheralDisconnectHandler );
  
  // start advertising
  BLE.advertise();

  return true;
}


void bleTask()
{
  const uint32_t BLE_UPDATE_INTERVAL = 10;
  static uint32_t previousMillis = 0;

  uint32_t currentMillis = millis();
  if ( currentMillis - previousMillis >= BLE_UPDATE_INTERVAL )
  {
    previousMillis = currentMillis;
    BLE.poll();
  }

  if ( sensorData.updated )
  {
    heartRate.values.heartRate = sensorData.heartRate;

    if ( sensorData.sensorContact )
    {
      heartRate.values.flags = ( heartRate.values.flags & 0b11111001 ) | HRM_SENSOR_CONTACT_DETECTED;
    }
    else
    {
      heartRate.values.flags = ( heartRate.values.flags & 0b11111001 ) | HRM_SENSOR_CONTACT_NOT_DETECTED;
    }

    heartRateCharacteristic.writeValue( heartRate.bytes, sizeof heartRate.bytes );
    sensorData.updated = false;
  }
}


void blePeripheralConnectHandler( BLEDevice central )
{
  digitalWrite( BLE_LED_PIN, HIGH );
  Serial.print( F( "Connected to central: " ) );
  Serial.println( central.address() );
}


void blePeripheralDisconnectHandler( BLEDevice central )
{
  digitalWrite( BLE_LED_PIN, LOW );
  Serial.print( F( "Disconnected from central: " ) );
  Serial.println( central.address() );
}

You can simply use an array of two bytes for the heartRateCharacteristic in your case but using the type with the union allows this to be extended with additional values e.g. energy expended. And this is more universal for other services that have multiple values in a characteristic.

As you can see in the notes I used multiple apps on iOS to test this and found EFR Connect and CySmart to be the best for this service. Which app are you using?

1 Like

Thank you very much for your example and your help.
I am not really good at coding so it took me some time to understand the typedef struct and the union on your code but I made it work :smiley: . I tried to send the RR interval also, but while I get the flag right ( It shows on the phone) I get no values.
I just added on your struct an uint16_t RR_interval and on the union.
I am using the NRF Connect app and besides the fact that I have not found an option for auto save all the data you stream, it works fine!
Thank you again!

I am trying to make the Pulse Oximeter Service work and I ended up on a problem.
I get my information from here:
https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Services/org.bluetooth.service.pulse_oximeter.xml
https://www.bluetooth.com/wp-content/uploads/Sitecore-Media-Library/Gatt/Xml/Characteristics/org.bluetooth.characteristic.plx_spot_check_measurement.xml
And my code looks something like this :

//libraries for SERIAL, MAX30105 and SP02 algorithm
#include <Wire.h>
#include "MAX30105.h"
#include "spo2_algorithm.h"

//init for SP02 algorithm
#define MAX_BRIGHTNESS 255
uint32_t irBuffer[100]; //infrared LED sensor data
uint32_t redBuffer[100];  //red LED sensor data

int32_t bufferLength; //data length
int32_t spo2; //SPO2 value
int8_t validSPO2; //indicator to show if the SPO2 calculation is valid
int32_t heartRate; //heart rate value
int8_t validHeartRate; //indicator to show if the heart rate calculation is valid

//The default address of the device is 0x57
MAX30105 particleSensor;  //Initialize sensor for HR and SP02
//----------------------------------------------------------------------------------------------------------------------
// BLE Sp02 Measurment from MAX30101
//----------------------------------------------------------------------------------------------------------------------

// Constants for flags (8bit) in Spot-Check Sp02 Measurement (last 3 bits reserved)(see XML link) MyFlag=ob11100010 
#define PLX_TIMESTAMP_FIELD_NOT_PRESENT                              0
#define PLX_TIMESTAMP_FIELD_PRESENT                                  1
#define PLX_MEASUREMENT_FIELD_NOT_PRESENT                        (0 << 1)
#define PLX_MEASUREMENT_FIELD_PRESENT                            (1 << 1)
#define PLX_DEVICE_SENSOR_STATUS_NOT_PRESENT                     (0 << 2)
#define PLX_DEVICE_SENSOR_STATUS_PRESENT                         (1 << 2)
#define PLX_PULSE_AMPLITUDE_NOT_PRESENT                          (0 << 3)
#define PLX_PULSE_AMPLITUDE_PRESENT                              (1 << 3)
#define PLX_DEVICE_CLOCK_NOT_SET_FALSE                           (0 << 4)                      
#define PLX_DEVICE_CLOCK_NOT_SET_TRUE                            (1 << 4) 

//Constants for flags (16bit) for Measurement Status  (first 5 bits reserved) My Initial Flag ob1100000000011111
#define MEASUREMENT_STATUS_ONGOING                               (1 << 5)
#define MEASUREMENT_STATUS_NOT_ONGOING                           (0 << 5)
#define MEASUREMENT_STATUS_EARLY_ESTIMATED_DATA                  (1 << 6)
#define MEASUREMENT_STATUS_NOT_EARLY_ESTIMATED_DATA              (0 << 6)
#define MEASUREMENT_STATUS_VALIDATED_DATA                        (1 << 7)
#define MEASUREMENT_STATUS_NOT_VALIDATED_DATA                    (0 << 7)
#define MEASUREMENT_STATUS_FULLY_QUALIFIED_DATA                  (1 << 8)
#define MEASUREMENT_STATUS_NOT_FULLY_QUALIFIED_DATA              (0 << 8)
#define MEASUREMENT_STATUS_DATA_FROM_MEASUREMENT_STORAGE         (1 << 9)
#define MEASUREMENT_STATUS_DATA_NOT_FROM_MEASUREMENT_STORAGE     (0 << 9)
#define MEASUREMENT_STATUS_DATA_FOR_DEMONSTATION                 (1 << 10)
#define MEASUREMENT_STATUS_DATA_NOT_FOR_DEMONSTATION             (0 << 10)
#define MEASUREMENT_STATUS_DATA_FOR_TESTING                      (1 << 11)
#define MEASUREMENT_STATUS_DATA_NOT_FOR_TESTING                  (0 << 11)
#define MEASUREMENT_STATUS_CALIBRATION_ONGOING                   (1 << 12)
#define MEASUREMENT_STATUS_CALIBRATION_NOT_ONGOING               (0 << 12)
#define MEASUREMENT_STATUS_MEASUREMENT_UNAVAILABLE               (1 << 13)
#define MEASUREMENT_STATUS_MEASUREMENT_AVAILABLE                 (0 << 13)
#define MEASUREMENT_STATUS_QUESTIONABLE_MEASUREMENT_DETECTED     (1 << 14)
#define MEASUREMENT_STATUS_NOT_QUESTIONABLE_MEASUREMENT_DETECTED (0 << 14)
#define MEASUREMENT_STATUS_INVALID_MEASUREMENT_DETECTED          (1 << 15)
#define MEASUREMENT_STATUS_VALID_MEASUREMENT_DETECTED            (0 << 15)     

//Constants for flags (24bit) for Measurement Status  (last 8 bits reserved)
#define DEVICE_SENSOR_EXT_DISPLAY_UPDATE_ONGOING                    1   
#define DEVICE_SENSOR_EXT_DISPLAY_UPDATE_NOT_ONGOING                0
#define DEVICE_SENSOR_EQUIPMENT_MALFUNCTION_DETECTED             (1 << 1)
#define DEVICE_SENSOR_EQUIPMENT_MALFUNCTION_NOT_DETECTED         (0 << 1)
#define DEVICE_SENSOR_SIGNAL_PROCESS_IRREGULARITY_DETECTED       (1 << 2)
#define DEVICE_SENSOR_SIGNAL_PROCESS_IRREGULARITY_NOT_DETECTED   (0 << 2)
#define DEVICE_SENSOR_INADEQUITE_SIGNAL_DETECTED                 (1 << 3)
#define DEVICE_SENSOR_INADEQUITE_SIGNAL_NOT_DETECTED             (0 << 3)
#define DEVICE_SENSOR_POOR_SIGNAL_DETECTED                       (1 << 4)
#define DEVICE_SENSOR_NOT_POOR_SIGNAL_DETECTED                   (0 << 4)
#define DEVICE_SENSOR_LOW_PERFUSION_DETECTED                     (1 << 5)
#define DEVICE_SENSOR_LOW_PERFUSION_NOT_DETECTED                 (0 << 5)
#define DEVICE_SENSOR_ERRATIC_SIGNAL_DETECTED                    (1 << 6)
#define DEVICE_SENSOR_ERRATIC_SIGNAL_NOT_DETECTED                (0 << 6)
#define DEVICE_SENSOR_NONPULSATILE_SIGNAL_DETECTED               (1 << 7)
#define DEVICE_SENSOR_NONPULSATILE_SIGNAL_NOT_DETECTED           (0 << 7)
#define DEVICE_SENSOR_QUESTIONABLE_PULSE_DETECTED                (1 << 8)
#define DEVICE_SENSOR_NOT_QUESTIONABLE_PULSE_DETECTED            (0 << 8)
#define DEVICE_SENSOR_SIGNAL_ANALYSIS_ONGOING                    (1 << 9)
#define DEVICE_SENSOR_SIGNAL_ANALYSIS_NOT_ONGOING                (0 << 9)
#define DEVICE_SENSOR_INTERFACE_DETECTED                         (1 << 10)
#define DEVICE_SENSOR_INTERFACE_NOT_DETECTED                     (0 << 10)
#define DEVICE_SENSOR_UNCONNECTED_TO_USER                        (1 << 11)
#define DEVICE_SENSOR_CONNECTED_TO_USER                          (0 << 11)
#define DEVICE_SENSOR_SENSOR_CONNECTED                           (1 << 12)
#define DEVICE_SENSOR_SENSOR_UNCONNECTED                         (0 << 12)
#define DEVICE_SENSOR_DISPLACED                                  (1 << 13)
#define DEVICE_SENSOR_NOT_DISPLACED                              (0 << 13)
#define DEVICE_SENSOR_MALFUNCTIONING                             (1 << 14)
#define DEVICE_SENSOR_NOT_MALFUNCTIONING                         (0 << 14)
#define DEVICE_SENSOR_DISCONNECTED                               (1 << 15)
#define DEVICE_SENSOR_CONNECTED                                  (0 << 15)

//for SP02
typedef struct __attribute__( ( packed ) )
{
  uint8_t flags1 ;
  float sp02_BLE;
  float sp02_PR_BLE;
  uint16_t flags_measurement ;
//cant make 24bit flag,wtf do i do  _int24 flags2 ;
} sp02_measurment;


union sp02_measurment_u
{
  struct __attribute__( ( packed ) )
  {
    sp02_measurment values;
  };
  uint8_t bytes1[ sizeof( sp02_measurment ) ];
} ;

union sp02_measurment_u SP02_Mes = { .values = { .flags1 = 0, .sp02_BLE = 0, .sp02_PR_BLE = 0, flags_measurement = 0 } };

float stored_final_sp02; // value that we sent through BLE
float stored_PR_sp02;
uint8_t flags1 = ob11100010;
uint16_t flags_measurement = ob0000000000011111;

void setup()
{
    //MAX30101 Init for SP02 algorithm
    Serial.begin(115200); // initialize serial communication at 115200 bits per second:
   while (!Serial);

    // Initialize sensor
    if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) //Use default I2C port, 400kHz speed
    {
      Serial.println(F("MAX30105 was not found. Please check wiring/power."));
      while (1);
    }

    ///SETUP configuration vars for SP02 ///
    //Default is 0x1F which gets us 6.4mA
    //powerLevel = 0x02, 0.4mA - Presence detection of ~4 inch
    //powerLevel = 0x1F, 6.4mA - Presence detection of ~8 inch
    //powerLevel = 0x7F, 25.4mA - Presence detection of ~8 inch
    //powerLevel = 0xFF, 50.0mA - Presence detection of ~12 inch
    byte ledBrightness = 60; //Options: 0=Off to 255=50mA
    byte sampleAverage = 4; //Options: 1, 2, 4, 8, 16, 32
    byte ledMode = 2; //Options: 1 = Red only, 2 = Red + IR, 3 = Red + IR + Green
    byte sampleRate = 100; //Options: 50, 100, 200, 400, 800, 1000, 1600, 3200
    //The longer the pulse width the longer range of detection you'll have
    //At 69us and 0.4mA it's about 2 inches
    //At 411us and 0.4mA it's about 6 inches
    int pulseWidth = 411; //Options: 69 get us 15 bit res, 118 get us 16bit res, 215 get us 17 bit res, 411 get us 18bit res
    int adcRange = 4096; //Options: 2048, 4096, 8192, 16384

    particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange); //Configure sensor with these settings

    //END_SP02//

    //BLE
  Serial.begin(9600);    // initialize serial communication
  while (!Serial);

  if (!BLE.begin()) {   // initialize BLE
    Serial.println("starting BLE failed!");
    while (1);
  }
  //set advertised local name and service
  BLE.setDeviceName("Arduino Nano 33 BLE device");
  BLE.setLocalName("Arduino Nano 33 BLE");  // Set name for connection
  
  BLE.setAdvertisedService(spo2Service); // Advertise service

 
  //BLE and characteristics for spo2Service
  spo2Service.addCharacteristic(spo2Characteristic);

  //add Services
  BLE.addService(spo2Service);

// set the initial value for the SP02 characteristic
  SP02_Mes.values.flags1 = flags1;
  SP02_Mes.values.flags_measurement = flags_measurement;
  spo2Characteristic.writeValue( SP02_Mes.bytes1, sizeof SP02_Mes.bytes1 );

  BLE.advertise();  // Start advertising
  Serial.print("Peripheral device MAC: ");
  Serial.println(BLE.address());
  Serial.println("Waiting for connections...");
    
}

void loop()
{

    BLEDevice central = BLE.central();                    //Waits for BLE Central device to connect
    
 if (central)                                          
  {
    Serial.print("Connected to central: ");
    Serial.println(central.address());

  while(central.connected()){

      int count = 0;
      int sum_sp02;
             
             SP02_measurement();
             
              //store sp02 in RAM
              for (int i = 1; i <= 25; i++)
              {
                if (stored_sp02[i] > 40 && stored_sp02[i] <= 100) {
        
                  count++;
                  sum_sp02 = (stored_sp02[i] + sum_sp02);
                  stored_final_sp02 = (sum_sp02 / count); //final measurement of sp02 ,average of max 25 measurements
                  stored_PR_sp02 = stored_final_sp02 ;
                  if (i == 25) {
                    count = 0;
                  }
                }
                if (stored_sp02[i] == -999) {
                  continue;
                }
      
            }

            Serial.println("Oi stored times einai: ");
            Serial.println("Temp, sp02");
            Serial.println(stored_temp, 4); Serial.println(stored_final_sp02);
            
            //set the value for charasteristics
            spo2Characteristic.writeValue( SP02_Mes.bytes1, sizeof SP02_Mes.bytes1 );
     
          } 
 
}

void SP02_measurement() {
  
//Sent to BLE that SP02 is ongoing
  SP02.Mes.values.flags_measurement = ob0010000000111111;
  spo2Characteristic.writeValue( SP02_Mes.bytes1, sizeof SP02_Mes.bytes1 );
  
  bufferLength = 100; //buffer length of 100 stores 4 seconds of samples running at 25sps

  //read the first 100 samples, and determine the signal range
  for (byte i = 0 ; i < bufferLength ; i++)
  {
    if (particleSensor.available() == false) //do we have new data?
      particleSensor.check(); //Check the sensor for new data

    redBuffer[i] = particleSensor.getRed();
    irBuffer[i] = particleSensor.getIR();
    particleSensor.nextSample(); //We're finished with this sample so move to next sample

    //    Serial.print(F("red="));
    //    Serial.print(redBuffer[i], DEC);
    //    Serial.print(F(", ir="));
    //    Serial.println(irBuffer[i], DEC);
  }

  //calculate heart rate and SpO2 after first 100 samples (first 4 seconds of samples)
  maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);


  //Continuously taking samples from MAX30105.  Heart rate and SpO2 are calculated every 1 second
  static unsigned long sp02Timer1   = 0;
  static unsigned long sp02Timer2   = 0;

  sp02Timer1 = millis();
  sp02Timer2 = millis();

  while (sp02Timer1 < sp02Timer2 + TIMER2_DURATION_MS)
  {
    //dumping the first 25 sets of samples in the memory and shift the last 75 sets of samples to the top
    for (byte i = 25; i < 100; i++)
    {
      redBuffer[i - 25] = redBuffer[i];
      irBuffer[i - 25] = irBuffer[i];
    }

    //take 25 sets of samples before calculating the heart rate.
    for (byte i = 75; i < 100; i++)
    {
      while (particleSensor.available() == false) //do we have new data?
        particleSensor.check(); //Check the sensor for new data


      redBuffer[i] = particleSensor.getRed();
      irBuffer[i] = particleSensor.getIR();
      particleSensor.nextSample(); //We're finished with this sample so move to next sample

      //PRINTS SP02 until Timer2 stops
      Serial.print(F(", SPO2="));
      Serial.print(spo2, DEC);

      Serial.print(F(", SPO2Valid="));
      Serial.println(validSPO2, DEC);

      stored_sp02[i - 74] = spo2;

      sp02Timer1 = millis();  // timer to exit out of sp02 measurements

    }


  }
  if(validSPO2 == 1){
     SP02_Mes.values.flags_measurement = ob0000010010011111;
     if( spo2 >= 93 ){
       SP02_Mes.values.flags_measurement = ob0000011010011111;
     }
     else if( spo2 <= 75 ){
        SP02_Mes.values.flags_measurement = ob0100010010011111;
     }
  }else{
    SP02_Mes.values.flags_measurement = ob1100000000011111;
  }
  Serial.println("We got Sp02 boi");

}

My problem is that the PLX service uses 3 flags, one with 8bit, one with 16bit and one with 24bit. I can not make the 24bit work.
How can I give to a variable a 24bit information?
And I keep getting errors when I try to give the 16bit flag the value that I want like this :
flag16bit = ob1010101100110011;
I am using the arduino Nano 33 BLE.

Because these are only flags and 8-bits are reserved, I would simply use a uint16_t and a uint8_t. The Pulse Oximeter Profile document I have is from 2015. The specification is stable. Unlikely the additional flags will be needed anytime soon. Even if, you can then pretend the flags are separate. The central will see an array of bytes anyway. The struct is just a nice way to access each element.

I believe the first character needs to be a zero 0.

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.