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