diff --git a/ConnectToKano.py b/ConnectToKano.py deleted file mode 100644 index 449b4e0..0000000 --- a/ConnectToKano.py +++ /dev/null @@ -1 +0,0 @@ -etwy \ No newline at end of file diff --git a/ReadCharacteristics.py b/ReadCharacteristics.py deleted file mode 100644 index 42522a7..0000000 --- a/ReadCharacteristics.py +++ /dev/null @@ -1,37 +0,0 @@ -import gatt - -manager = gatt.DeviceManager(adapter_name='hci0') - -class KanoWand(gatt.Device): - def services_resolved(self): - super().services_resolved() - print("Grabbing services") - print(self.is_connected()) - print(self.services) - - for service in self.services: - print("FOUND SERVICE") - # print(service) - # print(service.device) - print(service.uuid) - print(service.characteristics) - for char in service.characteristics: - print("FOUND CHARACTERISTIC") - print(char.uuid) - print(char.read_value()) - # device_information_service = next(s for s in self.services if s.uuid == '0000180a-0000-1000-8000-00805f9b34fb') - # - # firmware_version_characteristic = next( - # c for c in device_information_service.characteristics - # if c.uuid == '00002a26-0000-1000 -8000-00805f9b34fb') - # - # firmware_version_characteristic.read_value() - - def characteristic_value_updated(self, characteristic, value): - print("Firmware version:", value.decode("utf-8")) - - -device = KanoWand(mac_address='e3:ae:cd:af:28:e2', manager=manager) -device.connect() -manager.run() -manager.stop() \ No newline at end of file diff --git a/bleak-characteristic-notify.py b/bleak-characteristic-notify.py deleted file mode 100644 index e69de29..0000000 diff --git a/discover_and_probe_characteristics.py b/discover_and_probe_characteristics.py new file mode 100644 index 0000000..6cde484 --- /dev/null +++ b/discover_and_probe_characteristics.py @@ -0,0 +1,56 @@ +import asyncio +from bleak import discover +from pprint import pprint + +async def run(): + devices = await discover() + for d in devices: + print(d) + +loop = asyncio.get_event_loop() +loop.run_until_complete(run()) + +import asyncio +from bleak import BleakClient + +address = "D8:9B:12:D1:08:80" +MODEL_NBR_UUID = "64a7000f-f691-4b93-a6f4-0968f5b648f8" + +async def run(address, loop): + async with BleakClient(address, loop=loop) as client: + # await client.connect() + print(await client.is_connected()) + print(await client.get_services()) + services = await client.get_services() + pprint(services.descriptors) + pprint(services.characteristics) + pprint(services.services) + # print(services.descriptors) + # for key, val in services.descriptors.items(): + # print(f"{key} + {val}") + # + # print(services.characteristics) + # for key, val in services.characteristics.items(): + # print(f"{key} + {val}") + + print(services) + for x in services: + print(x) + for characteristic in x.characteristics: + print("") + print(characteristic) + print(characteristic.properties) + for descriptor in characteristic.descriptors: + print(descriptor) + print(x.description) + # for i in range(10): + # x = await client.read_gatt_descriptor(i) + # print(x) + + # model_number = await client.read_gatt_char() + # print(model_number) + # print("Model Number: {0}".format("".join(map(chr, model_number)))) + + +loop = asyncio.get_event_loop() +loop.run_until_complete(run(address, loop)) \ No newline at end of file diff --git a/subscribe_to_notification.py b/subscribe_to_notification.py index 0919ad4..27d1270 100644 --- a/subscribe_to_notification.py +++ b/subscribe_to_notification.py @@ -1,62 +1,152 @@ -""" -Notifications -------------- -Example showing how to add notifications to a characteristic and handle the responses. -Updated on 2019-07-03 by hbldh -""" - import logging import asyncio import platform import struct +import time +import pandas as pd from bleak import BleakClient from bleak import _logger as logger -from converters import BinToInt +from converters import BinToInt, BinToFloat + +""" +This is used for reading and decoding values from t he Kano Harry Potter Coding Wand +- Wand sends 25 updates to gyro and accel every second +- Wand sends 3 Dimensional data as 48 bit reverse marshalled integers +- Wand seems to send smaller single dimensional data in 16bit unsigned integers +""" BUTTON = 1 -GYROSCOPE = 2 +NINE_AXIS = 2 ACCELEROMETER = 3 BATTERY = 4 TEMPERATURE = 5 +MAGNETOMETER = 6 INT_DECODER = BinToInt(48) +FLOAT_DECODER = BinToFloat(16, 31) +BUTTON_STATUS = None CHARACTERISTIC_UUIDS = { - # ("64a7000d-f691-4b93-a6f4-0968f5b648f8"):BUTTON,#Button - # ("64a7000a-f691-4b93-a6f4-0968f5b648f8"):GYROSCOPE,#9 axis + ("64a7000d-f691-4b93-a6f4-0968f5b648f8"): BUTTON, # Button + ("64a7000a-f691-4b93-a6f4-0968f5b648f8"): NINE_AXIS, # 9 axis ("64a7000c-f691-4b93-a6f4-0968f5b648f8"): ACCELEROMETER, # Accel # ("64a70007-f691-4b93-a6f4-0968f5b648f8"):BATTERY, - # ("64a70014-f691-4b93-a6f4-0968f5b648f8"):TEMPERATURE + # ("64a70014-f691-4b93-a6f4-0968f5b648f8"):TEMPERATURE, + # ("64a70014-f691-4b93-a6f4-0968f5b648f8"):MAGNETOMETER } # <--- Change to the characteristic you want to enable notifications from. # TODO: RUMBLE # TODO: RGB -# TODO: MAGNETOMETER?? -# - -# device_address = "e3:ae:cd:af:28:e2" device_address = "D8:9B:12:D1:08:80" + +class Gyro(object): + def __init__(self): + self.x = 0 + self.y = 0 + self.z = 0 + + def __str__(self): + print(self.x, self.y, self.z) + + +class Accel(object): + def __init__(self): + self.x = 0 + self.y = 0 + self.z = 0 + + def __str__(self): + print(self.x, self.y, self.z) + + +class Sensors(object): + def __init__(self): + self.gyro = None + self.gyro_updates = 0 + + self.accel = None + self.accel_updates = 0 + self.dataframe = pd.DataFrame(columns=["gyro_x", "gyro_y", "gyro_z", "accel_x", "accel_y", "accel_z"]) + + def append_to_dataframe(self): + # print("Printing the gyro and accel", self.gyro, self.accel) + # print(self.dataframe) + # print(self.gyro_updates, self.accel_updates) + if self.gyro_updates == self.accel_updates: + # print({ + # "gyro_x": self.gyro.x, + # "gyro_y": self.gyro.y, + # "gyro_z": self.gyro.z, + # "accel_x": self.accel.x, + # "accel_y": self.accel.y, + # "accel_z": self.accel.z, + # }) + # try: + # + # except Exception as e: + # print("ERROR HAS OCCURRED") + # print(e) + self.dataframe = self.dataframe.append( + pd.DataFrame({ + "gyro_x": self.gyro.x, + "gyro_y": self.gyro.y, + "gyro_z": self.gyro.z, + "accel_x": self.accel.x, + "accel_y": self.accel.y, + "accel_z": self.accel.z, + }, index=[0]) + ) + + def save_df_to_file(self, filename): + self.dataframe.to_csv(filename, index=False) + + def set_gyro(self, gyro): + self.gyro = gyro + self.gyro_updates += 1 + + def set_accel(self, accel): + self.accel = accel + self.accel_updates += 1 + + def chunker(seq, size): return (seq[pos:pos + size] for pos in range(0, len(seq), size)) -def decode_button(data): - print(data) - print(int.from_bytes(data, byteorder='big')) -def decode_gyroscope(data): - decode_accelerometer(data) +count = 0 + + +def decode_button(data): + global sensors, count + if int.from_bytes(data, byteorder='big'): + print("Button Pressed") + sensors = Sensors() + count += 1 + else: + print("Button Released") + sensors.save_df_to_file(f"dataset{count}.csv") + + +def decode_nine_axis(data): + converted = [INT_DECODER.process(x, True) / 10 ** 14 for x in chunker(data, 6)] + + # print(converted, sum(converted)) + return converted + def decode_accelerometer(data): - converted = [INT_DECODER.process(x, True)/10**14 for x in chunker(data, 6)] - print(converted,sum(converted)) + converted = [INT_DECODER.process(x, True) / 10 ** 14 for x in chunker(data, 6)] + # print(converted, sum(converted)) + return converted + def decode_battery(data): - print(len(data)) - print(data) - print(int.from_bytes(data, byteorder='big')) + global BUTTON_STATUS + BUTTON_STATUS = int.from_bytes(data, byteorder='big') + def decode_temp(data): print(len(data)) @@ -64,30 +154,56 @@ def decode_temp(data): print(struct.unpack("h", data)) +def decode_magnet(data): + print(data) + print(len(data)) + + +count = 0 + +sensors = Sensors() + + def notification_handler(sender, data): + global count, sensors """Simple notification handler which prints the data received.""" - #TODO: Convert to a dictionary and a single decode command + # TODO: Convert to a dictionary and a single decode command sender = CHARACTERISTIC_UUIDS[sender] if sender == BUTTON: decode_button(data) - elif sender == GYROSCOPE: - decode_gyroscope(data) + elif sender == NINE_AXIS: + gyro = Gyro() + # print("getting gyro values") + + gyro.x, gyro.y, gyro.z = decode_nine_axis(data) + # print("got gyro values") + # print(gyro) + sensors.set_gyro(gyro) + sensors.append_to_dataframe() elif sender == ACCELEROMETER: - decode_accelerometer(data) + accel = Accel() + accel.x, accel.y, accel.z = decode_nine_axis(data) + sensors.set_accel(accel) + sensors.append_to_dataframe() + # decode_accelerometer(data) elif sender == BATTERY: decode_battery(data) elif sender == TEMPERATURE: decode_temp(data) + elif sender == MAGNETOMETER: + decode_magnet(data) async def start_notify(client): for char in CHARACTERISTIC_UUIDS.keys(): await client.start_notify(char, notification_handler) + async def stop_notify(client): for char in CHARACTERISTIC_UUIDS.keys(): await client.stop_notify(char) + async def run(address, loop, debug=False): if debug: import sys @@ -105,13 +221,16 @@ async def run(address, loop, debug=False): logger.info("Connected: {0}".format(x)) await start_notify(client) - await asyncio.sleep(60.0, loop=loop) + # await asyncio.sleep(60.0, loop=loop) + while True: + time.sleep(1) await stop_notify(client) if __name__ == "__main__": import os + os.environ["PYTHONASYNCIODEBUG"] = str(1) address = ( device_address # <--- Change to your device's address here if you are using Windows or Linux @@ -119,4 +238,4 @@ if __name__ == "__main__": else "243E23AE-4A99-406C-B317-18F1BD7B4CBE" # <--- Change to your device's address here if you are using macOS ) loop = asyncio.get_event_loop() - loop.run_until_complete(run(address, loop, True)) \ No newline at end of file + loop.run_until_complete(run(address, loop, True)) diff --git a/test2.py b/test2.py deleted file mode 100644 index 2c3655c..0000000 --- a/test2.py +++ /dev/null @@ -1,58 +0,0 @@ -import asyncio -from bleak import discover - -from pprint import pprint - - -async def run(): - devices = await discover() - for d in devices: - print(d) - -loop = asyncio.get_event_loop() -loop.run_until_complete(run()) - -# import asyncio -# from bleak import BleakClient -# -# address = "e3:ae:cd:af:28:e2" -# MODEL_NBR_UUID = "64a7000f-f691-4b93-a6f4-0968f5b648f8" -# -# async def run(address, loop): -# async with BleakClient(address, loop=loop) as client: -# # await client.connect() -# print(await client.is_connected()) -# print(await client.get_services()) -# services = await client.get_services() -# pprint(services.descriptors) -# pprint(services.characteristics) -# pprint(services.services) -# # print(services.descriptors) -# # for key, val in services.descriptors.items(): -# # print(f"{key} + {val}") -# # -# # print(services.characteristics) -# # for key, val in services.characteristics.items(): -# # print(f"{key} + {val}") -# -# print(services) -# for x in services: -# print(x) -# for characteristic in x.characteristics: -# print("") -# print(characteristic) -# print(characteristic.properties) -# for descriptor in characteristic.descriptors: -# print(descriptor) -# print(x.description) -# # for i in range(10): -# # x = await client.read_gatt_descriptor(i) -# # print(x) -# -# # model_number = await client.read_gatt_char() -# # print(model_number) -# # print("Model Number: {0}".format("".join(map(chr, model_number)))) -# -# -# loop = asyncio.get_event_loop() -# loop.run_until_complete(run(address, loop)) \ No newline at end of file