Bring up the USB gadget link (package 0b, Pi side and talos side)

The Pi presents NCM Ethernet and ACM serial from a configfs gadget, started by
robot-usb-gadget.service before networking. usb0 gets 10.55.0.2/30 with no
gateway through netplan, matched by interface name: a MAC match turns into
PermanentMACAddress= in networkd, which a gadget interface does not have, so it
stayed unmanaged. talos reaches the Pi at 10.55.0.2 (ping ~2.4 ms, SSH works)
through a NetworkManager profile, host/omarchy/robot-usb-link.sh, matched by the
gadget's fixed host MAC.

Also clamp the face's tunable parameters (focus sets a blur radius, persistence
divides) and fps, after a review flagged unvalidated node parameters.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AFN8nRRCiDiMN7ChBpHJhs
This commit is contained in:
2026-10-04 20:53:09 -07:00
co-authored by Claude Sonnet 5.5
parent 55d33ab490
commit 69a93eeb2f
9 changed files with 181 additions and 11 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ class FaceNode(Node):
def __init__(self):
super().__init__('hermes_face')
param = lambda name, default: self.declare_parameter(name, default).value # noqa: E731
self.fps = float(param('fps', 30.0))
self.fps = min(60.0, max(1.0, float(param('fps', 30.0)))) # a timer period of 1/fps
output = param('output', 'st7789') # 'st7789' or 'none' (render only)
self.pan_joint = param('pan_joint', 'pan_joint')
self.tilt_joint = param('tilt_joint', 'tilt_joint')
+8 -4
View File
@@ -125,12 +125,16 @@ class FaceRenderer:
if rotation not in (0, 90, 180, 270):
raise ValueError(f'rotation must be 0, 90, 180 or 270, not {rotation!r}')
self.rotation = rotation # output turned this many degrees counter-clockwise
if phosphor not in PHOSPHORS:
raise ValueError(f'unknown phosphor {phosphor!r}; known: {", ".join(PHOSPHORS)}')
self.rgb = PHOSPHORS[phosphor]
self.style = style
self.persist_ms = persist_ms
self.intensity = intensity
self.focus = focus
self.jitter = jitter
# Clamped, because these arrive as node parameters: focus sets a blur radius, and
# persistence divides.
self.persist_ms = min(5000.0, max(5.0, float(persist_ms)))
self.intensity = min(4.0, max(0.0, float(intensity)))
self.focus = min(1.0, max(0.0, float(focus)))
self.jitter = min(5.0, max(0.0, float(jitter)))
self.rng = np.random.default_rng(seed)
self.buf = np.zeros((H, W), np.float32)
+11
View File
@@ -99,3 +99,14 @@ def test_rotation_turns_the_frame_counter_clockwise(rotation):
def test_bad_rotation_rejected():
with pytest.raises(ValueError):
FaceRenderer(rotation=45)
def test_wild_parameters_are_clamped():
r = FaceRenderer(focus=1e9, persist_ms=0, intensity=-3, jitter=1e6, seed=0)
assert (r.focus, r.persist_ms, r.intensity, r.jitter) == (1.0, 5.0, 0.0, 5.0)
assert run(r, 1)[0].shape == (H, W, 3)
def test_unknown_phosphor_rejected():
with pytest.raises(ValueError):
FaceRenderer(phosphor='P99')