How to send data from my Arduino to a computer via Bluetooth in real-time?
In this tutorial, I am going to explain how to stream your measured data in Arduino to receive them in a Linux-based computer like a raspberry PI. Later we can plot the data on real-time I am using KST2.
The problem to solve is simple:
An Arduino connected to some sensors, which streams data continuously to a BLE device (a computer or a Raspberry Pi in this case).
Let’s start!
Contents
Arduino Side
On the Arduino, a normal c-code is running to read the data, process them if needed and send them through BLE.
I am using the new Arduino from Intel Curie 101, which I find very powerful and interesting.
For the measurement examples, I am going to use the eHealth sensor board to read values from some attached sensors: an electrocardiogram, temperature, air-flow and skin conductance sensors.

The Arduino code 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(); }
You need to add my modified eHealth library: SensorSuitBLE.zip
Unzip this folder and add it to your libraries directory. If you are using windows it should be something similar to ~/documents/Arduino/libraries
Computer Side
On the computer, a script in python is going to read the data coming from the Arduino via bluetooth low energy (BLE).
The laptop is using the internal built-in bluetooth device, but also another external USB devices can be use. For example, when I run this in my Odroid XU4 (a kind of raspery Pi) it needs a bluetooth USB dongle to work.
The python script is writing all the data into a CSV (comma separated values) file, which is plotted in real time using kst. Here I explain how to use kst to plot easily and effective data from a text or csv file.
The Python script uses the BluePy library
############################################################################## # Connecto to eHealth via BLE # September 2017 # www.misCircuitos.com Alberto Lopez ############################################################################## #devices owned EHEALTH = "84:68:3e:00:19:98" UUID_SUIT = "19b10010-e8f2-537e-4f6c-d104768a1313" import csv #handle csv files from struct import * #conversions import time #for time delays import binascii #convert from bluepy import btle #Bluetooth low energy lib from datetime import datetime #fortimestamp import subprocess #to call unix command class MyDelegate(btle.DefaultDelegate): def __init__(self): self.skin = 0 self.temperature = 0 self.heartRate = 0 self.airFlow = 0 btle.DefaultDelegate.__init__(self) def handleNotification(self, cHandle, data): #process data... if(cHandle == 0x0d): #skin conductivity read = unpack('4B',data) #read blocks of 4 hex character valor1 = format(read[1],'0b').rjust(8,'0') + format(read[0],'0b').rjust(8,'0') self.skin = int(valor1[0:16],2) if(cHandle == 0x010): read = unpack('4B',data) #read blocks of 4 hex character valor1 = format(read[1],'0b').rjust(8,'0') + format(read[0],'0b').rjust(8,'0') self.temperature = int(valor1[0:16],2) if(cHandle == 0x16): read = unpack('4B',data) #read blocks of 4 hex character valor1 = format(read[1],'0b').rjust(8,'0') + format(read[0],'0b').rjust(8,'0') self.heartRate = int(valor1[0:16],2) if(cHandle == 0x013): #airflow t0 = time.time() read = unpack('4B',data) #read blocks of 4 hex character valor1 = format(read[1],'0b').rjust(8,'0') + format(read[0],'0b').rjust(8,'0') self.airFlow = int(valor1[0:16],2) writeData(self.skin, self.temperature, self.heartRate, self.airFlow) duration = time.time() - t0 #for debugging purposes print(duration) def StartCMD(): #enable the notifications #ehealth.writeCharacteristic(0x0e,bytes("\x01\x00",'utf-8'),withResponse=True) #activate the notif. for skin conductivity #ehealth.writeCharacteristic(0x11,bytes("\x01\x00",'utf-8'),withResponse=True) #activate the notif. for Temperature ehealth.writeCharacteristic(0x14,bytes("\x01\x00",'utf-8'),withResponse=True) #activate the notif. for Airflow ehealth.writeCharacteristic(0x17,bytes("\x01\x00",'utf-8'),withResponse=True) #activate the notif. for Heartrate SuitSensorConfig.write(bytes("\x30",'utf-8'),True) '''#list of bits for commands: bit0: skin conductance bit1: Temperature bit2: pulse bit3: oxygen bit4: Heart Rate bit5: airFlow bit6: accelometer ''' time.sleep(2.0) # wait 1 second print("Configuration command sent") return def writeData(skin_, temperature_, heartRate_,airFlow_): text = str(datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f")) + "," + str(skin_) + "," + str(temperature_) + "," + str(heartRate_) + "," + str(airFlow_) + "\n" ehealthFile.write(text) return def newFile(): ehealthFile.write("Time,skin,temperature,heartRate,airFlow\n") return def askDeviceInfo(): ###Generic Information GenericConstructor = btle.UUID(0x1800) GenericService = ehealth.getServiceByUUID(GenericConstructor) DeviceNameCharac = GenericService.getCharacteristics()[0] ConnectionNCharac = GenericService.getCharacteristics()[1] return ######## MAIN ####### print("Connecting...") ehealth = btle.Peripheral(EHEALTH) #,btle.ADDR_TYPE_RANDOM,0) #BT device is hci0 print("connected") #askDeviceInfo() with open('Health.csv','w',1) as ehealthFile: newFile() ###Data from Sensors DatosConstructor = btle.UUID(UUID_SUIT) DatosService = ehealth.getServiceByUUID(DatosConstructor) SuitSensorConfig = DatosService.getCharacteristics()[0] SuitSensorVal1 = DatosService.getCharacteristics()[1] #skin conductance SuitSensorVal2 = DatosService.getCharacteristics()[2] #temperature SuitSensorVal3 = DatosService.getCharacteristics()[3] #heartRate SuitSensorVal4 = DatosService.getCharacteristics()[4] #airFlow subprocess.Popen(["kst2", "Health.kst"]) #open the real time plotter print(str(datetime.now())) #enable sensors and notifications ehealth.withDelegate(MyDelegate()) #create a notification object print("Waiting for notifications") StartCMD() while True: if ehealth.waitForNotifications(1.0): #handleNotification() calling continue eHealth.disconnect()
The python script is calling to run the program kst, which is in charge of plotting the data.
I already save the layout and configuration setting in a file for this specific application (under the file “Health.kst”), you can download this file and use it or create your own configuration file.
This can be made opening first the kst2 program with the:
sudo kst2
and selecting the file you want to plot, in this case “Health.csv”. More info about how to configure kst was explained by me:

Here is how the plotting looks like. Note in this example I had only the heart rate sensor and airflow sensor attached to the board.