From 7337e0802a20bc8394faaf32bb97c60210b6e942 Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 24 Feb 2025 14:27:31 +1100 Subject: [PATCH 01/24] github/workflows: Update actions/upload-artifact to v4. Because v3 is now deprecated. Signed-off-by: Damien George --- .github/workflows/build_packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_packages.yml b/.github/workflows/build_packages.yml index 8854a7307..a89658e2f 100644 --- a/.github/workflows/build_packages.yml +++ b/.github/workflows/build_packages.yml @@ -23,7 +23,7 @@ jobs: if: vars.MICROPY_PUBLISH_MIP_INDEX && github.event_name == 'push' && ! github.event.deleted run: source tools/ci.sh && ci_push_package_index - name: Upload packages as artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: packages-${{ github.sha }} path: ${{ env.PACKAGE_INDEX_PATH }} From 96e17b65d128cac2328e10f317241d3a64aca2c7 Mon Sep 17 00:00:00 2001 From: Damien George Date: Tue, 11 Mar 2025 16:27:09 +1100 Subject: [PATCH 02/24] mip: Make mip.install() skip /rom*/lib directories. If a ROMFS is mounted then "/rom/lib" is usually in `sys.path` before the writable filesystem's "lib" entry. The ROMFS directory cannot be installed to, so skip it if found. Signed-off-by: Damien George --- micropython/mip/manifest.py | 2 +- micropython/mip/mip/__init__.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/micropython/mip/manifest.py b/micropython/mip/manifest.py index 2a35f8c5b..9fb94ebcb 100644 --- a/micropython/mip/manifest.py +++ b/micropython/mip/manifest.py @@ -1,4 +1,4 @@ -metadata(version="0.4.0", description="On-device package installer for network-capable boards") +metadata(version="0.4.1", description="On-device package installer for network-capable boards") require("requests") diff --git a/micropython/mip/mip/__init__.py b/micropython/mip/mip/__init__.py index 8920ad8f4..7c0fb4d3a 100644 --- a/micropython/mip/mip/__init__.py +++ b/micropython/mip/mip/__init__.py @@ -171,7 +171,7 @@ def _install_package(package, index, target, version, mpy): def install(package, index=None, target=None, version=None, mpy=True): if not target: for p in sys.path: - if p.endswith("/lib"): + if not p.startswith("/rom") and p.endswith("/lib"): target = p break else: From 98d0a2b69a24b9b53309be34d7c5aa6aede45c5e Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 7 Nov 2024 12:25:13 +1100 Subject: [PATCH 03/24] umqtt.simple: Restore legacy ssl/ssl_params arguments. Commit 35d41dbb0e4acf1518f520220d405ebe2db257d6 changed the API for using SSL with umqtt, but only did a minor version increase. This broke various uses of this library, eg https://github.com/aws-samples/aws-iot-core-getting-started-micropython Reinstate the original API for specifying an SSL connection. This library now supports the following: - default, ssl=None or ssl=False: no SSL - ssl=True and optional ssl_params specified: use ssl.wrap_socket - ssl=: use provided SSL context to wrap socket Signed-off-by: Damien George --- micropython/umqtt.simple/manifest.py | 4 +++- micropython/umqtt.simple/umqtt/simple.py | 9 ++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/micropython/umqtt.simple/manifest.py b/micropython/umqtt.simple/manifest.py index 45f9edfbd..709a27505 100644 --- a/micropython/umqtt.simple/manifest.py +++ b/micropython/umqtt.simple/manifest.py @@ -1,5 +1,7 @@ -metadata(description="Lightweight MQTT client for MicroPython.", version="1.5.0") +metadata(description="Lightweight MQTT client for MicroPython.", version="1.6.0") # Originally written by Paul Sokolovsky. +require("ssl") + package("umqtt") diff --git a/micropython/umqtt.simple/umqtt/simple.py b/micropython/umqtt.simple/umqtt/simple.py index 2a5b91655..d9cdffc47 100644 --- a/micropython/umqtt.simple/umqtt/simple.py +++ b/micropython/umqtt.simple/umqtt/simple.py @@ -17,6 +17,7 @@ def __init__( password=None, keepalive=0, ssl=None, + ssl_params={}, ): if port == 0: port = 8883 if ssl else 1883 @@ -25,6 +26,7 @@ def __init__( self.server = server self.port = port self.ssl = ssl + self.ssl_params = ssl_params self.pid = 0 self.cb = None self.user = user @@ -65,7 +67,12 @@ def connect(self, clean_session=True, timeout=None): self.sock.settimeout(timeout) addr = socket.getaddrinfo(self.server, self.port)[0][-1] self.sock.connect(addr) - if self.ssl: + if self.ssl is True: + # Legacy support for ssl=True and ssl_params arguments. + import ssl + + self.sock = ssl.wrap_socket(self.sock, **self.ssl_params) + elif self.ssl: self.sock = self.ssl.wrap_socket(self.sock, server_hostname=self.server) premsg = bytearray(b"\x10\0\0\0\0\0") msg = bytearray(b"\x04MQTT\x04\x02\0\0") From 3e859d2118c4ef9af5f43c930b3138185e5a9fb4 Mon Sep 17 00:00:00 2001 From: marcsello Date: Mon, 30 Dec 2024 15:20:18 +0100 Subject: [PATCH 04/24] nrf24l01: Increase startup delay. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the datasheet of the NRF240L1 chip, 150 μs startup time is only acceptable when the chip is clocked externally. Most modules use a crystal, which require 1.5 ms to settle. It should be okay to wait more in both cases, for a reliable startup. Signed-off-by: Marcell Pünkösd --- micropython/drivers/radio/nrf24l01/nrf24l01.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropython/drivers/radio/nrf24l01/nrf24l01.py b/micropython/drivers/radio/nrf24l01/nrf24l01.py index 76d55312f..9b034f8ba 100644 --- a/micropython/drivers/radio/nrf24l01/nrf24l01.py +++ b/micropython/drivers/radio/nrf24l01/nrf24l01.py @@ -227,7 +227,7 @@ def send(self, buf, timeout=500): def send_start(self, buf): # power up self.reg_write(CONFIG, (self.reg_read(CONFIG) | PWR_UP) & ~PRIM_RX) - utime.sleep_us(150) + utime.sleep_us(1500) # needs to be 1.5ms # send the data self.cs(0) self.spi.readinto(self.buf, W_TX_PAYLOAD) From bd1ab77324641238e684cd26e1686a890868b096 Mon Sep 17 00:00:00 2001 From: marcsello Date: Mon, 30 Dec 2024 15:47:09 +0100 Subject: [PATCH 05/24] nrf24l01: Properly handle timeout. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The timeout condition was not handled before. Upon timeout, this caused the chip to stay active until another send command changed it's state. Sometimes when it was unable to transmit the data, it got stuck in the tx fifo causing it to fill up over time, which set the TX_FULL flag in the STATUS register. Since there was no exceptions raised, the user code could not differentiate a successful send or a timeout condition. Signed-off-by: Marcell Pünkösd --- micropython/drivers/radio/nrf24l01/nrf24l01.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/micropython/drivers/radio/nrf24l01/nrf24l01.py b/micropython/drivers/radio/nrf24l01/nrf24l01.py index 9b034f8ba..04f07b9d9 100644 --- a/micropython/drivers/radio/nrf24l01/nrf24l01.py +++ b/micropython/drivers/radio/nrf24l01/nrf24l01.py @@ -220,6 +220,13 @@ def send(self, buf, timeout=500): result = None while result is None and utime.ticks_diff(utime.ticks_ms(), start) < timeout: result = self.send_done() # 1 == success, 2 == fail + + if result is None: + # timed out, cancel sending and power down the module + self.flush_tx() + self.reg_write(CONFIG, self.reg_read(CONFIG) & ~PWR_UP) + raise OSError("timed out") + if result == 2: raise OSError("send failed") From c7103bb464507137a32edafc77698e40893b773e Mon Sep 17 00:00:00 2001 From: marcsello Date: Mon, 30 Dec 2024 16:10:49 +0100 Subject: [PATCH 06/24] nrf24l01: Optimize status reading. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The value of the STATUS register is always transmitted by the chip when reading any command. So a R_REGISTER command and the turnaround time can be spared by issuing a NOP command instead. This implementation suggested by the datasheet. This operation is compatible with both nRF24L01 and nRF24L01+. Signed-off-by: Marcell Pünkösd --- micropython/drivers/radio/nrf24l01/nrf24l01.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/micropython/drivers/radio/nrf24l01/nrf24l01.py b/micropython/drivers/radio/nrf24l01/nrf24l01.py index 04f07b9d9..d015250cf 100644 --- a/micropython/drivers/radio/nrf24l01/nrf24l01.py +++ b/micropython/drivers/radio/nrf24l01/nrf24l01.py @@ -130,6 +130,13 @@ def reg_write(self, reg, value): self.cs(1) return ret + def read_status(self): + self.cs(0) + # STATUS register is always shifted during command transmit + self.spi.readinto(self.buf, NOP) + self.cs(1) + return self.buf[0] + def flush_rx(self): self.cs(0) self.spi.readinto(self.buf, FLUSH_RX) @@ -250,7 +257,8 @@ def send_start(self, buf): # returns None if send still in progress, 1 for success, 2 for fail def send_done(self): - if not (self.reg_read(STATUS) & (TX_DS | MAX_RT)): + status = self.read_status() + if not (status & (TX_DS | MAX_RT)): return None # tx not finished # either finished or failed: get and clear status flags, power down From 221a877f8a572a489e9859d0851969d57d71b8d4 Mon Sep 17 00:00:00 2001 From: Damien George Date: Thu, 10 Apr 2025 22:33:53 +1000 Subject: [PATCH 07/24] nrf24l10: Bump minor version. Due to the previous three commits. Signed-off-by: Damien George --- micropython/drivers/radio/nrf24l01/manifest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropython/drivers/radio/nrf24l01/manifest.py b/micropython/drivers/radio/nrf24l01/manifest.py index 474d422f9..24276ee4b 100644 --- a/micropython/drivers/radio/nrf24l01/manifest.py +++ b/micropython/drivers/radio/nrf24l01/manifest.py @@ -1,3 +1,3 @@ -metadata(description="nrf24l01 2.4GHz radio driver.", version="0.1.0") +metadata(description="nrf24l01 2.4GHz radio driver.", version="0.2.0") module("nrf24l01.py", opt=3) From f72f3f1a391f15d6fbed01d76f8c97a27427db2f Mon Sep 17 00:00:00 2001 From: Leonard Techel Date: Wed, 29 Jan 2025 10:22:17 +0100 Subject: [PATCH 08/24] lora-sx126x: Fix invert_iq_rx / invert_iq_tx behaviour. This commit fixes a typo and changes a tuple that needs to be mutable to a list (because other parts of the code change elements of this list). Signed-off-by: Damien George --- micropython/lora/lora-sx126x/lora/sx126x.py | 6 +++--- micropython/lora/lora-sx126x/manifest.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/micropython/lora/lora-sx126x/lora/sx126x.py b/micropython/lora/lora-sx126x/lora/sx126x.py index 77052c97c..641367a9f 100644 --- a/micropython/lora/lora-sx126x/lora/sx126x.py +++ b/micropython/lora/lora-sx126x/lora/sx126x.py @@ -363,11 +363,11 @@ def configure(self, lora_cfg): if "preamble_len" in lora_cfg: self._preamble_len = lora_cfg["preamble_len"] - self._invert_iq = ( + self._invert_iq = [ lora_cfg.get("invert_iq_rx", self._invert_iq[0]), lora_cfg.get("invert_iq_tx", self._invert_iq[1]), self._invert_iq[2], - ) + ] if "freq_khz" in lora_cfg: self._rf_freq_hz = int(lora_cfg["freq_khz"] * 1000) @@ -449,7 +449,7 @@ def configure(self, lora_cfg): def _invert_workaround(self, enable): # Apply workaround for DS 15.4 Optimizing the Inverted IQ Operation if self._invert_iq[2] != enable: - val = self._read_read(_REG_IQ_POLARITY_SETUP) + val = self._reg_read(_REG_IQ_POLARITY_SETUP) val = (val & ~4) | _flag(4, enable) self._reg_write(_REG_IQ_POLARITY_SETUP, val) self._invert_iq[2] = enable diff --git a/micropython/lora/lora-sx126x/manifest.py b/micropython/lora/lora-sx126x/manifest.py index 785a975aa..038710820 100644 --- a/micropython/lora/lora-sx126x/manifest.py +++ b/micropython/lora/lora-sx126x/manifest.py @@ -1,3 +1,3 @@ -metadata(version="0.1.3") +metadata(version="0.1.4") require("lora") package("lora") From d1a74360a20dadfaae717d4d8c8bd3531672362f Mon Sep 17 00:00:00 2001 From: Dominik Heidler Date: Fri, 14 Mar 2025 13:36:30 +0100 Subject: [PATCH 09/24] unix-ffi/json: Accept both str and bytes as arg for json.loads(). Same as micropython's internal json lib does. Fixes #985. Signed-off-by: Dominik Heidler --- unix-ffi/json/json/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/unix-ffi/json/json/__init__.py b/unix-ffi/json/json/__init__.py index 69a045563..954618f33 100644 --- a/unix-ffi/json/json/__init__.py +++ b/unix-ffi/json/json/__init__.py @@ -354,8 +354,8 @@ def loads( object_pairs_hook=None, **kw ): - """Deserialize ``s`` (a ``str`` instance containing a JSON - document) to a Python object. + """Deserialize ``s`` (a ``str``, ``bytes`` or ``bytearray`` instance + containing a JSON document) to a Python object. ``object_hook`` is an optional function that will be called with the result of any object literal decode (a ``dict``). The return value of @@ -413,4 +413,6 @@ def loads( kw["parse_int"] = parse_int if parse_constant is not None: kw["parse_constant"] = parse_constant + if isinstance(s, (bytes, bytearray)): + s = s.decode('utf-8') return cls(**kw).decode(s) From 42caaf14de35e39ff2e843e9957c7a3f41702fa9 Mon Sep 17 00:00:00 2001 From: Bas van Doren Date: Sun, 6 Apr 2025 14:42:09 +0200 Subject: [PATCH 10/24] unix-ffi/machine: Use libc if librt is not present. Newer implementations of libc integrate the functions from librt, for example glibc since 2.17 and uClibc-ng. So if the librt.so cannot be loaded, it can be assumed that libc contains the expected functions. Signed-off-by: Bas van Doren --- unix-ffi/machine/machine/timer.py | 5 ++++- unix-ffi/machine/manifest.py | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/unix-ffi/machine/machine/timer.py b/unix-ffi/machine/machine/timer.py index 3f371142c..be00cee33 100644 --- a/unix-ffi/machine/machine/timer.py +++ b/unix-ffi/machine/machine/timer.py @@ -5,7 +5,10 @@ from signal import * libc = ffilib.libc() -librt = ffilib.open("librt") +try: + librt = ffilib.open("librt") +except OSError as e: + librt = libc CLOCK_REALTIME = 0 CLOCK_MONOTONIC = 1 diff --git a/unix-ffi/machine/manifest.py b/unix-ffi/machine/manifest.py index c0e40764d..f7c11b81a 100644 --- a/unix-ffi/machine/manifest.py +++ b/unix-ffi/machine/manifest.py @@ -1,4 +1,4 @@ -metadata(version="0.2.1") +metadata(version="0.2.2") # Originally written by Paul Sokolovsky. From 43ad7c58fd5fc247c255dacb37ab815ec212ee71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=83=E6=98=95=E6=9A=90?= Date: Fri, 6 Dec 2024 13:43:24 +0800 Subject: [PATCH 11/24] requests: Use the host in the redirect url, not the one in headers. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The host in headers extracted from the original url may not be the same as the host in the redirect url. Poping out the host in headers force the code to use the host in the redirect url, otherwise the redirect may fail due to inconsistence of hosts in the original url and the redirect url. Signed-off-by: 黃昕暐 --- python-ecosys/requests/manifest.py | 2 +- python-ecosys/requests/requests/__init__.py | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/python-ecosys/requests/manifest.py b/python-ecosys/requests/manifest.py index f8343e2a1..85f159753 100644 --- a/python-ecosys/requests/manifest.py +++ b/python-ecosys/requests/manifest.py @@ -1,3 +1,3 @@ -metadata(version="0.10.1", pypi="requests") +metadata(version="0.10.2", pypi="requests") package("requests") diff --git a/python-ecosys/requests/requests/__init__.py b/python-ecosys/requests/requests/__init__.py index 2951035f7..4ca7489a4 100644 --- a/python-ecosys/requests/requests/__init__.py +++ b/python-ecosys/requests/requests/__init__.py @@ -182,6 +182,8 @@ def request( if redirect: s.close() + # Use the host specified in the redirect URL, as it may not be the same as the original URL. + headers.pop("Host", None) if status in [301, 302, 303]: return request("GET", redirect, None, None, headers, stream) else: From 86df7233019678616f864e4325460a2f893c5e7f Mon Sep 17 00:00:00 2001 From: FuNK3Y Date: Fri, 7 Feb 2025 19:53:53 +0100 Subject: [PATCH 12/24] aiohttp: Fix header case sensitivity. According to RFC https://datatracker.ietf.org/doc/html/rfc7230#section-3.2 header names are case-insensitive. This commit makes sure that the module behaves consistently regardless of the casing of "Content-type" and "Content-Length" (other headers are not considered by the module). Without this fix, the client seems to wait for the connection termination (~10 seconds) prior to returning any content if the casing of "Content-Length" is different. Signed-off-by: FuNK3Y --- python-ecosys/aiohttp/aiohttp/__init__.py | 12 +++++++++--- python-ecosys/aiohttp/manifest.py | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/python-ecosys/aiohttp/aiohttp/__init__.py b/python-ecosys/aiohttp/aiohttp/__init__.py index 1565163c4..3f57bac83 100644 --- a/python-ecosys/aiohttp/aiohttp/__init__.py +++ b/python-ecosys/aiohttp/aiohttp/__init__.py @@ -18,8 +18,14 @@ class ClientResponse: def __init__(self, reader): self.content = reader + def _get_header(self, keyname, default): + for k in self.headers: + if k.lower() == keyname: + return self.headers[k] + return default + def _decode(self, data): - c_encoding = self.headers.get("Content-Encoding") + c_encoding = self._get_header("content-encoding", None) if c_encoding in ("gzip", "deflate", "gzip,deflate"): try: import deflate @@ -39,10 +45,10 @@ async def read(self, sz=-1): return self._decode(await self.content.read(sz)) async def text(self, encoding="utf-8"): - return (await self.read(int(self.headers.get("Content-Length", -1)))).decode(encoding) + return (await self.read(int(self._get_header("content-length", -1)))).decode(encoding) async def json(self): - return _json.loads(await self.read(int(self.headers.get("Content-Length", -1)))) + return _json.loads(await self.read(int(self._get_header("content-length", -1)))) def __repr__(self): return "" % (self.status, self.headers) diff --git a/python-ecosys/aiohttp/manifest.py b/python-ecosys/aiohttp/manifest.py index 748970e5b..5020ec527 100644 --- a/python-ecosys/aiohttp/manifest.py +++ b/python-ecosys/aiohttp/manifest.py @@ -1,6 +1,6 @@ metadata( description="HTTP client module for MicroPython asyncio module", - version="0.0.3", + version="0.0.4", pypi="aiohttp", ) From 05a56c3cad059245c62df5d76baa5ebc3340f812 Mon Sep 17 00:00:00 2001 From: jomas Date: Mon, 2 Dec 2024 11:23:07 +0100 Subject: [PATCH 13/24] aiohttp: Allow headers to be passed to a WebSocketClient. This commit will make it possible to add headers to a Websocket. Among other things, this allows making a connection to online MQTT brokers over websocket, using the header entry "Sec-WebSocket-Protocol":"mqtt" in the handshake of the upgrade protocol. Signed-off-by: Damien George --- python-ecosys/aiohttp/aiohttp/__init__.py | 2 +- python-ecosys/aiohttp/aiohttp/aiohttp_ws.py | 2 +- python-ecosys/aiohttp/manifest.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/python-ecosys/aiohttp/aiohttp/__init__.py b/python-ecosys/aiohttp/aiohttp/__init__.py index 3f57bac83..8c5493f30 100644 --- a/python-ecosys/aiohttp/aiohttp/__init__.py +++ b/python-ecosys/aiohttp/aiohttp/__init__.py @@ -269,7 +269,7 @@ def ws_connect(self, url, ssl=None): return _WSRequestContextManager(self, self._ws_connect(url, ssl=ssl)) async def _ws_connect(self, url, ssl=None): - ws_client = WebSocketClient(None) + ws_client = WebSocketClient(self._base_headers.copy()) await ws_client.connect(url, ssl=ssl, handshake_request=self.request_raw) self._reader = ws_client.reader return ClientWebSocketResponse(ws_client) diff --git a/python-ecosys/aiohttp/aiohttp/aiohttp_ws.py b/python-ecosys/aiohttp/aiohttp/aiohttp_ws.py index 07d833730..6e0818c92 100644 --- a/python-ecosys/aiohttp/aiohttp/aiohttp_ws.py +++ b/python-ecosys/aiohttp/aiohttp/aiohttp_ws.py @@ -136,7 +136,7 @@ def _encode_websocket_frame(cls, opcode, payload): return frame + payload async def handshake(self, uri, ssl, req): - headers = {} + headers = self.params _http_proto = "http" if uri.protocol != "wss" else "https" url = f"{_http_proto}://{uri.hostname}:{uri.port}{uri.path or '/'}" key = binascii.b2a_base64(bytes(random.getrandbits(8) for _ in range(16)))[:-1] diff --git a/python-ecosys/aiohttp/manifest.py b/python-ecosys/aiohttp/manifest.py index 5020ec527..d22a6ce11 100644 --- a/python-ecosys/aiohttp/manifest.py +++ b/python-ecosys/aiohttp/manifest.py @@ -1,6 +1,6 @@ metadata( description="HTTP client module for MicroPython asyncio module", - version="0.0.4", + version="0.0.5", pypi="aiohttp", ) From 9307e21dfb34152ac605cd810447716b459d7f7d Mon Sep 17 00:00:00 2001 From: Matthias Urlichs Date: Wed, 26 Mar 2025 12:00:40 +0100 Subject: [PATCH 14/24] usb-device-cdc: Optimise writing small data so it doesn't require alloc. Only allocate a memoryview when the (first) write was partial. Signed-off-by: Matthias Urlichs --- micropython/usb/usb-device-cdc/manifest.py | 2 +- micropython/usb/usb-device-cdc/usb/device/cdc.py | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/micropython/usb/usb-device-cdc/manifest.py b/micropython/usb/usb-device-cdc/manifest.py index 4520325e3..e844b6f01 100644 --- a/micropython/usb/usb-device-cdc/manifest.py +++ b/micropython/usb/usb-device-cdc/manifest.py @@ -1,3 +1,3 @@ -metadata(version="0.1.1") +metadata(version="0.1.2") require("usb-device") package("usb") diff --git a/micropython/usb/usb-device-cdc/usb/device/cdc.py b/micropython/usb/usb-device-cdc/usb/device/cdc.py index 28bfb0657..0acea184f 100644 --- a/micropython/usb/usb-device-cdc/usb/device/cdc.py +++ b/micropython/usb/usb-device-cdc/usb/device/cdc.py @@ -350,10 +350,9 @@ def _rd_cb(self, ep, res, num_bytes): ### def write(self, buf): - # use a memoryview to track how much of 'buf' we've written so far - # (unfortunately, this means a 1 block allocation for each write, but it's otherwise allocation free.) start = time.ticks_ms() - mv = memoryview(buf) + mv = buf + while True: # Keep pushing buf into _wb into it's all gone nbytes = self._wb.write(mv) @@ -362,6 +361,10 @@ def write(self, buf): if nbytes == len(mv): return len(buf) # Success + # if buf couldn't be fully written on the first attempt + # convert it to a memoryview to track partial writes + if mv is buf: + mv = memoryview(buf) mv = mv[nbytes:] # check for timeout From 48bf3a74a8599aca7ddf8dfbb9d08d8cd5172d25 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Apr 2025 12:23:33 +1000 Subject: [PATCH 15/24] inspect: Fix isgenerator logic. Also optimise both `isgenerator()` and `isgeneratorfunction()` so they use the same lambda, and don't have to create it each time they are called. Fixes issue #997. Signed-off-by: Damien George --- python-stdlib/inspect/inspect.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/python-stdlib/inspect/inspect.py b/python-stdlib/inspect/inspect.py index 06aba8762..fb2ad885d 100644 --- a/python-stdlib/inspect/inspect.py +++ b/python-stdlib/inspect/inspect.py @@ -1,5 +1,7 @@ import sys +_g = lambda: (yield) + def getmembers(obj, pred=None): res = [] @@ -16,11 +18,11 @@ def isfunction(obj): def isgeneratorfunction(obj): - return isinstance(obj, type(lambda: (yield))) + return isinstance(obj, type(_g)) def isgenerator(obj): - return isinstance(obj, type(lambda: (yield)())) + return isinstance(obj, type((_g)())) class _Class: From 2665047fa7c88729e8d581bcf4ed047298c0dc30 Mon Sep 17 00:00:00 2001 From: Damien George Date: Fri, 11 Apr 2025 12:24:53 +1000 Subject: [PATCH 16/24] inspect: Add basic unit tests. Signed-off-by: Damien George --- python-stdlib/inspect/test_inspect.py | 54 +++++++++++++++++++++++++++ tools/ci.sh | 1 + 2 files changed, 55 insertions(+) create mode 100644 python-stdlib/inspect/test_inspect.py diff --git a/python-stdlib/inspect/test_inspect.py b/python-stdlib/inspect/test_inspect.py new file mode 100644 index 000000000..6f262ca64 --- /dev/null +++ b/python-stdlib/inspect/test_inspect.py @@ -0,0 +1,54 @@ +import inspect +import unittest + + +def fun(): + return 1 + + +def gen(): + yield 1 + + +class Class: + def meth(self): + pass + + +entities = ( + fun, + gen, + gen(), + Class, + Class.meth, + Class().meth, + inspect, +) + + +class TestInspect(unittest.TestCase): + def _test_is_helper(self, f, *entities_true): + for entity in entities: + result = f(entity) + if entity in entities_true: + self.assertTrue(result) + else: + self.assertFalse(result) + + def test_isfunction(self): + self._test_is_helper(inspect.isfunction, entities[0], entities[4]) + + def test_isgeneratorfunction(self): + self._test_is_helper(inspect.isgeneratorfunction, entities[1]) + + def test_isgenerator(self): + self._test_is_helper(inspect.isgenerator, entities[2]) + + def test_ismethod(self): + self._test_is_helper(inspect.ismethod, entities[5]) + + def test_isclass(self): + self._test_is_helper(inspect.isclass, entities[3]) + + def test_ismodule(self): + self._test_is_helper(inspect.ismodule, entities[6]) diff --git a/tools/ci.sh b/tools/ci.sh index a5fcdf22e..6689e8aa4 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -86,6 +86,7 @@ function ci_package_tests_run { python-stdlib/datetime \ python-stdlib/fnmatch \ python-stdlib/hashlib \ + python-stdlib/inspect \ python-stdlib/pathlib \ python-stdlib/quopri \ python-stdlib/shutil \ From 5b496e944ec045177afa1620920a168410b7f60b Mon Sep 17 00:00:00 2001 From: Damien George Date: Mon, 14 Apr 2025 10:24:54 +1000 Subject: [PATCH 17/24] inspect: Implement iscoroutinefunction and iscoroutine. Signed-off-by: Damien George --- python-stdlib/inspect/inspect.py | 5 +++++ python-stdlib/inspect/manifest.py | 2 +- python-stdlib/inspect/test_inspect.py | 6 ++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/python-stdlib/inspect/inspect.py b/python-stdlib/inspect/inspect.py index fb2ad885d..c16c6b3e3 100644 --- a/python-stdlib/inspect/inspect.py +++ b/python-stdlib/inspect/inspect.py @@ -25,6 +25,11 @@ def isgenerator(obj): return isinstance(obj, type((_g)())) +# In MicroPython there's currently no way to distinguish between generators and coroutines. +iscoroutinefunction = isgeneratorfunction +iscoroutine = isgenerator + + class _Class: def meth(): pass diff --git a/python-stdlib/inspect/manifest.py b/python-stdlib/inspect/manifest.py index a9d5a2381..e99e659f2 100644 --- a/python-stdlib/inspect/manifest.py +++ b/python-stdlib/inspect/manifest.py @@ -1,3 +1,3 @@ -metadata(version="0.1.2") +metadata(version="0.1.3") module("inspect.py") diff --git a/python-stdlib/inspect/test_inspect.py b/python-stdlib/inspect/test_inspect.py index 6f262ca64..29ed80f11 100644 --- a/python-stdlib/inspect/test_inspect.py +++ b/python-stdlib/inspect/test_inspect.py @@ -44,6 +44,12 @@ def test_isgeneratorfunction(self): def test_isgenerator(self): self._test_is_helper(inspect.isgenerator, entities[2]) + def test_iscoroutinefunction(self): + self._test_is_helper(inspect.iscoroutinefunction, entities[1]) + + def test_iscoroutine(self): + self._test_is_helper(inspect.iscoroutine, entities[2]) + def test_ismethod(self): self._test_is_helper(inspect.ismethod, entities[5]) From f8c8875e250e1dfef0158f15ccbb66e8cee9aa57 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 24 Apr 2025 10:34:45 +1000 Subject: [PATCH 18/24] lora: Fix SNR value in SX126x received packets. Wasn't being treated as a signed value. Fixes issue #999. Signed-off-by: Angus Gratton --- micropython/lora/lora-sx126x/lora/sx126x.py | 5 +++-- micropython/lora/lora-sx126x/manifest.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/micropython/lora/lora-sx126x/lora/sx126x.py b/micropython/lora/lora-sx126x/lora/sx126x.py index 641367a9f..7fa4896ae 100644 --- a/micropython/lora/lora-sx126x/lora/sx126x.py +++ b/micropython/lora/lora-sx126x/lora/sx126x.py @@ -596,8 +596,9 @@ def _read_packet(self, rx_packet, flags): pkt_status = self._cmd("B", _CMD_GET_PACKET_STATUS, n_read=4) rx_packet.ticks_ms = ticks_ms - rx_packet.snr = pkt_status[2] # SNR, units: dB *4 - rx_packet.rssi = 0 - pkt_status[1] // 2 # RSSI, units: dBm + # SNR units are dB * 4 (signed) + rx_packet.rssi, rx_packet.snr = struct.unpack("xBbx", pkt_status) + rx_packet.rssi //= -2 # RSSI, units: dBm rx_packet.crc_error = (flags & _IRQ_CRC_ERR) != 0 return rx_packet diff --git a/micropython/lora/lora-sx126x/manifest.py b/micropython/lora/lora-sx126x/manifest.py index 038710820..76fa91d8d 100644 --- a/micropython/lora/lora-sx126x/manifest.py +++ b/micropython/lora/lora-sx126x/manifest.py @@ -1,3 +1,3 @@ -metadata(version="0.1.4") +metadata(version="0.1.5") require("lora") package("lora") From d887a021e831ee3b7e6f00f6a4c32b36ee6c4769 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 24 Apr 2025 10:41:10 +1000 Subject: [PATCH 19/24] top: Bump the Ruff version to 0.11.6. With small code fixes to match. Signed-off-by: Angus Gratton --- .github/workflows/ruff.yml | 3 ++- .pre-commit-config.yaml | 3 ++- .../bluetooth/aioble/examples/l2cap_file_client.py | 2 +- .../bluetooth/aioble/examples/l2cap_file_server.py | 2 +- micropython/drivers/codec/wm8960/wm8960.py | 12 ++++-------- micropython/drivers/display/lcd160cr/lcd160cr.py | 6 ++---- pyproject.toml | 3 ++- 7 files changed, 14 insertions(+), 17 deletions(-) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 71c4131f0..b347e34ee 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -6,6 +6,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: pip install --user ruff==0.1.2 + # Version should be kept in sync with .pre-commit_config.yaml & also micropython + - run: pip install --user ruff==0.11.6 - run: ruff check --output-format=github . - run: ruff format --diff . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 335c1c2fc..05f5d3df0 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,7 +8,8 @@ repos: verbose: true stages: [commit-msg] - repo: https://github.com/charliermarsh/ruff-pre-commit - rev: v0.1.2 + # Version should be kept in sync with .github/workflows/ruff.yml & also micropython + rev: v0.11.6 hooks: - id: ruff id: ruff-format diff --git a/micropython/bluetooth/aioble/examples/l2cap_file_client.py b/micropython/bluetooth/aioble/examples/l2cap_file_client.py index 9dce349a7..0817ca162 100644 --- a/micropython/bluetooth/aioble/examples/l2cap_file_client.py +++ b/micropython/bluetooth/aioble/examples/l2cap_file_client.py @@ -88,7 +88,7 @@ async def download(self, path, dest): await self._command(_COMMAND_SEND, path.encode()) - with open(dest, "wb") as f: # noqa: ASYNC101 + with open(dest, "wb") as f: # noqa: ASYNC230 total = 0 buf = bytearray(self._channel.our_mtu) mv = memoryview(buf) diff --git a/micropython/bluetooth/aioble/examples/l2cap_file_server.py b/micropython/bluetooth/aioble/examples/l2cap_file_server.py index fb806effc..3c3b3b44d 100644 --- a/micropython/bluetooth/aioble/examples/l2cap_file_server.py +++ b/micropython/bluetooth/aioble/examples/l2cap_file_server.py @@ -83,7 +83,7 @@ async def l2cap_task(connection): if send_file: print("Sending:", send_file) - with open(send_file, "rb") as f: # noqa: ASYNC101 + with open(send_file, "rb") as f: # noqa: ASYNC230 buf = bytearray(channel.peer_mtu) mv = memoryview(buf) while n := f.readinto(buf): diff --git a/micropython/drivers/codec/wm8960/wm8960.py b/micropython/drivers/codec/wm8960/wm8960.py index dc0dd655d..313649f36 100644 --- a/micropython/drivers/codec/wm8960/wm8960.py +++ b/micropython/drivers/codec/wm8960/wm8960.py @@ -331,8 +331,7 @@ def __init__( sysclk = 11289600 else: sysclk = 12288000 - if sysclk < sample_rate * 256: - sysclk = sample_rate * 256 + sysclk = max(sysclk, sample_rate * 256) if mclk_freq is None: mclk_freq = sysclk else: # sysclk_source == SYSCLK_MCLK @@ -691,10 +690,8 @@ def alc_mode(self, channel, mode=ALC_MODE): def alc_gain(self, target=-12, max_gain=30, min_gain=-17.25, noise_gate=-78): def limit(value, minval, maxval): value = int(value) - if value < minval: - value = minval - if value > maxval: - value = maxval + value = max(value, minval) + value = min(value, maxval) return value target = limit((16 + (target * 2) // 3), 0, 15) @@ -718,8 +715,7 @@ def logb(value, limit): while value > 1: value >>= 1 lb += 1 - if lb > limit: - lb = limit + lb = min(lb, limit) return lb attack = logb(attack / 6, 7) diff --git a/micropython/drivers/display/lcd160cr/lcd160cr.py b/micropython/drivers/display/lcd160cr/lcd160cr.py index 42b5e215b..177c6fea3 100644 --- a/micropython/drivers/display/lcd160cr/lcd160cr.py +++ b/micropython/drivers/display/lcd160cr/lcd160cr.py @@ -189,10 +189,8 @@ def clip_line(c, w, h): c[3] = h - 1 else: if c[0] == c[2]: - if c[1] < 0: - c[1] = 0 - if c[3] < 0: - c[3] = 0 + c[1] = max(c[1], 0) + c[3] = max(c[3], 0) else: if c[3] < c[1]: c[0], c[2] = c[2], c[0] diff --git a/pyproject.toml b/pyproject.toml index 4776ddfe9..83d29405d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -64,7 +64,7 @@ ignore = [ "ISC003", # micropython does not support implicit concatenation of f-strings "PIE810", # micropython does not support passing tuples to .startswith or .endswith "PLC1901", - "PLR1701", + "PLR1704", # sometimes desirable to redefine an argument to save code size "PLR1714", "PLR5501", "PLW0602", @@ -72,6 +72,7 @@ ignore = [ "PLW2901", "RUF012", "RUF100", + "SIM101", "W191", # tab-indent, redundant when using formatter ] line-length = 99 From 68e0dfce0a8708e7d9aef4446fad842cc5929410 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 24 Apr 2025 11:01:42 +1000 Subject: [PATCH 20/24] all: Apply Ruff 0.11.6 reformatting changes. Signed-off-by: Angus Gratton --- micropython/aiorepl/aiorepl.py | 16 ++++++++-------- micropython/drivers/imu/lsm9ds1/lsm9ds1.py | 1 + micropython/drivers/radio/nrf24l01/nrf24l01.py | 3 +-- micropython/drivers/sensor/hts221/hts221.py | 2 +- micropython/drivers/sensor/lps22h/lps22h.py | 1 + micropython/espflash/espflash.py | 16 ++++++++-------- .../lora/examples/reliable_delivery/sender.py | 2 +- .../examples/reliable_delivery/sender_async.py | 2 +- micropython/senml/examples/actuator.py | 1 - micropython/senml/examples/base.py | 1 - micropython/senml/examples/basic.py | 1 - micropython/senml/examples/basic2.py | 1 - micropython/senml/examples/basic_cbor.py | 1 - micropython/senml/examples/custom_record.py | 1 - micropython/senml/examples/gateway.py | 1 - micropython/senml/examples/gateway_actuators.py | 1 - .../senml/examples/supported_data_types.py | 1 - micropython/senml/senml/__init__.py | 1 - micropython/senml/senml/senml_pack.py | 1 - micropython/senml/senml/senml_record.py | 1 - python-ecosys/cbor2/cbor2/__init__.py | 1 - python-ecosys/cbor2/cbor2/_decoder.py | 1 - python-ecosys/cbor2/cbor2/_encoder.py | 1 - python-ecosys/cbor2/examples/cbor_test.py | 1 - 24 files changed, 22 insertions(+), 37 deletions(-) diff --git a/micropython/aiorepl/aiorepl.py b/micropython/aiorepl/aiorepl.py index 8f45dfac0..3f437459d 100644 --- a/micropython/aiorepl/aiorepl.py +++ b/micropython/aiorepl/aiorepl.py @@ -132,7 +132,7 @@ async def task(g=None, prompt="--> "): continue if curs: # move cursor to end of the line - sys.stdout.write("\x1B[{}C".format(curs)) + sys.stdout.write("\x1b[{}C".format(curs)) curs = 0 sys.stdout.write("\n") if cmd: @@ -153,10 +153,10 @@ async def task(g=None, prompt="--> "): if curs: cmd = "".join((cmd[: -curs - 1], cmd[-curs:])) sys.stdout.write( - "\x08\x1B[K" + "\x08\x1b[K" ) # move cursor back, erase to end of line sys.stdout.write(cmd[-curs:]) # redraw line - sys.stdout.write("\x1B[{}D".format(curs)) # reset cursor location + sys.stdout.write("\x1b[{}D".format(curs)) # reset cursor location else: cmd = cmd[:-1] sys.stdout.write("\x08 \x08") @@ -207,21 +207,21 @@ async def task(g=None, prompt="--> "): elif key == "[D": # left if curs < len(cmd) - 1: curs += 1 - sys.stdout.write("\x1B") + sys.stdout.write("\x1b") sys.stdout.write(key) elif key == "[C": # right if curs: curs -= 1 - sys.stdout.write("\x1B") + sys.stdout.write("\x1b") sys.stdout.write(key) elif key == "[H": # home pcurs = curs curs = len(cmd) - sys.stdout.write("\x1B[{}D".format(curs - pcurs)) # move cursor left + sys.stdout.write("\x1b[{}D".format(curs - pcurs)) # move cursor left elif key == "[F": # end pcurs = curs curs = 0 - sys.stdout.write("\x1B[{}C".format(pcurs)) # move cursor right + sys.stdout.write("\x1b[{}C".format(pcurs)) # move cursor right else: # sys.stdout.write("\\x") # sys.stdout.write(hex(c)) @@ -231,7 +231,7 @@ async def task(g=None, prompt="--> "): # inserting into middle of line cmd = "".join((cmd[:-curs], b, cmd[-curs:])) sys.stdout.write(cmd[-curs - 1 :]) # redraw line to end - sys.stdout.write("\x1B[{}D".format(curs)) # reset cursor location + sys.stdout.write("\x1b[{}D".format(curs)) # reset cursor location else: sys.stdout.write(b) cmd += b diff --git a/micropython/drivers/imu/lsm9ds1/lsm9ds1.py b/micropython/drivers/imu/lsm9ds1/lsm9ds1.py index e3d46429d..e5a96ad5c 100644 --- a/micropython/drivers/imu/lsm9ds1/lsm9ds1.py +++ b/micropython/drivers/imu/lsm9ds1/lsm9ds1.py @@ -43,6 +43,7 @@ print("") time.sleep_ms(100) """ + import array from micropython import const diff --git a/micropython/drivers/radio/nrf24l01/nrf24l01.py b/micropython/drivers/radio/nrf24l01/nrf24l01.py index d015250cf..9fbadcd60 100644 --- a/micropython/drivers/radio/nrf24l01/nrf24l01.py +++ b/micropython/drivers/radio/nrf24l01/nrf24l01.py @@ -1,5 +1,4 @@ -"""NRF24L01 driver for MicroPython -""" +"""NRF24L01 driver for MicroPython""" from micropython import const import utime diff --git a/micropython/drivers/sensor/hts221/hts221.py b/micropython/drivers/sensor/hts221/hts221.py index fec52a738..c6cd51f48 100644 --- a/micropython/drivers/sensor/hts221/hts221.py +++ b/micropython/drivers/sensor/hts221/hts221.py @@ -52,7 +52,7 @@ def __init__(self, i2c, data_rate=1, address=0x5F): # Set configuration register # Humidity and temperature average configuration - self.bus.writeto_mem(self.slv_addr, 0x10, b"\x1B") + self.bus.writeto_mem(self.slv_addr, 0x10, b"\x1b") # Set control register # PD | BDU | ODR diff --git a/micropython/drivers/sensor/lps22h/lps22h.py b/micropython/drivers/sensor/lps22h/lps22h.py index 1e7f4ec3e..7dec72528 100644 --- a/micropython/drivers/sensor/lps22h/lps22h.py +++ b/micropython/drivers/sensor/lps22h/lps22h.py @@ -37,6 +37,7 @@ print("Pressure: %.2f hPa Temperature: %.2f C"%(lps.pressure(), lps.temperature())) time.sleep_ms(10) """ + import machine from micropython import const diff --git a/micropython/espflash/espflash.py b/micropython/espflash/espflash.py index 74988777a..fbf4e1f7e 100644 --- a/micropython/espflash/espflash.py +++ b/micropython/espflash/espflash.py @@ -113,22 +113,22 @@ def _poll_reg(self, addr, flag, retry=10, delay=0.050): raise Exception(f"Register poll timeout. Addr: 0x{addr:02X} Flag: 0x{flag:02X}.") def _write_slip(self, pkt): - pkt = pkt.replace(b"\xDB", b"\xdb\xdd").replace(b"\xc0", b"\xdb\xdc") - self.uart.write(b"\xC0" + pkt + b"\xC0") + pkt = pkt.replace(b"\xdb", b"\xdb\xdd").replace(b"\xc0", b"\xdb\xdc") + self.uart.write(b"\xc0" + pkt + b"\xc0") self._log(pkt) def _read_slip(self): pkt = None # Find the packet start. - if self.uart.read(1) == b"\xC0": + if self.uart.read(1) == b"\xc0": pkt = bytearray() while True: b = self.uart.read(1) - if b is None or b == b"\xC0": + if b is None or b == b"\xc0": break pkt += b - pkt = pkt.replace(b"\xDB\xDD", b"\xDB").replace(b"\xDB\xDC", b"\xC0") - self._log(b"\xC0" + pkt + b"\xC0", False) + pkt = pkt.replace(b"\xdb\xdd", b"\xdb").replace(b"\xdb\xdc", b"\xc0") + self._log(b"\xc0" + pkt + b"\xc0", False) return pkt def _strerror(self, err): @@ -230,7 +230,7 @@ def flash_read_size(self): raise Exception(f"Unexpected flash size bits: 0x{flash_bits:02X}.") flash_size = 2**flash_bits - print(f"Flash size {flash_size/1024/1024} MBytes") + print(f"Flash size {flash_size / 1024 / 1024} MBytes") return flash_size def flash_attach(self): @@ -265,7 +265,7 @@ def flash_write_file(self, path, blksize=0x1000): self.md5sum.update(buf) # The last data block should be padded to the block size with 0xFF bytes. if len(buf) < blksize: - buf += b"\xFF" * (blksize - len(buf)) + buf += b"\xff" * (blksize - len(buf)) checksum = self._checksum(buf) if seq % erase_blocks == 0: # print(f"Erasing {seq} -> {seq+erase_blocks}...") diff --git a/micropython/lora/examples/reliable_delivery/sender.py b/micropython/lora/examples/reliable_delivery/sender.py index 2fba0d4d7..957e9d824 100644 --- a/micropython/lora/examples/reliable_delivery/sender.py +++ b/micropython/lora/examples/reliable_delivery/sender.py @@ -149,7 +149,7 @@ def send(self, sensor_data, adjust_output_power=True): delta = time.ticks_diff(maybe_ack.ticks_ms, sent_at) print( f"ACKed with RSSI {rssi}, {delta}ms after sent " - + f"(skew {delta-ACK_DELAY_MS-ack_packet_ms}ms)" + + f"(skew {delta - ACK_DELAY_MS - ack_packet_ms}ms)" ) if adjust_output_power: diff --git a/micropython/lora/examples/reliable_delivery/sender_async.py b/micropython/lora/examples/reliable_delivery/sender_async.py index a27420f6b..4c14d6f11 100644 --- a/micropython/lora/examples/reliable_delivery/sender_async.py +++ b/micropython/lora/examples/reliable_delivery/sender_async.py @@ -141,7 +141,7 @@ async def send(self, sensor_data, adjust_output_power=True): delta = time.ticks_diff(maybe_ack.ticks_ms, sent_at) print( f"ACKed with RSSI {rssi}, {delta}ms after sent " - + f"(skew {delta-ACK_DELAY_MS-ack_packet_ms}ms)" + + f"(skew {delta - ACK_DELAY_MS - ack_packet_ms}ms)" ) if adjust_output_power: diff --git a/micropython/senml/examples/actuator.py b/micropython/senml/examples/actuator.py index 8e254349d..2fac474cd 100644 --- a/micropython/senml/examples/actuator.py +++ b/micropython/senml/examples/actuator.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * diff --git a/micropython/senml/examples/base.py b/micropython/senml/examples/base.py index 426cbbd0e..6a49cfdd2 100644 --- a/micropython/senml/examples/base.py +++ b/micropython/senml/examples/base.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * import time diff --git a/micropython/senml/examples/basic.py b/micropython/senml/examples/basic.py index 18a3a9a06..3f3ed6150 100644 --- a/micropython/senml/examples/basic.py +++ b/micropython/senml/examples/basic.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * import time diff --git a/micropython/senml/examples/basic2.py b/micropython/senml/examples/basic2.py index c2ea153bd..ca53b4a6e 100644 --- a/micropython/senml/examples/basic2.py +++ b/micropython/senml/examples/basic2.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * import time diff --git a/micropython/senml/examples/basic_cbor.py b/micropython/senml/examples/basic_cbor.py index 804a886fc..b9d9d620b 100644 --- a/micropython/senml/examples/basic_cbor.py +++ b/micropython/senml/examples/basic_cbor.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * import time import cbor2 diff --git a/micropython/senml/examples/custom_record.py b/micropython/senml/examples/custom_record.py index e68d05f5b..1e83ea06b 100644 --- a/micropython/senml/examples/custom_record.py +++ b/micropython/senml/examples/custom_record.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * import time diff --git a/micropython/senml/examples/gateway.py b/micropython/senml/examples/gateway.py index d28e4cffc..e1827ff2d 100644 --- a/micropython/senml/examples/gateway.py +++ b/micropython/senml/examples/gateway.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * import time diff --git a/micropython/senml/examples/gateway_actuators.py b/micropython/senml/examples/gateway_actuators.py index add5ed24c..a7e5b378c 100644 --- a/micropython/senml/examples/gateway_actuators.py +++ b/micropython/senml/examples/gateway_actuators.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * diff --git a/micropython/senml/examples/supported_data_types.py b/micropython/senml/examples/supported_data_types.py index 3149f49d2..94976bb66 100644 --- a/micropython/senml/examples/supported_data_types.py +++ b/micropython/senml/examples/supported_data_types.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml import * import time diff --git a/micropython/senml/senml/__init__.py b/micropython/senml/senml/__init__.py index 93cbd7700..908375fdb 100644 --- a/micropython/senml/senml/__init__.py +++ b/micropython/senml/senml/__init__.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from .senml_base import SenmlBase from .senml_pack import SenmlPack from .senml_record import SenmlRecord diff --git a/micropython/senml/senml/senml_pack.py b/micropython/senml/senml/senml_pack.py index 4e106fd3e..5a0554467 100644 --- a/micropython/senml/senml/senml_pack.py +++ b/micropython/senml/senml/senml_pack.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from senml.senml_record import SenmlRecord from senml.senml_base import SenmlBase import json diff --git a/micropython/senml/senml/senml_record.py b/micropython/senml/senml/senml_record.py index 9dfe22873..ae40f0f70 100644 --- a/micropython/senml/senml/senml_record.py +++ b/micropython/senml/senml/senml_record.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - import binascii from senml.senml_base import SenmlBase diff --git a/python-ecosys/cbor2/cbor2/__init__.py b/python-ecosys/cbor2/cbor2/__init__.py index 7cd98734e..80790f0da 100644 --- a/python-ecosys/cbor2/cbor2/__init__.py +++ b/python-ecosys/cbor2/cbor2/__init__.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - from ._decoder import CBORDecoder from ._decoder import load from ._decoder import loads diff --git a/python-ecosys/cbor2/cbor2/_decoder.py b/python-ecosys/cbor2/cbor2/_decoder.py index 5d509a535..965dbfd46 100644 --- a/python-ecosys/cbor2/cbor2/_decoder.py +++ b/python-ecosys/cbor2/cbor2/_decoder.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - import io import struct diff --git a/python-ecosys/cbor2/cbor2/_encoder.py b/python-ecosys/cbor2/cbor2/_encoder.py index 80a4ac022..fe8715468 100644 --- a/python-ecosys/cbor2/cbor2/_encoder.py +++ b/python-ecosys/cbor2/cbor2/_encoder.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - import io import struct diff --git a/python-ecosys/cbor2/examples/cbor_test.py b/python-ecosys/cbor2/examples/cbor_test.py index b4f351786..a1cd7e93e 100644 --- a/python-ecosys/cbor2/examples/cbor_test.py +++ b/python-ecosys/cbor2/examples/cbor_test.py @@ -23,7 +23,6 @@ THE SOFTWARE. """ - import cbor2 input = [ From 0d60de65b7cf6bf784e2e4e9f84f1a70faf2ae83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Thu, 7 Mar 2024 15:01:10 +0100 Subject: [PATCH 21/24] utop: Add initial implementation for ESP32. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniël van de Giessen --- micropython/utop/README.md | 12 +++++ micropython/utop/manifest.py | 6 +++ micropython/utop/utop.py | 85 ++++++++++++++++++++++++++++++++++++ 3 files changed, 103 insertions(+) create mode 100644 micropython/utop/README.md create mode 100644 micropython/utop/manifest.py create mode 100644 micropython/utop/utop.py diff --git a/micropython/utop/README.md b/micropython/utop/README.md new file mode 100644 index 000000000..7b07e785d --- /dev/null +++ b/micropython/utop/README.md @@ -0,0 +1,12 @@ +# utop + +Provides a top-like live overview of the running system. + +On the `esp32` port this depends on the `esp32.idf_task_info()` function, which +can be enabled by adding the following lines to the board `sdkconfig`: + +```ini +CONFIG_FREERTOS_USE_TRACE_FACILITY=y +CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID=y +CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y +``` diff --git a/micropython/utop/manifest.py b/micropython/utop/manifest.py new file mode 100644 index 000000000..ebba07270 --- /dev/null +++ b/micropython/utop/manifest.py @@ -0,0 +1,6 @@ +metadata( + version="0.1.0", + description="Provides a top-like live overview of the running system.", +) + +module("utop.py") diff --git a/micropython/utop/utop.py b/micropython/utop/utop.py new file mode 100644 index 000000000..d4e94c94a --- /dev/null +++ b/micropython/utop/utop.py @@ -0,0 +1,85 @@ +import time + +try: + import esp32 + import _thread +except ImportError: + esp32 = None + + +def top(update_interval_ms=1000, timeout_ms=None, thread_names={}): + time_start = time.ticks_ms() + previous_total_runtime = None + previous_task_runtimes = {} + previous_line_count = 0 + esp32_task_state_names = dict( + enumerate(("running", "ready", "blocked", "suspended", "deleted", "invalid")) + ) + + while timeout_ms is None or abs(time.ticks_diff(time.ticks_ms(), time_start)) < timeout_ms: + if previous_line_count > 0: + print("\x1b[{}A".format(previous_line_count), end="") + line_count = 0 + + if esp32 is not None: + if not hasattr(esp32, "idf_task_info"): + print( + "INFO: esp32.idf_task_info() is not available, cannot list active tasks.\x1b[K" + ) + line_count += 1 + else: + print(" CPU% CORE PRIORITY STATE STACKWATERMARK NAME\x1b[K") + line_count += 1 + + total_runtime, tasks = esp32.idf_task_info() + current_thread_id = _thread.get_ident() + tasks.sort(key=lambda t: t[0]) + for ( + task_id, + task_name, + task_state, + task_priority, + task_runtime, + task_stackhighwatermark, + task_coreid, + ) in tasks: + task_runtime_percentage = "-" + if ( + total_runtime != previous_total_runtime + and task_id in previous_task_runtimes + ): + task_runtime_percentage = "{:.2f}%".format( + 100 + * ((task_runtime - previous_task_runtimes[task_id]) & (2**32 - 1)) + / ((total_runtime - previous_total_runtime) & (2**32 - 1)) + ) + print( + "{:>7} {:>4} {:>8d} {:<9} {:<14d} {}{}\x1b[K".format( + task_runtime_percentage, + "-" if task_coreid is None else task_coreid, + task_priority, + esp32_task_state_names.get(task_state, "unknown"), + task_stackhighwatermark, + thread_names.get(task_id, task_name), + " (*)" if task_id == current_thread_id else "", + ) + ) + line_count += 1 + + previous_task_runtimes[task_id] = task_runtime + previous_total_runtime = total_runtime + else: + print("INFO: Platform does not support listing active tasks.\x1b[K") + line_count += 1 + + if previous_line_count > line_count: + for _ in range(previous_line_count - line_count): + print("\x1b[K") + print("\x1b[{}A".format(previous_line_count - line_count), end="") + + previous_line_count = line_count + + try: + time.sleep_ms(update_interval_ms) + except KeyboardInterrupt: + break From 08e09afbe3d16ea97b78ba486153a7ea4a750232 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Mon, 12 May 2025 16:24:19 +0200 Subject: [PATCH 22/24] utop: Print MicroPython memory info. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniël van de Giessen --- micropython/utop/utop.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/micropython/utop/utop.py b/micropython/utop/utop.py index d4e94c94a..76fdaade0 100644 --- a/micropython/utop/utop.py +++ b/micropython/utop/utop.py @@ -1,3 +1,4 @@ +import micropython import time try: @@ -72,6 +73,12 @@ def top(update_interval_ms=1000, timeout_ms=None, thread_names={}): print("INFO: Platform does not support listing active tasks.\x1b[K") line_count += 1 + print("\x1b[K") + line_count += 1 + print("MicroPython ", end="") + micropython.mem_info() + line_count += 3 + if previous_line_count > line_count: for _ in range(previous_line_count - line_count): print("\x1b[K") From 54d5f7cee2b95c976589bd4c815c24c358557e3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Thu, 7 Mar 2024 15:01:45 +0100 Subject: [PATCH 23/24] utop: Print IDF heap details. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Daniël van de Giessen --- micropython/utop/utop.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/micropython/utop/utop.py b/micropython/utop/utop.py index 76fdaade0..799ff4212 100644 --- a/micropython/utop/utop.py +++ b/micropython/utop/utop.py @@ -79,6 +79,23 @@ def top(update_interval_ms=1000, timeout_ms=None, thread_names={}): micropython.mem_info() line_count += 3 + if esp32 is not None: + print("\x1b[K") + line_count += 1 + for name, cap in (("data", esp32.HEAP_DATA), ("exec", esp32.HEAP_EXEC)): + heaps = esp32.idf_heap_info(cap) + print( + "IDF heap ({}): {} regions, {} total, {} free, {} largest contiguous, {} min free watermark\x1b[K".format( + name, + len(heaps), + sum((h[0] for h in heaps)), + sum((h[1] for h in heaps)), + max((h[2] for h in heaps)), + sum((h[3] for h in heaps)), + ) + ) + line_count += 1 + if previous_line_count > line_count: for _ in range(previous_line_count - line_count): print("\x1b[K") From 567540d4e0be3fd4500cc199416bfa602bf7bcec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dani=C3=ABl=20van=20de=20Giessen?= Date: Wed, 14 May 2025 18:08:22 +0200 Subject: [PATCH 24/24] tools/verifygitlog.py: Sync with changes from the main repo. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This includes the following commits: - Allow long co-author and sign-off names. - Disallow a leading slash in commit subject line. - Apply stricter rules on git subject line. - Show invalid commit subjects in quotes. Signed-off-by: Daniël van de Giessen --- tools/verifygitlog.py | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/tools/verifygitlog.py b/tools/verifygitlog.py index 20be794f8..46fec1e0c 100755 --- a/tools/verifygitlog.py +++ b/tools/verifygitlog.py @@ -49,7 +49,7 @@ def git_log(pretty_format, *args): def diagnose_subject_line(subject_line, subject_line_format, err): - err.error("Subject line: " + subject_line) + err.error('Subject line: "' + subject_line + '"') if not subject_line.endswith("."): err.error('* must end with "."') if not re.match(r"^[^!]+: ", subject_line): @@ -98,20 +98,47 @@ def verify_message_body(raw_body, err): if len(subject_line) >= 73: err.error("Subject line must be 72 or fewer characters: " + subject_line) + # Do additional checks on the prefix of the subject line. + verify_subject_line_prefix(subject_line.split(": ")[0], err) + # Second one divides subject and body. if len(raw_body) > 1 and raw_body[1]: err.error("Second message line must be empty: " + raw_body[1]) # Message body lines. for line in raw_body[2:]: - # Long lines with URLs are exempt from the line length rule. - if len(line) >= 76 and "://" not in line: + # Long lines with URLs or human names are exempt from the line length rule. + if len(line) >= 76 and not ( + "://" in line + or line.startswith("Co-authored-by: ") + or line.startswith("Signed-off-by: ") + ): err.error("Message lines should be 75 or less characters: " + line) if not raw_body[-1].startswith("Signed-off-by: ") or "@" not in raw_body[-1]: err.error('Message must be signed-off. Use "git commit -s".') +def verify_subject_line_prefix(prefix, err): + ext = (".c", ".h", ".cpp", ".js", ".rst", ".md") + + if prefix.startswith((".", "/")): + err.error('Subject prefix cannot begin with "." or "/".') + + if prefix.endswith("/"): + err.error('Subject prefix cannot end with "/".') + + if prefix.startswith("ports/"): + err.error( + 'Subject prefix cannot begin with "ports/", start with the name of the port instead.' + ) + + if prefix.endswith(ext): + err.error( + "Subject prefix cannot end with a file extension, use the main part of the filename without the extension." + ) + + def run(args): verbose("run", *args)