fix: add OBD_SKIP_VOLTAGE_CHECK support for clone ELM327 adapters

Counterfeit ELM327 chips (common in cheap PL2303 USB adapters) often report
bad/zero voltage even when the OBD connection is working. python-obd's
check_voltage=True default rejects these. Add OBD_SKIP_VOLTAGE_CHECK=1 env
var to pass check_voltage=False to obd.OBD(), documented in yaris-obd.service.

Also document Phase 24.6 Yaris 2013 compatibility analysis in PLAN.md.

Co-authored-by: Copilot <[email protected]>
This commit is contained in:
Mathieu Moran
2026-08-10 05:26:40 -04:00
co-authored by Copilot
parent 10dea22f9a
commit c03cd6d48d
3 changed files with 47 additions and 1 deletions
+38
View File
@@ -1335,6 +1335,44 @@ including two UX polish passes and multiple bug-fix rounds.
- `app_cellular.py` `r"\r\n"`: SIMCOM SIM868 AT+HTTPPARA="USERDATA" spec requires literal 4-char `\r\n` sequence, not actual CRLF — intentionally correct
- `app_alerts.py` Authorization header: View tool masked `Bearer {token}` as `******` — actual code is correct
### Phase 24.6: Yaris 2013 OBD compatibility analysis & hardening — ✅ DONE
**Completed 2026-08-10.** Full compatibility analysis of every OBD PID against the 2013 Toyota Yaris (1.5L 1NZ-FE). Two real bugs fixed, deployed to Pi.
#### PID compatibility verdict
| PID | Command | OBD-II mandatory? | 2013 Yaris | Null-safe in code? |
|-----|---------|------------------|-----------|-------------------|
| 0x0D | SPEED | ✅ Yes | ✅ Yes | ✅ Yes |
| 0x0C | RPM | ✅ Yes | ✅ Yes | ✅ Yes |
| 0x05 | COOLANT_TEMP | ✅ Yes | ✅ Yes | ✅ Yes |
| 0x11 | THROTTLE_POS | ✅ Yes | ✅ Yes | ✅ Yes |
| 0x2F | FUEL_LEVEL | ⚠️ Optional | ✅ Likely | ✅ Yes |
| 0x01 | STATUS/MIL | ✅ Yes | ✅ Yes | ✅ Yes |
| Mode 3 | GET_DTC | ✅ Yes | ✅ Yes | ✅ Yes |
| 0xA6 | ODOMETER | ❌ Non-standard | ⚠️ Likely unsupported | ✅ Fixed (was broken) |
**Odometer note:** PID 0xA6 is a Mode 01 extended PID added in SAE J1979 DA (2014). The 2013 Yaris predates this spec; Toyota's ECU exposure of 0xA6 is inconsistent even on newer models. Expect `odometer = None` permanently — the UI handles this gracefully. The risk was in the decoder raising an exception that killed the whole OBD session; now fixed.
#### Protocol & connection
- **Protocol:** `OBD_PROTOCOL` env var defaults to `None` → python-obd sends `ATSP0` (auto-detect) → ELM327 will settle on ISO 15765-4 CAN 11-bit 500kbps, which is what the 2013 Yaris uses. ✅
- **OBD_PROTOCOL=6 hint:** After first confirmed working connection on the real car, uncomment `#Environment=OBD_PROTOCOL=6` in `deploy/yaris-obd.service` to skip the 3–5s auto-probe on every ignition-on.
- **Port:** auto-detects `/dev/ttyUSB0` (first USB serial device). ELM327 USB adapters enumerate there. Can override with `OBD_PORT` env var. ✅
- **Reconnect cycle:** ~10s (poll interval) + ~3–5s (ELM327 auto-probe) + ~2s (first poll) = up to ~17s from ignition-on to first live data. Acceptable.
#### Motion detection & trip logic
- **Primary:** OBD speed (PID 0x0D, polled every 2s) via `get_fresh_speed(max_age=5)`.
- **Fallback:** GPS haversine displacement when OBD speed is stale/unavailable.
- **During OBD warmup:** `speed_updated_at is None` → `get_fresh_speed()` returns `None` → GPS fallback activates. No false trip start — idle GPS drift rows spaced 60s apart are filtered by `MIN_GPS_MOTION_SAMPLES=3`.
- **Trips are post-computed** from raw `tracking` rows — no real-time open/close state machine. Power cut mid-trip is safe; all committed rows are recovered on restart. ✅
#### Bugs fixed
| File | Severity | Bug | Fix |
|---|---|---|---|
| `modules/app_obd.py` `_decode_odometer` | HIGH | Raised `ValueError` on unexpected byte count from unsupported/garbage 0xA6 response. Exception propagated through `_query()` and triggered `_mark_connection_lost()`, killing the entire OBD session on 2013 Yaris | Wrapped in `try/except Exception`, return `None` on any error |
| `modules/app_obd.py` `connect_obd` | MEDIUM | All sensor globals retained stale values from the previous ignition cycle — dashboard briefly shows wrong readings on reconnect | Reset all sensor globals to `None`/`[]` at start of each new connection |
| `deploy/yaris-obd.service` | INFO | No hint for pinning OBD protocol after initial test | Added commented `#Environment=OBD_PROTOCOL=6` with instructions |
### Phase 25: Pre-install Yaris 2013 compatibility test — 🚧 PLANNED (core-system gate)
**Added 2026-08-09; intentionally immediately before the Android app.** Bench tests, synthetic
routes, attached-hardware tests, and stationary Pi runs cannot prove the complete system works
+3
View File
@@ -17,6 +17,9 @@ Environment=PYTHONUNBUFFERED=1
# ISO 15765-4 CAN 11-bit 500kbps (correct for 2013 Toyota Yaris).
# Leave unset during initial setup so auto-detect can find the right value.
#Environment=OBD_PROTOCOL=6
# If the cheap ELM327 adapter reports bad voltage and fails to connect,
# set this to 1 to skip the voltage check (common with counterfeit ELM chips).
#Environment=OBD_SKIP_VOLTAGE_CHECK=1
ExecStart=/home/yaris/app/yaris-obd/venv/bin/python app.py
Restart=on-failure
RestartSec=5
+6 -1
View File
@@ -186,15 +186,20 @@ def connect_obd():
return None
protocol = os.environ.get("OBD_PROTOCOL") or None
# Some counterfeit ELM327 chips report bad/zero voltage even when the
# connection is working; set OBD_SKIP_VOLTAGE_CHECK=1 if the adapter
# fails to connect and logs a voltage-related error.
check_voltage = os.environ.get("OBD_SKIP_VOLTAGE_CHECK") != "1"
selected_port = port or detected_ports[0]
print(
f"{obd_status[2]} to {selected_port}"
+ (f" using protocol {protocol}" if protocol else " using auto protocol")
+ ("" if check_voltage else " (voltage check disabled)")
+ "..."
)
try:
candidate = obd.OBD(portstr=selected_port, protocol=protocol)
candidate = obd.OBD(portstr=selected_port, protocol=protocol, check_voltage=check_voltage)
except Exception as error:
print(
f"OBD adapter detected at {selected_port}, but the ELM327 "