m-teng/m_teng/backends/arduino/arduino.py

124 lines
4.3 KiB
Python
Raw Normal View History

2023-06-18 17:38:10 +02:00
import bleak as b
import asyncio
import numpy as np
TARGET_NAME = "ArduinoTENG"
# GATT service and characteristics UUIDs
TENG_SUUID = "00010000-9a74-4b30-9361-4a16ec09930f"
TENG_STATUS_CUUID = "00010001-9a74-4b30-9361-4a16ec09930f"
TENG_COMMAND_CUUID = "00010002-9a74-4b30-9361-4a16ec09930f"
TENG_READING_CUUID = "00010003-9a74-4b30-9361-4a16ec09930f"
TENG_SETTING_INTERVAL_CUUID = "00010004-9a74-4b30-9361-4a16ec09930f"
TENG_COMMANDS = {
"NOOP": int(0).to_bytes(1),
"MEASURE_BASELINE": int(1).to_bytes(1),
}
TENG_STATUS = ["ERROR", "BUSY", "WAIT_CONNECT", "MEASURING_BASELINE", "READING"]
# TODO save measurements on device buffer, transfer later
# wrapper for global variable
class Buffer:
def __init__(self):
self.data = None
_buffer = Buffer()
def teng_status_callback(characteristic, data):
value = int.from_bytes(data, byteorder="big", signed=False)
if 0 <= value and value < len(TENG_STATUS):
print(f"Status change: {TENG_STATUS[value]}")
else:
print(f"Status change (invalid): status={value}")
def disconnect_callback(client):
raise Exception(f"The bluetooth device {client.name} was disconnected")
async def init_arduino_async(n_tries: int=5) -> b.BleakClient:
n_try = 0
if n_tries <= 0: n_tries = "inf"
try:
target_device = None
while target_device is None and (n_tries == "inf" or n_try < n_tries):
print(f"Searching for bluetooth device '{TARGET_NAME}' ({n_try}/{n_tries})", end="\r")
devices = await b.BleakScanner.discover(return_adv=True)
# print(devices)
for adr, (device, adv_data) in devices.items():
if device.name == TARGET_NAME:
# print(adv_data)
target_device = device
break
n_try += 1
if target_device is None:
raise Exception(f"Could not find bluetooth device 'ArduinoTENG'")
# print(f"Found target device: {target_device.name}: {target_device.metadata}, {target_device.details}")
# print(target_device.name, target_device.details)
client = b.BleakClient(target_device, disconnect_callback=disconnect_callback)
return client
except asyncio.exceptions.CancelledError:
raise Exception(f"Cancelled")
def init(beep_success=True, n_tries: int=5) -> b.BleakClient:
"""
Connect to the arduino
@returns: BleakClient
"""
client = asyncio.run(init_arduino_async(n_tries=n_tries))
if beep_success: beep(client)
return client
def set_interval(client, interval: float):
"""
Set the measurement interval
@param interval: interval in seconds
"""
interval = int(interval * 1000) # convert to ms for arduinos delay)
await client.write_gatt_char(TENG_SETTING_INTERVAL_CUUID, interval.to_bytes(2, byteorder=LITTLEENDIAN, signed=False))
# async def main():
# for service in client.services:
# print(f"Service: {service.uuid}: {service.description}")
# for c in service.characteristics:
# print(f"\t{c.uuid}: {c.properties}, {c.descriptors}")
# teng_status = client.services.get_characteristic(TENG_STATUS_CUUID)
# teng_command = client.services.get_characteristic(TENG_COMMAND_CUUID)
# teng_reading = client.services.get_characteristic(TENG_READING_CUUID)
# client.start_notify(teng_status, teng_status_callback)
# await client.write_gatt_char(teng_command, TENG_COMMANDS["NOOP"])
# await asyncio.sleep(5)
# await client.write_gatt_char(teng_command, TENG_COMMANDS["MEASURE_BASELINE"])
# while client.is_connected:
# data = await client.read_gatt_char(teng_reading)
# value = int.from_bytes(data, byteorder="little", signed=False)
# print(f"Reading: {value}")
# await asyncio.sleep(0.5)
# except KeyboardInterrupt:
# pass
# except asyncio.exceptions.CancelledError:
# pass
# print("Disconnected")
def collect_buffer(instr, buffer_nr=1):
"""
@param buffer_nr: 1 -> current, 2 -> voltage
"""
assert(buffer_nr in (1, 2))
return np.vstack((_buffer.data[:,0], _buffer._data[:,buffer_nr])).T
def beep(client):
# TODO connect beeper to arduino?
print(beep)