Full Code to Connect to Athos via BLE to a PC

Rate this post

This python code let you connect Athos Training Suit to a linux-based computer like a Raspberry board via Bluetooth and freely.

Some years ago I published my advances on how I achieved connecting the “Athos Coaching System” sensor suit to a computer by applying techniques of reverse engineering. In that article, you can see the hardware set-up, but the main python code was missing…

The goal of all this workaround was to obtain the raw data from the sensors on a computer, in my case a Raspberry Pi, and have them available for any personal use.

Remember that officially, the company Athos only allows to upload the exercise data to their private cloud. Therefore, you can only access your personal data through their mobile app… and ONLY available for iPhones!! wtf ???¿?¿

So my work bridges this limitation and it permits you to avoid their private cloud service and store the data directly on your PC. Then, you can plot them in Real-Time with KST.

The following script in Python is coed to connect a Raspberry Pi via a Bluetooth dongle to the Athos Coaching System.

Enjoy and happy coding!! 🙂

##############################################################################
# Connecto to Athos via BLE
# September 2017
# KC- Alberto Lopez
##############################################################################

#devices owned
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"
UUID_DATA = "4e137a00-c29d-436f-8190-733fc4b08abc"

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
#################################################
DISPOSITIVO = E9
PLOT = True

class MyDelegate(btle.DefaultDelegate):
def __init__(self):
btle.DefaultDelegate.__init__(self)
def handleNotification(self, cHandle, data):
#process data...
if len(data) == 20:
read = unpack('20B',data) #read blocks of 20 hex character
convert =[0 for i in range(len(read))]
binary =[0 for i in range(len(read))]
for i in range(len(read)):
convert[i]= format(read[i],'02X') #convert to Hexadecimal
binary[i] = format(read[i], '08b') #convert to binary
hexad = ''.join(convert)
binarie = ''.join(binary)

print(hexad) #print hexadecimal on the terminal
WriteData(binarie)
WriteRaw(binarie)

def StartCMD_Suit():
Charac1Read.write(bytes("\x73",'utf-8'),True) #cmd for activate the device
time.sleep(2.0) # wait 1 second
athos.writeCharacteristic(28,bytes("\x01\x00",'utf-8'),True) #cmd for start the transmision
print("command sent, Suit configured")
return

#COLUMN FILE
def WriteData(bindata): #write into columns from supposed data
if bindata.startswith('01000000'):
columnFile.write(str(datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f")) + ",")
decimal = int(bindata[20:28],2) #dato0
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[32:40],2) #dato1
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[44:52],2) #dato2
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[60:64],2) #dato3
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[68:72],2) #dato4
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[80:88],2) #dato5
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[92:100],2) #dato6
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[108:116],2) #dato7
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[116:124],2) #dato8
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[128:136],2) #dato9
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[140:148],2) #dato10
columnFile.write(str(decimal)+ "\n")

def WriteData4BITS(bindata): #4 bits columns for Debugging
if bindata.startswith('01000000'):
columnFile.write(str(datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f"))+ ",")
decimal = int(bindata[16:20],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[20:24],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[24:28],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[28:32],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[32:36],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[36:40],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[40:44],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[44:48],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[48:52],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[52:56],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[56:60],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[60:64],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[64:68],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[68:72],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[72:76],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[76:80],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[80:84],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[84:88],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[88:92],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[92:96],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[96:100],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[100:104],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[104:108],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[108:112],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[112:116],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[116:120],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[120:124],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[124:128],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[128:132],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[132:136],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[136:140],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[140:144],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[144:148],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[148:152],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[152:156],2)
columnFile.write(str(decimal)+ ",")
decimal = int(bindata[156:160],2)
columnFile.write(str(decimal)+ "\n")
return

def WriteDataRaw(hexa): #integer in 3 bytes columns
if hexa.startswith('40'):
columnFile.write(str(datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f")))
decimal = int(hexa[1:2],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[2:4],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[4:7],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[7:10],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[10:13],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[13:16],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[16:19],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[19:22],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[22:25],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[25:28],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[28:31],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[31:34],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[34:37],16)
columnFile.write(str(decimal)+ ",")
decimal = int(hexa[37:40],16)
columnFile.write(str(decimal)+ "\n")
return

#RAW FILE
def WriteRaw(bindata): #binary with the columns suppositions
if bindata.startswith('01000000'):
rawFile.write(str(datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f")) +"\t")
rawFile.write(bindata[0:8]+"\t")
rawFile.write(bindata[8:16]+"\t")
rawFile.write(bindata[16:24]+"\t")
rawFile.write(bindata[24:28]+"\t")
rawFile.write(bindata[28:36]+"\t")
rawFile.write(bindata[36:40]+"\t")
rawFile.write(bindata[40:44]+"\t")
rawFile.write(bindata[44:52]+"\t")
rawFile.write(bindata[52:60]+"\t")
rawFile.write(bindata[60:64]+"\t")
rawFile.write(bindata[64:72]+"\t")
rawFile.write(bindata[72:80]+"\t")
rawFile.write(bindata[80:88]+"\t")
rawFile.write(bindata[88:92]+"\t")
rawFile.write(bindata[92:96]+"\t")
rawFile.write(bindata[96:104]+"\t")
rawFile.write(bindata[104:112]+"\t")
rawFile.write(bindata[112:116]+"\t")
rawFile.write(bindata[116:120]+"\t")
rawFile.write(bindata[120:128]+"\t")
rawFile.write(bindata[128:136]+"\t")
rawFile.write(bindata[136:140]+"\t")
rawFile.write(bindata[140:144]+"\t")
rawFile.write(bindata[144:148]+"\t")
rawFile.write(bindata[148:156]+"\t")
rawFile.write(bindata[156:160]+"\n")
return

def WritePureRawBIN(bindata): #pure raw data into file BIN
rawFile.write(str(datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f")) +"\t")
rawFile.write(bindata) #Write the raw hex data into the file
rawFile.write("\n")
return

def WritePureRawHEX(hexa): #pure raw data into file HEX
rawFile.write(str(datetime.now().strftime("%Y/%m/%d %H:%M:%S.%f")) +"\t")
rawFile.write(hexa) #Write the raw hex data into the file
rawFile.write("\n")
return

def newFile():
#for WriteData()
columnFile.write("Time,dato0,dato1,dato2,dato3,dato4,dato5,dato6,dato7,dato8,dato9,dato10\n")

#for WriteData4BITS()
#columnFile.write("Time,b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11,b12,b13,b14,b15,b16,b17,b18,b19,b20,b21,b22,b23,b24,b25,b26,b27,b28,b29,b30,b31,b32,b33,b34,b35\n")

#for WriteDataRaw()
#columnFile.write("Time,counter,Column0,Column1,Column2,Column3,Column4,Column5,Column6,Column7,Column8,Column9,Column10,Column11 \n")

return

def askDeviceInfo():
###Generic Information
GenericConstructor = btle.UUID(0x1800)
GenericService = athos.getServiceByUUID(GenericConstructor)
DeviceNameCharac = GenericService.getCharacteristics()[0]
ConnectionNCharac = GenericService.getCharacteristics()[1]

###Device Information
GeneralConstructor = btle.UUID(0x180A)
GeneralService = athos.getServiceByUUID(GeneralConstructor)
NameCharac = GeneralService.getCharacteristics()[0]
SerialNCharac = GeneralService.getCharacteristics()[1]
print("Sucessfully connected to " + str(NameCharac.read()) + " " + str(DeviceNameCharac.read()) + " device with Serial Number " + str(SerialNCharac.read()))

###Battery state
BatteryConstructor = btle.UUID(0x180F)
BatteryService = athos.getServiceByUUID(BatteryConstructor)
BatteryCharac = BatteryService.getCharacteristics()[0]
print(" ")
print("The battery state is " + str(ord(BatteryCharac.read())) + "%")
return

######## MAIN #######
athos = btle.Peripheral(DISPOSITIVO,btle.ADDR_TYPE_RANDOM,0) #BT device is hci0

athos.withDelegate(MyDelegate()) #create a notification object
askDeviceInfo()

with open('Column.csv','w',1) as columnFile:
with open('raw.csv','w') as rawFile:
newFile()

###Data from Sensors
DatosConstructor = btle.UUID(UUID_DATA)
DatosService = athos.getServiceByUUID(DatosConstructor)
Charac1Read = DatosService.getCharacteristics()[0]
#enable notifications
Charac1Read.write(bytes("\xFF",'utf-8'),True)

StartCMD_Suit()

if PLOT:
#for WriteData()
subprocess.Popen(["kst2", "WriteData.kst"]) #open the real time plotter

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

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

#generic
#subprocess.Popen(["kst2"])

print(type(str(datetime.now())))
print(str(datetime.now()))
print("non-blocking machine")
while True:
if athos.waitForNotifications(1.0):
#handleNotification() calling
continue

 

Leave a Comment

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