How to connect eHealth via Bluetooth with Python to Computer or Raspberry PI

5/5 - (18 votes)

I explain how to connect the eHealth sensor board from Cooking Hacks via Bluetooth to a Linux-based PC (or Raspberry Pi) to stream data and plot it on the computer with the bluePy library.

The eHealth Board

The eHealth sensor board is an Arduino-based board. It is a nice development platform to explore the biomedical sensors, but it has many drawbacks in terms of low reliability of the sensors. Depending on the application this sensor board can be adequate or not.

Generally, I think, the price does not worth with the product you get

Better you get separately (by other vendors maybe) the sensors you may need.

In my application, I wanted to stream real-time data from the human body and plot them in real-time wirelessly (via Bluetooth Low energy). The data should be as accurate and reliable as possible. The idea is to track human activities, so the sensors should be integrated somehow and send data continuously without the interaction of the person under test.

eHealth sensors

A Review over the eHealth Sensors

Here I explain the sensors I used, which not and why. The conclusions after testing all the eHealth sensors:

Patient position: this is in reality the NXP-accelerometer MMA8452Q. If we would like to use an accelerometer we would use better directly one like this from sparkfun with the same MMA8452Q component and 10$ instead of the 48$ of cooking hacks. Even you can buy the chip for less than 2$ in mouser or farnell.

https://www.cooking-hacks.com/media/catalog/product/cache/1/thumbnail/9df78eab33525d08d6e5fb8d27136e95/p/u/pulsometro_pecho_presentacion.1471339015.png SparkFun Triple Axis Accelerometer Breakout - MMA8452Q (with Headers)

Blood Pressure Sensor (Sphygmomanometer): This sensor does not allow to stream real time data. It is only one-time-measurements. This does not fit to add to my bio data collection project.

Pulse and Oxygen in Blood Sensor (SPO2): This sensor is not reliable. Actually it was funny when studying in deep the sensor I notice something wrong. Looking into the library, the transmitted data where read from the LCD, yes directly from the LCS.

The LCD input pins were bridged to the connector, later by a rather complex function in the software, the conversion of LCD-digits to a integer was made.

The business from “cooking-hacks” is: buy in ebay the oximeter sensor for 10€, hack it connecting 10 cables and a connector, for later sell it for only 66€. Fabulous.

The thing is that the solution is very smart and it works fine for small applications. The LCD has 3 digits, which are multiplexed. So there are 8 lines + one control line. The control or selector line is read with an interrupt, then you can read the digits and order them creating an integer value of the lecture.

The problem comes when your program is getting bigger, maybe you have many other “real time” measurements or you want to use more interrupts. Then stopping the program flow every time the selector line trigger to calculate a new digit, lows the overall performance a lot. For my case was almost impossible to synchronize the BLE and reading other sensors.

That is why this sensor was also discarded.

C:\Users\alopez\AppData\Local\Microsoft\Windows\INetCache\Content.Word\IMG_20180227_121918.jpg
Inside the oximeter sensor
C:\Users\alopez\AppData\Local\Microsoft\Windows\INetCache\Content.Word\IMG_20180227_121925.jpg
Inside the hacked sensor

A smarter solution for this would be to include an extra microcontroller inside which perform the calculation of the LCDs and sent it back vie SPI or I2C. Of course it is more costly.

Galvanic Skin Response Sensor (GSR – Sweating): This sensor only measured the voltage differences between 2 pins. The values jump from time to time. The values have to be filtered or threated.

Electrocardiogram Sensor (ECG): The heart rate have to be calculated out of this measurements. There is another better and more comfortable solutions to integrate on the system, like the ant+ heart rate sensors.

Electromyography Sensor (EMG): We discarded this sensor because I have already 22 of this integrated on the Athos T-shirt and shorts. Also the ECG sensor cannot be used at the same time than the EMG.

Temperature: This sensor work quite fine and accurate. It can be also calibrated by measuring the real resistors value of your board and correct this error by software.

Glucometer: This sensor make isolate measurements and it does not stream values. The process of measurement cannot be automated: You need to put a drop of blood in the probe, then connect the cable to the Arduino for send the data to Arduino. This does not match with my project design.

Airflow: This sensor works very fine. It send a value between 0 and 1023 when you blow air from your nose. The problem I find to track the breathing is that when you breathe through your mouth you cannot measure nothing.

Finally I implemented the breathing sensor, the electrocardiogram, temperature sensor and the skin-resistance sensor were implemented in the Arduino Curie 101, because it has already built-in with BLE capabilities (and because I wanted to try the combination of Intel-arduino)

How does it work?

A program should run in the Arduino and another software in the computer side, a Linux-based machine.

Arduino side

To implement the Arduino Code I wrote a specific library combining the BLE Curie lib ad the eHealth library. This library you can find here and I called “SensorSuitBLE”. This can be found at the end of the post.

The Arduino software *.ino is:

>#include 

SensorSuit Sensor;

void setup() {
  delay(5000);
    Sensor.initSystem();
    Sensor.initBLE();
  
    
}

void loop() {
  while(Sensor.amIconnected()==0){
    //Waiting for a Master to connect
    }
    Serial.println("Connected to central"); 
    //Serial.println(central.address());//print the central's MAC address:
        
        //Serial.print("I am connected = "); Serial.println(Sensor.amIconnected());
        
        while (Sensor.amIconnected()) {
        delay(100);
      //long currentMillis = millis();
        Sensor.updateSensors();
        Sensor.decodeConfigBLE();
     }

  Serial.println("Disconnected from central ");


}//end loop

//================================================
//!Interrupt Service Routine
//================================================
//Interrupt for Serial incomming bytes
void serialEvent() {

    Sensor.decodeSerialInput();
    
}

To debug the Arduino software, it is very useful to use the nRF connect app from NordicSemiconductors. There you can with you smart-phone scan and read the characteristics and notifications from the Arduino.

This is a first version of the project, which is logically very improvable and optimizable. The device name is “eHealth-board”, but this can be changed if you like something different.

Note: many of the connecting problems can be solved by the infallible and advanced method we learn in the university: RESET. Press the reset button hidden under the red board. Especially, if the connection get lost or disconnect in an improper way, you would need to reset. I should fix this bug in the coming versions, but for now it is ok.

Reset button of Arduino Curie under the eHealth board

Once the Arduino software is finished, tested and ready, you can forget about it. You just need to connect the Arduino to a power supply (USB or Battery) and it will send the lectures of the sensors.

The Voltage supply needs to be between 7 and 12V, therefore I built a battery-holder with the Arduino connector. This is quite bulky and not practical. Instead of this 2 small lithium batteries (3.8V+3.8V) in series can be used instead.

C:\Users\alopez\AppData\Local\Microsoft\Windows\INetCache\Content.Word\IMG_20180227_125347.jpg

Computer side

I am using a XU4. This is a Linux-machine like rasperyPi or an Ubuntu computer. But it would be also exportable to windows (I think). I am using the bluePy library for Bluetooth. You may install this before you can run the script.

To change which sensor you want to activate, there is a configuration byte inside the activation command. Modifying it you can set which sensors are going to be measured.

The python script is the following:

###################################################################
# Connecto to the eHealth board vie BLE with Intel Curie 101
# July 2017
# Know-Center Alberto Lopez
####################################################################
import binascii                    #convert
from bluepy import btle            #Bluetooth low energy lib
from struct import *            #conversions
import csv                        #handle csv files
from datetime import datetime     #fortimestamp
import time                     #for time delays
import subprocess                 #to call unix command


EHEALTH = "84:68:3e:00:19:98"
E9 = "e9:90:00:93:c2:a1"
D5 = "d5:48:d0:91:27:40"
UUID_SUIT = "19b10010-e8f2-537e-4f6c-d104768a1313"

DEBUG =True

def writeData(skin_, temperature_, heartRate_,airFlow_):
    ehealthFile.write(str(datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f")))
    ehealthFile.write(",")
    ehealthFile.write(str(skin_))
    ehealthFile.write(",")
    ehealthFile.write(str(temperature_))
    ehealthFile.write(",")
    ehealthFile.write(str(heartRate_))
    ehealthFile.write(",")
    ehealthFile.write(str(airFlow_))
    ehealthFile.write("\n")

def newFile():
    ehealthFile.write("Time,skin,temperature,heartRate,airFlow\n")
    return

######## MAIN #######

print("Connecting...")
eHealth = btle.Peripheral(EHEALTH) #device is hci0

print("connected!")

SuitSensor = btle.UUID(UUID_SUIT)
SuitService = eHealth.getServiceByUUID(SuitSensor)


SuitSensorConfig = SuitService.getCharacteristics()[0]

# Enable the sensors cmd
SuitSensorConfig.write(bytes("\x33",'utf-8'),True)

time.sleep(1.0) # Allow sensor to stabilise
val = SuitSensorConfig.read()
print("Config raw value", binascii.b2a_hex(val))

SuitSensorVal1 = SuitService.getCharacteristics()[1]
SuitSensorVal2 = SuitService.getCharacteristics()[2]
SuitSensorVal3 = SuitService.getCharacteristics()[3]
SuitSensorVal4 = SuitService.getCharacteristics()[4]

subprocess.Popen(["kst2", "Health.kst"]) #open the real time plotter

with open('Health.csv','w',1) as ehealthFile:
    newFile()

    while True:
        val1 = unpack('4B',SuitSensorVal1.read())        
        valor1 = format(val1[1],'0b').rjust(8,'0') + format(val1[0],'0b').rjust(8,'0')    
        skin = int(valor1[0:16],2)

        val1 = unpack('4B',SuitSensorVal2.read())
        valor1 = format(val1[1],'0b').rjust(8,'0') + format(val1[0],'0b').rjust(8,'0')    
        temperature = int(valor1[0:16],2)

        val1 = unpack('4B',SuitSensorVal4.read())        
        valor1 = format(val1[1],'0b').rjust(8,'0') + format(val1[0],'0b').rjust(8,'0')    
        heartRate = int(valor1[0:16],2)
        
        val1 = unpack('4B',SuitSensorVal3.read())
        valor1 = format(val1[1],'0b').rjust(8,'0') + format(val1[0],'0b').rjust(8,'0')    
        airFlow = int(valor1[0:16],2)
        
        if(DEBUG):
            print("Skin" + str(skin) + "\tTemperature " + str(temperature) + "\theartRate " +str(heartRate) + "\tairFlow " +str(airFlow))
        #time.sleep(0.10) # Allow sensor to stabilise
        writeData(skin,temperature,heartRate,airFlow)

eHealth.disconnect()

To run this script just open a terminal where the file is located and run the commad:

>> sudo python3 ConnectTo101.py

E:\Know\Selection_052.png


If you want something smarter like plotting it in real time in a graph, consider see my other tutorial:

“How to plot data in real-time using KST”

How to use kst to plot data in Real-time

For further information and documentation about sensors and how to connect, then see the official web from cooking hacks.

Library for Arduino

Header file:

/*  eHealth Sensor Suit BLE library
 *  Know-Center Graz
 *	June 2017
 *  Version 1.0
 *  Author: Alberto L. Gasso
 */
#ifndef SensorsuitBLE_h
#define SensorsuitBLE_h

#include "Arduino.h"
#include 

#define SENSOR_NUM 7
#define BIT0 0x01
#define BIT1 0x02
#define BIT2 0x04
#define BIT3 0x08
#define BIT4 0x10
#define BIT5 0x20
#define BIT6 0x40
#define BIT7 0x80

const char*const UUID_SUIT   = "19B10010-E8F2-537E-4F6C-D104768A1313"; //defined random UUID
const char*const ENABLE      = "FFFFFFFF-BEB0-BEB0-DEAD-AABBCCDDEEFF"; //defined random UUID	
const char*const SKIN        = "00000001-BEB0-BeB0-DEAD-AABBCCDDEEFF"; //defined random UUID
const char*const TEMPERATURE = "00000010-BEB0-BeB0-DEAD-AABBCCDDEEFF"; //defined random UUID
const char*const CARDIO      = "00010000-BEB0-BeB0-DEAD-AABBCCDDEEFF"; //defined random UUID
const char*const AIR         = "00100000-BEB0-BeB0-DEAD-AABBCCDDEEFF"; //defined random UUID


  static BLEPeripheral board; // create peripheral instance
  static BLEService eHealthService(UUID_SUIT); // create service

  // create switch characteristic and allow remote device to read and write
 static BLECharCharacteristic  enableCharact(ENABLE, BLERead | BLEWrite);

  // create Sensor characteristic and allow remote device to get notifications
  static BLEIntCharacteristic   skinCharact(SKIN, BLERead | BLENotify); // allows remote device to get notifications
  static BLEIntCharacteristic  temperatureCharact(TEMPERATURE, BLERead | BLENotify); // allows remote device to get notifications
  //static BLECharCharacteristic  rpmCharact(UUID_SUIT, BLERead | BLENotify); // allows remote device to get notifications
  //static  BLECharCharacteristic  spo2Charact(UUID_SUIT, BLERead | BLENotify); // allows remote device to get notifications
  static BLEIntCharacteristic  airCharact(AIR, BLERead | BLENotify); // allows remote device to get notifications
  static BLEIntCharacteristic  cardioCharact(CARDIO, BLERead | BLENotify); // allows remote device to get notifications
  
	// Library interface description
class SensorSuit{
		public: 

 
		//! Configuration parameter of each sensor.
		/*!
		\param void 
		 *		0 == Skin conductance		
		 *		1 == Temperature
		 *		2 == pulse
		 *		3 == oxygen
		 *		4 == Pcardio
		 *		5 == airFlow
		 *		6 == Accelerometer
		 */ static int state[SENSOR_NUM];
		
		/*! enables or disables the time stamp 
			in microseconds since Arduino was reseted
		\param void 
		 *		0 == disabled		
		 *		1 == enabled (default)
		 */	static int timeStamp;
//***************************************************************
// Constructor of the class										*
//***************************************************************
  
		//! Class constructor.
		SensorSuit(void);
		
		
//***************************************************************
// Public Functions												*
//***************************************************************

//BLE functions//

void initBLE(void);

void updateSensors(void);

void checkState(void);


int amIconnected(void);

uint8_t decodeConfigBLE(void);
//BLECentral getCentral(void);



//eHealth library modified functions//

		//! Initializes the BloodPressureSensor sensor and configure some values
		/*!
		\param float parameter with correction value
		\return void
		*/	void readBloodPressureSensor(void);
		
		//! Swap data for blood pressure mesure
	
		char swap(char _data);			
		//! Returns the corporal temperature.
		/*!
		\param void
		\return float : The corporal temperature value.   
		*/	float getTemperature( void );
 
		//! Returns the value of skin conductance. 
		/*!
		\param void
		\return int : The skin conductance value.  
		*/	int getSkinConductance(void);
		
		//! Returns an analogic value to represent the Electrocardiography.
		/*!
		\param void
		\return int : The analogic value (0-5V).  
		*/	int getECG(void);

		//! Returns an analogic value to represent the Electromyography.
		/*!
		\param void
		\return float : The analogic value (0-5V).  
		*/	int getEMG(void);
		 
		//! Returns the  value of the systolic pressure.
		/*!
		\param int 
		\return int : The systolic pressure.
		*/	int getSystolicPressure(int i);

		//! Returns the  value of the diastolic pressure.
		/*!
		\param int  
		\return int : The diastolic pressure.
		*/	int getDiastolicPressure(int i);

		//! Returns an analogic value to represent the air flow.
		/*!
		\param void   
		\return int : The value (0-1023) read from the analogic in.  
		*/	int getAirFlow(void);

		//!  Read the values stored in the glucometer. 
		/*!
		\param void
		\return void
		*/	void readGlucometer(void);

		//!Returns the number of data stored in the glucometer.
		/*!
		\param void
		\return int : length of data 
		*/	uint8_t getGlucometerLength(void);

		//!Returns the number of data stored in the blood pressure sensor.
		/*!
		\param void
		\return int : length of data 
		*/	uint8_t getBloodPressureLength(void);

		//!Struct to store data of the glucometer.
		struct glucoseData {
			uint8_t year; 
			uint8_t month;
			uint8_t day;
			uint8_t hour;
			uint8_t minutes;
			uint8_t glucose;
			uint8_t meridian;
		};

		//!Vector to store the glucometer measures and dates.
		glucoseData glucoseDataVector[8];

		//!Struct to store data of the blood pressure sensor.
		struct bloodPressureData {
			uint8_t year; 
			uint8_t month;
			uint8_t day;
			uint8_t hour;
			uint8_t minutes;
			uint8_t systolic;
			uint8_t diastolic;
			uint8_t pulse;
		};

		//!Vector to store the blood pressure measures and dates.
		bloodPressureData bloodPressureDataVector[8];
		
//Custom methods//

		//! Intialice the system.
		/*!
		\param void
		\return int : 0 when succesful  
		*/ uint8_t initSystem(void);

		   
		 //! read the sensors connected to the system.
		/*!
		\param void
		\return int : 0 when succesful  
		*/ uint8_t readSensors(void);
		
		
		//! Print the sensors connected to the system to debug
		/*!
		\param void
		\return int : 0 when succesful  
		*/ uint8_t printValuesOnTerminal(void);
		
		
		
		//! Print the sensors connected to the system to save data
		/*!
		\param void
		\return int : 0 when succesful  
		*/uint8_t printValuesForProcessing(void);
		
		
		//! Print the sensors connected to the system to debug
		/*!
		\param void
		\return int : 0 when succesful  
		*/ uint8_t decodeSerialInput(void);
		
		
		//! It enables the debug mode
		/*!
		 *		1 == Debug mode on		
		 *		0 == Logging mode on
		 */ uint8_t debudMode;
		 
		 
		/*! Return if the debug mode is on
		\param void
		\return int :	1 == Debug mode on		
		 *				0 == Logging mode on 
		*/ int isDebugModeOn(void);
		 
		//! Print the headers for the enabled sensor for data logging
		/*!
		\param void
		\return int : 0 when succesful  
		*/ uint8_t printHeaders(void);
//***************************************************************
// Private Functions												*
//***************************************************************
		private:
		//! Print the sensors actual configuration status
		/*!
		\param void
		\return int : 0 when succesful  
		*/ uint8_t printSensorStatus(void);

	
		//! Print the sensor state enable or disabled
		/*!
		\param void
		\return int : 0 when succesful  
		*/ uint8_t printStatus(int state);
		
//***************************************************************
// Private Variables											*
//***************************************************************

		//! Time stamp
		unsigned long time;
		
		//! It stores the skin conductance value
		int conductance;

		//! It stores the temperature in Celsius grads value
		int temperature;
		
		//! It stores the pulse value
		int pulse;
		
		//! It stores the skin oxygen in blood value
		int oxygen;
		
		//! It stores the cardiogram signal
		int cardio;

		//! It stores the air flow signal
		int airFlow;
		
		
		struct accel {
			int x;
			int y;
			int z;
		};
		//!It stores the number of data of the glucometer.
		uint8_t length;

};
#endif

Source file:

/*  eHealth Sensor Suit BLE library
 *    June 2017
 *  Version 1.0
 *  Author: Alberto L. Gasso
 */

// include this library's description file
#include "SensorSuitBLE.h"
#include "Arduino.h"

//***************************************************************
//  Variables and definitions                        *
//***************************************************************

    int SensorSuit::state[SENSOR_NUM];
    int SensorSuit::timeStamp;

//***************************************************************
// Constructor of the class                                        *
//***************************************************************

    //! Function that handles the creation and setup of instances
    SensorSuit::SensorSuit(void) { /*void constructor*/ }
    

//***************************************************************
// Public Methods                                                *
//***************************************************************
//!******************************************************************************
//BLE functions//
  
  
void SensorSuit::initBLE(void){
    //BLEPeripheral board; // create peripheral instance
    
     /*  BLEService eHealthService(UUID_SUIT); // create service

    // create switch characteristic and allow remote device to read and write
    BLECharCharacteristic  enableCharact(UUID_SUIT, BLERead | BLEWrite);

    // create Sensor characteristic and allow remote device to get notifications
    BLEFloatCharacteristic  skinCharact(UUID_SUIT, BLERead | BLENotify); // allows remote device to get notifications
    BLEFloatCharacteristic  temperatureCharact(UUID_SUIT, BLERead | BLENotify); // allows remote device to get notifications
    BLECharCharacteristic  rpmCharact(UUID_SUIT, BLERead | BLENotify); // allows remote device to get notifications
    BLECharCharacteristic  spo2Charact(UUID_SUIT, BLERead | BLENotify); // allows remote device to get notifications
    BLEIntCharacteristic  airCharact(UUID_SUIT, BLERead | BLENotify); // allows remote device to get notifications
    BLEIntCharacteristic  cardioCharact(UUID_SUIT, BLERead | BLENotify); // allows remote device to get notifications
   */
    // set the local name peripheral advertises
    board.setLocalName("eHealth-board");
    // set the UUID for the service this peripheral advertises:
    board.setAdvertisedServiceUuid(eHealthService.uuid());

    // add service and characteristics
    board.addAttribute(eHealthService);
    board.addAttribute(enableCharact);
    board.addAttribute(skinCharact);
    board.addAttribute(temperatureCharact);
    //board.addAttribute(rpmCharact);
    //board.addAttribute(spo2Charact);
    board.addAttribute(airCharact);
    board.addAttribute(cardioCharact);
    
    // advertise the service
    board.begin();
    
    Serial.println("Bluetooth device active, waiting for connections...");
}

int SensorSuit::amIconnected(void){
    BLECentral central = board.central();

    if (central) {
        //For Debugging
      //Serial.print("Connected to central: ");
      //print the central's MAC address:
      //Serial.println(central.address());
      return 1;
   } else{
       
       return 0;
   }
    
}



void SensorSuit::updateSensors(void){
    /*Debugging */
    //static float skin = 32;
    //static int temperature2 = 1000;
    //Serial.println("inside updateSensors");
    // skin++;
    //temperature2--;
    /*Debuggin */


    board.poll();
    readSensors(); //Read sensors

    //Write to the server     //TODO: update to the server only the enabled data to save energy
    skinCharact.setValue(conductance);
    //Serial.println(conductance,HEX);
    temperatureCharact.setValue(temperature);
    //rpmCharact.setValue(pulse);
    //spo2Charact.setValue();
    cardioCharact.setValue(cardio);
    airCharact.setValue(airFlow);
    /*
    positionXCharact.setValue(accel.x);
    positionYCharact.setValue(accel.y);
    positionZCharact.setValue(accel.z);
   */
  
}

void SensorSuit::checkState(void){
    
    
    if (enableCharact.written()){
    Serial.print("The configuration is ... ");Serial.println(enableCharact.value(),HEX);
  }

    if (enableCharact.value()){
      Serial.println("Reading on");
    } else {
      Serial.println("Readin off");
    }
}

  /*  BLECentral SensorSuit::getCentral(void){
       
       return board.central();
   }
 */


uint8_t SensorSuit::decodeConfigBLE(void){
    if(enableCharact.written()){  
         int i;
         int incomingByte = enableCharact.value(); // read the incoming byte
        for( i = 0; i<SENSOR_NUM;i++){ if(incomingByte&0x01){ state[i] = 1; //Sensor enable }else{ state[i] = 0; //sensor disable } incomingByte =incomingByte >> 1;
        }
        Serial.println("Sensor State");
        printHeaders();
        /* for( i = 0;i<SENSOR_NUM;i++){ Serial.println(state[i]); } */ return 0; } } //eHealth library modified functions// //!****************************************************************************** //! Name: readBloodPressureSensor() * //! Description: Initializes the BloodPressureSensor sensor. * //! Param : void * //! Returns: void * //! Example: eHealth.initBloodPressureSensor(); * //!****************************************************************************** void SensorSuit::readBloodPressureSensor(void) { unsigned char _data= 0x00; int ia=0; length=0; Serial.begin(19200); Serial.write(0xAA); delayMicroseconds(1); Serial.write(0x55); delayMicroseconds(1); Serial.write(0x88); delay(2500); Serial.print("\n"); if (Serial.available() > 0) { // The protocol sends the measures 

            for (int i = 0; i<4; i++){ // Read four dummy data    
                Serial.read();
            }
            
             while(_data != 0xD1){
                  if (ia==0){
                   _data = Serial.read();
                  }

            bloodPressureDataVector[length].year = swap(_data);
            bloodPressureDataVector[length].month = swap(Serial.read());
            bloodPressureDataVector[length].day = swap(Serial.read());
            bloodPressureDataVector[length].hour = swap(Serial.read());
            bloodPressureDataVector[length].minutes = swap(Serial.read());
            bloodPressureDataVector[length].systolic = swap(Serial.read());
            bloodPressureDataVector[length].diastolic = swap(Serial.read());
            bloodPressureDataVector[length].pulse = swap(Serial.read());
            length++;
            ia=1;
            for (int i = 0; i<4; i++){ // CheckSum 1
                Serial.read();
            }
            
            _data = Serial.read();
            }


             for (int i = 0; i<3; i++){ // CheckSum 2 Serial.read(); } } } //! Swap data for blood pressure mesure char SensorSuit::swap(char _data) { char highBits = (_data & 0xF0) / 16; char lowBits = (_data & 0x0F) * 16; return ~(highBits + lowBits); } //!****************************************************************************** //! Name: getSystolicPressure() * //! Description: Returns the value of the systolic pressure. * //! Param : int * //! Returns: int with the systolic pressure. * //! Example: int systolic = getSystolicPressure(1); * //!****************************************************************************** int SensorSuit::getSystolicPressure(int i) { return bloodPressureDataVector[i].systolic; } //!****************************************************************************** //! Name: getDiastolicPressure() * //! Description: Returns the value of the diastolic pressure. * //! Param : int * //! Returns: int with the diastolic pressure. * //! Example: int diastolic = getDiastolicPressure(1); * //!****************************************************************************** int SensorSuit::getDiastolicPressure(int i) { return bloodPressureDataVector[i].diastolic; } //!****************************************************************************** //! Name: readGlucometer() * //! Description: It reads the data stored in the glucometer * //! Param : void * //! Returns: void * //! Example: readGlucometer(); * //!****************************************************************************** void SensorSuit::readGlucometer(void) { // Configuring digital pins like INPUTS pinMode(5, OUTPUT); digitalWrite(5, HIGH); delay(100); Serial.begin(1200); delay(100); Serial.print("U"); // Start communication command. delay(1000); // Wait while receiving data. Serial.print("\n"); if (Serial.available() > 0) {
            length = Serial.read();// The protocol sends the number of measures 
            Serial.read(); // Read one dummy data

            for (int i = 0; i<length; i++) { // The protocol sends data in this order glucoseDataVector[i].year = Serial.read(); glucoseDataVector[i].month = Serial.read(); glucoseDataVector[i].day = Serial.read(); glucoseDataVector[i].hour = Serial.read(); glucoseDataVector[i].minutes = Serial.read(); Serial.read(); // Byte of separation must be 0x00. glucoseDataVector[i].glucose = Serial.read(); glucoseDataVector[i].meridian = Serial.read(); Serial.read(); // CheckSum 1 Serial.read(); // CheckSum 2 } } digitalWrite(5, LOW); } //!****************************************************************************** //! Name: getGlucometerLength() * //! Description: it returns the number of data stored in the glucometer * //! Param : void * //! Returns: uint8_t with length * //! Example: int length = getGlucometerLength(); * //!****************************************************************************** uint8_t SensorSuit::getGlucometerLength(void) { return length; } //!****************************************************************************** //! Name: getBloodPressureLength() * //! Description: it returns the number of data stored in * //! the blood pressure sensor * //! Param : void * //! Returns: uint8_t with length * //! Example: int length = getBloodPressureLength(); * //!****************************************************************************** uint8_t SensorSuit::getBloodPressureLength(void) { return length; } //!****************************************************************************** //! Name: getTemperature() * //! Description: Returns the corporal temperature. * //! Param : void * //! Returns: float with the corporal temperature value. * //! Example: float temperature = getTemperature(); * //!****************************************************************************** float SensorSuit::getTemperature(void) { //Local variables float Temperature= 0.0; //Corporal Temperature float Resistance; //Resistance of sensor. float ganancia=5.0; float Vcc=3.3; float RefTension=3.0; // Voltage Reference of Wheatstone bridge. float Ra=4700.0; //Wheatstone bridge resistance. float Rc=4700.0; //Wheatstone bridge resistance. float Rb=821.0; //Wheatstone bridge resistance. int sensorValue = analogRead(A3); float voltage2=((float)sensorValue*Vcc)/1023; // binary to voltage conversion // Wheatstone bridge output voltage. voltage2=voltage2/ganancia; // Resistance sensor calculate float aux=(voltage2/RefTension)+Rb/(Rb+Ra); Resistance=Rc*aux/(1-aux); if (Resistance >=1822.8) {
            // if temperature between 25ºC and 29.9ºC. R(tª)=6638.20457*(0.95768)^t
            Temperature=log(Resistance/6638.20457)/log(0.95768);  
        } else {
            if (Resistance >=1477.1){
                    // if temperature between 30ºC and 34.9ºC. R(tª)=6403.49306*(0.95883)^t
                    Temperature=log(Resistance/6403.49306)/log(0.95883);  
            } else {
                if (Resistance >=1204.8){
                    // if temperature between 35ºC and 39.9ºC. R(tª)=6118.01620*(0.96008)^t
                    Temperature=log(Resistance/6118.01620)/log(0.96008); 
                }
                else{
                    if (Resistance >=988.1){
                        // if temperature between 40ºC and 44.9ºC. R(tª)=5859.06368*(0.96112)^t
                        Temperature=log(Resistance/5859.06368)/log(0.96112); 
                    }
                    else {
                        if (Resistance >=811.7){
                            // if temperature between 45ºC and 50ºC. R(tª)=5575.94572*(0.96218)^t
                            Temperature=log(Resistance/5575.94572)/log(0.96218); 
                        }
                    }
                }
            }  
        }
        return Temperature;
    }
    //!******************************************************************************
    //!        Name:    getSkinConductance()                                            *
    //!        Description: Returns the value of skin conductance.                        *
    //!        Param : void                                                            *
    //!        Returns: int with the value of skin conductance                        *
    //!        Example: int conductance = getSkinConductance();                *
    //!******************************************************************************

    int SensorSuit::getSkinConductance(void)
    {
        // Given formula by the vendor: 2*(((sensorValue*5.0/1023) - 0.5) / 100000);
        int conductance = ((((analogRead(A2)+1)*5)-512)*20);
        //conductance1 = conductance1>>8;
        
        return conductance; //Return (value/1024) in uSiemens
        
    }
    
    //!******************************************************************************
    //!        Name:    getAirFlow()                                                    *
    //!        Description: Returns an analogic value to represent the air flow.        *
    //!        Param : void                                                            *
    //!        Returns: int with the airFlow value (0-1023).                            *
    //!        Example: int airFlow = getAirFlow();                            *
    //!******************************************************************************

    int SensorSuit::getAirFlow(void)
    {
        int airFlow = analogRead(A1);

        
        return airFlow; 
    }
    
        //!******************************************************************************
    //!        Name:    getECG()                                                        *
    //!        Description: Returns an analogic value to represent the ECG.            *
    //!        Param : void                                                            *
    //!        Returns: int with the ECG value in voltage                             *
    //!        Example: int volt = eHealth.getECG();                                    *
    //!******************************************************************************

    int SensorSuit::getECG(void)
    {
        int analog0 = analogRead(0);
        // binary to voltage conversion (float)analog0 * 5 / 1023.0
        return analog0;   
    }
    
//Custom Functions //    
    //!******************************************************************************
    //!    Name:    initSystem()                                                *
    //!    Description: Initializes the sensor and configure some values.        *
    //!    Param : void                                                                *
    //!    Returns: void                                                                *
    //!    Example: eHealth.initPositionSensor();                                        *
    //!******************************************************************************

    uint8_t SensorSuit::initSystem(void){
        debudMode = 1; //Debug mode on
        timeStamp = 1; //Time stamp on
        //start communication with computer
        Serial.begin(115200);
        //eHealth.AccelStatus();
        //eHealth.initPulsioximeter();
        if(debudMode){
            Serial.println("=============================================");
            Serial.println("||   System initialized .... ");
            Serial.println("|| - Project Sensor Suit - ");
            Serial.println("|| - Know Center - June 2017 - ");
            Serial.println("=============================================");
            Serial.println("Send the command cXXXXXXX where X is:");
            Serial.println("1 to enable \n0 to disable sensor");
        }
        return 0;
    }
    
        /*! Return if the debug mode is on
        \param void
        \return int :    1 == Debug mode on        
         *                0 == Logging mode on 
        */
    int SensorSuit::isDebugModeOn(void){
        
        return debudMode;
    }

    uint8_t SensorSuit::readSensors(void){
    if(timeStamp){
        time = micros();
    }    
    if(state[0] ==1){
        conductance = getSkinConductance();
        }
    if(state[1] ==1){
        temperature = getTemperature()*100;
    }
    if(state[2] ==1){
        //pulse = getBPM();
    }
    if(state[3] ==1){
        //oxygen = getOxygenSaturation();
    }
    if(state[4] ==1){
        cardio =getECG();
    }
    if(state[5] ==1){
        airFlow = getAirFlow();
    }
    if(state[6] ==1){
        //position = eHealth.getBodyPosition();
    }
    return 0;    
    }

    uint8_t SensorSuit::printValuesOnTerminal(void){
    // if(timeStamp){
        // Serial.print("Time Stamp : ");Serial.println(time);
    // }
    if(state[0] ==1){
        Serial.print("Conductance : ");Serial.println(conductance);
    }
    if(state[1] ==1){
        Serial.print("Temperature ( C): "); Serial.println(temperature, 2);
    }
    if(state[2] ==1){
        Serial.print("PRbpm : ");           Serial.println(pulse);
    }
    if(state[3] ==1){
        Serial.print("%SPo2 : ");         Serial.println(oxygen);
    }
    if(state[4] ==1){
        Serial.print("cardio/muscle : ");   Serial.println(cardio);
    }
    if(state[5] ==1){
        Serial.print("Air flow : ");        Serial.println(airFlow);
    }
    if(state[6] ==1){
        /* Serial.print("Accelerometer X: ");        Serial.println(positions.x);
        Serial.print("Accelerometer Y: ");        Serial.println(positions.y);
        Serial.print("Accelerometer Z: ");        Serial.println(positions.z); */
    }
    if(state[0] || state[1] ||state[2] || state[3] || state[4] || state[5] || state[6] ==1){
        Serial.println("========================================"); 
    }
            return 0;
    }
    
    uint8_t SensorSuit::printValuesForProcessing(void){
        if(timeStamp==1){
        Serial.print(time);
        Serial.print(";");
    }
        if(state[0] ==1){
            Serial.print(conductance);
            Serial.print(";");
        }
        if(state[1] ==1){
            Serial.print(temperature, 2);
            Serial.print(";");
        }
        if(state[2] ==1){
            Serial.print(pulse);
            Serial.print(";");
        }
        if(state[3] ==1){
            Serial.print(oxygen);
            Serial.print(";");
        }
        if(state[4] ==1){
            Serial.print(cardio);
            Serial.print(";");
        }
        if(state[5] ==1){
            Serial.print(airFlow);
            Serial.print(";");
        }
        if(state[6] ==1){
            /* Serial.print(positions.x);
            Serial.print(";");
            Serial.print(positions.y);
            Serial.print(";");
            Serial.print(positions.z);
            Serial.print(";"); */
        }
        if(state[0] || state[1] ||state[2] || state[3] || state[4] || state[5] || state[6] ==1){
        Serial.println(""); 
    }
        return 0;
    }
    
        
    
    uint8_t SensorSuit::decodeSerialInput(void){
        // read the incoming byte                 
         int incomingByte = Serial.read();
        //Serial.print(incomingByte,DEC);
        switch (incomingByte){
             case 'c': //configuration
               for(int i = 0; i< SENSOR_NUM;i++){
                   incomingByte = Serial.read();
                   switch(incomingByte){
                       case '0': //sensor disable
                           state[i] = 0;
                           break;
                       case '1': //Sensor enable
                           state[i] = 1;
                       break;
                 }}
                 Serial.println("newConfig");
                 
                 if(debudMode){
                    printSensorStatus();
                 }
               break;
             case 's': //status request
                printSensorStatus();
             break;
             case 'd':
                if(debudMode){ 
                    debudMode = 0;
                    Serial.println("StartRecording");
                    printHeaders();
                }
                else {
                    debudMode = 1;
                    
                }
             break;
        }
    return 0;        
    }
    
        //! Print the headers for the enabled sensor for data logging
        /*!
        \param void
        \return int : 0 when succesful  
        */ uint8_t SensorSuit::printHeaders(void){
        if(timeStamp==1){
            Serial.print("TimeStamp");
            Serial.print(";");
    }
        if(state[0] ==1){
            Serial.print("Conductance");
            Serial.print(";");
        }
        if(state[1] ==1){
            Serial.print("Temperature");
            Serial.print(";");
        }
        if(state[2] ==1){
            Serial.print("Pulse");
            Serial.print(";");
        }
        if(state[3] ==1){
            Serial.print("Oxygen");
            Serial.print(";");
        }
        if(state[4] ==1){
            Serial.print("Cardio");
            Serial.print(";");
        }
        if(state[5] ==1){
            Serial.print("AirFlow");
            Serial.print(";");
        }
        if(state[6] ==1){
            Serial.print("Position.x");
            Serial.print(";");
            Serial.print("Position.y");
            Serial.print(";");
            Serial.print("Position.z");
            Serial.print(";");
        }
        if(state[0] || state[1] ||state[2] || state[3] || state[4] || state[5] || state[6] ==1){
        Serial.println(""); 
    }
        return 0;
            
        }
    
//***************************************************************
// Private Methods                                                *
//***************************************************************    
    
    
uint8_t SensorSuit::printSensorStatus(void){
  Serial.print("1- Conductance  is: "); printStatus(state[0]);
  Serial.print("2- Temperature  is: "); printStatus(state[1]);
  Serial.print("3- Pulse  is:\t "); printStatus(state[2]);
  Serial.print("4- Oxygen  is: \t"); printStatus(state[3]);
  Serial.print("5- Cardio  is: \t"); printStatus(state[4]);
  Serial.print("6- Air Flow  is: \t"); printStatus(state[5]);
  Serial.print("7- Accelerometer  is: "); printStatus(state[6]);
  Serial.println("===============*");

  return 0;
}



uint8_t SensorSuit::printStatus(int state){
    switch(state){
        case 0:
            Serial.println("disabled");
        break;
        case 1:
            Serial.println("enabled");
        break;
        default:
            Serial.println("unkown");
        break;
    }
    return 0;
}

2 thoughts on “How to connect eHealth via Bluetooth with Python to Computer or Raspberry PI”

  1. Bonjour Alberto I recently bought the Patient position belt ( the old one 5.96 €) from cooking-hack as i want to use it in a project and connect it first to a standard Arduino and next to a lolin d32 to try to get data via Bluetooth. As normaly the sensor is a MMA8452Q i trying to find the pinout of the connector, I thought to you as you may be already open it during your test Best regards Christophe

    1. Hello Christophe, in the datasheet you can find the pinout and a then following the routes you can now the pinout of the connector. If not something should be labeled in the PCB, right?
      best Regards

Leave a Comment

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