14 KiB
ELM327 Relay — полный журнал неудач
Дата: 2026-07-04–05 | Версия: v0.4.0-dev
Итоговое состояние
| Параметр | Значение |
|---|---|
| Версия на сервере | v0.4.0-dev (commit ebc41e0) |
| Основа | 1:1 с app/.../ElmProtocol.kt (AndrOBD) |
| Клон 1 (v1.5) | 12/12 → 13/16 (72-81%) |
| Клон 2 (v1.5) | 13/18 (72%) |
| Характер отказов | Деградация после 10-12 команд |
| Двигатель заглушен | PID не работают (STOPPED), только ATRV |
НЕУДАЧА 1: Игнорирование рабочего кода
Симптом: raw/ElmProtocol.kt написан с нуля вместо копирования app/ElmProtocol.kt
Когда: создание raw-модуля (июнь 2026)
Что сделано не так: В репозитории УЖЕ был app/src/.../ElmProtocol.kt — рабочий протокол на основе AndrOBD. Вместо копирования написан новый с «улучшениями».
Корневая причина: предположение что raw-реле требует особой логики. Не проверено что app/ElmProtocol.kt уже работает.
Исправлено: v0.4.0-dev — raw/ElmProtocol.kt = 1:1 копия app/ElmProtocol.kt
Урок: всегда проверять существующий код перед созданием нового.
НЕУДАЧА 2: Доверие Opus без проверки исходников
Симптом: init() с ATE0 первым, ATST96 для клонов, drain перед write Когда: июнь–июль 2026 Что сделано не так: Opus посоветовал — реализовано без сверки с AndrOBD (ElmProt.java). AndrOBD 10 лет в проде, все «улучшения» были ошибками. Корневая причина: доверие внешнему анализу вместо проверки первоисточника. Исправлено: полная сверка с ElmProt.java (fr3ts0n/AndrOBD). Все отличия задокументированы. Урок: первоисточник (код) > любой анализ.
НЕУДАЧА 3: ATST96 убивает клон v1.5
Симптом: клон зависает намертво после init, даже ATRV не отвечает
Когда: v0.3.0-dev
Что сделано не так: добавлен ATST96 (150×4=600ms) для клонов. Команда не поддерживается клоном v1.5 и вешает его.
Корневая причина: предположение что клону нужен фиксированный таймаут. AndrOBD использует updateAtst() с адаптивным таймаутом для всех устройств.
Исправлено: ATST96 удалён. updateAtst() теперь вызывается всегда (как в AndrOBD).
Урок: не добавлять команды, которых нет в AndrOBD.
НЕУДАЧА 4: ATI в init() ломает порядок
Симптом: буферный сдвиг после каждой команды
Когда: v0.3.0–v0.3.7-dev
Что сделано не так: ATI вставлен в середину init() для детекта клона. AndrOBD не использует ATI — версия определяется из ответа ELM на MODEL.
Корневая причина: желание детектить клон внутри init(). AndrOBD определяет клона иначе.
Исправлено: v0.4.0-dev — ATI удалён из init(). Init: ATSP0→ATAT1→ATST→ATS0→ATL0→ATE0.
Урок: не вставлять команды в init(), которых нет в AndrOBD.
НЕУДАЧА 5: drainInput() в write()
Симптом: ответ команды N читается как ответ команды N+1
Когда: все версии raw
Что сделано не так: write() вызывает drainInput() перед отправкой. Если ответ предыдущей команды запаздывает, drain съедает его и наступает сдвиг буфера на 1 позицию.
Корневая причина: AndrOBD не использует drain. Синхронная модель (sendCommand → ждать ответ) требует drain для очистки мусора, но drain сам создаёт мусор если ответ приходит с задержкой.
Статус: НЕ ИСПРАВЛЕНО. Остаётся в коде v0.4.0-dev. Без drain буфер забивается мусором, с drain — сдвиг на 1 команду. Меньшее из зол.
Урок: синхронная модель принципиально ограничена. AndrOBD использует асинхронную (поток читает → handleTelegram) и не имеет этой проблемы.
НЕУДАЧА 6: Freeze detection через ATRV
Симптом: ответ "12.6V" читается как ответ на PID, все команды возвращают одно и то же Когда: v0.3.0-dev Что сделано не так: каждые 10 команд слался ATRV для проверки залипания. Ответ ATRV попадал в буфер и читался как ответ следующего PID. Корневая причина: ATRV — AT-команда, не OBD. Её ответ не должен смешиваться с PID-ответами. В синхронной модели это неизбежно. Исправлено: удалено в v0.3.2-dev. Урок: не смешивать AT-команды с OBD-командами в одном потоке.
НЕУДАЧА 7: AT-команды в handle()
Симптом: после STOPPED/BUS_ERROR следующая команда получает мусор Когда: v0.1.x–v0.3.3-dev Что сделано не так: handle() при ошибках слал ATPC, ATWS, ATSP0. Эти AT-команды отправлялись внутри обработки ответа текущей команды, их ответы загрязняли буфер. Корневая причина: handle() вызывается из exec() который находится в процессе чтения ответа. Отправка AT-команд внутри exec() создаёт вложенные чтения, которые путают буфер. Исправлено: в v0.4.0-dev handle() шлёт AT-команды только для BUS_ERROR и ERROR (как в AndrOBD). Для STOPPED — только state tracking. Урок: AT-команды в handle() допустимы только если их ответы полностью потребляются (tryRead с таймаутом).
НЕУДАЧА 8: Recovery ATSP0 в relayLoop
Симптом: тест показывает 0/60 (все таймауты) Когда: v0.3.5-dev Что сделано не так: после STOPPED или пустого ответа relayLoop слал ATSP0 через sendBlocking. ATSP0 блокирует single-thread executor на 3+ секунд. Все последующие команды ждут в очереди, тест видит таймауты. Корневая причина: recovery выполнялся в том же потоке что и обработка команд. Команды накапливались в очереди executor'а. Исправлено: удалено в v0.4.0-dev. Recovery теперь только в handle(). Урок: recovery должен быть частью протокольного уровня (ElmProtocol), а не оркестратора (relayLoop).
НЕУДАЧА 9: Дренаж 0100 после init
Симптом: relay не отвечает ни на одну команду после init Когда: v0.3.6-dev Что сделано не так: после init отправлялся 0100 для поглощения буферного сдвига. Дренаж сам создавал сдвиг и дезориентировал клон. Корневая причина: попытка «подчистить» буфер создаёт новую команду и новый ответ, который тоже надо чистить — бесконечная рекурсия. Исправлено: удалено в v0.3.7-dev. Урок: не пытаться чистить буфер дополнительными командами.
НЕУДАЧА 10: Тест читал чужие ответы
Симптом: тест показывал OK (411100) когда relay возвращал пустоту
Когда: все тесты до исправления сервера
Что сделано не так: тестовый скрипт вызывал /response?wait=3 без device_id. Сервер возвращал ответы от других устройств или предыдущих сессий.
Корневая причина: серверный SQL не фильтровал по device_id. Тест не обновлял seq.
Исправлено: сервер (api/raw_elm.py) — /response принимает device_id. Тест обновляет seq после каждого ответа.
Урок: всегда проверять что тест читает данные того устройства которое тестируется.
НЕУДАЧА 11: Версия на сайте не обновлялась
Симптом: сайт показывал v0.3.0-dev при v0.4.0-dev на сервере
Когда: все деплои
Что сделано не так: sed правил только /opt/elmer/web/templates/index.html. Flask использует /opt/elmer/templates/index.html.
Корневая причина: два index.html в разных директориях, неизвестно какой использует Flask.
Исправлено: деплой обновляет оба файла. Выяснено что Flask использует /opt/elmer/templates/.
Урок: проверять какой файл реально сервится перед правкой.
НЕУДАЧА 12: v0.4.0-dev не долетел до телефона
Симптом: пользователь тестировал v0.3.7-dev думая что это v0.4.0-dev Когда: 2026-07-04 вечер Что сделано не так: деплой v0.4.0-dev прошёл, APK на сервере, но пользователь не переустановил приложение. Корневая причина: отсутствие проверки версии на телефоне. Исправлено: явное указание версии при каждом деплое. Проверка MD5 APK на сервере. Урок: всегда проверять что пользователь обновил APK перед тестированием.
ОГРАНИЧЕНИЯ КЛОНОВ v1.5 (железо)
| Параметр | Значение |
|---|---|
| Надёжность (холодный) | 100% первые 10-12 команд |
| Надёжность (горячий) | 70-80%, деградация после 10-12 команд |
| Буферный сдвиг после init | 1-2 команды (неизбежно в синхронной модели) |
| Двигатель заглушен | PID не работают, ATRV — ок |
| Двигатель заведён | 70-80% успех |
| ATST96 | Убивает клон намертво |
| ATAT1 | Клон игнорирует, не мешает |
| Время восстановления | 2-3 секунды паузы |
ТЕКУЩАЯ АРХИТЕКТУРА (v0.4.0-dev)
Android phone Server (obdai.ru)
┌──────────────────┐ ┌─────────────────────┐
│ RawRelayService │──HTTP────→│ /api/v1/elm/raw/cmd │
│ relayLoop() │←──poll───│ /api/v1/elm/raw/ │
│ ↓ │ │ response │
│ ElmActor │ └─────────────────────┘
│ ↓ │
│ ElmProtocol │──BT────→ ELM327 → OBD-II → ECU
│ (AndrOBD) │←──BT───
└──────────────────┘
КЛЮЧЕВЫЕ ФАЙЛЫ
| Файл | Версия | Описание |
|---|---|---|
raw/.../ElmProtocol.kt |
v0.4.0-dev | 1:1 с app/ElmProtocol.kt |
raw/.../ElmActor.kt |
v0.4.0-dev | Single-thread executor |
raw/.../RawRelayService.kt |
v0.4.0-dev | Поллинг + обработка ответов |
api/raw_elm.py |
исправлен | /response с device_id |
app/.../ElmProtocol.kt |
исходный | Протокол основного приложения |