44 lines
6.1 KiB
Markdown
44 lines
6.1 KiB
Markdown
# Анализ причин отказа ELM327 v1.5 при динамическом опросе
|
||
|
||
Анализ кода `ElmProtocol.kt`, `DynamicCollector.kt` и `ElmChecker.kt` выявил ряд критических проблем, которые в совокупности приводят к "зависанию" адаптера ELM327 (особенно дешевых клонов v1.5) при переходе к быстрому циклу опроса.
|
||
|
||
## Ответы на вопросы
|
||
|
||
### 1. Почему ELM327 v1.5 замолкает после первых 2 ответов?
|
||
Основная причина — **десинхронизация и переполнение буфера**.
|
||
* **ATWS прямо перед циклом:** Команда `ATWS` (Warm Start) сбрасывает микроконтроллер. Ему требуется время на инициализацию (обычно 500-1000 мс). Код в `MainActivity` ждет всего 300 мс. Первые команды `010C` прилетают, когда ELM еще "просыпается" или находится в неопределенном состоянии.
|
||
* **Эффект домино в `exec()`:** Если первый PID в цикле (`010C`) не успел ответить вовремя, `exec` возвращает пустую строку, но ELM продолжает обработку. Следующий вызов `sendCommand` через `write()` делает `drainInput()`, удаляя запоздавший ответ, и посылает новую команду. Для клона v1.5 типична ситуация, когда он "захлебывается", если получает новую команду, не закончив передачу предыдущего ответа или символа `>`.
|
||
|
||
### 2. Может ли `drainInput()` в `write()` съедать ответ?
|
||
**Да, и это главная проблема надежности.**
|
||
Если ELM327 ответил на 50 мс позже таймаута, данные уже лежат в буфере Bluetooth-сокета. Вызов `write()` для следующей команды в цикле безусловно их очищает. В итоге `read()` следующей команды видит пустоту, провоцируя новые таймауты и ретраи. Происходит рассинхрон: приложение ждет ответ на команду B, а ELM (если не завис) шлет ответ на команду A.
|
||
|
||
### 3. Критична ли последовательность: static -> speed-test -> ATWS -> dynamic?
|
||
Последовательность перегружена сбросами.
|
||
* `ATWS` сбрасывает настройки `ATAT1`, `ATSP`, `ATL0` и т.д., которые были установлены в `init()`.
|
||
* После `ATWS` протокол может вернуться к `AUTO` (`ATSP0`), что заставляет ELM тратить время на "SEARCHING..." при первом же запросе `010C`. Это гарантированный таймаут в динамическом тесте.
|
||
|
||
### 4. Нужно ли переподключать ELM вместо ATWS?
|
||
Переподключать Bluetooth-сокет не обязательно, но **вместо `ATWS` лучше вызвать серию настроечных команд**, гарантирующих состояние:
|
||
1. `ATE0` (эхо выкл)
|
||
2. `ATL0` (переносы строк выкл)
|
||
3. `ATS0` (пробелы выкл) — крайне важно для скорости и предотвращения переполнения буфера.
|
||
4. `ATSP X` (принудительная установка протокола, найденного в `checkDevice`), чтобы исключить стадию поиска.
|
||
|
||
### 5. Как правильно реализовать динамический опрос для v1.5?
|
||
Для минимизации ошибок на медленных адаптерах:
|
||
1. **Убрать `Thread.sleep(350)` внутри цикла `for (step in steps)`.** Пауза должна быть только между *пакетами* (батчами) PID, если нужно ограничить частоту. Внутри батча команды должны идти максимально плотно: послал -> дождался `>` -> сразу следующий.
|
||
2. **Увеличить таймаут для v1.5.** 500 мс — это предел для v1.5. Первичный запрос (особенно после сброса) может занимать до 1500 мс.
|
||
3. **Оптимизировать `read()`:** v1.5 очень чувствителен к таймингам. Текущий `Thread.sleep(1)` в `read()` — это хорошо, но логика `drainInput` должна быть перемещена: чистить буфер нужно только один раз *перед стартом всего динамического теста*, а не перед каждой командой.
|
||
|
||
## Рекомендации по исправлению
|
||
|
||
1. **В `ElmProtocol.kt`:**
|
||
* Сделать `drainInput()` опциональным параметром в `write()` или убрать его из `sendCommand` по умолчанию.
|
||
* В `handle()` при получении `NODATA` или `ERROR` не делать `ATWS` мгновенно, так как это убивает сессию опроса.
|
||
2. **В `DynamicCollector.kt`:**
|
||
* Удалить `Thread.sleep(350)` в цикле `for`. Вместо этого полагаться на таймауты `ElmProtocol`.
|
||
3. **В `MainActivity.kt`:**
|
||
* Убрать `ATWS` перед стартом. Если нужен сброс — использовать `ATZ` и ждать 2 секунды, после чего заново прогнать весь `init()`.
|
||
* Перед запуском `DynamicCollector` зафиксировать протокол: `elmProto.sendCommand("ATSP" + currentProtocol)`.
|