diff --git a/doc/elm-reference.md b/doc/elm-reference.md index 9e82aa9..62b86d0 100644 --- a/doc/elm-reference.md +++ b/doc/elm-reference.md @@ -543,3 +543,168 @@ outputStream.flush(); | **Kotlin-интеграция** | AndroidOBD + CarScanApp | Чистый API, современный код | | **PID/DTC парсинг** | AndrOBD | Все формулы, все режимы | | **KWP2000 (VW)** | KWP Logger | Если Phaeton на старом протоколе | + +--- + +## Часть 3: СЫРОЙ КОД AndrOBD — золотой стандарт (1993 ⭐, 10 лет в продакшене) + +### 3.1. Bluetooth SPP подключение (BtCommService.java) + +```java +final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); + +// Стандартный socket +if (secure) { + tmp = device.createRfcommSocketToServiceRecord(SPP_UUID); +} else { + tmp = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID); +} + +// FALLBACK: reflection RFCOMM channel 1 (для китайских клонов!) +catch (IOException e) { + Class clazz = mmSocket.getRemoteDevice().getClass(); + Method m = clazz.getMethod("createRfcommSocket", Integer.TYPE); + sockFallback = (BluetoothSocket) m.invoke(mmSocket.getRemoteDevice(), 1); + mmSocket = sockFallback; + mmSocket.connect(); // пробуем снова +} +``` + +### 3.2. 🚨 КРИТИЧЕСКИ: 500мс пауза после BT connect + +```java +// AndrOBD issue #233 — без этой паузы Android теряет данные! +Thread.sleep(500); // CRITICAL: Fix for Android Bluetooth timing + +// Только после этого запускаем worker thread +mBtWorkerThread = new BtWorkerThread(socket, socketType); +mBtWorkerThread.start(); +``` + +### 3.3. Инициализация ELM327 (точный порядок) + +```java +private void initialize() { + setStatus(STAT.INITIALIZING); + + // 1. Кастомные init-команды (если есть) + cmdQueue.addAll(customInitCommands); + + // 2. Установить протокол (AUTO или конкретный) + pushCommand(CMD.SETPROT, preferredProtocol.ordinal()); // ATSP + + // 3. Инициализировать адаптивный тайминг + mAdaptiveTiming.initialize(); // ATAT1 + + // 4. Ускорить протокол (убрать пробелы и переводы строк) + pushCommand(CMD.SETSPACES, 0); // ATS0 + pushCommand(CMD.SETLINEFEED, 0); // ATL0 + + // 5. Выключить эхо + pushCommand(CMD.ECHO, 0); // ATE0 +} +// → ждёт "ELM" ответ → затем 0100 (запрос поддерживаемых PID) +``` + +### 3.4. Адаптивный таймаут (ГЕНИАЛЬНО) + +```java +// Константы +ELM_TIMEOUT_MAX = 1000 // мс — максимум +ELM_TIMEOUT_DEFAULT = 200 // мс — стартовое значение +ELM_TIMEOUT_RES = 4 // мс — шаг изменения +ELM_TIMEOUT_MIN = 12 // мс — минимум + +// На каждый NODATA → увеличить таймаут на 4мс +// На каждый успех → уменьшить таймаут на 4мс (но не ниже learned_min) + +void adapt(boolean increaseTimeout) { + if (increaseTimeout) { + if (elmMsgTimeout + 4 < 1000) + setElmMsgTimeout(elmMsgTimeout + 4); + } else { + if (elmMsgTimeout - 4 >= learnedMin) + setElmMsgTimeout(elmMsgTimeout - 4); + } +} + +// Каждое изменение отправляет: ATST +``` + +**Диапазон: 12мс → 1000мс, шаг 4мс. Старт: 200мс.** + +### 3.5. Обработка ответов (handleTelegram) + +```java +// 1. Фильтр эха +if (lastTxMsg.equalsIgnoreCase(bufferStr)) + return 0; // проигнорировать + +// 2. Определить тип ответа +switch (getResponseId(bufferStr)) { + case PROMPT: // ">" — конец ответа + case MODEL: // "ELM" → вызвать initialize() + case SEARCHING: // идёт поиск + case NODATA: // нет данных + case ERROR: // ошибка + case NOCONN: // "UNABLE" + case BUSERROR: // ошибка шины + // ... ещё 10+ +} +``` + +### 3.6. Обработка ошибок (production-grade) + +``` +BUS ERROR / UNABLE / CAN ERROR: + → DISCONNECTED + → переставить в очередь последнюю команду + → сбросить протокол (ATSP) + → переинициализировать тайминг + → закрыть протокол (ATPC) + +DATA ERROR / RX ERROR / BUFFER FULL: + → WARM START (ATWS) — мягкий перезапуск + +NO DATA: + → увеличить адаптивный таймаут + → переустановить протокол + +STOPPED: + → переставить последнюю команду в очередь +``` + +### 3.7. Мульти-фрейм (ISO-TP для VIN и длинных ответов) + +```java +// Формат: "014" = 1 строка, 14 hex символов +// "0:4902015756..." — первая строка с длиной +// "1:5A5A314B5A41..." — продолжение + +if (buffer[0] == '0' && buffer.length == 3) { + charsExpected = Integer.valueOf(bufferStr, 16) * 2; // 0x14 = 20 байт + lastRxMsg = ""; + responsePending = true; +} + +if (bufferStr.indexOf(':') >= 0) { + lastRxMsg += bufferStr.substring(idx + 1); +} + +if (lastRxMsg.length() >= charsExpected) { + result = handleDataMessage(lastRxMsg); // готово +} +``` + +### 3.8. Итоговый процесс подключения (хронология) + +``` +1. BT socket connect() +2. Thread.sleep(500) ← КРИТИЧЕСКИ! Без этого — потеря данных. +3. BtWorkerThread.start() +4. StreamHandler.run() — побайтовое чтение +5. ATSP0 → ATAT1 → ATS0 → ATL0 → ATE0 +6. Ждём "ELM" → статус INITIALIZED +7. 0100 → опрос поддерживаемых PID +8. ECU detect → выбор ECU → готов к работе +```