From d4e86fff391be42b9674a335350bae19309649ac Mon Sep 17 00:00:00 2001 From: Laurens Valk Date: Thu, 18 Dec 2025 15:45:06 +0100 Subject: [PATCH] pybricksdev/_vendored/pynxt: Replace raw usb with serial. This way we don't need to do things like detaching the kernel driver. This should improve our odds of being able to install NXT firmware from the browser. --- poetry.lock | 41 +++++++- pybricksdev/_vendored/pynxt/lowlevel.py | 74 -------------- pybricksdev/_vendored/pynxt/samba.py | 123 +++++++++++++++--------- pyproject.toml | 1 + 4 files changed, 119 insertions(+), 120 deletions(-) delete mode 100644 pybricksdev/_vendored/pynxt/lowlevel.py diff --git a/poetry.lock b/poetry.lock index d24bf15..9d8953c 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1023,6 +1023,21 @@ files = [ pyobjc-core = ">=11.1" pyobjc-framework-Cocoa = ">=11.1" +[[package]] +name = "pyserial" +version = "3.5" +description = "Python Serial Port Extension" +optional = false +python-versions = "*" +groups = ["main"] +files = [ + {file = "pyserial-3.5-py2.py3-none-any.whl", hash = "sha256:c4451db6ba391ca6ca299fb3ec7bae67a5c55dde170964c7a14ceefec02f2cf0"}, + {file = "pyserial-3.5.tar.gz", hash = "sha256:3c77e014170dfffbd816e6ffc205e9842efb10be9f58ec16d3e8675b4925cddb"}, +] + +[package.extras] +cp2110 = ["hidapi"] + [[package]] name = "pytest" version = "8.4.1" @@ -1489,6 +1504,9 @@ files = [ {file = "winrt_runtime-3.2.1-cp313-cp313-win32.whl", hash = "sha256:44e2733bc709b76c554aee6c7fe079443b8306b2e661e82eecfebe8b9d71e4d1"}, {file = "winrt_runtime-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:3c1fdcaeedeb2920dc3b9039db64089a6093cad2be56a3e64acc938849245a6d"}, {file = "winrt_runtime-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:28f3dab083412625ff4d2b46e81246932e6bebddf67bea7f05e01712f54e6159"}, + {file = "winrt_runtime-3.2.1-cp314-cp314-win32.whl", hash = "sha256:9b6298375468ac2f6815d0c008a059fc16508c8f587e824c7936ed9216480dad"}, + {file = "winrt_runtime-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:e36e587ab5fd681ee472cd9a5995743f75107a1a84d749c64f7e490bc86bc814"}, + {file = "winrt_runtime-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:35d6241a2ebd5598e4788e69768b8890ee1eee401a819865767a1fbdd3e9a650"}, {file = "winrt_runtime-3.2.1-cp39-cp39-win32.whl", hash = "sha256:07c0cb4a53a4448c2cb7597b62ae8c94343c289eeebd8f83f946eb2c817bde01"}, {file = "winrt_runtime-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:1856325ca3354b45e0789cf279be9a882134085d34214946db76110d98391efa"}, {file = "winrt_runtime-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:cf237858de1d62e4c9b132c66b52028a7a3e8534e8ab90b0e29a68f24f7be39d"}, @@ -1519,6 +1537,9 @@ files = [ {file = "winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win32.whl", hash = "sha256:12b0a16fb36ce0b42243ca81f22a6b53fbb344ed7ea07a6eeec294604f0505e4"}, {file = "winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6703dfbe444ee22426738830fb305c96a728ea9ccce905acfdf811d81045fdb3"}, {file = "winrt_windows_devices_bluetooth-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2cf8a0bfc9103e32dc7237af15f84be06c791f37711984abdca761f6318bbdb2"}, + {file = "winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win32.whl", hash = "sha256:de36ded53ca3ba12fc6dd4deb14b779acc391447726543815df4800348aad63a"}, + {file = "winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3295d932cc93259d5ccb23a41e3a3af4c78ce5d6a6223b2b7638985f604fa34c"}, + {file = "winrt_windows_devices_bluetooth-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:1f61c178766a1bbce0669f44790c6161ff4669404c477b4aedaa576348f9e102"}, {file = "winrt_windows_devices_bluetooth-3.2.1-cp39-cp39-win32.whl", hash = "sha256:32fc355bfdc5d6b3b1875df16eaf12f9b9fc0445e01177833c27d9a4fc0d50b6"}, {file = "winrt_windows_devices_bluetooth-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:b886ef1fc0ed49163ae6c2422dd5cb8dd4709da7972af26c8627e211872818d0"}, {file = "winrt_windows_devices_bluetooth-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:8643afa53f9fb8fe3b05967227f86f0c8e1d7b822289e60a848c6368acc977d2"}, @@ -1552,6 +1573,9 @@ files = [ {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win32.whl", hash = "sha256:4122348ea525a914e85615647a0b54ae8b2f42f92cdbf89c5a12eea53ef6ed90"}, {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:b66410c04b8dae634a7e4b615c3b7f8adda9c7d4d6902bcad5b253da1a684943"}, {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:07af19b1d252ddb9dd3eb2965118bc2b7cabff4dda6e499341b765e5038ca61d"}, + {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win32.whl", hash = "sha256:2985565c265b3f9eab625361b0e40e88c94b03d89f5171f36146f2e88b3ee214"}, + {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:d102f3fac64fde32332e370969dfbc6f37b405d8cc055d9da30d14d07449a3c2"}, + {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:ffeb5e946cd42c32c6999a62e240d6730c653cdfb7b49c7839afba375e20a62a"}, {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp39-cp39-win32.whl", hash = "sha256:6c4747d2e5b0e2ef24e9b84a848cf8fc50fb5b268a2086b5ee8680206d1e0197"}, {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:18d4c5d8b80ee2d29cc13c2fc1353fdb3c0f620c8083701c9b9ecf5e6c503c8d"}, {file = "winrt_windows_devices_bluetooth_advertisement-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:75dd856611d847299078d56aee60e319df52975b931c992cd1d32ad5143fe772"}, @@ -1585,6 +1609,9 @@ files = [ {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win32.whl", hash = "sha256:b1879c8dcf46bd2110b9ad4b0b185f4e2a5f95170d014539203a5fee2b2115f0"}, {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:8d8d89f01e9b6931fb48217847caac3227a0aeb38a5b7782af71c2e7b262ec30"}, {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:4e71207bb89798016b1795bb15daf78afe45529f2939b3b9e78894cfe650b383"}, + {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win32.whl", hash = "sha256:d5f83739ca370f0baf52b0400aebd6240ab80150081fbfba60fd6e7b2e7b4c5f"}, + {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:13786a5853a933de140d456cd818696e1121c7c296ae7b7af262fc5d2cffb851"}, + {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:5140682da2860f6a55eb6faf9e980724dc457c2e4b4b35a10e1cebd8fc97d892"}, {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp39-cp39-win32.whl", hash = "sha256:963339a0161f9970b577a6193924be783978d11693da48b41a025f61b3c5562a"}, {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:d43615c5dfa939dd30fe80dc0649434a13cc7cf0294ad0d7283d5a9f48c6ce86"}, {file = "winrt_windows_devices_bluetooth_genericattributeprofile-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:8e70fa970997e2e67a8a4172bc00b0b2a79b5ff5bb2668f79cf10b3fd63d3974"}, @@ -1618,6 +1645,9 @@ files = [ {file = "winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win32.whl", hash = "sha256:14a71cdcc84f624c209cbb846ed6bd9767a9a9437b2bf26b48ac9a91599da6e9"}, {file = "winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6ca40d334734829e178ad46375275c4f7b5d6d2d4fc2e8879690452cbfb36015"}, {file = "winrt_windows_devices_enumeration-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2d14d187f43e4409c7814b7d1693c03a270e77489b710d92fcbbaeca5de260d4"}, + {file = "winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win32.whl", hash = "sha256:e087364273ed7c717cd0191fed4be9def6fdf229fe9b536a4b8d0228f7814106"}, + {file = "winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:0da1ddb8285d97a6775c36265d7157acf1bbcb88bcc9a7ce9a4549906c822472"}, + {file = "winrt_windows_devices_enumeration-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:09bf07e74e897e97a49a9275d0a647819254ddb74142806bbbcf4777ed240a22"}, {file = "winrt_windows_devices_enumeration-3.2.1-cp39-cp39-win32.whl", hash = "sha256:986e8d651b769a0e60d2834834bdd3f6959f6a88caa0c9acb917797e6b43a588"}, {file = "winrt_windows_devices_enumeration-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10da7d403ac4afd385fe13bd5808c9a5dd616a8ef31ca5c64cea3f87673661c1"}, {file = "winrt_windows_devices_enumeration-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:679e471d21ac22cb50de1bf4dfc4c0c3f5da9f3e3fbc7f08dcacfe9de9d6dd58"}, @@ -1651,6 +1681,9 @@ files = [ {file = "winrt_windows_foundation-3.2.1-cp313-cp313-win32.whl", hash = "sha256:3998dc58ed50ecbdbabace1cdef3a12920b725e32a5806d648ad3f4829d5ba46"}, {file = "winrt_windows_foundation-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:6e98617c1e46665c7a56ce3f5d28e252798416d1ebfee3201267a644a4e3c479"}, {file = "winrt_windows_foundation-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:2a8c1204db5c352f6a563130a5a41d25b887aff7897bb677d4ff0b660315aad4"}, + {file = "winrt_windows_foundation-3.2.1-cp314-cp314-win32.whl", hash = "sha256:35e973ab3c77c2a943e139302256c040e017fd6ff1a75911c102964603bba1da"}, + {file = "winrt_windows_foundation-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:a22a7ebcec0d262e60119cff728f32962a02df60471ded8b2735a655eccc0ef5"}, + {file = "winrt_windows_foundation-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:3be7fbae829b98a6a946db4fbaf356b11db1fbcbb5d4f37e7a73ac6b25de8b87"}, {file = "winrt_windows_foundation-3.2.1-cp39-cp39-win32.whl", hash = "sha256:14d5191725301498e4feb744d91f5b46ce317bf3d28370efda407d5c87f4423b"}, {file = "winrt_windows_foundation-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:de5e4f61d253a91ba05019dbf4338c43f962bdad935721ced5e7997933994af5"}, {file = "winrt_windows_foundation-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:ebbf6e8168398c9ed0c72c8bdde95a406b9fbb9a23e3705d4f0fe28e5a209705"}, @@ -1684,6 +1717,9 @@ files = [ {file = "winrt_windows_foundation_collections-3.2.1-cp313-cp313-win32.whl", hash = "sha256:4267a711b63476d36d39227883aeb3fb19ac92b88a9fc9973e66fbce1fd4aed9"}, {file = "winrt_windows_foundation_collections-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:5e12a6e75036ee90484c33e204b85fb6785fcc9e7c8066ad65097301f48cdd10"}, {file = "winrt_windows_foundation_collections-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:34b556255562f1b36d07fba933c2bcd9f0db167fa96727a6cbb4717b152ad7a2"}, + {file = "winrt_windows_foundation_collections-3.2.1-cp314-cp314-win32.whl", hash = "sha256:33188ed2d63e844c8adfbb82d1d3d461d64aaf78d225ce9c5930421b413c45ab"}, + {file = "winrt_windows_foundation_collections-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:d4cfece7e9c0ead2941e55a1da82f20d2b9c8003bb7a8853bb7f999b539f80a4"}, + {file = "winrt_windows_foundation_collections-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:3884146fea13727510458f6a14040b7632d5d90127028b9bfd503c6c655d0c01"}, {file = "winrt_windows_foundation_collections-3.2.1-cp39-cp39-win32.whl", hash = "sha256:20610f098b84c87765018cbc71471092197881f3b92e5d06158fad3bfcea2563"}, {file = "winrt_windows_foundation_collections-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:e9739775320ac4c0238e1775d94a54e886d621f9995977e65d4feb8b3778c111"}, {file = "winrt_windows_foundation_collections-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:e4c6bddb1359d5014ceb45fe2ecd838d4afeb1184f2ea202c2d21037af0d08a3"}, @@ -1717,6 +1753,9 @@ files = [ {file = "winrt_windows_storage_streams-3.2.1-cp313-cp313-win32.whl", hash = "sha256:401bb44371720dc43bd1e78662615a2124372e7d5d9d65dfa8f77877bbcb8163"}, {file = "winrt_windows_storage_streams-3.2.1-cp313-cp313-win_amd64.whl", hash = "sha256:202c5875606398b8bfaa2a290831458bb55f2196a39c1d4e5fa88a03d65ef915"}, {file = "winrt_windows_storage_streams-3.2.1-cp313-cp313-win_arm64.whl", hash = "sha256:ca3c5ec0aab60895006bf61053a1aca6418bc7f9a27a34791ba3443b789d230d"}, + {file = "winrt_windows_storage_streams-3.2.1-cp314-cp314-win32.whl", hash = "sha256:5cd0dbad86fcc860366f6515fce97177b7eaa7069da261057be4813819ba37ee"}, + {file = "winrt_windows_storage_streams-3.2.1-cp314-cp314-win_amd64.whl", hash = "sha256:3c5bf41d725369b9986e6d64bad7079372b95c329897d684f955d7028c7f27a0"}, + {file = "winrt_windows_storage_streams-3.2.1-cp314-cp314-win_arm64.whl", hash = "sha256:293e09825559d0929bbe5de01e1e115f7a6283d8996ab55652e5af365f032987"}, {file = "winrt_windows_storage_streams-3.2.1-cp39-cp39-win32.whl", hash = "sha256:1c630cfdece58fcf82e4ed86c826326123529836d6d4d855ae8e9ceeff67b627"}, {file = "winrt_windows_storage_streams-3.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:d7ff22434a4829d616a04b068a191ac79e008f6c27541bb178c1f6f1fe7a1657"}, {file = "winrt_windows_storage_streams-3.2.1-cp39-cp39-win_arm64.whl", hash = "sha256:fa90244191108f85f6f7afb43a11d365aca4e0722fe8adc62fb4d2c678d0993d"}, @@ -1732,4 +1771,4 @@ all = ["winrt-Windows.Foundation.Collections[all] (>=3.2.1.0,<3.3.0.0)", "winrt- [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "f72f31d5e94d97b2cda640772bd557c5f66f0461a2b6e9aac573afbf2e201a26" +content-hash = "c9e2d8c27cf3dd9a8a848464c8328d53f53b618ef0a0198d5d9e2699e8f53b8d" diff --git a/pybricksdev/_vendored/pynxt/lowlevel.py b/pybricksdev/_vendored/pynxt/lowlevel.py deleted file mode 100644 index bcc112b..0000000 --- a/pybricksdev/_vendored/pynxt/lowlevel.py +++ /dev/null @@ -1,74 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# Copyright 2006 David Anderson - -import time - -import usb - -USB_BULK_OUT_EP = 0x1 -USB_BULK_IN_EP = 0x82 - - -def enumerate_usb(): - """Return a generator yielding all attached USB devices.""" - busses = usb.busses() - for bus in busses: - devices = bus.devices - for dev in devices: - yield dev - - -def get_device(vendor_id, product_id, version=0, timeout=None): - """Return the first device matching the given vendor/product ID.""" - while True: - for dev in enumerate_usb(): - if ( - dev.idVendor == vendor_id - and dev.idProduct == product_id - and dev.iSerialNumber == version - ): - return UsbBrick(dev) - if timeout is None or timeout <= 0: - return None - sleep_time = min(1.0, timeout) - time.sleep(sleep_time) - timeout -= sleep_time - - -class UsbBrick(object): - def __init__(self, dev): - self._dev = dev - - def __del__(self): - try: - self.close() - except Exception: - pass - - def open(self, interface, configuration=1, detach_kernel_driver=False): - self._iface = interface - self._config = configuration - self._hdl = self._dev.open() - if detach_kernel_driver: - self._hdl.detachKernelDriver(interface) - self._hdl.setConfiguration(configuration) - self._hdl.claimInterface(interface) - - def close(self): - self._hdl.releaseInterface() - del self._hdl - - def read(self, size, timeout=100): - """Read the given amount of data from the device and return it.""" - # For some reason, bulkRead returns a tuple of longs. This is - # dumb, so we convert it back to a string before returning, - # kthx. - try: - data = self._hdl.bulkRead(USB_BULK_IN_EP, size, timeout) - except usb.USBError: - return None - return "".join(chr(x) for x in data) - - def write(self, data, timeout=100): - """Write the given amount of data to the device.""" - return self._hdl.bulkWrite(USB_BULK_OUT_EP, data, timeout) diff --git a/pybricksdev/_vendored/pynxt/samba.py b/pybricksdev/_vendored/pynxt/samba.py index 576415b..033271c 100644 --- a/pybricksdev/_vendored/pynxt/samba.py +++ b/pybricksdev/_vendored/pynxt/samba.py @@ -1,77 +1,110 @@ -# SPDX-License-Identifier: GPL-2.0-only -# Copyright 2006 David Anderson - import struct +import time -from pybricksdev._vendored.pynxt import lowlevel +import serial +from serial.tools import list_ports ATMEL_VENDOR_ID = 0x03EB SAMBA_PRODUCT_ID = 0x6124 -SAMBA_USB_INTERFACE = 1 class SambaOpenError(Exception): - """An error occured while opening a connection to SAM-BA""" + """An error occurred while opening a connection to SAM-BA""" def _command(code, address): - return "%c%08X#" % (code, address) + return f"{code}{address:08X}#".encode("ascii") def _command2(code, address, value): - return "%c%08X,%08X#" % (code, address, value) + return f"{code}{address:08X},{value:08X}#".encode("ascii") -class SambaBrick(object): +class SambaBrick: def __init__(self): - self.usb = None + self.ser = None + + def open(self, timeout=5): + # enumerate serial ports (Web Serial equivalent of chooser) + matches = [ + p + for p in list_ports.comports() + if p.vid == ATMEL_VENDOR_ID and p.pid == SAMBA_PRODUCT_ID + ] + + if not matches: + raise SambaOpenError("No SAM-BA device found (03eb:6124).") + + if len(matches) > 1: + raise SambaOpenError( + "Multiple SAM-BA devices found; cannot choose automatically." + ) + + port = matches[0].device - def __del__(self): try: - self.close() - except Exception: - pass - - def open(self, timeout=None): - self.usb = lowlevel.get_device( - ATMEL_VENDOR_ID, SAMBA_PRODUCT_ID, timeout=timeout - ) - if not self.usb: - raise SambaOpenError("Could not find a SAM-BA brick to connect to") - self.usb.open(SAMBA_USB_INTERFACE, detach_kernel_driver=True) + self.ser = serial.Serial( + port=port, + timeout=timeout, + write_timeout=timeout, + exclusive=True, + ) + except serial.SerialException as e: + raise SambaOpenError(str(e)) + + # Give CDC ACM time to settle (mirrors browser behavior) + time.sleep(0.1) + + self.ser.reset_input_buffer() + self.ser.reset_output_buffer() # Initial SAM-BA handshake. - self.usb.write("N#") - res = self.usb.read(2) - if res != "\n\r": - raise SambaOpenError("Incorrect handshake response") + self._write(b"N#") + res = self._read_exact(2) + + if res != b"\n\r": + raise SambaOpenError(f"Incorrect handshake response: {res!r}") def close(self): - self.usb.close() - self.usb = None + if self.ser: + self.ser.close() + self.ser = None + + def _write(self, data: bytes): + self.ser.write(data) + self.ser.flush() + + def _read_exact(self, n: int) -> bytes: + buf = bytearray() + while len(buf) < n: + chunk = self.ser.read(n - len(buf)) + if not chunk: + raise SambaOpenError("Timeout while reading from SAM-BA") + buf.extend(chunk) + return bytes(buf) def write_byte(self, address, byte): - assert 0 <= byte <= 255 - self.usb.write(_command2("O", address, byte)) + assert 0 <= byte <= 0xFF + self._write(_command2("O", address, byte)) def write_halfword(self, address, halfword): - assert 0 <= halfword <= (2**16 - 1) - self.usb.write(_command2("H", address, halfword)) + assert 0 <= halfword <= 0xFFFF + self._write(_command2("H", address, halfword)) def write_word(self, address, word): - assert 0 <= word <= (2**32 - 1) - self.usb.write(_command2("W", address, word)) + assert 0 <= word <= 0xFFFFFFFF + self._write(_command2("W", address, word)) def write_buffer(self, address, data): - self.usb.write(_command2("S", address, len(data))) - self.usb.write(data) + self._write(_command2("S", address, len(data))) + self._write(data) def _read_common(self, code, address, size, struct_code): assert size in (1, 2, 4) - self.usb.write(_command2(code, address, size)) + self._write(_command2(code, address, size)) - res = self.usb.read(size).encode() - return struct.unpack("<%c" % struct_code, res)[0] + raw = self._read_exact(size) + return struct.unpack("<" + struct_code, raw)[0] def read_byte(self, address): return self._read_common("o", address, 1, "B") @@ -80,15 +113,15 @@ def read_halfword(self, address): return self._read_common("h", address, 2, "H") def read_word(self, address): - return self._read_common("w", address, 4, "L") + return self._read_common("w", address, 4, "I") def read_buffer(self, address, len): - self.usb.write(_command2("R", address, len)) - return self.usb.read(len) + self._write(_command2("R", address, len)) + return self._read_exact(len) def jump(self, address): - self.usb.write(_command("G", address)) + self._write(_command("G", address)) def version(self): - self.usb.write("V#") - return self.usb.read(4) + self._write(b"V#") + return self._read_exact(4) diff --git a/pyproject.toml b/pyproject.toml index 7d2e5e5..8ea4ab3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -49,6 +49,7 @@ reactivex = {version = ">=4.0.4", python = "<4"} hidapi = ">=0.14.0" pybricks = {version = ">=3", allow-prereleases = true, python = "<4"} questionary = {version = ">=2.1.1", python = "<4"} +pyserial = "^3.5" [tool.poetry.group.lint.dependencies] black = ">=23,<25"