Reusable runbook: when the phone OBD app can’t connect to the ELM327-over-TCP bridge even though the TCP port is listening, the cause is almost always a stale macOS Bluetooth SDP cache after the dongle power-cycled. Fix = reconnect just that Bluetooth device + bounce the bridge. Expect this every time the car ignition goes off (the ELM327 is bus-powered).
For Agents
- Bridge host: the Mac (
~/obd-bridge/obdbridge, a Swift binary; launchdcom.levander.obdbridge,KeepAlive). Logs:~/obd-bridge/bridge.err+~/obd-bridge/bridge.log.- Adapter: ELM327 BT dongle “OBDII”, address
00:1D:A5:68:98:8B(dashed form00-1d-a5-68-98-8b), PIN1234, paired to the Mac.- Data path: dongle ⇄ Bluetooth RFCOMM ⇄ bridge ⇄ ELM327-over-TCP on
100.83.222.120:35000(tailnet). Configure the phone OBD app as a WiFi adapter pointed at thathost:port.- The break is downstream of the bridge software — restarting the bridge alone does NOT fix it.
blueutilis at/opt/homebrew/bin/blueutil(v2.13.0).
Symptom
- Phone OBD app fails to connect. TCP
35000is listening (bridge is up). ~/obd-bridge/bridge.errloops:opening RFCOMM channel 1 to 00-1D-A5-68-98-8B openRFCOMMChannelSync failed: ret=0x-1ffffd44 RFCOMM open attempt N failed; retrying in 3s0x-1ffffd44decodes to IOReturn0x2bc=kIOReturnError(generic error).
One-glance diagnosis
A listening TCP port does NOT mean the serial link is up
lsof -iTCP:35000shows the bridge accepting connections regardless of whether the RFCOMM channel to the dongle actually opened. Always checkbridge.errfor RFCOMM errors before blaming the phone or the app.
Confirm the two facts, in order:
- Dongle powered + Bluetooth-connected?
blueutil --is-connected 00-1d-a5-68-98-8b # 1 = connected, 0 = not system_profiler SPBluetoothDataType # OBDII should appear under "Connected:" - Is a Serial Port (SPP) service being advertised? This is the root-cause check:
system_profiler SPBluetoothDataType | grep -A3 OBDII- Broken:
Services: 0x802000 < Braille ACL >— no SPP. The bridge queries SDP for the RFCOMM channel ID, finds none, falls back to hardcoded channel 1, and channel 1 fails to open. - Healthy: a Serial Port / SPP service is listed → SDP yields a real channel ID → RFCOMM opens.
- Broken:
It is NOT a serial-port-held-by-another-app problem
No
/dev/tty.*or/dev/cu.*claimant is involved here. Don’t go hunting for a process holding a serial device — the break is in the Bluetooth SDP service-record cache, not a POSIX tty.
Root cause
The ELM327 is bus-powered from the car’s OBD port, so it loses power whenever the car/ignition is off. On the next power-up, macOS’s cached SDP service record for the dongle can go stale — the adapter comes back advertising a bogus service set (< Braille ACL >) instead of Serial Port Profile. The bridge (obdbridge.swift) does an SDP lookup for the RFCOMM channel, gets nothing usable, falls back to channel 1, and the open fails with kIOReturnError.
The fix (that worked)
Disconnect and reconnect just that one device (so the BT keyboard/mouse survive — do NOT toggle the whole Bluetooth stack), then bounce the bridge to force a fresh SDP query:
blueutil --disconnect 00-1d-a5-68-98-8b
sleep 3
blueutil --connect 00-1d-a5-68-98-8b
launchctl kickstart -k gui/$(id -u)/com.levander.obdbridgeThen watch until it links up:
tail -f ~/obd-bridge/bridge.err
# wait for: RFCOMM connected → client connectedOnce SPP is advertised again, the bridge’s KeepAlive retry loop grabs the channel on its own.
blueutil --connectcan BLOCK for a long timeIn one attempt it hung until a 2-minute timeout while the dongle was slow to re-establish — but the reconnect + fresh SDP still succeeded (the retry loop picked it up). Don’t re-run
--connecton a hang; poll state with the non-blockingblueutil --is-connected 00-1d-a5-68-98-8binstead.
Restarting the bridge alone will NOT fix it
launchctl kickstartby itself re-runs the same failing SDP lookup. You must reconnect the Bluetooth device first so macOS refreshes the SDP record; the bridge bounce is only there to force an immediate fresh query instead of waiting out the retry backoff.
Expect recurrence
This recurs every time the dongle loses power (car off → ignition off → OBD port unpowered). It is not a one-time fix; keep this runbook handy and re-run the four fix commands whenever the phone can’t connect and bridge.err shows the RFCOMM loop.
Related
- homelab — device inventory and tailnet peers (the bridge is a Mac-hosted tailnet service on
100.83.222.120:35000) - SESSION-HANDOVER — the OBD2 bridge one-liner in current-state
- LOG
- TOPICS