Note
This repository is intended for development use only.
Local HTTP bridge between Gauge (web) and an ELM327 compatible OBD adapter that speaks over a serial device typically Linux Classic Bluetooth via
/dev/rfcomm0after you bind RFCOMM.
The bridge does not implement Bluetooth itself. It opens a TTY (e.g. rfcomm), sends ELM commands with a trailing
\r, and returns raw text until it sees an ELM>prompt.
Warning
Security note:
The server binds to localhost only. Do not expose it to the network without authentication.
Also any local process could send arbitrary ELM commands to your vehicle bus through the adapter.
- Gauge native web (Expo web in Chromium) often cannot use Classic Bluetooth SPP directly; Web Bluetooth targets BLE GATT.
- This service runs on your PC next to the browser: the app sets
EXPO_PUBLIC_OBD_BRIDGE_URL=http://127.0.0.1:8765and uses the “Local OBD bridge” device row to send AT / OBD commands through your existingrfcommor USB serial link.
- Node.js (for
npm start). - A paired OBD adapter on Linux when using rfcomm (pair with BlueZ / your desktop Bluetooth UI first).
- Read/write access to the serial device (often
dialoutoruucpgroup).
cd obd-bridge
npm install
npm startBy default it listens on 127.0.0.1:8765 (not all interfaces).
From the Gauge repo you can also run:
npm run bridge(if that script points at this folder).
| Variable | Default | Purpose |
|---|---|---|
OBD_BRIDGE_PORT |
8765 |
HTTP listen port |
OBD_SERIAL |
/dev/rfcomm0 |
Default serial path when opening the port |
OBD_BAUD |
115200 |
Serial baud rate |
OBD_SERIAL_SETTLE_MS |
2000 |
Delay after open before traffic (helps rfcomm/SPP) |
Example:
OBD_SERIAL=/dev/ttyUSB0 OBD_BAUD=115200 npm startAll routes use JSON where a body is required. CORS is enabled for local frontends.
Returns bridge status, default/preferred serial path, whether the port file exists, whether serial is open, baud, and—when the path looks like rfcomm—a best-effort parse of rfcomm show 0:
rfcomm.state: e.g.clean,connected, orclosedrfcomm.ok:falsewhen state isclosed(ELM will usually time out until you re-bind)rfcomm.hint: suggested bind command when the link is bad
Body (optional):
{ "path": "/dev/rfcomm0" }Omit path to use OBD_SERIAL / /dev/rfcomm0. Opens the serial port. If rfcomm is closed, the response may still be { ok: true } but include a warn string—check /health before relying on the link.
Closes the serial port (stops holding the TTY open from Node).
Body:
{ "cmd": "ATZ", "timeoutMs": 12000 }cmd: ELM command (no\rneeded; the bridge adds it).timeoutMs: optional; default30000.
Success:
{ "text": "...raw ELM response including > ..." }Errors return HTTP 4xx/5xx with { "error": "..." }. Timeout messages may include an rfcomm re-bind hint when /dev/rfcomm* is used and BlueZ reports closed.
Note: Commands are queued so only one runs at a time (real adapters expect that).
The first /api/elm may auto-open the preferred serial path if nothing is open yet.
-
In
gauge-native, copy.env.exampleto.envand set:EXPO_PUBLIC_OBD_BRIDGE_URL=http://127.0.0.1:8765
If the bridge uses another device path than the default, you can set
EXPO_PUBLIC_OBD_SERIAL_PATH(the app passes it to/api/openwhen connecting to the bridge—see Gauge’s bluetooth web implementation). -
Start this bridge, then start Expo web and open the app from
localhostor127.0.0.1(mixed content / Web Bluetooth expectations). -
On the Bluetooth screen, choose Local OBD bridge and proceed to live data.
The bridge does not run rfcomm bind for you (it needs root and is a system/BlueZ concern). You create /dev/rfcomm0 and the live ACL/SPP link with rfcomm or equivalent.
bluetoothctl devicesExample line: Device 00:04:38:8E:F3:38 OBDLink MX+
Channel 1 is typical for many ELM/SPP dongles (e.g. OBDLink); yours may differ.
sudo rfcomm bind 0 00:04:38:8E:F3:38 1Check:
rfcomm show 0You want a state other than closed (e.g. clean or connected). If you see channel 1 closed, the device node may exist but no data reaches the ELM—ATZ will time out in Gauge until you fix it.
sudo rfcomm release 00 is the rfcomm device index (rfcomm0). Use rfcomm -a to list bindings.
When rfcomm show 0 shows closed after sleep or adapter power-save:
sudo rfcomm release 0
sudo rfcomm bind 0 <your-adapter-MAC> 1
rfcomm show 0Then POST /api/close and POST /api/open on the bridge (or restart the bridge) so it reopens the TTY cleanly.
If /api/open says the port is not readable/writable:
sudo usermod -aG dialout $USER
# or on some systems: uucpRefresh CTRL + R, then retry.
| Symptom | Things to check |
|---|---|
ELM timeout: ATZ |
rfcomm show 0 — if closed, re-bind; ensure adapter is paired and awake. |
| Bridge OK but no adapter | Pair with Bluetooth UI first; correct MAC; try channel 1. |
/health open: false |
Normal until first /api/open or /api/elm; Gauge usually opens on connect. |
| Works only right after bind | Adapter or BlueZ dropped the link—automate bind at boot (systemd) if you need that. |