docs(series4): RESOLVED -- FTDI cable AND a cold boot; modems bridge one session
The cable swap alone did not fix it. A probe immediately after fitting a genuine FTDI cable (lsusb 0403:6001, FT232) returned the same "connected, no reply". A COLD BOOT of the unit was also required -- power button held five seconds through the two-stage battery-disconnect prompt, powered back up with the cable already attached. THOR connected the moment it finished booting, and an independent probe returned UM12947, idle, over cellular. So the Micromate's USB host does not rescan on hot-swap: it enumerates that port at boot and, having once failed to identify a device, does not try again. Changing the cable is a power-cycle operation -- and anyone swapping one in the field who probes immediately will conclude the new cable is faulty too. Full chain: an FTDI or CDC-ACM cable, AND a cold boot with it attached. SEPARATELY CONFIRMED, by accident: these modems bridge ONE TCP session to serial at a time. With THOR connected, the probe got "TCP ok, NO REPLY"; with THOR disconnected and nothing else changed, the same probe returned the serial number. The PAD accepts a second connection and then never forwards it -- it does not refuse and does not close. That makes "connected but no reply" ambiguous between a broken serial path and somebody else already holding the unit, and mm_probe reported only the former -- which would push a diagnosis in exactly the wrong direction, on the very fault we spent two days chasing. Its verdict now names both causes, tells you to eliminate contention first, and includes the cable-chipset and cold-boot steps. Recorded as an SFM design constraint: our receiver and THOR cannot both hold a unit, and during any migration both will exist. "Another client holds this unit" has to be a distinct visible state. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ru8Lg9HkkYvX9VWWo65SmL
This commit is contained in:
+23
-10
@@ -134,17 +134,30 @@ def probe(host: str, port: int, timeout: float) -> int:
|
||||
step(2, "POLL", f"NO REPLY in {timeout:.1f} s")
|
||||
print("\nverdict: the MODEM answered but the unit did not.")
|
||||
print(" TCP is fine end to end — something accepted the connection.")
|
||||
print(" What is missing is the serial side. Most likely the modem is")
|
||||
print(" not forwarding to its serial port, which is what a wedged")
|
||||
print(" transparent-TCP session looks like: the slot is held by a")
|
||||
print(" connection that never closed.")
|
||||
print(" What is missing is the serial side. Two quite different")
|
||||
print(" causes produce this, and they are NOT distinguishable from")
|
||||
print(" here:")
|
||||
print("\n 1. SOMEONE ELSE HOLDS THE SESSION. These modems bridge ONE")
|
||||
print(" TCP session to serial at a time. A second connection is")
|
||||
print(" accepted and then simply not forwarded. Confirmed 2026-09-26:")
|
||||
print(" with THOR connected this probe saw exactly this; the moment")
|
||||
print(" THOR disconnected the same probe returned the serial number.")
|
||||
print(" ** Check whether THOR (or anything else) has the unit first. **")
|
||||
print("\n 2. The serial path is genuinely broken — a stale session the")
|
||||
print(" modem never released, a cable the unit cannot enumerate, or")
|
||||
print(" a unit that is off.")
|
||||
print("\n Try, in order:")
|
||||
print(" 1. ACEmanager -> TCP Idle Timeout. If 0/disabled, a stale")
|
||||
print(" session holds the slot forever. 2 minutes is the value")
|
||||
print(" this project standardised on.")
|
||||
print(" 2. Reboot the modem. If that fixes it, the modem was")
|
||||
print(" holding state and the timeout is the permanent fix.")
|
||||
print(" 3. Check the unit's own screen — serial cable, power.")
|
||||
print(" 1. Disconnect any other client and re-probe. If it answers,")
|
||||
print(" it was contention, not a fault.")
|
||||
print(" 2. The cable's chipset. A Micromate drives FTDI and CDC-ACM")
|
||||
print(" only — a Prolific PL2303 gives it no serial port at all.")
|
||||
print(" lsusb: FTDI is 0403, Prolific 067b.")
|
||||
print(" 3. Power-cycle the UNIT with the cable attached (hold power")
|
||||
print(" 5 s, through the two-stage prompt). Its USB host rescans")
|
||||
print(" on cold boot; it may not on hot-swap.")
|
||||
print(" 4. AirLink OS -> TCP Idle Timeout. If 0/disabled, a stale")
|
||||
print(" session holds the slot indefinitely. 2 minutes is the")
|
||||
print(" value this project standardised on.")
|
||||
sock.close()
|
||||
return 5
|
||||
|
||||
|
||||
@@ -2162,6 +2162,52 @@ unit.** No USB-to-serial adapter anywhere in the path, so there is no chipset t
|
||||
get wrong. That workaround has been quietly routing around this exact failure
|
||||
mode.
|
||||
|
||||
### ✅ RESOLVED (2026-09-26) — and it took a cold boot as well as the cable
|
||||
|
||||
Swapping to a genuine FTDI cable (`lsusb`: `0403:6001`, FT232) **did not work on
|
||||
its own.** A probe immediately after the swap returned the same *connected, no
|
||||
reply*.
|
||||
|
||||
**A cold boot of the unit was also required.** Holding the power button for five
|
||||
seconds — through the two-stage prompt that disconnects the internal battery —
|
||||
and powering back up **with the cable already attached**, brought it straight up.
|
||||
THOR at the office connected the moment it finished booting, and an independent
|
||||
probe returned `UM12947, idle` over cellular.
|
||||
|
||||
⚠ **The USB host does not rescan on hot-swap.** The Micromate enumerates its
|
||||
USB-A host port at boot; once it has failed to identify a device there it does
|
||||
not appear to try again. Changing that cable is a power-cycle operation, and
|
||||
anyone swapping one in the field who probes immediately will conclude the new
|
||||
cable is faulty too.
|
||||
|
||||
So the full chain for a Micromate on a modem is:
|
||||
|
||||
1. an **FTDI** or CDC-ACM cable — a Prolific PL2303 gives the unit no serial port
|
||||
2. a **cold boot** with that cable attached
|
||||
|
||||
### ✅ These modems bridge ONE session at a time — confirmed
|
||||
|
||||
Long suspected, never demonstrated. Demonstrated now, by accident:
|
||||
|
||||
| | probe result |
|
||||
|---|---|
|
||||
| THOR connected to the unit | `TCP connect ok` … **`NO REPLY`** |
|
||||
| THOR disconnected, nothing else changed | `reply 68 B`, **`UM12947`**, idle |
|
||||
|
||||
The PAD **accepts** a second TCP connection and then does not forward it. It
|
||||
does not refuse, and it does not close — it simply never bridges.
|
||||
|
||||
⚠ **This means "connected but no reply" has two causes that are indistinguishable
|
||||
from the client side:** a genuinely broken serial path, and *somebody else
|
||||
already has the unit*. `bridges/mm_probe.py` originally reported only the first,
|
||||
which would send a diagnosis in exactly the wrong direction; its verdict now
|
||||
names both and tells you to eliminate contention first.
|
||||
|
||||
**For SFM this is a design constraint, not a footnote.** Our receiver and THOR
|
||||
cannot both hold a unit, and during any migration both will exist. "Another
|
||||
client holds this unit" needs to be a distinct, visible state — not folded into
|
||||
a failure, and certainly not into a green tick.
|
||||
|
||||
### How this was isolated — the method is reusable
|
||||
|
||||
A Micromate on an RX55 was unreachable from THOR. Rather than guess, each layer
|
||||
|
||||
Reference in New Issue
Block a user