feat: add nRF54L reset via OpenOCD before initializing broadcast

This commit is contained in:
pstruebi
2025-09-29 15:02:32 +02:00
parent 464bb6fe86
commit 01fabfef78
6 changed files with 90 additions and 5 deletions
Generated
+4 -4
View File
@@ -332,7 +332,7 @@ files = [
[[package]] [[package]]
name = "bumble" name = "bumble"
version = "0.0.216.dev1+g6eba81e3d" version = "0.0.218.dev6+g32d448edf"
description = "Bluetooth Stack for Apps, Emulation, Test and Experimentation" description = "Bluetooth Stack for Apps, Emulation, Test and Experimentation"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
@@ -371,8 +371,8 @@ test = ["coverage (>=6.4)", "pytest (>=8.2)", "pytest-asyncio (>=0.23.5)", "pyte
[package.source] [package.source]
type = "git" type = "git"
url = "ssh://git@ssh.pstruebi.xyz:222/auracaster/bumble_mirror.git" url = "ssh://git@ssh.pstruebi.xyz:222/auracaster/bumble_mirror.git"
reference = "6eba81e3ddb8ac0e4c336ca244892a0a8d43ba1c" reference = "32d448edf3276f6b9056765a12879054d8a01fd8"
resolved_reference = "6eba81e3ddb8ac0e4c336ca244892a0a8d43ba1c" resolved_reference = "32d448edf3276f6b9056765a12879054d8a01fd8"
[[package]] [[package]]
name = "cachetools" name = "cachetools"
@@ -2952,4 +2952,4 @@ test = ["pytest", "pytest-asyncio"]
[metadata] [metadata]
lock-version = "2.1" lock-version = "2.1"
python-versions = ">=3.11" python-versions = ">=3.11"
content-hash = "3afe565be2664b3d7f1cfdb1c5a73d931c14e97d3622aef24ba2f06f78e00e2b" content-hash = "6b5300c349ed045e8fd3e617e6262bbd7e5c48c518e4c62cedf7c17da50ce8c0"
+1 -1
View File
@@ -4,7 +4,7 @@ version = "0.0.1"
requires-python = ">=3.11" requires-python = ">=3.11"
dependencies = [ 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", "lc3py @ git+ssh://git@ssh.pstruebi.xyz:222/auracaster/liblc3.git@ce2e41faf8c06d038df9f32504c61109a14130be",
"aioconsole", "aioconsole",
"fastapi==0.115.11", "fastapi==0.115.11",
+5
View File
@@ -23,6 +23,7 @@ from auracast.utils.sounddevice_utils import (
list_usb_pw_inputs, list_usb_pw_inputs,
list_network_pw_inputs, list_network_pw_inputs,
) )
from auracast.utils.reset_utils import reset_nrf54l
load_dotenv() load_dotenv()
# make sure pipewire sets latency # 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.warning("Failed to shutdown previous multicaster", exc_info=True)
log.info('Initializing multicaster1 with config:\n %s', conf.model_dump_json(indent=2)) log.info('Initializing multicaster1 with config:\n %s', conf.model_dump_json(indent=2))
multicaster1 = multicast_control.Multicaster(conf, conf.bigs) multicaster1 = multicast_control.Multicaster(conf, conf.bigs)
# Ensure target is reset before initializing broadcast
await reset_nrf54l(0)
await multicaster1.init_broadcast() await multicaster1.init_broadcast()
if any(big.audio_source.startswith("device:") or big.audio_source.startswith("file:") for big in conf.bigs): 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") 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.") 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)) log.info('Initializing multicaster2 with config:\n %s', conf.model_dump_json(indent=2))
multicaster2 = multicast_control.Multicaster(conf, conf.bigs) multicaster2 = multicast_control.Multicaster(conf, conf.bigs)
# Ensure target is reset before initializing broadcast
await reset_nrf54l(1)
await multicaster2.init_broadcast() await multicaster2.init_broadcast()
if any(big.audio_source.startswith("device:") or big.audio_source.startswith("file:") for big in conf.bigs): 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") log.info("Auto-starting streaming on multicaster2")
+64
View File
@@ -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)
+8
View File
@@ -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
+8
View File
@@ -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