config interface
This commit is contained in:
parent
8ef2c4c551
commit
fe30533868
137
ble_interface.py
137
ble_interface.py
@ -16,6 +16,7 @@ class ble_interface:
|
||||
CURRENT = "current"
|
||||
VOLTAGE = "volts"
|
||||
CONFIGURATION = "conf"
|
||||
RANGE = "range"
|
||||
RFRSH_DELAY = "refresh_delay"
|
||||
ON_CONNECT = "on_connect"
|
||||
|
||||
@ -28,6 +29,7 @@ class ble_interface:
|
||||
self.services: Dict[str, BleakGATTService] = {}
|
||||
self.handlers = {
|
||||
self.CURRENT: self.__current_meas_handler,
|
||||
self.RANGE: self.__range_handler,
|
||||
}
|
||||
|
||||
async def __connect(self):
|
||||
@ -61,18 +63,21 @@ class ble_interface:
|
||||
|
||||
if(s.uuid == normalize_uuid_str(myUUIDs.CONFIGURATION_SERVICE)):
|
||||
self.services[self.CONFIGURATION] = s
|
||||
|
||||
if(s.uuid == normalize_uuid_str(myUUIDs.METROLOGY_RANGE_SERVICE)):
|
||||
self.services[self.RANGE] = s
|
||||
|
||||
def connect(self):
|
||||
self.log.info("connecting")
|
||||
|
||||
self.loop.create_task(self.__connect())
|
||||
|
||||
def subscribe_to(self, to: str):
|
||||
self.loop.create_task(self.__subscribe_to(to))
|
||||
def subscribe_to(self, to: str, char_uuid: str | None = None):
|
||||
self.loop.create_task(self.__subscribe_to(to, char_uuid))
|
||||
|
||||
async def __subscribe_to(self, to: str):
|
||||
async def __subscribe_to(self, to: str, char_uuid: str | None):
|
||||
self.log.info("subscribing to %s", to)
|
||||
for c in self.services[to].characteristics:
|
||||
for c in filter(lambda c: (not char_uuid) or (c.uuid == char_uuid), self.services[to].characteristics):
|
||||
await self.client.start_notify(c, self.handlers[to])
|
||||
|
||||
def add_callback(self, id: str, callback):
|
||||
@ -93,9 +98,125 @@ class ble_interface:
|
||||
handle = next(i for i,c in enumerate(self.services[self.CURRENT].characteristics) if c.handle == char.handle)
|
||||
|
||||
self.call_callback(self.CURRENT, handle, val)
|
||||
if(self.CURRENT in self.callbacks):
|
||||
for c in self.callbacks[self.CURRENT]:
|
||||
c(self, handle, val)
|
||||
|
||||
def __range_handler(self, char: BleakGATTCharacteristic, data: bytearray):
|
||||
val = struct.unpack("<i", data)[0]
|
||||
|
||||
self.call_callback(self.RANGE, val)
|
||||
|
||||
async def get_refresh_delay(self) -> int:
|
||||
s = self.services[self.CONFIGURATION]
|
||||
c = s.get_characteristic(myUUIDs.SAMPLING_RATE_CHAR);
|
||||
if(not c):
|
||||
return -1
|
||||
val = await self.client.read_gatt_char(c)
|
||||
return struct.unpack("<I", val)[0]
|
||||
|
||||
async def get_zero_cali_nsamp(self) -> int:
|
||||
s = self.services[self.CONFIGURATION]
|
||||
c = s.get_characteristic(myUUIDs.ZERO_CALI_NSAMP);
|
||||
if(not c):
|
||||
return -1
|
||||
val = await self.client.read_gatt_char(c)
|
||||
return struct.unpack("<I", val)[0]
|
||||
|
||||
async def get_auto_range(self) -> bool:
|
||||
s = self.services[self.RANGE]
|
||||
c = s.get_characteristic(myUUIDs.AUTO_RANGE_CHAR);
|
||||
if(not c):
|
||||
return False
|
||||
val = await self.client.read_gatt_char(c)
|
||||
self.log.info(val)
|
||||
return struct.unpack("<i", val)[0] != 0
|
||||
|
||||
async def get_range(self) -> bool:
|
||||
s = self.services[self.RANGE]
|
||||
c = s.get_characteristic(myUUIDs.ELECTRIC_CURRENT_RANGE_CHAR);
|
||||
if(not c):
|
||||
return False
|
||||
val = await self.client.read_gatt_char(c)
|
||||
self.log.info(val)
|
||||
return struct.unpack("<i", val)[0] != 0
|
||||
|
||||
|
||||
def update_refresh_delay(self, val: int):
|
||||
pass
|
||||
self.loop.create_task(self.__update_refresh_delay(val))
|
||||
|
||||
async def __update_refresh_delay(self, val: int):
|
||||
s = self.services[self.CONFIGURATION]
|
||||
c = s.get_characteristic(myUUIDs.SAMPLING_RATE_CHAR)
|
||||
if(not c):
|
||||
return
|
||||
|
||||
data = struct.pack("<I", val)
|
||||
|
||||
await self.client.write_gatt_char(c, data)
|
||||
|
||||
|
||||
def zero_cali(self):
|
||||
self.loop.create_task(self.__zero_cali())
|
||||
|
||||
async def __zero_cali(self):
|
||||
s = self.services[self.CONFIGURATION]
|
||||
c = s.get_characteristic(myUUIDs.ZERO_CALI_CHAR)
|
||||
if(not c):
|
||||
return
|
||||
|
||||
data = struct.pack("<B", 1)
|
||||
|
||||
await self.client.write_gatt_char(c, data)
|
||||
|
||||
def update_zero_cali_nsamp(self, val: int):
|
||||
self.loop.create_task(self.__update_zero_cali_nsamp(val))
|
||||
|
||||
async def __update_zero_cali_nsamp(self, val: int):
|
||||
s = self.services[self.CONFIGURATION]
|
||||
c = s.get_characteristic(myUUIDs.ZERO_CALI_NSAMP)
|
||||
if(not c):
|
||||
return
|
||||
|
||||
data = struct.pack("<I", val)
|
||||
|
||||
await self.client.write_gatt_char(c, data)
|
||||
|
||||
|
||||
def reset_offsets(self):
|
||||
self.loop.create_task(self.__reset_offsets())
|
||||
|
||||
async def __reset_offsets(self):
|
||||
s = self.services[self.CONFIGURATION]
|
||||
c = s.get_characteristic(myUUIDs.ZERO_CALI_RESET)
|
||||
if(not c):
|
||||
return
|
||||
|
||||
data = struct.pack("<B", 1)
|
||||
|
||||
await self.client.write_gatt_char(c, data)
|
||||
|
||||
|
||||
def update_auto_range(self, val: bool):
|
||||
self.loop.create_task(self.__update_auto_range(val))
|
||||
|
||||
async def __update_auto_range(self, val: bool):
|
||||
s = self.services[self.RANGE]
|
||||
c = s.get_characteristic(myUUIDs.AUTO_RANGE_CHAR)
|
||||
if(not c):
|
||||
return
|
||||
|
||||
data = struct.pack("<B", val)
|
||||
|
||||
await self.client.write_gatt_char(c, data)
|
||||
|
||||
|
||||
def update_range(self, val: int):
|
||||
self.loop.create_task(self.__update_range(val))
|
||||
|
||||
async def __update_range(self, val: int):
|
||||
s = self.services[self.RANGE]
|
||||
c = s.get_characteristic(myUUIDs.ELECTRIC_CURRENT_RANGE_CHAR)
|
||||
if(not c):
|
||||
return
|
||||
|
||||
data = struct.pack("<i", val)
|
||||
|
||||
await self.client.write_gatt_char(c, data)
|
||||
|
70
main.py
70
main.py
@ -21,10 +21,37 @@ loop: asyncio.AbstractEventLoop
|
||||
device: ble_interface
|
||||
|
||||
current_meas = {}
|
||||
max_current = 0
|
||||
start_time = None
|
||||
|
||||
def clear_plot(sender, app_data):
|
||||
logger.info("clear")
|
||||
global current_meas
|
||||
global start_time
|
||||
global max_current
|
||||
for d in current_meas:
|
||||
current_meas[d] = []
|
||||
start_time = None
|
||||
max_current = 0
|
||||
|
||||
def on_connect_handler(device: ble_interface):
|
||||
device.subscribe_to(device.CURRENT)
|
||||
device.subscribe_to(device.RANGE, normalize_uuid_str(myUUIDs.ELECTRIC_CURRENT_RANGE_CHAR))
|
||||
loop.create_task(update_conf_values())
|
||||
|
||||
async def update_conf_values():
|
||||
refresh_rate = await device.get_refresh_delay()
|
||||
dpg.set_value("refresh delay", refresh_rate)
|
||||
|
||||
zero_cali_nsamp = await device.get_zero_cali_nsamp()
|
||||
dpg.set_value("zero_nsamp", zero_cali_nsamp)
|
||||
|
||||
auto_range = await device.get_auto_range()
|
||||
dpg.set_value("auto_range", auto_range)
|
||||
|
||||
range = await device.get_range()
|
||||
dpg.set_value("range", range)
|
||||
|
||||
|
||||
def current_meas_handler(device: ble_interface, input_handle: int, newval: int):
|
||||
value = newval
|
||||
@ -44,27 +71,59 @@ def current_meas_handler(device: ble_interface, input_handle: int, newval: int):
|
||||
current_meas[handle] = []
|
||||
dpg.add_line_series([], [], label=f"{handle}", parent="y_axis", tag=f"serie{handle}")
|
||||
|
||||
current_meas[handle].append(((time.monotonic_ns() - start_time) * 1e-9, value * 1e-6))
|
||||
current_meas[handle].append(((time.monotonic_ns() - start_time) * 1e-9, value))
|
||||
|
||||
dpg.set_value(f"serie{handle}", [ [v[0] for v in current_meas[handle]], [v[1] for v in current_meas[handle]] ])
|
||||
# dpg.fit_axis_data("x_axis")
|
||||
dpg.fit_axis_data("x_axis")
|
||||
# dpg.fit_axis_data("y_axis")
|
||||
|
||||
global max_current
|
||||
max_current = max(max_current, newval)
|
||||
dpg.set_axis_limits("y_axis", 0., 1.1 * max_current)
|
||||
|
||||
txt = ""
|
||||
for h in current_meas:
|
||||
if(len(current_meas[h]) == 0):
|
||||
continue
|
||||
txt += f"{h} : {current_meas[h][-1][1]} | "
|
||||
dpg.set_value("last_current_values", txt)
|
||||
|
||||
def range_changed_handler(device: ble_interface, val: int):
|
||||
dpg.set_value("range", val)
|
||||
|
||||
def update_refresh_rate(sender, app_data):
|
||||
logger.info("refresh rate %d", app_data)
|
||||
device.update_refresh_delay(app_data)
|
||||
|
||||
def update_zero_nsamp(sender, app_data):
|
||||
logger.info("nsamp %d", app_data)
|
||||
device.update_zero_cali_nsamp(app_data)
|
||||
|
||||
def set_auto_range(sender, app_data):
|
||||
logger.info("auto range %d", app_data)
|
||||
device.update_auto_range(app_data)
|
||||
|
||||
def set_range(sender, app_data):
|
||||
logger.info("set range %d", app_data)
|
||||
device.update_range(app_data)
|
||||
|
||||
async def init_gui():
|
||||
dpg.create_context()
|
||||
dpg.create_viewport()
|
||||
dpg.setup_dearpygui()
|
||||
|
||||
with dpg.window(label="Controls", pos = [0, 0], width = dpg.get_viewport_width(), height = 100):
|
||||
with dpg.window(label="Controls", pos = [0, 0], width = 200, height = dpg.get_viewport_height()):
|
||||
dpg.add_button(label="Connect", callback=device.connect)
|
||||
dpg.add_input_int(label="Refresh Delay", callback=update_refresh_rate, on_enter=True, tag="refresh delay")
|
||||
dpg.add_button(label="Zero", callback=device.zero_cali)
|
||||
dpg.add_input_int(label="Zero nb sample", callback=update_zero_nsamp, on_enter=True, tag="zero_nsamp")
|
||||
dpg.add_button(label="Reset offsets", callback=device.reset_offsets)
|
||||
dpg.add_checkbox(label="Auto-range", callback=set_auto_range, tag="auto_range")
|
||||
dpg.add_input_int(label="Range", callback=set_range, on_enter=True, tag="range")
|
||||
|
||||
with dpg.window(label="Graphs", pos = [0, 100], width = dpg.get_viewport_width(), height = 500):
|
||||
with dpg.window(label="Graphs", pos = [200, 0], width = dpg.get_viewport_width()-200, height = dpg.get_viewport_height()):
|
||||
dpg.add_button(label="Clear", callback=clear_plot)
|
||||
dpg.add_text(label="Current", tag="last_current_values")
|
||||
with dpg.plot(label="Current", width = -1, height = -1):
|
||||
dpg.add_plot_legend()
|
||||
|
||||
@ -72,7 +131,7 @@ async def init_gui():
|
||||
dpg.add_plot_axis(dpg.mvYAxis, label="y", tag="y_axis")
|
||||
|
||||
dpg.set_axis_limits_auto("x_axis")
|
||||
dpg.set_axis_limits_auto("y_axis")
|
||||
# dpg.set_axis_limits_auto("y_axis")
|
||||
|
||||
dpg.show_viewport()
|
||||
while dpg.is_dearpygui_running():
|
||||
@ -92,6 +151,7 @@ if __name__ == "__main__":
|
||||
device = ble_interface("84:F7:03:1B:C6:A2", loop, logger)
|
||||
device.add_callback(device.ON_CONNECT, on_connect_handler)
|
||||
device.add_callback(device.CURRENT, current_meas_handler)
|
||||
device.add_callback(device.RANGE, range_changed_handler)
|
||||
|
||||
loop.create_task(init_gui())
|
||||
loop.run_forever()
|
||||
|
@ -6,6 +6,7 @@ VOLTAGE_CHAR = "2B18"
|
||||
SAMPLING_RATE_CHAR = "2C12"
|
||||
ZERO_CALI_CHAR = "2C13"
|
||||
ZERO_CALI_NSAMP = "2C14"
|
||||
ZERO_CALI_RESET = "2C16"
|
||||
ELECTRIC_CURRENT_RANGE_CHAR = "2AEF"
|
||||
AUTO_RANGE_CHAR = "2C15"
|
||||
CHAR_PRES_FORMAT = "2904"
|
||||
|
Loading…
x
Reference in New Issue
Block a user