# 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` | исходный | Протокол основного приложения |