diff --git a/.vscode/launch.json b/.vscode/launch.json index 71d13db..31c4c92 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "debugpy", "request": "launch", "program": "entrypoint.py", - "console": "integratedTerminal", + "console": "internalConsole", "args": [ "--local", "--custom" diff --git a/boxflat/app.py b/boxflat/app.py index 0adc548..64d6dba 100644 --- a/boxflat/app.py +++ b/boxflat/app.py @@ -24,7 +24,7 @@ def __init__(self, data_path: str, config_path: str, dry_run: bool, custom: bool self._panels = {} self._dry_run = dry_run - self.set_default_size(0, 800) + self.set_default_size(0, 850) self.set_title("Boxflat") # self.search_btn = Gtk.ToggleButton() # Search Button diff --git a/boxflat/bitwise.py b/boxflat/bitwise.py index dbeafe2..1ae3679 100644 --- a/boxflat/bitwise.py +++ b/boxflat/bitwise.py @@ -29,3 +29,9 @@ def unset_bit(value, bit_number: int) -> int: def bit(bit_number: int) -> int: return set_bit(0, bit_number) + + +def swap_nibbles(value: int) -> int: + ret = (value & 0x0f) << 4 + ret |= (value & 0xf0) >> 4 + return ret diff --git a/boxflat/connection_manager.py b/boxflat/connection_manager.py index 83e02fd..620bbcf 100644 --- a/boxflat/connection_manager.py +++ b/boxflat/connection_manager.py @@ -1,19 +1,15 @@ import yaml import os.path -from binascii import hexlify from .moza_command import * from serial import Serial from threading import Thread, Lock, Event import time from .hid_handler import MozaHidDevice -from .subscription import SubscriptionList, EventDispatcher +from .subscription import SubscriptionList, EventDispatcher, BlockingValue from queue import SimpleQueue +from .serial_handler import SerialHandler -import gi -gi.require_version('Gtk', '4.0') -from gi.repository import GLib - -CM_RETRY_COUNT=2 +CM_RETRY_COUNT=1 HidDeviceMapping = { "base" : MozaHidDevice.BASE, @@ -27,15 +23,11 @@ } -POLLING_ERROR_EXCLUSIONS = [ - "wheel-knob-mode" -] - - -class MozaQueueElement(): - def __init__(self, value=None, command_name=None): - self.value = value - self.command_name = command_name +class MozaSerialDevice(): + def __init__(self): + self.name = "" + self.path = "" + self.serial_handler = None @@ -49,6 +41,8 @@ def __init__(self, serial_data_path: str, dry_run=False): self._serial_devices = {} self._devices_lock = Lock() + self._exclusive_access = Event() + self._exclusive_access.set() with open(serial_data_path) as stream: try: @@ -61,22 +55,25 @@ def __init__(self, serial_data_path: str, dry_run=False): self._device_ids = self._serial_data["device-ids"] # register events - self._command_list = list(self._serial_data["commands"].keys()) + self._command_list = [] self._polling_list = [] - for command, data in self._serial_data["commands"].items(): - if self._device_ids[command.split("-")[0]] == -1: + for device in self._serial_data["commands"]: + if self._device_ids[device] == -1: continue - if data["read"] == -1: - continue + for command, data in self._serial_data["commands"][device].items(): + event_name = f"{device}-{command}" + self._command_list.append(event_name) - self._polling_list.append(command) + if data["read"] == -1: + continue + + self._polling_list.append(event_name) self._register_events(*self._polling_list) self._register_events("device-connected", "hid-device-connected") self._register_events("shutdown", "no-access") - self._serial_lock = Lock() self._refresh_cont = Event() @@ -84,12 +81,10 @@ def __init__(self, serial_data_path: str, dry_run=False): self._connected_thread = None self._connected_lock = Lock() - self._write_queue = SimpleQueue() - self._write_thread = None - self._message_start= int(self._serial_data["message-start"]) self._magic_value = int(self._serial_data["magic-value"]) self._serial_path = "/dev/serial/by-id" + # self._serial_path = "/dev/pts" def shutdown(self, *rest): @@ -114,35 +109,41 @@ def device_discovery(self, *args): serial_devices = {} for device in devices: if device.lower().find("base") != -1: - serial_devices["base"] = device - serial_devices["main"] = device + obj = MozaSerialDevice() + obj.path = device + obj.name = "base" + serial_devices["base"] = obj + # serial_devices["main"] = device # print("Base found") elif device.lower().find("hbp") != -1: - serial_devices["handbrake"] = device + obj = MozaSerialDevice() + obj.path = device + obj.name = "handbrake" + serial_devices["handbrake"] = obj # print("Handbrake found") elif device.lower().find("hgp") != -1: - serial_devices["hpattern"] = device + obj = MozaSerialDevice() + obj.path = device + obj.name = "hpattern" + serial_devices["hpattern"] = obj # print("H-Pattern shifter found") elif device.lower().find("sgp") != -1: - serial_devices["sequential"] = device + obj = MozaSerialDevice() + obj.path = device + obj.name = "sequential" + serial_devices["sequential"] = obj # print("Sequential shifter found") elif device.lower().find("pedals") != -1: - serial_devices["pedals"] = device + obj = MozaSerialDevice() + obj.path = device + obj.name = "pedals" + serial_devices["pedals"] = obj # print("Pedals found") - # TODO: Check this info somehow - # elif device.lower().find("hub") != -1: - # serial_devices["hub"] = device - # print("Hub found") - - # elif device.lower().find("stop") != -1: - # serial_devices["estop"] = device - # print("E-Stop found") - self._handle_devices(serial_devices) # print("Device discovery end\n") @@ -151,13 +152,23 @@ def _handle_devices(self, new_devices: dict): old_devices = None with self._devices_lock: old_devices = self._serial_devices - self._serial_devices = new_devices for device in new_devices: if device not in old_devices: + new_devices[device].serial_handler = SerialHandler( + new_devices[device].path, + self._message_start, device) + + new_devices[device].serial_handler.subscribe(self._receive_data, device) self._dispatch("device-connected", device) self._dispatch("hid-device-connected", HidDeviceMapping[device]) + else: + new_devices[device].serial_handler = old_devices[device].serial_handler + + with self._devices_lock: + self._serial_devices = new_devices + if len(new_devices) == 0 and self._refresh_cont.is_set(): self.refresh_cont(False) @@ -181,21 +192,18 @@ def refresh_cont(self, active: bool): def _polling_thread(self): while self._refresh_cont.is_set(): - time.sleep(0.5) + time.sleep(2) for command in self._polling_list: if self._event_sub_count(command) == 0: continue - # print("Polling data: " + command) - response = self.get_setting(command) - if response is None: - continue - - self._dispatch(command, response) + self._get_setting(command) + time.sleep(0.005) def _device_polling(self): + time.sleep(1) while not self._shutdown.is_set(): self.device_discovery() @@ -205,11 +213,12 @@ def _device_polling(self): self._clear_event_subscriptions("no-access") for command, subs in lists.items(): value = self.get_setting(command) + # value = 1 if value is None: value = -1 subs.call_with_value(value) - time.sleep(1) + time.sleep(2) self._connected_thread = None @@ -226,22 +235,11 @@ def set_cont_active(self, active: bool): def set_write_active(self, *args): - if not self._write_thread: - self._write_thread = Thread(daemon=True, target=self._write_handler) - self._write_thread.start() - if not self._connected_thread: self._connected_thread = Thread(daemon=True, target=self._device_polling) self._connected_thread.start() - def _write_handler(self): - while not self._shutdown.is_set(): - element = self._write_queue.get() - self._handle_setting(element.value, element.command_name, True) - self._write_thread = None - - def _get_device_id(self, device_type: str) -> int: id = int(self._serial_data["device-ids"][device_type]) if device_type != "base" and device_type in self._serial_devices: @@ -249,117 +247,44 @@ def _get_device_id(self, device_type: str) -> int: return id - def _get_device_path(self, device_type: str) -> str: - device_path = None - + def _get_device_handler(self, device_type: str) -> SerialHandler: + device_handler = None with self._devices_lock: if device_type in self._serial_devices: - device_path = self._serial_devices[device_type] + device_handler = self._serial_devices[device_type].serial_handler elif device_type != "hub" and "base" in self._serial_devices: - device_path = self._serial_devices["base"] - - return device_path - - - def send_serial_message(self, serial_path: str, message: bytes, read_response=False) -> bytes: - # msg = "" - # for b in message: - # msg += f"{hex(b)} " - # print(f"\nDevice: {serial_path}") - # print(f"Sending: {msg}") - - if self._dry_run: - return - - if serial_path == None: - # print("No compatible device found!") - return - - initial_len = message[1] - rest = bytes() - length = 0 - cmp = bytes([self._message_start]) - start = bytes() - - self._serial_lock.acquire() - try: - serial = Serial(serial_path, baudrate=115200, timeout=0.01) - time.sleep(1/500) - serial.reset_output_buffer() - serial.reset_input_buffer() - for i in range(CM_RETRY_COUNT): - serial.write(message) - - #time.sleep(1/500) - - # read_response = True # For teesting writes - start_time = time.time() - while read_response: - if time.time() - start_time > 0.02: - # print("Time's up!") - read_response = False - break - - start = serial.read(1) - if start != cmp: - continue - - length = int.from_bytes(serial.read(1)) - if length != message[1]: - continue - - # length + 3 because we need to read - # device id and checksum at the end - rest = serial.read(length+3) - if rest[2] != message[4]: - continue - break + device_handler = self._serial_devices["base"].serial_handler - serial.close() + return device_handler - #print(time.time() - start_time) - except Exception as error: - # print("Error opening device!") - read_response = False - self._notify_no_access() - self._serial_lock.release() + def _receive_data(self, data: bytes, device_name: str): + # print(f"Received: {data.hex(":")}") + command, value = MozaCommand.value_from_response( + data, device_name, + self._serial_data["commands"], + self._serial_data["ids-to-names"]) - if read_response == False: + if value is None or command is None: return - message = bytearray() - message.extend(cmp) - message.append(length) - message.extend(rest) - - # msg = "" - # for b in message: - # msg += f"{hex(b)} " - # print(f"Response: {msg}") - - return bytes(message) + self._dispatch(command, value) def _handle_command_v2(self, command_data: MozaCommand, rw: int) -> bytes: message = command_data.prepare_message(self._message_start, rw, self._magic_value) - device_path = self._get_device_path(command_data.device_type) + device_handler = self._get_device_handler(command_data.device_type) - response = self.send_serial_message(device_path, message, (rw == MOZA_COMMAND_READ)) - if response is not None: - response = response[-1-command_data.payload_length:-1] + if device_handler is None: + return - # only return payload - return response + device_handler.write_bytes(message) - def _handle_setting(self, value, command_name: str, rw: int) -> bool: - if command_name not in self._command_list: - print("Command not found: " + command_name) - return - - command = MozaCommand(command_name, self._serial_data["commands"]) + def _handle_setting(self, value, command_name: str, device_name: str, rw: int) -> bool: + command = MozaCommand() + command.set_data_from_name(command_name, self._serial_data["commands"], device_name) command.device_id = self._get_device_id(command.device_type) if command.device_id == -1: @@ -375,19 +300,61 @@ def _handle_setting(self, value, command_name: str, rw: int) -> bool: return command.set_payload(value) - response = self._handle_command_v2(command, rw) - if response is None: + self._handle_command_v2(command, rw) + + + def _split_name(self, command_name: str): + if command_name not in self._command_list: + print(f"Command not found: {command_name}") + return None, None + + device_name = command_name.split("-", maxsplit=1)[0] + command_name = command_name.split("-", maxsplit=1)[1] + return command_name, device_name + + + def set_setting(self, value, command_name: str, exclusive=False): + self._exclusive_access.wait() + if exclusive: + self._exclusive_access.clear() + time.sleep(0.005) + + name, device = self._split_name(command_name) + if name is None: return + self._handle_setting(value, name, device, MOZA_COMMAND_WRITE) - return command.get_payload(alt_data=response) + if exclusive: + self._exclusive_access.set() + # if self.get_setting(command_name) != value: + # self._handle_setting(value, name, device, MOZA_COMMAND_WRITE) - def set_setting(self, value, command_name: str): - self._write_queue.put(MozaQueueElement(value, command_name)) + def get_setting(self, command_name: str, exclusive=False): + self._exclusive_access.wait() + if exclusive: + self._exclusive_access.clear() + time.sleep(0.005) - def get_setting(self, command_name: str): - return self._handle_setting(1, command_name, MOZA_COMMAND_READ) + value = BlockingValue() + + self.subscribe_once(command_name, value.set_value) + self._get_setting(command_name, exclusive) + response = value.get_value_no_clear() + + if exclusive: + time.sleep(0.01) + self._exclusive_access.set() + + return response + + + def _get_setting(self, command_name: str, exclusive=False): + name, device = self._split_name(command_name) + if name is None: + return + self._handle_setting(1, name, device, MOZA_COMMAND_READ) def cycle_wheel_id(self) -> int: @@ -405,6 +372,3 @@ def cycle_wheel_id(self) -> int: def get_command_data(self) -> dict: return self._serial_data["commands"] - - -# TODO: Rewrite manager so it keeps a read and write connection open constantly. diff --git a/boxflat/hid_handler.py b/boxflat/hid_handler.py index 27aa6aa..d7d2cdd 100644 --- a/boxflat/hid_handler.py +++ b/boxflat/hid_handler.py @@ -187,7 +187,7 @@ def add_device(self, pattern: MozaHidDevice): fuzz = 0 # detect current fuzz. Needed for ABS_HAT axes - if device.absinfo(ecode).fuzz > 8: + if device.absinfo(ecode).fuzz > fuzz: device.set_absinfo(ecode, fuzz=fuzz) Thread(daemon=True, target=self._hid_read_loop, args=[device]).start() @@ -236,8 +236,8 @@ def _hid_read_loop(self, device: evdev.InputDevice): if event.type == EV_ABS: self._update_axis(device, event.code, event.value) - # elif event.type == EV_KEY: - # self._notify_button(event.code, event.value) + elif event.type == EV_KEY: + self._notify_button(event.code, event.value) except Exception as e: # print(e) diff --git a/boxflat/moza_command.py b/boxflat/moza_command.py index e50977b..cd4bbe3 100644 --- a/boxflat/moza_command.py +++ b/boxflat/moza_command.py @@ -1,26 +1,74 @@ from sys import byteorder from binascii import hexlify from struct import pack, unpack +import boxflat.bitwise as bitwise MOZA_COMMAND_READ=0 MOZA_COMMAND_WRITE=1 MOZA_COMMAND_DEAD=2 class MozaCommand(): - def __init__(self, name:str, commands_data: object): - self.id = list(commands_data[name]["id"]) - self.read_group = int(commands_data[name]["read"]) - self.write_group = int(commands_data[name]["write"]) + def __init__(self): + self.id = 0 + self.read_group = 0 + self.write_group = 0 + self._length = 0 + self._payload = None + self._name = None + self._type = None + self._device_id = None + + + def set_data_from_name(self, name: str, commands_data: dict, device_name: str): + self._device_type = device_name + commands = commands_data[self._device_type] + + self.id = list(commands[name]["id"]) + self.read_group = int(commands[name]["read"]) + self.write_group = int(commands[name]["write"]) - self._length = int(commands_data[name]["bytes"]) - self._payload = bytes(self.length) + self._length = int(commands[name]["bytes"]) + self._payload = bytes(self._length) - self.name = name.split("-", maxsplit=1)[1] - self._device_type = name.split("-")[0] - self._type = commands_data[name]["type"] + self.name = name + self._type = commands[name]["type"] self._device_id = None + @staticmethod + def value_from_response(values: bytes, device_name: str, commands_data: dict, device_ids: dict) -> tuple: + ret = (None, None) + if values is None: + return ret + + group = values[0] + group_byte = bytes([values[0]]) + device_id = values[1] + payload = values[2:] + payload_list = list(payload) + + group = bitwise.unset_bit(group, 7) + device_id = bitwise.swap_nibbles(device_id) + + if device_id not in device_ids: + return ret + + if device_name == "base": + device_name = device_ids[device_id] + + for name, values in commands_data[device_name].items(): + if group != values["read"]: + continue + + id_len = len(values["id"]) + if payload_list[:id_len] != values["id"]: + continue + + ret = f"{device_name}-{name}", MozaCommand.value_from_data(payload[id_len:], values["type"]) + break + return ret + + @property def payload(self) -> bytes: return self.get_payload_bytes() @@ -104,20 +152,27 @@ def set_payload(self, value): self._payload = data - def get_payload(self, alt_data=None): - data = self._payload if alt_data == None else alt_data - if self._type == "int": + def get_payload(self): + return self.value_from_data(self._payload, self._type) + + + @staticmethod + def value_from_data(data: bytes, value_type: str): + if value_type == "int": data = int.from_bytes(data) - elif self._type == "float": + elif value_type == "float": data = unpack(">f", data)[0] - elif self._type == "array": + elif value_type == "array": data = list(data) - elif self._type == "hex": + elif value_type == "hex": data = hexlify(data).decode("utf-8") + else: + data = None + return data @@ -125,7 +180,7 @@ def get_payload_length(self) -> int: return self._length - def _calculate_checksum(self, data: bytes, magic_value: int) -> int: + def checksum(self, data: bytes, magic_value: int) -> int: value = magic_value for d in data: value += int(d) @@ -147,6 +202,6 @@ def prepare_message(self, start_value: int, ret.append(self._device_id) ret.extend(self.id_bytes) ret.extend(self._payload) - ret.append(self._calculate_checksum(ret, magic_value)) + ret.append(self.checksum(ret, magic_value)) return bytes(ret) diff --git a/boxflat/panels/pedals.py b/boxflat/panels/pedals.py index 2f490c5..6389d49 100644 --- a/boxflat/panels/pedals.py +++ b/boxflat/panels/pedals.py @@ -68,8 +68,8 @@ def prepare_ui(self): self._cm.subscribe("pedals-throttle-dir", self._current_row.set_value) self._add_row(BoxflatCalibrationRow("Calibration", "Fully depress throttle once")) - self._current_row.subscribe("calibration-start", self._cm.set_setting, "pedals-throttle-calibration-start") - self._current_row.subscribe("calibration-stop", self._cm.set_setting, "pedals-throttle-calibration-stop") + self._current_row.subscribe("calibration-start", self._cm.set_setting, "pedals-throttle-calibration-start", True) + self._current_row.subscribe("calibration-stop", self._cm.set_setting, "pedals-throttle-calibration-stop", True) # Brake self.add_preferences_page("Brake") @@ -113,8 +113,8 @@ def prepare_ui(self): self._brake_calibration_row = BoxflatCalibrationRow("Calibration", "Fully depress brake once") self._add_row(self._brake_calibration_row) - self._current_row.subscribe("calibration-start", self._cm.set_setting, "pedals-throttle-brake-start") - self._current_row.subscribe("calibration-stop", self._cm.set_setting, "pedals-throttle-brake-stop") + self._current_row.subscribe("calibration-start", self._cm.set_setting, "pedals-throttle-brake-start", True) + self._current_row.subscribe("calibration-stop", self._cm.set_setting, "pedals-throttle-brake-stop", True) self._current_row.set_active(False) # Clutch @@ -153,8 +153,8 @@ def prepare_ui(self): self._cm.subscribe("pedals-clutch-dir", self._current_row.set_value) self._add_row(BoxflatCalibrationRow("Calibration", "Fully depress clutch once")) - self._current_row.subscribe("calibration-start", self._cm.set_setting, "pedals-clutch-calibration-start") - self._current_row.subscribe("calibration-stop", self._cm.set_setting, "pedals-clutch-calibration-stop") + self._current_row.subscribe("calibration-start", self._cm.set_setting, "pedals-clutch-calibration-start", True) + self._current_row.subscribe("calibration-stop", self._cm.set_setting, "pedals-clutch-calibration-stop", True) def _set_curve_preset(self, value: int, pedal: str): diff --git a/boxflat/panels/wheel.py b/boxflat/panels/wheel.py index a80daaa..9049bda 100644 --- a/boxflat/panels/wheel.py +++ b/boxflat/panels/wheel.py @@ -196,7 +196,7 @@ def prepare_ui(self): self._timing_row2.add_labels(f"RPM{i+1}", index=i) self._timing_row2.subscribe_slider(i, self._cm.set_setting, f"wheel-rpm-value{i+1}") self._cm.subscribe(f"wheel-rpm-value{i+1}", self._timing_row2.set_slider_value, i) - # self._cm.subscribe(f"wheel-rpm-value10", self._get_rpm_timings2_preset) + self._cm.subscribe(f"wheel-rpm-value10", self._get_rpm_timings2_preset) self._cm.subscribe("wheel-rpm-timings", self._get_rpm_timings) @@ -273,7 +273,12 @@ def _get_rpm_timings_preset(self, timings: list): def _set_rpm_timings2_preset(self, index): - self._timing_row2.set_sliders_value(self._timings2[index], mute=False) + # self._timing_row2.set_sliders_value(self._timings2[index], mute=False) + for i in range(len(self._timings2[index])): + self._cm.set_setting(self._timings2[index][i], f"wheel-rpm-value{i+1}", exclusive=True) + time.sleep(0.005) + self._cm.set_setting(self._timings2[index][i], f"wheel-rpm-value{i+1}") + time.sleep(0.005) def _get_rpm_timings2_preset(self, *args): @@ -347,10 +352,10 @@ def start_test(self, *args): def _wheel_rpm_test(self, *args): self._cm.set_setting(0, "wheel-send-telemetry") time.sleep(0.2) - initial_mode = self._cm.get_setting("wheel-indicator-mode") - self._cm.set_setting(1, "wheel-indicator-mode") + initial_mode = self._cm.get_setting("wheel-indicator-mode", exclusive=True) + self._cm.set_setting(1, "wheel-indicator-mode", exclusive=True) - t = 0.07 + t = 0.05 for j in range(2): for i in range(10): val = bit(i) @@ -395,4 +400,4 @@ def _wheel_rpm_test(self, *args): self._cm.set_setting(val, "wheel-send-telemetry") time.sleep(0.8) - self._cm.set_setting(initial_mode, "wheel-indicator-mode") + self._cm.set_setting(initial_mode, "wheel-indicator-mode", exclusive=True) diff --git a/boxflat/preset_handler.py b/boxflat/preset_handler.py index fa995f0..877f72d 100644 --- a/boxflat/preset_handler.py +++ b/boxflat/preset_handler.py @@ -4,6 +4,7 @@ import os from threading import Thread from .subscription import SimpleEventDispatcher +from time import sleep MozaDevicePresetSettings = { "base" : [ @@ -158,11 +159,11 @@ def set_name(self, name: str): def append_setting(self, setting_name: str): - command = MozaCommand(setting_name, self._cm.get_command_data()) - if command.device_type not in self._settings: - self._settings[command.device_type] = [] + device, name = setting_name.split("-", maxsplit=1) + if device not in self._settings: + self._settings[device] = [] - self._settings[command.device_type].append(command.name) + self._settings[device].append(name) def add_device_settings(self, device: str): @@ -201,7 +202,7 @@ def _save_preset(self): while tries < 3: tries += 1 replace = setting.replace("set-", "get-") - value = self._cm.get_setting(f"{device}-{replace}") + value = self._cm.get_setting(f"{device}-{replace}", exclusive=True) if value != -1: preset_data[device][setting] = value tries = 3 @@ -231,6 +232,6 @@ def _load_preset(self): for setting, value in settings.items(): setting = setting.replace("get-", "set-").replace("-end", "-max").replace("-start", "-min") # print(f"{key}-{setting}: {value}") - self._cm.set_setting(value, f"{key}-{setting}") + self._cm.set_setting(value, f"{key}-{setting}", exclusive=True) self._dispatch() diff --git a/boxflat/serial_handler.py b/boxflat/serial_handler.py new file mode 100644 index 0000000..669dd73 --- /dev/null +++ b/boxflat/serial_handler.py @@ -0,0 +1,97 @@ +from threading import Thread +from multiprocessing import Process, Queue, Event +from multiprocessing.queues import Empty +from serial import Serial, SerialException +from boxflat.subscription import SimpleEventDispatcher +from time import sleep + +from boxflat.moza_command import MozaCommand + + +class SerialHandler(SimpleEventDispatcher): + def __init__(self, serial_path: str, msg_start: str, device_name: str): + super().__init__() + self._read_queue = Queue() + self._text_read_queue = Queue() + self._write_queue = Queue() + self._running = Event() + + self._serial_path = serial_path + self._message_start = msg_start + self._device_name = device_name + + self._running.set() + Thread(target=self._notification_handler, daemon=True).start() + Process(target=self._thread_spawner, daemon=True).start() + + + def stop(self): + self._running.clear() + + + def write_bytes(self, message: bytes): + if message is None: + return + + self._write_queue.put(message) + + + def _notification_handler(self): + while self._running.is_set(): + try: + response = self._read_queue.get(timeout=0.5) + except: + continue + + self._dispatch(response) + + + def _thread_spawner(self): + serial = Serial(self._serial_path, baudrate=115200, exclusive=False, timeout=0.5) + print(f"\"{self._device_name}\" connected") + serial.reset_output_buffer() + serial.reset_input_buffer() + + p1 = Thread(target=self._serial_read_handler, args=[serial], daemon=True, name="serial-read") + p2 = Thread(target=self._serial_write_handler, args=[serial], daemon=True, name="serial-write") + + p1.start() + p2.start() + + p1.join() + p2.join() + serial.close() + print(f"\"{self._device_name}\" disconnected") + + + def _serial_read_handler(self, serial: Serial): + start = bytes([self._message_start]) + + while self._running.is_set(): + try: + if serial.read(1) != start: + continue + + payload_length = int().from_bytes(serial.read(1)) + if payload_length < 2 or payload_length > 11: + continue + + self._read_queue.put(serial.read(payload_length + 2)) + + except: + self.stop() + + + def _serial_write_handler(self, serial: Serial): + data = None + while self._running.is_set(): + try: + data = self._write_queue.get(timeout=0.5) + except Empty: + continue + + # print(f"Writing: {data.hex(":")}") + try: + serial.write(data) + except SerialException: + self.stop() diff --git a/boxflat/subscription.py b/boxflat/subscription.py index 6dc9955..7a8e843 100644 --- a/boxflat/subscription.py +++ b/boxflat/subscription.py @@ -1,3 +1,7 @@ +from threading import Event +from queue import SimpleQueue + + class Subscription(): def __init__(self, callback: callable, *args): self._callback = callback @@ -28,15 +32,39 @@ def call_with_custom_args(self, *args): class SubscriptionList(): def __init__(self): self._subscriptions = [] + self._single_time_subs = SimpleQueue() def count(self) -> int: - return len(self._subscriptions) + count = len(self._subscriptions) + count += self._single_time_subs.qsize() + return count + + + def get(self, index: int) -> Subscription: + return self._subscriptions[index] + + + def append(self, callback: callable, *args) -> Subscription: + if not callable(callback): + return + + sub = Subscription(callback, *args) + self._subscriptions.append(sub) + return sub + + + def append_single(self, callback: callable, *args): + if not callable(callback): + return + + sub = Subscription(callback, *args) + self._single_time_subs.put(sub) - def append(self, callback: callable, *args): - if callable(callback): - self._subscriptions.append(Subscription(callback, *args)) + def remove(self, sub: Subscription): + if sub in self._subscriptions: + self._subscriptions.remove(sub) def append_subscription(self, subscription: Subscription): @@ -44,35 +72,49 @@ def append_subscription(self, subscription: Subscription): def call(self): - """ - Call all subscribers with default arguments - """ for sub in self._subscriptions: sub.call() + while not self._single_time_subs.empty(): + self._single_time_subs.get().call() + def call_with_value(self, value): for sub in self._subscriptions: sub.call_with_value(value) + while not self._single_time_subs.empty(): + self._single_time_subs.get().call_with_value(value) + def call_with_values(self, *values): for sub in self._subscriptions: sub.call_with_values(*values) + while not self._single_time_subs.empty(): + self._single_time_subs.get().call_with_values(*values) + def call_without_args(self): for sub in self._subscriptions: sub.call_without_args() + while not self._single_time_subs.empty(): + self._single_time_subs.get().call_without_args() + def call_with_custom_args(self, *args): for sub in self._subscriptions: sub.call_with_custom_args(*args) + while not self._single_time_subs.empty(): + self._single_time_subs.get().call_with_custom_args(*args) + def clear(self): self._subscriptions.clear() + while not self._single_time_subs.empty(): + self._single_time_subs.get() @@ -121,7 +163,8 @@ def _deregister_event(self, event_name: str) -> bool: self.__events.pop(event_name) return True - def _deregister_events(self) -> bool: + + def _deregister_all_events(self) -> bool: self.__events = {} @@ -136,12 +179,18 @@ def _dispatch(self, event_name: str, *values) -> bool: return True - def subscribe(self, event_name: str, callback: callable, *args) -> bool: + def subscribe(self, event_name: str, callback: callable, *args) -> Subscription: if not self.__find_event(event_name): return False - self.__events[event_name].append(callback, *args) - return True + return self.__events[event_name].append(callback, *args) + + + def subscribe_once(self, event_name: str, callback: callable, *args): + if not self.__find_event(event_name): + return + + self.__events[event_name].append_single(callback, *args) def _clear_event_subscriptions(self, event_name: str) -> bool: @@ -151,6 +200,13 @@ def _clear_event_subscriptions(self, event_name: str) -> bool: self.__events[event_name].clear() + def _remove_event_subscription(self, event_name: str, sub: Subscription) -> bool: + if not self.__find_event(event_name): + return False + + self.__events[event_name].remove(sub) + + def _clear_subscriptions(self, event_names=None): if not event_names: event_names = self.__events.keys() @@ -176,6 +232,10 @@ def subscribe(self, callback: callable, *args): self.__events.append(callback, *args) + def subscribe_once(self, event_name: str, callback: callable, *args): + self.__events.append_single(callback, *args) + + def _clear_subscriptions(self): self.__events.clear() @@ -203,43 +263,24 @@ def value(self, new_value): self._value = new_value -class Semaphore(EventDispatcher): - def __init__(self, maximum: int): - super().__init__() - self._value = 0 - self._max = maximum - self._register_event("value-changed") - self._register_event("quorum-established") - self._register_event("quorum-dissolved") - - - @property - def value(self): - return self._value +class BlockingValue(): + def __init__(self): + self._value = None + self._event = Event() - @value.setter - def value(self, new_value: int): - if new_value > self._max: - return - if new_value < 0: - return - - old_value = self._value + def set_value(self, new_value): self._value = new_value - - if new_value != old_value: - self._dispatch(new_value) - if new_value == self._max: - self._dispatch("quorum-established") - elif old_value == self._max: - self._dispatch("quorum-dissolved") + self._event.set() - def increment(self): - self.value += 1 + def get_value(self, timeout=0.05): + self._event.wait(timeout) + self._event.clear() + return self._value - def decrement(self): - self.value -= 1 + def get_value_no_clear(self, timeout=0.05): + self._event.wait(timeout) + return self._value diff --git a/boxflat/widgets/calibration_row.py b/boxflat/widgets/calibration_row.py index 2f33952..7f30395 100644 --- a/boxflat/widgets/calibration_row.py +++ b/boxflat/widgets/calibration_row.py @@ -27,9 +27,9 @@ def _calibration(self): GLib.idle_add(self.set_subtitle, "Press all paddles!") sleep(4) - self._dispatch("calibration-start") + self._dispatch("calibration-start", 1) - for i in reversed(range(3 if self._alternative else 8)): + for i in reversed(range(3 if self._alternative else 7)): GLib.idle_add(self.set_subtitle, f"{text} {i+1}s") sleep(1) @@ -37,7 +37,7 @@ def _calibration(self): GLib.idle_add(self.set_subtitle, "Release paddles") sleep(3) - self._dispatch("calibration-stop") + self._dispatch("calibration-stop", 0) print("Calibration stop") GLib.idle_add(self.set_subtitle, tmp) diff --git a/boxflat/widgets/equalizer_row.py b/boxflat/widgets/equalizer_row.py index 564899e..bcff689 100644 --- a/boxflat/widgets/equalizer_row.py +++ b/boxflat/widgets/equalizer_row.py @@ -168,7 +168,7 @@ def _notify_sliders(self, scale): if self._mute.is_set(): return - self._cooldown = 1 + self._cooldown = 2 self.set_button_value(-1) self._sliders_subs.call_with_value(self.get_sliders_value()) diff --git a/data/serial.yml b/data/serial.yml index 74316d2..02087d0 100644 --- a/data/serial.yml +++ b/data/serial.yml @@ -14,6 +14,16 @@ device-ids: dash: -1 estop: -1 + +ids-to-names: + 18: main + 19: base + 23: wheel + 25: pedals + 26: hpattern + 26: sequential + 27: handbrake + # TODO: EEPROM commands # Maybe that's how we update FW? # =========================================== @@ -33,1410 +43,1418 @@ device-ids: # =========================================== commands: # Main commands(?) - main-output: - read: 30 - write: -1 - id: [57] - bytes: 7 - type: int - - main-set-compat-mode: - read: -1 - write: 31 - id: [19] - bytes: 1 - type: int - - main-get-compat-mode: - read: 31 - write: -1 - id: [23] - bytes: 1 - type: int - - # BLE off = 0 - # BLE on = 85 - main-get-ble-mode: - read: 31 - write: -1 - id: [70] - bytes: 1 - type: int - - main-set-ble-mode: - read: -1 - write: 31 - id: [71] - bytes: 1 - type: int - - main-get-led-status: - read: 31 - write: -1 - id: [8] - bytes: 1 - type: int - - main-set-led-status: - read: -1 - write: 31 - id: [9] - bytes: 1 - type: int - - main-set-ffb-status: - read: -1 - write: 31 - id: [51] - bytes: 1 - type: int - - main-get-ffb-status: - read: 31 - write: -1 - id: [52] - bytes: 1 - type: int - - main-set-default-ffb-status: - read: -1 - write: 31 - id: [53] - bytes: 1 - type: int - - main-get-default-ffb-status: - read: 31 - write: -1 - id: [54] - bytes: 1 - type: int + main: + output: + read: 30 + write: -1 + id: [57] + bytes: 7 + type: int + + set-compat-mode: + read: -1 + write: 31 + id: [19] + bytes: 1 + type: int + + get-compat-mode: + read: 31 + write: -1 + id: [23] + bytes: 1 + type: int + + # BLE off = 0 + # BLE on = 85 + get-ble-mode: + read: 31 + write: -1 + id: [70] + bytes: 1 + type: int + + set-ble-mode: + read: -1 + write: 31 + id: [71] + bytes: 1 + type: int + + get-led-status: + read: 31 + write: -1 + id: [8] + bytes: 1 + type: int + + set-led-status: + read: -1 + write: 31 + id: [9] + bytes: 1 + type: int + + set-ffb-status: + read: -1 + write: 31 + id: [51] + bytes: 1 + type: int + + get-ffb-status: + read: 31 + write: -1 + id: [52] + bytes: 1 + type: int + + set-default-ffb-status: + read: -1 + write: 31 + id: [53] + bytes: 1 + type: int + + get-default-ffb-status: + read: 31 + write: -1 + id: [54] + bytes: 1 + type: int # Pedals - pedals-throttle-dir: - read: 35 - write: 36 - id: [1] - bytes: 2 - type: int - - pedals-throttle-min: - read: 35 - write: 36 - id: [2] - bytes: 2 - type: int - - pedals-throttle-max: - read: 35 - write: 36 - id: [3] - bytes: 2 - type: int - - pedals-brake-dir: - read: 35 - write: 36 - id: [4] - bytes: 2 - type: int - - pedals-brake-min: - read: 35 - write: 36 - id: [5] - bytes: 2 - type: int - - pedals-brake-max: - read: 35 - write: 36 - id: [6] - bytes: 2 - type: int - - pedals-clutch-dir: - read: 35 - write: 36 - id: [7] - bytes: 2 - type: int - - pedals-clutch-min: - read: 35 - write: 36 - id: [8] - bytes: 2 - type: int - - pedals-clutch-max: - read: 35 - write: 36 - id: [9] - bytes: 2 - type: int - - pedals-compat-mode: - read: 35 - write: 36 - id: [13] - bytes: 2 - type: int - - pedals-throttle-y1: - read: 35 - write: 36 - id: [14] - bytes: 4 - type: float - - pedals-throttle-y2: - read: 35 - write: 36 - id: [15] - bytes: 4 - type: float - - pedals-throttle-y3: - read: 35 - write: 36 - id: [16] - bytes: 4 - type: float - - pedals-throttle-y4: - read: 35 - write: 36 - id: [17] - bytes: 4 - type: float - - pedals-brake-y1: - read: 35 - write: 36 - id: [18] - bytes: 4 - type: float - - pedals-brake-y2: - read: 35 - write: 36 - id: [19] - bytes: 4 - type: float - - pedals-brake-y3: - read: 35 - write: 36 - id: [20] - bytes: 4 - type: float - - pedals-brake-y4: - read: 35 - write: 36 - id: [21] - bytes: 4 - type: float - - pedals-clutch-y1: - read: 35 - write: 36 - id: [22] - bytes: 4 - type: float - - pedals-clutch-y2: - read: 35 - write: 36 - id: [23] - bytes: 4 - type: float - - pedals-clutch-y3: - read: 35 - write: 36 - id: [24] - bytes: 4 - type: float - - pedals-clutch-y4: - read: 35 - write: 36 - id: [25] - bytes: 4 - type: float - - pedals-brake-angle-ratio: - read: 35 - write: 36 - id: [26] - bytes: 4 - type: float - - pedals-throttle-y5: - read: 35 - write: 36 - id: [27] - bytes: 4 - type: float - - pedals-brake-y5: - read: 35 - write: 36 - id: [28] - bytes: 4 - type: float - - pedals-clutch-y5: - read: 35 - write: 36 - id: [29] - bytes: 4 - type: float - - pedals-throttle-hid-source: - read: 35 - write: 36 - id: [30] - bytes: 2 - type: int - - pedals-throttle-hid-cmd: - read: 35 - write: 36 - id: [31] - bytes: 2 - type: int - - pedals-throttle-output: - read: 37 - write: -1 - id: [1] - bytes: 2 - type: int - - pedals-brake-output: - read: 37 - write: -1 - id: [2] - bytes: 2 - type: int - - pedals-clutch-output: - read: 37 - write: -1 - id: [3] - bytes: 2 - type: int + pedals: + throttle-dir: + read: 35 + write: 36 + id: [1] + bytes: 2 + type: int + + throttle-min: + read: 35 + write: 36 + id: [2] + bytes: 2 + type: int + + throttle-max: + read: 35 + write: 36 + id: [3] + bytes: 2 + type: int + + brake-dir: + read: 35 + write: 36 + id: [4] + bytes: 2 + type: int + + brake-min: + read: 35 + write: 36 + id: [5] + bytes: 2 + type: int + + brake-max: + read: 35 + write: 36 + id: [6] + bytes: 2 + type: int + + clutch-dir: + read: 35 + write: 36 + id: [7] + bytes: 2 + type: int + + clutch-min: + read: 35 + write: 36 + id: [8] + bytes: 2 + type: int + + clutch-max: + read: 35 + write: 36 + id: [9] + bytes: 2 + type: int + + compat-mode: + read: 35 + write: 36 + id: [13] + bytes: 2 + type: int + + throttle-y1: + read: 35 + write: 36 + id: [14] + bytes: 4 + type: float + + throttle-y2: + read: 35 + write: 36 + id: [15] + bytes: 4 + type: float + + throttle-y3: + read: 35 + write: 36 + id: [16] + bytes: 4 + type: float + + throttle-y4: + read: 35 + write: 36 + id: [17] + bytes: 4 + type: float + + brake-y1: + read: 35 + write: 36 + id: [18] + bytes: 4 + type: float + + brake-y2: + read: 35 + write: 36 + id: [19] + bytes: 4 + type: float + + brake-y3: + read: 35 + write: 36 + id: [20] + bytes: 4 + type: float + + brake-y4: + read: 35 + write: 36 + id: [21] + bytes: 4 + type: float + + clutch-y1: + read: 35 + write: 36 + id: [22] + bytes: 4 + type: float + + clutch-y2: + read: 35 + write: 36 + id: [23] + bytes: 4 + type: float + + clutch-y3: + read: 35 + write: 36 + id: [24] + bytes: 4 + type: float + + clutch-y4: + read: 35 + write: 36 + id: [25] + bytes: 4 + type: float + + brake-angle-ratio: + read: 35 + write: 36 + id: [26] + bytes: 4 + type: float + + throttle-y5: + read: 35 + write: 36 + id: [27] + bytes: 4 + type: float + + brake-y5: + read: 35 + write: 36 + id: [28] + bytes: 4 + type: float + + clutch-y5: + read: 35 + write: 36 + id: [29] + bytes: 4 + type: float + + throttle-hid-source: + read: 35 + write: 36 + id: [30] + bytes: 2 + type: int + + throttle-hid-cmd: + read: 35 + write: 36 + id: [31] + bytes: 2 + type: int + + throttle-output: + read: 37 + write: -1 + id: [1] + bytes: 2 + type: int + + brake-output: + read: 37 + write: -1 + id: [2] + bytes: 2 + type: int + + clutch-output: + read: 37 + write: -1 + id: [3] + bytes: 2 + type: int + + throttle-calibration-start: + read: -1 + write: 38 + id: [12] + bytes: 2 + type: int + + brake-calibration-start: + read: -1 + write: 38 + id: [13] + bytes: 2 + type: int + + clutch-calibration-start: + read: -1 + write: 38 + id: [14] + bytes: 2 + type: int + + throttle-calibration-stop: + read: -1 + write: 38 + id: [16] + bytes: 2 + type: int + + brake-calibration-stop: + read: -1 + write: 38 + id: [17] + bytes: 2 + type: int + + clutch-calibration-stop: + read: -1 + write: 38 + id: [18] + bytes: 2 + type: int # Wheelbase - base-limit: - read: 40 - write: 41 - id: [1] - bytes: 2 - type: int - - base-ffb-strength: - read: 40 - write: 41 - id: [2] - bytes: 2 - type: int - - base-inertia: - read: 40 - write: 41 - id: [4] - bytes: 2 - type: int - - base-damper: - read: 40 - write: 41 - id: [7] - bytes: 2 - type: int - - base-friction: - read: 40 - write: 41 - id: [8] - bytes: 2 - type: int - - base-spring: - read: 40 - write: 41 - id: [9] - bytes: 2 - type: int - - base-speed: - read: 40 - write: 41 - id: [10] - bytes: 2 - type: int - - base-road-sensitivity: - read: 40 - write: 41 - id: [12] - bytes: 2 - type: int - - # hands-off protection - base-protection: - read: 40 - write: 41 - id: [13] - bytes: 2 - type: int - - base-protection-mode: - read: 40 - write: 41 - id: [45] - bytes: 2 - type: int - - base-equalizer1: - read: 40 - write: 41 - id: [14] - bytes: 2 - type: int - - base-equalizer2: - read: 40 - write: 41 - id: [15] - bytes: 2 - type: int - - base-equalizer3: - read: 40 - write: 41 - id: [16] - bytes: 2 - type: int - - base-equalizer4: - read: 40 - write: 41 - id: [17] - bytes: 2 - type: int - - base-equalizer5: - read: 40 - write: 41 - id: [20] - bytes: 2 - type: int - - base-equalizer6: - read: 40 - write: 41 - id: [44] - bytes: 2 - type: int - - base-torque: - read: 40 - write: 41 - id: [18] - bytes: 2 - type: int - - # hands-off protection - base-natural-inertia: - read: 40 - write: 41 - id: [19] - bytes: 2 - type: int - - # hands-off protection - base-natural-inertia-enable: - read: 40 - write: 41 - id: [22] - bytes: 2 - type: int - - base-max-angle: - read: 40 - write: 41 - id: [23] - bytes: 2 - type: int - - base-ffb-reverse: - read: 40 - write: 41 - id: [24] - bytes: 2 - type: int - - base-speed-damping: - read: 40 - write: 41 - id: [25] - bytes: 2 - type: int - - base-speed-damping-point: - read: 40 - write: 41 - id: [26] - bytes: 2 - type: int - - base-soft-limit-strength: - read: 40 - write: 41 - id: [27] - bytes: 2 - type: int - - base-soft-limit-retain: - read: 40 - write: 41 - id: [28] - bytes: 2 - type: int - - base-temp-strategy: - read: 40 - write: 41 - id: [30] - bytes: 2 - type: int - - base-soft-limit-stiffness: - read: 40 - write: 41 - id: [31] - bytes: 2 - type: int - - base-ffb-curve-x1: - read: 40 - write: 41 - id: [34, 1] - bytes: 1 - type: int - - base-ffb-curve-x2: - read: 40 - write: 41 - id: [34, 2] - bytes: 1 - type: int - - base-ffb-curve-x3: - read: 40 - write: 41 - id: [34, 3] - bytes: 1 - type: int - - base-ffb-curve-x4: - read: 40 - write: 41 - id: [34, 4] - bytes: 1 - type: int - - base-ffb-curve-y1: - read: 40 - write: 41 - id: [34, 5] - bytes: 1 - type: int - - base-ffb-curve-y2: - read: 40 - write: 41 - id: [34, 6] - bytes: 1 - type: int - - base-ffb-curve-y3: - read: 40 - write: 41 - id: [34, 7] - bytes: 1 - type: int - - base-ffb-curve-y4: - read: 40 - write: 41 - id: [34, 8] - bytes: 1 - type: int - - base-ffb-curve-y5: - read: 40 - write: 41 - id: [34, 9] - bytes: 1 - type: int - - base-ffb-curve-y0: - read: -1 - write: -1 - id: [34, 10] - bytes: 1 - type: int - - base-state: - read: 43 - write: -1 - id: [1] - bytes: 2 - type: int - - base-state-err: - read: 43 - write: -1 - id: [2] - bytes: 2 - type: int - - base-mcu-temp: - read: 43 - write: -1 - id: [4] - bytes: 2 - type: int - - base-mosfet-temp: - read: 43 - write: -1 - id: [5] - bytes: 2 - type: int - - base-motor-temp: - read: 43 - write: -1 - id: [6] - bytes: 2 - type: int + base: + limit: + read: 40 + write: 41 + id: [1] + bytes: 2 + type: int + + ffb-strength: + read: 40 + write: 41 + id: [2] + bytes: 2 + type: int + + inertia: + read: 40 + write: 41 + id: [4] + bytes: 2 + type: int + + damper: + read: 40 + write: 41 + id: [7] + bytes: 2 + type: int + + friction: + read: 40 + write: 41 + id: [8] + bytes: 2 + type: int + + spring: + read: 40 + write: 41 + id: [9] + bytes: 2 + type: int + + speed: + read: 40 + write: 41 + id: [10] + bytes: 2 + type: int + + road-sensitivity: + read: 40 + write: 41 + id: [12] + bytes: 2 + type: int + + # hands-off protection + protection: + read: 40 + write: 41 + id: [13] + bytes: 2 + type: int + + protection-mode: + read: 40 + write: 41 + id: [45] + bytes: 2 + type: int + + equalizer1: + read: 40 + write: 41 + id: [14] + bytes: 2 + type: int + + equalizer2: + read: 40 + write: 41 + id: [15] + bytes: 2 + type: int + + equalizer3: + read: 40 + write: 41 + id: [16] + bytes: 2 + type: int + + equalizer4: + read: 40 + write: 41 + id: [17] + bytes: 2 + type: int + + equalizer5: + read: 40 + write: 41 + id: [20] + bytes: 2 + type: int + + equalizer6: + read: 40 + write: 41 + id: [44] + bytes: 2 + type: int + + torque: + read: 40 + write: 41 + id: [18] + bytes: 2 + type: int + + # hands-off protection + natural-inertia: + read: 40 + write: 41 + id: [19] + bytes: 2 + type: int + + # hands-off protection + natural-inertia-enable: + read: 40 + write: 41 + id: [22] + bytes: 2 + type: int + + max-angle: + read: 40 + write: 41 + id: [23] + bytes: 2 + type: int + + ffb-reverse: + read: 40 + write: 41 + id: [24] + bytes: 2 + type: int + + speed-damping: + read: 40 + write: 41 + id: [25] + bytes: 2 + type: int + + speed-damping-point: + read: 40 + write: 41 + id: [26] + bytes: 2 + type: int + + soft-limit-strength: + read: 40 + write: 41 + id: [27] + bytes: 2 + type: int + + soft-limit-retain: + read: 40 + write: 41 + id: [28] + bytes: 2 + type: int + + temp-strategy: + read: 40 + write: 41 + id: [30] + bytes: 2 + type: int + + soft-limit-stiffness: + read: 40 + write: 41 + id: [31] + bytes: 2 + type: int + + ffb-curve-x1: + read: 40 + write: 41 + id: [34, 1] + bytes: 1 + type: int + + ffb-curve-x2: + read: 40 + write: 41 + id: [34, 2] + bytes: 1 + type: int + + ffb-curve-x3: + read: 40 + write: 41 + id: [34, 3] + bytes: 1 + type: int + + ffb-curve-x4: + read: 40 + write: 41 + id: [34, 4] + bytes: 1 + type: int + + ffb-curve-y1: + read: 40 + write: 41 + id: [34, 5] + bytes: 1 + type: int + + ffb-curve-y2: + read: 40 + write: 41 + id: [34, 6] + bytes: 1 + type: int + + ffb-curve-y3: + read: 40 + write: 41 + id: [34, 7] + bytes: 1 + type: int + + ffb-curve-y4: + read: 40 + write: 41 + id: [34, 8] + bytes: 1 + type: int + + ffb-curve-y5: + read: 40 + write: 41 + id: [34, 9] + bytes: 1 + type: int + + ffb-curve-y0: + read: -1 + write: -1 + id: [34, 10] + bytes: 1 + type: int + + state: + read: 43 + write: -1 + id: [1] + bytes: 2 + type: int + + state-err: + read: 43 + write: -1 + id: [2] + bytes: 2 + type: int + + mcu-temp: + read: 43 + write: -1 + id: [4] + bytes: 2 + type: int + + mosfet-temp: + read: 43 + write: -1 + id: [5] + bytes: 2 + type: int + + motor-temp: + read: 43 + write: -1 + id: [6] + bytes: 2 + type: int + + calibration: + read: -1 + write: 42 + id: [1] + bytes: 2 + type: int # Dashboard - dash-brightness: - read: 51 - write: 50 - id: [0] - bytes: 1 - type: int - - dash-ui-index: - read: 51 - write: 50 - id: [1] - bytes: 1 - type: int - - dash-speed-unit: - read: 51 - write: 50 - id: [2] - bytes: 1 - type: int - - dash-temp-unit: - read: 51 - write: 50 - id: [3] - bytes: 1 - type: int + dash: + dash-brightness: + read: 51 + write: 50 + id: [0] + bytes: 1 + type: int + + dash-ui-index: + read: 51 + write: 50 + id: [1] + bytes: 1 + type: int + + dash-speed-unit: + read: 51 + write: 50 + id: [2] + bytes: 1 + type: int + + dash-temp-unit: + read: 51 + write: 50 + id: [3] + bytes: 1 + type: int # Steering wheel - wheel-colors: - read: -1 # 64 - write: 63 - id: [0] - bytes: 15 - type: hex - - wheel-brightness: - read: 64 - write: 63 - id: [1] - bytes: 1 - type: int - - wheel-rpm-timings: - read: 64 - write: 63 - id: [2] - bytes: 10 - type: array - - wheel-paddles-mode: - read: 64 - write: 63 - id: [3] - bytes: 1 - type: int - - wheel-indicator-mode: - read: 64 - write: 63 - id: [4] - bytes: 1 - type: int - - wheel-stick-mode: - read: 64 - write: 63 - id: [5] - bytes: 2 - type: int - - wheel-set-display-mode: - read: -1 - write: 63 - id: [7] - bytes: 1 - type: int - - wheel-get-display-mode: - read: 64 - write: -1 - id: [8] - bytes: 1 - type: int - - wheel-clutch-point: - read: 64 - write: 63 - id: [9] - bytes: 1 - type: int - - wheel-knob-mode: - read: 64 - write: 63 - id: [10] - bytes: 1 - type: int - - wheel-paddle-adaptive-mode: - read: 64 - write: 63 - id: [11] - bytes: 1 - type: int - - wheel-paddle-button-mode: - read: 64 - write: 63 - id: [13] - bytes: 1 - type: int - - wheel-rpm-blink-color1: - read: -1 - write: 63 - id: [15, 0] - bytes: 3 - type: array - - wheel-rpm-blink-color2: - read: -1 - write: 63 - id: [15, 1] - bytes: 3 - type: array - - wheel-rpm-blink-color3: - read: -1 - write: 63 - id: [15, 2] - bytes: 3 - type: array - - wheel-rpm-blink-color4: - read: -1 - write: 63 - id: [15, 3] - bytes: 3 - type: array - - wheel-rpm-blink-color5: - read: -1 - write: 63 - id: [15, 4] - bytes: 3 - type: array - - wheel-rpm-blink-color6: - read: -1 - write: 63 - id: [15, 5] - bytes: 3 - type: array - - wheel-rpm-blink-color7: - read: -1 - write: 63 - id: [15, 6] - bytes: 3 - type: array - - wheel-rpm-blink-color8: - read: -1 - write: 63 - id: [15, 7] - bytes: 3 - type: array - - wheel-rpm-blink-color9: - read: -1 - write: 63 - id: [15, 8] - bytes: 3 - type: array - - wheel-rpm-blink-color10: - read: -1 - write: 63 - id: [15, 9] - bytes: 3 - type: array - - wheel-key-combination: - read: 64 - write: 63 - id: [19] - bytes: 4 - type: array - - wheel-rpm-color1: - read: 64 - write: 63 - id: [21, 0, 0] - bytes: 3 - type: array - - wheel-rpm-color2: - read: 64 - write: 63 - id: [21, 0, 1] - bytes: 3 - type: array - - wheel-rpm-color3: - read: 64 - write: 63 - id: [21, 0, 2] - bytes: 3 - type: array - - wheel-rpm-color4: - read: 64 - write: 63 - id: [21, 0, 3] - bytes: 3 - type: array - - wheel-rpm-color5: - read: 64 - write: 63 - id: [21, 0, 4] - bytes: 3 - type: array - - wheel-rpm-color6: - read: 64 - write: 63 - id: [21, 0, 5] - bytes: 3 - type: array - - wheel-rpm-color7: - read: 64 - write: 63 - id: [21, 0, 6] - bytes: 3 - type: array - - wheel-rpm-color8: - read: 64 - write: 63 - id: [21, 0, 7] - bytes: 3 - type: array - - wheel-rpm-color9: - read: 64 - write: 63 - id: [21, 0, 8] - bytes: 3 - type: array - - wheel-rpm-color10: - read: 64 - write: 63 - id: [21, 0, 9] - bytes: 3 - type: array - - wheel-button-color1: - read: 64 - write: 63 - id: [21, 1, 0] - bytes: 3 - type: array - - wheel-button-color2: - read: 64 - write: 63 - id: [21, 1, 1] - bytes: 3 - type: array - - wheel-button-color3: - read: 64 - write: 63 - id: [21, 1, 2] - bytes: 3 - type: array - - wheel-button-color4: - read: 64 - write: 63 - id: [21, 1, 3] - bytes: 3 - type: array - - wheel-button-color5: - read: 64 - write: 63 - id: [21, 1, 4] - bytes: 3 - type: array - - wheel-button-color6: - read: 64 - write: 63 - id: [21, 1, 5] - bytes: 3 - type: array - - wheel-button-color7: - read: 64 - write: 63 - id: [21, 1, 6] - bytes: 3 - type: array - - wheel-button-color8: - read: 64 - write: 63 - id: [21, 1, 7] - bytes: 3 - type: array - - wheel-button-color9: - read: 64 - write: 63 - id: [21, 1, 8] - bytes: 3 - type: array - - wheel-button-color10: - read: 64 - write: 63 - id: [21, 1, 9] - bytes: 3 - type: array - - wheel-flag-color1: - read: 64 - write: 63 - id: [21, 2, 0] - bytes: 3 - type: array - - wheel-flag-color2: - read: 64 - write: 63 - id: [21, 2, 1] - bytes: 3 - type: array - - wheel-flag-color3: - read: 64 - write: 63 - id: [21, 2, 2] - bytes: 3 - type: array - - wheel-flag-color4: - read: 64 - write: 63 - id: [21, 2, 3] - bytes: 3 - type: array - - wheel-flag-color5: - read: 64 - write: 63 - id: [21, 2, 4] - bytes: 3 - type: array - - wheel-flag-color6: - read: 64 - write: 63 - id: [21, 2, 5] - bytes: 3 - type: array - - wheel-flag-color7: - read: 64 - write: 63 - id: [21, 2, 6] - bytes: 3 - type: array - - wheel-flag-color8: - read: 64 - write: 63 - id: [21, 2, 7] - bytes: 3 - type: array - - wheel-flag-color9: - read: 64 - write: 63 - id: [21, 2, 8] - bytes: 3 - type: array - - wheel-flag-color10: - read: 64 - write: 63 - id: [21, 2, 9] - bytes: 3 - type: array - - wheel-rpm-brightness: - read: 64 - write: 63 - id: [20, 0] - bytes: 1 - type: int - - wheel-buttons-brightness: - read: 64 - write: 63 - id: [20, 1] - bytes: 1 - type: int - - wheel-flags-brightness: - read: 64 - write: 63 - id: [20, 2] - bytes: 1 - type: int - - wheel-rpm-interval: - read: 64 - write: 63 - id: [22] - bytes: 4 - type: int - - wheel-rpm-mode: - read: 64 - write: 63 - id: [23] - bytes: 1 - type: int - - wheel-rpm-value1: - read: 64 - write: 63 - id: [24, 0] - bytes: 2 - type: int - - wheel-rpm-value2: - read: 64 - write: 63 - id: [24, 1] - bytes: 2 - type: int - - wheel-rpm-value3: - read: 64 - write: 63 - id: [24, 2] - bytes: 2 - type: int - - wheel-rpm-value4: - read: 64 - write: 63 - id: [24, 3] - bytes: 2 - type: int - - wheel-rpm-value5: - read: 64 - write: 63 - id: [24, 4] - bytes: 2 - type: int - - wheel-rpm-value6: - read: 64 - write: 63 - id: [24, 5] - bytes: 2 - type: int - - wheel-rpm-value7: - read: 64 - write: 63 - id: [24, 6] - bytes: 2 - type: int - - wheel-rpm-value8: - read: 64 - write: 63 - id: [24, 7] - bytes: 2 - type: int - - wheel-rpm-value9: - read: 64 - write: 63 - id: [24, 8] - bytes: 2 - type: int - - wheel-rpm-value10: - read: 64 - write: 63 - id: [24, 9] - bytes: 2 - type: int + wheel: + colors: + read: -1 # 64 + write: 63 + id: [0] + bytes: 15 + type: hex + + brightness: + read: 64 + write: 63 + id: [1] + bytes: 1 + type: int + + rpm-timings: + read: 64 + write: 63 + id: [2] + bytes: 10 + type: array + + paddles-mode: + read: 64 + write: 63 + id: [3] + bytes: 1 + type: int + + indicator-mode: + read: 64 + write: 63 + id: [4] + bytes: 1 + type: int + + stick-mode: + read: 64 + write: 63 + id: [5] + bytes: 2 + type: int + + set-display-mode: + read: -1 + write: 63 + id: [7] + bytes: 1 + type: int + + get-display-mode: + read: 64 + write: -1 + id: [8] + bytes: 1 + type: int + + clutch-point: + read: 64 + write: 63 + id: [9] + bytes: 1 + type: int + + knob-mode: + read: 64 + write: 63 + id: [10] + bytes: 1 + type: int + + paddle-adaptive-mode: + read: 64 + write: 63 + id: [11] + bytes: 1 + type: int + + paddle-button-mode: + read: 64 + write: 63 + id: [13] + bytes: 1 + type: int + + rpm-blink-color1: + read: -1 + write: 63 + id: [15, 0] + bytes: 3 + type: array + + rpm-blink-color2: + read: -1 + write: 63 + id: [15, 1] + bytes: 3 + type: array + + rpm-blink-color3: + read: -1 + write: 63 + id: [15, 2] + bytes: 3 + type: array + + rpm-blink-color4: + read: -1 + write: 63 + id: [15, 3] + bytes: 3 + type: array + + rpm-blink-color5: + read: -1 + write: 63 + id: [15, 4] + bytes: 3 + type: array + + rpm-blink-color6: + read: -1 + write: 63 + id: [15, 5] + bytes: 3 + type: array + + rpm-blink-color7: + read: -1 + write: 63 + id: [15, 6] + bytes: 3 + type: array + + rpm-blink-color8: + read: -1 + write: 63 + id: [15, 7] + bytes: 3 + type: array + + rpm-blink-color9: + read: -1 + write: 63 + id: [15, 8] + bytes: 3 + type: array + + rpm-blink-color10: + read: -1 + write: 63 + id: [15, 9] + bytes: 3 + type: array + + key-combination: + read: 64 + write: 63 + id: [19] + bytes: 4 + type: array + + rpm-color1: + read: 64 + write: 63 + id: [21, 0, 0] + bytes: 3 + type: array + + rpm-color2: + read: 64 + write: 63 + id: [21, 0, 1] + bytes: 3 + type: array + + rpm-color3: + read: 64 + write: 63 + id: [21, 0, 2] + bytes: 3 + type: array + + rpm-color4: + read: 64 + write: 63 + id: [21, 0, 3] + bytes: 3 + type: array + + rpm-color5: + read: 64 + write: 63 + id: [21, 0, 4] + bytes: 3 + type: array + + rpm-color6: + read: 64 + write: 63 + id: [21, 0, 5] + bytes: 3 + type: array + + rpm-color7: + read: 64 + write: 63 + id: [21, 0, 6] + bytes: 3 + type: array + + rpm-color8: + read: 64 + write: 63 + id: [21, 0, 7] + bytes: 3 + type: array + + rpm-color9: + read: 64 + write: 63 + id: [21, 0, 8] + bytes: 3 + type: array + + rpm-color10: + read: 64 + write: 63 + id: [21, 0, 9] + bytes: 3 + type: array + + button-color1: + read: 64 + write: 63 + id: [21, 1, 0] + bytes: 3 + type: array + + button-color2: + read: 64 + write: 63 + id: [21, 1, 1] + bytes: 3 + type: array + + button-color3: + read: 64 + write: 63 + id: [21, 1, 2] + bytes: 3 + type: array + + button-color4: + read: 64 + write: 63 + id: [21, 1, 3] + bytes: 3 + type: array + + button-color5: + read: 64 + write: 63 + id: [21, 1, 4] + bytes: 3 + type: array + + button-color6: + read: 64 + write: 63 + id: [21, 1, 5] + bytes: 3 + type: array + + button-color7: + read: 64 + write: 63 + id: [21, 1, 6] + bytes: 3 + type: array + + button-color8: + read: 64 + write: 63 + id: [21, 1, 7] + bytes: 3 + type: array + + button-color9: + read: 64 + write: 63 + id: [21, 1, 8] + bytes: 3 + type: array + + button-color10: + read: 64 + write: 63 + id: [21, 1, 9] + bytes: 3 + type: array + + flag-color1: + read: 64 + write: 63 + id: [21, 2, 0] + bytes: 3 + type: array + + flag-color2: + read: 64 + write: 63 + id: [21, 2, 1] + bytes: 3 + type: array + + flag-color3: + read: 64 + write: 63 + id: [21, 2, 2] + bytes: 3 + type: array + + flag-color4: + read: 64 + write: 63 + id: [21, 2, 3] + bytes: 3 + type: array + + flag-color5: + read: 64 + write: 63 + id: [21, 2, 4] + bytes: 3 + type: array + + flag-color6: + read: 64 + write: 63 + id: [21, 2, 5] + bytes: 3 + type: array + + flag-color7: + read: 64 + write: 63 + id: [21, 2, 6] + bytes: 3 + type: array + + flag-color8: + read: 64 + write: 63 + id: [21, 2, 7] + bytes: 3 + type: array + + flag-color9: + read: 64 + write: 63 + id: [21, 2, 8] + bytes: 3 + type: array + + flag-color10: + read: 64 + write: 63 + id: [21, 2, 9] + bytes: 3 + type: array + + rpm-brightness: + read: 64 + write: 63 + id: [20, 0] + bytes: 1 + type: int + + buttons-brightness: + read: 64 + write: 63 + id: [20, 1] + bytes: 1 + type: int + + flags-brightness: + read: 64 + write: 63 + id: [20, 2] + bytes: 1 + type: int + + rpm-interval: + read: 64 + write: 63 + id: [22] + bytes: 4 + type: int + + rpm-mode: + read: 64 + write: 63 + id: [23] + bytes: 1 + type: int + + rpm-value1: + read: 64 + write: 63 + id: [24, 0] + bytes: 2 + type: int + + rpm-value2: + read: 64 + write: 63 + id: [24, 1] + bytes: 2 + type: int + + rpm-value3: + read: 64 + write: 63 + id: [24, 2] + bytes: 2 + type: int + + rpm-value4: + read: 64 + write: 63 + id: [24, 3] + bytes: 2 + type: int + + rpm-value5: + read: 64 + write: 63 + id: [24, 4] + bytes: 2 + type: int + + rpm-value6: + read: 64 + write: 63 + id: [24, 5] + bytes: 2 + type: int + + rpm-value7: + read: 64 + write: 63 + id: [24, 6] + bytes: 2 + type: int + + rpm-value8: + read: 64 + write: 63 + id: [24, 7] + bytes: 2 + type: int + + rpm-value9: + read: 64 + write: 63 + id: [24, 8] + bytes: 2 + type: int + + rpm-value10: + read: 64 + write: 63 + id: [24, 9] + bytes: 2 + type: int + + paddles-calibration: + read: -1 + write: 63 + id: [8] + bytes: 1 + type: int + + paddles-calibration2: + read: -1 + write: 63 + id: [8] + bytes: 1 + type: int + + # telemetry stuff + send-telemetry: + read: -1 + write: 65 + id: [253, 222] + bytes: 4 + type: int # hpattern shifter - hpattern-hid-mode: - read: 81 - write: 82 - id: [1] - bytes: 2 - type: int - - # not a real command - # we're checking this so we can discern between - # sequential shifter and h-pattern shifter - # command has a range of {1,2} so if we get 0 - # in response, hpattern is connected - hpattern-paddle-sync: - read: 81 - write: 82 - id: [6] - bytes: 2 - type: int - - hpattern-output: - read: 83 - write: -1 - id: [1] - bytes: 2 - type: int + hpattern: + hid-mode: + read: 81 + write: 82 + id: [1] + bytes: 2 + type: int + + # not a real command + # we're checking this so we can discern between + # sequential shifter and h-pattern shifter + # command has a range of {1,2} so if we get 0 + # in response, hpattern is connected + paddle-sync: + read: 81 + write: 82 + id: [6] + bytes: 2 + type: int + + output: + read: 83 + write: -1 + id: [1] + bytes: 2 + type: int + + calibration-start: + read: -1 + write: 84 + id: [3] + bytes: 2 + type: int + + calibration-stop: + read: -1 + write: 84 + id: [4] + bytes: 2 + type: int # estop - estop-set-status: - read: -1 - write: 70 - id: [0] - bytes: 1 - type: int - - estop-get-status: - read: 70 - write: -1 - id: [1] - bytes: 1 - type: int + estop: + set-status: + read: -1 + write: 70 + id: [0] + bytes: 1 + type: int + + get-status: + read: 70 + write: -1 + id: [1] + bytes: 1 + type: int # Sequential shifter - sequential-hid-mode: - read: 81 - write: 82 - id: [1] - bytes: 2 - type: int - - sequential-apply-mode: - read: 81 - write: 82 - id: [2] - bytes: 2 - type: int - - sequential-brightness: - read: 81 - write: 82 - id: [3] - bytes: 2 - type: int - - sequential-colors: - read: 81 - write: 82 - id: [4] - bytes: 2 - type: array - - sequential-direction: - read: 81 - write: 82 - id: [5] - bytes: 2 - type: int - - sequential-paddle-sync: - read: 81 - write: 82 - id: [6] - bytes: 2 - type: int + sequential: + hid-mode: + read: 81 + write: 82 + id: [1] + bytes: 2 + type: int + + apply-mode: + read: 81 + write: 82 + id: [2] + bytes: 2 + type: int + + brightness: + read: 81 + write: 82 + id: [3] + bytes: 2 + type: int + + colors: + read: 81 + write: 82 + id: [4] + bytes: 2 + type: array + + direction: + read: 81 + write: 82 + id: [5] + bytes: 2 + type: int + + paddle-sync: + read: 81 + write: 82 + id: [6] + bytes: 2 + type: int # Handbrake - handbrake-direction: - read: 91 - write: 92 - id: [1] - bytes: 2 - type: int - - handbrake-min: - read: 91 - write: 92 - id: [2] - bytes: 2 - type: int - - handbrake-max: - read: 91 - write: 92 - id: [3] - bytes: 2 - type: int - - handbrake-hid-mode: - read: 91 - write: 92 - id: [4] - bytes: 2 - type: int - - handbrake-y1: - read: 91 - write: 92 - id: [5] - bytes: 4 - type: float - - handbrake-y2: - read: 91 - write: 92 - id: [6] - bytes: 4 - type: float - - handbrake-y3: - read: 91 - write: 92 - id: [7] - bytes: 4 - type: float - - handbrake-y4: - read: 91 - write: 92 - id: [8] - bytes: 4 - type: float - - handbrake-y5: - read: 91 - write: 92 - id: [9] - bytes: 4 - type: float - - handbrake-button-threshold: - read: 91 - write: 92 - id: [10] - bytes: 2 - type: int - - handbrake-mode: - read: 91 - write: 92 - id: [11] - bytes: 2 - type: int - - handbrake-output: - read: 93 - write: -1 - id: [1] - bytes: 2 - type: int + handbrake: + direction: + read: 91 + write: 92 + id: [1] + bytes: 2 + type: int + + min: + read: 91 + write: 92 + id: [2] + bytes: 2 + type: int + + max: + read: 91 + write: 92 + id: [3] + bytes: 2 + type: int + + hid-mode: + read: 91 + write: 92 + id: [4] + bytes: 2 + type: int + + y1: + read: 91 + write: 92 + id: [5] + bytes: 4 + type: float + + y2: + read: 91 + write: 92 + id: [6] + bytes: 4 + type: float + + y3: + read: 91 + write: 92 + id: [7] + bytes: 4 + type: float + + y4: + read: 91 + write: 92 + id: [8] + bytes: 4 + type: float + + y5: + read: 91 + write: 92 + id: [9] + bytes: 4 + type: float + + button-threshold: + read: 91 + write: 92 + id: [10] + bytes: 2 + type: int + + mode: + read: 91 + write: 92 + id: [11] + bytes: 2 + type: int + + output: + read: 93 + write: -1 + id: [1] + bytes: 2 + type: int + + calibration-start: + read: -1 + write: 94 + id: [3] + bytes: 2 + type: int + + calibration-stop: + read: -1 + write: 94 + id: [4] + bytes: 2 + type: int # Hub - hub-port1: - read: 100 - write: -1 - id: [1] - bytes: 1 - type: int - - hub-port2: - read: 100 - write: -1 - id: [2] - bytes: 1 - type: int - - hub-port3: - read: 100 - write: -1 - id: [3] - bytes: 1 - type: int - - hub-port4: - read: 100 - write: -1 - id: [4] - bytes: 1 - type: int - - hub-pedals: - read: 100 - write: -1 - id: [5] - bytes: 1 - type: int - -# calibration commands - # calibration expects 1 as value - base-calibration: - read: -1 - write: 42 - id: [1] - bytes: 2 - type: int - - wheel-paddles-calibration: - read: -1 - write: 63 - id: [8] - bytes: 1 - type: int - - wheel-paddles-calibration2: - read: -1 - write: 63 - id: [8] - bytes: 1 - type: int - - pedals-throttle-start-calibration: - read: -1 - write: 38 - id: [12] - bytes: 2 - type: int - - pedals-brake-start-calibration: - read: -1 - write: 38 - id: [13] - bytes: 2 - type: int - - pedals-clutch-start-calibration: - read: -1 - write: 38 - id: [14] - bytes: 2 - type: int - - pedals-throttle-stop-calibration: - read: -1 - write: 38 - id: [15] - bytes: 2 - type: int - - pedals-brake-stop-calibration: - read: -1 - write: 38 - id: [16] - bytes: 2 - type: int - - pedals-clutch-stop-calibration: - read: -1 - write: 38 - id: [17] - bytes: 2 - type: int - - hpattern-start-calibration: - read: -1 - write: 84 - id: [3] - bytes: 2 - type: int - - hpattern-stop-calibration: - read: -1 - write: 84 - id: [4] - bytes: 2 - type: int - - handbrake-start-calibration: - read: -1 - write: 94 - id: [3] - bytes: 2 - type: int - - handbrake-stop-calibration: - read: -1 - write: 94 - id: [4] - bytes: 2 - type: int - -# telemetry stuff - wheel-send-telemetry: - read: -1 - write: 65 - id: [253, 222] - bytes: 4 - type: int + hub: + port1: + read: 100 + write: -1 + id: [1] + bytes: 1 + type: int + + port2: + read: 100 + write: -1 + id: [2] + bytes: 1 + type: int + + port3: + read: 100 + write: -1 + id: [3] + bytes: 1 + type: int + + port4: + read: 100 + write: -1 + id: [4] + bytes: 1 + type: int + + pedals: + read: 100 + write: -1 + id: [5] + bytes: 1 + type: int diff --git a/data/version b/data/version index 9b43a2c..233f361 100644 --- a/data/version +++ b/data/version @@ -1 +1 @@ -v1.19.4 +v1.19.5 diff --git a/io.github.lawstorant.boxflat.metainfo.xml b/io.github.lawstorant.boxflat.metainfo.xml index 1f6b342..62cb8b5 100644 --- a/io.github.lawstorant.boxflat.metainfo.xml +++ b/io.github.lawstorant.boxflat.metainfo.xml @@ -97,6 +97,20 @@ forest10pl@gmail.com + + +

Major rewrite pt.2

+
    +
  • Fixed pedals calibration (apparently, this was borked for a long time)
  • +
  • Asynchronous serial I/O
  • +
  • Every serial device gets it's own I/O process
  • +
  • Exclusive access mode for get/set commands
  • +
  • Subscribtions can now be only single-shot
  • +
  • BlockingValue class enables waiting for value read
  • +
  • Discover commands from received data
  • +
+
+

Reduce disconnection false-positives before serial connection rewrite