From 01fabfef7896024b4c8ecbfe654b5930e236bf46 Mon Sep 17 00:00:00 2001 From: pstruebi Date: Mon, 29 Sep 2025 15:02:32 +0200 Subject: [PATCH] feat: add nRF54L reset via OpenOCD before initializing broadcast --- poetry.lock | 8 ++-- pyproject.toml | 2 +- src/auracast/server/multicast_server.py | 5 ++ src/auracast/utils/reset_utils.py | 64 +++++++++++++++++++++++++ src/openocd/raspberrypi-swd0.cfg | 8 ++++ src/openocd/raspberrypi-swd1.cfg | 8 ++++ 6 files changed, 90 insertions(+), 5 deletions(-) create mode 100644 src/auracast/utils/reset_utils.py create mode 100644 src/openocd/raspberrypi-swd0.cfg create mode 100644 src/openocd/raspberrypi-swd1.cfg diff --git a/poetry.lock b/poetry.lock index 70213c8..dba8f46 100644 --- a/poetry.lock +++ b/poetry.lock @@ -332,7 +332,7 @@ files = [ [[package]] name = "bumble" -version = "0.0.216.dev1+g6eba81e3d" +version = "0.0.218.dev6+g32d448edf" description = "Bluetooth Stack for Apps, Emulation, Test and Experimentation" optional = false python-versions = ">=3.9" @@ -371,8 +371,8 @@ test = ["coverage (>=6.4)", "pytest (>=8.2)", "pytest-asyncio (>=0.23.5)", "pyte [package.source] type = "git" url = "ssh://git@ssh.pstruebi.xyz:222/auracaster/bumble_mirror.git" -reference = "6eba81e3ddb8ac0e4c336ca244892a0a8d43ba1c" -resolved_reference = "6eba81e3ddb8ac0e4c336ca244892a0a8d43ba1c" +reference = "32d448edf3276f6b9056765a12879054d8a01fd8" +resolved_reference = "32d448edf3276f6b9056765a12879054d8a01fd8" [[package]] name = "cachetools" @@ -2952,4 +2952,4 @@ test = ["pytest", "pytest-asyncio"] [metadata] lock-version = "2.1" python-versions = ">=3.11" -content-hash = "3afe565be2664b3d7f1cfdb1c5a73d931c14e97d3622aef24ba2f06f78e00e2b" +content-hash = "6b5300c349ed045e8fd3e617e6262bbd7e5c48c518e4c62cedf7c17da50ce8c0" diff --git a/pyproject.toml b/pyproject.toml index dd44d25..799f3ce 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ version = "0.0.1" requires-python = ">=3.11" dependencies = [ - "bumble @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/bumble_mirror.git@6eba81e3ddb8ac0e4c336ca244892a0a8d43ba1c", + "bumble @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/bumble_mirror.git@32d448edf3276f6b9056765a12879054d8a01fd8", "lc3py @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/liblc3.git@ce2e41faf8c06d038df9f32504c61109a14130be", "aioconsole", "fastapi==0.115.11", diff --git a/src/auracast/server/multicast_server.py b/src/auracast/server/multicast_server.py index b777d83..6d0241b 100644 --- a/src/auracast/server/multicast_server.py +++ b/src/auracast/server/multicast_server.py @@ -23,6 +23,7 @@ from auracast.utils.sounddevice_utils import ( list_usb_pw_inputs, list_network_pw_inputs, ) +from auracast.utils.reset_utils import reset_nrf54l load_dotenv() # make sure pipewire sets latency @@ -157,6 +158,8 @@ async def initialize(conf: auracast_config.AuracastConfigGroup): log.warning("Failed to shutdown previous multicaster", exc_info=True) log.info('Initializing multicaster1 with config:\n %s', conf.model_dump_json(indent=2)) multicaster1 = multicast_control.Multicaster(conf, conf.bigs) + # Ensure target is reset before initializing broadcast + await reset_nrf54l(0) await multicaster1.init_broadcast() if any(big.audio_source.startswith("device:") or big.audio_source.startswith("file:") for big in conf.bigs): log.info("Auto-starting streaming on multicaster1") @@ -188,6 +191,8 @@ async def initialize2(conf: auracast_config.AuracastConfigGroup): raise HTTPException(status_code=400, detail=f"Audio device '{device_name}' not found.") log.info('Initializing multicaster2 with config:\n %s', conf.model_dump_json(indent=2)) multicaster2 = multicast_control.Multicaster(conf, conf.bigs) + # Ensure target is reset before initializing broadcast + await reset_nrf54l(1) await multicaster2.init_broadcast() if any(big.audio_source.startswith("device:") or big.audio_source.startswith("file:") for big in conf.bigs): log.info("Auto-starting streaming on multicaster2") diff --git a/src/auracast/utils/reset_utils.py b/src/auracast/utils/reset_utils.py new file mode 100644 index 0000000..54bb659 --- /dev/null +++ b/src/auracast/utils/reset_utils.py @@ -0,0 +1,64 @@ +import os +import asyncio +import logging as log + +async def reset_nrf54l(slot: int = 0, timeout: float = 8.0): + """ + Reset the nRF54L target using OpenOCD before starting broadcast. + + Looks for interface config files in the project at `src/openocd/` relative to this module only. + Accepts filename variants per slot: + - slot 0: raspberrypi-swd0.cfg or swd0.cfg + - slot 1: raspberrypi-swd1.cfg or swd1.cfg + + Executes the equivalent of: + openocd \ + -f ./raspberrypi-${INTERFACE}.cfg \ + -f target/nordic/nrf54l.cfg \ + -c "init" \ + -c "reset run" \ + -c "shutdown" + + Best-effort: if OpenOCD is unavailable, logs a warning and continues. + """ + try: + # Resolve project directory and filenames + proj_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'openocd')) + names = ['raspberrypi-swd0.cfg', 'swd0.cfg'] if slot == 0 else ['raspberrypi-swd1.cfg', 'swd1.cfg'] + cfg = None + for n in names: + p = os.path.join(proj_dir, n) + if os.path.exists(p): + cfg = p + break + if not cfg: + log.warning("reset_nrf54l: no interface CFG found in project dir %s; skipping reset", proj_dir) + return + + # Build openocd command (no sudo required as per project setup). + cmd = ['openocd', '-f', cfg, '-f', 'target/nordic/nrf54l.cfg', + '-c', 'init', '-c', 'reset run', '-c', 'shutdown'] + async def _run(cmd): + proc = await asyncio.create_subprocess_exec( + *cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.STDOUT + ) + try: + out, _ = await asyncio.wait_for(proc.communicate(), timeout=timeout) + except asyncio.TimeoutError: + log.error("reset_nrf54l: %s timed out; terminating", cmd[0]) + proc.kill() + return False + rc = proc.returncode + if rc != 0: + log.error("reset_nrf54l: %s exited with code %s; output: %s", cmd[0], rc, (out or b'').decode(errors='ignore')) + return False + return True + + ok = await _run(cmd) + if ok: + log.info("reset_nrf54l: reset succeeded (slot %d) using %s", slot, cfg) + + except FileNotFoundError: + log.error("reset_nrf54l: openocd not found; skipping reset") + except Exception: + log.error("reset_nrf54l failed", exc_info=True) diff --git a/src/openocd/raspberrypi-swd0.cfg b/src/openocd/raspberrypi-swd0.cfg new file mode 100644 index 0000000..853f575 --- /dev/null +++ b/src/openocd/raspberrypi-swd0.cfg @@ -0,0 +1,8 @@ +adapter driver bcm2835gpio +transport select swd +adapter gpio swclk 17 +adapter gpio swdio 18 +#adapter gpio trst 26 +#reset_config trst_only + +adapter speed 1000 diff --git a/src/openocd/raspberrypi-swd1.cfg b/src/openocd/raspberrypi-swd1.cfg new file mode 100644 index 0000000..103c0f9 --- /dev/null +++ b/src/openocd/raspberrypi-swd1.cfg @@ -0,0 +1,8 @@ +adapter driver bcm2835gpio +transport select swd +adapter gpio swclk 24 +adapter gpio swdio 23 +#adapter gpio trst 27 +#reset_config trst_only + +adapter speed 1000